diff --git a/applications/admin/static/codemirror/AUTHORS b/applications/admin/static/codemirror/AUTHORS
new file mode 100644
index 00000000..2ca41e67
--- /dev/null
+++ b/applications/admin/static/codemirror/AUTHORS
@@ -0,0 +1,427 @@
+List of CodeMirror contributors. Updated before every release.
+
+4r2r
+Aaron Brooks
+Abdelouahab
+Abe Fettig
+Adam Ahmed
+Adam King
+adanlobato
+Adán Lobato
+Adrian Aichner
+aeroson
+Ahmad Amireh
+Ahmad M. Zawawi
+ahoward
+Akeksandr Motsjonov
+Alberto González Palomo
+Alberto Pose
+Albert Xing
+Alexander Pavlov
+Alexander Schepanovski
+Alexander Shvets
+Alexander Solovyov
+Alexandre Bique
+alexey-k
+Alex Piggott
+Amsul
+amuntean
+Amy
+Ananya Sen
+anaran
+AndersMad
+Anders Nawroth
+Anderson Mesquita
+Andrea G
+Andreas Reischuck
+Andre von Houck
+Andrey Fedorov
+Andrey Klyuchnikov
+Andrey Lushnikov
+Andy Joslin
+Andy Kimball
+Andy Li
+angelozerr
+angelo.zerr@gmail.com
+Ankit
+Ankit Ahuja
+Ansel Santosa
+Anthony Grimes
+Anton Kovalyov
+areos
+as3boyan
+AtomicPages LLC
+Atul Bhouraskar
+Aurelian Oancea
+Bastian Müller
+Bem Jones-Bey
+benbro
+Beni Cherniavsky-Paskin
+Benjamin DeCoste
+Ben Keen
+Bernhard Sirlinger
+Bert Chang
+Billy Moon
+binny
+B Krishna Chaitanya
+Blaine G
+blukat29
+boomyjee
+borawjm
+Brandon Frohs
+Brandon Wamboldt
+Brett Zamir
+Brian Grinstead
+Brian Sletten
+Bruce Mitchener
+Chandra Sekhar Pydi
+Charles Skelton
+Cheah Chu Yeow
+Chris Coyier
+Chris Granger
+Chris Houseknecht
+Chris Morgan
+Christian Oyarzun
+Christopher Brown
+ciaranj
+CodeAnimal
+ComFreek
+Curtis Gagliardi
+dagsta
+daines
+Dale Jung
+Dan Bentley
+Dan Heberden
+Daniel, Dao Quang Minh
+Daniele Di Sarli
+Daniel Faust
+Daniel Huigens
+Daniel KJ
+Daniel Neel
+Daniel Parnell
+Danny Yoo
+darealshinji
+Darius Roberts
+Dave Myers
+David Mignot
+David Pathakjee
+David Vázquez
+deebugger
+Deep Thought
+Devon Carew
+dignifiedquire
+Dimage Sapelkin
+domagoj412
+Dominator008
+Domizio Demichelis
+Doug Wikle
+Drew Bratcher
+Drew Hintz
+Drew Khoury
+Dror BG
+duralog
+eborden
+edsharp
+ekhaled
+Enam Mijbah Noor
+Eric Allam
+eustas
+Fabien O'Carroll
+Fabio Zendhi Nagao
+Faiza Alsaied
+Fauntleroy
+fbuchinger
+feizhang365
+Felipe Lalanne
+Felix Raab
+Filip Noetzel
+flack
+ForbesLindesay
+Forbes Lindesay
+Ford_Lawnmower
+Forrest Oliphant
+Frank Wiegand
+Gabriel Gheorghian
+Gabriel Horner
+Gabriel Nahmias
+galambalazs
+Gautam Mehta
+gekkoe
+Gerard Braad
+Gergely Hegykozi
+Glenn Jorde
+Glenn Ruehle
+Golevka
+Gordon Smith
+Grant Skinner
+greengiant
+Gregory Koberger
+Guillaume Massé
+Guillaume Massé
+Gustavo Rodrigues
+Hakan Tunc
+Hans Engel
+Hardest
+Hasan Karahan
+Herculano Campos
+Hiroyuki Makino
+hitsthings
+Hocdoc
+Ian Beck
+Ian Dickinson
+Ian Wehrman
+Ian Wetherbee
+Ice White
+ICHIKAWA, Yuji
+ilvalle
+Ingo Richter
+Irakli Gozalishvili
+Ivan Kurnosov
+Jacob Lee
+Jakob Miland
+Jakub Vrana
+Jakub Vrána
+James Campos
+James Thorne
+Jamie Hill
+Jan Jongboom
+jankeromnes
+Jan Keromnes
+Jan Odvarko
+Jan T. Sott
+Jared Forsyth
+Jason
+Jason Barnabe
+Jason Grout
+Jason Johnston
+Jason San Jose
+Jason Siefken
+Jaydeep Solanki
+Jean Boussier
+jeffkenton
+Jeff Pickhardt
+jem (graphite)
+Jeremy Parmenter
+Jochen Berger
+Johan Ask
+John Connor
+John Lees-Miller
+John Snelson
+John Van Der Loo
+Jonathan Malmaud
+jongalloway
+Jon Malmaud
+Jon Sangster
+Joost-Wim Boekesteijn
+Joseph Pecoraro
+Joshua Newman
+Josh Watzman
+jots
+jsoojeon
+Juan Benavides Romero
+Jucovschi Constantin
+Juho Vuori
+Justin Hileman
+jwallers@gmail.com
+kaniga
+Ken Newman
+Ken Rockot
+Kevin Sawicki
+Kevin Ushey
+Klaus Silveira
+Koh Zi Han, Cliff
+komakino
+Konstantin Lopuhin
+koops
+ks-ifware
+kubelsmieci
+Lanfei
+Lanny
+Laszlo Vidacs
+leaf corcoran
+Leonid Khachaturov
+Leon Sorokin
+Leonya Khachaturov
+Liam Newman
+LM
+lochel
+Lorenzo Stoakes
+Luciano Longo
+Luke Stagner
+lynschinzer
+Maksim Lin
+Maksym Taran
+Malay Majithia
+Manuel Rego Casasnovas
+Marat Dreizin
+Marcel Gerber
+Marco Aurélio
+Marco Munizaga
+Marcus Bointon
+Marek Rudnicki
+Marijn Haverbeke
+Mário Gonçalves
+Mario Pietsch
+Mark Lentczner
+Marko Bonaci
+Martin Balek
+Martín Gaitán
+Martin Hasoň
+Mason Malone
+Mateusz Paprocki
+Mathias Bynens
+mats cronqvist
+Matthew Beale
+Matthias Bussonnier
+Matthias BUSSONNIER
+Matt McDonald
+Matt Pass
+Matt Sacks
+mauricio
+Maximilian Hils
+Maxim Kraev
+Max Kirsch
+Max Xiantu
+mbarkhau
+Metatheos
+Micah Dubinko
+Michael Lehenbauer
+Michael Zhou
+Mighty Guava
+Miguel Castillo
+Mike
+Mike Brevoort
+Mike Diaz
+Mike Ivanov
+Mike Kadin
+MinRK
+Miraculix87
+misfo
+mloginov
+Moritz Schwörer
+mps
+mtaran-google
+Narciso Jaramillo
+Nathan Williams
+ndr
+nerbert
+nextrevision
+nguillaumin
+Ng Zhi An
+Nicholas Bollweg
+Nicholas Bollweg (Nick)
+Nick Small
+Niels van Groningen
+nightwing
+Nikita Beloglazov
+Nikita Vasilyev
+Nikolay Kostov
+nilp0inter
+Nisarg Jhaveri
+nlwillia
+Norman Rzepka
+pablo
+Page
+Panupong Pasupat
+paris
+Patil Arpith
+Patrick Stoica
+Patrick Strawderman
+Paul Garvin
+Paul Ivanov
+Pavel Feldman
+Pavel Strashkin
+Paweł Bartkiewicz
+peteguhl
+Peter Flynn
+peterkroon
+Peter Kroon
+prasanthj
+Prasanth J
+Radek Piórkowski
+Rahul
+Randall Mason
+Randy Burden
+Randy Edmunds
+Rasmus Erik Voel Jensen
+Richard van der Meer
+Richard Z.H. Wang
+Robert Crossfield
+Roberto Abdelkader Martínez Pérez
+robertop23
+Robert Plummer
+Ruslan Osmanov
+Ryan Prior
+sabaca
+Samuel Ainsworth
+sandeepshetty
+Sander AKA Redsandro
+santec
+Sascha Peilicke
+satchmorun
+sathyamoorthi
+SCLINIC\jdecker
+Scott Aikin
+Scott Goodhew
+Sebastian Zaha
+shaund
+shaun gilchrist
+Shawn A
+sheopory
+Shiv Deepak
+Shmuel Englard
+Shubham Jain
+silverwind
+snasa
+soliton4
+sonson
+spastorelli
+srajanpaliwal
+Stanislav Oaserele
+Stas Kobzar
+Stefan Borsje
+Steffen Beyer
+Steve O'Hara
+stoskov
+Taha Jahangir
+Takuji Shimokawa
+Tarmil
+tel
+tfjgeorge
+Thaddee Tyl
+TheHowl
+think
+Thomas Dvornik
+Thomas Schmid
+Tim Alby
+Tim Baumann
+Timothy Farrell
+Timothy Hatcher
+TobiasBg
+Tomas-A
+Tomas Varaneckas
+Tom Erik Støwer
+Tom MacWright
+Tony Jian
+Travis Heppe
+Triangle717
+twifkak
+Vestimir Markov
+vf
+Vincent Woo
+Volker Mische
+wenli
+Wesley Wiser
+Will Binns-Smith
+William Jamieson
+William Stein
+Willy
+Wojtek Ptak
+Xavier Mendez
+Yassin N. Hassan
+YNH Webdev
+Yunchi Luo
+Yuvi Panda
+Zachary Dremann
+zziuni
+魏鹏刚
diff --git a/applications/admin/static/codemirror/CONTRIBUTING.md b/applications/admin/static/codemirror/CONTRIBUTING.md
index 8938f620..c4296ce4 100644
--- a/applications/admin/static/codemirror/CONTRIBUTING.md
+++ b/applications/admin/static/codemirror/CONTRIBUTING.md
@@ -7,17 +7,17 @@
## Getting help
Community discussion, questions, and informal bug reporting is done on the
-[CodeMirror Google group](http://groups.google.com/group/codemirror).
+[discuss.CodeMirror forum](http://discuss.codemirror.net).
## Submitting bug reports
The preferred way to report bugs is to use the
-[GitHub issue tracker](http://github.com/marijnh/CodeMirror/issues). Before
+[GitHub issue tracker](http://github.com/codemirror/CodeMirror/issues). Before
reporting a bug, read these pointers.
**Note:** The issue tracker is for *bugs*, not requests for help. Questions
should be asked on the
-[CodeMirror Google group](http://groups.google.com/group/codemirror) instead.
+[discuss.CodeMirror forum](http://discuss.codemirror.net) instead.
### Reporting bugs effectively
@@ -48,7 +48,7 @@ should be asked on the
## Contributing code
- Make sure you have a [GitHub Account](https://github.com/signup/free)
-- Fork [CodeMirror](https://github.com/marijnh/CodeMirror/)
+- Fork [CodeMirror](https://github.com/codemirror/CodeMirror/)
([how to fork a repo](https://help.github.com/articles/fork-a-repo))
- Make your changes
- If your changes are easy to test or likely to regress, add tests.
@@ -70,3 +70,7 @@ should be asked on the
- Note that the linter (`bin/lint`) which is run after each commit
complains about unused variables and functions. Prefix their names
with an underscore to muffle it.
+
+- CodeMirror does *not* follow JSHint or JSLint prescribed style.
+ Patches that try to 'fix' code to pass one of these linters will be
+ unceremoniously discarded.
diff --git a/applications/admin/static/codemirror/LICENSE b/applications/admin/static/codemirror/LICENSE
index 442d11cd..d21bbea5 100644
--- a/applications/admin/static/codemirror/LICENSE
+++ b/applications/admin/static/codemirror/LICENSE
@@ -1,4 +1,4 @@
-Copyright (C) 2013 by Marijn Haverbeke and others
+Copyright (C) 2014 by Marijn Haverbeke and others
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/applications/admin/static/codemirror/README.md b/applications/admin/static/codemirror/README.md
index 61f6b645..42b06f74 100644
--- a/applications/admin/static/codemirror/README.md
+++ b/applications/admin/static/codemirror/README.md
@@ -1,6 +1,6 @@
# CodeMirror
-[](http://travis-ci.org/marijnh/CodeMirror)
-[](http://badge.fury.io/js/codemirror)
+[](https://travis-ci.org/codemirror/CodeMirror)
+[](https://www.npmjs.org/package/codemirror)
CodeMirror is a JavaScript component that provides a code editor in
the browser. When a mode is available for the language you are coding
@@ -8,4 +8,4 @@ in, it will color your code, and optionally help with indentation.
The project page is http://codemirror.net
The manual is at http://codemirror.net/doc/manual.html
-The contributing guidelines are in [CONTRIBUTING.md](https://github.com/marijnh/CodeMirror/blob/master/CONTRIBUTING.md)
+The contributing guidelines are in [CONTRIBUTING.md](https://github.com/codemirror/CodeMirror/blob/master/CONTRIBUTING.md)
diff --git a/applications/admin/static/codemirror/addon/comment/comment.js b/applications/admin/static/codemirror/addon/comment/comment.js
index 1eb9a05c..2dd114d3 100644
--- a/applications/admin/static/codemirror/addon/comment/comment.js
+++ b/applications/admin/static/codemirror/addon/comment/comment.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
@@ -106,7 +109,7 @@
CodeMirror.defineExtension("uncomment", function(from, to, options) {
if (!options) options = noOptions;
var self = this, mode = self.getModeAt(from);
- var end = Math.min(to.line, self.lastLine()), start = Math.min(from.line, end);
+ var end = Math.min(to.ch != 0 || to.line == from.line ? to.line : to.line - 1, self.lastLine()), start = Math.min(from.line, end);
// Try finding line comments
var lineString = options.lineComment || mode.lineComment, lines = [];
@@ -150,6 +153,17 @@
!/comment/.test(self.getTokenTypeAt(Pos(end, close + 1))))
return false;
+ // Avoid killing block comments completely outside the selection.
+ // Positions of the last startString before the start of the selection, and the first endString after it.
+ var lastStart = startLine.lastIndexOf(startString, from.ch);
+ var firstEnd = lastStart == -1 ? -1 : startLine.slice(0, from.ch).indexOf(endString, lastStart + startString.length);
+ if (lastStart != -1 && firstEnd != -1 && firstEnd + endString.length != from.ch) return false;
+ // Positions of the first endString after the end of the selection, and the last startString before it.
+ firstEnd = endLine.indexOf(endString, to.ch);
+ var almostLastStart = endLine.slice(to.ch).lastIndexOf(startString, firstEnd - to.ch);
+ lastStart = (firstEnd == -1 || almostLastStart == -1) ? -1 : to.ch + almostLastStart;
+ if (firstEnd != -1 && lastStart != -1 && lastStart != to.ch) return false;
+
self.operation(function() {
self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)),
Pos(end, close + endString.length));
diff --git a/applications/admin/static/codemirror/addon/comment/continuecomment.js b/applications/admin/static/codemirror/addon/comment/continuecomment.js
new file mode 100644
index 00000000..b11d51e6
--- /dev/null
+++ b/applications/admin/static/codemirror/addon/comment/continuecomment.js
@@ -0,0 +1,85 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ var modes = ["clike", "css", "javascript"];
+
+ for (var i = 0; i < modes.length; ++i)
+ CodeMirror.extendMode(modes[i], {blockCommentContinue: " * "});
+
+ function continueComment(cm) {
+ if (cm.getOption("disableInput")) return CodeMirror.Pass;
+ var ranges = cm.listSelections(), mode, inserts = [];
+ for (var i = 0; i < ranges.length; i++) {
+ var pos = ranges[i].head, token = cm.getTokenAt(pos);
+ if (token.type != "comment") return CodeMirror.Pass;
+ var modeHere = CodeMirror.innerMode(cm.getMode(), token.state).mode;
+ if (!mode) mode = modeHere;
+ else if (mode != modeHere) return CodeMirror.Pass;
+
+ var insert = null;
+ if (mode.blockCommentStart && mode.blockCommentContinue) {
+ var end = token.string.indexOf(mode.blockCommentEnd);
+ var full = cm.getRange(CodeMirror.Pos(pos.line, 0), CodeMirror.Pos(pos.line, token.end)), found;
+ if (end != -1 && end == token.string.length - mode.blockCommentEnd.length && pos.ch >= end) {
+ // Comment ended, don't continue it
+ } else if (token.string.indexOf(mode.blockCommentStart) == 0) {
+ insert = full.slice(0, token.start);
+ if (!/^\s*$/.test(insert)) {
+ insert = "";
+ for (var j = 0; j < token.start; ++j) insert += " ";
+ }
+ } else if ((found = full.indexOf(mode.blockCommentContinue)) != -1 &&
+ found + mode.blockCommentContinue.length > token.start &&
+ /^\s*$/.test(full.slice(0, found))) {
+ insert = full.slice(0, found);
+ }
+ if (insert != null) insert += mode.blockCommentContinue;
+ }
+ if (insert == null && mode.lineComment && continueLineCommentEnabled(cm)) {
+ var line = cm.getLine(pos.line), found = line.indexOf(mode.lineComment);
+ if (found > -1) {
+ insert = line.slice(0, found);
+ if (/\S/.test(insert)) insert = null;
+ else insert += mode.lineComment + line.slice(found + mode.lineComment.length).match(/^\s*/)[0];
+ }
+ }
+ if (insert == null) return CodeMirror.Pass;
+ inserts[i] = "\n" + insert;
+ }
+
+ cm.operation(function() {
+ for (var i = ranges.length - 1; i >= 0; i--)
+ cm.replaceRange(inserts[i], ranges[i].from(), ranges[i].to(), "+insert");
+ });
+ }
+
+ function continueLineCommentEnabled(cm) {
+ var opt = cm.getOption("continueComments");
+ if (opt && typeof opt == "object")
+ return opt.continueLineComment !== false;
+ return true;
+ }
+
+ CodeMirror.defineOption("continueComments", null, function(cm, val, prev) {
+ if (prev && prev != CodeMirror.Init)
+ cm.removeKeyMap("continueComment");
+ if (val) {
+ var key = "Enter";
+ if (typeof val == "string")
+ key = val;
+ else if (typeof val == "object" && val.key)
+ key = val.key;
+ var map = {name: "continueComment"};
+ map[key] = continueComment;
+ cm.addKeyMap(map);
+ }
+ });
+});
diff --git a/applications/admin/static/codemirror/addon/dialog/dialog.js b/applications/admin/static/codemirror/addon/dialog/dialog.js
index 586b7370..e0e8ad4e 100644
--- a/applications/admin/static/codemirror/addon/dialog/dialog.js
+++ b/applications/admin/static/codemirror/addon/dialog/dialog.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
// Open simple dialogs on top of an editor. Relies on dialog.css.
(function(mod) {
@@ -12,11 +15,11 @@
var wrap = cm.getWrapperElement();
var dialog;
dialog = wrap.appendChild(document.createElement("div"));
- if (bottom) {
+ if (bottom)
dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom";
- } else {
+ else
dialog.className = "CodeMirror-dialog CodeMirror-dialog-top";
- }
+
if (typeof template == "string") {
dialog.innerHTML = template;
} else { // Assuming it's a detached DOM element.
@@ -32,40 +35,59 @@
}
CodeMirror.defineExtension("openDialog", function(template, callback, options) {
+ if (!options) options = {};
+
closeNotification(this, null);
- var dialog = dialogDiv(this, template, options && options.bottom);
+
+ var dialog = dialogDiv(this, template, options.bottom);
var closed = false, me = this;
- function close() {
- if (closed) return;
- closed = true;
- dialog.parentNode.removeChild(dialog);
+ function close(newVal) {
+ if (typeof newVal == 'string') {
+ inp.value = newVal;
+ } else {
+ if (closed) return;
+ closed = true;
+ dialog.parentNode.removeChild(dialog);
+ me.focus();
+
+ if (options.onClose) options.onClose(dialog);
+ }
}
+
var inp = dialog.getElementsByTagName("input")[0], button;
if (inp) {
- if (options && options.value) inp.value = options.value;
+ if (options.value) {
+ inp.value = options.value;
+ inp.select();
+ }
+
+ if (options.onInput)
+ CodeMirror.on(inp, "input", function(e) { options.onInput(e, inp.value, close);});
+ if (options.onKeyUp)
+ CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);});
+
CodeMirror.on(inp, "keydown", function(e) {
if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; }
- if (e.keyCode == 13 || e.keyCode == 27) {
+ if (e.keyCode == 27 || (options.closeOnEnter !== false && e.keyCode == 13)) {
inp.blur();
CodeMirror.e_stop(e);
close();
- me.focus();
- if (e.keyCode == 13) callback(inp.value);
}
+ if (e.keyCode == 13) callback(inp.value, e);
});
- if (options && options.onKeyUp) {
- CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);});
- }
- if (options && options.value) inp.value = options.value;
+
+ if (options.closeOnBlur !== false) CodeMirror.on(inp, "blur", close);
+
inp.focus();
- CodeMirror.on(inp, "blur", close);
} else if (button = dialog.getElementsByTagName("button")[0]) {
CodeMirror.on(button, "click", function() {
close();
me.focus();
});
+
+ if (options.closeOnBlur !== false) CodeMirror.on(button, "blur", close);
+
button.focus();
- CodeMirror.on(button, "blur", close);
}
return close;
});
@@ -110,8 +132,8 @@
CodeMirror.defineExtension("openNotification", function(template, options) {
closeNotification(this, close);
var dialog = dialogDiv(this, template, options && options.bottom);
- var duration = options && (options.duration === undefined ? 5000 : options.duration);
var closed = false, doneTimer;
+ var duration = options && typeof options.duration !== "undefined" ? options.duration : 5000;
function close() {
if (closed) return;
@@ -124,7 +146,10 @@
CodeMirror.e_preventDefault(e);
close();
});
+
if (duration)
- doneTimer = setTimeout(close, options.duration);
+ doneTimer = setTimeout(close, duration);
+
+ return close;
});
});
diff --git a/applications/admin/static/codemirror/addon/display/fullscreen.js b/applications/admin/static/codemirror/addon/display/fullscreen.js
index e39c6e16..cd3673b9 100644
--- a/applications/admin/static/codemirror/addon/display/fullscreen.js
+++ b/applications/admin/static/codemirror/addon/display/fullscreen.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
diff --git a/applications/admin/static/codemirror/addon/display/panel.js b/applications/admin/static/codemirror/addon/display/panel.js
new file mode 100644
index 00000000..22c0453e
--- /dev/null
+++ b/applications/admin/static/codemirror/addon/display/panel.js
@@ -0,0 +1,94 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ CodeMirror.defineExtension("addPanel", function(node, options) {
+ if (!this.state.panels) initPanels(this);
+
+ var info = this.state.panels;
+ if (options && options.position == "bottom")
+ info.wrapper.appendChild(node);
+ else
+ info.wrapper.insertBefore(node, info.wrapper.firstChild);
+ var height = (options && options.height) || node.offsetHeight;
+ this._setSize(null, info.heightLeft -= height);
+ info.panels++;
+ return new Panel(this, node, options, height);
+ });
+
+ function Panel(cm, node, options, height) {
+ this.cm = cm;
+ this.node = node;
+ this.options = options;
+ this.height = height;
+ this.cleared = false;
+ }
+
+ Panel.prototype.clear = function() {
+ if (this.cleared) return;
+ this.cleared = true;
+ var info = this.cm.state.panels;
+ this.cm._setSize(null, info.heightLeft += this.height);
+ info.wrapper.removeChild(this.node);
+ if (--info.panels == 0) removePanels(this.cm);
+ };
+
+ Panel.prototype.changed = function(height) {
+ var newHeight = height == null ? this.node.offsetHeight : height;
+ var info = this.cm.state.panels;
+ this.cm._setSize(null, info.height += (newHeight - this.height));
+ this.height = newHeight;
+ };
+
+ function initPanels(cm) {
+ var wrap = cm.getWrapperElement();
+ var style = window.getComputedStyle ? window.getComputedStyle(wrap) : wrap.currentStyle;
+ var height = parseInt(style.height);
+ var info = cm.state.panels = {
+ setHeight: wrap.style.height,
+ heightLeft: height,
+ panels: 0,
+ wrapper: document.createElement("div")
+ };
+ wrap.parentNode.insertBefore(info.wrapper, wrap);
+ var hasFocus = cm.hasFocus();
+ info.wrapper.appendChild(wrap);
+ if (hasFocus) cm.focus();
+
+ cm._setSize = cm.setSize;
+ if (height != null) cm.setSize = function(width, newHeight) {
+ if (newHeight == null) return this._setSize(width, newHeight);
+ info.setHeight = newHeight;
+ if (typeof newHeight != "number") {
+ var px = /^(\d+\.?\d*)px$/.exec(newHeight);
+ if (px) {
+ newHeight = Number(px[1]);
+ } else {
+ info.wrapper.style.height = newHeight;
+ newHeight = info.wrapper.offsetHeight;
+ info.wrapper.style.height = "";
+ }
+ }
+ cm._setSize(width, info.heightLeft += (newHeight - height));
+ height = newHeight;
+ };
+ }
+
+ function removePanels(cm) {
+ var info = cm.state.panels;
+ cm.state.panels = null;
+
+ var wrap = cm.getWrapperElement();
+ info.wrapper.parentNode.replaceChild(wrap, info.wrapper);
+ wrap.style.height = info.setHeight;
+ cm.setSize = cm._setSize;
+ cm.setSize();
+ }
+});
diff --git a/applications/admin/static/codemirror/addon/display/placeholder.js b/applications/admin/static/codemirror/addon/display/placeholder.js
index 0fdc9b0d..bb0c3931 100644
--- a/applications/admin/static/codemirror/addon/display/placeholder.js
+++ b/applications/admin/static/codemirror/addon/display/placeholder.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
diff --git a/applications/admin/static/codemirror/addon/display/rulers.js b/applications/admin/static/codemirror/addon/display/rulers.js
new file mode 100644
index 00000000..13185d30
--- /dev/null
+++ b/applications/admin/static/codemirror/addon/display/rulers.js
@@ -0,0 +1,64 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ CodeMirror.defineOption("rulers", false, function(cm, val, old) {
+ if (old && old != CodeMirror.Init) {
+ clearRulers(cm);
+ cm.off("refresh", refreshRulers);
+ }
+ if (val && val.length) {
+ setRulers(cm);
+ cm.on("refresh", refreshRulers);
+ }
+ });
+
+ function clearRulers(cm) {
+ for (var i = cm.display.lineSpace.childNodes.length - 1; i >= 0; i--) {
+ var node = cm.display.lineSpace.childNodes[i];
+ if (/(^|\s)CodeMirror-ruler($|\s)/.test(node.className))
+ node.parentNode.removeChild(node);
+ }
+ }
+
+ function setRulers(cm) {
+ var val = cm.getOption("rulers");
+ var cw = cm.defaultCharWidth();
+ var left = cm.charCoords(CodeMirror.Pos(cm.firstLine(), 0), "div").left;
+ var minH = cm.display.scroller.offsetHeight + 30;
+ for (var i = 0; i < val.length; i++) {
+ var elt = document.createElement("div");
+ elt.className = "CodeMirror-ruler";
+ var col, cls = null, conf = val[i];
+ if (typeof conf == "number") {
+ col = conf;
+ } else {
+ col = conf.column;
+ if (conf.className) elt.className += " " + conf.className;
+ if (conf.color) elt.style.borderColor = conf.color;
+ if (conf.lineStyle) elt.style.borderLeftStyle = conf.lineStyle;
+ if (conf.width) elt.style.borderLeftWidth = conf.width;
+ cls = val[i].className;
+ }
+ elt.style.left = (left + col * cw) + "px";
+ elt.style.top = "-50px";
+ elt.style.bottom = "-20px";
+ elt.style.minHeight = minH + "px";
+ cm.display.lineSpace.insertBefore(elt, cm.display.cursorDiv);
+ }
+ }
+
+ function refreshRulers(cm) {
+ clearRulers(cm);
+ setRulers(cm);
+ }
+});
diff --git a/applications/admin/static/codemirror/addon/edit/closebrackets.js b/applications/admin/static/codemirror/addon/edit/closebrackets.js
index f48ad881..f6b42f02 100644
--- a/applications/admin/static/codemirror/addon/edit/closebrackets.js
+++ b/applications/admin/static/codemirror/addon/edit/closebrackets.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
@@ -10,6 +13,8 @@
var DEFAULT_EXPLODE_ON_ENTER = "[]{}";
var SPACE_CHAR_REGEX = /\s/;
+ var Pos = CodeMirror.Pos;
+
CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) {
if (old != CodeMirror.Init && old)
cm.removeKeyMap("autoCloseBrackets");
@@ -26,11 +31,27 @@
});
function charsAround(cm, pos) {
- var str = cm.getRange(CodeMirror.Pos(pos.line, pos.ch - 1),
- CodeMirror.Pos(pos.line, pos.ch + 1));
+ var str = cm.getRange(Pos(pos.line, pos.ch - 1),
+ Pos(pos.line, pos.ch + 1));
return str.length == 2 ? str : null;
}
+ // Project the token type that will exists after the given char is
+ // typed, and use it to determine whether it would cause the start
+ // of a string token.
+ function enteringString(cm, pos, ch) {
+ var line = cm.getLine(pos.line);
+ var token = cm.getTokenAt(pos);
+ if (/\bstring2?\b/.test(token.type)) return false;
+ var stream = new CodeMirror.StringStream(line.slice(0, pos.ch) + ch + line.slice(pos.ch), 4);
+ stream.pos = stream.start = token.start;
+ for (;;) {
+ var type1 = cm.getMode().token(stream, token.state);
+ if (stream.pos >= pos.ch + 1) return /\bstring2?\b/.test(type1);
+ stream.start = stream.pos;
+ }
+ }
+
function buildKeymap(pairs) {
var map = {
name : "autoCloseBrackets",
@@ -44,53 +65,68 @@
}
for (var i = ranges.length - 1; i >= 0; i--) {
var cur = ranges[i].head;
- cm.replaceRange("", CodeMirror.Pos(cur.line, cur.ch - 1), CodeMirror.Pos(cur.line, cur.ch + 1));
+ cm.replaceRange("", Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1));
}
}
};
var closingBrackets = "";
for (var i = 0; i < pairs.length; i += 2) (function(left, right) {
- if (left != right) closingBrackets += right;
+ closingBrackets += right;
map["'" + left + "'"] = function(cm) {
if (cm.getOption("disableInput")) return CodeMirror.Pass;
var ranges = cm.listSelections(), type, next;
for (var i = 0; i < ranges.length; i++) {
var range = ranges[i], cur = range.head, curType;
- if (left == "'" && cm.getTokenTypeAt(cur) == "comment")
- return CodeMirror.Pass;
- var next = cm.getRange(cur, CodeMirror.Pos(cur.line, cur.ch + 1));
- if (!range.empty())
+ var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1));
+ if (!range.empty()) {
curType = "surround";
- else if (left == right && next == right)
- curType = "skip";
- else if (left == right && CodeMirror.isWordChar(next))
- return CodeMirror.Pass;
- else if (cm.getLine(cur.line).length == cur.ch || closingBrackets.indexOf(next) >= 0 || SPACE_CHAR_REGEX.test(next))
+ } else if (left == right && next == right) {
+ if (cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == left + left + left)
+ curType = "skipThree";
+ else
+ curType = "skip";
+ } else if (left == right && cur.ch > 1 &&
+ cm.getRange(Pos(cur.line, cur.ch - 2), cur) == left + left &&
+ (cur.ch <= 2 || cm.getRange(Pos(cur.line, cur.ch - 3), Pos(cur.line, cur.ch - 2)) != left)) {
+ curType = "addFour";
+ } else if (left == '"' || left == "'") {
+ if (!CodeMirror.isWordChar(next) && enteringString(cm, cur, left)) curType = "both";
+ else return CodeMirror.Pass;
+ } else if (cm.getLine(cur.line).length == cur.ch || closingBrackets.indexOf(next) >= 0 || SPACE_CHAR_REGEX.test(next)) {
curType = "both";
- else
+ } else {
return CodeMirror.Pass;
+ }
if (!type) type = curType;
else if (type != curType) return CodeMirror.Pass;
}
- if (type == "skip") {
- cm.execCommand("goCharRight");
- } else if (type == "surround") {
- var sels = cm.getSelections();
- for (var i = 0; i < sels.length; i++)
- sels[i] = left + sels[i] + right;
- cm.replaceSelections(sels, "around");
- } else if (type == "both") {
- cm.replaceSelection(left + right, null);
- cm.execCommand("goCharLeft");
- }
+ cm.operation(function() {
+ if (type == "skip") {
+ cm.execCommand("goCharRight");
+ } else if (type == "skipThree") {
+ for (var i = 0; i < 3; i++)
+ cm.execCommand("goCharRight");
+ } else if (type == "surround") {
+ var sels = cm.getSelections();
+ for (var i = 0; i < sels.length; i++)
+ sels[i] = left + sels[i] + right;
+ cm.replaceSelections(sels, "around");
+ } else if (type == "both") {
+ cm.replaceSelection(left + right, null);
+ cm.execCommand("goCharLeft");
+ } else if (type == "addFour") {
+ cm.replaceSelection(left + left + left + left, "before");
+ cm.execCommand("goCharRight");
+ }
+ });
};
if (left != right) map["'" + right + "'"] = function(cm) {
var ranges = cm.listSelections();
for (var i = 0; i < ranges.length; i++) {
var range = ranges[i];
if (!range.empty() ||
- cm.getRange(range.head, CodeMirror.Pos(range.head.line, range.head.ch + 1)) != right)
+ cm.getRange(range.head, Pos(range.head.line, range.head.ch + 1)) != right)
return CodeMirror.Pass;
}
cm.execCommand("goCharRight");
diff --git a/applications/admin/static/codemirror/addon/edit/closetag.js b/applications/admin/static/codemirror/addon/edit/closetag.js
index c7c0701b..369bea30 100644
--- a/applications/admin/static/codemirror/addon/edit/closetag.js
+++ b/applications/admin/static/codemirror/addon/edit/closetag.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
/**
* Tag-closer extension for CodeMirror.
*
@@ -55,6 +58,7 @@
var pos = ranges[i].head, tok = cm.getTokenAt(pos);
var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
if (inner.mode.name != "xml" || !state.tagName) return CodeMirror.Pass;
+
var opt = cm.getOption("autoCloseTags"), html = inner.mode.configuration == "html";
var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose);
var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent);
@@ -68,8 +72,7 @@
tok.type == "tag" && state.type == "closeTag" ||
tok.string.indexOf("/") == (tok.string.length - 1) || // match something like
dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1 ||
- CodeMirror.scanForClosingTag && CodeMirror.scanForClosingTag(cm, pos, tagName,
- Math.min(cm.lastLine() + 1, pos.line + 50)))
+ closingTagExists(cm, tagName, pos, state, true))
return CodeMirror.Pass;
var indent = indentTags && indexOf(indentTags, lowerTagName) > -1;
@@ -91,26 +94,73 @@
}
}
- function autoCloseSlash(cm) {
- if (cm.getOption("disableInput")) return CodeMirror.Pass;
+ function autoCloseCurrent(cm, typingSlash) {
var ranges = cm.listSelections(), replacements = [];
+ var head = typingSlash ? "/" : "";
for (var i = 0; i < ranges.length; i++) {
if (!ranges[i].empty()) return CodeMirror.Pass;
var pos = ranges[i].head, tok = cm.getTokenAt(pos);
var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
- if (tok.type == "string" || tok.string.charAt(0) != "<" ||
- tok.start != pos.ch - 1 || inner.mode.name != "xml" ||
- !state.context || !state.context.tagName)
+ if (typingSlash && (tok.type == "string" || tok.string.charAt(0) != "<" ||
+ tok.start != pos.ch - 1))
return CodeMirror.Pass;
- replacements[i] = "/" + state.context.tagName + ">";
+ // Kludge to get around the fact that we are not in XML mode
+ // when completing in JS/CSS snippet in htmlmixed mode. Does not
+ // work for other XML embedded languages (there is no general
+ // way to go from a mixed mode to its current XML state).
+ if (inner.mode.name != "xml") {
+ if (cm.getMode().name == "htmlmixed" && inner.mode.name == "javascript")
+ replacements[i] = head + "script>";
+ else if (cm.getMode().name == "htmlmixed" && inner.mode.name == "css")
+ replacements[i] = head + "style>";
+ else
+ return CodeMirror.Pass;
+ } else {
+ if (!state.context || !state.context.tagName ||
+ closingTagExists(cm, state.context.tagName, pos, state))
+ return CodeMirror.Pass;
+ replacements[i] = head + state.context.tagName + ">";
+ }
}
cm.replaceSelections(replacements);
+ ranges = cm.listSelections();
+ for (var i = 0; i < ranges.length; i++)
+ if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line)
+ cm.indentLine(ranges[i].head.line);
}
+ function autoCloseSlash(cm) {
+ if (cm.getOption("disableInput")) return CodeMirror.Pass;
+ autoCloseCurrent(cm, true);
+ }
+
+ CodeMirror.commands.closeTag = function(cm) { return autoCloseCurrent(cm); };
+
function indexOf(collection, elt) {
if (collection.indexOf) return collection.indexOf(elt);
for (var i = 0, e = collection.length; i < e; ++i)
if (collection[i] == elt) return i;
return -1;
}
+
+ // If xml-fold is loaded, we use its functionality to try and verify
+ // whether a given tag is actually unclosed.
+ function closingTagExists(cm, tagName, pos, state, newTag) {
+ if (!CodeMirror.scanForClosingTag) return false;
+ var end = Math.min(cm.lastLine() + 1, pos.line + 500);
+ var nextClose = CodeMirror.scanForClosingTag(cm, pos, null, end);
+ if (!nextClose || nextClose.tag != tagName) return false;
+ var cx = state.context;
+ // If the immediate wrapping context contains onCx instances of
+ // the same tag, a closing tag only exists if there are at least
+ // that many closing tags of that type following.
+ for (var onCx = newTag ? 1 : 0; cx && cx.tagName == tagName; cx = cx.prev) ++onCx;
+ pos = nextClose.to;
+ for (var i = 1; i < onCx; i++) {
+ var next = CodeMirror.scanForClosingTag(cm, pos, null, end);
+ if (!next || next.tag != tagName) return false;
+ pos = next.to;
+ }
+ return true;
+ }
});
diff --git a/applications/admin/static/codemirror/addon/edit/continuecomment.js b/applications/admin/static/codemirror/addon/edit/continuecomment.js
deleted file mode 100644
index 30802622..00000000
--- a/applications/admin/static/codemirror/addon/edit/continuecomment.js
+++ /dev/null
@@ -1,44 +0,0 @@
-(function() {
- var modes = ["clike", "css", "javascript"];
- for (var i = 0; i < modes.length; ++i)
- CodeMirror.extendMode(modes[i], {blockCommentStart: "/*",
- blockCommentEnd: "*/",
- blockCommentContinue: " * "});
-
- function continueComment(cm) {
- var pos = cm.getCursor(), token = cm.getTokenAt(pos);
- var mode = CodeMirror.innerMode(cm.getMode(), token.state).mode;
- var space;
-
- if (token.type == "comment" && mode.blockCommentStart) {
- var end = token.string.indexOf(mode.blockCommentEnd);
- var full = cm.getRange(CodeMirror.Pos(pos.line, 0), CodeMirror.Pos(pos.line, token.end)), found;
- if (end != -1 && end == token.string.length - mode.blockCommentEnd.length) {
- // Comment ended, don't continue it
- } else if (token.string.indexOf(mode.blockCommentStart) == 0) {
- space = full.slice(0, token.start);
- if (!/^\s*$/.test(space)) {
- space = "";
- for (var i = 0; i < token.start; ++i) space += " ";
- }
- } else if ((found = full.indexOf(mode.blockCommentContinue)) != -1 &&
- found + mode.blockCommentContinue.length > token.start &&
- /^\s*$/.test(full.slice(0, found))) {
- space = full.slice(0, found);
- }
- }
-
- if (space != null)
- cm.replaceSelection("\n" + space + mode.blockCommentContinue, "end");
- else
- return CodeMirror.Pass;
- }
-
- CodeMirror.defineOption("continueComments", null, function(cm, val, prev) {
- if (prev && prev != CodeMirror.Init)
- cm.removeKeyMap("continueComment");
- var map = {name: "continueComment"};
- map[typeof val == "string" ? val : "Enter"] = continueComment;
- cm.addKeyMap(map);
- });
-})();
diff --git a/applications/admin/static/codemirror/addon/edit/continuelist.js b/applications/admin/static/codemirror/addon/edit/continuelist.js
index 2946aa6a..ca8d2675 100644
--- a/applications/admin/static/codemirror/addon/edit/continuelist.js
+++ b/applications/admin/static/codemirror/addon/edit/continuelist.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
@@ -8,26 +11,39 @@
})(function(CodeMirror) {
"use strict";
- var listRE = /^(\s*)([*+-]|(\d+)\.)(\s*)/,
- unorderedBullets = "*+-";
+ var listRE = /^(\s*)(>[> ]*|[*+-]\s|(\d+)\.)(\s*)/,
+ emptyListRE = /^(\s*)(>[> ]*|[*+-]|(\d+)\.)(\s*)$/,
+ unorderedListRE = /[*+-]\s/;
CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) {
if (cm.getOption("disableInput")) return CodeMirror.Pass;
var ranges = cm.listSelections(), replacements = [];
for (var i = 0; i < ranges.length; i++) {
var pos = ranges[i].head, match;
- var inList = cm.getStateAfter(pos.line).list !== false;
+ var eolState = cm.getStateAfter(pos.line);
+ var inList = eolState.list !== false;
+ var inQuote = eolState.quote !== false;
- if (!ranges[i].empty() || !inList || !(match = cm.getLine(pos.line).match(listRE))) {
+ if (!ranges[i].empty() || (!inList && !inQuote) || !(match = cm.getLine(pos.line).match(listRE))) {
cm.execCommand("newlineAndIndent");
return;
}
- var indent = match[1], after = match[4];
- var bullet = unorderedBullets.indexOf(match[2]) >= 0
- ? match[2]
- : (parseInt(match[3], 10) + 1) + ".";
+ if (cm.getLine(pos.line).match(emptyListRE)) {
+ cm.replaceRange("", {
+ line: pos.line, ch: 0
+ }, {
+ line: pos.line, ch: pos.ch + 1
+ });
+ replacements[i] = "\n";
- replacements[i] = "\n" + indent + bullet + after;
+ } else {
+ var indent = match[1], after = match[4];
+ var bullet = unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0
+ ? match[2]
+ : (parseInt(match[3], 10) + 1) + ".";
+
+ replacements[i] = "\n" + indent + bullet + after;
+ }
}
cm.replaceSelections(replacements);
diff --git a/applications/admin/static/codemirror/addon/edit/matchbrackets.js b/applications/admin/static/codemirror/addon/edit/matchbrackets.js
index 576ec143..fa1ae030 100644
--- a/applications/admin/static/codemirror/addon/edit/matchbrackets.js
+++ b/applications/admin/static/codemirror/addon/edit/matchbrackets.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
@@ -22,15 +25,24 @@
var style = cm.getTokenTypeAt(Pos(where.line, pos + 1));
var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style || null, config);
+ if (found == null) return null;
return {from: Pos(where.line, pos), to: found && found.pos,
match: found && found.ch == match.charAt(0), forward: dir > 0};
}
+ // bracketRegex is used to specify which type of bracket to scan
+ // should be a regexp, e.g. /[[\]]/
+ //
+ // Note: If "where" is on an open bracket, then this bracket is ignored.
+ //
+ // Returns false when no bracket was found, null when it reached
+ // maxScanLines and gave up
function scanForBracket(cm, where, dir, style, config) {
var maxScanLen = (config && config.maxScanLineLength) || 10000;
- var maxScanLines = (config && config.maxScanLines) || 500;
+ var maxScanLines = (config && config.maxScanLines) || 1000;
- var stack = [], re = /[(){}[\]]/;
+ var stack = [];
+ var re = config && config.bracketRegex ? config.bracketRegex : /[(){}[\]]/;
var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1)
: Math.max(cm.firstLine() - 1, where.line - maxScanLines);
for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) {
@@ -49,6 +61,7 @@
}
}
}
+ return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null;
}
function matchBrackets(cm, autoclear, config) {
@@ -57,11 +70,10 @@
var marks = [], ranges = cm.listSelections();
for (var i = 0; i < ranges.length; i++) {
var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, false, config);
- if (match && cm.getLine(match.from.line).length <= maxHighlightLen &&
- match.to && cm.getLine(match.to.line).length <= maxHighlightLen) {
+ if (match && cm.getLine(match.from.line).length <= maxHighlightLen) {
var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style}));
- if (match.to)
+ if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen)
marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style}));
}
}
@@ -99,10 +111,10 @@
});
CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
- CodeMirror.defineExtension("findMatchingBracket", function(pos, strict){
- return findMatchingBracket(this, pos, strict);
+ CodeMirror.defineExtension("findMatchingBracket", function(pos, strict, config){
+ return findMatchingBracket(this, pos, strict, config);
});
- CodeMirror.defineExtension("scanForBracket", function(pos, dir, style){
- return scanForBracket(this, pos, dir, style);
+ CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){
+ return scanForBracket(this, pos, dir, style, config);
});
});
diff --git a/applications/admin/static/codemirror/addon/edit/matchtags.js b/applications/admin/static/codemirror/addon/edit/matchtags.js
new file mode 100644
index 00000000..fb1911a8
--- /dev/null
+++ b/applications/admin/static/codemirror/addon/edit/matchtags.js
@@ -0,0 +1,66 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror", "../fold/xml-fold"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ CodeMirror.defineOption("matchTags", false, function(cm, val, old) {
+ if (old && old != CodeMirror.Init) {
+ cm.off("cursorActivity", doMatchTags);
+ cm.off("viewportChange", maybeUpdateMatch);
+ clear(cm);
+ }
+ if (val) {
+ cm.state.matchBothTags = typeof val == "object" && val.bothTags;
+ cm.on("cursorActivity", doMatchTags);
+ cm.on("viewportChange", maybeUpdateMatch);
+ doMatchTags(cm);
+ }
+ });
+
+ function clear(cm) {
+ if (cm.state.tagHit) cm.state.tagHit.clear();
+ if (cm.state.tagOther) cm.state.tagOther.clear();
+ cm.state.tagHit = cm.state.tagOther = null;
+ }
+
+ function doMatchTags(cm) {
+ cm.state.failedTagMatch = false;
+ cm.operation(function() {
+ clear(cm);
+ if (cm.somethingSelected()) return;
+ var cur = cm.getCursor(), range = cm.getViewport();
+ range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to);
+ var match = CodeMirror.findMatchingTag(cm, cur, range);
+ if (!match) return;
+ if (cm.state.matchBothTags) {
+ var hit = match.at == "open" ? match.open : match.close;
+ if (hit) cm.state.tagHit = cm.markText(hit.from, hit.to, {className: "CodeMirror-matchingtag"});
+ }
+ var other = match.at == "close" ? match.open : match.close;
+ if (other)
+ cm.state.tagOther = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"});
+ else
+ cm.state.failedTagMatch = true;
+ });
+ }
+
+ function maybeUpdateMatch(cm) {
+ if (cm.state.failedTagMatch) doMatchTags(cm);
+ }
+
+ CodeMirror.commands.toMatchingTag = function(cm) {
+ var found = CodeMirror.findMatchingTag(cm, cm.getCursor());
+ if (found) {
+ var other = found.at == "close" ? found.open : found.close;
+ if (other) cm.extendSelection(other.to, other.from);
+ }
+ };
+});
diff --git a/applications/admin/static/codemirror/addon/edit/trailingspace.js b/applications/admin/static/codemirror/addon/edit/trailingspace.js
index ec07221e..fa7b56be 100644
--- a/applications/admin/static/codemirror/addon/edit/trailingspace.js
+++ b/applications/admin/static/codemirror/addon/edit/trailingspace.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
diff --git a/applications/admin/static/codemirror/addon/fold/brace-fold.js b/applications/admin/static/codemirror/addon/fold/brace-fold.js
index f0ee6202..1605f6c2 100644
--- a/applications/admin/static/codemirror/addon/fold/brace-fold.js
+++ b/applications/admin/static/codemirror/addon/fold/brace-fold.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
diff --git a/applications/admin/static/codemirror/addon/fold/comment-fold.js b/applications/admin/static/codemirror/addon/fold/comment-fold.js
index d72c5479..b75db7ea 100644
--- a/applications/admin/static/codemirror/addon/fold/comment-fold.js
+++ b/applications/admin/static/codemirror/addon/fold/comment-fold.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
diff --git a/applications/admin/static/codemirror/addon/fold/foldcode.js b/applications/admin/static/codemirror/addon/fold/foldcode.js
index d7a0bb5e..62911f93 100644
--- a/applications/admin/static/codemirror/addon/fold/foldcode.js
+++ b/applications/admin/static/codemirror/addon/fold/foldcode.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
@@ -9,10 +12,14 @@
"use strict";
function doFold(cm, pos, options, force) {
- var finder = options && (options.call ? options : options.rangeFinder);
- if (!finder) finder = CodeMirror.fold.auto;
+ if (options && options.call) {
+ var finder = options;
+ options = null;
+ } else {
+ var finder = getOption(cm, options, "rangeFinder");
+ }
if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0);
- var minSize = options && options.minFoldSize || 0;
+ var minSize = getOption(cm, options, "minFoldSize");
function getRange(allowFolded) {
var range = finder(cm, pos);
@@ -29,14 +36,17 @@
}
var range = getRange(true);
- if (options && options.scanUp) while (!range && pos.line > cm.firstLine()) {
+ if (getOption(cm, options, "scanUp")) while (!range && pos.line > cm.firstLine()) {
pos = CodeMirror.Pos(pos.line - 1, 0);
range = getRange(false);
}
if (!range || range.cleared || force === "unfold") return;
- var myWidget = makeWidget(options);
- CodeMirror.on(myWidget, "mousedown", function() { myRange.clear(); });
+ var myWidget = makeWidget(cm, options);
+ CodeMirror.on(myWidget, "mousedown", function(e) {
+ myRange.clear();
+ CodeMirror.e_preventDefault(e);
+ });
var myRange = cm.markText(range.from, range.to, {
replacedWith: myWidget,
clearOnEnter: true,
@@ -48,8 +58,8 @@
CodeMirror.signal(cm, "fold", cm, range.from, range.to);
}
- function makeWidget(options) {
- var widget = (options && options.widget) || "\u2194";
+ function makeWidget(cm, options) {
+ var widget = getOption(cm, options, "widget");
if (typeof widget == "string") {
var text = document.createTextNode(widget);
widget = document.createElement("span");
@@ -114,4 +124,26 @@
if (cur) return cur;
}
});
+
+ var defaultOptions = {
+ rangeFinder: CodeMirror.fold.auto,
+ widget: "\u2194",
+ minFoldSize: 0,
+ scanUp: false
+ };
+
+ CodeMirror.defineOption("foldOptions", null);
+
+ function getOption(cm, options, name) {
+ if (options && options[name] !== undefined)
+ return options[name];
+ var editorOptions = cm.options.foldOptions;
+ if (editorOptions && editorOptions[name] !== undefined)
+ return editorOptions[name];
+ return defaultOptions[name];
+ }
+
+ CodeMirror.defineExtension("foldOption", function(options, name) {
+ return getOption(this, options, name);
+ });
});
diff --git a/applications/admin/static/codemirror/addon/fold/foldgutter.css b/applications/admin/static/codemirror/addon/fold/foldgutter.css
index 49805393..ad19ae2d 100644
--- a/applications/admin/static/codemirror/addon/fold/foldgutter.css
+++ b/applications/admin/static/codemirror/addon/fold/foldgutter.css
@@ -10,7 +10,6 @@
}
.CodeMirror-foldgutter-open,
.CodeMirror-foldgutter-folded {
- color: #555;
cursor: pointer;
}
.CodeMirror-foldgutter-open:after {
diff --git a/applications/admin/static/codemirror/addon/fold/foldgutter.js b/applications/admin/static/codemirror/addon/fold/foldgutter.js
index 9caba59a..33594767 100644
--- a/applications/admin/static/codemirror/addon/fold/foldgutter.js
+++ b/applications/admin/static/codemirror/addon/fold/foldgutter.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), require("./foldcode"));
@@ -55,7 +58,7 @@
function marker(spec) {
if (typeof spec == "string") {
var elt = document.createElement("div");
- elt.className = spec;
+ elt.className = spec + " CodeMirror-guttermarker-subtle";
return elt;
} else {
return spec.cloneNode(true);
@@ -64,14 +67,16 @@
function updateFoldInfo(cm, from, to) {
var opts = cm.state.foldGutter.options, cur = from;
+ var minSize = cm.foldOption(opts, "minFoldSize");
+ var func = cm.foldOption(opts, "rangeFinder");
cm.eachLine(from, to, function(line) {
var mark = null;
if (isFolded(cm, cur)) {
mark = marker(opts.indicatorFolded);
} else {
- var pos = Pos(cur, 0), func = opts.rangeFinder || CodeMirror.fold.auto;
+ var pos = Pos(cur, 0);
var range = func && func(cm, pos);
- if (range && range.from.line + 1 < range.to.line)
+ if (range && range.to.line - range.from.line >= minSize)
mark = marker(opts.indicatorOpen);
}
cm.setGutterMarker(line, opts.gutter, mark);
diff --git a/applications/admin/static/codemirror/addon/fold/indent-fold.js b/applications/admin/static/codemirror/addon/fold/indent-fold.js
index d0130836..e29f15e9 100644
--- a/applications/admin/static/codemirror/addon/fold/indent-fold.js
+++ b/applications/admin/static/codemirror/addon/fold/indent-fold.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
diff --git a/applications/admin/static/codemirror/addon/fold/markdown-fold.js b/applications/admin/static/codemirror/addon/fold/markdown-fold.js
new file mode 100644
index 00000000..ce84c946
--- /dev/null
+++ b/applications/admin/static/codemirror/addon/fold/markdown-fold.js
@@ -0,0 +1,49 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.registerHelper("fold", "markdown", function(cm, start) {
+ var maxDepth = 100;
+
+ function isHeader(lineNo) {
+ var tokentype = cm.getTokenTypeAt(CodeMirror.Pos(lineNo, 0));
+ return tokentype && /\bheader\b/.test(tokentype);
+ }
+
+ function headerLevel(lineNo, line, nextLine) {
+ var match = line && line.match(/^#+/);
+ if (match && isHeader(lineNo)) return match[0].length;
+ match = nextLine && nextLine.match(/^[=\-]+\s*$/);
+ if (match && isHeader(lineNo + 1)) return nextLine[0] == "=" ? 1 : 2;
+ return maxDepth;
+ }
+
+ var firstLine = cm.getLine(start.line), nextLine = cm.getLine(start.line + 1);
+ var level = headerLevel(start.line, firstLine, nextLine);
+ if (level === maxDepth) return undefined;
+
+ var lastLineNo = cm.lastLine();
+ var end = start.line, nextNextLine = cm.getLine(end + 2);
+ while (end < lastLineNo) {
+ if (headerLevel(end + 1, nextLine, nextNextLine) <= level) break;
+ ++end;
+ nextLine = nextNextLine;
+ nextNextLine = cm.getLine(end + 2);
+ }
+
+ return {
+ from: CodeMirror.Pos(start.line, firstLine.length),
+ to: CodeMirror.Pos(end, cm.getLine(end).length)
+ };
+});
+
+});
diff --git a/applications/admin/static/codemirror/addon/fold/xml-fold.js b/applications/admin/static/codemirror/addon/fold/xml-fold.js
index d554e2fc..504727f3 100644
--- a/applications/admin/static/codemirror/addon/fold/xml-fold.js
+++ b/applications/admin/static/codemirror/addon/fold/xml-fold.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
@@ -148,8 +151,9 @@
if (iter.text.indexOf(">") == -1 && iter.text.indexOf("<") == -1) return;
var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch);
var start = end && toTagStart(iter);
- if (!end || end == "selfClose" || !start || cmp(iter, pos) > 0) return;
+ if (!end || !start || cmp(iter, pos) > 0) return;
var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]};
+ if (end == "selfClose") return {open: here, close: null, at: "open"};
if (start[1]) { // closing tag
return {open: findMatchingOpen(iter, start[2]), close: here, at: "close"};
@@ -173,6 +177,6 @@
// Used by addon/edit/closetag.js
CodeMirror.scanForClosingTag = function(cm, pos, name, end) {
var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null);
- return !!findMatchingClose(iter, name);
+ return findMatchingClose(iter, name);
};
});
diff --git a/applications/admin/static/codemirror/addon/hint/anyword-hint.js b/applications/admin/static/codemirror/addon/hint/anyword-hint.js
new file mode 100644
index 00000000..8e74a920
--- /dev/null
+++ b/applications/admin/static/codemirror/addon/hint/anyword-hint.js
@@ -0,0 +1,41 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ var WORD = /[\w$]+/, RANGE = 500;
+
+ CodeMirror.registerHelper("hint", "anyword", function(editor, options) {
+ var word = options && options.word || WORD;
+ var range = options && options.range || RANGE;
+ var cur = editor.getCursor(), curLine = editor.getLine(cur.line);
+ var end = cur.ch, start = end;
+ while (start && word.test(curLine.charAt(start - 1))) --start;
+ var curWord = start != end && curLine.slice(start, end);
+
+ var list = [], seen = {};
+ var re = new RegExp(word.source, "g");
+ for (var dir = -1; dir <= 1; dir += 2) {
+ var line = cur.line, endLine = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir;
+ for (; line != endLine; line += dir) {
+ var text = editor.getLine(line), m;
+ while (m = re.exec(text)) {
+ if (line == cur.line && m[0] === curWord) continue;
+ if ((!curWord || m[0].lastIndexOf(curWord, 0) == 0) && !Object.prototype.hasOwnProperty.call(seen, m[0])) {
+ seen[m[0]] = true;
+ list.push(m[0]);
+ }
+ }
+ }
+ }
+ return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)};
+ });
+});
diff --git a/applications/admin/static/codemirror/addon/hint/css-hint.js b/applications/admin/static/codemirror/addon/hint/css-hint.js
new file mode 100644
index 00000000..488da344
--- /dev/null
+++ b/applications/admin/static/codemirror/addon/hint/css-hint.js
@@ -0,0 +1,56 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"), require("../../mode/css/css"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror", "../../mode/css/css"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ var pseudoClasses = {link: 1, visited: 1, active: 1, hover: 1, focus: 1,
+ "first-letter": 1, "first-line": 1, "first-child": 1,
+ before: 1, after: 1, lang: 1};
+
+ CodeMirror.registerHelper("hint", "css", function(cm) {
+ var cur = cm.getCursor(), token = cm.getTokenAt(cur);
+ var inner = CodeMirror.innerMode(cm.getMode(), token.state);
+ if (inner.mode.name != "css") return;
+
+ var start = token.start, end = cur.ch, word = token.string.slice(0, end - start);
+ if (/[^\w$_-]/.test(word)) {
+ word = ""; start = end = cur.ch;
+ }
+
+ var spec = CodeMirror.resolveMode("text/css");
+
+ var result = [];
+ function add(keywords) {
+ for (var name in keywords)
+ if (!word || name.lastIndexOf(word, 0) == 0)
+ result.push(name);
+ }
+
+ var st = inner.state.state;
+ if (st == "pseudo" || token.type == "variable-3") {
+ add(pseudoClasses);
+ } else if (st == "block" || st == "maybeprop") {
+ add(spec.propertyKeywords);
+ } else if (st == "prop" || st == "parens" || st == "at" || st == "params") {
+ add(spec.valueKeywords);
+ add(spec.colorKeywords);
+ } else if (st == "media" || st == "media_parens") {
+ add(spec.mediaTypes);
+ add(spec.mediaFeatures);
+ }
+
+ if (result.length) return {
+ list: result,
+ from: CodeMirror.Pos(cur.line, start),
+ to: CodeMirror.Pos(cur.line, end)
+ };
+ });
+});
diff --git a/applications/admin/static/codemirror/addon/hint/html-hint.js b/applications/admin/static/codemirror/addon/hint/html-hint.js
old mode 100755
new mode 100644
index cbe7c61a..c6769bca
--- a/applications/admin/static/codemirror/addon/hint/html-hint.js
+++ b/applications/admin/static/codemirror/addon/hint/html-hint.js
@@ -1,8 +1,11 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
- mod(require("../../lib/codemirror"));
+ mod(require("../../lib/codemirror"), require("./xml-hint"));
else if (typeof define == "function" && define.amd) // AMD
- define(["../../lib/codemirror"], mod);
+ define(["../../lib/codemirror", "./xml-hint"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
diff --git a/applications/admin/static/codemirror/addon/hint/javascript-hint.js b/applications/admin/static/codemirror/addon/hint/javascript-hint.js
index 305bb85a..7bcbf4a0 100644
--- a/applications/admin/static/codemirror/addon/hint/javascript-hint.js
+++ b/applications/admin/static/codemirror/addon/hint/javascript-hint.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
@@ -27,15 +30,20 @@
function scriptHint(editor, keywords, getToken, options) {
// Find the token at the cursor
- var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
+ var cur = editor.getCursor(), token = getToken(editor, cur);
if (/\b(?:string|comment)\b/.test(token.type)) return;
token.state = CodeMirror.innerMode(editor.getMode(), token.state).state;
// If it's not a 'word-style' token, ignore the token.
if (!/^[\w$_]*$/.test(token.string)) {
- token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
- type: token.string == "." ? "property" : null};
+ token = {start: cur.ch, end: cur.ch, string: "", state: token.state,
+ type: token.string == "." ? "property" : null};
+ } else if (token.end > cur.ch) {
+ token.end = cur.ch;
+ token.string = token.string.slice(0, cur.ch - token.start);
}
+
+ var tprop = token;
// If it is a property, find out what it is a property of.
while (tprop.type == "property") {
tprop = getToken(editor, Pos(cur.line, tprop.start));
@@ -90,7 +98,7 @@
"if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" ");
function getCompletions(token, context, keywords, options) {
- var found = [], start = token.string;
+ var found = [], start = token.string, global = options && options.globalScope || window;
function maybeAdd(str) {
if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str);
}
@@ -108,27 +116,29 @@
if (obj.type && obj.type.indexOf("variable") === 0) {
if (options && options.additionalContext)
base = options.additionalContext[obj.string];
- base = base || window[obj.string];
+ if (!options || options.useGlobalScope !== false)
+ base = base || global[obj.string];
} else if (obj.type == "string") {
base = "";
} else if (obj.type == "atom") {
base = 1;
} else if (obj.type == "function") {
- if (window.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&
- (typeof window.jQuery == 'function'))
- base = window.jQuery();
- else if (window._ != null && (obj.string == '_') && (typeof window._ == 'function'))
- base = window._();
+ if (global.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&
+ (typeof global.jQuery == 'function'))
+ base = global.jQuery();
+ else if (global._ != null && (obj.string == '_') && (typeof global._ == 'function'))
+ base = global._();
}
while (base != null && context.length)
base = base[context.pop().string];
if (base != null) gatherCompletions(base);
} else {
- // If not, just look in the window object and any local scope
+ // If not, just look in the global object and any local scope
// (reading into JS mode internals to get at the local and global variables)
for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name);
- gatherCompletions(window);
+ if (!options || options.useGlobalScope !== false)
+ gatherCompletions(global);
forEach(keywords, maybeAdd);
}
return found;
diff --git a/applications/admin/static/codemirror/addon/hint/python-hint.js b/applications/admin/static/codemirror/addon/hint/python-hint.js
deleted file mode 100644
index eebfcc76..00000000
--- a/applications/admin/static/codemirror/addon/hint/python-hint.js
+++ /dev/null
@@ -1,99 +0,0 @@
-(function(mod) {
- if (typeof exports == "object" && typeof module == "object") // CommonJS
- mod(require("../../lib/codemirror"));
- else if (typeof define == "function" && define.amd) // AMD
- define(["../../lib/codemirror"], mod);
- else // Plain browser env
- mod(CodeMirror);
-})(function(CodeMirror) {
- "use strict";
-
- function forEach(arr, f) {
- for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
- }
-
- function arrayContains(arr, item) {
- if (!Array.prototype.indexOf) {
- var i = arr.length;
- while (i--) {
- if (arr[i] === item) {
- return true;
- }
- }
- return false;
- }
- return arr.indexOf(item) != -1;
- }
-
- function scriptHint(editor, _keywords, getToken) {
- // Find the token at the cursor
- var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
- // If it's not a 'word-style' token, ignore the token.
-
- if (!/^[\w$_]*$/.test(token.string)) {
- token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
- className: token.string == ":" ? "python-type" : null};
- }
-
- if (!context) var context = [];
- context.push(tprop);
-
- var completionList = getCompletions(token, context);
- completionList = completionList.sort();
-
- return {list: completionList,
- from: CodeMirror.Pos(cur.line, token.start),
- to: CodeMirror.Pos(cur.line, token.end)};
- }
-
- function pythonHint(editor) {
- return scriptHint(editor, pythonKeywordsU, function (e, cur) {return e.getTokenAt(cur);});
- }
- CodeMirror.registerHelper("hint", "python", pythonHint);
-
- var pythonKeywords = "and del from not while as elif global or with assert else if pass yield"
-+ "break except import print class exec in raise continue finally is return def for lambda try";
- var pythonKeywordsL = pythonKeywords.split(" ");
- var pythonKeywordsU = pythonKeywords.toUpperCase().split(" ");
-
- var pythonBuiltins = "abs divmod input open staticmethod all enumerate int ord str "
-+ "any eval isinstance pow sum basestring execfile issubclass print super"
-+ "bin file iter property tuple bool filter len range type"
-+ "bytearray float list raw_input unichr callable format locals reduce unicode"
-+ "chr frozenset long reload vars classmethod getattr map repr xrange"
-+ "cmp globals max reversed zip compile hasattr memoryview round __import__"
-+ "complex hash min set apply delattr help next setattr buffer"
-+ "dict hex object slice coerce dir id oct sorted intern ";
- var pythonBuiltinsL = pythonBuiltins.split(" ").join("() ").split(" ");
- var pythonBuiltinsU = pythonBuiltins.toUpperCase().split(" ").join("() ").split(" ");
-
- function getCompletions(token, context) {
- var found = [], start = token.string;
- function maybeAdd(str) {
- if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str);
- }
-
- function gatherCompletions(_obj) {
- forEach(pythonBuiltinsL, maybeAdd);
- forEach(pythonBuiltinsU, maybeAdd);
- forEach(pythonKeywordsL, maybeAdd);
- forEach(pythonKeywordsU, maybeAdd);
- }
-
- if (context) {
- // If this is a property, see if it belongs to some object we can
- // find in the current environment.
- var obj = context.pop(), base;
-
- if (obj.type == "variable")
- base = obj.string;
- else if(obj.type == "variable-3")
- base = ":" + obj.string;
-
- while (base != null && context.length)
- base = base[context.pop().string];
- if (base != null) gatherCompletions(base);
- }
- return found;
- }
-});
diff --git a/applications/admin/static/codemirror/addon/hint/show-hint.css b/applications/admin/static/codemirror/addon/hint/show-hint.css
index 8a4ff052..924e638f 100644
--- a/applications/admin/static/codemirror/addon/hint/show-hint.css
+++ b/applications/admin/static/codemirror/addon/hint/show-hint.css
@@ -32,7 +32,7 @@
cursor: pointer;
}
-.CodeMirror-hint-active {
+li.CodeMirror-hint-active {
background: #08f;
color: white;
}
diff --git a/applications/admin/static/codemirror/addon/hint/show-hint.js b/applications/admin/static/codemirror/addon/hint/show-hint.js
index 6f04c565..fda5ffaa 100644
--- a/applications/admin/static/codemirror/addon/hint/show-hint.js
+++ b/applications/admin/static/codemirror/addon/hint/show-hint.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
@@ -11,28 +14,35 @@
var HINT_ELEMENT_CLASS = "CodeMirror-hint";
var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active";
+ // This is the old interface, kept around for now to stay
+ // backwards-compatible.
CodeMirror.showHint = function(cm, getHints, options) {
- // We want a single cursor position.
- if (cm.listSelections().length > 1 || cm.somethingSelected()) return;
- if (getHints == null) {
- if (options && options.async) return;
- else getHints = CodeMirror.hint.auto;
- }
-
- if (cm.state.completionActive) cm.state.completionActive.close();
-
- var completion = cm.state.completionActive = new Completion(cm, getHints, options || {});
- CodeMirror.signal(cm, "startCompletion", cm);
- if (completion.options.async)
- getHints(cm, function(hints) { completion.showHints(hints); }, completion.options);
- else
- return completion.showHints(getHints(cm, completion.options));
+ if (!getHints) return cm.showHint(options);
+ if (options && options.async) getHints.async = true;
+ var newOpts = {hint: getHints};
+ if (options) for (var prop in options) newOpts[prop] = options[prop];
+ return cm.showHint(newOpts);
};
- function Completion(cm, getHints, options) {
+ CodeMirror.defineExtension("showHint", function(options) {
+ // We want a single cursor position.
+ if (this.listSelections().length > 1 || this.somethingSelected()) return;
+
+ if (this.state.completionActive) this.state.completionActive.close();
+ var completion = this.state.completionActive = new Completion(this, options);
+ var getHints = completion.options.hint;
+ if (!getHints) return;
+
+ CodeMirror.signal(this, "startCompletion", this);
+ if (getHints.async)
+ getHints(this, function(hints) { completion.showHints(hints); }, completion.options);
+ else
+ return completion.showHints(getHints(this, completion.options));
+ });
+
+ function Completion(cm, options) {
this.cm = cm;
- this.getHints = getHints;
- this.options = options;
+ this.options = this.buildOptions(options);
this.widget = this.onClose = null;
}
@@ -53,7 +63,8 @@
pick: function(data, i) {
var completion = data.list[i];
if (completion.hint) completion.hint(this.cm, data, completion);
- else this.cm.replaceRange(getText(completion), completion.from||data.from, completion.to||data.to);
+ else this.cm.replaceRange(getText(completion), completion.from || data.from,
+ completion.to || data.to, "complete");
CodeMirror.signal(data, "pick", completion);
this.close();
},
@@ -61,7 +72,7 @@
showHints: function(data) {
if (!data || !data.list.length || !this.active()) return this.close();
- if (this.options.completeSingle != false && data.list.length == 1)
+ if (this.options.completeSingle && data.list.length == 1)
this.pick(data, 0);
else
this.showWidget(data);
@@ -72,7 +83,7 @@
CodeMirror.signal(data, "shown");
var debounce = 0, completion = this, finished;
- var closeOn = this.options.closeCharacters || /[\s()\[\]{};:>,]/;
+ var closeOn = this.options.closeCharacters;
var startPos = this.cm.getCursor(), startLen = this.cm.getLine(startPos.line).length;
var requestAnimationFrame = window.requestAnimationFrame || function(fn) {
@@ -91,15 +102,17 @@
function update() {
if (finished) return;
CodeMirror.signal(data, "update");
- if (completion.options.async)
- completion.getHints(completion.cm, finishUpdate, completion.options);
+ var getHints = completion.options.hint;
+ if (getHints.async)
+ getHints(completion.cm, finishUpdate, completion.options);
else
- finishUpdate(completion.getHints(completion.cm, completion.options));
+ finishUpdate(getHints(completion.cm, completion.options));
}
function finishUpdate(data_) {
data = data_;
if (finished) return;
if (!data || !data.list.length) return done();
+ if (completion.widget) completion.widget.close();
completion.widget = new Widget(completion, data);
}
@@ -124,6 +137,17 @@
}
this.cm.on("cursorActivity", activity);
this.onClose = done;
+ },
+
+ buildOptions: function(options) {
+ var editor = this.cm.options.hintOptions;
+ var out = {};
+ for (var prop in defaultOptions) out[prop] = defaultOptions[prop];
+ if (editor) for (var prop in editor)
+ if (editor[prop] !== undefined) out[prop] = editor[prop];
+ if (options) for (var prop in options)
+ if (options[prop] !== undefined) out[prop] = options[prop];
+ return out;
}
};
@@ -132,7 +156,7 @@
else return completion.text;
}
- function buildKeyMap(options, handle) {
+ function buildKeyMap(completion, handle) {
var baseMap = {
Up: function() {handle.moveFocus(-1);},
Down: function() {handle.moveFocus(1);},
@@ -144,7 +168,8 @@
Tab: handle.pick,
Esc: handle.close
};
- var ourMap = options.customKeys ? {} : baseMap;
+ var custom = completion.options.customKeys;
+ var ourMap = custom ? {} : baseMap;
function addBinding(key, val) {
var bound;
if (typeof val != "string")
@@ -156,12 +181,13 @@
bound = val;
ourMap[key] = bound;
}
- if (options.customKeys)
- for (var key in options.customKeys) if (options.customKeys.hasOwnProperty(key))
- addBinding(key, options.customKeys[key]);
- if (options.extraKeys)
- for (var key in options.extraKeys) if (options.extraKeys.hasOwnProperty(key))
- addBinding(key, options.extraKeys[key]);
+ if (custom)
+ for (var key in custom) if (custom.hasOwnProperty(key))
+ addBinding(key, custom[key]);
+ var extra = completion.options.extraKeys;
+ if (extra)
+ for (var key in extra) if (extra.hasOwnProperty(key))
+ addBinding(key, extra[key]);
return ourMap;
}
@@ -175,11 +201,11 @@
function Widget(completion, data) {
this.completion = completion;
this.data = data;
- var widget = this, cm = completion.cm, options = completion.options;
+ var widget = this, cm = completion.cm;
var hints = this.hints = document.createElement("ul");
hints.className = "CodeMirror-hints";
- this.selectedHint = options.getDefaultSelection ? options.getDefaultSelection(cm,options,data) : 0;
+ this.selectedHint = data.selectedHint || 0;
var completions = data.list;
for (var i = 0; i < completions.length; ++i) {
@@ -192,19 +218,19 @@
elt.hintId = i;
}
- var pos = cm.cursorCoords(options.alignWithWord !== false ? data.from : null);
+ var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null);
var left = pos.left, top = pos.bottom, below = true;
hints.style.left = left + "px";
hints.style.top = top + "px";
// If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
- (options.container || document.body).appendChild(hints);
+ (completion.options.container || document.body).appendChild(hints);
var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;
if (overlapY > 0) {
- var height = box.bottom - box.top, curTop = box.top - (pos.bottom - pos.top);
+ var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);
if (curTop - height > 0) { // Fits above cursor
- hints.style.top = (top = curTop - height) + "px";
+ hints.style.top = (top = pos.top - height) + "px";
below = false;
} else if (height > winH) {
hints.style.height = (winH - 5) + "px";
@@ -217,7 +243,7 @@
}
}
}
- var overlapX = box.left - winW;
+ var overlapX = box.right - winW;
if (overlapX > 0) {
if (box.right - box.left > winW) {
hints.style.width = (winW - 5) + "px";
@@ -226,7 +252,7 @@
hints.style.left = (left = pos.left - overlapX) + "px";
}
- cm.addKeyMap(this.keyMap = buildKeyMap(options, {
+ cm.addKeyMap(this.keyMap = buildKeyMap(completion, {
moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },
setFocus: function(n) { widget.changeActive(n); },
menuSize: function() { return widget.screenAmount(); },
@@ -236,7 +262,7 @@
data: data
}));
- if (options.closeOnUnfocus !== false) {
+ if (completion.options.closeOnUnfocus) {
var closingOnBlur;
cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); });
cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); });
@@ -262,7 +288,7 @@
var t = getHintElement(hints, e.target || e.srcElement);
if (t && t.hintId != null) {
widget.changeActive(t.hintId);
- if (options.completeOnSingleClick) widget.pick();
+ if (completion.options.completeOnSingleClick) widget.pick();
}
});
@@ -282,7 +308,7 @@
this.completion.cm.removeKeyMap(this.keyMap);
var cm = this.completion.cm;
- if (this.completion.options.closeOnUnfocus !== false) {
+ if (this.completion.options.closeOnUnfocus) {
cm.off("blur", this.onBlur);
cm.off("focus", this.onFocus);
}
@@ -346,4 +372,18 @@
});
CodeMirror.commands.autocomplete = CodeMirror.showHint;
+
+ var defaultOptions = {
+ hint: CodeMirror.hint.auto,
+ completeSingle: true,
+ alignWithWord: true,
+ closeCharacters: /[\s()\[\]{};:>,]/,
+ closeOnUnfocus: true,
+ completeOnSingleClick: false,
+ container: null,
+ customKeys: null,
+ extraKeys: null
+ };
+
+ CodeMirror.defineOption("hintOptions", null);
});
diff --git a/applications/admin/static/codemirror/addon/hint/sql-hint.js b/applications/admin/static/codemirror/addon/hint/sql-hint.js
new file mode 100644
index 00000000..92c889e1
--- /dev/null
+++ b/applications/admin/static/codemirror/addon/hint/sql-hint.js
@@ -0,0 +1,197 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"), require("../../mode/sql/sql"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror", "../../mode/sql/sql"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ var tables;
+ var defaultTable;
+ var keywords;
+ var CONS = {
+ QUERY_DIV: ";",
+ ALIAS_KEYWORD: "AS"
+ };
+ var Pos = CodeMirror.Pos;
+
+ function getKeywords(editor) {
+ var mode = editor.doc.modeOption;
+ if (mode === "sql") mode = "text/x-sql";
+ return CodeMirror.resolveMode(mode).keywords;
+ }
+
+ function match(string, word) {
+ var len = string.length;
+ var sub = word.substr(0, len);
+ return string.toUpperCase() === sub.toUpperCase();
+ }
+
+ function addMatches(result, search, wordlist, formatter) {
+ for (var word in wordlist) {
+ if (!wordlist.hasOwnProperty(word)) continue;
+ if (Array.isArray(wordlist)) {
+ word = wordlist[word];
+ }
+ if (match(search, word)) {
+ result.push(formatter(word));
+ }
+ }
+ }
+
+ function nameCompletion(cur, token, result, editor) {
+ var useBacktick = (token.string.charAt(0) == "`");
+ var string = token.string.substr(1);
+ var prevToken = editor.getTokenAt(Pos(cur.line, token.start));
+ if (token.string.charAt(0) == "." || prevToken.string == "."){
+ //Suggest colunm names
+ if (prevToken.string == ".") {
+ var prevToken = editor.getTokenAt(Pos(cur.line, token.start - 1));
+ }
+ var table = prevToken.string;
+ //Check if backtick is used in table name. If yes, use it for columns too.
+ var useBacktickTable = false;
+ if (table.match(/`/g)) {
+ useBacktickTable = true;
+ table = table.replace(/`/g, "");
+ }
+ //Check if table is available. If not, find table by Alias
+ if (!tables.hasOwnProperty(table))
+ table = findTableByAlias(table, editor);
+ var columns = tables[table];
+ if (!columns) return;
+
+ if (useBacktick) {
+ addMatches(result, string, columns, function(w) {return "`" + w + "`";});
+ }
+ else if(useBacktickTable) {
+ addMatches(result, string, columns, function(w) {return ".`" + w + "`";});
+ }
+ else {
+ addMatches(result, string, columns, function(w) {return "." + w;});
+ }
+ }
+ else {
+ //Suggest table names or colums in defaultTable
+ while (token.start && string.charAt(0) == ".") {
+ token = editor.getTokenAt(Pos(cur.line, token.start - 1));
+ string = token.string + string;
+ }
+ if (useBacktick) {
+ addMatches(result, string, tables, function(w) {return "`" + w + "`";});
+ addMatches(result, string, defaultTable, function(w) {return "`" + w + "`";});
+ }
+ else {
+ addMatches(result, string, tables, function(w) {return w;});
+ addMatches(result, string, defaultTable, function(w) {return w;});
+ }
+ }
+ }
+
+ function eachWord(lineText, f) {
+ if (!lineText) return;
+ var excepted = /[,;]/g;
+ var words = lineText.split(" ");
+ for (var i = 0; i < words.length; i++) {
+ f(words[i]?words[i].replace(excepted, '') : '');
+ }
+ }
+
+ function convertCurToNumber(cur) {
+ // max characters of a line is 999,999.
+ return cur.line + cur.ch / Math.pow(10, 6);
+ }
+
+ function convertNumberToCur(num) {
+ return Pos(Math.floor(num), +num.toString().split('.').pop());
+ }
+
+ function findTableByAlias(alias, editor) {
+ var doc = editor.doc;
+ var fullQuery = doc.getValue();
+ var aliasUpperCase = alias.toUpperCase();
+ var previousWord = "";
+ var table = "";
+ var separator = [];
+ var validRange = {
+ start: Pos(0, 0),
+ end: Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).length)
+ };
+
+ //add separator
+ var indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV);
+ while(indexOfSeparator != -1) {
+ separator.push(doc.posFromIndex(indexOfSeparator));
+ indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV, indexOfSeparator+1);
+ }
+ separator.unshift(Pos(0, 0));
+ separator.push(Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).text.length));
+
+ //find valid range
+ var prevItem = 0;
+ var current = convertCurToNumber(editor.getCursor());
+ for (var i=0; i< separator.length; i++) {
+ var _v = convertCurToNumber(separator[i]);
+ if (current > prevItem && current <= _v) {
+ validRange = { start: convertNumberToCur(prevItem), end: convertNumberToCur(_v) };
+ break;
+ }
+ prevItem = _v;
+ }
+
+ var query = doc.getRange(validRange.start, validRange.end, false);
+
+ for (var i = 0; i < query.length; i++) {
+ var lineText = query[i];
+ eachWord(lineText, function(word) {
+ var wordUpperCase = word.toUpperCase();
+ if (wordUpperCase === aliasUpperCase && tables.hasOwnProperty(previousWord)) {
+ table = previousWord;
+ }
+ if (wordUpperCase !== CONS.ALIAS_KEYWORD) {
+ previousWord = word;
+ }
+ });
+ if (table) break;
+ }
+ return table;
+ }
+
+ CodeMirror.registerHelper("hint", "sql", function(editor, options) {
+ tables = (options && options.tables) || {};
+ var defaultTableName = options && options.defaultTable;
+ defaultTable = (defaultTableName && tables[defaultTableName] || []);
+ keywords = keywords || getKeywords(editor);
+
+ var cur = editor.getCursor();
+ var result = [];
+ var token = editor.getTokenAt(cur), start, end, search;
+ if (token.end > cur.ch) {
+ token.end = cur.ch;
+ token.string = token.string.slice(0, cur.ch - token.start);
+ }
+
+ if (token.string.match(/^[.`\w@]\w*$/)) {
+ search = token.string;
+ start = token.start;
+ end = token.end;
+ } else {
+ start = end = cur.ch;
+ search = "";
+ }
+ if (search.charAt(0) == "." || search.charAt(0) == "`") {
+ nameCompletion(cur, token, result, editor);
+ } else {
+ addMatches(result, search, tables, function(w) {return w;});
+ addMatches(result, search, defaultTable, function(w) {return w;});
+ addMatches(result, search, keywords, function(w) {return w.toUpperCase();});
+ }
+
+ return {list: result, from: Pos(cur.line, start), to: Pos(cur.line, end)};
+ });
+});
diff --git a/applications/admin/static/codemirror/addon/hint/xml-hint.js b/applications/admin/static/codemirror/addon/hint/xml-hint.js
index 85756490..9b9baa0c 100644
--- a/applications/admin/static/codemirror/addon/hint/xml-hint.js
+++ b/applications/admin/static/codemirror/addon/hint/xml-hint.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
@@ -15,30 +18,55 @@
var quote = (options && options.quoteChar) || '"';
if (!tags) return;
var cur = cm.getCursor(), token = cm.getTokenAt(cur);
+ if (token.end > cur.ch) {
+ token.end = cur.ch;
+ token.string = token.string.slice(0, cur.ch - token.start);
+ }
var inner = CodeMirror.innerMode(cm.getMode(), token.state);
if (inner.mode.name != "xml") return;
var result = [], replaceToken = false, prefix;
- var isTag = token.string.charAt(0) == "<";
- if (!inner.state.tagName || isTag) { // Tag completion
- if (isTag) {
- prefix = token.string.slice(1);
- replaceToken = true;
- }
+ var tag = /\btag\b/.test(token.type) && !/>$/.test(token.string);
+ var tagName = tag && /^\w/.test(token.string), tagStart;
+
+ if (tagName) {
+ var before = cm.getLine(cur.line).slice(Math.max(0, token.start - 2), token.start);
+ var tagType = /<\/$/.test(before) ? "close" : /<$/.test(before) ? "open" : null;
+ if (tagType) tagStart = token.start - (tagType == "close" ? 2 : 1);
+ } else if (tag && token.string == "<") {
+ tagType = "open";
+ } else if (tag && token.string == "") {
+ tagType = "close";
+ }
+
+ if (!tag && !inner.state.tagName || tagType) {
+ if (tagName)
+ prefix = token.string;
+ replaceToken = tagType;
var cx = inner.state.context, curTag = cx && tags[cx.tagName];
var childList = cx ? curTag && curTag.children : tags["!top"];
- if (childList) {
+ if (childList && tagType != "close") {
for (var i = 0; i < childList.length; ++i) if (!prefix || childList[i].lastIndexOf(prefix, 0) == 0)
result.push("<" + childList[i]);
- } else {
- for (var name in tags) if (tags.hasOwnProperty(name) && name != "!top" && (!prefix || name.lastIndexOf(prefix, 0) == 0))
- result.push("<" + name);
+ } else if (tagType != "close") {
+ for (var name in tags)
+ if (tags.hasOwnProperty(name) && name != "!top" && name != "!attrs" && (!prefix || name.lastIndexOf(prefix, 0) == 0))
+ result.push("<" + name);
}
- if (cx && (!prefix || ("/" + cx.tagName).lastIndexOf(prefix, 0) == 0))
+ if (cx && (!prefix || tagType == "close" && cx.tagName.lastIndexOf(prefix, 0) == 0))
result.push("" + cx.tagName + ">");
} else {
// Attribute completion
var curTag = tags[inner.state.tagName], attrs = curTag && curTag.attrs;
- if (!attrs) return;
+ var globalAttrs = tags["!attrs"];
+ if (!attrs && !globalAttrs) return;
+ if (!attrs) {
+ attrs = globalAttrs;
+ } else if (globalAttrs) { // Combine tag-local and global attributes
+ var set = {};
+ for (var nm in globalAttrs) if (globalAttrs.hasOwnProperty(nm)) set[nm] = globalAttrs[nm];
+ for (var nm in attrs) if (attrs.hasOwnProperty(nm)) set[nm] = attrs[nm];
+ attrs = set;
+ }
if (token.type == "string" || token.string == "=") { // A value
var before = cm.getRange(Pos(cur.line, Math.max(0, cur.ch - 60)),
Pos(cur.line, token.type == "string" ? token.start : token.end));
@@ -47,9 +75,16 @@
if (typeof atValues == 'function') atValues = atValues.call(this, cm); // Functions can be used to supply values for autocomplete widget
if (token.type == "string") {
prefix = token.string;
+ var n = 0;
if (/['"]/.test(token.string.charAt(0))) {
quote = token.string.charAt(0);
prefix = token.string.slice(1);
+ n++;
+ }
+ var len = token.string.length;
+ if (/['"]/.test(token.string.charAt(len - 1))) {
+ quote = token.string.charAt(len - 1);
+ prefix = token.string.substr(n, len - 2);
}
replaceToken = true;
}
@@ -66,7 +101,7 @@
}
return {
list: result,
- from: replaceToken ? Pos(cur.line, token.start) : cur,
+ from: replaceToken ? Pos(cur.line, tagStart == null ? token.start : tagStart) : cur,
to: replaceToken ? Pos(cur.line, token.end) : cur
};
}
diff --git a/applications/admin/static/codemirror/addon/lint/coffeescript-lint.js b/applications/admin/static/codemirror/addon/lint/coffeescript-lint.js
index 6df17f8f..7e39428f 100644
--- a/applications/admin/static/codemirror/addon/lint/coffeescript-lint.js
+++ b/applications/admin/static/codemirror/addon/lint/coffeescript-lint.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
// Depends on coffeelint.js from http://www.coffeelint.org/js/coffeelint.js
// declare global: coffeelint
diff --git a/applications/admin/static/codemirror/addon/lint/css-lint.js b/applications/admin/static/codemirror/addon/lint/css-lint.js
new file mode 100644
index 00000000..1f61b479
--- /dev/null
+++ b/applications/admin/static/codemirror/addon/lint/css-lint.js
@@ -0,0 +1,35 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+// Depends on csslint.js from https://github.com/stubbornella/csslint
+
+// declare global: CSSLint
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.registerHelper("lint", "css", function(text) {
+ var found = [];
+ if (!window.CSSLint) return found;
+ var results = CSSLint.verify(text), messages = results.messages, message = null;
+ for ( var i = 0; i < messages.length; i++) {
+ message = messages[i];
+ var startLine = message.line -1, endLine = message.line -1, startCol = message.col -1, endCol = message.col;
+ found.push({
+ from: CodeMirror.Pos(startLine, startCol),
+ to: CodeMirror.Pos(endLine, endCol),
+ message: message.message,
+ severity : message.type
+ });
+ }
+ return found;
+});
+
+});
diff --git a/applications/admin/static/codemirror/addon/lint/javascript-lint.js b/applications/admin/static/codemirror/addon/lint/javascript-lint.js
index bbb51083..3d65ba69 100644
--- a/applications/admin/static/codemirror/addon/lint/javascript-lint.js
+++ b/applications/admin/static/codemirror/addon/lint/javascript-lint.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
@@ -19,6 +22,7 @@
"Unclosed string", "Stopping, unable to continue" ];
function validator(text, options) {
+ if (!window.JSHINT) return [];
JSHINT(text, options);
var errors = JSHINT.data().errors, result = [];
if (errors) parseErrors(errors, result);
diff --git a/applications/admin/static/codemirror/addon/lint/json-lint.js b/applications/admin/static/codemirror/addon/lint/json-lint.js
index 1f5f82d0..9dbb616b 100644
--- a/applications/admin/static/codemirror/addon/lint/json-lint.js
+++ b/applications/admin/static/codemirror/addon/lint/json-lint.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
// Depends on jsonlint.js from https://github.com/zaach/jsonlint
// declare global: jsonlint
diff --git a/applications/admin/static/codemirror/addon/lint/lint.js b/applications/admin/static/codemirror/addon/lint/lint.js
index 393a6890..66f187e2 100644
--- a/applications/admin/static/codemirror/addon/lint/lint.js
+++ b/applications/admin/static/codemirror/addon/lint/lint.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
@@ -8,7 +11,6 @@
})(function(CodeMirror) {
"use strict";
var GUTTER_ID = "CodeMirror-lint-markers";
- var SEVERITIES = /^(?:error|warning)$/;
function showTooltip(e, content) {
var tt = document.createElement("div");
@@ -107,7 +109,7 @@
function annotationTooltip(ann) {
var severity = ann.severity;
- if (!SEVERITIES.test(severity)) severity = "error";
+ if (!severity) severity = "error";
var tip = document.createElement("div");
tip.className = "CodeMirror-lint-message-" + severity;
tip.appendChild(document.createTextNode(ann.message));
@@ -116,10 +118,11 @@
function startLinting(cm) {
var state = cm.state.lint, options = state.options;
+ var passOptions = options.options || options; // Support deprecated passing of `options` property in options
if (options.async)
- options.getAnnotations(cm, updateLinting, options);
+ options.getAnnotations(cm.getValue(), updateLinting, passOptions, cm);
else
- updateLinting(cm, options.getAnnotations(cm.getValue(), options.options));
+ updateLinting(cm, options.getAnnotations(cm.getValue(), passOptions, cm));
}
function updateLinting(cm, annotationsNotSorted) {
@@ -138,7 +141,7 @@
for (var i = 0; i < anns.length; ++i) {
var ann = anns[i];
var severity = ann.severity;
- if (!SEVERITIES.test(severity)) severity = "error";
+ if (!severity) severity = "error";
maxSeverity = getMaxSeverity(maxSeverity, severity);
if (options.formatAnnotation) ann = options.formatAnnotation(ann);
@@ -168,20 +171,14 @@
showTooltipFor(e, annotationTooltip(ann), target);
}
- // When the mouseover fires, the cursor might not actually be over
- // the character itself yet. These pairs of x,y offsets are used to
- // probe a few nearby points when no suitable marked range is found.
- var nearby = [0, 0, 0, 5, 0, -5, 5, 0, -5, 0];
-
function onMouseOver(cm, e) {
- if (!/\bCodeMirror-lint-mark-/.test((e.target || e.srcElement).className)) return;
- for (var i = 0; i < nearby.length; i += 2) {
- var spans = cm.findMarksAt(cm.coordsChar({left: e.clientX + nearby[i],
- top: e.clientY + nearby[i + 1]}, "client"));
- for (var j = 0; j < spans.length; ++j) {
- var span = spans[j], ann = span.__annotation;
- if (ann) return popupSpanTooltip(ann, e);
- }
+ var target = e.target || e.srcElement;
+ if (!/\bCodeMirror-lint-mark-/.test(target.className)) return;
+ var box = target.getBoundingClientRect(), x = (box.left + box.right) / 2, y = (box.top + box.bottom) / 2;
+ var spans = cm.findMarksAt(cm.coordsChar({left: x, top: y}, "client"));
+ for (var i = 0; i < spans.length; ++i) {
+ var ann = spans[i].__annotation;
+ if (ann) return popupSpanTooltip(ann, e);
}
}
diff --git a/applications/admin/static/codemirror/addon/lint/yaml-lint.js b/applications/admin/static/codemirror/addon/lint/yaml-lint.js
new file mode 100644
index 00000000..3f77e525
--- /dev/null
+++ b/applications/admin/static/codemirror/addon/lint/yaml-lint.js
@@ -0,0 +1,28 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+// Depends on js-yaml.js from https://github.com/nodeca/js-yaml
+
+// declare global: jsyaml
+
+CodeMirror.registerHelper("lint", "yaml", function(text) {
+ var found = [];
+ try { jsyaml.load(text); }
+ catch(e) {
+ var loc = e.mark;
+ found.push({ from: CodeMirror.Pos(loc.line, loc.column), to: CodeMirror.Pos(loc.line, loc.column), message: e.message });
+ }
+ return found;
+});
+
+});
diff --git a/applications/admin/static/codemirror/addon/merge/dep/diff_match_patch.js b/applications/admin/static/codemirror/addon/merge/dep/diff_match_patch.js
deleted file mode 100644
index 9d615dc9..00000000
--- a/applications/admin/static/codemirror/addon/merge/dep/diff_match_patch.js
+++ /dev/null
@@ -1,50 +0,0 @@
-// From https://code.google.com/p/google-diff-match-patch/ , licensed under the Apache License 2.0
-(function(){function diff_match_patch(){this.Diff_Timeout=1;this.Diff_EditCost=4;this.Match_Threshold=0.5;this.Match_Distance=1E3;this.Patch_DeleteThreshold=0.5;this.Patch_Margin=4;this.Match_MaxBits=32}
-diff_match_patch.prototype.diff_main=function(a,b,c,d){"undefined"==typeof d&&(d=0>=this.Diff_Timeout?Number.MAX_VALUE:(new Date).getTime()+1E3*this.Diff_Timeout);if(null==a||null==b)throw Error("Null input. (diff_main)");if(a==b)return a?[[0,a]]:[];"undefined"==typeof c&&(c=!0);var e=c,f=this.diff_commonPrefix(a,b);c=a.substring(0,f);a=a.substring(f);b=b.substring(f);var f=this.diff_commonSuffix(a,b),g=a.substring(a.length-f);a=a.substring(0,a.length-f);b=b.substring(0,b.length-f);a=this.diff_compute_(a,
-b,e,d);c&&a.unshift([0,c]);g&&a.push([0,g]);this.diff_cleanupMerge(a);return a};
-diff_match_patch.prototype.diff_compute_=function(a,b,c,d){if(!a)return[[1,b]];if(!b)return[[-1,a]];var e=a.length>b.length?a:b,f=a.length>b.length?b:a,g=e.indexOf(f);return-1!=g?(c=[[1,e.substring(0,g)],[0,f],[1,e.substring(g+f.length)]],a.length>b.length&&(c[0][0]=c[2][0]=-1),c):1==f.length?[[-1,a],[1,b]]:(e=this.diff_halfMatch_(a,b))?(f=e[0],a=e[1],g=e[2],b=e[3],e=e[4],f=this.diff_main(f,g,c,d),c=this.diff_main(a,b,c,d),f.concat([[0,e]],c)):c&&100c);v++){for(var n=-v+r;n<=v-t;n+=2){var l=g+n,m;m=n==-v||n!=v&&j[l-1]d)t+=2;else if(s>e)r+=2;else if(q&&(l=g+k-n,0<=l&&l=
-u)return this.diff_bisectSplit_(a,b,m,s,c)}}for(n=-v+p;n<=v-w;n+=2){l=g+n;u=n==-v||n!=v&&i[l-1]d)w+=2;else if(m>e)p+=2;else if(!q&&(l=g+k-n,0<=l&&(l=u)))return this.diff_bisectSplit_(a,b,m,s,c)}}return[[-1,a],[1,b]]};
-diff_match_patch.prototype.diff_bisectSplit_=function(a,b,c,d,e){var f=a.substring(0,c),g=b.substring(0,d);a=a.substring(c);b=b.substring(d);f=this.diff_main(f,g,!1,e);e=this.diff_main(a,b,!1,e);return f.concat(e)};
-diff_match_patch.prototype.diff_linesToChars_=function(a,b){function c(a){for(var b="",c=0,f=-1,g=d.length;fd?a=a.substring(c-d):c=a.length?[h,j,n,l,g]:null}if(0>=this.Diff_Timeout)return null;
-var d=a.length>b.length?a:b,e=a.length>b.length?b:a;if(4>d.length||2*e.lengthd[4].length?g:d:d:g;var j;a.length>b.length?(g=h[0],d=h[1],e=h[2],j=h[3]):(e=h[0],j=h[1],g=h[2],d=h[3]);h=h[4];return[g,d,e,j,h]};
-diff_match_patch.prototype.diff_cleanupSemantic=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=0,h=0,j=0,i=0;f=e){if(d>=b.length/2||d>=c.length/2)a.splice(f,0,[0,c.substring(0,d)]),a[f-1][1]=b.substring(0,b.length-d),a[f+1][1]=c.substring(d),f++}else if(e>=b.length/2||e>=c.length/2)a.splice(f,0,[0,b.substring(0,e)]),a[f-1][0]=1,a[f-1][1]=c.substring(0,c.length-e),a[f+1][0]=-1,a[f+1][1]=b.substring(e),f++;f++}f++}};
-diff_match_patch.prototype.diff_cleanupSemanticLossless=function(a){function b(a,b){if(!a||!b)return 6;var c=a.charAt(a.length-1),d=b.charAt(0),e=c.match(diff_match_patch.nonAlphaNumericRegex_),f=d.match(diff_match_patch.nonAlphaNumericRegex_),g=e&&c.match(diff_match_patch.whitespaceRegex_),h=f&&d.match(diff_match_patch.whitespaceRegex_),c=g&&c.match(diff_match_patch.linebreakRegex_),d=h&&d.match(diff_match_patch.linebreakRegex_),i=c&&a.match(diff_match_patch.blanklineEndRegex_),j=d&&b.match(diff_match_patch.blanklineStartRegex_);
-return i||j?5:c||d?4:e&&!g&&h?3:g||h?2:e||f?1:0}for(var c=1;c=i&&(i=k,g=d,h=e,j=f)}a[c-1][1]!=g&&(g?a[c-1][1]=g:(a.splice(c-1,1),c--),a[c][1]=
-h,j?a[c+1][1]=j:(a.splice(c+1,1),c--))}c++}};diff_match_patch.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/;diff_match_patch.whitespaceRegex_=/\s/;diff_match_patch.linebreakRegex_=/[\r\n]/;diff_match_patch.blanklineEndRegex_=/\n\r?\n$/;diff_match_patch.blanklineStartRegex_=/^\r?\n\r?\n/;
-diff_match_patch.prototype.diff_cleanupEfficiency=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=!1,h=!1,j=!1,i=!1;fb)break;e=c;f=d}return a.length!=g&&-1===a[g][0]?f:f+(b-e)};
-diff_match_patch.prototype.diff_prettyHtml=function(a){for(var b=[],c=/&/g,d=//g,f=/\n/g,g=0;g");switch(h){case 1:b[g]=''+j+" ";break;case -1:b[g]=''+j+"";break;case 0:b[g]=""+j+" "}}return b.join("")};
-diff_match_patch.prototype.diff_text1=function(a){for(var b=[],c=0;ci)throw Error("Invalid number in diff_fromDelta: "+h);h=a.substring(e,e+=i);"="==f[g].charAt(0)?c[d++]=[0,h]:c[d++]=[-1,h];break;default:if(f[g])throw Error("Invalid diff operation in diff_fromDelta: "+
-f[g]);}}if(e!=a.length)throw Error("Delta length ("+e+") does not equal source text length ("+a.length+").");return c};diff_match_patch.prototype.match_main=function(a,b,c){if(null==a||null==b||null==c)throw Error("Null input. (match_main)");c=Math.max(0,Math.min(c,a.length));return a==b?0:a.length?a.substring(c,c+b.length)==b?c:this.match_bitap_(a,b,c):-1};
-diff_match_patch.prototype.match_bitap_=function(a,b,c){function d(a,d){var e=a/b.length,g=Math.abs(c-d);return!f.Match_Distance?g?1:e:e+g/f.Match_Distance}if(b.length>this.Match_MaxBits)throw Error("Pattern too long for this browser.");var e=this.match_alphabet_(b),f=this,g=this.Match_Threshold,h=a.indexOf(b,c);-1!=h&&(g=Math.min(d(0,h),g),h=a.lastIndexOf(b,c+b.length),-1!=h&&(g=Math.min(d(0,h),g)));for(var j=1<=i;p--){var w=e[a.charAt(p-1)];k[p]=0===t?(k[p+1]<<1|1)&w:(k[p+1]<<1|1)&w|((r[p+1]|r[p])<<1|1)|r[p+1];if(k[p]&j&&(w=d(t,p-1),w<=g))if(g=w,h=p-1,h>c)i=Math.max(1,2*c-h);else break}if(d(t+1,c)>g)break;r=k}return h};
-diff_match_patch.prototype.match_alphabet_=function(a){for(var b={},c=0;c=2*this.Patch_Margin&&
-e&&(this.patch_addContext_(a,h),c.push(a),a=new diff_match_patch.patch_obj,e=0,h=d,f=g)}1!==i&&(f+=k.length);-1!==i&&(g+=k.length)}e&&(this.patch_addContext_(a,h),c.push(a));return c};diff_match_patch.prototype.patch_deepCopy=function(a){for(var b=[],c=0;cthis.Match_MaxBits){if(j=this.match_main(b,h.substring(0,this.Match_MaxBits),g),-1!=j&&(i=this.match_main(b,h.substring(h.length-this.Match_MaxBits),g+h.length-this.Match_MaxBits),-1==i||j>=i))j=-1}else j=this.match_main(b,h,g);
-if(-1==j)e[f]=!1,d-=a[f].length2-a[f].length1;else if(e[f]=!0,d=j-g,g=-1==i?b.substring(j,j+h.length):b.substring(j,i+this.Match_MaxBits),h==g)b=b.substring(0,j)+this.diff_text2(a[f].diffs)+b.substring(j+h.length);else if(g=this.diff_main(h,g,!1),h.length>this.Match_MaxBits&&this.diff_levenshtein(g)/h.length>this.Patch_DeleteThreshold)e[f]=!1;else{this.diff_cleanupSemanticLossless(g);for(var h=0,k,i=0;ie[0][1].length){var f=b-e[0][1].length;e[0][1]=c.substring(e[0][1].length)+e[0][1];d.start1-=f;d.start2-=f;d.length1+=f;d.length2+=f}d=a[a.length-1];e=d.diffs;0==e.length||0!=e[e.length-1][0]?(e.push([0,
-c]),d.length1+=b,d.length2+=b):b>e[e.length-1][1].length&&(f=b-e[e.length-1][1].length,e[e.length-1][1]+=c.substring(0,f),d.length1+=f,d.length2+=f);return c};
-diff_match_patch.prototype.patch_splitMax=function(a){for(var b=this.Match_MaxBits,c=0;c 2*b?(h.length1+=i.length,e+=i.length,j=!1,h.diffs.push([g,i]),d.diffs.shift()):(i=i.substring(0,b-h.length1-this.Patch_Margin),h.length1+=i.length,e+=i.length,0===g?(h.length2+=i.length,f+=i.length):j=!1,h.diffs.push([g,i]),i==d.diffs[0][1]?d.diffs.shift():d.diffs[0][1]=d.diffs[0][1].substring(i.length))}g=this.diff_text2(h.diffs);g=g.substring(g.length-this.Patch_Margin);i=this.diff_text1(d.diffs).substring(0,this.Patch_Margin);""!==i&&
-(h.length1+=i.length,h.length2+=i.length,0!==h.diffs.length&&0===h.diffs[h.diffs.length-1][0]?h.diffs[h.diffs.length-1][1]+=i:h.diffs.push([0,i]));j||a.splice(++c,0,h)}}};diff_match_patch.prototype.patch_toText=function(a){for(var b=[],c=0;c now) return false;
- var sInfo = editor.getScrollInfo(), halfScreen = .5 * sInfo.clientHeight, midY = sInfo.top + halfScreen;
- var mid = editor.lineAtHeight(midY, "local");
- var around = chunkBoundariesAround(dv.diff, mid, type == DIFF_INSERT);
- var off = getOffsets(editor, type == DIFF_INSERT ? around.edit : around.orig);
- var offOther = getOffsets(other, type == DIFF_INSERT ? around.orig : around.edit);
- var ratio = (midY - off.top) / (off.bot - off.top);
- var targetPos = (offOther.top - halfScreen) + ratio * (offOther.bot - offOther.top);
+ var sInfo = editor.getScrollInfo();
+ if (dv.mv.options.connect == "align") {
+ targetPos = sInfo.top;
+ } else {
+ var halfScreen = .5 * sInfo.clientHeight, midY = sInfo.top + halfScreen;
+ var mid = editor.lineAtHeight(midY, "local");
+ var around = chunkBoundariesAround(dv.diff, mid, type == DIFF_INSERT);
+ var off = getOffsets(editor, type == DIFF_INSERT ? around.edit : around.orig);
+ var offOther = getOffsets(other, type == DIFF_INSERT ? around.orig : around.edit);
+ var ratio = (midY - off.top) / (off.bot - off.top);
+ var targetPos = (offOther.top - halfScreen) + ratio * (offOther.bot - offOther.top);
- var botDist, mix;
- // Some careful tweaking to make sure no space is left out of view
- // when scrolling to top or bottom.
- if (targetPos > sInfo.top && (mix = sInfo.top / halfScreen) < 1) {
- targetPos = targetPos * mix + sInfo.top * (1 - mix);
- } else if ((botDist = sInfo.height - sInfo.clientHeight - sInfo.top) < halfScreen) {
- var otherInfo = other.getScrollInfo();
- var botDistOther = otherInfo.height - otherInfo.clientHeight - targetPos;
- if (botDistOther > botDist && (mix = botDist / halfScreen) < 1)
- targetPos = targetPos * mix + (otherInfo.height - otherInfo.clientHeight - botDist) * (1 - mix);
+ var botDist, mix;
+ // Some careful tweaking to make sure no space is left out of view
+ // when scrolling to top or bottom.
+ if (targetPos > sInfo.top && (mix = sInfo.top / halfScreen) < 1) {
+ targetPos = targetPos * mix + sInfo.top * (1 - mix);
+ } else if ((botDist = sInfo.height - sInfo.clientHeight - sInfo.top) < halfScreen) {
+ var otherInfo = other.getScrollInfo();
+ var botDistOther = otherInfo.height - otherInfo.clientHeight - targetPos;
+ if (botDistOther > botDist && (mix = botDist / halfScreen) < 1)
+ targetPos = targetPos * mix + (otherInfo.height - otherInfo.clientHeight - botDist) * (1 - mix);
+ }
}
other.scrollTo(sInfo.left, targetPos);
@@ -154,7 +168,7 @@
function setScrollLock(dv, val, action) {
dv.lockScroll = val;
- if (val && action != false) syncScroll(dv, DIFF_INSERT) && drawConnectors(dv);
+ if (val && action != false) syncScroll(dv, DIFF_INSERT) && makeConnections(dv);
dv.lockButton.innerHTML = val ? "\u21db\u21da" : "\u21db \u21da";
}
@@ -165,7 +179,7 @@
var mark = arr[i];
if (mark instanceof CodeMirror.TextMarker) {
mark.clear();
- } else {
+ } else if (mark.parent) {
editor.removeLineClass(mark, "background", classes.chunk);
editor.removeLineClass(mark, "background", classes.start);
editor.removeLineClass(mark, "background", classes.end);
@@ -242,47 +256,112 @@
// Updating the gap between editor and original
- function drawConnectors(dv) {
+ function makeConnections(dv) {
if (!dv.showDifferences) return;
+ var align = dv.mv.options.connect == "align", oldScrollEdit, oldScrollOrig;
+ if (align) {
+ if (!dv.orig.curOp) return dv.orig.operation(function() {
+ makeConnections(dv);
+ });
+ oldScrollEdit = dv.edit.getScrollInfo().top;
+ oldScrollOrig = dv.orig.getScrollInfo().top;
+ for (var i = 0; i < dv.aligners.length; i++)
+ dv.aligners[i].clear();
+ dv.aligners.length = 0;
+ var extraSpaceAbove = {edit: 0, orig: 0};
+ }
+
if (dv.svg) {
clear(dv.svg);
var w = dv.gap.offsetWidth;
attrs(dv.svg, "width", w, "height", dv.gap.offsetHeight);
}
- clear(dv.copyButtons);
+ if (dv.copyButtons) clear(dv.copyButtons);
- var flip = dv.type == "left";
var vpEdit = dv.edit.getViewport(), vpOrig = dv.orig.getViewport();
var sTopEdit = dv.edit.getScrollInfo().top, sTopOrig = dv.orig.getScrollInfo().top;
iterateChunks(dv.diff, function(topOrig, botOrig, topEdit, botEdit) {
- if (topEdit > vpEdit.to || botEdit < vpEdit.from ||
- topOrig > vpOrig.to || botOrig < vpOrig.from)
- return;
- var topLpx = dv.orig.heightAtLine(topOrig, "local") - sTopOrig, top = topLpx;
- if (dv.svg) {
- var topRpx = dv.edit.heightAtLine(topEdit, "local") - sTopEdit;
- if (flip) { var tmp = topLpx; topLpx = topRpx; topRpx = tmp; }
- var botLpx = dv.orig.heightAtLine(botOrig, "local") - sTopOrig;
- var botRpx = dv.edit.heightAtLine(botEdit, "local") - sTopEdit;
- if (flip) { var tmp = botLpx; botLpx = botRpx; botRpx = tmp; }
- var curveTop = " C " + w/2 + " " + topRpx + " " + w/2 + " " + topLpx + " " + (w + 2) + " " + topLpx;
- var curveBot = " C " + w/2 + " " + botLpx + " " + w/2 + " " + botRpx + " -1 " + botRpx;
- attrs(dv.svg.appendChild(document.createElementNS(svgNS, "path")),
- "d", "M -1 " + topRpx + curveTop + " L " + (w + 2) + " " + botLpx + curveBot + " z",
- "class", dv.classes.connect);
+ if (topEdit <= vpEdit.to && botEdit >= vpEdit.from &&
+ topOrig <= vpOrig.to && botOrig >= vpOrig.from)
+ drawConnectorsForChunk(dv, topOrig, botOrig, topEdit, botEdit, sTopOrig, sTopEdit, w);
+ if (align && (topEdit <= vpEdit.to || topOrig <= vpOrig.to)) {
+ var above = (botEdit < vpEdit.from && botOrig < vpOrig.from);
+ alignChunks(dv, topOrig, botOrig, topEdit, botEdit, above && extraSpaceAbove);
}
- var copy = dv.copyButtons.appendChild(elt("div", dv.type == "left" ? "\u21dd" : "\u21dc",
- "CodeMirror-merge-copy"));
- copy.title = "Revert chunk";
- copy.chunk = {topEdit: topEdit, botEdit: botEdit, topOrig: topOrig, botOrig: botOrig};
- copy.style.top = top + "px";
});
+ if (align) {
+ if (extraSpaceAbove.edit)
+ dv.aligners.push(padBelow(dv.edit, 0, extraSpaceAbove.edit));
+ if (extraSpaceAbove.orig)
+ dv.aligners.push(padBelow(dv.orig, 0, extraSpaceAbove.orig));
+ dv.edit.scrollTo(null, oldScrollEdit);
+ dv.orig.scrollTo(null, oldScrollOrig);
+ }
}
- function copyChunk(dv, chunk) {
+ function drawConnectorsForChunk(dv, topOrig, botOrig, topEdit, botEdit, sTopOrig, sTopEdit, w) {
+ var flip = dv.type == "left";
+ var top = dv.orig.heightAtLine(topOrig, "local") - sTopOrig;
+ if (dv.svg) {
+ var topLpx = top;
+ var topRpx = dv.edit.heightAtLine(topEdit, "local") - sTopEdit;
+ if (flip) { var tmp = topLpx; topLpx = topRpx; topRpx = tmp; }
+ var botLpx = dv.orig.heightAtLine(botOrig, "local") - sTopOrig;
+ var botRpx = dv.edit.heightAtLine(botEdit, "local") - sTopEdit;
+ if (flip) { var tmp = botLpx; botLpx = botRpx; botRpx = tmp; }
+ var curveTop = " C " + w/2 + " " + topRpx + " " + w/2 + " " + topLpx + " " + (w + 2) + " " + topLpx;
+ var curveBot = " C " + w/2 + " " + botLpx + " " + w/2 + " " + botRpx + " -1 " + botRpx;
+ attrs(dv.svg.appendChild(document.createElementNS(svgNS, "path")),
+ "d", "M -1 " + topRpx + curveTop + " L " + (w + 2) + " " + botLpx + curveBot + " z",
+ "class", dv.classes.connect);
+ }
+ if (dv.copyButtons) {
+ var copy = dv.copyButtons.appendChild(elt("div", dv.type == "left" ? "\u21dd" : "\u21dc",
+ "CodeMirror-merge-copy"));
+ var editOriginals = dv.mv.options.allowEditingOriginals;
+ copy.title = editOriginals ? "Push to left" : "Revert chunk";
+ copy.chunk = {topEdit: topEdit, botEdit: botEdit, topOrig: topOrig, botOrig: botOrig};
+ copy.style.top = top + "px";
+
+ if (editOriginals) {
+ var topReverse = dv.orig.heightAtLine(topEdit, "local") - sTopEdit;
+ var copyReverse = dv.copyButtons.appendChild(elt("div", dv.type == "right" ? "\u21dd" : "\u21dc",
+ "CodeMirror-merge-copy-reverse"));
+ copyReverse.title = "Push to right";
+ copyReverse.chunk = {topEdit: topOrig, botEdit: botOrig, topOrig: topEdit, botOrig: botEdit};
+ copyReverse.style.top = topReverse + "px";
+ dv.type == "right" ? copyReverse.style.left = "2px" : copyReverse.style.right = "2px";
+ }
+ }
+ }
+
+ function alignChunks(dv, topOrig, botOrig, topEdit, botEdit, aboveViewport) {
+ var topOrigPx = dv.orig.heightAtLine(topOrig, "local");
+ var botOrigPx = dv.orig.heightAtLine(botOrig, "local");
+ var topEditPx = dv.edit.heightAtLine(topEdit, "local");
+ var botEditPx = dv.edit.heightAtLine(botEdit, "local");
+ var origH = botOrigPx -topOrigPx, editH = botEditPx - topEditPx;
+ var diff = editH - origH;
+ if (diff > 1) {
+ if (aboveViewport) aboveViewport.orig += diff;
+ else dv.aligners.push(padBelow(dv.orig, botOrig - 1, diff));
+ } else if (diff < -1) {
+ if (aboveViewport) aboveViewport.edit -= diff;
+ else dv.aligners.push(padBelow(dv.edit, botEdit - 1, -diff));
+ }
+ return 0;
+ }
+
+ function padBelow(cm, line, size) {
+ var elt = document.createElement("div");
+ elt.style.height = size + "px"; elt.style.minWidth = "1px";
+ return cm.addLineWidget(line, elt, {height: size});
+ }
+
+ function copyChunk(dv, to, from, chunk) {
if (dv.diffOutOfDate) return;
- dv.edit.replaceRange(dv.orig.getRange(Pos(chunk.topOrig, 0), Pos(chunk.botOrig, 0)),
+ to.replaceRange(from.getRange(Pos(chunk.topOrig, 0), Pos(chunk.botOrig, 0)),
Pos(chunk.topEdit, 0), Pos(chunk.botEdit, 0));
}
@@ -291,7 +370,15 @@
var MergeView = CodeMirror.MergeView = function(node, options) {
if (!(this instanceof MergeView)) return new MergeView(node, options);
+ this.options = options;
var origLeft = options.origLeft, origRight = options.origRight == null ? options.orig : options.origRight;
+ if (origLeft && origRight) {
+ if (options.connect == "align")
+ throw new Error("connect: \"align\" is not supported for three-way merge views");
+ if (options.collapseIdentical)
+ throw new Error("collapseIdentical option is not supported for three-way merge views");
+ }
+
var hasLeft = origLeft != null, hasRight = origRight != null;
var panes = 1 + (hasLeft ? 1 : 0) + (hasRight ? 1 : 0);
var wrap = [], left = this.left = null, right = this.right = null;
@@ -316,15 +403,19 @@
(hasRight ? rightPane : editPane).className += " CodeMirror-merge-pane-rightmost";
wrap.push(elt("div", null, null, "height: 0; clear: both;"));
+
var wrapElt = this.wrap = node.appendChild(elt("div", wrap, "CodeMirror-merge CodeMirror-merge-" + panes + "pane"));
this.edit = CodeMirror(editPane, copyObj(options));
if (left) left.init(leftPane, origLeft, options);
if (right) right.init(rightPane, origRight, options);
+ if (options.collapseIdentical)
+ collapseIdenticalStretches(left || right, options.collapseIdentical);
+
var onResize = function() {
- if (left) drawConnectors(left);
- if (right) drawConnectors(right);
+ if (left) makeConnections(left);
+ if (right) makeConnections(right);
};
CodeMirror.on(window, "resize", onResize);
var resizeInterval = setInterval(function() {
@@ -338,16 +429,26 @@
lock.title = "Toggle locked scrolling";
var lockWrap = elt("div", [lock], "CodeMirror-merge-scrolllock-wrap");
CodeMirror.on(lock, "click", function() { setScrollLock(dv, !dv.lockScroll); });
- dv.copyButtons = elt("div", null, "CodeMirror-merge-copybuttons-" + dv.type);
- CodeMirror.on(dv.copyButtons, "click", function(e) {
- var node = e.target || e.srcElement;
- if (node.chunk) copyChunk(dv, node.chunk);
- });
- var gapElts = [dv.copyButtons, lockWrap];
- var svg = document.createElementNS && document.createElementNS(svgNS, "svg");
- if (svg && !svg.createSVGRect) svg = null;
- dv.svg = svg;
- if (svg) gapElts.push(svg);
+ var gapElts = [lockWrap];
+ if (dv.mv.options.revertButtons !== false) {
+ dv.copyButtons = elt("div", null, "CodeMirror-merge-copybuttons-" + dv.type);
+ CodeMirror.on(dv.copyButtons, "click", function(e) {
+ var node = e.target || e.srcElement;
+ if (!node.chunk) return;
+ if (node.className == "CodeMirror-merge-copy-reverse") {
+ copyChunk(dv, dv.orig, dv.edit, node.chunk);
+ return;
+ }
+ copyChunk(dv, dv.edit, dv.orig, node.chunk);
+ });
+ gapElts.unshift(dv.copyButtons);
+ }
+ if (dv.mv.options.connect != "align") {
+ var svg = document.createElementNS && document.createElementNS(svgNS, "svg");
+ if (svg && !svg.createSVGRect) svg = null;
+ dv.svg = svg;
+ if (svg) gapElts.push(svg);
+ }
return dv.gap = elt("div", gapElts, "CodeMirror-merge-gap");
}
@@ -362,10 +463,10 @@
if (this.left) this.left.setShowDifferences(val);
},
rightChunks: function() {
- return this.right && getChunks(this.right.diff);
+ return this.right && getChunks(this.right);
},
leftChunks: function() {
- return this.left && getChunks(this.left.diff);
+ return this.left && getChunks(this.left);
}
};
@@ -416,9 +517,10 @@
f(startOrig, orig.line + 1, startEdit, edit.line + 1);
}
- function getChunks(diff) {
+ function getChunks(dv) {
+ ensureDiff(dv);
var collect = [];
- iterateChunks(diff, function(topOrig, botOrig, topEdit, botEdit) {
+ iterateChunks(dv.diff, function(topOrig, botOrig, topEdit, botEdit) {
collect.push({origFrom: topOrig, origTo: botOrig,
editFrom: topEdit, editTo: botEdit});
});
@@ -458,6 +560,46 @@
return {edit: {before: beforeE, after: afterE}, orig: {before: beforeO, after: afterO}};
}
+ function collapseSingle(cm, from, to) {
+ cm.addLineClass(from, "wrap", "CodeMirror-merge-collapsed-line");
+ var widget = document.createElement("span");
+ widget.className = "CodeMirror-merge-collapsed-widget";
+ widget.title = "Identical text collapsed. Click to expand.";
+ var mark = cm.markText(Pos(from, 0), Pos(to - 1), {
+ inclusiveLeft: true,
+ inclusiveRight: true,
+ replacedWith: widget,
+ clearOnEnter: true
+ });
+ function clear() {
+ mark.clear();
+ cm.removeLineClass(from, "wrap", "CodeMirror-merge-collapsed-line");
+ }
+ widget.addEventListener("click", clear);
+ return {mark: mark, clear: clear};
+ }
+
+ function collapseStretch(dv, origStart, editStart, size) {
+ var mOrig = collapseSingle(dv.orig, origStart, origStart + size);
+ var mEdit = collapseSingle(dv.edit, editStart, editStart + size);
+ mOrig.mark.on("clear", function() { mEdit.clear(); });
+ mEdit.mark.on("clear", function() { mOrig.clear(); });
+ }
+
+ function collapseIdenticalStretches(dv, margin) {
+ if (typeof margin != "number") margin = 2;
+ var lastOrig = dv.orig.firstLine(), lastEdit = dv.edit.firstLine();
+ iterateChunks(dv.diff, function(topOrig, botOrig, _topEdit, botEdit) {
+ var identicalSize = topOrig - margin - lastOrig;
+ if (identicalSize > margin)
+ collapseStretch(dv, lastOrig, lastEdit, identicalSize);
+ lastOrig = botOrig + margin; lastEdit = botEdit + margin;
+ });
+ var bottomSize = dv.orig.lastLine() + 1 - lastOrig;
+ if (bottomSize > margin)
+ collapseStretch(dv, lastOrig, lastEdit, bottomSize);
+ }
+
// General utilities
function elt(tag, content, className, style) {
diff --git a/applications/admin/static/codemirror/addon/mode/loadmode.js b/applications/admin/static/codemirror/addon/mode/loadmode.js
index e08c2813..10117ec2 100644
--- a/applications/admin/static/codemirror/addon/mode/loadmode.js
+++ b/applications/admin/static/codemirror/addon/mode/loadmode.js
@@ -1,11 +1,14 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
- mod(require("../../lib/codemirror"));
+ mod(require("../../lib/codemirror"), "cjs");
else if (typeof define == "function" && define.amd) // AMD
- define(["../../lib/codemirror"], mod);
+ define(["../../lib/codemirror"], function(CM) { mod(CM, "amd"); });
else // Plain browser env
- mod(CodeMirror);
-})(function(CodeMirror) {
+ mod(CodeMirror, "plain");
+})(function(CodeMirror, env) {
if (!CodeMirror.modeURL) CodeMirror.modeURL = "../mode/%N/%N.js";
var loading = {};
@@ -32,21 +35,24 @@
if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont);
if (loading.hasOwnProperty(mode)) return loading[mode].push(cont);
- var script = document.createElement("script");
- script.src = CodeMirror.modeURL.replace(/%N/g, mode);
- var others = document.getElementsByTagName("script")[0];
- others.parentNode.insertBefore(script, others);
- var list = loading[mode] = [cont];
- var count = 0, poll = setInterval(function() {
- if (++count > 100) return clearInterval(poll);
- if (CodeMirror.modes.hasOwnProperty(mode)) {
- clearInterval(poll);
- loading[mode] = null;
+ var file = CodeMirror.modeURL.replace(/%N/g, mode);
+ if (env == "plain") {
+ var script = document.createElement("script");
+ script.src = file;
+ var others = document.getElementsByTagName("script")[0];
+ var list = loading[mode] = [cont];
+ CodeMirror.on(script, "load", function() {
ensureDeps(mode, function() {
for (var i = 0; i < list.length; ++i) list[i]();
});
- }
- }, 200);
+ });
+ others.parentNode.insertBefore(script, others);
+ } else if (env == "cjs") {
+ require(file);
+ cont();
+ } else if (env == "amd") {
+ requirejs([file], cont);
+ }
};
CodeMirror.autoLoadMode = function(instance, mode) {
diff --git a/applications/admin/static/codemirror/addon/mode/multiplex.js b/applications/admin/static/codemirror/addon/mode/multiplex.js
index 07385c35..6a95b323 100644
--- a/applications/admin/static/codemirror/addon/mode/multiplex.js
+++ b/applications/admin/static/codemirror/addon/mode/multiplex.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
diff --git a/applications/admin/static/codemirror/addon/mode/multiplex_test.js b/applications/admin/static/codemirror/addon/mode/multiplex_test.js
index c0656357..d3394342 100644
--- a/applications/admin/static/codemirror/addon/mode/multiplex_test.js
+++ b/applications/admin/static/codemirror/addon/mode/multiplex_test.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function() {
CodeMirror.defineMode("markdown_with_stex", function(){
var inner = CodeMirror.getMode({}, "stex");
diff --git a/applications/admin/static/codemirror/addon/mode/overlay.js b/applications/admin/static/codemirror/addon/mode/overlay.js
index 6f556a13..e1b9ed37 100644
--- a/applications/admin/static/codemirror/addon/mode/overlay.js
+++ b/applications/admin/static/codemirror/addon/mode/overlay.js
@@ -1,10 +1,14 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
// Utility function that allows modes to be combined. The mode given
// as the base argument takes care of most of the normal mode
// functionality, but a second (typically simple) mode is used, which
// can override the style of text. Both modes get to parse all of the
// text, but when both assign a non-null style to a piece of code, the
-// overlay wins, unless the combine argument was true, in which case
-// the styles are combined.
+// overlay wins, unless the combine argument was true and not overridden,
+// or state.overlay.combineTokens was true, in which case the styles are
+// combined.
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -23,7 +27,8 @@ CodeMirror.overlayMode = function(base, overlay, combine) {
base: CodeMirror.startState(base),
overlay: CodeMirror.startState(overlay),
basePos: 0, baseCur: null,
- overlayPos: 0, overlayCur: null
+ overlayPos: 0, overlayCur: null,
+ streamSeen: null
};
},
copyState: function(state) {
@@ -36,6 +41,12 @@ CodeMirror.overlayMode = function(base, overlay, combine) {
},
token: function(stream, state) {
+ if (stream != state.streamSeen ||
+ Math.min(state.basePos, state.overlayPos) < stream.start) {
+ state.streamSeen = stream;
+ state.basePos = state.overlayPos = stream.start;
+ }
+
if (stream.start == state.basePos) {
state.baseCur = base.token(stream, state.base);
state.basePos = stream.pos;
@@ -46,10 +57,14 @@ CodeMirror.overlayMode = function(base, overlay, combine) {
state.overlayPos = stream.pos;
}
stream.pos = Math.min(state.basePos, state.overlayPos);
- if (stream.eol()) state.basePos = state.overlayPos = 0;
+ // state.overlay.combineTokens always takes precedence over combine,
+ // unless set to null
if (state.overlayCur == null) return state.baseCur;
- if (state.baseCur != null && combine) return state.baseCur + " " + state.overlayCur;
+ else if (state.baseCur != null &&
+ state.overlay.combineTokens ||
+ combine && state.overlay.combineTokens == null)
+ return state.baseCur + " " + state.overlayCur;
else return state.overlayCur;
},
diff --git a/applications/admin/static/codemirror/addon/mode/simple.js b/applications/admin/static/codemirror/addon/mode/simple.js
new file mode 100644
index 00000000..795328b8
--- /dev/null
+++ b/applications/admin/static/codemirror/addon/mode/simple.js
@@ -0,0 +1,213 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ CodeMirror.defineSimpleMode = function(name, states) {
+ CodeMirror.defineMode(name, function(config) {
+ return CodeMirror.simpleMode(config, states);
+ });
+ };
+
+ CodeMirror.simpleMode = function(config, states) {
+ ensureState(states, "start");
+ var states_ = {}, meta = states.meta || {}, hasIndentation = false;
+ for (var state in states) if (state != meta && states.hasOwnProperty(state)) {
+ var list = states_[state] = [], orig = states[state];
+ for (var i = 0; i < orig.length; i++) {
+ var data = orig[i];
+ list.push(new Rule(data, states));
+ if (data.indent || data.dedent) hasIndentation = true;
+ }
+ }
+ var mode = {
+ startState: function() {
+ return {state: "start", pending: null,
+ local: null, localState: null,
+ indent: hasIndentation ? [] : null};
+ },
+ copyState: function(state) {
+ var s = {state: state.state, pending: state.pending,
+ local: state.local, localState: null,
+ indent: state.indent && state.indent.slice(0)};
+ if (state.localState)
+ s.localState = CodeMirror.copyState(state.local.mode, state.localState);
+ if (state.stack)
+ s.stack = state.stack.slice(0);
+ for (var pers = state.persistentStates; pers; pers = pers.next)
+ s.persistentStates = {mode: pers.mode,
+ spec: pers.spec,
+ state: pers.state == state.localState ? s.localState : CodeMirror.copyState(pers.mode, pers.state),
+ next: s.persistentStates};
+ return s;
+ },
+ token: tokenFunction(states_, config),
+ innerMode: function(state) { return state.local && {mode: state.local.mode, state: state.localState}; },
+ indent: indentFunction(states_, meta)
+ };
+ if (meta) for (var prop in meta) if (meta.hasOwnProperty(prop))
+ mode[prop] = meta[prop];
+ return mode;
+ };
+
+ function ensureState(states, name) {
+ if (!states.hasOwnProperty(name))
+ throw new Error("Undefined state " + name + "in simple mode");
+ }
+
+ function toRegex(val, caret) {
+ if (!val) return /(?:)/;
+ var flags = "";
+ if (val instanceof RegExp) {
+ if (val.ignoreCase) flags = "i";
+ val = val.source;
+ } else {
+ val = String(val);
+ }
+ return new RegExp((caret === false ? "" : "^") + "(?:" + val + ")", flags);
+ }
+
+ function asToken(val) {
+ if (!val) return null;
+ if (typeof val == "string") return val.replace(/\./g, " ");
+ var result = [];
+ for (var i = 0; i < val.length; i++)
+ result.push(val[i] && val[i].replace(/\./g, " "));
+ return result;
+ }
+
+ function Rule(data, states) {
+ if (data.next || data.push) ensureState(states, data.next || data.push);
+ this.regex = toRegex(data.regex);
+ this.token = asToken(data.token);
+ this.data = data;
+ }
+
+ function tokenFunction(states, config) {
+ return function(stream, state) {
+ if (state.pending) {
+ var pend = state.pending.shift();
+ if (state.pending.length == 0) state.pending = null;
+ stream.pos += pend.text.length;
+ return pend.token;
+ }
+
+ if (state.local) {
+ if (state.local.end && stream.match(state.local.end)) {
+ var tok = state.local.endToken || null;
+ state.local = state.localState = null;
+ return tok;
+ } else {
+ var tok = state.local.mode.token(stream, state.localState), m;
+ if (state.local.endScan && (m = state.local.endScan.exec(stream.current())))
+ stream.pos = stream.start + m.index;
+ return tok;
+ }
+ }
+
+ var curState = states[state.state];
+ for (var i = 0; i < curState.length; i++) {
+ var rule = curState[i];
+ var matches = (!rule.data.sol || stream.sol()) && stream.match(rule.regex);
+ if (matches) {
+ if (rule.data.next) {
+ state.state = rule.data.next;
+ } else if (rule.data.push) {
+ (state.stack || (state.stack = [])).push(state.state);
+ state.state = rule.data.push;
+ } else if (rule.data.pop && state.stack && state.stack.length) {
+ state.state = state.stack.pop();
+ }
+
+ if (rule.data.mode)
+ enterLocalMode(config, state, rule.data.mode, rule.token);
+ if (rule.data.indent)
+ state.indent.push(stream.indentation() + config.indentUnit);
+ if (rule.data.dedent)
+ state.indent.pop();
+ if (matches.length > 2) {
+ state.pending = [];
+ for (var j = 2; j < matches.length; j++)
+ if (matches[j])
+ state.pending.push({text: matches[j], token: rule.token[j - 1]});
+ stream.backUp(matches[0].length - (matches[1] ? matches[1].length : 0));
+ return rule.token[0];
+ } else if (rule.token && rule.token.join) {
+ return rule.token[0];
+ } else {
+ return rule.token;
+ }
+ }
+ }
+ stream.next();
+ return null;
+ };
+ }
+
+ function cmp(a, b) {
+ if (a === b) return true;
+ if (!a || typeof a != "object" || !b || typeof b != "object") return false;
+ var props = 0;
+ for (var prop in a) if (a.hasOwnProperty(prop)) {
+ if (!b.hasOwnProperty(prop) || !cmp(a[prop], b[prop])) return false;
+ props++;
+ }
+ for (var prop in b) if (b.hasOwnProperty(prop)) props--;
+ return props == 0;
+ }
+
+ function enterLocalMode(config, state, spec, token) {
+ var pers;
+ if (spec.persistent) for (var p = state.persistentStates; p && !pers; p = p.next)
+ if (spec.spec ? cmp(spec.spec, p.spec) : spec.mode == p.mode) pers = p;
+ var mode = pers ? pers.mode : spec.mode || CodeMirror.getMode(config, spec.spec);
+ var lState = pers ? pers.state : CodeMirror.startState(mode);
+ if (spec.persistent && !pers)
+ state.persistentStates = {mode: mode, spec: spec.spec, state: lState, next: state.persistentStates};
+
+ state.localState = lState;
+ state.local = {mode: mode,
+ end: spec.end && toRegex(spec.end),
+ endScan: spec.end && spec.forceEnd !== false && toRegex(spec.end, false),
+ endToken: token && token.join ? token[token.length - 1] : token};
+ }
+
+ function indexOf(val, arr) {
+ for (var i = 0; i < arr.length; i++) if (arr[i] === val) return true;
+ }
+
+ function indentFunction(states, meta) {
+ return function(state, textAfter, line) {
+ if (state.local && state.local.mode.indent)
+ return state.local.mode.indent(state.localState, textAfter, line);
+ if (state.indent == null || state.local || meta.dontIndentStates && indexOf(state.state, meta.dontIndentStates) > -1)
+ return CodeMirror.Pass;
+
+ var pos = state.indent.length - 1, rules = states[state.state];
+ scan: for (;;) {
+ for (var i = 0; i < rules.length; i++) {
+ var rule = rules[i];
+ if (rule.data.dedent && rule.data.dedentIfLineStart !== false) {
+ var m = rule.regex.exec(textAfter);
+ if (m && m[0]) {
+ pos--;
+ if (rule.next || rule.push) rules = states[rule.next || rule.push];
+ textAfter = textAfter.slice(m[0].length);
+ continue scan;
+ }
+ }
+ }
+ break;
+ }
+ return pos < 0 ? 0 : state.indent[pos];
+ };
+ }
+});
diff --git a/applications/admin/static/codemirror/addon/runmode/colorize.js b/applications/admin/static/codemirror/addon/runmode/colorize.js
index 0f9530b1..eb7060d0 100644
--- a/applications/admin/static/codemirror/addon/runmode/colorize.js
+++ b/applications/admin/static/codemirror/addon/runmode/colorize.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), require("./runmode"));
diff --git a/applications/admin/static/codemirror/addon/runmode/runmode-standalone.js b/applications/admin/static/codemirror/addon/runmode/runmode-standalone.js
index eaa2b8f2..f4f352c8 100644
--- a/applications/admin/static/codemirror/addon/runmode/runmode-standalone.js
+++ b/applications/admin/static/codemirror/addon/runmode/runmode-standalone.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
window.CodeMirror = {};
(function() {
@@ -71,7 +74,11 @@ CodeMirror.startState = function (mode, a1, a2) {
};
var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
-CodeMirror.defineMode = function (name, mode) { modes[name] = mode; };
+CodeMirror.defineMode = function (name, mode) {
+ if (arguments.length > 2)
+ mode.dependencies = Array.prototype.slice.call(arguments, 2);
+ modes[name] = mode;
+};
CodeMirror.defineMIME = function (mime, spec) { mimeModes[mime] = spec; };
CodeMirror.resolveMode = function(spec) {
if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
@@ -139,6 +146,7 @@ CodeMirror.runMode = function (string, modespec, callback, options) {
for (var i = 0, e = lines.length; i < e; ++i) {
if (i) callback("\n");
var stream = new CodeMirror.StringStream(lines[i]);
+ if (!stream.string && mode.blankLine) mode.blankLine(state);
while (!stream.eol()) {
var style = mode.token(stream, state);
callback(stream.current(), style, i, stream.start, state);
diff --git a/applications/admin/static/codemirror/addon/runmode/runmode.js b/applications/admin/static/codemirror/addon/runmode/runmode.js
index 351840e0..07d2279f 100644
--- a/applications/admin/static/codemirror/addon/runmode/runmode.js
+++ b/applications/admin/static/codemirror/addon/runmode/runmode.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
@@ -57,6 +60,7 @@ CodeMirror.runMode = function(string, modespec, callback, options) {
for (var i = 0, e = lines.length; i < e; ++i) {
if (i) callback("\n");
var stream = new CodeMirror.StringStream(lines[i]);
+ if (!stream.string && mode.blankLine) mode.blankLine(state);
while (!stream.eol()) {
var style = mode.token(stream, state);
callback(stream.current(), style, i, stream.start, state);
diff --git a/applications/admin/static/codemirror/addon/runmode/runmode.node.js b/applications/admin/static/codemirror/addon/runmode/runmode.node.js
index 74c39be7..8b8140b4 100644
--- a/applications/admin/static/codemirror/addon/runmode/runmode.node.js
+++ b/applications/admin/static/codemirror/addon/runmode/runmode.node.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
/* Just enough of CodeMirror to run runMode under node.js */
// declare global: StringStream
@@ -71,10 +74,8 @@ exports.startState = function(mode, a1, a2) {
var modes = exports.modes = {}, mimeModes = exports.mimeModes = {};
exports.defineMode = function(name, mode) {
- if (arguments.length > 2) {
- mode.dependencies = [];
- for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
- }
+ if (arguments.length > 2)
+ mode.dependencies = Array.prototype.slice.call(arguments, 2);
modes[name] = mode;
};
exports.defineMIME = function(mime, spec) { mimeModes[mime] = spec; };
@@ -107,6 +108,7 @@ exports.runMode = function(string, modespec, callback, options) {
for (var i = 0, e = lines.length; i < e; ++i) {
if (i) callback("\n");
var stream = new exports.StringStream(lines[i]);
+ if (!stream.string && mode.blankLine) mode.blankLine(state);
while (!stream.eol()) {
var style = mode.token(stream, state);
callback(stream.current(), style, i, stream.start, state);
diff --git a/applications/admin/static/codemirror/addon/scroll/annotatescrollbar.js b/applications/admin/static/codemirror/addon/scroll/annotatescrollbar.js
new file mode 100644
index 00000000..6dfff1a6
--- /dev/null
+++ b/applications/admin/static/codemirror/addon/scroll/annotatescrollbar.js
@@ -0,0 +1,76 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ CodeMirror.defineExtension("annotateScrollbar", function(className) {
+ return new Annotation(this, className);
+ });
+
+ function Annotation(cm, className) {
+ this.cm = cm;
+ this.className = className;
+ this.annotations = [];
+ this.div = cm.getWrapperElement().appendChild(document.createElement("div"));
+ this.div.style.cssText = "position: absolute; right: 0; top: 0; z-index: 7; pointer-events: none";
+ this.computeScale();
+
+ var self = this;
+ cm.on("refresh", this.resizeHandler = function(){
+ if (self.computeScale()) self.redraw();
+ });
+ }
+
+ Annotation.prototype.computeScale = function() {
+ var cm = this.cm;
+ var hScale = (cm.getWrapperElement().clientHeight - cm.display.barHeight) /
+ cm.heightAtLine(cm.lastLine() + 1, "local");
+ if (hScale != this.hScale) {
+ this.hScale = hScale;
+ return true;
+ }
+ };
+
+ Annotation.prototype.update = function(annotations) {
+ this.annotations = annotations;
+ this.redraw();
+ };
+
+ Annotation.prototype.redraw = function() {
+ var cm = this.cm, hScale = this.hScale;
+ if (!cm.display.barWidth) return;
+
+ var frag = document.createDocumentFragment(), anns = this.annotations;
+ for (var i = 0, nextTop; i < anns.length; i++) {
+ var ann = anns[i];
+ var top = nextTop || cm.charCoords(ann.from, "local").top * hScale;
+ var bottom = cm.charCoords(ann.to, "local").bottom * hScale;
+ while (i < anns.length - 1) {
+ nextTop = cm.charCoords(anns[i + 1].from, "local").top * hScale;
+ if (nextTop > bottom + .9) break;
+ ann = anns[++i];
+ bottom = cm.charCoords(ann.to, "local").bottom * hScale;
+ }
+ var height = Math.max(bottom - top, 3);
+
+ var elt = frag.appendChild(document.createElement("div"));
+ elt.style.cssText = "position: absolute; right: 0px; width: " + Math.max(cm.display.barWidth - 1, 2) + "px; top: " + top + "px; height: " + height + "px";
+ elt.className = this.className;
+ }
+ this.div.textContent = "";
+ this.div.appendChild(frag);
+ };
+
+ Annotation.prototype.clear = function() {
+ this.cm.off("refresh", this.resizeHandler);
+ this.div.parentNode.removeChild(this.div);
+ };
+});
diff --git a/applications/admin/static/codemirror/addon/scroll/scrollpastend.js b/applications/admin/static/codemirror/addon/scroll/scrollpastend.js
new file mode 100644
index 00000000..008ae4c7
--- /dev/null
+++ b/applications/admin/static/codemirror/addon/scroll/scrollpastend.js
@@ -0,0 +1,46 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ CodeMirror.defineOption("scrollPastEnd", false, function(cm, val, old) {
+ if (old && old != CodeMirror.Init) {
+ cm.off("change", onChange);
+ cm.off("refresh", updateBottomMargin);
+ cm.display.lineSpace.parentNode.style.paddingBottom = "";
+ cm.state.scrollPastEndPadding = null;
+ }
+ if (val) {
+ cm.on("change", onChange);
+ cm.on("refresh", updateBottomMargin);
+ updateBottomMargin(cm);
+ }
+ });
+
+ function onChange(cm, change) {
+ if (CodeMirror.changeEnd(change).line == cm.lastLine())
+ updateBottomMargin(cm);
+ }
+
+ function updateBottomMargin(cm) {
+ var padding = "";
+ if (cm.lineCount() > 1) {
+ var totalH = cm.display.scroller.clientHeight - 30,
+ lastLineH = cm.getLineHandle(cm.lastLine()).height;
+ padding = (totalH - lastLineH) + "px";
+ }
+ if (cm.state.scrollPastEndPadding != padding) {
+ cm.state.scrollPastEndPadding = padding;
+ cm.display.lineSpace.parentNode.style.paddingBottom = padding;
+ cm.setSize();
+ }
+ }
+});
diff --git a/applications/admin/static/codemirror/addon/scroll/simplescrollbars.css b/applications/admin/static/codemirror/addon/scroll/simplescrollbars.css
new file mode 100644
index 00000000..5eea7aa1
--- /dev/null
+++ b/applications/admin/static/codemirror/addon/scroll/simplescrollbars.css
@@ -0,0 +1,66 @@
+.CodeMirror-simplescroll-horizontal div, .CodeMirror-simplescroll-vertical div {
+ position: absolute;
+ background: #ccc;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ border: 1px solid #bbb;
+ border-radius: 2px;
+}
+
+.CodeMirror-simplescroll-horizontal, .CodeMirror-simplescroll-vertical {
+ position: absolute;
+ z-index: 6;
+ background: #eee;
+}
+
+.CodeMirror-simplescroll-horizontal {
+ bottom: 0; left: 0;
+ height: 8px;
+}
+.CodeMirror-simplescroll-horizontal div {
+ bottom: 0;
+ height: 100%;
+}
+
+.CodeMirror-simplescroll-vertical {
+ right: 0; top: 0;
+ width: 8px;
+}
+.CodeMirror-simplescroll-vertical div {
+ right: 0;
+ width: 100%;
+}
+
+
+.CodeMirror-overlayscroll .CodeMirror-scrollbar-filler, .CodeMirror-overlayscroll .CodeMirror-gutter-filler {
+ display: none;
+}
+
+.CodeMirror-overlayscroll-horizontal div, .CodeMirror-overlayscroll-vertical div {
+ position: absolute;
+ background: #bcd;
+ border-radius: 3px;
+}
+
+.CodeMirror-overlayscroll-horizontal, .CodeMirror-overlayscroll-vertical {
+ position: absolute;
+ z-index: 6;
+}
+
+.CodeMirror-overlayscroll-horizontal {
+ bottom: 0; left: 0;
+ height: 6px;
+}
+.CodeMirror-overlayscroll-horizontal div {
+ bottom: 0;
+ height: 100%;
+}
+
+.CodeMirror-overlayscroll-vertical {
+ right: 0; top: 0;
+ width: 6px;
+}
+.CodeMirror-overlayscroll-vertical div {
+ right: 0;
+ width: 100%;
+}
diff --git a/applications/admin/static/codemirror/addon/scroll/simplescrollbars.js b/applications/admin/static/codemirror/addon/scroll/simplescrollbars.js
new file mode 100644
index 00000000..bb06adb8
--- /dev/null
+++ b/applications/admin/static/codemirror/addon/scroll/simplescrollbars.js
@@ -0,0 +1,141 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ function Bar(cls, orientation, scroll) {
+ this.orientation = orientation;
+ this.scroll = scroll;
+ this.screen = this.total = this.size = 1;
+ this.pos = 0;
+
+ this.node = document.createElement("div");
+ this.node.className = cls + "-" + orientation;
+ this.inner = this.node.appendChild(document.createElement("div"));
+
+ var self = this;
+ CodeMirror.on(this.inner, "mousedown", function(e) {
+ if (e.which != 1) return;
+ CodeMirror.e_preventDefault(e);
+ var axis = self.orientation == "horizontal" ? "pageX" : "pageY";
+ var start = e[axis], startpos = self.pos;
+ function done() {
+ CodeMirror.off(document, "mousemove", move);
+ CodeMirror.off(document, "mouseup", done);
+ }
+ function move(e) {
+ if (e.which != 1) return done();
+ self.moveTo(startpos + (e[axis] - start) * (self.total / self.size));
+ }
+ CodeMirror.on(document, "mousemove", move);
+ CodeMirror.on(document, "mouseup", done);
+ });
+
+ CodeMirror.on(this.node, "click", function(e) {
+ CodeMirror.e_preventDefault(e);
+ var innerBox = self.inner.getBoundingClientRect(), where;
+ if (self.orientation == "horizontal")
+ where = e.clientX < innerBox.left ? -1 : e.clientX > innerBox.right ? 1 : 0;
+ else
+ where = e.clientY < innerBox.top ? -1 : e.clientY > innerBox.bottom ? 1 : 0;
+ self.moveTo(self.pos + where * self.screen);
+ });
+
+ function onWheel(e) {
+ var moved = CodeMirror.wheelEventPixels(e)[self.orientation == "horizontal" ? "x" : "y"];
+ var oldPos = self.pos;
+ self.moveTo(self.pos + moved);
+ if (self.pos != oldPos) CodeMirror.e_preventDefault(e);
+ }
+ CodeMirror.on(this.node, "mousewheel", onWheel);
+ CodeMirror.on(this.node, "DOMMouseScroll", onWheel);
+ }
+
+ Bar.prototype.moveTo = function(pos, update) {
+ if (pos < 0) pos = 0;
+ if (pos > this.total - this.screen) pos = this.total - this.screen;
+ if (pos == this.pos) return;
+ this.pos = pos;
+ this.inner.style[this.orientation == "horizontal" ? "left" : "top"] =
+ (pos * (this.size / this.total)) + "px";
+ if (update !== false) this.scroll(pos, this.orientation);
+ };
+
+ Bar.prototype.update = function(scrollSize, clientSize, barSize) {
+ this.screen = clientSize;
+ this.total = scrollSize;
+ this.size = barSize;
+
+ // FIXME clip to min size?
+ this.inner.style[this.orientation == "horizontal" ? "width" : "height"] =
+ this.screen * (this.size / this.total) + "px";
+ this.inner.style[this.orientation == "horizontal" ? "left" : "top"] =
+ this.pos * (this.size / this.total) + "px";
+ };
+
+ function SimpleScrollbars(cls, place, scroll) {
+ this.addClass = cls;
+ this.horiz = new Bar(cls, "horizontal", scroll);
+ place(this.horiz.node);
+ this.vert = new Bar(cls, "vertical", scroll);
+ place(this.vert.node);
+ this.width = null;
+ }
+
+ SimpleScrollbars.prototype.update = function(measure) {
+ if (this.width == null) {
+ var style = window.getComputedStyle ? window.getComputedStyle(this.horiz.node) : this.horiz.node.currentStyle;
+ if (style) this.width = parseInt(style.height);
+ }
+ var width = this.width || 0;
+
+ var needsH = measure.scrollWidth > measure.clientWidth + 1;
+ var needsV = measure.scrollHeight > measure.clientHeight + 1;
+ this.vert.node.style.display = needsV ? "block" : "none";
+ this.horiz.node.style.display = needsH ? "block" : "none";
+
+ if (needsV) {
+ this.vert.update(measure.scrollHeight, measure.clientHeight,
+ measure.viewHeight - (needsH ? width : 0));
+ this.vert.node.style.display = "block";
+ this.vert.node.style.bottom = needsH ? width + "px" : "0";
+ }
+ if (needsH) {
+ this.horiz.update(measure.scrollWidth, measure.clientWidth,
+ measure.viewWidth - (needsV ? width : 0) - measure.barLeft);
+ this.horiz.node.style.right = needsV ? width + "px" : "0";
+ this.horiz.node.style.left = measure.barLeft + "px";
+ }
+
+ return {right: needsV ? width : 0, bottom: needsH ? width : 0};
+ };
+
+ SimpleScrollbars.prototype.setScrollTop = function(pos) {
+ this.vert.moveTo(pos, false);
+ };
+
+ SimpleScrollbars.prototype.setScrollLeft = function(pos) {
+ this.horiz.moveTo(pos, false);
+ };
+
+ SimpleScrollbars.prototype.clear = function() {
+ var parent = this.horiz.node.parentNode;
+ parent.removeChild(this.horiz.node);
+ parent.removeChild(this.vert.node);
+ };
+
+ CodeMirror.scrollbarModel.simple = function(place, scroll) {
+ return new SimpleScrollbars("CodeMirror-simplescroll", place, scroll);
+ };
+ CodeMirror.scrollbarModel.overlay = function(place, scroll) {
+ return new SimpleScrollbars("CodeMirror-overlayscroll", place, scroll);
+ };
+});
diff --git a/applications/admin/static/codemirror/addon/search/match-highlighter.js b/applications/admin/static/codemirror/addon/search/match-highlighter.js
index d9c818b8..e9a22721 100644
--- a/applications/admin/static/codemirror/addon/search/match-highlighter.js
+++ b/applications/admin/static/codemirror/addon/search/match-highlighter.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
// Highlighting text that matches the selection
//
// Defines an option highlightSelectionMatches, which, when enabled,
@@ -5,12 +8,15 @@
// document.
//
// The option can be set to true to simply enable it, or to a
-// {minChars, style, showToken} object to explicitly configure it.
-// minChars is the minimum amount of characters that should be
+// {minChars, style, wordsOnly, showToken, delay} object to explicitly
+// configure it. minChars is the minimum amount of characters that should be
// selected for the behavior to occur, and style is the token style to
// apply to the matches. This will be prefixed by "cm-" to create an
-// actual CSS class name. showToken, when enabled, will cause the
-// current token to be highlighted when nothing is selected.
+// actual CSS class name. If wordsOnly is enabled, the matches will be
+// highlighted only if the selected text is a word. showToken, when enabled,
+// will cause the current token to be highlighted when nothing is selected.
+// delay is used to specify how much time to wait, in milliseconds, before
+// highlighting the matches.
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -25,6 +31,7 @@
var DEFAULT_MIN_CHARS = 2;
var DEFAULT_TOKEN_STYLE = "matchhighlight";
var DEFAULT_DELAY = 100;
+ var DEFAULT_WORDS_ONLY = false;
function State(options) {
if (typeof options == "object") {
@@ -32,10 +39,12 @@
this.style = options.style;
this.showToken = options.showToken;
this.delay = options.delay;
+ this.wordsOnly = options.wordsOnly;
}
if (this.style == null) this.style = DEFAULT_TOKEN_STYLE;
if (this.minChars == null) this.minChars = DEFAULT_MIN_CHARS;
if (this.delay == null) this.delay = DEFAULT_DELAY;
+ if (this.wordsOnly == null) this.wordsOnly = DEFAULT_WORDS_ONLY;
this.overlay = this.timeout = null;
}
@@ -76,13 +85,32 @@
cm.addOverlay(state.overlay = makeOverlay(line.slice(start, end), re, state.style));
return;
}
- if (cm.getCursor("head").line != cm.getCursor("anchor").line) return;
- var selection = cm.getSelections()[0].replace(/^\s+|\s+$/g, "");
+ var from = cm.getCursor("from"), to = cm.getCursor("to");
+ if (from.line != to.line) return;
+ if (state.wordsOnly && !isWord(cm, from, to)) return;
+ var selection = cm.getRange(from, to).replace(/^\s+|\s+$/g, "");
if (selection.length >= state.minChars)
cm.addOverlay(state.overlay = makeOverlay(selection, false, state.style));
});
}
+ function isWord(cm, from, to) {
+ var str = cm.getRange(from, to);
+ if (str.match(/^\w+$/) !== null) {
+ if (from.ch > 0) {
+ var pos = {line: from.line, ch: from.ch - 1};
+ var chr = cm.getRange(pos, from);
+ if (chr.match(/\W/) === null) return false;
+ }
+ if (to.ch < cm.getLine(from.line).length) {
+ var pos = {line: to.line, ch: to.ch + 1};
+ var chr = cm.getRange(to, pos);
+ if (chr.match(/\W/) === null) return false;
+ }
+ return true;
+ } else return false;
+ }
+
function boundariesAround(stream, re) {
return (!stream.start || !re.test(stream.string.charAt(stream.start - 1))) &&
(stream.pos == stream.string.length || !re.test(stream.string.charAt(stream.pos)));
diff --git a/applications/admin/static/codemirror/addon/search/matchesonscrollbar.css b/applications/admin/static/codemirror/addon/search/matchesonscrollbar.css
new file mode 100644
index 00000000..77932cc9
--- /dev/null
+++ b/applications/admin/static/codemirror/addon/search/matchesonscrollbar.css
@@ -0,0 +1,8 @@
+.CodeMirror-search-match {
+ background: gold;
+ border-top: 1px solid orange;
+ border-bottom: 1px solid orange;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ opacity: .5;
+}
diff --git a/applications/admin/static/codemirror/addon/search/matchesonscrollbar.js b/applications/admin/static/codemirror/addon/search/matchesonscrollbar.js
new file mode 100644
index 00000000..937d3f78
--- /dev/null
+++ b/applications/admin/static/codemirror/addon/search/matchesonscrollbar.js
@@ -0,0 +1,90 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"), require("./searchcursor"), require("../scroll/annotatescrollbar"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror", "./searchcursor", "../scroll/annotatescrollbar"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ CodeMirror.defineExtension("showMatchesOnScrollbar", function(query, caseFold, className) {
+ return new SearchAnnotation(this, query, caseFold, className);
+ });
+
+ function SearchAnnotation(cm, query, caseFold, className) {
+ this.cm = cm;
+ this.annotation = cm.annotateScrollbar(className || "CodeMirror-search-match");
+ this.query = query;
+ this.caseFold = caseFold;
+ this.gap = {from: cm.firstLine(), to: cm.lastLine() + 1};
+ this.matches = [];
+ this.update = null;
+
+ this.findMatches();
+ this.annotation.update(this.matches);
+
+ var self = this;
+ cm.on("change", this.changeHandler = function(_cm, change) { self.onChange(change); });
+ }
+
+ var MAX_MATCHES = 1000;
+
+ SearchAnnotation.prototype.findMatches = function() {
+ if (!this.gap) return;
+ for (var i = 0; i < this.matches.length; i++) {
+ var match = this.matches[i];
+ if (match.from.line >= this.gap.to) break;
+ if (match.to.line >= this.gap.from) this.matches.splice(i--, 1);
+ }
+ var cursor = this.cm.getSearchCursor(this.query, CodeMirror.Pos(this.gap.from, 0), this.caseFold);
+ while (cursor.findNext()) {
+ var match = {from: cursor.from(), to: cursor.to()};
+ if (match.from.line >= this.gap.to) break;
+ this.matches.splice(i++, 0, match);
+ if (this.matches.length > MAX_MATCHES) break;
+ }
+ this.gap = null;
+ };
+
+ function offsetLine(line, changeStart, sizeChange) {
+ if (line <= changeStart) return line;
+ return Math.max(changeStart, line + sizeChange);
+ }
+
+ SearchAnnotation.prototype.onChange = function(change) {
+ var startLine = change.from.line;
+ var endLine = CodeMirror.changeEnd(change).line;
+ var sizeChange = endLine - change.to.line;
+ if (this.gap) {
+ this.gap.from = Math.min(offsetLine(this.gap.from, startLine, sizeChange), change.from.line);
+ this.gap.to = Math.max(offsetLine(this.gap.to, startLine, sizeChange), change.from.line);
+ } else {
+ this.gap = {from: change.from.line, to: endLine + 1};
+ }
+
+ if (sizeChange) for (var i = 0; i < this.matches.length; i++) {
+ var match = this.matches[i];
+ var newFrom = offsetLine(match.from.line, startLine, sizeChange);
+ if (newFrom != match.from.line) match.from = CodeMirror.Pos(newFrom, match.from.ch);
+ var newTo = offsetLine(match.to.line, startLine, sizeChange);
+ if (newTo != match.to.line) match.to = CodeMirror.Pos(newTo, match.to.ch);
+ }
+ clearTimeout(this.update);
+ var self = this;
+ this.update = setTimeout(function() { self.updateAfterChange(); }, 250);
+ };
+
+ SearchAnnotation.prototype.updateAfterChange = function() {
+ this.findMatches();
+ this.annotation.update(this.matches);
+ };
+
+ SearchAnnotation.prototype.clear = function() {
+ this.cm.off("change", this.changeHandler);
+ this.annotation.clear();
+ };
+});
diff --git a/applications/admin/static/codemirror/addon/search/search.js b/applications/admin/static/codemirror/addon/search/search.js
index 19f51f1d..0251067a 100644
--- a/applications/admin/static/codemirror/addon/search/search.js
+++ b/applications/admin/static/codemirror/addon/search/search.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
// Define search commands. Depends on dialog.js or another
// implementation of the openDialog method.
@@ -16,21 +19,21 @@
})(function(CodeMirror) {
"use strict";
function searchOverlay(query, caseInsensitive) {
- var startChar;
- if (typeof query == "string") {
- startChar = query.charAt(0);
- query = new RegExp("^" + query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"),
- caseInsensitive ? "i" : "");
- } else {
- query = new RegExp("^(?:" + query.source + ")", query.ignoreCase ? "i" : "");
- }
+ if (typeof query == "string")
+ query = new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), caseInsensitive ? "gi" : "g");
+ else if (!query.global)
+ query = new RegExp(query.source, query.ignoreCase ? "gi" : "g");
+
return {token: function(stream) {
- if (stream.match(query)) return "searching";
- while (!stream.eol()) {
- stream.next();
- if (startChar && !caseInsensitive)
- stream.skipTo(startChar) || stream.skipToEnd();
- if (stream.match(query, false)) break;
+ query.lastIndex = stream.pos;
+ var match = query.exec(stream.string);
+ if (match && match.index == stream.pos) {
+ stream.pos += match[0].length;
+ return "searching";
+ } else if (match) {
+ stream.pos = match.index;
+ } else {
+ stream.skipToEnd();
}
}};
}
@@ -60,15 +63,15 @@
function parseQuery(query) {
var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
if (isRE) {
- query = new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i");
- if (query.test("")) query = /x^/;
- } else if (query == "") {
- query = /x^/;
+ try { query = new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i"); }
+ catch(e) {} // Not a regular expression after all, do a string search
}
+ if (typeof query == "string" ? query == "" : query.test(""))
+ query = /x^/;
return query;
}
var queryDialog =
- 'Search: (Use /re/ syntax for regexp search) ';
+ 'Search: (Use /re/ syntax for regexp search) ';
function doSearch(cm, rev) {
var state = getSearchState(cm);
if (state.query) return findNext(cm, rev);
@@ -79,6 +82,10 @@
cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));
state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query));
cm.addOverlay(state.overlay);
+ if (cm.showMatchesOnScrollbar) {
+ if (state.annotate) { state.annotate.clear(); state.annotate = null; }
+ state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query));
+ }
state.posFrom = state.posTo = cm.getCursor();
findNext(cm, rev);
});
@@ -100,13 +107,15 @@
if (!state.query) return;
state.query = null;
cm.removeOverlay(state.overlay);
+ if (state.annotate) { state.annotate.clear(); state.annotate = null; }
});}
var replaceQueryDialog =
- 'Replace: (Use /re/ syntax for regexp search) ';
- var replacementQueryDialog = 'With: ';
+ 'Replace: (Use /re/ syntax for regexp search) ';
+ var replacementQueryDialog = 'With: ';
var doReplaceConfirm = "Replace? Yes No Stop ";
function replace(cm, all) {
+ if (cm.getOption("readOnly")) return;
dialog(cm, replaceQueryDialog, "Replace:", cm.getSelection(), function(query) {
if (!query) return;
query = parseQuery(query);
diff --git a/applications/admin/static/codemirror/addon/search/searchcursor.js b/applications/admin/static/codemirror/addon/search/searchcursor.js
index 899f44c4..55c108b5 100644
--- a/applications/admin/static/codemirror/addon/search/searchcursor.js
+++ b/applications/admin/static/codemirror/addon/search/searchcursor.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
@@ -104,7 +107,7 @@
var from = Pos(pos.line, cut);
for (var ln = pos.line + 1, i = 1; i < last; ++i, ++ln)
if (target[i] != fold(doc.getLine(ln))) return;
- if (doc.getLine(ln).slice(0, origTarget[last].length) != target[last]) return;
+ if (fold(doc.getLine(ln).slice(0, origTarget[last].length)) != target[last]) return;
return {from: from, to: Pos(ln, origTarget[last].length)};
}
};
diff --git a/applications/admin/static/codemirror/addon/selection/active-line.js b/applications/admin/static/codemirror/addon/selection/active-line.js
index a818f109..22da2e0a 100644
--- a/applications/admin/static/codemirror/addon/selection/active-line.js
+++ b/applications/admin/static/codemirror/addon/selection/active-line.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
// Because sometimes you need to style the cursor's line.
//
// Adds an option 'styleActiveLine' which, when enabled, gives the
@@ -46,7 +49,9 @@
function updateActiveLines(cm, ranges) {
var active = [];
for (var i = 0; i < ranges.length; i++) {
- var line = cm.getLineHandleVisualStart(ranges[i].head.line);
+ var range = ranges[i];
+ if (!range.empty()) continue;
+ var line = cm.getLineHandleVisualStart(range.head.line);
if (active[active.length - 1] != line) active.push(line);
}
if (sameArray(cm.state.activeLines, active)) return;
diff --git a/applications/admin/static/codemirror/addon/selection/mark-selection.js b/applications/admin/static/codemirror/addon/selection/mark-selection.js
index ae0d3931..5c42d21e 100644
--- a/applications/admin/static/codemirror/addon/selection/mark-selection.js
+++ b/applications/admin/static/codemirror/addon/selection/mark-selection.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
// Because sometimes you need to mark the selected *text*.
//
// Adds an option 'styleSelectedText' which, when enabled, gives
diff --git a/applications/admin/static/codemirror/addon/selection/selection-pointer.js b/applications/admin/static/codemirror/addon/selection/selection-pointer.js
new file mode 100644
index 00000000..8cc0fc68
--- /dev/null
+++ b/applications/admin/static/codemirror/addon/selection/selection-pointer.js
@@ -0,0 +1,95 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ CodeMirror.defineOption("selectionPointer", false, function(cm, val) {
+ var data = cm.state.selectionPointer;
+ if (data) {
+ CodeMirror.off(cm.getWrapperElement(), "mousemove", data.mousemove);
+ CodeMirror.off(cm.getWrapperElement(), "mouseout", data.mouseout);
+ cm.off("cursorActivity", reset);
+ cm.off("scroll", reset);
+ cm.state.selectionPointer = null;
+ cm.display.lineDiv.style.cursor = "";
+ }
+ if (val) {
+ data = cm.state.selectionPointer = {
+ value: typeof val == "string" ? val : "default",
+ mousemove: function(event) { mousemove(cm, event); },
+ mouseout: function(event) { mouseout(cm, event); },
+ rects: null,
+ mouseX: null, mouseY: null,
+ willUpdate: false
+ };
+ CodeMirror.on(cm.getWrapperElement(), "mousemove", data.mousemove);
+ CodeMirror.on(cm.getWrapperElement(), "mouseout", data.mouseout);
+ cm.on("cursorActivity", reset);
+ cm.on("scroll", reset);
+ }
+ });
+
+ function mousemove(cm, event) {
+ var data = cm.state.selectionPointer;
+ if (event.buttons == null ? event.which : event.buttons) {
+ data.mouseX = data.mouseY = null;
+ } else {
+ data.mouseX = event.clientX;
+ data.mouseY = event.clientY;
+ }
+ scheduleUpdate(cm);
+ }
+
+ function mouseout(cm, event) {
+ if (!cm.getWrapperElement().contains(event.relatedTarget)) {
+ var data = cm.state.selectionPointer;
+ data.mouseX = data.mouseY = null;
+ scheduleUpdate(cm);
+ }
+ }
+
+ function reset(cm) {
+ cm.state.selectionPointer.rects = null;
+ scheduleUpdate(cm);
+ }
+
+ function scheduleUpdate(cm) {
+ if (!cm.state.selectionPointer.willUpdate) {
+ cm.state.selectionPointer.willUpdate = true;
+ setTimeout(function() {
+ update(cm);
+ cm.state.selectionPointer.willUpdate = false;
+ }, 50);
+ }
+ }
+
+ function update(cm) {
+ var data = cm.state.selectionPointer;
+ if (!data) return;
+ if (data.rects == null && data.mouseX != null) {
+ data.rects = [];
+ if (cm.somethingSelected()) {
+ for (var sel = cm.display.selectionDiv.firstChild; sel; sel = sel.nextSibling)
+ data.rects.push(sel.getBoundingClientRect());
+ }
+ }
+ var inside = false;
+ if (data.mouseX != null) for (var i = 0; i < data.rects.length; i++) {
+ var rect = data.rects[i];
+ if (rect.left <= data.mouseX && rect.right >= data.mouseX &&
+ rect.top <= data.mouseY && rect.bottom >= data.mouseY)
+ inside = true;
+ }
+ var cursor = inside ? data.value : "";
+ if (cm.display.lineDiv.style.cursor != cursor)
+ cm.display.lineDiv.style.cursor = cursor;
+ }
+});
diff --git a/applications/admin/static/codemirror/addon/tern/tern.css b/applications/admin/static/codemirror/addon/tern/tern.css
new file mode 100644
index 00000000..76fba33d
--- /dev/null
+++ b/applications/admin/static/codemirror/addon/tern/tern.css
@@ -0,0 +1,86 @@
+.CodeMirror-Tern-completion {
+ padding-left: 22px;
+ position: relative;
+}
+.CodeMirror-Tern-completion:before {
+ position: absolute;
+ left: 2px;
+ bottom: 2px;
+ border-radius: 50%;
+ font-size: 12px;
+ font-weight: bold;
+ height: 15px;
+ width: 15px;
+ line-height: 16px;
+ text-align: center;
+ color: white;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+.CodeMirror-Tern-completion-unknown:before {
+ content: "?";
+ background: #4bb;
+}
+.CodeMirror-Tern-completion-object:before {
+ content: "O";
+ background: #77c;
+}
+.CodeMirror-Tern-completion-fn:before {
+ content: "F";
+ background: #7c7;
+}
+.CodeMirror-Tern-completion-array:before {
+ content: "A";
+ background: #c66;
+}
+.CodeMirror-Tern-completion-number:before {
+ content: "1";
+ background: #999;
+}
+.CodeMirror-Tern-completion-string:before {
+ content: "S";
+ background: #999;
+}
+.CodeMirror-Tern-completion-bool:before {
+ content: "B";
+ background: #999;
+}
+
+.CodeMirror-Tern-completion-guess {
+ color: #999;
+}
+
+.CodeMirror-Tern-tooltip {
+ border: 1px solid silver;
+ border-radius: 3px;
+ color: #444;
+ padding: 2px 5px;
+ font-size: 90%;
+ font-family: monospace;
+ background-color: white;
+ white-space: pre-wrap;
+
+ max-width: 40em;
+ position: absolute;
+ z-index: 10;
+ -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+ -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+ box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+
+ transition: opacity 1s;
+ -moz-transition: opacity 1s;
+ -webkit-transition: opacity 1s;
+ -o-transition: opacity 1s;
+ -ms-transition: opacity 1s;
+}
+
+.CodeMirror-Tern-hint-doc {
+ max-width: 25em;
+ margin-top: -3px;
+}
+
+.CodeMirror-Tern-fname { color: black; }
+.CodeMirror-Tern-farg { color: #70a; }
+.CodeMirror-Tern-farg-current { text-decoration: underline; }
+.CodeMirror-Tern-type { color: #07c; }
+.CodeMirror-Tern-fhint-guess { opacity: .7; }
diff --git a/applications/admin/static/codemirror/addon/tern/tern.js b/applications/admin/static/codemirror/addon/tern/tern.js
new file mode 100644
index 00000000..86729e2d
--- /dev/null
+++ b/applications/admin/static/codemirror/addon/tern/tern.js
@@ -0,0 +1,670 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+// Glue code between CodeMirror and Tern.
+//
+// Create a CodeMirror.TernServer to wrap an actual Tern server,
+// register open documents (CodeMirror.Doc instances) with it, and
+// call its methods to activate the assisting functions that Tern
+// provides.
+//
+// Options supported (all optional):
+// * defs: An array of JSON definition data structures.
+// * plugins: An object mapping plugin names to configuration
+// options.
+// * getFile: A function(name, c) that can be used to access files in
+// the project that haven't been loaded yet. Simply do c(null) to
+// indicate that a file is not available.
+// * fileFilter: A function(value, docName, doc) that will be applied
+// to documents before passing them on to Tern.
+// * switchToDoc: A function(name, doc) that should, when providing a
+// multi-file view, switch the view or focus to the named file.
+// * showError: A function(editor, message) that can be used to
+// override the way errors are displayed.
+// * completionTip: Customize the content in tooltips for completions.
+// Is passed a single argument—the completion's data as returned by
+// Tern—and may return a string, DOM node, or null to indicate that
+// no tip should be shown. By default the docstring is shown.
+// * typeTip: Like completionTip, but for the tooltips shown for type
+// queries.
+// * responseFilter: A function(doc, query, request, error, data) that
+// will be applied to the Tern responses before treating them
+//
+//
+// It is possible to run the Tern server in a web worker by specifying
+// these additional options:
+// * useWorker: Set to true to enable web worker mode. You'll probably
+// want to feature detect the actual value you use here, for example
+// !!window.Worker.
+// * workerScript: The main script of the worker. Point this to
+// wherever you are hosting worker.js from this directory.
+// * workerDeps: An array of paths pointing (relative to workerScript)
+// to the Acorn and Tern libraries and any Tern plugins you want to
+// load. Or, if you minified those into a single script and included
+// them in the workerScript, simply leave this undefined.
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+ // declare global: tern
+
+ CodeMirror.TernServer = function(options) {
+ var self = this;
+ this.options = options || {};
+ var plugins = this.options.plugins || (this.options.plugins = {});
+ if (!plugins.doc_comment) plugins.doc_comment = true;
+ if (this.options.useWorker) {
+ this.server = new WorkerServer(this);
+ } else {
+ this.server = new tern.Server({
+ getFile: function(name, c) { return getFile(self, name, c); },
+ async: true,
+ defs: this.options.defs || [],
+ plugins: plugins
+ });
+ }
+ this.docs = Object.create(null);
+ this.trackChange = function(doc, change) { trackChange(self, doc, change); };
+
+ this.cachedArgHints = null;
+ this.activeArgHints = null;
+ this.jumpStack = [];
+
+ this.getHint = function(cm, c) { return hint(self, cm, c); };
+ this.getHint.async = true;
+ };
+
+ CodeMirror.TernServer.prototype = {
+ addDoc: function(name, doc) {
+ var data = {doc: doc, name: name, changed: null};
+ this.server.addFile(name, docValue(this, data));
+ CodeMirror.on(doc, "change", this.trackChange);
+ return this.docs[name] = data;
+ },
+
+ delDoc: function(id) {
+ var found = resolveDoc(this, id);
+ if (!found) return;
+ CodeMirror.off(found.doc, "change", this.trackChange);
+ delete this.docs[found.name];
+ this.server.delFile(found.name);
+ },
+
+ hideDoc: function(id) {
+ closeArgHints(this);
+ var found = resolveDoc(this, id);
+ if (found && found.changed) sendDoc(this, found);
+ },
+
+ complete: function(cm) {
+ cm.showHint({hint: this.getHint});
+ },
+
+ showType: function(cm, pos, c) { showContextInfo(this, cm, pos, "type", c); },
+
+ showDocs: function(cm, pos, c) { showContextInfo(this, cm, pos, "documentation", c); },
+
+ updateArgHints: function(cm) { updateArgHints(this, cm); },
+
+ jumpToDef: function(cm) { jumpToDef(this, cm); },
+
+ jumpBack: function(cm) { jumpBack(this, cm); },
+
+ rename: function(cm) { rename(this, cm); },
+
+ selectName: function(cm) { selectName(this, cm); },
+
+ request: function (cm, query, c, pos) {
+ var self = this;
+ var doc = findDoc(this, cm.getDoc());
+ var request = buildRequest(this, doc, query, pos);
+
+ this.server.request(request, function (error, data) {
+ if (!error && self.options.responseFilter)
+ data = self.options.responseFilter(doc, query, request, error, data);
+ c(error, data);
+ });
+ }
+ };
+
+ var Pos = CodeMirror.Pos;
+ var cls = "CodeMirror-Tern-";
+ var bigDoc = 250;
+
+ function getFile(ts, name, c) {
+ var buf = ts.docs[name];
+ if (buf)
+ c(docValue(ts, buf));
+ else if (ts.options.getFile)
+ ts.options.getFile(name, c);
+ else
+ c(null);
+ }
+
+ function findDoc(ts, doc, name) {
+ for (var n in ts.docs) {
+ var cur = ts.docs[n];
+ if (cur.doc == doc) return cur;
+ }
+ if (!name) for (var i = 0;; ++i) {
+ n = "[doc" + (i || "") + "]";
+ if (!ts.docs[n]) { name = n; break; }
+ }
+ return ts.addDoc(name, doc);
+ }
+
+ function resolveDoc(ts, id) {
+ if (typeof id == "string") return ts.docs[id];
+ if (id instanceof CodeMirror) id = id.getDoc();
+ if (id instanceof CodeMirror.Doc) return findDoc(ts, id);
+ }
+
+ function trackChange(ts, doc, change) {
+ var data = findDoc(ts, doc);
+
+ var argHints = ts.cachedArgHints;
+ if (argHints && argHints.doc == doc && cmpPos(argHints.start, change.to) <= 0)
+ ts.cachedArgHints = null;
+
+ var changed = data.changed;
+ if (changed == null)
+ data.changed = changed = {from: change.from.line, to: change.from.line};
+ var end = change.from.line + (change.text.length - 1);
+ if (change.from.line < changed.to) changed.to = changed.to - (change.to.line - end);
+ if (end >= changed.to) changed.to = end + 1;
+ if (changed.from > change.from.line) changed.from = change.from.line;
+
+ if (doc.lineCount() > bigDoc && change.to - changed.from > 100) setTimeout(function() {
+ if (data.changed && data.changed.to - data.changed.from > 100) sendDoc(ts, data);
+ }, 200);
+ }
+
+ function sendDoc(ts, doc) {
+ ts.server.request({files: [{type: "full", name: doc.name, text: docValue(ts, doc)}]}, function(error) {
+ if (error) window.console.error(error);
+ else doc.changed = null;
+ });
+ }
+
+ // Completion
+
+ function hint(ts, cm, c) {
+ ts.request(cm, {type: "completions", types: true, docs: true, urls: true}, function(error, data) {
+ if (error) return showError(ts, cm, error);
+ var completions = [], after = "";
+ var from = data.start, to = data.end;
+ if (cm.getRange(Pos(from.line, from.ch - 2), from) == "[\"" &&
+ cm.getRange(to, Pos(to.line, to.ch + 2)) != "\"]")
+ after = "\"]";
+
+ for (var i = 0; i < data.completions.length; ++i) {
+ var completion = data.completions[i], className = typeToIcon(completion.type);
+ if (data.guess) className += " " + cls + "guess";
+ completions.push({text: completion.name + after,
+ displayText: completion.name,
+ className: className,
+ data: completion});
+ }
+
+ var obj = {from: from, to: to, list: completions};
+ var tooltip = null;
+ CodeMirror.on(obj, "close", function() { remove(tooltip); });
+ CodeMirror.on(obj, "update", function() { remove(tooltip); });
+ CodeMirror.on(obj, "select", function(cur, node) {
+ remove(tooltip);
+ var content = ts.options.completionTip ? ts.options.completionTip(cur.data) : cur.data.doc;
+ if (content) {
+ tooltip = makeTooltip(node.parentNode.getBoundingClientRect().right + window.pageXOffset,
+ node.getBoundingClientRect().top + window.pageYOffset, content);
+ tooltip.className += " " + cls + "hint-doc";
+ }
+ });
+ c(obj);
+ });
+ }
+
+ function typeToIcon(type) {
+ var suffix;
+ if (type == "?") suffix = "unknown";
+ else if (type == "number" || type == "string" || type == "bool") suffix = type;
+ else if (/^fn\(/.test(type)) suffix = "fn";
+ else if (/^\[/.test(type)) suffix = "array";
+ else suffix = "object";
+ return cls + "completion " + cls + "completion-" + suffix;
+ }
+
+ // Type queries
+
+ function showContextInfo(ts, cm, pos, queryName, c) {
+ ts.request(cm, queryName, function(error, data) {
+ if (error) return showError(ts, cm, error);
+ if (ts.options.typeTip) {
+ var tip = ts.options.typeTip(data);
+ } else {
+ var tip = elt("span", null, elt("strong", null, data.type || "not found"));
+ if (data.doc)
+ tip.appendChild(document.createTextNode(" — " + data.doc));
+ if (data.url) {
+ tip.appendChild(document.createTextNode(" "));
+ tip.appendChild(elt("a", null, "[docs]")).href = data.url;
+ }
+ }
+ tempTooltip(cm, tip);
+ if (c) c();
+ }, pos);
+ }
+
+ // Maintaining argument hints
+
+ function updateArgHints(ts, cm) {
+ closeArgHints(ts);
+
+ if (cm.somethingSelected()) return;
+ var state = cm.getTokenAt(cm.getCursor()).state;
+ var inner = CodeMirror.innerMode(cm.getMode(), state);
+ if (inner.mode.name != "javascript") return;
+ var lex = inner.state.lexical;
+ if (lex.info != "call") return;
+
+ var ch, argPos = lex.pos || 0, tabSize = cm.getOption("tabSize");
+ for (var line = cm.getCursor().line, e = Math.max(0, line - 9), found = false; line >= e; --line) {
+ var str = cm.getLine(line), extra = 0;
+ for (var pos = 0;;) {
+ var tab = str.indexOf("\t", pos);
+ if (tab == -1) break;
+ extra += tabSize - (tab + extra) % tabSize - 1;
+ pos = tab + 1;
+ }
+ ch = lex.column - extra;
+ if (str.charAt(ch) == "(") {found = true; break;}
+ }
+ if (!found) return;
+
+ var start = Pos(line, ch);
+ var cache = ts.cachedArgHints;
+ if (cache && cache.doc == cm.getDoc() && cmpPos(start, cache.start) == 0)
+ return showArgHints(ts, cm, argPos);
+
+ ts.request(cm, {type: "type", preferFunction: true, end: start}, function(error, data) {
+ if (error || !data.type || !(/^fn\(/).test(data.type)) return;
+ ts.cachedArgHints = {
+ start: pos,
+ type: parseFnType(data.type),
+ name: data.exprName || data.name || "fn",
+ guess: data.guess,
+ doc: cm.getDoc()
+ };
+ showArgHints(ts, cm, argPos);
+ });
+ }
+
+ function showArgHints(ts, cm, pos) {
+ closeArgHints(ts);
+
+ var cache = ts.cachedArgHints, tp = cache.type;
+ var tip = elt("span", cache.guess ? cls + "fhint-guess" : null,
+ elt("span", cls + "fname", cache.name), "(");
+ for (var i = 0; i < tp.args.length; ++i) {
+ if (i) tip.appendChild(document.createTextNode(", "));
+ var arg = tp.args[i];
+ tip.appendChild(elt("span", cls + "farg" + (i == pos ? " " + cls + "farg-current" : ""), arg.name || "?"));
+ if (arg.type != "?") {
+ tip.appendChild(document.createTextNode(":\u00a0"));
+ tip.appendChild(elt("span", cls + "type", arg.type));
+ }
+ }
+ tip.appendChild(document.createTextNode(tp.rettype ? ") ->\u00a0" : ")"));
+ if (tp.rettype) tip.appendChild(elt("span", cls + "type", tp.rettype));
+ var place = cm.cursorCoords(null, "page");
+ ts.activeArgHints = makeTooltip(place.right + 1, place.bottom, tip);
+ }
+
+ function parseFnType(text) {
+ var args = [], pos = 3;
+
+ function skipMatching(upto) {
+ var depth = 0, start = pos;
+ for (;;) {
+ var next = text.charAt(pos);
+ if (upto.test(next) && !depth) return text.slice(start, pos);
+ if (/[{\[\(]/.test(next)) ++depth;
+ else if (/[}\]\)]/.test(next)) --depth;
+ ++pos;
+ }
+ }
+
+ // Parse arguments
+ if (text.charAt(pos) != ")") for (;;) {
+ var name = text.slice(pos).match(/^([^, \(\[\{]+): /);
+ if (name) {
+ pos += name[0].length;
+ name = name[1];
+ }
+ args.push({name: name, type: skipMatching(/[\),]/)});
+ if (text.charAt(pos) == ")") break;
+ pos += 2;
+ }
+
+ var rettype = text.slice(pos).match(/^\) -> (.*)$/);
+
+ return {args: args, rettype: rettype && rettype[1]};
+ }
+
+ // Moving to the definition of something
+
+ function jumpToDef(ts, cm) {
+ function inner(varName) {
+ var req = {type: "definition", variable: varName || null};
+ var doc = findDoc(ts, cm.getDoc());
+ ts.server.request(buildRequest(ts, doc, req), function(error, data) {
+ if (error) return showError(ts, cm, error);
+ if (!data.file && data.url) { window.open(data.url); return; }
+
+ if (data.file) {
+ var localDoc = ts.docs[data.file], found;
+ if (localDoc && (found = findContext(localDoc.doc, data))) {
+ ts.jumpStack.push({file: doc.name,
+ start: cm.getCursor("from"),
+ end: cm.getCursor("to")});
+ moveTo(ts, doc, localDoc, found.start, found.end);
+ return;
+ }
+ }
+ showError(ts, cm, "Could not find a definition.");
+ });
+ }
+
+ if (!atInterestingExpression(cm))
+ dialog(cm, "Jump to variable", function(name) { if (name) inner(name); });
+ else
+ inner();
+ }
+
+ function jumpBack(ts, cm) {
+ var pos = ts.jumpStack.pop(), doc = pos && ts.docs[pos.file];
+ if (!doc) return;
+ moveTo(ts, findDoc(ts, cm.getDoc()), doc, pos.start, pos.end);
+ }
+
+ function moveTo(ts, curDoc, doc, start, end) {
+ doc.doc.setSelection(start, end);
+ if (curDoc != doc && ts.options.switchToDoc) {
+ closeArgHints(ts);
+ ts.options.switchToDoc(doc.name, doc.doc);
+ }
+ }
+
+ // The {line,ch} representation of positions makes this rather awkward.
+ function findContext(doc, data) {
+ var before = data.context.slice(0, data.contextOffset).split("\n");
+ var startLine = data.start.line - (before.length - 1);
+ var start = Pos(startLine, (before.length == 1 ? data.start.ch : doc.getLine(startLine).length) - before[0].length);
+
+ var text = doc.getLine(startLine).slice(start.ch);
+ for (var cur = startLine + 1; cur < doc.lineCount() && text.length < data.context.length; ++cur)
+ text += "\n" + doc.getLine(cur);
+ if (text.slice(0, data.context.length) == data.context) return data;
+
+ var cursor = doc.getSearchCursor(data.context, 0, false);
+ var nearest, nearestDist = Infinity;
+ while (cursor.findNext()) {
+ var from = cursor.from(), dist = Math.abs(from.line - start.line) * 10000;
+ if (!dist) dist = Math.abs(from.ch - start.ch);
+ if (dist < nearestDist) { nearest = from; nearestDist = dist; }
+ }
+ if (!nearest) return null;
+
+ if (before.length == 1)
+ nearest.ch += before[0].length;
+ else
+ nearest = Pos(nearest.line + (before.length - 1), before[before.length - 1].length);
+ if (data.start.line == data.end.line)
+ var end = Pos(nearest.line, nearest.ch + (data.end.ch - data.start.ch));
+ else
+ var end = Pos(nearest.line + (data.end.line - data.start.line), data.end.ch);
+ return {start: nearest, end: end};
+ }
+
+ function atInterestingExpression(cm) {
+ var pos = cm.getCursor("end"), tok = cm.getTokenAt(pos);
+ if (tok.start < pos.ch && (tok.type == "comment" || tok.type == "string")) return false;
+ return /\w/.test(cm.getLine(pos.line).slice(Math.max(pos.ch - 1, 0), pos.ch + 1));
+ }
+
+ // Variable renaming
+
+ function rename(ts, cm) {
+ var token = cm.getTokenAt(cm.getCursor());
+ if (!/\w/.test(token.string)) return showError(ts, cm, "Not at a variable");
+ dialog(cm, "New name for " + token.string, function(newName) {
+ ts.request(cm, {type: "rename", newName: newName, fullDocs: true}, function(error, data) {
+ if (error) return showError(ts, cm, error);
+ applyChanges(ts, data.changes);
+ });
+ });
+ }
+
+ function selectName(ts, cm) {
+ var name = findDoc(ts, cm.doc).name;
+ ts.request(cm, {type: "refs"}, function(error, data) {
+ if (error) return showError(ts, cm, error);
+ var ranges = [], cur = 0;
+ for (var i = 0; i < data.refs.length; i++) {
+ var ref = data.refs[i];
+ if (ref.file == name) {
+ ranges.push({anchor: ref.start, head: ref.end});
+ if (cmpPos(cur, ref.start) >= 0 && cmpPos(cur, ref.end) <= 0)
+ cur = ranges.length - 1;
+ }
+ }
+ cm.setSelections(ranges, cur);
+ });
+ }
+
+ var nextChangeOrig = 0;
+ function applyChanges(ts, changes) {
+ var perFile = Object.create(null);
+ for (var i = 0; i < changes.length; ++i) {
+ var ch = changes[i];
+ (perFile[ch.file] || (perFile[ch.file] = [])).push(ch);
+ }
+ for (var file in perFile) {
+ var known = ts.docs[file], chs = perFile[file];;
+ if (!known) continue;
+ chs.sort(function(a, b) { return cmpPos(b.start, a.start); });
+ var origin = "*rename" + (++nextChangeOrig);
+ for (var i = 0; i < chs.length; ++i) {
+ var ch = chs[i];
+ known.doc.replaceRange(ch.text, ch.start, ch.end, origin);
+ }
+ }
+ }
+
+ // Generic request-building helper
+
+ function buildRequest(ts, doc, query, pos) {
+ var files = [], offsetLines = 0, allowFragments = !query.fullDocs;
+ if (!allowFragments) delete query.fullDocs;
+ if (typeof query == "string") query = {type: query};
+ query.lineCharPositions = true;
+ if (query.end == null) {
+ query.end = pos || doc.doc.getCursor("end");
+ if (doc.doc.somethingSelected())
+ query.start = doc.doc.getCursor("start");
+ }
+ var startPos = query.start || query.end;
+
+ if (doc.changed) {
+ if (doc.doc.lineCount() > bigDoc && allowFragments !== false &&
+ doc.changed.to - doc.changed.from < 100 &&
+ doc.changed.from <= startPos.line && doc.changed.to > query.end.line) {
+ files.push(getFragmentAround(doc, startPos, query.end));
+ query.file = "#0";
+ var offsetLines = files[0].offsetLines;
+ if (query.start != null) query.start = Pos(query.start.line - -offsetLines, query.start.ch);
+ query.end = Pos(query.end.line - offsetLines, query.end.ch);
+ } else {
+ files.push({type: "full",
+ name: doc.name,
+ text: docValue(ts, doc)});
+ query.file = doc.name;
+ doc.changed = null;
+ }
+ } else {
+ query.file = doc.name;
+ }
+ for (var name in ts.docs) {
+ var cur = ts.docs[name];
+ if (cur.changed && cur != doc) {
+ files.push({type: "full", name: cur.name, text: docValue(ts, cur)});
+ cur.changed = null;
+ }
+ }
+
+ return {query: query, files: files};
+ }
+
+ function getFragmentAround(data, start, end) {
+ var doc = data.doc;
+ var minIndent = null, minLine = null, endLine, tabSize = 4;
+ for (var p = start.line - 1, min = Math.max(0, p - 50); p >= min; --p) {
+ var line = doc.getLine(p), fn = line.search(/\bfunction\b/);
+ if (fn < 0) continue;
+ var indent = CodeMirror.countColumn(line, null, tabSize);
+ if (minIndent != null && minIndent <= indent) continue;
+ minIndent = indent;
+ minLine = p;
+ }
+ if (minLine == null) minLine = min;
+ var max = Math.min(doc.lastLine(), end.line + 20);
+ if (minIndent == null || minIndent == CodeMirror.countColumn(doc.getLine(start.line), null, tabSize))
+ endLine = max;
+ else for (endLine = end.line + 1; endLine < max; ++endLine) {
+ var indent = CodeMirror.countColumn(doc.getLine(endLine), null, tabSize);
+ if (indent <= minIndent) break;
+ }
+ var from = Pos(minLine, 0);
+
+ return {type: "part",
+ name: data.name,
+ offsetLines: from.line,
+ text: doc.getRange(from, Pos(endLine, 0))};
+ }
+
+ // Generic utilities
+
+ var cmpPos = CodeMirror.cmpPos;
+
+ function elt(tagname, cls /*, ... elts*/) {
+ var e = document.createElement(tagname);
+ if (cls) e.className = cls;
+ for (var i = 2; i < arguments.length; ++i) {
+ var elt = arguments[i];
+ if (typeof elt == "string") elt = document.createTextNode(elt);
+ e.appendChild(elt);
+ }
+ return e;
+ }
+
+ function dialog(cm, text, f) {
+ if (cm.openDialog)
+ cm.openDialog(text + ": ", f);
+ else
+ f(prompt(text, ""));
+ }
+
+ // Tooltips
+
+ function tempTooltip(cm, content) {
+ var where = cm.cursorCoords();
+ var tip = makeTooltip(where.right + 1, where.bottom, content);
+ function clear() {
+ if (!tip.parentNode) return;
+ cm.off("cursorActivity", clear);
+ fadeOut(tip);
+ }
+ setTimeout(clear, 1700);
+ cm.on("cursorActivity", clear);
+ }
+
+ function makeTooltip(x, y, content) {
+ var node = elt("div", cls + "tooltip", content);
+ node.style.left = x + "px";
+ node.style.top = y + "px";
+ document.body.appendChild(node);
+ return node;
+ }
+
+ function remove(node) {
+ var p = node && node.parentNode;
+ if (p) p.removeChild(node);
+ }
+
+ function fadeOut(tooltip) {
+ tooltip.style.opacity = "0";
+ setTimeout(function() { remove(tooltip); }, 1100);
+ }
+
+ function showError(ts, cm, msg) {
+ if (ts.options.showError)
+ ts.options.showError(cm, msg);
+ else
+ tempTooltip(cm, String(msg));
+ }
+
+ function closeArgHints(ts) {
+ if (ts.activeArgHints) { remove(ts.activeArgHints); ts.activeArgHints = null; }
+ }
+
+ function docValue(ts, doc) {
+ var val = doc.doc.getValue();
+ if (ts.options.fileFilter) val = ts.options.fileFilter(val, doc.name, doc.doc);
+ return val;
+ }
+
+ // Worker wrapper
+
+ function WorkerServer(ts) {
+ var worker = new Worker(ts.options.workerScript);
+ worker.postMessage({type: "init",
+ defs: ts.options.defs,
+ plugins: ts.options.plugins,
+ scripts: ts.options.workerDeps});
+ var msgId = 0, pending = {};
+
+ function send(data, c) {
+ if (c) {
+ data.id = ++msgId;
+ pending[msgId] = c;
+ }
+ worker.postMessage(data);
+ }
+ worker.onmessage = function(e) {
+ var data = e.data;
+ if (data.type == "getFile") {
+ getFile(ts, data.name, function(err, text) {
+ send({type: "getFile", err: String(err), text: text, id: data.id});
+ });
+ } else if (data.type == "debug") {
+ window.console.log(data.message);
+ } else if (data.id && pending[data.id]) {
+ pending[data.id](data.err, data.body);
+ delete pending[data.id];
+ }
+ };
+ worker.onerror = function(e) {
+ for (var id in pending) pending[id](e);
+ pending = {};
+ };
+
+ this.addFile = function(name, text) { send({type: "add", name: name, text: text}); };
+ this.delFile = function(name) { send({type: "del", name: name}); };
+ this.request = function(body, c) { send({type: "req", body: body}, c); };
+ }
+});
diff --git a/applications/admin/static/codemirror/addon/tern/worker.js b/applications/admin/static/codemirror/addon/tern/worker.js
new file mode 100644
index 00000000..48277af8
--- /dev/null
+++ b/applications/admin/static/codemirror/addon/tern/worker.js
@@ -0,0 +1,44 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+// declare global: tern, server
+
+var server;
+
+this.onmessage = function(e) {
+ var data = e.data;
+ switch (data.type) {
+ case "init": return startServer(data.defs, data.plugins, data.scripts);
+ case "add": return server.addFile(data.name, data.text);
+ case "del": return server.delFile(data.name);
+ case "req": return server.request(data.body, function(err, reqData) {
+ postMessage({id: data.id, body: reqData, err: err && String(err)});
+ });
+ case "getFile":
+ var c = pending[data.id];
+ delete pending[data.id];
+ return c(data.err, data.text);
+ default: throw new Error("Unknown message type: " + data.type);
+ }
+};
+
+var nextId = 0, pending = {};
+function getFile(file, c) {
+ postMessage({type: "getFile", name: file, id: ++nextId});
+ pending[nextId] = c;
+}
+
+function startServer(defs, plugins, scripts) {
+ if (scripts) importScripts.apply(null, scripts);
+
+ server = new tern.Server({
+ getFile: getFile,
+ async: true,
+ defs: defs,
+ plugins: plugins
+ });
+}
+
+var console = {
+ log: function(v) { postMessage({type: "debug", message: v}); }
+};
diff --git a/applications/admin/static/codemirror/addon/wrap/hardwrap.js b/applications/admin/static/codemirror/addon/wrap/hardwrap.js
new file mode 100644
index 00000000..fe9b4dd6
--- /dev/null
+++ b/applications/admin/static/codemirror/addon/wrap/hardwrap.js
@@ -0,0 +1,139 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ var Pos = CodeMirror.Pos;
+
+ function findParagraph(cm, pos, options) {
+ var startRE = options.paragraphStart || cm.getHelper(pos, "paragraphStart");
+ for (var start = pos.line, first = cm.firstLine(); start > first; --start) {
+ var line = cm.getLine(start);
+ if (startRE && startRE.test(line)) break;
+ if (!/\S/.test(line)) { ++start; break; }
+ }
+ var endRE = options.paragraphEnd || cm.getHelper(pos, "paragraphEnd");
+ for (var end = pos.line + 1, last = cm.lastLine(); end <= last; ++end) {
+ var line = cm.getLine(end);
+ if (endRE && endRE.test(line)) { ++end; break; }
+ if (!/\S/.test(line)) break;
+ }
+ return {from: start, to: end};
+ }
+
+ function findBreakPoint(text, column, wrapOn, killTrailingSpace) {
+ for (var at = column; at > 0; --at)
+ if (wrapOn.test(text.slice(at - 1, at + 1))) break;
+ if (at == 0) at = column;
+ var endOfText = at;
+ if (killTrailingSpace)
+ while (text.charAt(endOfText - 1) == " ") --endOfText;
+ return {from: endOfText, to: at};
+ }
+
+ function wrapRange(cm, from, to, options) {
+ from = cm.clipPos(from); to = cm.clipPos(to);
+ var column = options.column || 80;
+ var wrapOn = options.wrapOn || /\s\S|-[^\.\d]/;
+ var killTrailing = options.killTrailingSpace !== false;
+ var changes = [], curLine = "", curNo = from.line;
+ var lines = cm.getRange(from, to, false);
+ if (!lines.length) return null;
+ var leadingSpace = lines[0].match(/^[ \t]*/)[0];
+
+ for (var i = 0; i < lines.length; ++i) {
+ var text = lines[i], oldLen = curLine.length, spaceInserted = 0;
+ if (curLine && text && !wrapOn.test(curLine.charAt(curLine.length - 1) + text.charAt(0))) {
+ curLine += " ";
+ spaceInserted = 1;
+ }
+ var spaceTrimmed = "";
+ if (i) {
+ spaceTrimmed = text.match(/^\s*/)[0];
+ text = text.slice(spaceTrimmed.length);
+ }
+ curLine += text;
+ if (i) {
+ var firstBreak = curLine.length > column && leadingSpace == spaceTrimmed &&
+ findBreakPoint(curLine, column, wrapOn, killTrailing);
+ // If this isn't broken, or is broken at a different point, remove old break
+ if (!firstBreak || firstBreak.from != oldLen || firstBreak.to != oldLen + spaceInserted) {
+ changes.push({text: [spaceInserted ? " " : ""],
+ from: Pos(curNo, oldLen),
+ to: Pos(curNo + 1, spaceTrimmed.length)});
+ } else {
+ curLine = leadingSpace + text;
+ ++curNo;
+ }
+ }
+ while (curLine.length > column) {
+ var bp = findBreakPoint(curLine, column, wrapOn, killTrailing);
+ changes.push({text: ["", leadingSpace],
+ from: Pos(curNo, bp.from),
+ to: Pos(curNo, bp.to)});
+ curLine = leadingSpace + curLine.slice(bp.to);
+ ++curNo;
+ }
+ }
+ if (changes.length) cm.operation(function() {
+ for (var i = 0; i < changes.length; ++i) {
+ var change = changes[i];
+ cm.replaceRange(change.text, change.from, change.to);
+ }
+ });
+ return changes.length ? {from: changes[0].from, to: CodeMirror.changeEnd(changes[changes.length - 1])} : null;
+ }
+
+ CodeMirror.defineExtension("wrapParagraph", function(pos, options) {
+ options = options || {};
+ if (!pos) pos = this.getCursor();
+ var para = findParagraph(this, pos, options);
+ return wrapRange(this, Pos(para.from, 0), Pos(para.to - 1), options);
+ });
+
+ CodeMirror.commands.wrapLines = function(cm) {
+ cm.operation(function() {
+ var ranges = cm.listSelections(), at = cm.lastLine() + 1;
+ for (var i = ranges.length - 1; i >= 0; i--) {
+ var range = ranges[i], span;
+ if (range.empty()) {
+ var para = findParagraph(cm, range.head, {});
+ span = {from: Pos(para.from, 0), to: Pos(para.to - 1)};
+ } else {
+ span = {from: range.from(), to: range.to()};
+ }
+ if (span.to.line >= at) continue;
+ at = span.from.line;
+ wrapRange(cm, span.from, span.to, {});
+ }
+ });
+ };
+
+ CodeMirror.defineExtension("wrapRange", function(from, to, options) {
+ return wrapRange(this, from, to, options || {});
+ });
+
+ CodeMirror.defineExtension("wrapParagraphsInRange", function(from, to, options) {
+ options = options || {};
+ var cm = this, paras = [];
+ for (var line = from.line; line <= to.line;) {
+ var para = findParagraph(cm, Pos(line, 0), options);
+ paras.push(para);
+ line = para.to;
+ }
+ var madeChange = false;
+ if (paras.length) cm.operation(function() {
+ for (var i = paras.length - 1; i >= 0; --i)
+ madeChange = madeChange || wrapRange(cm, Pos(paras[i].from, 0), Pos(paras[i].to - 1), options);
+ });
+ return madeChange;
+ });
+});
diff --git a/applications/admin/static/codemirror/bin/lint b/applications/admin/static/codemirror/bin/lint
index 4f70994c..47f45f36 100755
--- a/applications/admin/static/codemirror/bin/lint
+++ b/applications/admin/static/codemirror/bin/lint
@@ -1,16 +1,3 @@
#!/usr/bin/env node
-var lint = require("../test/lint/lint"),
- path = require("path");
-
-if (process.argv.length > 2) {
- lint.checkDir(process.argv[2]);
-} else {
- process.chdir(path.resolve(__dirname, ".."));
- lint.checkDir("lib");
- lint.checkDir("mode");
- lint.checkDir("addon");
- lint.checkDir("keymap");
-}
-
-process.exit(lint.success() ? 0 : 1);
+process.exit(require("../test/lint").ok ? 0 : 1);
diff --git a/applications/admin/static/codemirror/keymap/emacs.js b/applications/admin/static/codemirror/keymap/emacs.js
index 8d3ab62d..c4135237 100644
--- a/applications/admin/static/codemirror/keymap/emacs.js
+++ b/applications/admin/static/codemirror/keymap/emacs.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../lib/codemirror"));
@@ -129,8 +132,8 @@
};
}
- function findEnd(cm, by, dir) {
- var pos = cm.getCursor(), prefix = getPrefix(cm);
+ function findEnd(cm, pos, by, dir) {
+ var prefix = getPrefix(cm);
if (prefix < 0) { dir = -dir; prefix = -prefix; }
for (var i = 0; i < prefix; ++i) {
var newPos = by(cm, pos, dir);
@@ -142,14 +145,31 @@
function move(by, dir) {
var f = function(cm) {
- cm.extendSelection(findEnd(cm, by, dir));
+ cm.extendSelection(findEnd(cm, cm.getCursor(), by, dir));
};
f.motion = true;
return f;
}
function killTo(cm, by, dir) {
- kill(cm, cm.getCursor(), findEnd(cm, by, dir), true);
+ var selections = cm.listSelections(), cursor;
+ var i = selections.length;
+ while (i--) {
+ cursor = selections[i].head;
+ kill(cm, cursor, findEnd(cm, cursor, by, dir), true);
+ }
+ }
+
+ function killRegion(cm) {
+ if (cm.somethingSelected()) {
+ var selections = cm.listSelections(), selection;
+ var i = selections.length;
+ while (i--) {
+ selection = selections[i];
+ kill(cm, selection.anchor, selection.head);
+ }
+ return true;
+ }
}
function addPrefix(cm, digit) {
@@ -253,7 +273,7 @@
// Actual keymap
- var keyMap = CodeMirror.keyMap.emacs = {
+ var keyMap = CodeMirror.keyMap.emacs = CodeMirror.normalizeKeyMap({
"Ctrl-W": function(cm) {kill(cm, cm.getCursor("start"), cm.getCursor("end"));},
"Ctrl-K": repeated(function(cm) {
var start = cm.getCursor(), end = cm.clipPos(Pos(start.line));
@@ -280,9 +300,9 @@
"Ctrl-F": move(byChar, 1), "Ctrl-B": move(byChar, -1),
"Right": move(byChar, 1), "Left": move(byChar, -1),
"Ctrl-D": function(cm) { killTo(cm, byChar, 1); },
- "Delete": function(cm) { killTo(cm, byChar, 1); },
+ "Delete": function(cm) { killRegion(cm) || killTo(cm, byChar, 1); },
"Ctrl-H": function(cm) { killTo(cm, byChar, -1); },
- "Backspace": function(cm) { killTo(cm, byChar, -1); },
+ "Backspace": function(cm) { killRegion(cm) || killTo(cm, byChar, -1); },
"Alt-F": move(byWord, 1), "Alt-B": move(byWord, -1),
"Alt-D": function(cm) { killTo(cm, byWord, 1); },
@@ -306,7 +326,8 @@
"Ctrl-Alt-F": move(byExpr, 1), "Ctrl-Alt-B": move(byExpr, -1),
"Shift-Ctrl-Alt-2": function(cm) {
- cm.setSelection(findEnd(cm, byExpr, 1), cm.getCursor());
+ var cursor = cm.getCursor();
+ cm.setSelection(findEnd(cm, cursor, byExpr, 1), cursor);
},
"Ctrl-Alt-T": function(cm) {
var leftStart = byExpr(cm, cm.getCursor(), -1), leftEnd = byExpr(cm, leftStart, 1);
@@ -324,13 +345,7 @@
},
"Ctrl-O": repeated(function(cm) { cm.replaceSelection("\n", "start"); }),
"Ctrl-T": repeated(function(cm) {
- var pos = cm.getCursor();
- if (pos.ch < cm.getLine(pos.line).length) pos = Pos(pos.line, pos.ch + 1);
- var from = cm.findPosH(pos, -2, "char");
- var range = cm.getRange(from, pos);
- if (range.length != 2) return;
- cm.setSelection(from, pos);
- cm.replaceSelection(range.charAt(1) + range.charAt(0), null, "+transpose");
+ cm.execCommand("transposeChars");
}),
"Alt-C": repeated(function(cm) {
@@ -356,27 +371,7 @@
"Alt-/": "autocomplete",
"Ctrl-J": "newlineAndIndent", "Enter": false, "Tab": "indentAuto",
- "Alt-G": function(cm) {cm.setOption("keyMap", "emacs-Alt-G");},
- "Ctrl-X": function(cm) {cm.setOption("keyMap", "emacs-Ctrl-X");},
- "Ctrl-Q": function(cm) {cm.setOption("keyMap", "emacs-Ctrl-Q");},
- "Ctrl-U": addPrefixMap
- };
-
- CodeMirror.keyMap["emacs-Ctrl-X"] = {
- "Tab": function(cm) {
- cm.indentSelection(getPrefix(cm, true) || cm.getOption("indentUnit"));
- },
- "Ctrl-X": function(cm) {
- cm.setSelection(cm.getCursor("head"), cm.getCursor("anchor"));
- },
-
- "Ctrl-S": "save", "Ctrl-W": "save", "S": "saveAll", "F": "open", "U": repeated("undo"), "K": "close",
- "Delete": function(cm) { kill(cm, cm.getCursor(), bySentence(cm, cm.getCursor(), 1), true); },
- auto: "emacs", nofallthrough: true, disableInput: true
- };
-
- CodeMirror.keyMap["emacs-Alt-G"] = {
- "G": function(cm) {
+ "Alt-G G": function(cm) {
var prefix = getPrefix(cm, true);
if (prefix != null && prefix > 0) return cm.setCursor(prefix - 1);
@@ -386,13 +381,25 @@
cm.setCursor(num - 1);
});
},
- auto: "emacs", nofallthrough: true, disableInput: true
- };
- CodeMirror.keyMap["emacs-Ctrl-Q"] = {
- "Tab": repeated("insertTab"),
- auto: "emacs", nofallthrough: true
- };
+ "Ctrl-X Tab": function(cm) {
+ cm.indentSelection(getPrefix(cm, true) || cm.getOption("indentUnit"));
+ },
+ "Ctrl-X Ctrl-X": function(cm) {
+ cm.setSelection(cm.getCursor("head"), cm.getCursor("anchor"));
+ },
+ "Ctrl-X Ctrl-S": "save",
+ "Ctrl-X Ctrl-W": "save",
+ "Ctrl-X S": "saveAll",
+ "Ctrl-X F": "open",
+ "Ctrl-X U": repeated("undo"),
+ "Ctrl-X K": "close",
+ "Ctrl-X Delete": function(cm) { kill(cm, cm.getCursor(), bySentence(cm, cm.getCursor(), 1), true); },
+ "Ctrl-X H": "selectAll",
+
+ "Ctrl-Q Tab": repeated("insertTab"),
+ "Ctrl-U": addPrefixMap
+ });
var prefixMap = {"Ctrl-G": clearPrefix};
function regPrefix(d) {
diff --git a/applications/admin/static/codemirror/keymap/sublime.js b/applications/admin/static/codemirror/keymap/sublime.js
new file mode 100644
index 00000000..45936c36
--- /dev/null
+++ b/applications/admin/static/codemirror/keymap/sublime.js
@@ -0,0 +1,540 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+// A rough approximation of Sublime Text's keybindings
+// Depends on addon/search/searchcursor.js and optionally addon/dialog/dialogs.js
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../lib/codemirror"), require("../addon/search/searchcursor"), require("../addon/edit/matchbrackets"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../lib/codemirror", "../addon/search/searchcursor", "../addon/edit/matchbrackets"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ var map = CodeMirror.keyMap.sublime = {fallthrough: "default"};
+ var cmds = CodeMirror.commands;
+ var Pos = CodeMirror.Pos;
+ var mac = CodeMirror.keyMap["default"] == CodeMirror.keyMap.macDefault;
+ var ctrl = mac ? "Cmd-" : "Ctrl-";
+
+ // This is not exactly Sublime's algorithm. I couldn't make heads or tails of that.
+ function findPosSubword(doc, start, dir) {
+ if (dir < 0 && start.ch == 0) return doc.clipPos(Pos(start.line - 1));
+ var line = doc.getLine(start.line);
+ if (dir > 0 && start.ch >= line.length) return doc.clipPos(Pos(start.line + 1, 0));
+ var state = "start", type;
+ for (var pos = start.ch, e = dir < 0 ? 0 : line.length, i = 0; pos != e; pos += dir, i++) {
+ var next = line.charAt(dir < 0 ? pos - 1 : pos);
+ var cat = next != "_" && CodeMirror.isWordChar(next) ? "w" : "o";
+ if (cat == "w" && next.toUpperCase() == next) cat = "W";
+ if (state == "start") {
+ if (cat != "o") { state = "in"; type = cat; }
+ } else if (state == "in") {
+ if (type != cat) {
+ if (type == "w" && cat == "W" && dir < 0) pos--;
+ if (type == "W" && cat == "w" && dir > 0) { type = "w"; continue; }
+ break;
+ }
+ }
+ }
+ return Pos(start.line, pos);
+ }
+
+ function moveSubword(cm, dir) {
+ cm.extendSelectionsBy(function(range) {
+ if (cm.display.shift || cm.doc.extend || range.empty())
+ return findPosSubword(cm.doc, range.head, dir);
+ else
+ return dir < 0 ? range.from() : range.to();
+ });
+ }
+
+ cmds[map["Alt-Left"] = "goSubwordLeft"] = function(cm) { moveSubword(cm, -1); };
+ cmds[map["Alt-Right"] = "goSubwordRight"] = function(cm) { moveSubword(cm, 1); };
+
+ cmds[map[ctrl + "Up"] = "scrollLineUp"] = function(cm) {
+ var info = cm.getScrollInfo();
+ if (!cm.somethingSelected()) {
+ var visibleBottomLine = cm.lineAtHeight(info.top + info.clientHeight, "local");
+ if (cm.getCursor().line >= visibleBottomLine)
+ cm.execCommand("goLineUp");
+ }
+ cm.scrollTo(null, info.top - cm.defaultTextHeight());
+ };
+ cmds[map[ctrl + "Down"] = "scrollLineDown"] = function(cm) {
+ var info = cm.getScrollInfo();
+ if (!cm.somethingSelected()) {
+ var visibleTopLine = cm.lineAtHeight(info.top, "local")+1;
+ if (cm.getCursor().line <= visibleTopLine)
+ cm.execCommand("goLineDown");
+ }
+ cm.scrollTo(null, info.top + cm.defaultTextHeight());
+ };
+
+ cmds[map["Shift-" + ctrl + "L"] = "splitSelectionByLine"] = function(cm) {
+ var ranges = cm.listSelections(), lineRanges = [];
+ for (var i = 0; i < ranges.length; i++) {
+ var from = ranges[i].from(), to = ranges[i].to();
+ for (var line = from.line; line <= to.line; ++line)
+ if (!(to.line > from.line && line == to.line && to.ch == 0))
+ lineRanges.push({anchor: line == from.line ? from : Pos(line, 0),
+ head: line == to.line ? to : Pos(line)});
+ }
+ cm.setSelections(lineRanges, 0);
+ };
+
+ map["Shift-Tab"] = "indentLess";
+
+ cmds[map["Esc"] = "singleSelectionTop"] = function(cm) {
+ var range = cm.listSelections()[0];
+ cm.setSelection(range.anchor, range.head, {scroll: false});
+ };
+
+ cmds[map[ctrl + "L"] = "selectLine"] = function(cm) {
+ var ranges = cm.listSelections(), extended = [];
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i];
+ extended.push({anchor: Pos(range.from().line, 0),
+ head: Pos(range.to().line + 1, 0)});
+ }
+ cm.setSelections(extended);
+ };
+
+ map["Shift-" + ctrl + "K"] = "deleteLine";
+
+ function insertLine(cm, above) {
+ cm.operation(function() {
+ var len = cm.listSelections().length, newSelection = [], last = -1;
+ for (var i = 0; i < len; i++) {
+ var head = cm.listSelections()[i].head;
+ if (head.line <= last) continue;
+ var at = Pos(head.line + (above ? 0 : 1), 0);
+ cm.replaceRange("\n", at, null, "+insertLine");
+ cm.indentLine(at.line, null, true);
+ newSelection.push({head: at, anchor: at});
+ last = head.line + 1;
+ }
+ cm.setSelections(newSelection);
+ });
+ }
+
+ cmds[map[ctrl + "Enter"] = "insertLineAfter"] = function(cm) { insertLine(cm, false); };
+
+ cmds[map["Shift-" + ctrl + "Enter"] = "insertLineBefore"] = function(cm) { insertLine(cm, true); };
+
+ function wordAt(cm, pos) {
+ var start = pos.ch, end = start, line = cm.getLine(pos.line);
+ while (start && CodeMirror.isWordChar(line.charAt(start - 1))) --start;
+ while (end < line.length && CodeMirror.isWordChar(line.charAt(end))) ++end;
+ return {from: Pos(pos.line, start), to: Pos(pos.line, end), word: line.slice(start, end)};
+ }
+
+ cmds[map[ctrl + "D"] = "selectNextOccurrence"] = function(cm) {
+ var from = cm.getCursor("from"), to = cm.getCursor("to");
+ var fullWord = cm.state.sublimeFindFullWord == cm.doc.sel;
+ if (CodeMirror.cmpPos(from, to) == 0) {
+ var word = wordAt(cm, from);
+ if (!word.word) return;
+ cm.setSelection(word.from, word.to);
+ fullWord = true;
+ } else {
+ var text = cm.getRange(from, to);
+ var query = fullWord ? new RegExp("\\b" + text + "\\b") : text;
+ var cur = cm.getSearchCursor(query, to);
+ if (cur.findNext()) {
+ cm.addSelection(cur.from(), cur.to());
+ } else {
+ cur = cm.getSearchCursor(query, Pos(cm.firstLine(), 0));
+ if (cur.findNext())
+ cm.addSelection(cur.from(), cur.to());
+ }
+ }
+ if (fullWord)
+ cm.state.sublimeFindFullWord = cm.doc.sel;
+ };
+
+ var mirror = "(){}[]";
+ function selectBetweenBrackets(cm) {
+ var pos = cm.getCursor(), opening = cm.scanForBracket(pos, -1);
+ if (!opening) return;
+ for (;;) {
+ var closing = cm.scanForBracket(pos, 1);
+ if (!closing) return;
+ if (closing.ch == mirror.charAt(mirror.indexOf(opening.ch) + 1)) {
+ cm.setSelection(Pos(opening.pos.line, opening.pos.ch + 1), closing.pos, false);
+ return true;
+ }
+ pos = Pos(closing.pos.line, closing.pos.ch + 1);
+ }
+ }
+
+ cmds[map["Shift-" + ctrl + "Space"] = "selectScope"] = function(cm) {
+ selectBetweenBrackets(cm) || cm.execCommand("selectAll");
+ };
+ cmds[map["Shift-" + ctrl + "M"] = "selectBetweenBrackets"] = function(cm) {
+ if (!selectBetweenBrackets(cm)) return CodeMirror.Pass;
+ };
+
+ cmds[map[ctrl + "M"] = "goToBracket"] = function(cm) {
+ cm.extendSelectionsBy(function(range) {
+ var next = cm.scanForBracket(range.head, 1);
+ if (next && CodeMirror.cmpPos(next.pos, range.head) != 0) return next.pos;
+ var prev = cm.scanForBracket(range.head, -1);
+ return prev && Pos(prev.pos.line, prev.pos.ch + 1) || range.head;
+ });
+ };
+
+ var swapLineCombo = mac ? "Cmd-Ctrl-" : "Shift-Ctrl-";
+
+ cmds[map[swapLineCombo + "Up"] = "swapLineUp"] = function(cm) {
+ var ranges = cm.listSelections(), linesToMove = [], at = cm.firstLine() - 1, newSels = [];
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i], from = range.from().line - 1, to = range.to().line;
+ newSels.push({anchor: Pos(range.anchor.line - 1, range.anchor.ch),
+ head: Pos(range.head.line - 1, range.head.ch)});
+ if (range.to().ch == 0 && !range.empty()) --to;
+ if (from > at) linesToMove.push(from, to);
+ else if (linesToMove.length) linesToMove[linesToMove.length - 1] = to;
+ at = to;
+ }
+ cm.operation(function() {
+ for (var i = 0; i < linesToMove.length; i += 2) {
+ var from = linesToMove[i], to = linesToMove[i + 1];
+ var line = cm.getLine(from);
+ cm.replaceRange("", Pos(from, 0), Pos(from + 1, 0), "+swapLine");
+ if (to > cm.lastLine())
+ cm.replaceRange("\n" + line, Pos(cm.lastLine()), null, "+swapLine");
+ else
+ cm.replaceRange(line + "\n", Pos(to, 0), null, "+swapLine");
+ }
+ cm.setSelections(newSels);
+ cm.scrollIntoView();
+ });
+ };
+
+ cmds[map[swapLineCombo + "Down"] = "swapLineDown"] = function(cm) {
+ var ranges = cm.listSelections(), linesToMove = [], at = cm.lastLine() + 1;
+ for (var i = ranges.length - 1; i >= 0; i--) {
+ var range = ranges[i], from = range.to().line + 1, to = range.from().line;
+ if (range.to().ch == 0 && !range.empty()) from--;
+ if (from < at) linesToMove.push(from, to);
+ else if (linesToMove.length) linesToMove[linesToMove.length - 1] = to;
+ at = to;
+ }
+ cm.operation(function() {
+ for (var i = linesToMove.length - 2; i >= 0; i -= 2) {
+ var from = linesToMove[i], to = linesToMove[i + 1];
+ var line = cm.getLine(from);
+ if (from == cm.lastLine())
+ cm.replaceRange("", Pos(from - 1), Pos(from), "+swapLine");
+ else
+ cm.replaceRange("", Pos(from, 0), Pos(from + 1, 0), "+swapLine");
+ cm.replaceRange(line + "\n", Pos(to, 0), null, "+swapLine");
+ }
+ cm.scrollIntoView();
+ });
+ };
+
+ map[ctrl + "/"] = "toggleComment";
+
+ cmds[map[ctrl + "J"] = "joinLines"] = function(cm) {
+ var ranges = cm.listSelections(), joined = [];
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i], from = range.from();
+ var start = from.line, end = range.to().line;
+ while (i < ranges.length - 1 && ranges[i + 1].from().line == end)
+ end = ranges[++i].to().line;
+ joined.push({start: start, end: end, anchor: !range.empty() && from});
+ }
+ cm.operation(function() {
+ var offset = 0, ranges = [];
+ for (var i = 0; i < joined.length; i++) {
+ var obj = joined[i];
+ var anchor = obj.anchor && Pos(obj.anchor.line - offset, obj.anchor.ch), head;
+ for (var line = obj.start; line <= obj.end; line++) {
+ var actual = line - offset;
+ if (line == obj.end) head = Pos(actual, cm.getLine(actual).length + 1);
+ if (actual < cm.lastLine()) {
+ cm.replaceRange(" ", Pos(actual), Pos(actual + 1, /^\s*/.exec(cm.getLine(actual + 1))[0].length));
+ ++offset;
+ }
+ }
+ ranges.push({anchor: anchor || head, head: head});
+ }
+ cm.setSelections(ranges, 0);
+ });
+ };
+
+ cmds[map["Shift-" + ctrl + "D"] = "duplicateLine"] = function(cm) {
+ cm.operation(function() {
+ var rangeCount = cm.listSelections().length;
+ for (var i = 0; i < rangeCount; i++) {
+ var range = cm.listSelections()[i];
+ if (range.empty())
+ cm.replaceRange(cm.getLine(range.head.line) + "\n", Pos(range.head.line, 0));
+ else
+ cm.replaceRange(cm.getRange(range.from(), range.to()), range.from());
+ }
+ cm.scrollIntoView();
+ });
+ };
+
+ map[ctrl + "T"] = "transposeChars";
+
+ function sortLines(cm, caseSensitive) {
+ var ranges = cm.listSelections(), toSort = [], selected;
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i];
+ if (range.empty()) continue;
+ var from = range.from().line, to = range.to().line;
+ while (i < ranges.length - 1 && ranges[i + 1].from().line == to)
+ to = range[++i].to().line;
+ toSort.push(from, to);
+ }
+ if (toSort.length) selected = true;
+ else toSort.push(cm.firstLine(), cm.lastLine());
+
+ cm.operation(function() {
+ var ranges = [];
+ for (var i = 0; i < toSort.length; i += 2) {
+ var from = toSort[i], to = toSort[i + 1];
+ var start = Pos(from, 0), end = Pos(to);
+ var lines = cm.getRange(start, end, false);
+ if (caseSensitive)
+ lines.sort();
+ else
+ lines.sort(function(a, b) {
+ var au = a.toUpperCase(), bu = b.toUpperCase();
+ if (au != bu) { a = au; b = bu; }
+ return a < b ? -1 : a == b ? 0 : 1;
+ });
+ cm.replaceRange(lines, start, end);
+ if (selected) ranges.push({anchor: start, head: end});
+ }
+ if (selected) cm.setSelections(ranges, 0);
+ });
+ }
+
+ cmds[map["F9"] = "sortLines"] = function(cm) { sortLines(cm, true); };
+ cmds[map[ctrl + "F9"] = "sortLinesInsensitive"] = function(cm) { sortLines(cm, false); };
+
+ cmds[map["F2"] = "nextBookmark"] = function(cm) {
+ var marks = cm.state.sublimeBookmarks;
+ if (marks) while (marks.length) {
+ var current = marks.shift();
+ var found = current.find();
+ if (found) {
+ marks.push(current);
+ return cm.setSelection(found.from, found.to);
+ }
+ }
+ };
+
+ cmds[map["Shift-F2"] = "prevBookmark"] = function(cm) {
+ var marks = cm.state.sublimeBookmarks;
+ if (marks) while (marks.length) {
+ marks.unshift(marks.pop());
+ var found = marks[marks.length - 1].find();
+ if (!found)
+ marks.pop();
+ else
+ return cm.setSelection(found.from, found.to);
+ }
+ };
+
+ cmds[map[ctrl + "F2"] = "toggleBookmark"] = function(cm) {
+ var ranges = cm.listSelections();
+ var marks = cm.state.sublimeBookmarks || (cm.state.sublimeBookmarks = []);
+ for (var i = 0; i < ranges.length; i++) {
+ var from = ranges[i].from(), to = ranges[i].to();
+ var found = cm.findMarks(from, to);
+ for (var j = 0; j < found.length; j++) {
+ if (found[j].sublimeBookmark) {
+ found[j].clear();
+ for (var k = 0; k < marks.length; k++)
+ if (marks[k] == found[j])
+ marks.splice(k--, 1);
+ break;
+ }
+ }
+ if (j == found.length)
+ marks.push(cm.markText(from, to, {sublimeBookmark: true, clearWhenEmpty: false}));
+ }
+ };
+
+ cmds[map["Shift-" + ctrl + "F2"] = "clearBookmarks"] = function(cm) {
+ var marks = cm.state.sublimeBookmarks;
+ if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear();
+ marks.length = 0;
+ };
+
+ cmds[map["Alt-F2"] = "selectBookmarks"] = function(cm) {
+ var marks = cm.state.sublimeBookmarks, ranges = [];
+ if (marks) for (var i = 0; i < marks.length; i++) {
+ var found = marks[i].find();
+ if (!found)
+ marks.splice(i--, 0);
+ else
+ ranges.push({anchor: found.from, head: found.to});
+ }
+ if (ranges.length)
+ cm.setSelections(ranges, 0);
+ };
+
+ map["Alt-Q"] = "wrapLines";
+
+ var cK = ctrl + "K ";
+
+ function modifyWordOrSelection(cm, mod) {
+ cm.operation(function() {
+ var ranges = cm.listSelections(), indices = [], replacements = [];
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i];
+ if (range.empty()) { indices.push(i); replacements.push(""); }
+ else replacements.push(mod(cm.getRange(range.from(), range.to())));
+ }
+ cm.replaceSelections(replacements, "around", "case");
+ for (var i = indices.length - 1, at; i >= 0; i--) {
+ var range = ranges[indices[i]];
+ if (at && CodeMirror.cmpPos(range.head, at) > 0) continue;
+ var word = wordAt(cm, range.head);
+ at = word.from;
+ cm.replaceRange(mod(word.word), word.from, word.to);
+ }
+ });
+ }
+
+ map[cK + ctrl + "Backspace"] = "delLineLeft";
+
+ cmds[map[cK + ctrl + "K"] = "delLineRight"] = function(cm) {
+ cm.operation(function() {
+ var ranges = cm.listSelections();
+ for (var i = ranges.length - 1; i >= 0; i--)
+ cm.replaceRange("", ranges[i].anchor, Pos(ranges[i].to().line), "+delete");
+ cm.scrollIntoView();
+ });
+ };
+
+ cmds[map[cK + ctrl + "U"] = "upcaseAtCursor"] = function(cm) {
+ modifyWordOrSelection(cm, function(str) { return str.toUpperCase(); });
+ };
+ cmds[map[cK + ctrl + "L"] = "downcaseAtCursor"] = function(cm) {
+ modifyWordOrSelection(cm, function(str) { return str.toLowerCase(); });
+ };
+
+ cmds[map[cK + ctrl + "Space"] = "setSublimeMark"] = function(cm) {
+ if (cm.state.sublimeMark) cm.state.sublimeMark.clear();
+ cm.state.sublimeMark = cm.setBookmark(cm.getCursor());
+ };
+ cmds[map[cK + ctrl + "A"] = "selectToSublimeMark"] = function(cm) {
+ var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
+ if (found) cm.setSelection(cm.getCursor(), found);
+ };
+ cmds[map[cK + ctrl + "W"] = "deleteToSublimeMark"] = function(cm) {
+ var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
+ if (found) {
+ var from = cm.getCursor(), to = found;
+ if (CodeMirror.cmpPos(from, to) > 0) { var tmp = to; to = from; from = tmp; }
+ cm.state.sublimeKilled = cm.getRange(from, to);
+ cm.replaceRange("", from, to);
+ }
+ };
+ cmds[map[cK + ctrl + "X"] = "swapWithSublimeMark"] = function(cm) {
+ var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
+ if (found) {
+ cm.state.sublimeMark.clear();
+ cm.state.sublimeMark = cm.setBookmark(cm.getCursor());
+ cm.setCursor(found);
+ }
+ };
+ cmds[map[cK + ctrl + "Y"] = "sublimeYank"] = function(cm) {
+ if (cm.state.sublimeKilled != null)
+ cm.replaceSelection(cm.state.sublimeKilled, null, "paste");
+ };
+
+ map[cK + ctrl + "G"] = "clearBookmarks";
+ cmds[map[cK + ctrl + "C"] = "showInCenter"] = function(cm) {
+ var pos = cm.cursorCoords(null, "local");
+ cm.scrollTo(null, (pos.top + pos.bottom) / 2 - cm.getScrollInfo().clientHeight / 2);
+ };
+
+ cmds[map["Shift-Alt-Up"] = "selectLinesUpward"] = function(cm) {
+ cm.operation(function() {
+ var ranges = cm.listSelections();
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i];
+ if (range.head.line > cm.firstLine())
+ cm.addSelection(Pos(range.head.line - 1, range.head.ch));
+ }
+ });
+ };
+ cmds[map["Shift-Alt-Down"] = "selectLinesDownward"] = function(cm) {
+ cm.operation(function() {
+ var ranges = cm.listSelections();
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i];
+ if (range.head.line < cm.lastLine())
+ cm.addSelection(Pos(range.head.line + 1, range.head.ch));
+ }
+ });
+ };
+
+ function getTarget(cm) {
+ var from = cm.getCursor("from"), to = cm.getCursor("to");
+ if (CodeMirror.cmpPos(from, to) == 0) {
+ var word = wordAt(cm, from);
+ if (!word.word) return;
+ from = word.from;
+ to = word.to;
+ }
+ return {from: from, to: to, query: cm.getRange(from, to), word: word};
+ }
+
+ function findAndGoTo(cm, forward) {
+ var target = getTarget(cm);
+ if (!target) return;
+ var query = target.query;
+ var cur = cm.getSearchCursor(query, forward ? target.to : target.from);
+
+ if (forward ? cur.findNext() : cur.findPrevious()) {
+ cm.setSelection(cur.from(), cur.to());
+ } else {
+ cur = cm.getSearchCursor(query, forward ? Pos(cm.firstLine(), 0)
+ : cm.clipPos(Pos(cm.lastLine())));
+ if (forward ? cur.findNext() : cur.findPrevious())
+ cm.setSelection(cur.from(), cur.to());
+ else if (target.word)
+ cm.setSelection(target.from, target.to);
+ }
+ };
+ cmds[map[ctrl + "F3"] = "findUnder"] = function(cm) { findAndGoTo(cm, true); };
+ cmds[map["Shift-" + ctrl + "F3"] = "findUnderPrevious"] = function(cm) { findAndGoTo(cm,false); };
+ cmds[map["Alt-F3"] = "findAllUnder"] = function(cm) {
+ var target = getTarget(cm);
+ if (!target) return;
+ var cur = cm.getSearchCursor(target.query);
+ var matches = [];
+ var primaryIndex = -1;
+ while (cur.findNext()) {
+ matches.push({anchor: cur.from(), head: cur.to()});
+ if (cur.from().line <= target.from.line && cur.from().ch <= target.from.ch)
+ primaryIndex++;
+ }
+ cm.setSelections(matches, primaryIndex);
+ };
+
+ map["Shift-" + ctrl + "["] = "fold";
+ map["Shift-" + ctrl + "]"] = "unfold";
+ map[cK + ctrl + "0"] = map[cK + ctrl + "j"] = "unfoldAll";
+
+ map[ctrl + "I"] = "findIncremental";
+ map["Shift-" + ctrl + "I"] = "findIncrementalReverse";
+ map[ctrl + "H"] = "replace";
+ map["F3"] = "findNext";
+ map["Shift-F3"] = "findPrev";
+
+ CodeMirror.normalizeKeyMap(map);
+});
diff --git a/applications/admin/static/codemirror/keymap/vim.js b/applications/admin/static/codemirror/keymap/vim.js
index f9cdfd54..88a404a2 100644
--- a/applications/admin/static/codemirror/keymap/vim.js
+++ b/applications/admin/static/codemirror/keymap/vim.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
/**
* Supported keybindings:
*
@@ -58,9 +61,9 @@
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
- mod(require("../lib/codemirror"), require("../addon/search/searchcursor"), require("../addon/dialog/dialog"));
+ mod(require("../lib/codemirror"), require("../addon/search/searchcursor"), require("../addon/dialog/dialog"), require("../addon/edit/matchbrackets.js"));
else if (typeof define == "function" && define.amd) // AMD
- define(["../lib/codemirror", "../addon/search/searchcursor", "../addon/dialog/dialog"], mod);
+ define(["../lib/codemirror", "../addon/search/searchcursor", "../addon/dialog/dialog", "../addon/edit/matchbrackets"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
@@ -69,304 +72,256 @@
var defaultKeymap = [
// Key to key mapping. This goes first to make it possible to override
// existing mappings.
- { keys: [''], type: 'keyToKey', toKeys: ['h'] },
- { keys: [''], type: 'keyToKey', toKeys: ['l'] },
- { keys: [''], type: 'keyToKey', toKeys: ['k'] },
- { keys: [''], type: 'keyToKey', toKeys: ['j'] },
- { keys: [''], type: 'keyToKey', toKeys: ['l'] },
- { keys: [''], type: 'keyToKey', toKeys: ['h'] },
- { keys: [''], type: 'keyToKey', toKeys: ['W'] },
- { keys: [''], type: 'keyToKey', toKeys: ['B'] },
- { keys: [''], type: 'keyToKey', toKeys: ['w'] },
- { keys: [''], type: 'keyToKey', toKeys: ['b'] },
- { keys: [''], type: 'keyToKey', toKeys: ['j'] },
- { keys: [''], type: 'keyToKey', toKeys: ['k'] },
- { keys: [''], type: 'keyToKey', toKeys: [''] },
- { keys: [''], type: 'keyToKey', toKeys: [''] },
- { keys: ['s'], type: 'keyToKey', toKeys: ['c', 'l'], context: 'normal' },
- { keys: ['s'], type: 'keyToKey', toKeys: ['x', 'i'], context: 'visual'},
- { keys: ['S'], type: 'keyToKey', toKeys: ['c', 'c'], context: 'normal' },
- { keys: ['S'], type: 'keyToKey', toKeys: ['d', 'c', 'c'], context: 'visual' },
- { keys: [''], type: 'keyToKey', toKeys: ['0'] },
- { keys: [''], type: 'keyToKey', toKeys: ['$'] },
- { keys: [''], type: 'keyToKey', toKeys: [''] },
- { keys: [''], type: 'keyToKey', toKeys: [''] },
- { keys: [''], type: 'keyToKey', toKeys: ['j', '^'], context: 'normal' },
+ { keys: '', type: 'keyToKey', toKeys: 'h' },
+ { keys: '', type: 'keyToKey', toKeys: 'l' },
+ { keys: '', type: 'keyToKey', toKeys: 'k' },
+ { keys: '', type: 'keyToKey', toKeys: 'j' },
+ { keys: '', type: 'keyToKey', toKeys: 'l' },
+ { keys: '', type: 'keyToKey', toKeys: 'h', context: 'normal'},
+ { keys: '', type: 'keyToKey', toKeys: 'W' },
+ { keys: '', type: 'keyToKey', toKeys: 'B', context: 'normal' },
+ { keys: '', type: 'keyToKey', toKeys: 'w' },
+ { keys: '', type: 'keyToKey', toKeys: 'b', context: 'normal' },
+ { keys: '', type: 'keyToKey', toKeys: 'j' },
+ { keys: '', type: 'keyToKey', toKeys: 'k' },
+ { keys: '', type: 'keyToKey', toKeys: '' },
+ { keys: '', type: 'keyToKey', toKeys: '' },
+ { keys: '', type: 'keyToKey', toKeys: '', context: 'insert' },
+ { keys: '', type: 'keyToKey', toKeys: '', context: 'insert' },
+ { keys: 's', type: 'keyToKey', toKeys: 'cl', context: 'normal' },
+ { keys: 's', type: 'keyToKey', toKeys: 'xi', context: 'visual'},
+ { keys: 'S', type: 'keyToKey', toKeys: 'cc', context: 'normal' },
+ { keys: 'S', type: 'keyToKey', toKeys: 'dcc', context: 'visual' },
+ { keys: '', type: 'keyToKey', toKeys: '0' },
+ { keys: '', type: 'keyToKey', toKeys: '$' },
+ { keys: '', type: 'keyToKey', toKeys: '' },
+ { keys: '', type: 'keyToKey', toKeys: '' },
+ { keys: '', type: 'keyToKey', toKeys: 'j^', context: 'normal' },
// Motions
- { keys: ['H'], type: 'motion',
- motion: 'moveToTopLine',
- motionArgs: { linewise: true, toJumplist: true }},
- { keys: ['M'], type: 'motion',
- motion: 'moveToMiddleLine',
- motionArgs: { linewise: true, toJumplist: true }},
- { keys: ['L'], type: 'motion',
- motion: 'moveToBottomLine',
- motionArgs: { linewise: true, toJumplist: true }},
- { keys: ['h'], type: 'motion',
- motion: 'moveByCharacters',
- motionArgs: { forward: false }},
- { keys: ['l'], type: 'motion',
- motion: 'moveByCharacters',
- motionArgs: { forward: true }},
- { keys: ['j'], type: 'motion',
- motion: 'moveByLines',
- motionArgs: { forward: true, linewise: true }},
- { keys: ['k'], type: 'motion',
- motion: 'moveByLines',
- motionArgs: { forward: false, linewise: true }},
- { keys: ['g','j'], type: 'motion',
- motion: 'moveByDisplayLines',
- motionArgs: { forward: true }},
- { keys: ['g','k'], type: 'motion',
- motion: 'moveByDisplayLines',
- motionArgs: { forward: false }},
- { keys: ['w'], type: 'motion',
- motion: 'moveByWords',
- motionArgs: { forward: true, wordEnd: false }},
- { keys: ['W'], type: 'motion',
- motion: 'moveByWords',
- motionArgs: { forward: true, wordEnd: false, bigWord: true }},
- { keys: ['e'], type: 'motion',
- motion: 'moveByWords',
- motionArgs: { forward: true, wordEnd: true, inclusive: true }},
- { keys: ['E'], type: 'motion',
- motion: 'moveByWords',
- motionArgs: { forward: true, wordEnd: true, bigWord: true,
- inclusive: true }},
- { keys: ['b'], type: 'motion',
- motion: 'moveByWords',
- motionArgs: { forward: false, wordEnd: false }},
- { keys: ['B'], type: 'motion',
- motion: 'moveByWords',
- motionArgs: { forward: false, wordEnd: false, bigWord: true }},
- { keys: ['g', 'e'], type: 'motion',
- motion: 'moveByWords',
- motionArgs: { forward: false, wordEnd: true, inclusive: true }},
- { keys: ['g', 'E'], type: 'motion',
- motion: 'moveByWords',
- motionArgs: { forward: false, wordEnd: true, bigWord: true,
- inclusive: true }},
- { keys: ['{'], type: 'motion', motion: 'moveByParagraph',
- motionArgs: { forward: false, toJumplist: true }},
- { keys: ['}'], type: 'motion', motion: 'moveByParagraph',
- motionArgs: { forward: true, toJumplist: true }},
- { keys: [''], type: 'motion',
- motion: 'moveByPage', motionArgs: { forward: true }},
- { keys: [''], type: 'motion',
- motion: 'moveByPage', motionArgs: { forward: false }},
- { keys: [''], type: 'motion',
- motion: 'moveByScroll',
- motionArgs: { forward: true, explicitRepeat: true }},
- { keys: [''], type: 'motion',
- motion: 'moveByScroll',
- motionArgs: { forward: false, explicitRepeat: true }},
- { keys: ['g', 'g'], type: 'motion',
- motion: 'moveToLineOrEdgeOfDocument',
- motionArgs: { forward: false, explicitRepeat: true, linewise: true, toJumplist: true }},
- { keys: ['G'], type: 'motion',
- motion: 'moveToLineOrEdgeOfDocument',
- motionArgs: { forward: true, explicitRepeat: true, linewise: true, toJumplist: true }},
- { keys: ['0'], type: 'motion', motion: 'moveToStartOfLine' },
- { keys: ['^'], type: 'motion',
- motion: 'moveToFirstNonWhiteSpaceCharacter' },
- { keys: ['+'], type: 'motion',
- motion: 'moveByLines',
- motionArgs: { forward: true, toFirstChar:true }},
- { keys: ['-'], type: 'motion',
- motion: 'moveByLines',
- motionArgs: { forward: false, toFirstChar:true }},
- { keys: ['_'], type: 'motion',
- motion: 'moveByLines',
- motionArgs: { forward: true, toFirstChar:true, repeatOffset:-1 }},
- { keys: ['$'], type: 'motion',
- motion: 'moveToEol',
- motionArgs: { inclusive: true }},
- { keys: ['%'], type: 'motion',
- motion: 'moveToMatchedSymbol',
- motionArgs: { inclusive: true, toJumplist: true }},
- { keys: ['f', 'character'], type: 'motion',
- motion: 'moveToCharacter',
- motionArgs: { forward: true , inclusive: true }},
- { keys: ['F', 'character'], type: 'motion',
- motion: 'moveToCharacter',
- motionArgs: { forward: false }},
- { keys: ['t', 'character'], type: 'motion',
- motion: 'moveTillCharacter',
- motionArgs: { forward: true, inclusive: true }},
- { keys: ['T', 'character'], type: 'motion',
- motion: 'moveTillCharacter',
- motionArgs: { forward: false }},
- { keys: [';'], type: 'motion', motion: 'repeatLastCharacterSearch',
- motionArgs: { forward: true }},
- { keys: [','], type: 'motion', motion: 'repeatLastCharacterSearch',
- motionArgs: { forward: false }},
- { keys: ['\'', 'character'], type: 'motion', motion: 'goToMark',
- motionArgs: {toJumplist: true}},
- { keys: ['`', 'character'], type: 'motion', motion: 'goToMark',
- motionArgs: {toJumplist: true}},
- { keys: [']', '`'], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true } },
- { keys: ['[', '`'], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false } },
- { keys: [']', '\''], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true, linewise: true } },
- { keys: ['[', '\''], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false, linewise: true } },
- { keys: [']', 'character'], type: 'motion',
- motion: 'moveToSymbol',
- motionArgs: { forward: true, toJumplist: true}},
- { keys: ['[', 'character'], type: 'motion',
- motion: 'moveToSymbol',
- motionArgs: { forward: false, toJumplist: true}},
- { keys: ['|'], type: 'motion',
- motion: 'moveToColumn',
- motionArgs: { }},
- { keys: ['o'], type: 'motion', motion: 'moveToOtherHighlightedEnd', motionArgs: { },context:'visual'},
+ { keys: 'H', type: 'motion', motion: 'moveToTopLine', motionArgs: { linewise: true, toJumplist: true }},
+ { keys: 'M', type: 'motion', motion: 'moveToMiddleLine', motionArgs: { linewise: true, toJumplist: true }},
+ { keys: 'L', type: 'motion', motion: 'moveToBottomLine', motionArgs: { linewise: true, toJumplist: true }},
+ { keys: 'h', type: 'motion', motion: 'moveByCharacters', motionArgs: { forward: false }},
+ { keys: 'l', type: 'motion', motion: 'moveByCharacters', motionArgs: { forward: true }},
+ { keys: 'j', type: 'motion', motion: 'moveByLines', motionArgs: { forward: true, linewise: true }},
+ { keys: 'k', type: 'motion', motion: 'moveByLines', motionArgs: { forward: false, linewise: true }},
+ { keys: 'gj', type: 'motion', motion: 'moveByDisplayLines', motionArgs: { forward: true }},
+ { keys: 'gk', type: 'motion', motion: 'moveByDisplayLines', motionArgs: { forward: false }},
+ { keys: 'w', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: false }},
+ { keys: 'W', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: false, bigWord: true }},
+ { keys: 'e', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: true, inclusive: true }},
+ { keys: 'E', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: true, bigWord: true, inclusive: true }},
+ { keys: 'b', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: false }},
+ { keys: 'B', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: false, bigWord: true }},
+ { keys: 'ge', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: true, inclusive: true }},
+ { keys: 'gE', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: true, bigWord: true, inclusive: true }},
+ { keys: '{', type: 'motion', motion: 'moveByParagraph', motionArgs: { forward: false, toJumplist: true }},
+ { keys: '}', type: 'motion', motion: 'moveByParagraph', motionArgs: { forward: true, toJumplist: true }},
+ { keys: '', type: 'motion', motion: 'moveByPage', motionArgs: { forward: true }},
+ { keys: '', type: 'motion', motion: 'moveByPage', motionArgs: { forward: false }},
+ { keys: '', type: 'motion', motion: 'moveByScroll', motionArgs: { forward: true, explicitRepeat: true }},
+ { keys: '', type: 'motion', motion: 'moveByScroll', motionArgs: { forward: false, explicitRepeat: true }},
+ { keys: 'gg', type: 'motion', motion: 'moveToLineOrEdgeOfDocument', motionArgs: { forward: false, explicitRepeat: true, linewise: true, toJumplist: true }},
+ { keys: 'G', type: 'motion', motion: 'moveToLineOrEdgeOfDocument', motionArgs: { forward: true, explicitRepeat: true, linewise: true, toJumplist: true }},
+ { keys: '0', type: 'motion', motion: 'moveToStartOfLine' },
+ { keys: '^', type: 'motion', motion: 'moveToFirstNonWhiteSpaceCharacter' },
+ { keys: '+', type: 'motion', motion: 'moveByLines', motionArgs: { forward: true, toFirstChar:true }},
+ { keys: '-', type: 'motion', motion: 'moveByLines', motionArgs: { forward: false, toFirstChar:true }},
+ { keys: '_', type: 'motion', motion: 'moveByLines', motionArgs: { forward: true, toFirstChar:true, repeatOffset:-1 }},
+ { keys: '$', type: 'motion', motion: 'moveToEol', motionArgs: { inclusive: true }},
+ { keys: '%', type: 'motion', motion: 'moveToMatchedSymbol', motionArgs: { inclusive: true, toJumplist: true }},
+ { keys: 'f', type: 'motion', motion: 'moveToCharacter', motionArgs: { forward: true , inclusive: true }},
+ { keys: 'F', type: 'motion', motion: 'moveToCharacter', motionArgs: { forward: false }},
+ { keys: 't', type: 'motion', motion: 'moveTillCharacter', motionArgs: { forward: true, inclusive: true }},
+ { keys: 'T', type: 'motion', motion: 'moveTillCharacter', motionArgs: { forward: false }},
+ { keys: ';', type: 'motion', motion: 'repeatLastCharacterSearch', motionArgs: { forward: true }},
+ { keys: ',', type: 'motion', motion: 'repeatLastCharacterSearch', motionArgs: { forward: false }},
+ { keys: '\'', type: 'motion', motion: 'goToMark', motionArgs: {toJumplist: true, linewise: true}},
+ { keys: '`', type: 'motion', motion: 'goToMark', motionArgs: {toJumplist: true}},
+ { keys: ']`', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true } },
+ { keys: '[`', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false } },
+ { keys: ']\'', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true, linewise: true } },
+ { keys: '[\'', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false, linewise: true } },
+ // the next two aren't motions but must come before more general motion declarations
+ { keys: ']p', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: true, isEdit: true, matchIndent: true}},
+ { keys: '[p', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: false, isEdit: true, matchIndent: true}},
+ { keys: ']', type: 'motion', motion: 'moveToSymbol', motionArgs: { forward: true, toJumplist: true}},
+ { keys: '[', type: 'motion', motion: 'moveToSymbol', motionArgs: { forward: false, toJumplist: true}},
+ { keys: '|', type: 'motion', motion: 'moveToColumn'},
+ { keys: 'o', type: 'motion', motion: 'moveToOtherHighlightedEnd', context:'visual'},
+ { keys: 'O', type: 'motion', motion: 'moveToOtherHighlightedEnd', motionArgs: {sameLine: true}, context:'visual'},
// Operators
- { keys: ['d'], type: 'operator', operator: 'delete' },
- { keys: ['y'], type: 'operator', operator: 'yank' },
- { keys: ['c'], type: 'operator', operator: 'change' },
- { keys: ['>'], type: 'operator', operator: 'indent',
- operatorArgs: { indentRight: true }},
- { keys: ['<'], type: 'operator', operator: 'indent',
- operatorArgs: { indentRight: false }},
- { keys: ['g', '~'], type: 'operator', operator: 'swapcase' },
- { keys: ['n'], type: 'motion', motion: 'findNext',
- motionArgs: { forward: true, toJumplist: true }},
- { keys: ['N'], type: 'motion', motion: 'findNext',
- motionArgs: { forward: false, toJumplist: true }},
+ { keys: 'd', type: 'operator', operator: 'delete' },
+ { keys: 'y', type: 'operator', operator: 'yank' },
+ { keys: 'c', type: 'operator', operator: 'change' },
+ { keys: '>', type: 'operator', operator: 'indent', operatorArgs: { indentRight: true }},
+ { keys: '<', type: 'operator', operator: 'indent', operatorArgs: { indentRight: false }},
+ { keys: 'g~', type: 'operator', operator: 'changeCase' },
+ { keys: 'gu', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: true}, isEdit: true },
+ { keys: 'gU', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: false}, isEdit: true },
+ { keys: 'n', type: 'motion', motion: 'findNext', motionArgs: { forward: true, toJumplist: true }},
+ { keys: 'N', type: 'motion', motion: 'findNext', motionArgs: { forward: false, toJumplist: true }},
// Operator-Motion dual commands
- { keys: ['x'], type: 'operatorMotion', operator: 'delete',
- motion: 'moveByCharacters', motionArgs: { forward: true },
- operatorMotionArgs: { visualLine: false }},
- { keys: ['X'], type: 'operatorMotion', operator: 'delete',
- motion: 'moveByCharacters', motionArgs: { forward: false },
- operatorMotionArgs: { visualLine: true }},
- { keys: ['D'], type: 'operatorMotion', operator: 'delete',
- motion: 'moveToEol', motionArgs: { inclusive: true },
- operatorMotionArgs: { visualLine: true }},
- { keys: ['Y'], type: 'operatorMotion', operator: 'yank',
- motion: 'moveToEol', motionArgs: { inclusive: true },
- operatorMotionArgs: { visualLine: true }},
- { keys: ['C'], type: 'operatorMotion',
- operator: 'change',
- motion: 'moveToEol', motionArgs: { inclusive: true },
- operatorMotionArgs: { visualLine: true }},
- { keys: ['~'], type: 'operatorMotion',
- operator: 'swapcase', operatorArgs: { shouldMoveCursor: true },
- motion: 'moveByCharacters', motionArgs: { forward: true }},
+ { keys: 'x', type: 'operatorMotion', operator: 'delete', motion: 'moveByCharacters', motionArgs: { forward: true }, operatorMotionArgs: { visualLine: false }},
+ { keys: 'X', type: 'operatorMotion', operator: 'delete', motion: 'moveByCharacters', motionArgs: { forward: false }, operatorMotionArgs: { visualLine: true }},
+ { keys: 'D', type: 'operatorMotion', operator: 'delete', motion: 'moveToEol', motionArgs: { inclusive: true }, context: 'normal'},
+ { keys: 'D', type: 'operator', operator: 'delete', operatorArgs: { linewise: true }, context: 'visual'},
+ { keys: 'Y', type: 'operatorMotion', operator: 'yank', motion: 'moveToEol', motionArgs: { inclusive: true }, context: 'normal'},
+ { keys: 'Y', type: 'operator', operator: 'yank', operatorArgs: { linewise: true }, context: 'visual'},
+ { keys: 'C', type: 'operatorMotion', operator: 'change', motion: 'moveToEol', motionArgs: { inclusive: true }, context: 'normal'},
+ { keys: 'C', type: 'operator', operator: 'change', operatorArgs: { linewise: true }, context: 'visual'},
+ { keys: '~', type: 'operatorMotion', operator: 'changeCase', motion: 'moveByCharacters', motionArgs: { forward: true }, operatorArgs: { shouldMoveCursor: true }, context: 'normal'},
+ { keys: '~', type: 'operator', operator: 'changeCase', context: 'visual'},
+ { keys: '', type: 'operatorMotion', operator: 'delete', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: false }, context: 'insert' },
// Actions
- { keys: [''], type: 'action', action: 'jumpListWalk',
- actionArgs: { forward: true }},
- { keys: [''], type: 'action', action: 'jumpListWalk',
- actionArgs: { forward: false }},
- { keys: [''], type: 'action',
- action: 'scroll',
- actionArgs: { forward: true, linewise: true }},
- { keys: [''], type: 'action',
- action: 'scroll',
- actionArgs: { forward: false, linewise: true }},
- { keys: ['a'], type: 'action', action: 'enterInsertMode', isEdit: true,
- actionArgs: { insertAt: 'charAfter' }},
- { keys: ['A'], type: 'action', action: 'enterInsertMode', isEdit: true,
- actionArgs: { insertAt: 'eol' }},
- { keys: ['i'], type: 'action', action: 'enterInsertMode', isEdit: true,
- actionArgs: { insertAt: 'inplace' }},
- { keys: ['I'], type: 'action', action: 'enterInsertMode', isEdit: true,
- actionArgs: { insertAt: 'firstNonBlank' }},
- { keys: ['o'], type: 'action', action: 'newLineAndEnterInsertMode',
- isEdit: true, interlaceInsertRepeat: true,
- actionArgs: { after: true }},
- { keys: ['O'], type: 'action', action: 'newLineAndEnterInsertMode',
- isEdit: true, interlaceInsertRepeat: true,
- actionArgs: { after: false }},
- { keys: ['v'], type: 'action', action: 'toggleVisualMode' },
- { keys: ['V'], type: 'action', action: 'toggleVisualMode',
- actionArgs: { linewise: true }},
- { keys: ['g', 'v'], type: 'action', action: 'reselectLastSelection' },
- { keys: ['J'], type: 'action', action: 'joinLines', isEdit: true },
- { keys: ['p'], type: 'action', action: 'paste', isEdit: true,
- actionArgs: { after: true, isEdit: true }},
- { keys: ['P'], type: 'action', action: 'paste', isEdit: true,
- actionArgs: { after: false, isEdit: true }},
- { keys: ['r', 'character'], type: 'action', action: 'replace', isEdit: true },
- { keys: ['@', 'character'], type: 'action', action: 'replayMacro' },
- { keys: ['q', 'character'], type: 'action', action: 'enterMacroRecordMode' },
+ { keys: '', type: 'action', action: 'jumpListWalk', actionArgs: { forward: true }},
+ { keys: '', type: 'action', action: 'jumpListWalk', actionArgs: { forward: false }},
+ { keys: '', type: 'action', action: 'scroll', actionArgs: { forward: true, linewise: true }},
+ { keys: '', type: 'action', action: 'scroll', actionArgs: { forward: false, linewise: true }},
+ { keys: 'a', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'charAfter' }, context: 'normal' },
+ { keys: 'A', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'eol' }, context: 'normal' },
+ { keys: 'A', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'endOfSelectedArea' }, context: 'visual' },
+ { keys: 'i', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'inplace' }, context: 'normal' },
+ { keys: 'I', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'firstNonBlank'}, context: 'normal' },
+ { keys: 'I', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'startOfSelectedArea' }, context: 'visual' },
+ { keys: 'o', type: 'action', action: 'newLineAndEnterInsertMode', isEdit: true, interlaceInsertRepeat: true, actionArgs: { after: true }, context: 'normal' },
+ { keys: 'O', type: 'action', action: 'newLineAndEnterInsertMode', isEdit: true, interlaceInsertRepeat: true, actionArgs: { after: false }, context: 'normal' },
+ { keys: 'v', type: 'action', action: 'toggleVisualMode' },
+ { keys: 'V', type: 'action', action: 'toggleVisualMode', actionArgs: { linewise: true }},
+ { keys: '', type: 'action', action: 'toggleVisualMode', actionArgs: { blockwise: true }},
+ { keys: 'gv', type: 'action', action: 'reselectLastSelection' },
+ { keys: 'J', type: 'action', action: 'joinLines', isEdit: true },
+ { keys: 'p', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: true, isEdit: true }},
+ { keys: 'P', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: false, isEdit: true }},
+ { keys: 'r', type: 'action', action: 'replace', isEdit: true },
+ { keys: '@', type: 'action', action: 'replayMacro' },
+ { keys: 'q', type: 'action', action: 'enterMacroRecordMode' },
// Handle Replace-mode as a special case of insert mode.
- { keys: ['R'], type: 'action', action: 'enterInsertMode', isEdit: true,
- actionArgs: { replace: true }},
- { keys: ['u'], type: 'action', action: 'undo' },
- { keys: [''], type: 'action', action: 'redo' },
- { keys: ['m', 'character'], type: 'action', action: 'setMark' },
- { keys: ['"', 'character'], type: 'action', action: 'setRegister' },
- { keys: ['z', 'z'], type: 'action', action: 'scrollToCursor',
- actionArgs: { position: 'center' }},
- { keys: ['z', '.'], type: 'action', action: 'scrollToCursor',
- actionArgs: { position: 'center' },
- motion: 'moveToFirstNonWhiteSpaceCharacter' },
- { keys: ['z', 't'], type: 'action', action: 'scrollToCursor',
- actionArgs: { position: 'top' }},
- { keys: ['z', ''], type: 'action', action: 'scrollToCursor',
- actionArgs: { position: 'top' },
- motion: 'moveToFirstNonWhiteSpaceCharacter' },
- { keys: ['z', '-'], type: 'action', action: 'scrollToCursor',
- actionArgs: { position: 'bottom' }},
- { keys: ['z', 'b'], type: 'action', action: 'scrollToCursor',
- actionArgs: { position: 'bottom' },
- motion: 'moveToFirstNonWhiteSpaceCharacter' },
- { keys: ['.'], type: 'action', action: 'repeatLastEdit' },
- { keys: [''], type: 'action', action: 'incrementNumberToken',
- isEdit: true,
- actionArgs: {increase: true, backtrack: false}},
- { keys: [''], type: 'action', action: 'incrementNumberToken',
- isEdit: true,
- actionArgs: {increase: false, backtrack: false}},
+ { keys: 'R', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { replace: true }},
+ { keys: 'u', type: 'action', action: 'undo', context: 'normal' },
+ { keys: 'u', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: true}, context: 'visual', isEdit: true },
+ { keys: 'U', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: false}, context: 'visual', isEdit: true },
+ { keys: '', type: 'action', action: 'redo' },
+ { keys: 'm', type: 'action', action: 'setMark' },
+ { keys: '"', type: 'action', action: 'setRegister' },
+ { keys: 'zz', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'center' }},
+ { keys: 'z.', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'center' }, motion: 'moveToFirstNonWhiteSpaceCharacter' },
+ { keys: 'zt', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'top' }},
+ { keys: 'z', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'top' }, motion: 'moveToFirstNonWhiteSpaceCharacter' },
+ { keys: 'z-', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'bottom' }},
+ { keys: 'zb', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'bottom' }, motion: 'moveToFirstNonWhiteSpaceCharacter' },
+ { keys: '.', type: 'action', action: 'repeatLastEdit' },
+ { keys: '', type: 'action', action: 'incrementNumberToken', isEdit: true, actionArgs: {increase: true, backtrack: false}},
+ { keys: '', type: 'action', action: 'incrementNumberToken', isEdit: true, actionArgs: {increase: false, backtrack: false}},
// Text object motions
- { keys: ['a', 'character'], type: 'motion',
- motion: 'textObjectManipulation' },
- { keys: ['i', 'character'], type: 'motion',
- motion: 'textObjectManipulation',
- motionArgs: { textObjectInner: true }},
+ { keys: 'a', type: 'motion', motion: 'textObjectManipulation' },
+ { keys: 'i', type: 'motion', motion: 'textObjectManipulation', motionArgs: { textObjectInner: true }},
// Search
- { keys: ['/'], type: 'search',
- searchArgs: { forward: true, querySrc: 'prompt', toJumplist: true }},
- { keys: ['?'], type: 'search',
- searchArgs: { forward: false, querySrc: 'prompt', toJumplist: true }},
- { keys: ['*'], type: 'search',
- searchArgs: { forward: true, querySrc: 'wordUnderCursor', toJumplist: true }},
- { keys: ['#'], type: 'search',
- searchArgs: { forward: false, querySrc: 'wordUnderCursor', toJumplist: true }},
+ { keys: '/', type: 'search', searchArgs: { forward: true, querySrc: 'prompt', toJumplist: true }},
+ { keys: '?', type: 'search', searchArgs: { forward: false, querySrc: 'prompt', toJumplist: true }},
+ { keys: '*', type: 'search', searchArgs: { forward: true, querySrc: 'wordUnderCursor', wholeWordOnly: true, toJumplist: true }},
+ { keys: '#', type: 'search', searchArgs: { forward: false, querySrc: 'wordUnderCursor', wholeWordOnly: true, toJumplist: true }},
+ { keys: 'g*', type: 'search', searchArgs: { forward: true, querySrc: 'wordUnderCursor', toJumplist: true }},
+ { keys: 'g#', type: 'search', searchArgs: { forward: false, querySrc: 'wordUnderCursor', toJumplist: true }},
// Ex command
- { keys: [':'], type: 'ex' }
+ { keys: ':', type: 'ex' }
];
var Pos = CodeMirror.Pos;
var Vim = function() {
- CodeMirror.defineOption('vimMode', false, function(cm, val) {
- if (val) {
- cm.setOption('keyMap', 'vim');
- cm.setOption('disableInput', true);
- CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
- cm.on('beforeSelectionChange', beforeSelectionChange);
- maybeInitVimState(cm);
- CodeMirror.on(cm.getInputField(), 'paste', getOnPasteFn(cm));
- } else if (cm.state.vim) {
- cm.setOption('keyMap', 'default');
- cm.setOption('disableInput', false);
- cm.off('beforeSelectionChange', beforeSelectionChange);
- CodeMirror.off(cm.getInputField(), 'paste', getOnPasteFn(cm));
- cm.state.vim = null;
- }
- });
- function beforeSelectionChange(cm, obj) {
- var vim = cm.state.vim;
- if (vim.insertMode || vim.exMode) return;
-
- var head = obj.ranges[0].head;
- var anchor = obj.ranges[0].anchor;
- if (head.ch && head.ch == cm.doc.getLine(head.line).length) {
- var pos = Pos(head.line, head.ch - 1);
- obj.update([{anchor: cursorEqual(head, anchor) ? pos : anchor,
- head: pos}]);
- }
+ function enterVimMode(cm) {
+ cm.setOption('disableInput', true);
+ cm.setOption('showCursorWhenSelecting', false);
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
+ cm.on('cursorActivity', onCursorActivity);
+ maybeInitVimState(cm);
+ CodeMirror.on(cm.getInputField(), 'paste', getOnPasteFn(cm));
}
+
+ function leaveVimMode(cm) {
+ cm.setOption('disableInput', false);
+ cm.off('cursorActivity', onCursorActivity);
+ CodeMirror.off(cm.getInputField(), 'paste', getOnPasteFn(cm));
+ cm.state.vim = null;
+ }
+
+ function detachVimMap(cm, next) {
+ if (this == CodeMirror.keyMap.vim)
+ CodeMirror.rmClass(cm.getWrapperElement(), "cm-fat-cursor");
+
+ if (!next || next.attach != attachVimMap)
+ leaveVimMode(cm, false);
+ }
+ function attachVimMap(cm, prev) {
+ if (this == CodeMirror.keyMap.vim)
+ CodeMirror.addClass(cm.getWrapperElement(), "cm-fat-cursor");
+
+ if (!prev || prev.attach != attachVimMap)
+ enterVimMode(cm);
+ }
+
+ // Deprecated, simply setting the keymap works again.
+ CodeMirror.defineOption('vimMode', false, function(cm, val, prev) {
+ if (val && cm.getOption("keyMap") != "vim")
+ cm.setOption("keyMap", "vim");
+ else if (!val && prev != CodeMirror.Init && /^vim/.test(cm.getOption("keyMap")))
+ cm.setOption("keyMap", "default");
+ });
+
+ function cmKey(key, cm) {
+ if (!cm) { return undefined; }
+ var vimKey = cmKeyToVimKey(key);
+ if (!vimKey) {
+ return false;
+ }
+ var cmd = CodeMirror.Vim.findKey(cm, vimKey);
+ if (typeof cmd == 'function') {
+ CodeMirror.signal(cm, 'vim-keypress', vimKey);
+ }
+ return cmd;
+ }
+
+ var modifiers = {'Shift': 'S', 'Ctrl': 'C', 'Alt': 'A', 'Cmd': 'D', 'Mod': 'A'};
+ var specialKeys = {Enter:'CR',Backspace:'BS',Delete:'Del'};
+ function cmKeyToVimKey(key) {
+ if (key.charAt(0) == '\'') {
+ // Keypress character binding of format "'a'"
+ return key.charAt(1);
+ }
+ var pieces = key.split('-');
+ if (/-$/.test(key)) {
+ // If the - key was typed, split will result in 2 extra empty strings
+ // in the array. Replace them with 1 '-'.
+ pieces.splice(-2, 2, '-');
+ }
+ var lastPiece = pieces[pieces.length - 1];
+ if (pieces.length == 1 && pieces[0].length == 1) {
+ // No-modifier bindings use literal character bindings above. Skip.
+ return false;
+ } else if (pieces.length == 2 && pieces[0] == 'Shift' && lastPiece.length == 1) {
+ // Ignore Shift+char bindings as they should be handled by literal character.
+ return false;
+ }
+ var hasCharacter = false;
+ for (var i = 0; i < pieces.length; i++) {
+ var piece = pieces[i];
+ if (piece in modifiers) { pieces[i] = modifiers[piece]; }
+ else { hasCharacter = true; }
+ if (piece in specialKeys) { pieces[i] = specialKeys[piece]; }
+ }
+ if (!hasCharacter) {
+ // Vim does not support modifier only keys.
+ return false;
+ }
+ // TODO: Current bindings expect the character to be lower case, but
+ // it looks like vim key notation uses upper case.
+ if (isUpperCase(lastPiece)) {
+ pieces[pieces.length - 1] = lastPiece.toLowerCase();
+ }
+ return '<' + pieces.join('-') + '>';
+ }
+
function getOnPasteFn(cm) {
var vim = cm.state.vim;
if (!vim.onPasteFn) {
@@ -392,11 +347,8 @@
var upperCaseAlphabet = makeKeyRange(65, 26);
var lowerCaseAlphabet = makeKeyRange(97, 26);
var numbers = makeKeyRange(48, 10);
- var specialSymbols = '~`!@#$%^&*()_-+=[{}]\\|/?.,<>:;"\''.split('');
- var specialKeys = ['Left', 'Right', 'Up', 'Down', 'Space', 'Backspace',
- 'Esc', 'Home', 'End', 'PageUp', 'PageDown', 'Enter'];
var validMarks = [].concat(upperCaseAlphabet, lowerCaseAlphabet, numbers, ['<', '>']);
- var validRegisters = [].concat(upperCaseAlphabet, lowerCaseAlphabet, numbers, ['-', '"']);
+ var validRegisters = [].concat(upperCaseAlphabet, lowerCaseAlphabet, numbers, ['-', '"', '.', ':', '/']);
function isLine(cm, line) {
return line >= cm.firstLine() && line <= cm.lastLine();
@@ -549,13 +501,16 @@
this.latestRegister = undefined;
this.isPlaying = false;
this.isRecording = false;
+ this.replaySearchQueries = [];
this.onRecordingDone = undefined;
this.lastInsertModeChanges = createInsertModeChanges();
}
MacroModeState.prototype = {
exitMacroRecordMode: function() {
var macroModeState = vimGlobalState.macroModeState;
- macroModeState.onRecordingDone(); // close dialog
+ if (macroModeState.onRecordingDone) {
+ macroModeState.onRecordingDone(); // close dialog
+ }
macroModeState.onRecordingDone = undefined;
macroModeState.isRecording = false;
},
@@ -565,8 +520,10 @@
if (register) {
register.clear();
this.latestRegister = registerName;
- this.onRecordingDone = cm.openDialog(
- '(recording)['+registerName+']', null, {bottom:true});
+ if (cm.openDialog) {
+ this.onRecordingDone = cm.openDialog(
+ '(recording)['+registerName+']', null, {bottom:true});
+ }
this.isRecording = true;
}
}
@@ -595,6 +552,8 @@
// executed in between.
lastMotion: null,
marks: {},
+ // Mark for rendering fake cursor for visual mode.
+ fakeCursor: null,
insertMode: false,
// Repeat count for changes made in insert mode, triggered by key
// sequences like 3,i. Only exists when insertMode is true.
@@ -602,7 +561,11 @@
visualMode: false,
// If we are in visual line mode. No effect if visualMode is false.
visualLine: false,
- lastSelection: null
+ visualBlock: false,
+ lastSelection: null,
+ lastPastedText: null,
+ sel: {
+ }
};
}
return cm.state.vim;
@@ -614,11 +577,17 @@
searchQuery: null,
// Whether we are searching backwards.
searchIsReversed: false,
+ // Replace part of the last substituted pattern
+ lastSubstituteReplacePart: undefined,
jumpList: createCircularJumpList(),
macroModeState: new MacroModeState,
// Recording latest f, t, F or T motion command.
lastChararacterSearch: {increment:0, forward:true, selectedCharacter:''},
- registerController: new RegisterController({})
+ registerController: new RegisterController({}),
+ // search history buffer
+ searchHistoryController: new HistoryController({}),
+ // ex Command history buffer
+ exCommandHistoryController : new HistoryController({})
};
for (var optionName in options) {
var option = options[optionName];
@@ -626,6 +595,7 @@
}
}
+ var lastInsertModeKeyTimer;
var vimApi= {
buildKeyMap: function() {
// TODO: Convert keymap into dictionary format for fast lookup.
@@ -646,6 +616,8 @@
// Testing hook.
maybeInitVimState_: maybeInitVimState,
+ suppressErrorLogging: false,
+
InsertModeKey: InsertModeKey,
map: function(lhs, rhs, ctx) {
// Add user defined key bindings.
@@ -661,59 +633,145 @@
exCommands[name]=func;
exCommandDispatcher.commandMap_[prefix]={name:name, shortName:prefix, type:'api'};
},
- // This is the outermost function called by CodeMirror, after keys have
- // been mapped to their Vim equivalents.
- handleKey: function(cm, key) {
- var command;
+ handleKey: function (cm, key, origin) {
+ var command = this.findKey(cm, key, origin);
+ if (typeof command === 'function') {
+ return command();
+ }
+ },
+ /**
+ * This is the outermost function called by CodeMirror, after keys have
+ * been mapped to their Vim equivalents.
+ *
+ * Finds a command based on the key (and cached keys if there is a
+ * multi-key sequence). Returns `undefined` if no key is matched, a noop
+ * function if a partial match is found (multi-key), and a function to
+ * execute the bound command if a a key is matched. The function always
+ * returns true.
+ */
+ findKey: function(cm, key, origin) {
var vim = maybeInitVimState(cm);
- var macroModeState = vimGlobalState.macroModeState;
- if (macroModeState.isRecording) {
- if (key == 'q') {
- macroModeState.exitMacroRecordMode();
- vim.inputState = new InputState();
- return;
- }
- }
- if (key == '') {
- // Clear input state and get back to normal mode.
- vim.inputState = new InputState();
- if (vim.visualMode) {
- exitVisualMode(cm);
- }
- return;
- }
- // Enter visual mode when the mouse selects text.
- if (!vim.visualMode &&
- !cursorEqual(cm.getCursor('head'), cm.getCursor('anchor'))) {
- vim.visualMode = true;
- vim.visualLine = false;
- CodeMirror.signal(cm, "vim-mode-change", {mode: "visual"});
- cm.on('mousedown', exitVisualMode);
- }
- if (key != '0' || (key == '0' && vim.inputState.getRepeat() === 0)) {
- // Have to special case 0 since it's both a motion and a number.
- command = commandDispatcher.matchCommand(key, defaultKeymap, vim);
- }
- if (!command) {
- if (isNumber(key)) {
- // Increment count unless count is 0 and key is 0.
- vim.inputState.pushRepeatDigit(key);
- }
+ function handleMacroRecording() {
+ var macroModeState = vimGlobalState.macroModeState;
if (macroModeState.isRecording) {
- logKey(macroModeState, key);
+ if (key == 'q') {
+ macroModeState.exitMacroRecordMode();
+ clearInputState(cm);
+ return true;
+ }
+ if (origin != 'mapping') {
+ logKey(macroModeState, key);
+ }
}
- return;
}
- if (command.type == 'keyToKey') {
+ function handleEsc() {
+ if (key == '') {
+ // Clear input state and get back to normal mode.
+ clearInputState(cm);
+ if (vim.visualMode) {
+ exitVisualMode(cm);
+ } else if (vim.insertMode) {
+ exitInsertMode(cm);
+ }
+ return true;
+ }
+ }
+ function doKeyToKey(keys) {
// TODO: prevent infinite recursion.
- for (var i = 0; i < command.toKeys.length; i++) {
- this.handleKey(cm, command.toKeys[i]);
+ var match;
+ while (keys) {
+ // Pull off one command key, which is either a single character
+ // or a special sequence wrapped in '<' and '>', e.g. ''.
+ match = (/<\w+-.+?>|<\w+>|./).exec(keys);
+ key = match[0];
+ keys = keys.substring(match.index + key.length);
+ CodeMirror.Vim.handleKey(cm, key, 'mapping');
}
+ }
+
+ function handleKeyInsertMode() {
+ if (handleEsc()) { return true; }
+ var keys = vim.inputState.keyBuffer = vim.inputState.keyBuffer + key;
+ var keysAreChars = key.length == 1;
+ var match = commandDispatcher.matchCommand(keys, defaultKeymap, vim.inputState, 'insert');
+ // Need to check all key substrings in insert mode.
+ while (keys.length > 1 && match.type != 'full') {
+ var keys = vim.inputState.keyBuffer = keys.slice(1);
+ var thisMatch = commandDispatcher.matchCommand(keys, defaultKeymap, vim.inputState, 'insert');
+ if (thisMatch.type != 'none') { match = thisMatch; }
+ }
+ if (match.type == 'none') { clearInputState(cm); return false; }
+ else if (match.type == 'partial') {
+ if (lastInsertModeKeyTimer) { window.clearTimeout(lastInsertModeKeyTimer); }
+ lastInsertModeKeyTimer = window.setTimeout(
+ function() { if (vim.insertMode && vim.inputState.keyBuffer) { clearInputState(cm); } },
+ getOption('insertModeEscKeysTimeout'));
+ return !keysAreChars;
+ }
+
+ if (lastInsertModeKeyTimer) { window.clearTimeout(lastInsertModeKeyTimer); }
+ if (keysAreChars) {
+ var here = cm.getCursor();
+ cm.replaceRange('', offsetCursor(here, 0, -(keys.length - 1)), here, '+input');
+ }
+ clearInputState(cm);
+ return match.command;
+ }
+
+ function handleKeyNonInsertMode() {
+ if (handleMacroRecording() || handleEsc()) { return true; };
+
+ var keys = vim.inputState.keyBuffer = vim.inputState.keyBuffer + key;
+ if (/^[1-9]\d*$/.test(keys)) { return true; }
+
+ var keysMatcher = /^(\d*)(.*)$/.exec(keys);
+ if (!keysMatcher) { clearInputState(cm); return false; }
+ var context = vim.visualMode ? 'visual' :
+ 'normal';
+ var match = commandDispatcher.matchCommand(keysMatcher[2] || keysMatcher[1], defaultKeymap, vim.inputState, context);
+ if (match.type == 'none') { clearInputState(cm); return false; }
+ else if (match.type == 'partial') { return true; }
+
+ vim.inputState.keyBuffer = '';
+ var keysMatcher = /^(\d*)(.*)$/.exec(keys);
+ if (keysMatcher[1] && keysMatcher[1] != '0') {
+ vim.inputState.pushRepeatDigit(keysMatcher[1]);
+ }
+ return match.command;
+ }
+
+ var command;
+ if (vim.insertMode) { command = handleKeyInsertMode(); }
+ else { command = handleKeyNonInsertMode(); }
+ if (command === false) {
+ return undefined;
+ } else if (command === true) {
+ // TODO: Look into using CodeMirror's multi-key handling.
+ // Return no-op since we are caching the key. Counts as handled, but
+ // don't want act on it just yet.
+ return function() {};
} else {
- if (macroModeState.isRecording) {
- logKey(macroModeState, key);
- }
- commandDispatcher.processCommand(cm, vim, command);
+ return function() {
+ return cm.operation(function() {
+ cm.curOp.isVimOp = true;
+ try {
+ if (command.type == 'keyToKey') {
+ doKeyToKey(command.toKeys);
+ } else {
+ commandDispatcher.processCommand(cm, vim, command);
+ }
+ } catch (e) {
+ // clear VIM state in case it's in a bad state.
+ cm.state.vim = undefined;
+ maybeInitVimState(cm);
+ if (!CodeMirror.Vim.suppressErrorLogging) {
+ console['log'](e);
+ }
+ throw e;
+ }
+ return true;
+ });
+ };
}
},
handleEx: function(cm, input) {
@@ -754,27 +812,37 @@
return repeat;
};
+ function clearInputState(cm, reason) {
+ cm.state.vim.inputState = new InputState();
+ CodeMirror.signal(cm, 'vim-command-done', reason);
+ }
+
/*
* Register stores information about copy and paste registers. Besides
* text, a register must store whether it is linewise (i.e., when it is
* pasted, should it insert itself into a new line, or should the text be
* inserted at the cursor position.)
*/
- function Register(text, linewise) {
+ function Register(text, linewise, blockwise) {
this.clear();
this.keyBuffer = [text || ''];
this.insertModeChanges = [];
+ this.searchQueries = [];
this.linewise = !!linewise;
+ this.blockwise = !!blockwise;
}
Register.prototype = {
- setText: function(text, linewise) {
+ setText: function(text, linewise, blockwise) {
this.keyBuffer = [text || ''];
this.linewise = !!linewise;
+ this.blockwise = !!blockwise;
},
pushText: function(text, linewise) {
// if this register has ever been set to linewise, use linewise.
- if (linewise || this.linewise) {
- this.keyBuffer.push('\n');
+ if (linewise) {
+ if (!this.linewise) {
+ this.keyBuffer.push('\n');
+ }
this.linewise = true;
}
this.keyBuffer.push(text);
@@ -782,9 +850,13 @@
pushInsertModeChanges: function(changes) {
this.insertModeChanges.push(createInsertModeChanges(changes));
},
+ pushSearchQuery: function(query) {
+ this.searchQueries.push(query);
+ },
clear: function() {
this.keyBuffer = [];
this.insertModeChanges = [];
+ this.searchQueries = [];
this.linewise = false;
},
toString: function() {
@@ -803,9 +875,12 @@
function RegisterController(registers) {
this.registers = registers;
this.unnamedRegister = registers['"'] = new Register();
+ registers['.'] = new Register();
+ registers[':'] = new Register();
+ registers['/'] = new Register();
}
RegisterController.prototype = {
- pushText: function(registerName, operator, text, linewise) {
+ pushText: function(registerName, operator, text, linewise, blockwise) {
if (linewise && text.charAt(0) == '\n') {
text = text.slice(1) + '\n';
}
@@ -822,7 +897,7 @@
switch (operator) {
case 'yank':
// The 0 register contains the text from the most recent yank.
- this.registers['0'] = new Register(text, linewise);
+ this.registers['0'] = new Register(text, linewise, blockwise);
break;
case 'delete':
case 'change':
@@ -838,21 +913,20 @@
break;
}
// Make sure the unnamed register is set to what just happened
- this.unnamedRegister.setText(text, linewise);
+ this.unnamedRegister.setText(text, linewise, blockwise);
return;
}
// If we've gotten to this point, we've actually specified a register
var append = isUpperCase(registerName);
if (append) {
- register.append(text, linewise);
- // The unnamed register always has the same value as the last used
- // register.
- this.unnamedRegister.append(text, linewise);
+ register.pushText(text, linewise);
} else {
- register.setText(text, linewise);
- this.unnamedRegister.setText(text, linewise);
+ register.setText(text, linewise, blockwise);
}
+ // The unnamed register always has the same value as the last used
+ // register.
+ this.unnamedRegister.setText(register.toString(), linewise);
},
// Gets the register named @name. If one of @name doesn't already exist,
// create it. If @name is invalid, return the unnamedRegister.
@@ -875,85 +949,65 @@
}
}
};
-
+ function HistoryController() {
+ this.historyBuffer = [];
+ this.iterator;
+ this.initialPrefix = null;
+ }
+ HistoryController.prototype = {
+ // the input argument here acts a user entered prefix for a small time
+ // until we start autocompletion in which case it is the autocompleted.
+ nextMatch: function (input, up) {
+ var historyBuffer = this.historyBuffer;
+ var dir = up ? -1 : 1;
+ if (this.initialPrefix === null) this.initialPrefix = input;
+ for (var i = this.iterator + dir; up ? i >= 0 : i < historyBuffer.length; i+= dir) {
+ var element = historyBuffer[i];
+ for (var j = 0; j <= element.length; j++) {
+ if (this.initialPrefix == element.substring(0, j)) {
+ this.iterator = i;
+ return element;
+ }
+ }
+ }
+ // should return the user input in case we reach the end of buffer.
+ if (i >= historyBuffer.length) {
+ this.iterator = historyBuffer.length;
+ return this.initialPrefix;
+ }
+ // return the last autocompleted query or exCommand as it is.
+ if (i < 0 ) return input;
+ },
+ pushInput: function(input) {
+ var index = this.historyBuffer.indexOf(input);
+ if (index > -1) this.historyBuffer.splice(index, 1);
+ if (input.length) this.historyBuffer.push(input);
+ },
+ reset: function() {
+ this.initialPrefix = null;
+ this.iterator = this.historyBuffer.length;
+ }
+ };
var commandDispatcher = {
- matchCommand: function(key, keyMap, vim) {
- var inputState = vim.inputState;
- var keys = inputState.keyBuffer.concat(key);
- var matchedCommands = [];
- var selectedCharacter;
- for (var i = 0; i < keyMap.length; i++) {
- var command = keyMap[i];
- if (matchKeysPartial(keys, command.keys)) {
- if (inputState.operator && command.type == 'action') {
- // Ignore matched action commands after an operator. Operators
- // only operate on motions. This check is really for text
- // objects since aW, a[ etcs conflicts with a.
- continue;
- }
- // Match commands that take as an argument.
- if (command.keys[keys.length - 1] == 'character') {
- selectedCharacter = keys[keys.length - 1];
- if (selectedCharacter.length>1){
- switch(selectedCharacter){
- case '':
- selectedCharacter='\n';
- break;
- case '':
- selectedCharacter=' ';
- break;
- default:
- continue;
- }
- }
- }
- // Add the command to the list of matched commands. Choose the best
- // command later.
- matchedCommands.push(command);
- }
+ matchCommand: function(keys, keyMap, inputState, context) {
+ var matches = commandMatches(keys, keyMap, context, inputState);
+ if (!matches.full && !matches.partial) {
+ return {type: 'none'};
+ } else if (!matches.full && matches.partial) {
+ return {type: 'partial'};
}
- // Returns the command if it is a full match, or null if not.
- function getFullyMatchedCommandOrNull(command) {
- if (keys.length < command.keys.length) {
- // Matches part of a multi-key command. Buffer and wait for next
- // stroke.
- inputState.keyBuffer.push(key);
- return null;
- } else {
- if (command.keys[keys.length - 1] == 'character') {
- inputState.selectedCharacter = selectedCharacter;
- }
- // Clear the buffer since a full match was found.
- inputState.keyBuffer = [];
- return command;
+ var bestMatch;
+ for (var i = 0; i < matches.full.length; i++) {
+ var match = matches.full[i];
+ if (!bestMatch) {
+ bestMatch = match;
}
}
-
- if (!matchedCommands.length) {
- // Clear the buffer since there were no matches.
- inputState.keyBuffer = [];
- return null;
- } else if (matchedCommands.length == 1) {
- return getFullyMatchedCommandOrNull(matchedCommands[0]);
- } else {
- // Find the best match in the list of matchedCommands.
- var context = vim.visualMode ? 'visual' : 'normal';
- var bestMatch; // Default to first in the list.
- for (var i = 0; i < matchedCommands.length; i++) {
- var current = matchedCommands[i];
- if (current.context == context) {
- bestMatch = current;
- break;
- } else if (!bestMatch && !current.context) {
- // Only set an imperfect match to best match if no best match is
- // set and the imperfect match is not restricted to another
- // context.
- bestMatch = current;
- }
- }
- return getFullyMatchedCommandOrNull(bestMatch);
+ if (bestMatch.keys.slice(-11) == '') {
+ inputState.selectedCharacter = lastChar(keys);
}
+ return {type: 'full', command: bestMatch};
},
processCommand: function(cm, vim, command) {
vim.inputState.repeatOverride = command.repeatOverride;
@@ -972,10 +1026,12 @@
break;
case 'search':
this.processSearch(cm, vim, command);
+ clearInputState(cm);
break;
case 'ex':
case 'keyToEx':
this.processEx(cm, vim, command);
+ clearInputState(cm);
break;
default:
break;
@@ -998,7 +1054,7 @@
return;
} else {
// 2 different operators in a row doesn't make sense.
- vim.inputState = new InputState();
+ clearInputState(cm);
}
}
inputState.operator = command.operator;
@@ -1043,7 +1099,7 @@
actionArgs.repeat = repeat || 1;
actionArgs.repeatIsExplicit = repeatIsExplicit;
actionArgs.registerName = inputState.registerName;
- vim.inputState = new InputState();
+ clearInputState(cm);
vim.lastMotion = null;
if (command.isEdit) {
this.recordLastEdit(vim, inputState, command);
@@ -1056,11 +1112,14 @@
return;
}
var forward = command.searchArgs.forward;
+ var wholeWordOnly = command.searchArgs.wholeWordOnly;
getSearchState(cm).setReversed(!forward);
var promptPrefix = (forward) ? '/' : '?';
var originalQuery = getSearchState(cm).getQuery();
var originalScrollPos = cm.getScrollInfo();
function handleQuery(query, ignoreCase, smartCase) {
+ vimGlobalState.searchHistoryController.pushInput(query);
+ vimGlobalState.searchHistoryController.reset();
try {
updateSearchQuery(cm, query, ignoreCase, smartCase);
} catch (e) {
@@ -1076,8 +1135,21 @@
function onPromptClose(query) {
cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
handleQuery(query, true /** ignoreCase */, true /** smartCase */);
+ var macroModeState = vimGlobalState.macroModeState;
+ if (macroModeState.isRecording) {
+ logSearchQuery(macroModeState, query);
+ }
}
- function onPromptKeyUp(_e, query) {
+ function onPromptKeyUp(e, query, close) {
+ var keyName = CodeMirror.keyName(e), up;
+ if (keyName == 'Up' || keyName == 'Down') {
+ up = keyName == 'Up' ? true : false;
+ query = vimGlobalState.searchHistoryController.nextMatch(query, up) || '';
+ close(query);
+ } else {
+ if ( keyName != 'Left' && keyName != 'Right' && keyName != 'Ctrl' && keyName != 'Alt' && keyName != 'Shift')
+ vimGlobalState.searchHistoryController.reset();
+ }
var parsedQuery;
try {
parsedQuery = updateSearchQuery(cm, query,
@@ -1092,13 +1164,14 @@
cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
}
}
- function onPromptKeyDown(e, _query, close) {
+ function onPromptKeyDown(e, query, close) {
var keyName = CodeMirror.keyName(e);
if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[') {
+ vimGlobalState.searchHistoryController.pushInput(query);
+ vimGlobalState.searchHistoryController.reset();
updateSearchQuery(cm, originalQuery);
clearSearchHighlight(cm);
cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
-
CodeMirror.e_stop(e);
close();
cm.focus();
@@ -1106,13 +1179,19 @@
}
switch (command.searchArgs.querySrc) {
case 'prompt':
- showPrompt(cm, {
- onClose: onPromptClose,
- prefix: promptPrefix,
- desc: searchPromptDesc,
- onKeyUp: onPromptKeyUp,
- onKeyDown: onPromptKeyDown
- });
+ var macroModeState = vimGlobalState.macroModeState;
+ if (macroModeState.isPlaying) {
+ var query = macroModeState.replaySearchQueries.shift();
+ handleQuery(query, true /** ignoreCase */, false /** smartCase */);
+ } else {
+ showPrompt(cm, {
+ onClose: onPromptClose,
+ prefix: promptPrefix,
+ desc: searchPromptDesc,
+ onKeyUp: onPromptKeyUp,
+ onKeyDown: onPromptKeyDown
+ });
+ }
break;
case 'wordUnderCursor':
var word = expandWordUnderCursor(cm, false /** inclusive */,
@@ -1130,8 +1209,8 @@
}
var query = cm.getLine(word.start.line).substring(word.start.ch,
word.end.ch);
- if (isKeyword) {
- query = '\\b' + query + '\\b';
+ if (isKeyword && wholeWordOnly) {
+ query = '\\b' + query + '\\b';
} else {
query = escapeRegex(query);
}
@@ -1150,15 +1229,27 @@
function onPromptClose(input) {
// Give the prompt some time to close so that if processCommand shows
// an error, the elements don't overlap.
+ vimGlobalState.exCommandHistoryController.pushInput(input);
+ vimGlobalState.exCommandHistoryController.reset();
exCommandDispatcher.processCommand(cm, input);
}
- function onPromptKeyDown(e, _input, close) {
- var keyName = CodeMirror.keyName(e);
+ function onPromptKeyDown(e, input, close) {
+ var keyName = CodeMirror.keyName(e), up;
if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[') {
+ vimGlobalState.exCommandHistoryController.pushInput(input);
+ vimGlobalState.exCommandHistoryController.reset();
CodeMirror.e_stop(e);
close();
cm.focus();
}
+ if (keyName == 'Up' || keyName == 'Down') {
+ up = keyName == 'Up' ? true : false;
+ input = vimGlobalState.exCommandHistoryController.nextMatch(input, up) || '';
+ close(input);
+ } else {
+ if ( keyName != 'Left' && keyName != 'Right' && keyName != 'Ctrl' && keyName != 'Alt' && keyName != 'Shift')
+ vimGlobalState.exCommandHistoryController.reset();
+ }
}
if (command.type == 'keyToEx') {
// Handle user defined Ex to Ex mappings
@@ -1182,13 +1273,13 @@
var operator = inputState.operator;
var operatorArgs = inputState.operatorArgs || {};
var registerName = inputState.registerName;
- var selectionEnd = copyCursor(cm.getCursor('head'));
- var selectionStart = copyCursor(cm.getCursor('anchor'));
- // The difference between cur and selection cursors are that cur is
- // being operated on and ignores that there is a selection.
- var curStart = copyCursor(selectionEnd);
- var curOriginal = copyCursor(curStart);
- var curEnd;
+ var sel = vim.sel;
+ // TODO: Make sure cm and vim selections are identical outside visual mode.
+ var origHead = copyCursor(vim.visualMode ? sel.head: cm.getCursor('head'));
+ var origAnchor = copyCursor(vim.visualMode ? sel.anchor : cm.getCursor('anchor'));
+ var oldHead = copyCursor(origHead);
+ var oldAnchor = copyCursor(origAnchor);
+ var newHead, newAnchor;
var repeat;
if (operator) {
this.recordLastEdit(vim, inputState);
@@ -1213,9 +1304,9 @@
inputState.selectedCharacter;
}
motionArgs.repeat = repeat;
- vim.inputState = new InputState();
+ clearInputState(cm);
if (motion) {
- var motionResult = motions[motion](cm, motionArgs, vim);
+ var motionResult = motions[motion](cm, origHead, motionArgs, vim);
vim.lastMotion = motions[motion];
if (!motionResult) {
return;
@@ -1228,101 +1319,141 @@
recordJumpPosition(cm, cachedCursor, motionResult);
delete jumpList.cachedCursor;
} else {
- recordJumpPosition(cm, curOriginal, motionResult);
+ recordJumpPosition(cm, origHead, motionResult);
}
}
if (motionResult instanceof Array) {
- curStart = motionResult[0];
- curEnd = motionResult[1];
+ newAnchor = motionResult[0];
+ newHead = motionResult[1];
} else {
- curEnd = motionResult;
+ newHead = motionResult;
}
// TODO: Handle null returns from motion commands better.
- if (!curEnd) {
- curEnd = Pos(curStart.line, curStart.ch);
+ if (!newHead) {
+ newHead = copyCursor(origHead);
}
if (vim.visualMode) {
- // Check if the selection crossed over itself. Will need to shift
- // the start point if that happened.
- if (cursorIsBefore(selectionStart, selectionEnd) &&
- (cursorEqual(selectionStart, curEnd) ||
- cursorIsBefore(curEnd, selectionStart))) {
- // The end of the selection has moved from after the start to
- // before the start. We will shift the start right by 1.
- selectionStart.ch += 1;
- } else if (cursorIsBefore(selectionEnd, selectionStart) &&
- (cursorEqual(selectionStart, curEnd) ||
- cursorIsBefore(selectionStart, curEnd))) {
- // The opposite happened. We will shift the start left by 1.
- selectionStart.ch -= 1;
+ if (!(vim.visualBlock && newHead.ch === Infinity)) {
+ newHead = clipCursorToContent(cm, newHead, vim.visualBlock);
}
- selectionEnd = curEnd;
- selectionStart = (motionResult instanceof Array) ? curStart : selectionStart;
- if (vim.visualLine) {
- if (cursorIsBefore(selectionStart, selectionEnd)) {
- selectionStart.ch = 0;
-
- var lastLine = cm.lastLine();
- if (selectionEnd.line > lastLine) {
- selectionEnd.line = lastLine;
- }
- selectionEnd.ch = lineLength(cm, selectionEnd.line);
- } else {
- selectionEnd.ch = 0;
- selectionStart.ch = lineLength(cm, selectionStart.line);
- }
+ if (newAnchor) {
+ newAnchor = clipCursorToContent(cm, newAnchor, true);
}
- cm.setSelection(selectionStart, selectionEnd);
+ newAnchor = newAnchor || oldAnchor;
+ sel.anchor = newAnchor;
+ sel.head = newHead;
+ updateCmSelection(cm);
updateMark(cm, vim, '<',
- cursorIsBefore(selectionStart, selectionEnd) ? selectionStart
- : selectionEnd);
+ cursorIsBefore(newAnchor, newHead) ? newAnchor
+ : newHead);
updateMark(cm, vim, '>',
- cursorIsBefore(selectionStart, selectionEnd) ? selectionEnd
- : selectionStart);
+ cursorIsBefore(newAnchor, newHead) ? newHead
+ : newAnchor);
} else if (!operator) {
- curEnd = clipCursorToContent(cm, curEnd);
- cm.setCursor(curEnd.line, curEnd.ch);
+ newHead = clipCursorToContent(cm, newHead);
+ cm.setCursor(newHead.line, newHead.ch);
}
}
-
if (operator) {
- var inverted = false;
- vim.lastMotion = null;
- operatorArgs.repeat = repeat; // Indent in visual mode needs this.
+ if (operatorArgs.lastSel) {
+ // Replaying a visual mode operation
+ newAnchor = oldAnchor;
+ var lastSel = operatorArgs.lastSel;
+ var lineOffset = Math.abs(lastSel.head.line - lastSel.anchor.line);
+ var chOffset = Math.abs(lastSel.head.ch - lastSel.anchor.ch);
+ if (lastSel.visualLine) {
+ // Linewise Visual mode: The same number of lines.
+ newHead = Pos(oldAnchor.line + lineOffset, oldAnchor.ch);
+ } else if (lastSel.visualBlock) {
+ // Blockwise Visual mode: The same number of lines and columns.
+ newHead = Pos(oldAnchor.line + lineOffset, oldAnchor.ch + chOffset);
+ } else if (lastSel.head.line == lastSel.anchor.line) {
+ // Normal Visual mode within one line: The same number of characters.
+ newHead = Pos(oldAnchor.line, oldAnchor.ch + chOffset);
+ } else {
+ // Normal Visual mode with several lines: The same number of lines, in the
+ // last line the same number of characters as in the last line the last time.
+ newHead = Pos(oldAnchor.line + lineOffset, oldAnchor.ch);
+ }
+ vim.visualMode = true;
+ vim.visualLine = lastSel.visualLine;
+ vim.visualBlock = lastSel.visualBlock;
+ sel = vim.sel = {
+ anchor: newAnchor,
+ head: newHead
+ };
+ updateCmSelection(cm);
+ } else if (vim.visualMode) {
+ operatorArgs.lastSel = {
+ anchor: copyCursor(sel.anchor),
+ head: copyCursor(sel.head),
+ visualBlock: vim.visualBlock,
+ visualLine: vim.visualLine
+ };
+ }
+ var curStart, curEnd, linewise, mode;
+ var cmSel;
if (vim.visualMode) {
- curStart = selectionStart;
- curEnd = selectionEnd;
- motionArgs.inclusive = true;
- }
- // Swap start and end if motion was backward.
- if (cursorIsBefore(curEnd, curStart)) {
- var tmp = curStart;
- curStart = curEnd;
- curEnd = tmp;
- inverted = true;
- }
- if (motionArgs.inclusive && !(vim.visualMode && inverted)) {
- // Move the selection end one to the right to include the last
- // character.
- curEnd.ch++;
- }
- var linewise = motionArgs.linewise ||
- (vim.visualMode && vim.visualLine);
- if (linewise) {
- // Expand selection to entire line.
- expandSelectionToLine(cm, curStart, curEnd);
- } else if (motionArgs.forward) {
- // Clip to trailing newlines only if the motion goes forward.
- clipToLine(cm, curStart, curEnd);
+ // Init visual op
+ curStart = cursorMin(sel.head, sel.anchor);
+ curEnd = cursorMax(sel.head, sel.anchor);
+ linewise = vim.visualLine || operatorArgs.linewise;
+ mode = vim.visualBlock ? 'block' :
+ linewise ? 'line' :
+ 'char';
+ cmSel = makeCmSelection(cm, {
+ anchor: curStart,
+ head: curEnd
+ }, mode);
+ if (linewise) {
+ var ranges = cmSel.ranges;
+ if (mode == 'block') {
+ // Linewise operators in visual block mode extend to end of line
+ for (var i = 0; i < ranges.length; i++) {
+ ranges[i].head.ch = lineLength(cm, ranges[i].head.line);
+ }
+ } else if (mode == 'line') {
+ ranges[0].head = Pos(ranges[0].head.line + 1, 0);
+ }
+ }
+ } else {
+ // Init motion op
+ curStart = copyCursor(newAnchor || oldAnchor);
+ curEnd = copyCursor(newHead || oldHead);
+ if (cursorIsBefore(curEnd, curStart)) {
+ var tmp = curStart;
+ curStart = curEnd;
+ curEnd = tmp;
+ }
+ linewise = motionArgs.linewise || operatorArgs.linewise;
+ if (linewise) {
+ // Expand selection to entire line.
+ expandSelectionToLine(cm, curStart, curEnd);
+ } else if (motionArgs.forward) {
+ // Clip to trailing newlines only if the motion goes forward.
+ clipToLine(cm, curStart, curEnd);
+ }
+ mode = 'char';
+ var exclusive = !motionArgs.inclusive || linewise;
+ cmSel = makeCmSelection(cm, {
+ anchor: curStart,
+ head: curEnd
+ }, mode, exclusive);
}
+ cm.setSelections(cmSel.ranges, cmSel.primary);
+ vim.lastMotion = null;
+ operatorArgs.repeat = repeat; // For indent in visual mode.
operatorArgs.registerName = registerName;
// Keep track of linewise as it affects how paste and change behave.
operatorArgs.linewise = linewise;
- operators[operator](cm, operatorArgs, vim, curStart,
- curEnd, curOriginal);
+ var operatorMoveTo = operators[operator](
+ cm, operatorArgs, cmSel.ranges, oldAnchor, newHead);
if (vim.visualMode) {
exitVisualMode(cm);
}
+ if (operatorMoveTo) {
+ cm.setCursor(operatorMoveTo);
+ }
}
},
recordLastEdit: function(vim, inputState, actionCommand) {
@@ -1341,7 +1472,7 @@
*/
// All of the functions below return Cursor objects.
var motions = {
- moveToTopLine: function(cm, motionArgs) {
+ moveToTopLine: function(cm, _head, motionArgs) {
var line = getUserVisibleLines(cm).top + motionArgs.repeat -1;
return Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));
},
@@ -1350,17 +1481,17 @@
var line = Math.floor((range.top + range.bottom) * 0.5);
return Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));
},
- moveToBottomLine: function(cm, motionArgs) {
+ moveToBottomLine: function(cm, _head, motionArgs) {
var line = getUserVisibleLines(cm).bottom - motionArgs.repeat +1;
return Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));
},
- expandToLine: function(cm, motionArgs) {
+ expandToLine: function(_cm, head, motionArgs) {
// Expands forward to end of line, and then to next line if repeat is
// >1. Does not handle backward motion!
- var cur = cm.getCursor();
+ var cur = head;
return Pos(cur.line + motionArgs.repeat - 1, Infinity);
},
- findNext: function(cm, motionArgs) {
+ findNext: function(cm, _head, motionArgs) {
var state = getSearchState(cm);
var query = state.getQuery();
if (!query) {
@@ -1372,25 +1503,27 @@
highlightSearchMatches(cm, query);
return findNext(cm, prev/** prev */, query, motionArgs.repeat);
},
- goToMark: function(_cm, motionArgs, vim) {
+ goToMark: function(cm, _head, motionArgs, vim) {
var mark = vim.marks[motionArgs.selectedCharacter];
if (mark) {
- return mark.find();
+ var pos = mark.find();
+ return motionArgs.linewise ? { line: pos.line, ch: findFirstNonWhiteSpaceCharacter(cm.getLine(pos.line)) } : pos;
}
return null;
},
- moveToOtherHighlightedEnd: function(cm) {
- var curEnd = copyCursor(cm.getCursor('head'));
- var curStart = copyCursor(cm.getCursor('anchor'));
- if (cursorIsBefore(curStart, curEnd)) {
- curEnd.ch += 1;
- } else if (cursorIsBefore(curEnd, curStart)) {
- curStart.ch -= 1;
+ moveToOtherHighlightedEnd: function(cm, _head, motionArgs, vim) {
+ if (vim.visualBlock && motionArgs.sameLine) {
+ var sel = vim.sel;
+ return [
+ clipCursorToContent(cm, Pos(sel.anchor.line, sel.head.ch)),
+ clipCursorToContent(cm, Pos(sel.head.line, sel.anchor.ch))
+ ];
+ } else {
+ return ([vim.sel.head, vim.sel.anchor]);
}
- return ([curEnd,curStart]);
},
- jumpToMark: function(cm, motionArgs, vim) {
- var best = cm.getCursor();
+ jumpToMark: function(cm, head, motionArgs, vim) {
+ var best = head;
for (var i = 0; i < motionArgs.repeat; i++) {
var cursor = best;
for (var key in vim.marks) {
@@ -1410,8 +1543,8 @@
var equal = cursorEqual(cursor, best);
var between = (motionArgs.forward) ?
- cusrorIsBetween(cursor, mark, best) :
- cusrorIsBetween(best, mark, cursor);
+ cursorIsBetween(cursor, mark, best) :
+ cursorIsBetween(best, mark, cursor);
if (equal || between) {
best = mark;
@@ -1427,14 +1560,14 @@
}
return best;
},
- moveByCharacters: function(cm, motionArgs) {
- var cur = cm.getCursor();
+ moveByCharacters: function(_cm, head, motionArgs) {
+ var cur = head;
var repeat = motionArgs.repeat;
var ch = motionArgs.forward ? cur.ch + repeat : cur.ch - repeat;
return Pos(cur.line, ch);
},
- moveByLines: function(cm, motionArgs, vim) {
- var cur = cm.getCursor();
+ moveByLines: function(cm, head, motionArgs, vim) {
+ var cur = head;
var endCh = cur.ch;
// Depending what our last motion was, we may want to do different
// things. If our last motion was moving vertically, we want to
@@ -1469,8 +1602,8 @@
vim.lastHSPos = cm.charCoords(Pos(line, endCh),'div').left;
return Pos(line, endCh);
},
- moveByDisplayLines: function(cm, motionArgs, vim) {
- var cur = cm.getCursor();
+ moveByDisplayLines: function(cm, head, motionArgs, vim) {
+ var cur = head;
switch (vim.lastMotion) {
case this.moveByDisplayLines:
case this.moveByScroll:
@@ -1497,43 +1630,28 @@
vim.lastHPos = res.ch;
return res;
},
- moveByPage: function(cm, motionArgs) {
+ moveByPage: function(cm, head, motionArgs) {
// CodeMirror only exposes functions that move the cursor page down, so
// doing this bad hack to move the cursor and move it back. evalInput
// will move the cursor to where it should be in the end.
- var curStart = cm.getCursor();
+ var curStart = head;
var repeat = motionArgs.repeat;
- cm.moveV((motionArgs.forward ? repeat : -repeat), 'page');
- var curEnd = cm.getCursor();
- cm.setCursor(curStart);
- return curEnd;
+ return cm.findPosV(curStart, (motionArgs.forward ? repeat : -repeat), 'page');
},
- moveByParagraph: function(cm, motionArgs) {
- var line = cm.getCursor().line;
- var repeat = motionArgs.repeat;
- var inc = motionArgs.forward ? 1 : -1;
- for (var i = 0; i < repeat; i++) {
- if ((!motionArgs.forward && line === cm.firstLine() ) ||
- (motionArgs.forward && line == cm.lastLine())) {
- break;
- }
- line += inc;
- while (line !== cm.firstLine() && line != cm.lastLine() && cm.getLine(line)) {
- line += inc;
- }
- }
- return Pos(line, 0);
+ moveByParagraph: function(cm, head, motionArgs) {
+ var dir = motionArgs.forward ? 1 : -1;
+ return findParagraph(cm, head, motionArgs.repeat, dir);
},
- moveByScroll: function(cm, motionArgs, vim) {
+ moveByScroll: function(cm, head, motionArgs, vim) {
var scrollbox = cm.getScrollInfo();
var curEnd = null;
var repeat = motionArgs.repeat;
if (!repeat) {
repeat = scrollbox.clientHeight / (2 * cm.defaultTextHeight());
}
- var orig = cm.charCoords(cm.getCursor(), 'local');
+ var orig = cm.charCoords(head, 'local');
motionArgs.repeat = repeat;
- var curEnd = motions.moveByDisplayLines(cm, motionArgs, vim);
+ var curEnd = motions.moveByDisplayLines(cm, head, motionArgs, vim);
if (!curEnd) {
return null;
}
@@ -1541,11 +1659,11 @@
cm.scrollTo(null, scrollbox.top + dest.top - orig.top);
return curEnd;
},
- moveByWords: function(cm, motionArgs) {
- return moveToWord(cm, motionArgs.repeat, !!motionArgs.forward,
+ moveByWords: function(cm, head, motionArgs) {
+ return moveToWord(cm, head, motionArgs.repeat, !!motionArgs.forward,
!!motionArgs.wordEnd, !!motionArgs.bigWord);
},
- moveTillCharacter: function(cm, motionArgs) {
+ moveTillCharacter: function(cm, _head, motionArgs) {
var repeat = motionArgs.repeat;
var curEnd = moveToCharacter(cm, repeat, motionArgs.forward,
motionArgs.selectedCharacter);
@@ -1555,26 +1673,26 @@
curEnd.ch += increment;
return curEnd;
},
- moveToCharacter: function(cm, motionArgs) {
+ moveToCharacter: function(cm, head, motionArgs) {
var repeat = motionArgs.repeat;
recordLastCharacterSearch(0, motionArgs);
return moveToCharacter(cm, repeat, motionArgs.forward,
- motionArgs.selectedCharacter) || cm.getCursor();
+ motionArgs.selectedCharacter) || head;
},
- moveToSymbol: function(cm, motionArgs) {
+ moveToSymbol: function(cm, head, motionArgs) {
var repeat = motionArgs.repeat;
return findSymbol(cm, repeat, motionArgs.forward,
- motionArgs.selectedCharacter) || cm.getCursor();
+ motionArgs.selectedCharacter) || head;
},
- moveToColumn: function(cm, motionArgs, vim) {
+ moveToColumn: function(cm, head, motionArgs, vim) {
var repeat = motionArgs.repeat;
// repeat is equivalent to which column we want to move to!
vim.lastHPos = repeat - 1;
- vim.lastHSPos = cm.charCoords(cm.getCursor(),'div').left;
+ vim.lastHSPos = cm.charCoords(head,'div').left;
return moveToColumn(cm, repeat);
},
- moveToEol: function(cm, motionArgs, vim) {
- var cur = cm.getCursor();
+ moveToEol: function(cm, head, motionArgs, vim) {
+ var cur = head;
vim.lastHPos = Infinity;
var retval= Pos(cur.line + motionArgs.repeat - 1, Infinity);
var end=cm.clipPos(retval);
@@ -1582,42 +1700,39 @@
vim.lastHSPos = cm.charCoords(end,'div').left;
return retval;
},
- moveToFirstNonWhiteSpaceCharacter: function(cm) {
+ moveToFirstNonWhiteSpaceCharacter: function(cm, head) {
// Go to the start of the line where the text begins, or the end for
// whitespace-only lines
- var cursor = cm.getCursor();
+ var cursor = head;
return Pos(cursor.line,
findFirstNonWhiteSpaceCharacter(cm.getLine(cursor.line)));
},
- moveToMatchedSymbol: function(cm) {
- var cursor = cm.getCursor();
+ moveToMatchedSymbol: function(cm, head) {
+ var cursor = head;
var line = cursor.line;
var ch = cursor.ch;
var lineText = cm.getLine(line);
var symbol;
- var startContext = cm.getTokenAt(cursor).type;
- var startCtxLevel = getContextLevel(startContext);
do {
symbol = lineText.charAt(ch++);
if (symbol && isMatchableSymbol(symbol)) {
- var endContext = cm.getTokenAt(Pos(line, ch)).type;
- var endCtxLevel = getContextLevel(endContext);
- if (startCtxLevel >= endCtxLevel) {
+ var style = cm.getTokenTypeAt(Pos(line, ch));
+ if (style !== "string" && style !== "comment") {
break;
}
}
} while (symbol);
if (symbol) {
- return findMatchedSymbol(cm, Pos(line, ch-1), symbol);
+ var matched = cm.findMatchingBracket(Pos(line, ch));
+ return matched.to;
} else {
return cursor;
}
},
- moveToStartOfLine: function(cm) {
- var cursor = cm.getCursor();
- return Pos(cursor.line, 0);
+ moveToStartOfLine: function(_cm, head) {
+ return Pos(head.line, 0);
},
- moveToLineOrEdgeOfDocument: function(cm, motionArgs) {
+ moveToLineOrEdgeOfDocument: function(cm, _head, motionArgs) {
var lineNum = motionArgs.forward ? cm.lastLine() : cm.firstLine();
if (motionArgs.repeatIsExplicit) {
lineNum = motionArgs.repeat - cm.getOption('firstLineNumber');
@@ -1625,7 +1740,7 @@
return Pos(lineNum,
findFirstNonWhiteSpaceCharacter(cm.getLine(lineNum)));
},
- textObjectManipulation: function(cm, motionArgs) {
+ textObjectManipulation: function(cm, head, motionArgs, vim) {
// TODO: lots of possible exceptions that can be thrown here. Try da(
// outside of a () block.
@@ -1637,6 +1752,13 @@
var selfPaired = {'\'': true, '"': true};
var character = motionArgs.selectedCharacter;
+ // 'b' refers to '()' block.
+ // 'B' refers to '{}' block.
+ if (character == 'b') {
+ character = '(';
+ } else if (character == 'B') {
+ character = '{';
+ }
// Inclusive is the difference between a and i
// TODO: Instead of using the additional text object map to perform text
@@ -1647,24 +1769,38 @@
var tmp;
if (mirroredPairs[character]) {
- tmp = selectCompanionObject(cm, mirroredPairs[character], inclusive);
+ tmp = selectCompanionObject(cm, head, character, inclusive);
} else if (selfPaired[character]) {
- tmp = findBeginningAndEnd(cm, character, inclusive);
+ tmp = findBeginningAndEnd(cm, head, character, inclusive);
} else if (character === 'W') {
tmp = expandWordUnderCursor(cm, inclusive, true /** forward */,
true /** bigWord */);
} else if (character === 'w') {
tmp = expandWordUnderCursor(cm, inclusive, true /** forward */,
false /** bigWord */);
+ } else if (character === 'p') {
+ tmp = findParagraph(cm, head, motionArgs.repeat, 0, inclusive);
+ motionArgs.linewise = true;
+ if (vim.visualMode) {
+ if (!vim.visualLine) { vim.visualLine = true; }
+ } else {
+ var operatorArgs = vim.inputState.operatorArgs;
+ if (operatorArgs) { operatorArgs.linewise = true; }
+ tmp.end.line--;
+ }
} else {
// No text object defined for this, don't move.
return null;
}
- return [tmp.start, tmp.end];
+ if (!cm.state.vim.visualMode) {
+ return [tmp.start, tmp.end];
+ } else {
+ return expandSelection(cm, tmp.start, tmp.end);
+ }
},
- repeatLastCharacterSearch: function(cm, motionArgs) {
+ repeatLastCharacterSearch: function(cm, head, motionArgs) {
var lastSearch = vimGlobalState.lastChararacterSearch;
var repeat = motionArgs.repeat;
var forward = motionArgs.forward === lastSearch.forward;
@@ -1674,65 +1810,107 @@
var curEnd = moveToCharacter(cm, repeat, forward, lastSearch.selectedCharacter);
if (!curEnd) {
cm.moveH(increment, 'char');
- return cm.getCursor();
+ return head;
}
curEnd.ch += increment;
return curEnd;
}
};
+ function fillArray(val, times) {
+ var arr = [];
+ for (var i = 0; i < times; i++) {
+ arr.push(val);
+ }
+ return arr;
+ }
+ /**
+ * An operator acts on a text selection. It receives the list of selections
+ * as input. The corresponding CodeMirror selection is guaranteed to
+ * match the input selection.
+ */
var operators = {
- change: function(cm, operatorArgs, _vim, curStart, curEnd) {
- vimGlobalState.registerController.pushText(
- operatorArgs.registerName, 'change', cm.getRange(curStart, curEnd),
- operatorArgs.linewise);
- if (operatorArgs.linewise) {
- // Push the next line back down, if there is a next line.
- var replacement = curEnd.line > cm.lastLine() ? '' : '\n';
- cm.replaceRange(replacement, curStart, curEnd);
- cm.indentLine(curStart.line, 'smart');
- // null ch so setCursor moves to end of line.
- curStart.ch = null;
- } else {
- // Exclude trailing whitespace if the range is not all whitespace.
- var text = cm.getRange(curStart, curEnd);
+ change: function(cm, args, ranges) {
+ var finalHead, text;
+ var vim = cm.state.vim;
+ vimGlobalState.macroModeState.lastInsertModeChanges.inVisualBlock = vim.visualBlock;
+ if (!vim.visualMode) {
+ var anchor = ranges[0].anchor,
+ head = ranges[0].head;
+ text = cm.getRange(anchor, head);
if (!isWhiteSpaceString(text)) {
+ // Exclude trailing whitespace if the range is not all whitespace.
var match = (/\s+$/).exec(text);
if (match) {
- curEnd = offsetCursor(curEnd, 0, - match[0].length);
+ head = offsetCursor(head, 0, - match[0].length);
+ text = text.slice(0, - match[0].length);
}
}
- cm.replaceRange('', curStart, curEnd);
- }
- actions.enterInsertMode(cm, {}, cm.state.vim);
- cm.setCursor(curStart);
- },
- // delete is a javascript keyword.
- 'delete': function(cm, operatorArgs, _vim, curStart, curEnd) {
- // If the ending line is past the last line, inclusive, instead of
- // including the trailing \n, include the \n before the starting line
- if (operatorArgs.linewise &&
- curEnd.line > cm.lastLine() && curStart.line > cm.firstLine()) {
- curStart.line--;
- curStart.ch = lineLength(cm, curStart.line);
+ var wasLastLine = head.line - 1 == cm.lastLine();
+ cm.replaceRange('', anchor, head);
+ if (args.linewise && !wasLastLine) {
+ // Push the next line back down, if there is a next line.
+ CodeMirror.commands.newlineAndIndent(cm);
+ // null ch so setCursor moves to end of line.
+ anchor.ch = null;
+ }
+ finalHead = anchor;
+ } else {
+ text = cm.getSelection();
+ var replacement = fillArray('', ranges.length);
+ cm.replaceSelections(replacement);
+ finalHead = cursorMin(ranges[0].head, ranges[0].anchor);
}
vimGlobalState.registerController.pushText(
- operatorArgs.registerName, 'delete', cm.getRange(curStart, curEnd),
- operatorArgs.linewise);
- cm.replaceRange('', curStart, curEnd);
- if (operatorArgs.linewise) {
- cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm));
- } else {
- cm.setCursor(curStart);
- }
+ args.registerName, 'change', text,
+ args.linewise, ranges.length > 1);
+ actions.enterInsertMode(cm, {head: finalHead}, cm.state.vim);
},
- indent: function(cm, operatorArgs, vim, curStart, curEnd) {
- var startLine = curStart.line;
- var endLine = curEnd.line;
+ // delete is a javascript keyword.
+ 'delete': function(cm, args, ranges) {
+ var finalHead, text;
+ var vim = cm.state.vim;
+ if (!vim.visualBlock) {
+ var anchor = ranges[0].anchor,
+ head = ranges[0].head;
+ if (args.linewise &&
+ head.line != cm.firstLine() &&
+ anchor.line == cm.lastLine() &&
+ anchor.line == head.line - 1) {
+ // Special case for dd on last line (and first line).
+ if (anchor.line == cm.firstLine()) {
+ anchor.ch = 0;
+ } else {
+ anchor = Pos(anchor.line - 1, lineLength(cm, anchor.line - 1));
+ }
+ }
+ text = cm.getRange(anchor, head);
+ cm.replaceRange('', anchor, head);
+ finalHead = anchor;
+ if (args.linewise) {
+ finalHead = motions.moveToFirstNonWhiteSpaceCharacter(cm, anchor);
+ }
+ } else {
+ text = cm.getSelection();
+ var replacement = fillArray('', ranges.length);
+ cm.replaceSelections(replacement);
+ finalHead = ranges[0].anchor;
+ }
+ vimGlobalState.registerController.pushText(
+ args.registerName, 'delete', text,
+ args.linewise, vim.visualBlock);
+ return finalHead;
+ },
+ indent: function(cm, args, ranges) {
+ var vim = cm.state.vim;
+ var startLine = ranges[0].anchor.line;
+ var endLine = vim.visualBlock ?
+ ranges[ranges.length - 1].anchor.line :
+ ranges[0].head.line;
// In visual mode, n> shifts the selection right n times, instead of
// shifting n lines right once.
- var repeat = (vim.visualMode) ? operatorArgs.repeat : 1;
- if (operatorArgs.linewise) {
+ var repeat = (vim.visualMode) ? args.repeat : 1;
+ if (args.linewise) {
// The only way to delete a newline is to delete until the start of
// the next line, so in linewise mode evalInput will include the next
// line. We don't want this in indent, so we go back a line.
@@ -1740,30 +1918,52 @@
}
for (var i = startLine; i <= endLine; i++) {
for (var j = 0; j < repeat; j++) {
- cm.indentLine(i, operatorArgs.indentRight);
+ cm.indentLine(i, args.indentRight);
}
}
- cm.setCursor(curStart);
- cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm));
+ return motions.moveToFirstNonWhiteSpaceCharacter(cm, ranges[0].anchor);
},
- swapcase: function(cm, operatorArgs, _vim, curStart, curEnd, curOriginal) {
- var toSwap = cm.getRange(curStart, curEnd);
- var swapped = '';
- for (var i = 0; i < toSwap.length; i++) {
- var character = toSwap.charAt(i);
- swapped += isUpperCase(character) ? character.toLowerCase() :
- character.toUpperCase();
+ changeCase: function(cm, args, ranges, oldAnchor, newHead) {
+ var selections = cm.getSelections();
+ var swapped = [];
+ var toLower = args.toLower;
+ for (var j = 0; j < selections.length; j++) {
+ var toSwap = selections[j];
+ var text = '';
+ if (toLower === true) {
+ text = toSwap.toLowerCase();
+ } else if (toLower === false) {
+ text = toSwap.toUpperCase();
+ } else {
+ for (var i = 0; i < toSwap.length; i++) {
+ var character = toSwap.charAt(i);
+ text += isUpperCase(character) ? character.toLowerCase() :
+ character.toUpperCase();
+ }
+ }
+ swapped.push(text);
}
- cm.replaceRange(swapped, curStart, curEnd);
- if (!operatorArgs.shouldMoveCursor) {
- cm.setCursor(curOriginal);
+ cm.replaceSelections(swapped);
+ if (args.shouldMoveCursor){
+ return newHead;
+ } else if (!cm.state.vim.visualMode && args.linewise && ranges[0].anchor.line + 1 == ranges[0].head.line) {
+ return motions.moveToFirstNonWhiteSpaceCharacter(cm, oldAnchor);
+ } else if (args.linewise){
+ return oldAnchor;
+ } else {
+ return cursorMin(ranges[0].anchor, ranges[0].head);
}
},
- yank: function(cm, operatorArgs, _vim, curStart, curEnd, curOriginal) {
+ yank: function(cm, args, ranges, oldAnchor) {
+ var vim = cm.state.vim;
+ var text = cm.getSelection();
+ var endPos = vim.visualMode
+ ? cursorMin(vim.sel.anchor, vim.sel.head, ranges[0].head, ranges[0].anchor)
+ : oldAnchor;
vimGlobalState.registerController.pushText(
- operatorArgs.registerName, 'yank',
- cm.getRange(curStart, curEnd), operatorArgs.linewise);
- cm.setCursor(curOriginal);
+ args.registerName, 'yank',
+ text, args.linewise, vim.visualBlock);
+ return endPos;
}
};
@@ -1855,14 +2055,45 @@
vim.insertMode = true;
vim.insertModeRepeat = actionArgs && actionArgs.repeat || 1;
var insertAt = (actionArgs) ? actionArgs.insertAt : null;
+ var sel = vim.sel;
+ var head = actionArgs.head || cm.getCursor('head');
+ var height = cm.listSelections().length;
if (insertAt == 'eol') {
- var cursor = cm.getCursor();
- cursor = Pos(cursor.line, lineLength(cm, cursor.line));
- cm.setCursor(cursor);
+ head = Pos(head.line, lineLength(cm, head.line));
} else if (insertAt == 'charAfter') {
- cm.setCursor(offsetCursor(cm.getCursor(), 0, 1));
+ head = offsetCursor(head, 0, 1);
} else if (insertAt == 'firstNonBlank') {
- cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm));
+ head = motions.moveToFirstNonWhiteSpaceCharacter(cm, head);
+ } else if (insertAt == 'startOfSelectedArea') {
+ if (!vim.visualBlock) {
+ if (sel.head.line < sel.anchor.line) {
+ head = sel.head;
+ } else {
+ head = Pos(sel.anchor.line, 0);
+ }
+ } else {
+ head = Pos(
+ Math.min(sel.head.line, sel.anchor.line),
+ Math.min(sel.head.ch, sel.anchor.ch));
+ height = Math.abs(sel.head.line - sel.anchor.line) + 1;
+ }
+ } else if (insertAt == 'endOfSelectedArea') {
+ if (!vim.visualBlock) {
+ if (sel.head.line >= sel.anchor.line) {
+ head = offsetCursor(sel.head, 0, 1);
+ } else {
+ head = Pos(sel.anchor.line, 0);
+ }
+ } else {
+ head = Pos(
+ Math.min(sel.head.line, sel.anchor.line),
+ Math.max(sel.head.ch + 1, sel.anchor.ch));
+ height = Math.abs(sel.head.line - sel.anchor.line) + 1;
+ }
+ } else if (insertAt == 'inplace') {
+ if (vim.visualMode){
+ return;
+ }
}
cm.setOption('keyMap', 'vim-insert');
cm.setOption('disableInput', false);
@@ -1878,82 +2109,73 @@
if (!vimGlobalState.macroModeState.isPlaying) {
// Only record if not replaying.
cm.on('change', onChange);
- cm.on('cursorActivity', onCursorActivity);
CodeMirror.on(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown);
}
+ if (vim.visualMode) {
+ exitVisualMode(cm);
+ }
+ selectForInsert(cm, head, height);
},
toggleVisualMode: function(cm, actionArgs, vim) {
var repeat = actionArgs.repeat;
- var curStart = cm.getCursor();
- var curEnd;
+ var anchor = cm.getCursor();
+ var head;
// TODO: The repeat should actually select number of characters/lines
// equal to the repeat times the size of the previous visual
// operation.
if (!vim.visualMode) {
- cm.on('mousedown', exitVisualMode);
+ // Entering visual mode
vim.visualMode = true;
vim.visualLine = !!actionArgs.linewise;
- if (vim.visualLine) {
- curStart.ch = 0;
- curEnd = clipCursorToContent(
- cm, Pos(curStart.line + repeat - 1, lineLength(cm, curStart.line)),
+ vim.visualBlock = !!actionArgs.blockwise;
+ head = clipCursorToContent(
+ cm, Pos(anchor.line, anchor.ch + repeat - 1),
true /** includeLineBreak */);
- } else {
- curEnd = clipCursorToContent(
- cm, Pos(curStart.line, curStart.ch + repeat),
- true /** includeLineBreak */);
- }
- // Make the initial selection.
- if (!actionArgs.repeatIsExplicit && !vim.visualLine) {
- // This is a strange case. Here the implicit repeat is 1. The
- // following commands lets the cursor hover over the 1 character
- // selection.
- cm.setCursor(curEnd);
- cm.setSelection(curEnd, curStart);
- } else {
- cm.setSelection(curStart, curEnd);
- }
- CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: vim.visualLine ? "linewise" : ""});
+ vim.sel = {
+ anchor: anchor,
+ head: head
+ };
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: vim.visualLine ? "linewise" : vim.visualBlock ? "blockwise" : ""});
+ updateCmSelection(cm);
+ updateMark(cm, vim, '<', cursorMin(anchor, head));
+ updateMark(cm, vim, '>', cursorMax(anchor, head));
+ } else if (vim.visualLine ^ actionArgs.linewise ||
+ vim.visualBlock ^ actionArgs.blockwise) {
+ // Toggling between modes
+ vim.visualLine = !!actionArgs.linewise;
+ vim.visualBlock = !!actionArgs.blockwise;
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: vim.visualLine ? "linewise" : vim.visualBlock ? "blockwise" : ""});
+ updateCmSelection(cm);
} else {
- curStart = cm.getCursor('anchor');
- curEnd = cm.getCursor('head');
- if (!vim.visualLine && actionArgs.linewise) {
- // Shift-V pressed in characterwise visual mode. Switch to linewise
- // visual mode instead of exiting visual mode.
- vim.visualLine = true;
- curStart.ch = cursorIsBefore(curStart, curEnd) ? 0 :
- lineLength(cm, curStart.line);
- curEnd.ch = cursorIsBefore(curStart, curEnd) ?
- lineLength(cm, curEnd.line) : 0;
- cm.setSelection(curStart, curEnd);
- CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: "linewise"});
- } else if (vim.visualLine && !actionArgs.linewise) {
- // v pressed in linewise visual mode. Switch to characterwise visual
- // mode instead of exiting visual mode.
- vim.visualLine = false;
- CodeMirror.signal(cm, "vim-mode-change", {mode: "visual"});
- } else {
- exitVisualMode(cm);
- }
+ exitVisualMode(cm);
}
- updateMark(cm, vim, '<', cursorIsBefore(curStart, curEnd) ? curStart
- : curEnd);
- updateMark(cm, vim, '>', cursorIsBefore(curStart, curEnd) ? curEnd
- : curStart);
},
reselectLastSelection: function(cm, _actionArgs, vim) {
- if (vim.lastSelection) {
- var lastSelection = vim.lastSelection;
- cm.setSelection(lastSelection.curStart, lastSelection.curEnd);
- if (lastSelection.visualLine) {
- vim.visualMode = true;
- vim.visualLine = true;
+ var lastSelection = vim.lastSelection;
+ if (vim.visualMode) {
+ updateLastSelection(cm, vim);
+ }
+ if (lastSelection) {
+ var anchor = lastSelection.anchorMark.find();
+ var head = lastSelection.headMark.find();
+ if (!anchor || !head) {
+ // If the marks have been destroyed due to edits, do nothing.
+ return;
}
- else {
- vim.visualMode = true;
- vim.visualLine = false;
- }
- CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: vim.visualLine ? "linewise" : ""});
+ vim.sel = {
+ anchor: anchor,
+ head: head
+ };
+ vim.visualMode = true;
+ vim.visualLine = lastSelection.visualLine;
+ vim.visualBlock = lastSelection.visualBlock;
+ updateCmSelection(cm);
+ updateMark(cm, vim, '<', cursorMin(anchor, head));
+ updateMark(cm, vim, '>', cursorMax(anchor, head));
+ CodeMirror.signal(cm, 'vim-mode-change', {
+ mode: 'visual',
+ subMode: vim.visualLine ? 'linewise' :
+ vim.visualBlock ? 'blockwise' : ''});
}
},
joinLines: function(cm, actionArgs, vim) {
@@ -1970,18 +2192,19 @@
Infinity));
}
var finalCh = 0;
- cm.operation(function() {
- for (var i = curStart.line; i < curEnd.line; i++) {
- finalCh = lineLength(cm, curStart.line);
- var tmp = Pos(curStart.line + 1,
- lineLength(cm, curStart.line + 1));
- var text = cm.getRange(curStart, tmp);
- text = text.replace(/\n\s*/g, ' ');
- cm.replaceRange(text, curStart, tmp);
- }
- var curFinalPos = Pos(curStart.line, finalCh);
- cm.setCursor(curFinalPos);
- });
+ for (var i = curStart.line; i < curEnd.line; i++) {
+ finalCh = lineLength(cm, curStart.line);
+ var tmp = Pos(curStart.line + 1,
+ lineLength(cm, curStart.line + 1));
+ var text = cm.getRange(curStart, tmp);
+ text = text.replace(/\n\s*/g, ' ');
+ cm.replaceRange(text, curStart, tmp);
+ }
+ var curFinalPos = Pos(curStart.line, finalCh);
+ cm.setCursor(curFinalPos);
+ if (vim.visualMode) {
+ exitVisualMode(cm);
+ }
},
newLineAndEnterInsertMode: function(cm, actionArgs, vim) {
vim.insertMode = true;
@@ -2001,7 +2224,7 @@
}
this.enterInsertMode(cm, { repeat: actionArgs.repeat }, vim);
},
- paste: function(cm, actionArgs) {
+ paste: function(cm, actionArgs, vim) {
var cur = copyCursor(cm.getCursor());
var register = vimGlobalState.registerController.getRegister(
actionArgs.registerName);
@@ -2009,12 +2232,44 @@
if (!text) {
return;
}
+ if (actionArgs.matchIndent) {
+ var tabSize = cm.getOption("tabSize");
+ // length that considers tabs and tabSize
+ var whitespaceLength = function(str) {
+ var tabs = (str.split("\t").length - 1);
+ var spaces = (str.split(" ").length - 1);
+ return tabs * tabSize + spaces * 1;
+ };
+ var currentLine = cm.getLine(cm.getCursor().line);
+ var indent = whitespaceLength(currentLine.match(/^\s*/)[0]);
+ // chomp last newline b/c don't want it to match /^\s*/gm
+ var chompedText = text.replace(/\n$/, '');
+ var wasChomped = text !== chompedText;
+ var firstIndent = whitespaceLength(text.match(/^\s*/)[0]);
+ var text = chompedText.replace(/^\s*/gm, function(wspace) {
+ var newIndent = indent + (whitespaceLength(wspace) - firstIndent);
+ if (newIndent < 0) {
+ return "";
+ }
+ else if (cm.getOption("indentWithTabs")) {
+ var quotient = Math.floor(newIndent / tabSize);
+ return Array(quotient + 1).join('\t');
+ }
+ else {
+ return Array(newIndent + 1).join(' ');
+ }
+ });
+ text += wasChomped ? "\n" : "";
+ }
if (actionArgs.repeat > 1) {
var text = Array(actionArgs.repeat + 1).join(text);
}
var linewise = register.linewise;
+ var blockwise = register.blockwise;
if (linewise) {
- if (actionArgs.after) {
+ if(vim.visualMode) {
+ text = vim.visualLine ? text.slice(0, -1) : '\n' + text.slice(0, text.length - 1) + '\n';
+ } else if (actionArgs.after) {
// Move the newline at the end to the start instead, and paste just
// before the newline character of the line we are on right now.
text = '\n' + text.slice(0, text.length - 1);
@@ -2023,26 +2278,96 @@
cur.ch = 0;
}
} else {
+ if (blockwise) {
+ text = text.split('\n');
+ for (var i = 0; i < text.length; i++) {
+ text[i] = (text[i] == '') ? ' ' : text[i];
+ }
+ }
cur.ch += actionArgs.after ? 1 : 0;
}
- cm.replaceRange(text, cur);
- // Now fine tune the cursor to where we want it.
var curPosFinal;
var idx;
- if (linewise && actionArgs.after) {
- curPosFinal = Pos(
- cur.line + 1,
- findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line + 1)));
- } else if (linewise && !actionArgs.after) {
- curPosFinal = Pos(
- cur.line,
- findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line)));
- } else if (!linewise && actionArgs.after) {
- idx = cm.indexFromPos(cur);
- curPosFinal = cm.posFromIndex(idx + text.length - 1);
+ if (vim.visualMode) {
+ // save the pasted text for reselection if the need arises
+ vim.lastPastedText = text;
+ var lastSelectionCurEnd;
+ var selectedArea = getSelectedAreaRange(cm, vim);
+ var selectionStart = selectedArea[0];
+ var selectionEnd = selectedArea[1];
+ var selectedText = cm.getSelection();
+ var selections = cm.listSelections();
+ var emptyStrings = new Array(selections.length).join('1').split('1');
+ // save the curEnd marker before it get cleared due to cm.replaceRange.
+ if (vim.lastSelection) {
+ lastSelectionCurEnd = vim.lastSelection.headMark.find();
+ }
+ // push the previously selected text to unnamed register
+ vimGlobalState.registerController.unnamedRegister.setText(selectedText);
+ if (blockwise) {
+ // first delete the selected text
+ cm.replaceSelections(emptyStrings);
+ // Set new selections as per the block length of the yanked text
+ selectionEnd = Pos(selectionStart.line + text.length-1, selectionStart.ch);
+ cm.setCursor(selectionStart);
+ selectBlock(cm, selectionEnd);
+ cm.replaceSelections(text);
+ curPosFinal = selectionStart;
+ } else if (vim.visualBlock) {
+ cm.replaceSelections(emptyStrings);
+ cm.setCursor(selectionStart);
+ cm.replaceRange(text, selectionStart, selectionStart);
+ curPosFinal = selectionStart;
+ } else {
+ cm.replaceRange(text, selectionStart, selectionEnd);
+ curPosFinal = cm.posFromIndex(cm.indexFromPos(selectionStart) + text.length - 1);
+ }
+ // restore the the curEnd marker
+ if(lastSelectionCurEnd) {
+ vim.lastSelection.headMark = cm.setBookmark(lastSelectionCurEnd);
+ }
+ if (linewise) {
+ curPosFinal.ch=0;
+ }
} else {
- idx = cm.indexFromPos(cur);
- curPosFinal = cm.posFromIndex(idx + text.length);
+ if (blockwise) {
+ cm.setCursor(cur);
+ for (var i = 0; i < text.length; i++) {
+ var line = cur.line+i;
+ if (line > cm.lastLine()) {
+ cm.replaceRange('\n', Pos(line, 0));
+ }
+ var lastCh = lineLength(cm, line);
+ if (lastCh < cur.ch) {
+ extendLineToColumn(cm, line, cur.ch);
+ }
+ }
+ cm.setCursor(cur);
+ selectBlock(cm, Pos(cur.line + text.length-1, cur.ch));
+ cm.replaceSelections(text);
+ curPosFinal = cur;
+ } else {
+ cm.replaceRange(text, cur);
+ // Now fine tune the cursor to where we want it.
+ if (linewise && actionArgs.after) {
+ curPosFinal = Pos(
+ cur.line + 1,
+ findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line + 1)));
+ } else if (linewise && !actionArgs.after) {
+ curPosFinal = Pos(
+ cur.line,
+ findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line)));
+ } else if (!linewise && actionArgs.after) {
+ idx = cm.indexFromPos(cur);
+ curPosFinal = cm.posFromIndex(idx + text.length - 1);
+ } else {
+ idx = cm.indexFromPos(cur);
+ curPosFinal = cm.posFromIndex(idx + text.length);
+ }
+ }
+ }
+ if (vim.visualMode) {
+ exitVisualMode(cm);
}
cm.setCursor(curPosFinal);
},
@@ -2067,13 +2392,11 @@
var curStart = cm.getCursor();
var replaceTo;
var curEnd;
- if (vim.visualMode){
- curStart=cm.getCursor('start');
- curEnd=cm.getCursor('end');
- // workaround to catch the character under the cursor
- // existing workaround doesn't cover actions
- curEnd=cm.clipPos(Pos(curEnd.line, curEnd.ch+1));
- }else{
+ var selections = cm.listSelections();
+ if (vim.visualMode) {
+ curStart = cm.getCursor('start');
+ curEnd = cm.getCursor('end');
+ } else {
var line = cm.getLine(curStart.line);
replaceTo = curStart.ch + actionArgs.repeat;
if (replaceTo > line.length) {
@@ -2081,19 +2404,29 @@
}
curEnd = Pos(curStart.line, replaceTo);
}
- if (replaceWith=='\n'){
+ if (replaceWith=='\n') {
if (!vim.visualMode) cm.replaceRange('', curStart, curEnd);
// special case, where vim help says to replace by just one line-break
(CodeMirror.commands.newlineAndIndentContinueComment || CodeMirror.commands.newlineAndIndent)(cm);
- }else {
- var replaceWithStr=cm.getRange(curStart, curEnd);
+ } else {
+ var replaceWithStr = cm.getRange(curStart, curEnd);
//replace all characters in range by selected, but keep linebreaks
- replaceWithStr=replaceWithStr.replace(/[^\n]/g,replaceWith);
- cm.replaceRange(replaceWithStr, curStart, curEnd);
- if (vim.visualMode){
+ replaceWithStr = replaceWithStr.replace(/[^\n]/g, replaceWith);
+ if (vim.visualBlock) {
+ // Tabs are split in visua block before replacing
+ var spaces = new Array(cm.getOption("tabSize")+1).join(' ');
+ replaceWithStr = cm.getSelection();
+ replaceWithStr = replaceWithStr.replace(/\t/g, spaces).replace(/[^\n]/g, replaceWith).split('\n');
+ cm.replaceSelections(replaceWithStr);
+ } else {
+ cm.replaceRange(replaceWithStr, curStart, curEnd);
+ }
+ if (vim.visualMode) {
+ curStart = cursorIsBefore(selections[0].anchor, selections[0].head) ?
+ selections[0].anchor : selections[0].head;
cm.setCursor(curStart);
exitVisualMode(cm);
- }else{
+ } else {
cm.setCursor(offsetCursor(curEnd, 0, -1));
}
}
@@ -2136,7 +2469,8 @@
repeat = vim.lastEditInputState.repeatOverride || repeat;
}
repeatLastEdit(cm, vim, repeat, false /** repeatForInsert */);
- }
+ },
+ exitInsertMode: exitInsertMode
};
/*
@@ -2164,16 +2498,66 @@
return ret;
}
function offsetCursor(cur, offsetLine, offsetCh) {
+ if (typeof offsetLine === 'object') {
+ offsetCh = offsetLine.ch;
+ offsetLine = offsetLine.line;
+ }
return Pos(cur.line + offsetLine, cur.ch + offsetCh);
}
- function matchKeysPartial(pressed, mapped) {
- for (var i = 0; i < pressed.length; i++) {
- // 'character' means any character. For mark, register commads, etc.
- if (pressed[i] != mapped[i] && mapped[i] != 'character') {
- return false;
+ function getOffset(anchor, head) {
+ return {
+ line: head.line - anchor.line,
+ ch: head.line - anchor.line
+ };
+ }
+ function commandMatches(keys, keyMap, context, inputState) {
+ // Partial matches are not applied. They inform the key handler
+ // that the current key sequence is a subsequence of a valid key
+ // sequence, so that the key buffer is not cleared.
+ var match, partial = [], full = [];
+ for (var i = 0; i < keyMap.length; i++) {
+ var command = keyMap[i];
+ if (context == 'insert' && command.context != 'insert' ||
+ command.context && command.context != context ||
+ inputState.operator && command.type == 'action' ||
+ !(match = commandMatch(keys, command.keys))) { continue; }
+ if (match == 'partial') { partial.push(command); }
+ if (match == 'full') { full.push(command); }
+ }
+ return {
+ partial: partial.length && partial,
+ full: full.length && full
+ };
+ }
+ function commandMatch(pressed, mapped) {
+ if (mapped.slice(-11) == '') {
+ // Last character matches anything.
+ var prefixLen = mapped.length - 11;
+ var pressedPrefix = pressed.slice(0, prefixLen);
+ var mappedPrefix = mapped.slice(0, prefixLen);
+ return pressedPrefix == mappedPrefix && pressed.length > prefixLen ? 'full' :
+ mappedPrefix.indexOf(pressedPrefix) == 0 ? 'partial' : false;
+ } else {
+ return pressed == mapped ? 'full' :
+ mapped.indexOf(pressed) == 0 ? 'partial' : false;
+ }
+ }
+ function lastChar(keys) {
+ var match = /^.*(<[\w\-]+>)$/.exec(keys);
+ var selectedCharacter = match ? match[1] : keys.slice(-1);
+ if (selectedCharacter.length > 1){
+ switch(selectedCharacter){
+ case '':
+ selectedCharacter='\n';
+ break;
+ case '':
+ selectedCharacter=' ';
+ break;
+ default:
+ break;
}
}
- return true;
+ return selectedCharacter;
}
function repeatFn(cm, fn, repeat) {
return function() {
@@ -2197,7 +2581,19 @@
}
return false;
}
- function cusrorIsBetween(cur1, cur2, cur3) {
+ function cursorMin(cur1, cur2) {
+ if (arguments.length > 2) {
+ cur2 = cursorMin.apply(undefined, Array.prototype.slice.call(arguments, 1));
+ }
+ return cursorIsBefore(cur1, cur2) ? cur1 : cur2;
+ }
+ function cursorMax(cur1, cur2) {
+ if (arguments.length > 2) {
+ cur2 = cursorMax.apply(undefined, Array.prototype.slice.call(arguments, 1));
+ }
+ return cursorIsBefore(cur1, cur2) ? cur2 : cur1;
+ }
+ function cursorIsBetween(cur1, cur2, cur3) {
// returns true if cur2 is between cur1 and cur3.
var cur1before2 = cursorIsBefore(cur1, cur2);
var cur2before3 = cursorIsBefore(cur2, cur3);
@@ -2218,25 +2614,254 @@
function escapeRegex(s) {
return s.replace(/([.?*+$\[\]\/\\(){}|\-])/g, '\\$1');
}
+ function extendLineToColumn(cm, lineNum, column) {
+ var endCh = lineLength(cm, lineNum);
+ var spaces = new Array(column-endCh+1).join(' ');
+ cm.setCursor(Pos(lineNum, endCh));
+ cm.replaceRange(spaces, cm.getCursor());
+ }
+ // This functions selects a rectangular block
+ // of text with selectionEnd as any of its corner
+ // Height of block:
+ // Difference in selectionEnd.line and first/last selection.line
+ // Width of the block:
+ // Distance between selectionEnd.ch and any(first considered here) selection.ch
+ function selectBlock(cm, selectionEnd) {
+ var selections = [], ranges = cm.listSelections();
+ var head = copyCursor(cm.clipPos(selectionEnd));
+ var isClipped = !cursorEqual(selectionEnd, head);
+ var curHead = cm.getCursor('head');
+ var primIndex = getIndex(ranges, curHead);
+ var wasClipped = cursorEqual(ranges[primIndex].head, ranges[primIndex].anchor);
+ var max = ranges.length - 1;
+ var index = max - primIndex > primIndex ? max : 0;
+ var base = ranges[index].anchor;
- function exitVisualMode(cm) {
- cm.off('mousedown', exitVisualMode);
+ var firstLine = Math.min(base.line, head.line);
+ var lastLine = Math.max(base.line, head.line);
+ var baseCh = base.ch, headCh = head.ch;
+
+ var dir = ranges[index].head.ch - baseCh;
+ var newDir = headCh - baseCh;
+ if (dir > 0 && newDir <= 0) {
+ baseCh++;
+ if (!isClipped) { headCh--; }
+ } else if (dir < 0 && newDir >= 0) {
+ baseCh--;
+ if (!wasClipped) { headCh++; }
+ } else if (dir < 0 && newDir == -1) {
+ baseCh--;
+ headCh++;
+ }
+ for (var line = firstLine; line <= lastLine; line++) {
+ var range = {anchor: new Pos(line, baseCh), head: new Pos(line, headCh)};
+ selections.push(range);
+ }
+ primIndex = head.line == lastLine ? selections.length - 1 : 0;
+ cm.setSelections(selections);
+ selectionEnd.ch = headCh;
+ base.ch = baseCh;
+ return base;
+ }
+ function selectForInsert(cm, head, height) {
+ var sel = [];
+ for (var i = 0; i < height; i++) {
+ var lineHead = offsetCursor(head, i, 0);
+ sel.push({anchor: lineHead, head: lineHead});
+ }
+ cm.setSelections(sel, 0);
+ }
+ // getIndex returns the index of the cursor in the selections.
+ function getIndex(ranges, cursor, end) {
+ for (var i = 0; i < ranges.length; i++) {
+ var atAnchor = end != 'head' && cursorEqual(ranges[i].anchor, cursor);
+ var atHead = end != 'anchor' && cursorEqual(ranges[i].head, cursor);
+ if (atAnchor || atHead) {
+ return i;
+ }
+ }
+ return -1;
+ }
+ function getSelectedAreaRange(cm, vim) {
+ var lastSelection = vim.lastSelection;
+ var getCurrentSelectedAreaRange = function() {
+ var selections = cm.listSelections();
+ var start = selections[0];
+ var end = selections[selections.length-1];
+ var selectionStart = cursorIsBefore(start.anchor, start.head) ? start.anchor : start.head;
+ var selectionEnd = cursorIsBefore(end.anchor, end.head) ? end.head : end.anchor;
+ return [selectionStart, selectionEnd];
+ };
+ var getLastSelectedAreaRange = function() {
+ var selectionStart = cm.getCursor();
+ var selectionEnd = cm.getCursor();
+ var block = lastSelection.visualBlock;
+ if (block) {
+ var width = block.width;
+ var height = block.height;
+ selectionEnd = Pos(selectionStart.line + height, selectionStart.ch + width);
+ var selections = [];
+ // selectBlock creates a 'proper' rectangular block.
+ // We do not want that in all cases, so we manually set selections.
+ for (var i = selectionStart.line; i < selectionEnd.line; i++) {
+ var anchor = Pos(i, selectionStart.ch);
+ var head = Pos(i, selectionEnd.ch);
+ var range = {anchor: anchor, head: head};
+ selections.push(range);
+ }
+ cm.setSelections(selections);
+ } else {
+ var start = lastSelection.anchorMark.find();
+ var end = lastSelection.headMark.find();
+ var line = end.line - start.line;
+ var ch = end.ch - start.ch;
+ selectionEnd = {line: selectionEnd.line + line, ch: line ? selectionEnd.ch : ch + selectionEnd.ch};
+ if (lastSelection.visualLine) {
+ selectionStart = Pos(selectionStart.line, 0);
+ selectionEnd = Pos(selectionEnd.line, lineLength(cm, selectionEnd.line));
+ }
+ cm.setSelection(selectionStart, selectionEnd);
+ }
+ return [selectionStart, selectionEnd];
+ };
+ if (!vim.visualMode) {
+ // In case of replaying the action.
+ return getLastSelectedAreaRange();
+ } else {
+ return getCurrentSelectedAreaRange();
+ }
+ }
+ // Updates the previous selection with the current selection's values. This
+ // should only be called in visual mode.
+ function updateLastSelection(cm, vim) {
+ var anchor = vim.sel.anchor;
+ var head = vim.sel.head;
+ // To accommodate the effect of lastPastedText in the last selection
+ if (vim.lastPastedText) {
+ head = cm.posFromIndex(cm.indexFromPos(anchor) + vim.lastPastedText.length);
+ vim.lastPastedText = null;
+ }
+ vim.lastSelection = {'anchorMark': cm.setBookmark(anchor),
+ 'headMark': cm.setBookmark(head),
+ 'anchor': copyCursor(anchor),
+ 'head': copyCursor(head),
+ 'visualMode': vim.visualMode,
+ 'visualLine': vim.visualLine,
+ 'visualBlock': vim.visualBlock};
+ }
+ function expandSelection(cm, start, end) {
+ var sel = cm.state.vim.sel;
+ var head = sel.head;
+ var anchor = sel.anchor;
+ var tmp;
+ if (cursorIsBefore(end, start)) {
+ tmp = end;
+ end = start;
+ start = tmp;
+ }
+ if (cursorIsBefore(head, anchor)) {
+ head = cursorMin(start, head);
+ anchor = cursorMax(anchor, end);
+ } else {
+ anchor = cursorMin(start, anchor);
+ head = cursorMax(head, end);
+ head = offsetCursor(head, 0, -1);
+ if (head.ch == -1 && head.line != cm.firstLine()) {
+ head = Pos(head.line - 1, lineLength(cm, head.line - 1));
+ }
+ }
+ return [anchor, head];
+ }
+ /**
+ * Updates the CodeMirror selection to match the provided vim selection.
+ * If no arguments are given, it uses the current vim selection state.
+ */
+ function updateCmSelection(cm, sel, mode) {
var vim = cm.state.vim;
- // can't use selection state here because yank has already reset its cursor
- vim.lastSelection = {'curStart': vim.marks['<'].find(),
- 'curEnd': vim.marks['>'].find(), 'visualMode': vim.visualMode,
- 'visualLine': vim.visualLine};
+ sel = sel || vim.sel;
+ var mode = mode ||
+ vim.visualLine ? 'line' : vim.visualBlock ? 'block' : 'char';
+ var cmSel = makeCmSelection(cm, sel, mode);
+ cm.setSelections(cmSel.ranges, cmSel.primary);
+ updateFakeCursor(cm);
+ }
+ function makeCmSelection(cm, sel, mode, exclusive) {
+ var head = copyCursor(sel.head);
+ var anchor = copyCursor(sel.anchor);
+ if (mode == 'char') {
+ var headOffset = !exclusive && !cursorIsBefore(sel.head, sel.anchor) ? 1 : 0;
+ var anchorOffset = cursorIsBefore(sel.head, sel.anchor) ? 1 : 0;
+ head = offsetCursor(sel.head, 0, headOffset);
+ anchor = offsetCursor(sel.anchor, 0, anchorOffset);
+ return {
+ ranges: [{anchor: anchor, head: head}],
+ primary: 0
+ };
+ } else if (mode == 'line') {
+ if (!cursorIsBefore(sel.head, sel.anchor)) {
+ anchor.ch = 0;
+
+ var lastLine = cm.lastLine();
+ if (head.line > lastLine) {
+ head.line = lastLine;
+ }
+ head.ch = lineLength(cm, head.line);
+ } else {
+ head.ch = 0;
+ anchor.ch = lineLength(cm, anchor.line);
+ }
+ return {
+ ranges: [{anchor: anchor, head: head}],
+ primary: 0
+ };
+ } else if (mode == 'block') {
+ var top = Math.min(anchor.line, head.line),
+ left = Math.min(anchor.ch, head.ch),
+ bottom = Math.max(anchor.line, head.line),
+ right = Math.max(anchor.ch, head.ch) + 1;
+ var height = bottom - top + 1;
+ var primary = head.line == top ? 0 : height - 1;
+ var ranges = [];
+ for (var i = 0; i < height; i++) {
+ ranges.push({
+ anchor: Pos(top + i, left),
+ head: Pos(top + i, right)
+ });
+ }
+ return {
+ ranges: ranges,
+ primary: primary
+ };
+ }
+ }
+ function getHead(cm) {
+ var cur = cm.getCursor('head');
+ if (cm.getSelection().length == 1) {
+ // Small corner case when only 1 character is selected. The "real"
+ // head is the left of head and anchor.
+ cur = cursorMin(cur, cm.getCursor('anchor'));
+ }
+ return cur;
+ }
+
+ /**
+ * If moveHead is set to false, the CodeMirror selection will not be
+ * touched. The caller assumes the responsibility of putting the cursor
+ * in the right place.
+ */
+ function exitVisualMode(cm, moveHead) {
+ var vim = cm.state.vim;
+ if (moveHead !== false) {
+ cm.setCursor(clipCursorToContent(cm, vim.sel.head));
+ }
+ updateLastSelection(cm, vim);
vim.visualMode = false;
vim.visualLine = false;
- var selectionStart = cm.getCursor('anchor');
- var selectionEnd = cm.getCursor('head');
- if (!cursorEqual(selectionStart, selectionEnd)) {
- // Clear the selection and set the cursor only if the selection has not
- // already been cleared. Otherwise we risk moving the cursor somewhere
- // it's not supposed to be.
- cm.setCursor(clipCursorToContent(cm, selectionEnd));
- }
+ vim.visualBlock = false;
CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
+ if (vim.fakeCursor) {
+ vim.fakeCursor.clear();
+ }
}
// Remove any trailing newlines from the selection. For
@@ -2287,7 +2912,7 @@
}
function expandWordUnderCursor(cm, inclusive, _forward, bigWord, noSymbol) {
- var cur = cm.getCursor();
+ var cur = getHead(cm);
var line = cm.getLine(cur.line);
var idx = cur.ch;
@@ -2562,6 +3187,7 @@
/**
* @param {CodeMirror} cm CodeMirror object.
+ * @param {Pos} cur The position to start from.
* @param {int} repeat Number of words to move past.
* @param {boolean} forward True to search forward. False to search
* backward.
@@ -2571,8 +3197,7 @@
* False if only alphabet characters count as part of the word.
* @return {Cursor} The position the cursor should move to.
*/
- function moveToWord(cm, repeat, forward, wordEnd, bigWord) {
- var cur = cm.getCursor();
+ function moveToWord(cm, cur, repeat, forward, wordEnd, bigWord) {
var curStart = copyCursor(cur);
var words = [];
if (forward && !wordEnd || !forward && wordEnd) {
@@ -2670,74 +3295,81 @@
return idx;
}
- function getContextLevel(ctx) {
- return (ctx === 'string' || ctx === 'comment') ? 1 : 0;
- }
-
- function findMatchedSymbol(cm, cur, symb) {
- var line = cur.line;
- var ch = cur.ch;
- symb = symb ? symb : cm.getLine(line).charAt(ch);
-
- var symbContext = cm.getTokenAt(Pos(line, ch + 1)).type;
- var symbCtxLevel = getContextLevel(symbContext);
-
- var reverseSymb = ({
- '(': ')', ')': '(',
- '[': ']', ']': '[',
- '{': '}', '}': '{'})[symb];
-
- // Couldn't find a matching symbol, abort
- if (!reverseSymb) {
- return cur;
+ function findParagraph(cm, head, repeat, dir, inclusive) {
+ var line = head.line;
+ var min = cm.firstLine();
+ var max = cm.lastLine();
+ var start, end, i = line;
+ function isEmpty(i) { return !cm.getLine(i); }
+ function isBoundary(i, dir, any) {
+ if (any) { return isEmpty(i) != isEmpty(i + dir); }
+ return !isEmpty(i) && isEmpty(i + dir);
+ }
+ if (dir) {
+ while (min <= i && i <= max && repeat > 0) {
+ if (isBoundary(i, dir)) { repeat--; }
+ i += dir;
+ }
+ return new Pos(i, 0);
}
- // set our increment to move forward (+1) or backwards (-1)
- // depending on which bracket we're matching
- var increment = ({'(': 1, '{': 1, '[': 1})[symb] || -1;
- var endLine = increment === 1 ? cm.lineCount() : -1;
- var depth = 1, nextCh = symb, index = ch, lineText = cm.getLine(line);
- // Simple search for closing paren--just count openings and closings till
- // we find our match
- // TODO: use info from CodeMirror to ignore closing brackets in comments
- // and quotes, etc.
- while (line !== endLine && depth > 0) {
- index += increment;
- nextCh = lineText.charAt(index);
- if (!nextCh) {
- line += increment;
- lineText = cm.getLine(line) || '';
- if (increment > 0) {
- index = 0;
- } else {
- var lineLen = lineText.length;
- index = (lineLen > 0) ? (lineLen-1) : 0;
- }
- nextCh = lineText.charAt(index);
- }
- var revSymbContext = cm.getTokenAt(Pos(line, index + 1)).type;
- var revSymbCtxLevel = getContextLevel(revSymbContext);
- if (symbCtxLevel >= revSymbCtxLevel) {
- if (nextCh === symb) {
- depth++;
- } else if (nextCh === reverseSymb) {
- depth--;
+ var vim = cm.state.vim;
+ if (vim.visualLine && isBoundary(line, 1, true)) {
+ var anchor = vim.sel.anchor;
+ if (isBoundary(anchor.line, -1, true)) {
+ if (!inclusive || anchor.line != line) {
+ line += 1;
}
}
}
-
- if (nextCh) {
- return Pos(line, index);
+ var startState = isEmpty(line);
+ for (i = line; i <= max && repeat; i++) {
+ if (isBoundary(i, 1, true)) {
+ if (!inclusive || isEmpty(i) != startState) {
+ repeat--;
+ }
+ }
}
- return cur;
+ end = new Pos(i, 0);
+ // select boundary before paragraph for the last one
+ if (i > max && !startState) { startState = true; }
+ else { inclusive = false; }
+ for (i = line; i > min; i--) {
+ if (!inclusive || isEmpty(i) == startState || i == line) {
+ if (isBoundary(i, -1, true)) { break; }
+ }
+ }
+ start = new Pos(i, 0);
+ return { start: start, end: end };
}
// TODO: perhaps this finagling of start and end positions belonds
// in codmirror/replaceRange?
- function selectCompanionObject(cm, revSymb, inclusive) {
- var cur = copyCursor(cm.getCursor());
- var end = findMatchedSymbol(cm, cur, revSymb);
- var start = findMatchedSymbol(cm, end);
+ function selectCompanionObject(cm, head, symb, inclusive) {
+ var cur = head, start, end;
+
+ var bracketRegexp = ({
+ '(': /[()]/, ')': /[()]/,
+ '[': /[[\]]/, ']': /[[\]]/,
+ '{': /[{}]/, '}': /[{}]/})[symb];
+ var openSym = ({
+ '(': '(', ')': '(',
+ '[': '[', ']': '[',
+ '{': '{', '}': '{'})[symb];
+ var curChar = cm.getLine(cur.line).charAt(cur.ch);
+ // Due to the behavior of scanForBracket, we need to add an offset if the
+ // cursor is on a matching open bracket.
+ var offset = curChar === openSym ? 1 : 0;
+
+ start = cm.scanForBracket(Pos(cur.line, cur.ch + offset), -1, null, {'bracketRegex': bracketRegexp});
+ end = cm.scanForBracket(Pos(cur.line, cur.ch + offset), 1, null, {'bracketRegex': bracketRegexp});
+
+ if (!start || !end) {
+ return { start: cur, end: cur };
+ }
+
+ start = start.pos;
+ end = end.pos;
if ((start.line == end.line && start.ch > end.ch)
|| (start.line > end.line)) {
@@ -2758,8 +3390,8 @@
// Takes in a symbol and a cursor and tries to simulate text objects that
// have identical opening and closing symbols
// TODO support across multiple lines
- function findBeginningAndEnd(cm, symb, inclusive) {
- var cur = copyCursor(cm.getCursor());
+ function findBeginningAndEnd(cm, head, symb, inclusive) {
+ var cur = copyCursor(head);
var line = cm.getLine(cur.line);
var chars = line.split('');
var start, end, i, len;
@@ -2837,6 +3469,12 @@
},
setReversed: function(reversed) {
vimGlobalState.isReversed = reversed;
+ },
+ getScrollbarAnnotate: function() {
+ return this.annotate;
+ },
+ setScrollbarAnnotate: function(annotate) {
+ this.annotate = annotate;
}
};
function getSearchState(cm) {
@@ -2852,6 +3490,18 @@
onClose(prompt(shortText, ''));
}
}
+ function splitBySlash(argString) {
+ var slashes = findUnescapedSlashes(argString) || [];
+ if (!slashes.length) return [];
+ var tokens = [];
+ // in case of strings like foo/bar
+ if (slashes[0] !== 0) return;
+ for (var i = 0; i < slashes.length; i++) {
+ if (typeof slashes[i] == 'number')
+ tokens.push(argString.substring(slashes[i] + 1, slashes[i+1]));
+ }
+ return tokens;
+ }
function findUnescapedSlashes(str) {
var escapeNextChar = false;
@@ -2869,15 +3519,15 @@
// Translates a search string from ex (vim) syntax into javascript form.
function translateRegex(str) {
// When these match, add a '\' if unescaped or remove one if escaped.
- var specials = ['|', '(', ')', '{'];
+ var specials = '|(){';
// Remove, but never add, a '\' for these.
- var unescape = ['}'];
+ var unescape = '}';
var escapeNextChar = false;
var out = [];
for (var i = -1; i < str.length; i++) {
var c = str.charAt(i) || '';
var n = str.charAt(i+1) || '';
- var specialComesNext = (specials.indexOf(n) != -1);
+ var specialComesNext = (n && specials.indexOf(n) != -1);
if (escapeNextChar) {
if (c !== '\\' || !specialComesNext) {
out.push(c);
@@ -2887,7 +3537,7 @@
if (c === '\\') {
escapeNextChar = true;
// Treat the unescape list as special for removing, but not adding '\'.
- if (unescape.indexOf(n) != -1) {
+ if (n && unescape.indexOf(n) != -1) {
specialComesNext = true;
}
// Not passing this test means removing a '\'.
@@ -2975,6 +3625,9 @@
* through to the Regex object.
*/
function parseQuery(query, ignoreCase, smartCase) {
+ // First update the last search register
+ var lastSearchRegister = vimGlobalState.registerController.getRegister('/');
+ lastSearchRegister.setText(query);
// Check if the query is already a regex.
if (query instanceof RegExp) { return query; }
// First try to extract regex + flags from the input. If no flags found,
@@ -3100,14 +3753,21 @@
};
}
function highlightSearchMatches(cm, query) {
- var overlay = getSearchState(cm).getOverlay();
+ var searchState = getSearchState(cm);
+ var overlay = searchState.getOverlay();
if (!overlay || query != overlay.query) {
if (overlay) {
cm.removeOverlay(overlay);
}
overlay = searchOverlay(query);
cm.addOverlay(overlay);
- getSearchState(cm).setOverlay(overlay);
+ if (cm.showMatchesOnScrollbar) {
+ if (searchState.getScrollbarAnnotate()) {
+ searchState.getScrollbarAnnotate().clear();
+ }
+ searchState.setScrollbarAnnotate(cm.showMatchesOnScrollbar(query));
+ }
+ searchState.setOverlay(overlay);
}
}
function findNext(cm, prev, query, repeat) {
@@ -3132,8 +3792,13 @@
});
}
function clearSearchHighlight(cm) {
+ var state = getSearchState(cm);
cm.removeOverlay(getSearchState(cm).getOverlay());
- getSearchState(cm).setOverlay(null);
+ state.setOverlay(null);
+ if (state.getScrollbarAnnotate()) {
+ state.getScrollbarAnnotate().clear();
+ state.setScrollbarAnnotate(null);
+ }
}
/**
* Check if pos is in the specified range, INCLUSIVE.
@@ -3177,6 +3842,7 @@
// shortNames must not match the prefix of the other command.
var defaultExCommandMap = [
{ name: 'map' },
+ { name: 'imap', shortName: 'im' },
{ name: 'nmap', shortName: 'nm' },
{ name: 'vmap', shortName: 'vm' },
{ name: 'unmap' },
@@ -3185,22 +3851,27 @@
{ name: 'redo', shortName: 'red' },
{ name: 'set', shortName: 'set' },
{ name: 'sort', shortName: 'sor' },
- { name: 'substitute', shortName: 's' },
+ { name: 'substitute', shortName: 's', possiblyAsync: true },
{ name: 'nohlsearch', shortName: 'noh' },
{ name: 'delmarks', shortName: 'delm' },
- { name: 'registers', shortName: 'reg' }
+ { name: 'registers', shortName: 'reg', excludeFromCommandHistory: true },
+ { name: 'global', shortName: 'g' }
];
- Vim.ExCommandDispatcher = function() {
+ var ExCommandDispatcher = function() {
this.buildCommandMap_();
};
- Vim.ExCommandDispatcher.prototype = {
- processCommand: function(cm, input) {
+ ExCommandDispatcher.prototype = {
+ processCommand: function(cm, input, opt_params) {
var vim = cm.state.vim;
+ var commandHistoryRegister = vimGlobalState.registerController.getRegister(':');
+ var previousCommand = commandHistoryRegister.toString();
if (vim.visualMode) {
exitVisualMode(cm);
}
var inputStream = new CodeMirror.StringStream(input);
- var params = {};
+ // update ": with the latest command whether valid or invalid
+ commandHistoryRegister.setText(input);
+ var params = opt_params || {};
params.input = input;
try {
this.parseInput_(cm, inputStream, params);
@@ -3208,6 +3879,7 @@
showConfirm(cm, e);
throw e;
}
+ var command;
var commandName;
if (!params.commandName) {
// If only a line range is defined, move to the line.
@@ -3215,14 +3887,17 @@
commandName = 'move';
}
} else {
- var command = this.matchCommand_(params.commandName);
+ command = this.matchCommand_(params.commandName);
if (command) {
commandName = command.name;
+ if (command.excludeFromCommandHistory) {
+ commandHistoryRegister.setText(previousCommand);
+ }
this.parseCommandArgs_(inputStream, params, command);
if (command.type == 'exToKey') {
// Handle Ex to Key mapping.
for (var i = 0; i < command.toKeys.length; i++) {
- CodeMirror.Vim.handleKey(cm, command.toKeys[i]);
+ CodeMirror.Vim.handleKey(cm, command.toKeys[i], 'mapping');
}
return;
} else if (command.type == 'exToEx') {
@@ -3238,6 +3913,12 @@
}
try {
exCommands[commandName](cm, params);
+ // Possibly asynchronous commands (e.g. substitute, which might have a
+ // user confirmation), are responsible for calling the callback when
+ // done. All others have it taken care of for them here.
+ if ((!command || !command.possiblyAsync) && params.callback) {
+ params.callback();
+ }
} catch(e) {
showConfirm(cm, e);
throw e;
@@ -3340,7 +4021,7 @@
this.commandMap_[commandName] = {
name: commandName,
type: 'exToKey',
- toKeys: parseKeyString(rhs),
+ toKeys: rhs,
user: true
};
}
@@ -3348,7 +4029,7 @@
if (rhs != ':' && rhs.charAt(0) == ':') {
// Key to Ex mapping.
var mapping = {
- keys: parseKeyString(lhs),
+ keys: lhs,
type: 'keyToEx',
exArgs: { input: rhs.substring(1) },
user: true};
@@ -3357,9 +4038,9 @@
} else {
// Key to key mapping
var mapping = {
- keys: parseKeyString(lhs),
+ keys: lhs,
type: 'keyToKey',
- toKeys: parseKeyString(rhs),
+ toKeys: rhs,
user: true
};
if (ctx) { mapping.context = ctx; }
@@ -3368,15 +4049,6 @@
}
},
unmap: function(lhs, ctx) {
- var arrayEquals = function(a, b) {
- if (a === b) return true;
- if (a == null || b == null) return true;
- if (a.length != b.length) return false;
- for (var i = 0; i < a.length; i++) {
- if (a[i] !== b[i]) return false;
- }
- return true;
- };
if (lhs != ':' && lhs.charAt(0) == ':') {
// Ex to Ex or Ex to key mapping
if (ctx) { throw Error('Mode not supported for ex mappings'); }
@@ -3387,9 +4059,9 @@
}
} else {
// Key to Ex or key to key mapping
- var keys = parseKeyString(lhs);
+ var keys = lhs;
for (var i = 0; i < defaultKeymap.length; i++) {
- if (arrayEquals(keys, defaultKeymap[i].keys)
+ if (keys == defaultKeymap[i].keys
&& defaultKeymap[i].context === ctx
&& defaultKeymap[i].user) {
defaultKeymap.splice(i, 1);
@@ -3401,21 +4073,6 @@
}
};
- // Converts a key string sequence of the form abd into Vim's
- // keymap representation.
- function parseKeyString(str) {
- var key, match;
- var keys = [];
- while (str) {
- match = (/<\w+-.+?>|<\w+>|./).exec(str);
- if (match === null)break;
- key = match[0];
- str = str.substring(match.index + key.length);
- keys.push(key);
- }
- return keys;
- }
-
var exCommands = {
map: function(cm, params, ctx) {
var mapArgs = params.args;
@@ -3427,6 +4084,7 @@
}
exCommandDispatcher.map(mapArgs[0], mapArgs[1], ctx);
},
+ imap: function(cm, params) { this.map(cm, params, 'insert'); },
nmap: function(cm, params) { this.map(cm, params, 'normal'); },
vmap: function(cm, params) { this.map(cm, params, 'visual'); },
unmap: function(cm, params, ctx) {
@@ -3509,7 +4167,7 @@
continue;
}
var register = registers[registerName] || new Register();
- regInfo += '"' + registerName + ' ' + register.text + ' ';
+ regInfo += '"' + registerName + ' ' + register.toString() + ' ';
}
}
showConfirm(cm, regInfo);
@@ -3589,44 +4247,112 @@
}
cm.replaceRange(text.join('\n'), curStart, curEnd);
},
+ global: function(cm, params) {
+ // a global command is of the form
+ // :[range]g/pattern/[cmd]
+ // argString holds the string /pattern/[cmd]
+ var argString = params.argString;
+ if (!argString) {
+ showConfirm(cm, 'Regular Expression missing from global');
+ return;
+ }
+ // range is specified here
+ var lineStart = (params.line !== undefined) ? params.line : cm.firstLine();
+ var lineEnd = params.lineEnd || params.line || cm.lastLine();
+ // get the tokens from argString
+ var tokens = splitBySlash(argString);
+ var regexPart = argString, cmd;
+ if (tokens.length) {
+ regexPart = tokens[0];
+ cmd = tokens.slice(1, tokens.length).join('/');
+ }
+ if (regexPart) {
+ // If regex part is empty, then use the previous query. Otherwise
+ // use the regex part as the new query.
+ try {
+ updateSearchQuery(cm, regexPart, true /** ignoreCase */,
+ true /** smartCase */);
+ } catch (e) {
+ showConfirm(cm, 'Invalid regex: ' + regexPart);
+ return;
+ }
+ }
+ // now that we have the regexPart, search for regex matches in the
+ // specified range of lines
+ var query = getSearchState(cm).getQuery();
+ var matchedLines = [], content = '';
+ for (var i = lineStart; i <= lineEnd; i++) {
+ var matched = query.test(cm.getLine(i));
+ if (matched) {
+ matchedLines.push(i+1);
+ content+= cm.getLine(i) + ' ';
+ }
+ }
+ // if there is no [cmd], just display the list of matched lines
+ if (!cmd) {
+ showConfirm(cm, content);
+ return;
+ }
+ var index = 0;
+ var nextCommand = function() {
+ if (index < matchedLines.length) {
+ var command = matchedLines[index] + cmd;
+ exCommandDispatcher.processCommand(cm, command, {
+ callback: nextCommand
+ });
+ }
+ index++;
+ };
+ nextCommand();
+ },
substitute: function(cm, params) {
if (!cm.getSearchCursor) {
throw new Error('Search feature not available. Requires searchcursor.js or ' +
'any other getSearchCursor implementation.');
}
var argString = params.argString;
- var slashes = findUnescapedSlashes(argString);
- if (slashes[0] !== 0) {
- showConfirm(cm, 'Substitutions should be of the form ' +
- ':s/pattern/replace/');
- return;
- }
- var regexPart = argString.substring(slashes[0] + 1, slashes[1]);
- var replacePart = '';
- var flagsPart;
- var count;
+ var tokens = argString ? splitBySlash(argString) : [];
+ var regexPart, replacePart = '', trailing, flagsPart, count;
var confirm = false; // Whether to confirm each replace.
- if (slashes[1]) {
- replacePart = argString.substring(slashes[1] + 1, slashes[2]);
- if (getOption('pcre')) {
- replacePart = unescapeRegexReplace(replacePart);
- } else {
- replacePart = translateRegexReplace(replacePart);
+ var global = false; // True to replace all instances on a line, false to replace only 1.
+ if (tokens.length) {
+ regexPart = tokens[0];
+ replacePart = tokens[1];
+ if (replacePart !== undefined) {
+ if (getOption('pcre')) {
+ replacePart = unescapeRegexReplace(replacePart);
+ } else {
+ replacePart = translateRegexReplace(replacePart);
+ }
+ vimGlobalState.lastSubstituteReplacePart = replacePart;
+ }
+ trailing = tokens[2] ? tokens[2].split(' ') : [];
+ } else {
+ // either the argString is empty or its of the form ' hello/world'
+ // actually splitBySlash returns a list of tokens
+ // only if the string starts with a '/'
+ if (argString && argString.length) {
+ showConfirm(cm, 'Substitutions should be of the form ' +
+ ':s/pattern/replace/');
+ return;
}
}
- if (slashes[2]) {
- // After the 3rd slash, we can have flags followed by a space followed
- // by count.
- var trailing = argString.substring(slashes[2] + 1).split(' ');
+ // After the 3rd slash, we can have flags followed by a space followed
+ // by count.
+ if (trailing) {
flagsPart = trailing[0];
count = parseInt(trailing[1]);
- }
- if (flagsPart) {
- if (flagsPart.indexOf('c') != -1) {
- confirm = true;
- flagsPart.replace('c', '');
+ if (flagsPart) {
+ if (flagsPart.indexOf('c') != -1) {
+ confirm = true;
+ flagsPart.replace('c', '');
+ }
+ if (flagsPart.indexOf('g') != -1) {
+ global = true;
+ flagsPart.replace('g', '');
+ }
+ regexPart = regexPart + '/' + flagsPart;
}
- regexPart = regexPart + '/' + flagsPart;
}
if (regexPart) {
// If regex part is empty, then use the previous query. Otherwise use
@@ -3639,6 +4365,11 @@
return;
}
}
+ replacePart = replacePart || vimGlobalState.lastSubstituteReplacePart;
+ if (replacePart === undefined) {
+ showConfirm(cm, 'No previous substitute regular expression');
+ return;
+ }
var state = getSearchState(cm);
var query = state.getQuery();
var lineStart = (params.line !== undefined) ? params.line : cm.getCursor().line;
@@ -3649,7 +4380,7 @@
}
var startPos = clipCursorToContent(cm, Pos(lineStart, 0));
var cursor = cm.getSearchCursor(query, startPos);
- doReplace(cm, confirm, lineStart, lineEnd, cursor, query, replacePart);
+ doReplace(cm, confirm, global, lineStart, lineEnd, cursor, query, replacePart, params.callback);
},
redo: CodeMirror.commands.redo,
undo: CodeMirror.commands.undo,
@@ -3728,7 +4459,7 @@
}
};
- var exCommandDispatcher = new Vim.ExCommandDispatcher();
+ var exCommandDispatcher = new ExCommandDispatcher();
/**
* @param {CodeMirror} cm CodeMirror instance we are in.
@@ -3738,9 +4469,10 @@
* @param {RegExp} query Query for performing matches with.
* @param {string} replaceWith Text to replace matches with. May contain $1,
* $2, etc for replacing captured groups using Javascript replace.
+ * @param {function()} callback A callback for when the replace is done.
*/
- function doReplace(cm, confirm, lineStart, lineEnd, searchCursor, query,
- replaceWith) {
+ function doReplace(cm, confirm, global, lineStart, lineEnd, searchCursor, query,
+ replaceWith, callback) {
// Set up all the functions.
cm.state.vim.exMode = true;
var done = false;
@@ -3760,17 +4492,21 @@
searchCursor.replace(newText);
}
function next() {
- var found = searchCursor.findNext();
- if (!found) {
- done = true;
- } else if (isInRange(searchCursor.from(), lineStart, lineEnd)) {
+ var found;
+ // The below only loops to skip over multiple occurrences on the same
+ // line when 'global' is not true.
+ while(found = searchCursor.findNext() &&
+ isInRange(searchCursor.from(), lineStart, lineEnd)) {
+ if (!global && lastPos && searchCursor.from().line == lastPos.line) {
+ continue;
+ }
cm.scrollIntoView(searchCursor.from(), 30);
cm.setSelection(searchCursor.from(), searchCursor.to());
lastPos = searchCursor.from();
done = false;
- } else {
- done = true;
+ return;
}
+ done = true;
}
function stop(close) {
if (close) { close(); }
@@ -3781,6 +4517,7 @@
vim.exMode = false;
vim.lastHPos = vim.lastHSPos = lastPos.ch;
}
+ if (callback) { callback(); }
}
function onPromptKeyDown(e, _value, close) {
// Swallow all keys.
@@ -3792,7 +4529,13 @@
case 'N':
next(); break;
case 'A':
- cm.operation(replaceAll); break;
+ // replaceAll contains a call to close of its own. We don't want it
+ // to fire too early or multiple times.
+ var savedCallback = callback;
+ callback = undefined;
+ cm.operation(replaceAll);
+ callback = savedCallback;
+ break;
case 'L':
replace();
// fall through and exit.
@@ -3804,6 +4547,7 @@
break;
}
if (done) { stop(close); }
+ return true;
}
// Actually do replace.
@@ -3814,6 +4558,7 @@
}
if (!confirm) {
replaceAll();
+ if (callback) { callback(); };
return;
}
showPrompt(cm, {
@@ -3822,72 +4567,44 @@
});
}
- // Register Vim with CodeMirror
- function buildVimKeyMap() {
- /**
- * Handle the raw key event from CodeMirror. Translate the
- * Shift + key modifier to the resulting letter, while preserving other
- * modifers.
- */
- function cmKeyToVimKey(key, modifier) {
- var vimKey = key;
- if (isUpperCase(vimKey) && modifier == 'Ctrl') {
- vimKey = vimKey.toLowerCase();
- }
- if (modifier) {
- // Vim will parse modifier+key combination as a single key.
- vimKey = modifier.charAt(0) + '-' + vimKey;
- }
- var specialKey = ({Enter:'CR',Backspace:'BS',Delete:'Del'})[vimKey];
- vimKey = specialKey ? specialKey : vimKey;
- vimKey = vimKey.length > 1 ? '<'+ vimKey + '>' : vimKey;
- return vimKey;
- }
-
- // Closure to bind CodeMirror, key, modifier.
- function keyMapper(vimKey) {
- return function(cm) {
- CodeMirror.Vim.handleKey(cm, vimKey);
- };
- }
-
- var cmToVimKeymap = {
- 'nofallthrough': true,
- 'style': 'fat-cursor'
- };
- function bindKeys(keys, modifier) {
- for (var i = 0; i < keys.length; i++) {
- var key = keys[i];
- if (!modifier && key.length == 1) {
- // Wrap all keys without modifiers with '' to identify them by their
- // key characters instead of key identifiers.
- key = "'" + key + "'";
- }
- var vimKey = cmKeyToVimKey(keys[i], modifier);
- var cmKey = modifier ? modifier + '-' + key : key;
- cmToVimKeymap[cmKey] = keyMapper(vimKey);
- }
- }
- bindKeys(upperCaseAlphabet);
- bindKeys(lowerCaseAlphabet);
- bindKeys(upperCaseAlphabet, 'Ctrl');
- bindKeys(specialSymbols);
- bindKeys(specialSymbols, 'Ctrl');
- bindKeys(numbers);
- bindKeys(numbers, 'Ctrl');
- bindKeys(specialKeys);
- bindKeys(specialKeys, 'Ctrl');
- return cmToVimKeymap;
- }
- CodeMirror.keyMap.vim = buildVimKeyMap();
+ CodeMirror.keyMap.vim = {
+ attach: attachVimMap,
+ detach: detachVimMap,
+ call: cmKey
+ };
function exitInsertMode(cm) {
var vim = cm.state.vim;
var macroModeState = vimGlobalState.macroModeState;
+ var insertModeChangeRegister = vimGlobalState.registerController.getRegister('.');
var isPlaying = macroModeState.isPlaying;
+ var lastChange = macroModeState.lastInsertModeChanges;
+ // In case of visual block, the insertModeChanges are not saved as a
+ // single word, so we convert them to a single word
+ // so as to update the ". register as expected in real vim.
+ var text = [];
if (!isPlaying) {
+ var selLength = lastChange.inVisualBlock ? vim.lastSelection.visualBlock.height : 1;
+ var changes = lastChange.changes;
+ var text = [];
+ var i = 0;
+ // In case of multiple selections in blockwise visual,
+ // the inserted text, for example: 'foo', is stored as
+ // 'f', 'f', InsertModeKey 'o', 'o', 'o', 'o'. (if you have a block with 2 lines).
+ // We push the contents of the changes array as per the following:
+ // 1. In case of InsertModeKey, just increment by 1.
+ // 2. In case of a character, jump by selLength (2 in the example).
+ while (i < changes.length) {
+ // This loop will convert 'ffoooo' to 'foo'.
+ text.push(changes[i]);
+ if (changes[i] instanceof InsertModeKey) {
+ i++;
+ } else {
+ i+= selLength;
+ }
+ }
+ lastChange.changes = text;
cm.off('change', onChange);
- cm.off('cursorActivity', onCursorActivity);
CodeMirror.off(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown);
}
if (!isPlaying && vim.insertModeRepeat > 1) {
@@ -3897,23 +4614,26 @@
vim.lastEditInputState.repeatOverride = vim.insertModeRepeat;
}
delete vim.insertModeRepeat;
- cm.setCursor(cm.getCursor().line, cm.getCursor().ch-1);
vim.insertMode = false;
+ cm.setCursor(cm.getCursor().line, cm.getCursor().ch-1);
cm.setOption('keyMap', 'vim');
cm.setOption('disableInput', true);
cm.toggleOverwrite(false); // exit replace mode if we were in it.
+ // update the ". register before exiting insert mode
+ insertModeChangeRegister.setText(lastChange.changes.join(''));
CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
if (macroModeState.isRecording) {
logInsertModeChange(macroModeState);
}
}
+ // The timeout in milliseconds for the two-character ESC keymap should be
+ // adjusted according to your typing speed to prevent false positives.
+ defineOption('insertModeEscKeysTimeout', 200, 'number');
+
CodeMirror.keyMap['vim-insert'] = {
// TODO: override navigation keys so that Esc will cancel automatic
// indentation from o, O, i_
- 'Esc': exitInsertMode,
- 'Ctrl-[': exitInsertMode,
- 'Ctrl-C': exitInsertMode,
'Ctrl-N': 'autocomplete',
'Ctrl-P': 'autocomplete',
'Enter': function(cm) {
@@ -3921,12 +4641,18 @@
CodeMirror.commands.newlineAndIndent;
fn(cm);
},
- fallthrough: ['default']
+ fallthrough: ['default'],
+ attach: attachVimMap,
+ detach: detachVimMap,
+ call: cmKey
};
CodeMirror.keyMap['vim-replace'] = {
'Backspace': 'goCharLeft',
- fallthrough: ['vim-insert']
+ fallthrough: ['vim-insert'],
+ attach: attachVimMap,
+ detach: detachVimMap,
+ call: cmKey
};
function executeMacroRegister(cm, vim, macroModeState, registerName) {
@@ -3934,6 +4660,7 @@
var keyBuffer = register.keyBuffer;
var imc = 0;
macroModeState.isPlaying = true;
+ macroModeState.replaySearchQueries = register.searchQueries.slice(0);
for (var i = 0; i < keyBuffer.length; i++) {
var text = keyBuffer[i];
var match, key;
@@ -3943,10 +4670,12 @@
match = (/<\w+-.+?>|<\w+>|./).exec(text);
key = match[0];
text = text.substring(match.index + key.length);
- CodeMirror.Vim.handleKey(cm, key);
+ CodeMirror.Vim.handleKey(cm, key, 'macro');
if (vim.insertMode) {
- repeatInsertModeChanges(
- cm, register.insertModeChanges[imc++].changes, 1);
+ var changes = register.insertModeChanges[imc++].changes;
+ vimGlobalState.macroModeState.lastInsertModeChanges.changes =
+ changes;
+ repeatInsertModeChanges(cm, changes, 1);
exitInsertMode(cm);
}
}
@@ -3972,6 +4701,15 @@
}
}
+ function logSearchQuery(macroModeState, query) {
+ if (macroModeState.isPlaying) { return; }
+ var registerName = macroModeState.latestRegister;
+ var register = vimGlobalState.registerController.getRegister(registerName);
+ if (register) {
+ register.pushSearchQuery(query);
+ }
+ }
+
/**
* Listens for changes made in insert mode.
* Should only be active in insert mode.
@@ -3995,18 +4733,63 @@
/**
* Listens for any kind of cursor activity on CodeMirror.
- * - For tracking cursor activity in insert mode.
- * - Should only be active in insert mode.
*/
- function onCursorActivity() {
- var macroModeState = vimGlobalState.macroModeState;
- if (macroModeState.isPlaying) { return; }
- var lastChange = macroModeState.lastInsertModeChanges;
- if (lastChange.expectCursorActivityForChange) {
- lastChange.expectCursorActivityForChange = false;
- } else {
- // Cursor moved outside the context of an edit. Reset the change.
- lastChange.changes = [];
+ function onCursorActivity(cm) {
+ var vim = cm.state.vim;
+ if (vim.insertMode) {
+ // Tracking cursor activity in insert mode (for macro support).
+ var macroModeState = vimGlobalState.macroModeState;
+ if (macroModeState.isPlaying) { return; }
+ var lastChange = macroModeState.lastInsertModeChanges;
+ if (lastChange.expectCursorActivityForChange) {
+ lastChange.expectCursorActivityForChange = false;
+ } else {
+ // Cursor moved outside the context of an edit. Reset the change.
+ lastChange.changes = [];
+ }
+ } else if (!cm.curOp.isVimOp) {
+ handleExternalSelection(cm, vim);
+ }
+ if (vim.visualMode) {
+ updateFakeCursor(cm);
+ }
+ }
+ function updateFakeCursor(cm) {
+ var vim = cm.state.vim;
+ var from = copyCursor(vim.sel.head);
+ var to = offsetCursor(from, 0, 1);
+ if (vim.fakeCursor) {
+ vim.fakeCursor.clear();
+ }
+ vim.fakeCursor = cm.markText(from, to, {className: 'cm-animate-fat-cursor'});
+ }
+ function handleExternalSelection(cm, vim) {
+ var anchor = cm.getCursor('anchor');
+ var head = cm.getCursor('head');
+ // Enter or exit visual mode to match mouse selection.
+ if (vim.visualMode && cursorEqual(head, anchor) && lineLength(cm, head.line) > head.ch) {
+ exitVisualMode(cm, false);
+ } else if (!vim.visualMode && !vim.insertMode && cm.somethingSelected()) {
+ vim.visualMode = true;
+ vim.visualLine = false;
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "visual"});
+ }
+ if (vim.visualMode) {
+ // Bind CodeMirror selection model to vim selection model.
+ // Mouse selections are considered visual characterwise.
+ var headOffset = !cursorIsBefore(head, anchor) ? -1 : 0;
+ var anchorOffset = cursorIsBefore(head, anchor) ? -1 : 0;
+ head = offsetCursor(head, 0, headOffset);
+ anchor = offsetCursor(anchor, 0, anchorOffset);
+ vim.sel = {
+ anchor: anchor,
+ head: head
+ };
+ updateMark(cm, vim, '<', cursorMin(head, anchor));
+ updateMark(cm, vim, '>', cursorMax(head, anchor));
+ } else if (!vim.insertMode) {
+ // Reset lastHPos if selection was modified by something outside of vim mode e.g. by mouse.
+ vim.lastHPos = cm.getCursor().ch;
}
}
@@ -4024,12 +4807,13 @@
var macroModeState = vimGlobalState.macroModeState;
var lastChange = macroModeState.lastInsertModeChanges;
var keyName = CodeMirror.keyName(e);
+ if (!keyName) { return; }
function onKeyFound() {
lastChange.changes.push(new InsertModeKey(keyName));
return true;
}
if (keyName.indexOf('Delete') != -1 || keyName.indexOf('Backspace') != -1) {
- CodeMirror.lookupKey(keyName, ['vim-insert'], onKeyFound);
+ CodeMirror.lookupKey(keyName, 'vim-insert', onKeyFound);
}
}
@@ -4060,11 +4844,7 @@
// insert mode changes. Will conform to that behavior.
repeat = !vim.lastEditActionCommand ? 1 : repeat;
var changeObject = macroModeState.lastInsertModeChanges;
- // This isn't strictly necessary, but since lastInsertModeChanges is
- // supposed to be immutable during replay, this helps catch bugs.
- macroModeState.lastInsertModeChanges = {};
repeatInsertModeChanges(cm, changeObject.changes, repeat);
- macroModeState.lastInsertModeChanges = changeObject;
}
}
vim.inputState = vim.lastEditInputState;
@@ -4102,17 +4882,34 @@
}
return true;
}
+ var head = cm.getCursor('head');
+ var inVisualBlock = vimGlobalState.macroModeState.lastInsertModeChanges.inVisualBlock;
+ if (inVisualBlock) {
+ // Set up block selection again for repeating the changes.
+ var vim = cm.state.vim;
+ var lastSel = vim.lastSelection;
+ var offset = getOffset(lastSel.anchor, lastSel.head);
+ selectForInsert(cm, head, offset.line + 1);
+ repeat = cm.listSelections().length;
+ cm.setCursor(head);
+ }
for (var i = 0; i < repeat; i++) {
+ if (inVisualBlock) {
+ cm.setCursor(offsetCursor(head, i, 0));
+ }
for (var j = 0; j < changes.length; j++) {
var change = changes[j];
if (change instanceof InsertModeKey) {
- CodeMirror.lookupKey(change.keyName, ['vim-insert'], keyHandler);
+ CodeMirror.lookupKey(change.keyName, 'vim-insert', keyHandler);
} else {
var cur = cm.getCursor();
cm.replaceRange(change, cur, cur);
}
}
}
+ if (inVisualBlock) {
+ cm.setCursor(offsetCursor(head, 0, 1));
+ }
}
resetVimGlobalState();
diff --git a/applications/admin/static/codemirror/lib/codemirror.css b/applications/admin/static/codemirror/lib/codemirror.css
index d263e44b..c56510e9 100644
--- a/applications/admin/static/codemirror/lib/codemirror.css
+++ b/applications/admin/static/codemirror/lib/codemirror.css
@@ -5,10 +5,6 @@
font-family: monospace;
height: 300px;
}
-.CodeMirror-scroll {
- /* Set scrolling behaviour here */
- overflow: auto;
-}
/* PADDING */
@@ -40,6 +36,9 @@
box-sizing: content-box;
}
+.CodeMirror-guttermarker { color: black; }
+.CodeMirror-guttermarker-subtle { color: #999; }
+
/* CURSOR */
.CodeMirror div.CodeMirror-cursor {
@@ -49,15 +48,42 @@
.CodeMirror div.CodeMirror-secondarycursor {
border-left: 1px solid silver;
}
-.CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor {
+.CodeMirror.cm-fat-cursor div.CodeMirror-cursor {
width: auto;
border: 0;
background: #7e7;
}
+.CodeMirror.cm-fat-cursor div.CodeMirror-cursors {
+ z-index: 1;
+}
+
+.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;
+}
+@-moz-keyframes blink {
+ 0% { background: #7e7; }
+ 50% { background: none; }
+ 100% { background: #7e7; }
+}
+@-webkit-keyframes blink {
+ 0% { background: #7e7; }
+ 50% { background: none; }
+ 100% { background: #7e7; }
+}
+@keyframes blink {
+ 0% { background: #7e7; }
+ 50% { background: none; }
+ 100% { background: #7e7; }
+}
+
/* Can style cursor different in overwrite (non-insert) mode */
div.CodeMirror-overwrite div.CodeMirror-cursor {}
-.cm-tab { display: inline-block; }
+.cm-tab { display: inline-block; text-decoration: inherit; }
.CodeMirror-ruler {
border-left: 1px solid #ccc;
@@ -70,11 +96,12 @@ div.CodeMirror-overwrite div.CodeMirror-cursor {}
.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 {color: black;}
+.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 {color: #085;}
-.cm-s-default .cm-property {color: black;}
-.cm-s-default .cm-operator {color: black;}
.cm-s-default .cm-comment {color: #a50;}
.cm-s-default .cm-string {color: #a11;}
.cm-s-default .cm-string-2 {color: #f50;}
@@ -94,12 +121,16 @@ div.CodeMirror-overwrite div.CodeMirror-cursor {}
.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-error {color: #f00;}
.cm-invalidchar {color: #f00;}
+/* Default styles for common addons */
+
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
+.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
.CodeMirror-activeline-background {background: #e8f2ff;}
/* STOP */
@@ -116,6 +147,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
}
.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;
@@ -160,7 +192,6 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
.CodeMirror-gutters {
position: absolute; left: 0; top: 0;
- padding-bottom: 30px;
z-index: 3;
}
.CodeMirror-gutter {
@@ -168,13 +199,17 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
height: 100%;
-moz-box-sizing: content-box;
box-sizing: content-box;
- padding-bottom: 30px;
- margin-bottom: -32px;
display: inline-block;
+ margin-bottom: -30px;
/* Hack to make IE7 behave */
*zoom:1;
*display:inline;
}
+.CodeMirror-gutter-wrapper {
+ position: absolute;
+ z-index: 4;
+ height: 100%;
+}
.CodeMirror-gutter-elt {
position: absolute;
cursor: default;
@@ -183,6 +218,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
.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 */
@@ -220,10 +256,6 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
.CodeMirror-widget {}
-.CodeMirror-wrap .CodeMirror-scroll {
- overflow-x: hidden;
-}
-
.CodeMirror-measure {
position: absolute;
width: 100%;
@@ -242,7 +274,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
div.CodeMirror-cursors {
visibility: hidden;
position: relative;
- z-index: 1;
+ z-index: 3;
}
.CodeMirror-focused div.CodeMirror-cursors {
visibility: visible;
@@ -250,6 +282,7 @@ div.CodeMirror-cursors {
.CodeMirror-selected { background: #d9d9d9; }
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
+.CodeMirror-crosshair { cursor: crosshair; }
.cm-searching {
background: #ffa;
@@ -268,3 +301,9 @@ 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/applications/admin/static/codemirror/lib/codemirror.js b/applications/admin/static/codemirror/lib/codemirror.js
index c3205cc1..03a34dbb 100644
--- a/applications/admin/static/codemirror/lib/codemirror.js
+++ b/applications/admin/static/codemirror/lib/codemirror.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
// This is CodeMirror (http://codemirror.net), a code editor
// implemented in JavaScript on top of the browser's DOM.
//
@@ -22,18 +25,15 @@
var gecko = /gecko\/\d/i.test(navigator.userAgent);
// ie_uptoN means Internet Explorer version N or lower
var ie_upto10 = /MSIE \d/.test(navigator.userAgent);
- var ie_upto7 = ie_upto10 && (document.documentMode == null || document.documentMode < 8);
- var ie_upto8 = ie_upto10 && (document.documentMode == null || document.documentMode < 9);
- var ie_upto9 = ie_upto10 && (document.documentMode == null || document.documentMode < 10);
- var ie_11up = /Trident\/([7-9]|\d{2,})\./.test(navigator.userAgent);
+ var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent);
var ie = ie_upto10 || ie_11up;
+ var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]);
var webkit = /WebKit\//.test(navigator.userAgent);
var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
var chrome = /Chrome\//.test(navigator.userAgent);
var presto = /Opera\//.test(navigator.userAgent);
var safari = /Apple Computer/.test(navigator.vendor);
var khtml = /KHTML\//.test(navigator.userAgent);
- var mac_geLion = /Mac OS X 1\d\D([7-9]|\d\d)\D/.test(navigator.userAgent);
var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent);
var phantom = /PhantomJS/.test(navigator.userAgent);
@@ -48,7 +48,7 @@
if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
// Some browsers use the wrong event properties to signal cmd/ctrl on OS X
var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
- var captureRightClick = gecko || (ie && !ie_upto8);
+ var captureRightClick = gecko || (ie && ie_version >= 9);
// Optimize some code when these features are not used.
var sawReadOnlySpans = false, sawCollapsedSpans = false;
@@ -61,10 +61,9 @@
function CodeMirror(place, options) {
if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
- this.options = options = options || {};
+ this.options = options = options ? copyObj(options) : {};
// Determine effective options based on given values and defaults.
- for (var opt in defaults) if (!options.hasOwnProperty(opt))
- options[opt] = defaults[opt];
+ copyObj(defaults, options, false);
setGuttersForLineNumbers(options);
var doc = options.value;
@@ -78,6 +77,7 @@
if (options.lineWrapping)
this.display.wrapper.className += " CodeMirror-wrap";
if (options.autofocus && !mobile) focusInput(this);
+ initScrollbars(this);
this.state = {
keyMaps: [], // stores maps added by addKeyMap
@@ -87,29 +87,36 @@
suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in readInput
draggingText: false,
- highlight: new Delayed() // stores highlight worker timeout
+ highlight: new Delayed(), // stores highlight worker timeout
+ keySeq: null // Unfinished key sequence
};
// Override magic textarea content restore that IE sometimes does
// on our hidden textarea on reload
- if (ie_upto10) setTimeout(bind(resetInput, this, true), 20);
+ if (ie && ie_version < 11) setTimeout(bind(resetInput, this, true), 20);
registerEventHandlers(this);
+ ensureGlobalHandlers();
- var cm = this;
- runInOp(this, function() {
- cm.curOp.forceUpdate = true;
- attachDoc(cm, doc);
+ startOperation(this);
+ this.curOp.forceUpdate = true;
+ attachDoc(this, doc);
- if ((options.autofocus && !mobile) || activeElt() == display.input)
- setTimeout(bind(onFocus, cm), 20);
- else
- onBlur(cm);
+ if ((options.autofocus && !mobile) || activeElt() == display.input)
+ setTimeout(bind(onFocus, this), 20);
+ else
+ onBlur(this);
- for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt))
- optionHandlers[opt](cm, options[opt], Init);
- for (var i = 0; i < initHooks.length; ++i) initHooks[i](cm);
- });
+ for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt))
+ optionHandlers[opt](this, options[opt], Init);
+ maybeUpdateLineNumberWidth(this);
+ for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
+ endOperation(this);
+ // Suppress optimizelegibility in Webkit, since it breaks text
+ // measuring on line wrapping boundaries.
+ if (webkit && options.lineWrapping &&
+ getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
+ display.lineDiv.style.textRendering = "auto";
}
// DISPLAY CONSTRUCTOR
@@ -136,14 +143,13 @@
// Wraps and hides input textarea
d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
- // The fake scrollbar elements.
- d.scrollbarH = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
- d.scrollbarV = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
// Covers bottom-right square when both scrollbars are present.
d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
+ d.scrollbarFiller.setAttribute("not-content", "true");
// Covers bottom of gutter when coverGutterNextToScrollbar is on
// and h scrollbar is present.
d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
+ d.gutterFiller.setAttribute("not-content", "true");
// Will contain the actual code, positioned to cover the viewport.
d.lineDiv = elt("div", null, "CodeMirror-code");
// Elements are added to these to represent selection and cursors.
@@ -160,10 +166,11 @@
d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
// Set to the height of the document, allowing scrolling.
d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
+ d.sizerWidth = null;
// Behavior of elts with overflow: auto and padding is
// inconsistent across browsers. This is used to ensure the
// scrollable area is big enough.
- d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerCutOff + "px; width: 1px;");
+ d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
// Will contain the gutters, if any.
d.gutters = elt("div", null, "CodeMirror-gutters");
d.lineGutter = null;
@@ -171,34 +178,38 @@
d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
d.scroller.setAttribute("tabIndex", "-1");
// The element in which the editor lives.
- d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV,
- d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
+ d.wrapper = elt("div", [d.inputDiv, d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
// Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
- if (ie_upto7) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
+ if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
// Needed to hide big blue blinking cursor on Mobile Safari
if (ios) input.style.width = "0px";
if (!webkit) d.scroller.draggable = true;
// Needed to handle Tab key in KHTML
if (khtml) { d.inputDiv.style.height = "1px"; d.inputDiv.style.position = "absolute"; }
- // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
- if (ie_upto7) d.scrollbarH.style.minHeight = d.scrollbarV.style.minWidth = "18px";
- if (place.appendChild) place.appendChild(d.wrapper);
- else place(d.wrapper);
+ if (place) {
+ if (place.appendChild) place.appendChild(d.wrapper);
+ else place(d.wrapper);
+ }
// Current rendered range (may be bigger than the view window).
d.viewFrom = d.viewTo = doc.first;
+ d.reportedViewFrom = d.reportedViewTo = doc.first;
// Information about the rendered lines.
d.view = [];
+ d.renderedView = null;
// Holds info about a single rendered line when it was rendered
// for measurement, while not in view.
d.externalMeasured = null;
// Empty space (in pixels) above the view
d.viewOffset = 0;
- d.lastSizeC = 0;
+ d.lastWrapHeight = d.lastWrapWidth = 0;
d.updateLineNumbers = null;
+ d.nativeBarWidth = d.barHeight = d.barWidth = 0;
+ d.scrollbarsClipped = false;
+
// Used to only resize the line number gutter when necessary (when
// the amount of lines crosses a boundary that makes its width change)
d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
@@ -232,6 +243,10 @@
// True when shift is held down.
d.shift = false;
+
+ // Used to track whether anything happened since the context menu
+ // was opened.
+ d.selForContextMenu = null;
}
// STATE UPDATES
@@ -256,10 +271,11 @@
function wrappingChanged(cm) {
if (cm.options.lineWrapping) {
- cm.display.wrapper.className += " CodeMirror-wrap";
+ addClass(cm.display.wrapper, "CodeMirror-wrap");
cm.display.sizer.style.minWidth = "";
+ cm.display.sizerWidth = null;
} else {
- cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", "");
+ rmClass(cm.display.wrapper, "CodeMirror-wrap");
findMaxLine(cm);
}
estimateLineHeights(cm);
@@ -297,12 +313,6 @@
});
}
- function keyMapChanged(cm) {
- var map = keyMap[cm.options.keyMap], style = map.style;
- cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
- (style ? " cm-keymap-" + style : "");
- }
-
function themeChanged(cm) {
cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
@@ -329,9 +339,12 @@
}
}
gutters.style.display = i ? "" : "none";
- var width = gutters.offsetWidth;
+ updateGutterSpace(cm);
+ }
+
+ function updateGutterSpace(cm) {
+ var width = cm.display.gutters.offsetWidth;
cm.display.sizer.style.marginLeft = width + "px";
- if (i) cm.display.scrollbarH.style.left = cm.options.fixedGutter ? width + "px" : 0;
}
// Compute the character length of a line, taking into account
@@ -387,85 +400,187 @@
// Prepare DOM reads needed to update the scrollbars. Done in one
// shot to minimize update/measure roundtrips.
function measureForScrollbars(cm) {
- var scroll = cm.display.scroller;
+ var d = cm.display, gutterW = d.gutters.offsetWidth;
+ var docH = Math.round(cm.doc.height + paddingVert(cm.display));
return {
- clientHeight: scroll.clientHeight,
- barHeight: cm.display.scrollbarV.clientHeight,
- scrollWidth: scroll.scrollWidth, clientWidth: scroll.clientWidth,
- barWidth: cm.display.scrollbarH.clientWidth,
- docHeight: Math.round(cm.doc.height + paddingVert(cm.display))
+ clientHeight: d.scroller.clientHeight,
+ viewHeight: d.wrapper.clientHeight,
+ scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
+ viewWidth: d.wrapper.clientWidth,
+ barLeft: cm.options.fixedGutter ? gutterW : 0,
+ docHeight: docH,
+ scrollHeight: docH + scrollGap(cm) + d.barHeight,
+ nativeBarWidth: d.nativeBarWidth,
+ gutterWidth: gutterW
};
}
+ function NativeScrollbars(place, scroll, cm) {
+ this.cm = cm;
+ var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
+ var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
+ place(vert); place(horiz);
+
+ on(vert, "scroll", function() {
+ if (vert.clientHeight) scroll(vert.scrollTop, "vertical");
+ });
+ on(horiz, "scroll", function() {
+ if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal");
+ });
+
+ this.checkedOverlay = false;
+ // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
+ if (ie && ie_version < 8) this.horiz.style.minHeight = this.vert.style.minWidth = "18px";
+ }
+
+ NativeScrollbars.prototype = copyObj({
+ update: function(measure) {
+ var needsH = measure.scrollWidth > measure.clientWidth + 1;
+ var needsV = measure.scrollHeight > measure.clientHeight + 1;
+ var sWidth = measure.nativeBarWidth;
+
+ if (needsV) {
+ this.vert.style.display = "block";
+ this.vert.style.bottom = needsH ? sWidth + "px" : "0";
+ var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
+ // A bug in IE8 can cause this value to be negative, so guard it.
+ this.vert.firstChild.style.height =
+ Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
+ } else {
+ this.vert.style.display = "";
+ this.vert.firstChild.style.height = "0";
+ }
+
+ if (needsH) {
+ this.horiz.style.display = "block";
+ this.horiz.style.right = needsV ? sWidth + "px" : "0";
+ this.horiz.style.left = measure.barLeft + "px";
+ var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
+ this.horiz.firstChild.style.width =
+ (measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
+ } else {
+ this.horiz.style.display = "";
+ this.horiz.firstChild.style.width = "0";
+ }
+
+ if (!this.checkedOverlay && measure.clientHeight > 0) {
+ if (sWidth == 0) this.overlayHack();
+ this.checkedOverlay = true;
+ }
+
+ return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0};
+ },
+ setScrollLeft: function(pos) {
+ if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos;
+ },
+ setScrollTop: function(pos) {
+ if (this.vert.scrollTop != pos) this.vert.scrollTop = pos;
+ },
+ overlayHack: function() {
+ var w = mac && !mac_geMountainLion ? "12px" : "18px";
+ this.horiz.style.minHeight = this.vert.style.minWidth = w;
+ var self = this;
+ var barMouseDown = function(e) {
+ if (e_target(e) != self.vert && e_target(e) != self.horiz)
+ operation(self.cm, onMouseDown)(e);
+ };
+ on(this.vert, "mousedown", barMouseDown);
+ on(this.horiz, "mousedown", barMouseDown);
+ },
+ clear: function() {
+ var parent = this.horiz.parentNode;
+ parent.removeChild(this.horiz);
+ parent.removeChild(this.vert);
+ }
+ }, NativeScrollbars.prototype);
+
+ function NullScrollbars() {}
+
+ NullScrollbars.prototype = copyObj({
+ update: function() { return {bottom: 0, right: 0}; },
+ setScrollLeft: function() {},
+ setScrollTop: function() {},
+ clear: function() {}
+ }, NullScrollbars.prototype);
+
+ CodeMirror.scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
+
+ function initScrollbars(cm) {
+ if (cm.display.scrollbars) {
+ cm.display.scrollbars.clear();
+ if (cm.display.scrollbars.addClass)
+ rmClass(cm.display.wrapper, cm.display.scrollbars.addClass);
+ }
+
+ cm.display.scrollbars = new CodeMirror.scrollbarModel[cm.options.scrollbarStyle](function(node) {
+ cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
+ on(node, "mousedown", function() {
+ if (cm.state.focused) setTimeout(bind(focusInput, cm), 0);
+ });
+ node.setAttribute("not-content", "true");
+ }, function(pos, axis) {
+ if (axis == "horizontal") setScrollLeft(cm, pos);
+ else setScrollTop(cm, pos);
+ }, cm);
+ if (cm.display.scrollbars.addClass)
+ addClass(cm.display.wrapper, cm.display.scrollbars.addClass);
+ }
+
+ function updateScrollbars(cm, measure) {
+ if (!measure) measure = measureForScrollbars(cm);
+ var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
+ updateScrollbarsInner(cm, measure);
+ for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
+ if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
+ updateHeightsInViewport(cm);
+ updateScrollbarsInner(cm, measureForScrollbars(cm));
+ startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
+ }
+ }
+
// Re-synchronize the fake scrollbars with the actual size of the
// content.
- function updateScrollbars(cm, measure) {
- if (!measure) measure = measureForScrollbars(cm);
+ function updateScrollbarsInner(cm, measure) {
var d = cm.display;
- var scrollHeight = measure.docHeight + scrollerCutOff;
- var needsH = measure.scrollWidth > measure.clientWidth;
- var needsV = scrollHeight > measure.clientHeight;
- if (needsV) {
- d.scrollbarV.style.display = "block";
- d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
- // A bug in IE8 can cause this value to be negative, so guard it.
- d.scrollbarV.firstChild.style.height =
- Math.max(0, scrollHeight - measure.clientHeight + (measure.barHeight || d.scrollbarV.clientHeight)) + "px";
- } else {
- d.scrollbarV.style.display = "";
- d.scrollbarV.firstChild.style.height = "0";
- }
- if (needsH) {
- d.scrollbarH.style.display = "block";
- d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + "px" : "0";
- d.scrollbarH.firstChild.style.width =
- (measure.scrollWidth - measure.clientWidth + (measure.barWidth || d.scrollbarH.clientWidth)) + "px";
- } else {
- d.scrollbarH.style.display = "";
- d.scrollbarH.firstChild.style.width = "0";
- }
- if (needsH && needsV) {
- d.scrollbarFiller.style.display = "block";
- d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px";
- } else d.scrollbarFiller.style.display = "";
- if (needsH && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
- d.gutterFiller.style.display = "block";
- d.gutterFiller.style.height = scrollbarWidth(d.measure) + "px";
- d.gutterFiller.style.width = d.gutters.offsetWidth + "px";
- } else d.gutterFiller.style.display = "";
+ var sizes = d.scrollbars.update(measure);
- if (mac_geLion && scrollbarWidth(d.measure) === 0) {
- d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
- var barMouseDown = function(e) {
- if (e_target(e) != d.scrollbarV && e_target(e) != d.scrollbarH)
- operation(cm, onMouseDown)(e);
- };
- on(d.scrollbarV, "mousedown", barMouseDown);
- on(d.scrollbarH, "mousedown", barMouseDown);
- }
+ d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
+ d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
+
+ if (sizes.right && sizes.bottom) {
+ d.scrollbarFiller.style.display = "block";
+ d.scrollbarFiller.style.height = sizes.bottom + "px";
+ d.scrollbarFiller.style.width = sizes.right + "px";
+ } else d.scrollbarFiller.style.display = "";
+ if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
+ d.gutterFiller.style.display = "block";
+ d.gutterFiller.style.height = sizes.bottom + "px";
+ d.gutterFiller.style.width = measure.gutterWidth + "px";
+ } else d.gutterFiller.style.display = "";
}
// Compute the lines that are visible in a given viewport (defaults
- // the the current scroll position). viewPort may contain top,
+ // the the current scroll position). viewport may contain top,
// height, and ensure (see op.scrollToPos) properties.
- function visibleLines(display, doc, viewPort) {
- var top = viewPort && viewPort.top != null ? viewPort.top : display.scroller.scrollTop;
+ function visibleLines(display, doc, viewport) {
+ var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
top = Math.floor(top - paddingTop(display));
- var bottom = viewPort && viewPort.bottom != null ? viewPort.bottom : top + display.wrapper.clientHeight;
+ var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
// Ensure is a {from: {line, ch}, to: {line, ch}} object, and
// forces those lines into the viewport (if possible).
- if (viewPort && viewPort.ensure) {
- var ensureFrom = viewPort.ensure.from.line, ensureTo = viewPort.ensure.to.line;
- if (ensureFrom < from)
- return {from: ensureFrom,
- to: lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight)};
- if (Math.min(ensureTo, doc.lastLine()) >= to)
- return {from: lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight),
- to: ensureTo};
+ if (viewport && viewport.ensure) {
+ var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
+ if (ensureFrom < from) {
+ from = ensureFrom;
+ to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
+ } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
+ from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
+ to = ensureTo;
+ }
}
- return {from: from, to: to};
+ return {from: from, to: Math.max(to, from + 1)};
}
// LINE NUMBERS
@@ -503,9 +618,7 @@
display.lineNumWidth = display.lineNumInnerWidth + padding;
display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
display.lineGutter.style.width = display.lineNumWidth + "px";
- var width = display.gutters.offsetWidth;
- display.scrollbarH.style.left = cm.options.fixedGutter ? width + "px" : 0;
- display.sizer.style.marginLeft = width + "px";
+ updateGutterSpace(cm);
return true;
}
return false;
@@ -524,76 +637,58 @@
// DISPLAY DRAWING
- // Updates the display, selection, and scrollbars, using the
- // information in display.view to find out which nodes are no longer
- // up-to-date. Tries to bail out early when no changes are needed,
- // unless forced is true.
- // Returns true if an actual update happened, false otherwise.
- function updateDisplay(cm, viewPort, forced) {
- var oldFrom = cm.display.viewFrom, oldTo = cm.display.viewTo, updated;
- var visible = visibleLines(cm.display, cm.doc, viewPort);
- for (var first = true;; first = false) {
- var oldWidth = cm.display.scroller.clientWidth;
- if (!updateDisplayInner(cm, visible, forced)) break;
- updated = true;
+ function DisplayUpdate(cm, viewport, force) {
+ var display = cm.display;
- // If the max line changed since it was last measured, measure it,
- // and ensure the document's width matches it.
- if (cm.display.maxLineChanged && !cm.options.lineWrapping)
- adjustContentWidth(cm);
+ this.viewport = viewport;
+ // Store some values that we'll need later (but don't want to force a relayout for)
+ this.visible = visibleLines(display, cm.doc, viewport);
+ this.editorIsHidden = !display.wrapper.offsetWidth;
+ this.wrapperHeight = display.wrapper.clientHeight;
+ this.wrapperWidth = display.wrapper.clientWidth;
+ this.oldDisplayWidth = displayWidth(cm);
+ this.force = force;
+ this.dims = getDimensions(cm);
+ }
- var barMeasure = measureForScrollbars(cm);
- updateSelection(cm);
- setDocumentHeight(cm, barMeasure);
- updateScrollbars(cm, barMeasure);
- if (first && cm.options.lineWrapping && oldWidth != cm.display.scroller.clientWidth) {
- forced = true;
- continue;
- }
- forced = false;
-
- // Clip forced viewport to actual scrollable area.
- if (viewPort && viewPort.top != null)
- viewPort = {top: Math.min(barMeasure.docHeight - scrollerCutOff - barMeasure.clientHeight, viewPort.top)};
- // Updated line heights might result in the drawn area not
- // actually covering the viewport. Keep looping until it does.
- visible = visibleLines(cm.display, cm.doc, viewPort);
- if (visible.from >= cm.display.viewFrom && visible.to <= cm.display.viewTo)
- break;
+ function maybeClipScrollbars(cm) {
+ var display = cm.display;
+ if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
+ display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
+ display.heightForcer.style.height = scrollGap(cm) + "px";
+ display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
+ display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
+ display.scrollbarsClipped = true;
}
-
- cm.display.updateLineNumbers = null;
- if (updated) {
- signalLater(cm, "update", cm);
- if (cm.display.viewFrom != oldFrom || cm.display.viewTo != oldTo)
- signalLater(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
- }
- return updated;
}
// Does the actual updating of the line display. Bails out
// (returning false) when there is nothing to be done and forced is
// false.
- function updateDisplayInner(cm, visible, forced) {
+ function updateDisplayIfNeeded(cm, update) {
var display = cm.display, doc = cm.doc;
- if (!display.wrapper.offsetWidth) {
+
+ if (update.editorIsHidden) {
resetView(cm);
- return;
+ return false;
}
// Bail out if the visible area is already rendered and nothing changed.
- if (!forced && visible.from >= display.viewFrom && visible.to <= display.viewTo &&
- countDirtyView(cm) == 0)
- return;
+ if (!update.force &&
+ update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
+ (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
+ display.renderedView == display.view && countDirtyView(cm) == 0)
+ return false;
- if (maybeUpdateLineNumberWidth(cm))
+ if (maybeUpdateLineNumberWidth(cm)) {
resetView(cm);
- var dims = getDimensions(cm);
+ update.dims = getDimensions(cm);
+ }
// Compute a suitable new viewport (from & to)
var end = doc.first + doc.size;
- var from = Math.max(visible.from - cm.options.viewportMargin, doc.first);
- var to = Math.min(end, visible.to + cm.options.viewportMargin);
+ var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
+ var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max(doc.first, display.viewFrom);
if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, display.viewTo);
if (sawCollapsedSpans) {
@@ -602,7 +697,7 @@
}
var different = from != display.viewFrom || to != display.viewTo ||
- display.lastSizeC != display.wrapper.clientHeight;
+ display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
adjustView(cm, from, to);
display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
@@ -610,47 +705,86 @@
cm.display.mover.style.top = display.viewOffset + "px";
var toUpdate = countDirtyView(cm);
- if (!different && toUpdate == 0 && !forced) return;
+ if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
+ (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
+ return false;
// For big changes, we hide the enclosing element during the
// update, since that speeds up the operations on most browsers.
var focused = activeElt();
if (toUpdate > 4) display.lineDiv.style.display = "none";
- patchDisplay(cm, display.updateLineNumbers, dims);
+ patchDisplay(cm, display.updateLineNumbers, update.dims);
if (toUpdate > 4) display.lineDiv.style.display = "";
+ display.renderedView = display.view;
// There might have been a widget with a focused element that got
// hidden or updated, if so re-focus it.
if (focused && activeElt() != focused && focused.offsetHeight) focused.focus();
// Prevent selection and cursors from interfering with the scroll
- // width.
+ // width and height.
removeChildren(display.cursorDiv);
removeChildren(display.selectionDiv);
+ display.gutters.style.height = 0;
if (different) {
- display.lastSizeC = display.wrapper.clientHeight;
+ display.lastWrapHeight = update.wrapperHeight;
+ display.lastWrapWidth = update.wrapperWidth;
startWorker(cm, 400);
}
- updateHeightsInViewport(cm);
+ display.updateLineNumbers = null;
return true;
}
- function adjustContentWidth(cm) {
- var display = cm.display;
- var width = measureChar(cm, display.maxLine, display.maxLine.text.length).left;
- display.maxLineChanged = false;
- var minWidth = Math.max(0, width + 3);
- var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + minWidth + scrollerCutOff - display.scroller.clientWidth);
- display.sizer.style.minWidth = minWidth + "px";
- if (maxScrollLeft < cm.doc.scrollLeft)
- setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true);
+ function postUpdateDisplay(cm, update) {
+ var force = update.force, viewport = update.viewport;
+ for (var first = true;; first = false) {
+ if (first && cm.options.lineWrapping && update.oldDisplayWidth != displayWidth(cm)) {
+ force = true;
+ } else {
+ force = false;
+ // Clip forced viewport to actual scrollable area.
+ if (viewport && viewport.top != null)
+ viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)};
+ // Updated line heights might result in the drawn area not
+ // actually covering the viewport. Keep looping until it does.
+ update.visible = visibleLines(cm.display, cm.doc, viewport);
+ if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
+ break;
+ }
+ if (!updateDisplayIfNeeded(cm, update)) break;
+ updateHeightsInViewport(cm);
+ var barMeasure = measureForScrollbars(cm);
+ updateSelection(cm);
+ setDocumentHeight(cm, barMeasure);
+ updateScrollbars(cm, barMeasure);
+ }
+
+ signalLater(cm, "update", cm);
+ if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
+ signalLater(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
+ cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
+ }
+ }
+
+ function updateDisplaySimple(cm, viewport) {
+ var update = new DisplayUpdate(cm, viewport);
+ if (updateDisplayIfNeeded(cm, update)) {
+ updateHeightsInViewport(cm);
+ postUpdateDisplay(cm, update);
+ var barMeasure = measureForScrollbars(cm);
+ updateSelection(cm);
+ setDocumentHeight(cm, barMeasure);
+ updateScrollbars(cm, barMeasure);
+ }
}
function setDocumentHeight(cm, measure) {
- cm.display.sizer.style.minHeight = cm.display.heightForcer.style.top = measure.docHeight + "px";
- cm.display.gutters.style.height = Math.max(measure.docHeight, measure.clientHeight - scrollerCutOff) + "px";
+ cm.display.sizer.style.minHeight = measure.docHeight + "px";
+ var total = measure.docHeight + cm.display.barHeight;
+ cm.display.heightForcer.style.top = total + "px";
+ cm.display.gutters.style.height = Math.max(total + scrollGap(cm), measure.clientHeight) + "px";
}
// Read the actual heights of the rendered lines, and update their
@@ -661,7 +795,7 @@
for (var i = 0; i < display.view.length; i++) {
var cur = display.view[i], height;
if (cur.hidden) continue;
- if (ie_upto7) {
+ if (ie && ie_version < 8) {
var bot = cur.node.offsetTop + cur.node.offsetHeight;
height = bot - prevBottom;
prevBottom = bot;
@@ -691,9 +825,10 @@
// view, so that we don't interleave reading and writing to the DOM.
function getDimensions(cm) {
var d = cm.display, left = {}, width = {};
+ var gutterLeft = d.gutters.clientLeft;
for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
- left[cm.options.gutters[i]] = n.offsetLeft;
- width[cm.options.gutters[i]] = n.offsetWidth;
+ left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft;
+ width[cm.options.gutters[i]] = n.clientWidth;
}
return {fixedPos: compensateForHScroll(d),
gutterTotalWidth: d.gutters.offsetWidth,
@@ -770,7 +905,7 @@
if (lineView.text.parentNode)
lineView.text.parentNode.replaceChild(lineView.node, lineView.text);
lineView.node.appendChild(lineView.text);
- if (ie_upto7) lineView.node.style.zIndex = 2;
+ if (ie && ie_version < 8) lineView.node.style.zIndex = 2;
}
return lineView.node;
}
@@ -836,9 +971,12 @@
if (cm.options.lineNumbers || markers) {
var wrap = ensureLineWrapped(lineView);
var gutterWrap = lineView.gutter =
- wrap.insertBefore(elt("div", null, "CodeMirror-gutter-wrapper", "position: absolute; left: " +
- (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"),
+ wrap.insertBefore(elt("div", null, "CodeMirror-gutter-wrapper", "left: " +
+ (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) +
+ "px; width: " + dims.gutterTotalWidth + "px"),
lineView.text);
+ if (lineView.line.gutterClass)
+ gutterWrap.className += " " + lineView.line.gutterClass;
if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
lineView.lineNumber = gutterWrap.appendChild(
elt("div", lineNumberFor(cm.options, lineN),
@@ -890,7 +1028,7 @@
var wrap = ensureLineWrapped(lineView);
for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
- if (!widget.handleMouseEvents) node.ignoreEvents = true;
+ if (!widget.handleMouseEvents) node.setAttribute("cm-ignore-events", "true");
positionLineWidget(widget, node, lineView, dims);
if (allowAbove && widget.above)
wrap.insertBefore(node, lineView.gutter || lineView.text);
@@ -1126,7 +1264,8 @@
if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
sel = filterSelectionChange(doc, sel);
- var bias = cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1;
+ var bias = options && options.bias ||
+ (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
if (!(options && options.scroll === false) && doc.cm)
@@ -1138,9 +1277,10 @@
doc.sel = sel;
- if (doc.cm)
- doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged =
- doc.cm.curOp.cursorActivity = true;
+ if (doc.cm) {
+ doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true;
+ signalCursorActivity(doc.cm);
+ }
signalLater(doc, "cursorActivity", doc);
}
@@ -1220,39 +1360,49 @@
// SELECTION DRAWING
// Redraw the selection and/or cursor
- function updateSelection(cm) {
- var display = cm.display, doc = cm.doc;
- var curFragment = document.createDocumentFragment();
- var selFragment = document.createDocumentFragment();
+ function drawSelection(cm) {
+ var display = cm.display, doc = cm.doc, result = {};
+ var curFragment = result.cursors = document.createDocumentFragment();
+ var selFragment = result.selection = document.createDocumentFragment();
for (var i = 0; i < doc.sel.ranges.length; i++) {
var range = doc.sel.ranges[i];
var collapsed = range.empty();
if (collapsed || cm.options.showCursorWhenSelecting)
- updateSelectionCursor(cm, range, curFragment);
+ drawSelectionCursor(cm, range, curFragment);
if (!collapsed)
- updateSelectionRange(cm, range, selFragment);
+ drawSelectionRange(cm, range, selFragment);
}
// Move the hidden textarea near the cursor to prevent scrolling artifacts
if (cm.options.moveInputWithCursor) {
var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
- var top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
- headPos.top + lineOff.top - wrapOff.top));
- var left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
- headPos.left + lineOff.left - wrapOff.left));
- display.inputDiv.style.top = top + "px";
- display.inputDiv.style.left = left + "px";
+ result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
+ headPos.top + lineOff.top - wrapOff.top));
+ result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
+ headPos.left + lineOff.left - wrapOff.left));
}
- removeChildrenAndAdd(display.cursorDiv, curFragment);
- removeChildrenAndAdd(display.selectionDiv, selFragment);
+ return result;
+ }
+
+ function showSelection(cm, drawn) {
+ removeChildrenAndAdd(cm.display.cursorDiv, drawn.cursors);
+ removeChildrenAndAdd(cm.display.selectionDiv, drawn.selection);
+ if (drawn.teTop != null) {
+ cm.display.inputDiv.style.top = drawn.teTop + "px";
+ cm.display.inputDiv.style.left = drawn.teLeft + "px";
+ }
+ }
+
+ function updateSelection(cm) {
+ showSelection(cm, drawSelection(cm));
}
// Draws a cursor for the given range
- function updateSelectionCursor(cm, range, output) {
- var pos = cursorCoords(cm, range.head, "div");
+ function drawSelectionCursor(cm, range, output) {
+ var pos = cursorCoords(cm, range.head, "div", null, null, !cm.options.singleCursorHeightPerLine);
var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
cursor.style.left = pos.left + "px";
@@ -1270,13 +1420,16 @@
}
// Draws the given range as a highlighted selection
- function updateSelectionRange(cm, range, output) {
+ function drawSelectionRange(cm, range, output) {
var display = cm.display, doc = cm.doc;
var fragment = document.createDocumentFragment();
- var padding = paddingH(cm.display), leftSide = padding.left, rightSide = display.lineSpace.offsetWidth - padding.right;
+ var padding = paddingH(cm.display), leftSide = padding.left;
+ var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
function add(left, top, width, bottom) {
if (top < 0) top = 0;
+ top = Math.round(top);
+ bottom = Math.round(bottom);
fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
"px; top: " + top + "px; width: " + (width == null ? rightSide - left : width) +
"px; height: " + (bottom - top) + "px"));
@@ -1352,6 +1505,8 @@
display.blinker = setInterval(function() {
display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden";
}, cm.options.cursorBlinkRate);
+ else if (cm.options.cursorBlinkRate < 0)
+ display.cursorDiv.style.visibility = "hidden";
}
// HIGHLIGHT WORKER
@@ -1367,15 +1522,20 @@
if (doc.frontier >= cm.display.viewTo) return;
var end = +new Date + cm.options.workTime;
var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
+ var changedLines = [];
- runInOp(cm, function() {
doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(line) {
if (doc.frontier >= cm.display.viewFrom) { // Visible
var oldStyles = line.styles;
- line.styles = highlightLine(cm, line, state, true);
- var ischange = !oldStyles || oldStyles.length != line.styles.length;
+ var highlighted = highlightLine(cm, line, state, true);
+ line.styles = highlighted.styles;
+ var oldCls = line.styleClasses, newCls = highlighted.classes;
+ if (newCls) line.styleClasses = newCls;
+ else if (oldCls) line.styleClasses = null;
+ var ischange = !oldStyles || oldStyles.length != line.styles.length ||
+ oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
- if (ischange) regLineChange(cm, doc.frontier, "text");
+ if (ischange) changedLines.push(doc.frontier);
line.stateAfter = copyState(doc.mode, state);
} else {
processLine(cm, line.text, state);
@@ -1387,6 +1547,9 @@
return true;
}
});
+ if (changedLines.length) runInOp(cm, function() {
+ for (var i = 0; i < changedLines.length; i++)
+ regLineChange(cm, changedLines[i], "text");
});
}
@@ -1435,8 +1598,17 @@
if (display.cachedPaddingH) return display.cachedPaddingH;
var e = removeChildrenAndAdd(display.measure, elt("pre", "x"));
var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
- return display.cachedPaddingH = {left: parseInt(style.paddingLeft),
- right: parseInt(style.paddingRight)};
+ var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
+ if (!isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH = data;
+ return data;
+ }
+
+ function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth; }
+ function displayWidth(cm) {
+ return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth;
+ }
+ function displayHeight(cm) {
+ return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight;
}
// Ensure the lineView.wrapping.heights array is populated. This is
@@ -1445,7 +1617,7 @@
// height.
function ensureLineHeights(cm, lineView, rect) {
var wrapping = cm.options.lineWrapping;
- var curWidth = wrapping && cm.display.scroller.clientWidth;
+ var curWidth = wrapping && displayWidth(cm);
if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
var heights = lineView.measure.heights = [];
if (wrapping) {
@@ -1528,7 +1700,7 @@
// Given a prepared measurement object, measures the position of an
// actual character (or fetches it from the cache).
- function measureCharPrepared(cm, prepared, ch, bias) {
+ function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
if (prepared.before) ch = -1;
var key = ch + (bias || ""), found;
if (prepared.cache.hasOwnProperty(key)) {
@@ -1543,7 +1715,9 @@
found = measureCharInner(cm, prepared, ch, bias);
if (!found.bogus) prepared.cache[key] = found;
}
- return {left: found.left, right: found.right, top: found.top, bottom: found.bottom};
+ return {left: found.left, right: found.right,
+ top: varHeight ? found.rtop : found.top,
+ bottom: varHeight ? found.rbottom : found.bottom};
}
var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
@@ -1587,19 +1761,26 @@
var rect;
if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
- while (start && isExtendingChar(prepared.line.text.charAt(mStart + start))) --start;
- while (mStart + end < mEnd && isExtendingChar(prepared.line.text.charAt(mStart + end))) ++end;
- if (ie_upto8 && start == 0 && end == mEnd - mStart) {
- rect = node.parentNode.getBoundingClientRect();
- } else if (ie && cm.options.lineWrapping) {
- var rects = range(node, start, end).getClientRects();
- if (rects.length)
- rect = rects[bias == "right" ? rects.length - 1 : 0];
- else
- rect = nullRect;
- } else {
- rect = range(node, start, end).getBoundingClientRect();
+ for (var i = 0; i < 4; i++) { // Retry a maximum of 4 times when nonsense rectangles are returned
+ while (start && isExtendingChar(prepared.line.text.charAt(mStart + start))) --start;
+ while (mStart + end < mEnd && isExtendingChar(prepared.line.text.charAt(mStart + end))) ++end;
+ if (ie && ie_version < 9 && start == 0 && end == mEnd - mStart) {
+ rect = node.parentNode.getBoundingClientRect();
+ } else if (ie && cm.options.lineWrapping) {
+ var rects = range(node, start, end).getClientRects();
+ if (rects.length)
+ rect = rects[bias == "right" ? rects.length - 1 : 0];
+ else
+ rect = nullRect;
+ } else {
+ rect = range(node, start, end).getBoundingClientRect() || nullRect;
+ }
+ if (rect.left || rect.right || start == 0) break;
+ end = start;
+ start = start - 1;
+ collapse = "right";
}
+ if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measure, rect);
} else { // If it is a widget, simply get the box for the whole widget.
if (start > 0) collapse = bias = "right";
var rects;
@@ -1608,7 +1789,7 @@
else
rect = node.getBoundingClientRect();
}
- if (ie_upto8 && !start && (!rect || !rect.left && !rect.right)) {
+ if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
var rSpan = node.parentNode.getClientRects()[0];
if (rSpan)
rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom};
@@ -1616,18 +1797,33 @@
rect = nullRect;
}
- var top, bot = (rect.bottom + rect.top) / 2 - prepared.rect.top;
+ var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
+ var mid = (rtop + rbot) / 2;
var heights = prepared.view.measure.heights;
for (var i = 0; i < heights.length - 1; i++)
- if (bot < heights[i]) break;
- top = i ? heights[i - 1] : 0; bot = heights[i];
+ if (mid < heights[i]) break;
+ var top = i ? heights[i - 1] : 0, bot = heights[i];
var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
top: top, bottom: bot};
if (!rect.left && !rect.right) result.bogus = true;
+ if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
+
return result;
}
+ // Work around problem with bounding client rects on ranges being
+ // returned incorrectly when zoomed on IE10 and below.
+ function maybeUpdateRectForZooming(measure, rect) {
+ if (!window.screen || screen.logicalXDPI == null ||
+ screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
+ return rect;
+ var scaleX = screen.logicalXDPI / screen.deviceXDPI;
+ var scaleY = screen.logicalYDPI / screen.deviceYDPI;
+ return {left: rect.left * scaleX, right: rect.right * scaleX,
+ top: rect.top * scaleY, bottom: rect.bottom * scaleY};
+ }
+
function clearLineMeasurementCacheFor(lineView) {
if (lineView.measure) {
lineView.measure.cache = {};
@@ -1656,7 +1852,8 @@
// Converts a {top, bottom, left, right} box from line-local
// coordinates into another coordinate system. Context may be one of
- // "line", "div" (display.lineDiv), "local"/null (editor), or "page".
+ // "line", "div" (display.lineDiv), "local"/null (editor), "window",
+ // or "page".
function intoCoordSystem(cm, lineObj, rect, context) {
if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
var size = widgetHeight(lineObj.widgets[i]);
@@ -1704,11 +1901,11 @@
// Returns a box for a given cursor position, which may have an
// 'other' property containing the position of the secondary cursor
// on a bidi boundary.
- function cursorCoords(cm, pos, context, lineObj, preparedMeasure) {
+ function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
lineObj = lineObj || getLine(cm.doc, pos.line);
if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj);
function get(ch, right) {
- var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left");
+ var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
if (right) m.left = m.right; else m.right = m.left;
return intoCoordSystem(cm, lineObj, m, context);
}
@@ -1860,66 +2057,171 @@
// error-prone). Instead, display updates are batched and then all
// combined and executed at once.
+ var operationGroup = null;
+
var nextOpId = 0;
// Start a new operation.
function startOperation(cm) {
cm.curOp = {
+ cm: cm,
viewChanged: false, // Flag that indicates that lines might need to be redrawn
startHeight: cm.doc.height, // Used to detect need to update scrollbar
forceUpdate: false, // Used to force a redraw
updateInput: null, // Whether to reset the input textarea
typing: false, // Whether this reset should be careful to leave existing text (for compositing)
changeObjs: null, // Accumulated changes, for firing change events
- cursorActivity: false, // Whether to fire a cursorActivity event
+ cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
+ cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
selectionChanged: false, // Whether the selection needs to be redrawn
updateMaxLine: false, // Set when the widest line needs to be determined anew
scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
scrollToPos: null, // Used to scroll to a specific position
id: ++nextOpId // Unique ID
};
- if (!delayedCallbackDepth++) delayedCallbacks = [];
+ if (operationGroup) {
+ operationGroup.ops.push(cm.curOp);
+ } else {
+ cm.curOp.ownsGroup = operationGroup = {
+ ops: [cm.curOp],
+ delayedCallbacks: []
+ };
+ }
+ }
+
+ function fireCallbacksForOps(group) {
+ // Calls delayed callbacks and cursorActivity handlers until no
+ // new ones appear
+ var callbacks = group.delayedCallbacks, i = 0;
+ do {
+ for (; i < callbacks.length; i++)
+ callbacks[i]();
+ for (var j = 0; j < group.ops.length; j++) {
+ var op = group.ops[j];
+ if (op.cursorActivityHandlers)
+ while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
+ op.cursorActivityHandlers[op.cursorActivityCalled++](op.cm);
+ }
+ } while (i < callbacks.length);
}
// Finish an operation, updating the display and signalling delayed events
function endOperation(cm) {
- var op = cm.curOp, doc = cm.doc, display = cm.display;
- cm.curOp = null;
+ var op = cm.curOp, group = op.ownsGroup;
+ if (!group) return;
+ try { fireCallbacksForOps(group); }
+ finally {
+ operationGroup = null;
+ for (var i = 0; i < group.ops.length; i++)
+ group.ops[i].cm.curOp = null;
+ endOperations(group);
+ }
+ }
+
+ // The DOM updates done when an operation finishes are batched so
+ // that the minimum number of relayouts are required.
+ function endOperations(group) {
+ var ops = group.ops;
+ for (var i = 0; i < ops.length; i++) // Read DOM
+ endOperation_R1(ops[i]);
+ for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
+ endOperation_W1(ops[i]);
+ for (var i = 0; i < ops.length; i++) // Read DOM
+ endOperation_R2(ops[i]);
+ for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
+ endOperation_W2(ops[i]);
+ for (var i = 0; i < ops.length; i++) // Read DOM
+ endOperation_finish(ops[i]);
+ }
+
+ function endOperation_R1(op) {
+ var cm = op.cm, display = cm.display;
+ maybeClipScrollbars(cm);
if (op.updateMaxLine) findMaxLine(cm);
- // If it looks like an update might be needed, call updateDisplay
- if (op.viewChanged || op.forceUpdate || op.scrollTop != null ||
- op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
- op.scrollToPos.to.line >= display.viewTo) ||
- display.maxLineChanged && cm.options.lineWrapping) {
- var updated = updateDisplay(cm, {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
- if (cm.display.scroller.offsetHeight) cm.doc.scrollTop = cm.display.scroller.scrollTop;
- }
- // If no update was run, but the selection changed, redraw that.
- if (!updated && op.selectionChanged) updateSelection(cm);
- if (!updated && op.startHeight != cm.doc.height) updateScrollbars(cm);
+ op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
+ op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
+ op.scrollToPos.to.line >= display.viewTo) ||
+ display.maxLineChanged && cm.options.lineWrapping;
+ op.update = op.mustUpdate &&
+ new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
+ }
- // Propagate the scroll position to the actual DOM scroller
- if (op.scrollTop != null && display.scroller.scrollTop != op.scrollTop) {
- var top = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop));
- display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = top;
+ function endOperation_W1(op) {
+ op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
+ }
+
+ function endOperation_R2(op) {
+ var cm = op.cm, display = cm.display;
+ if (op.updatedDisplay) updateHeightsInViewport(cm);
+
+ op.barMeasure = measureForScrollbars(cm);
+
+ // If the max line changed since it was last measured, measure it,
+ // and ensure the document's width matches it.
+ // updateDisplay_W2 will use these properties to do the actual resizing
+ if (display.maxLineChanged && !cm.options.lineWrapping) {
+ op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
+ cm.display.sizerWidth = op.adjustWidthTo;
+ op.barMeasure.scrollWidth =
+ Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
+ op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
}
- if (op.scrollLeft != null && display.scroller.scrollLeft != op.scrollLeft) {
- var left = Math.max(0, Math.min(display.scroller.scrollWidth - display.scroller.clientWidth, op.scrollLeft));
- display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = left;
- alignHorizontally(cm);
- }
- // If we need to scroll a specific position into view, do so.
- if (op.scrollToPos) {
- var coords = scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos.from),
- clipPos(cm.doc, op.scrollToPos.to), op.scrollToPos.margin);
- if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coords);
+
+ if (op.updatedDisplay || op.selectionChanged)
+ op.newSelectionNodes = drawSelection(cm);
+ }
+
+ function endOperation_W2(op) {
+ var cm = op.cm;
+
+ if (op.adjustWidthTo != null) {
+ cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
+ if (op.maxScrollLeft < cm.doc.scrollLeft)
+ setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true);
+ cm.display.maxLineChanged = false;
}
+ if (op.newSelectionNodes)
+ showSelection(cm, op.newSelectionNodes);
+ if (op.updatedDisplay)
+ setDocumentHeight(cm, op.barMeasure);
+ if (op.updatedDisplay || op.startHeight != cm.doc.height)
+ updateScrollbars(cm, op.barMeasure);
+
if (op.selectionChanged) restartBlink(cm);
if (cm.state.focused && op.updateInput)
resetInput(cm, op.typing);
+ }
+
+ function endOperation_finish(op) {
+ var cm = op.cm, display = cm.display, doc = cm.doc;
+
+ if (op.updatedDisplay) postUpdateDisplay(cm, op.update);
+
+ // Abort mouse wheel delta measurement, when scrolling explicitly
+ if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
+ display.wheelStartX = display.wheelStartY = null;
+
+ // Propagate the scroll position to the actual DOM scroller
+ if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) {
+ doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop));
+ display.scrollbars.setScrollTop(doc.scrollTop);
+ display.scroller.scrollTop = doc.scrollTop;
+ }
+ if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) {
+ doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - displayWidth(cm), op.scrollLeft));
+ display.scrollbars.setScrollLeft(doc.scrollLeft);
+ display.scroller.scrollLeft = doc.scrollLeft;
+ alignHorizontally(cm);
+ }
+ // If we need to scroll a specific position into view, do so.
+ if (op.scrollToPos) {
+ var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
+ clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
+ if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coords);
+ }
// Fire events for markers that are hidden/unidden by editing or
// undoing
@@ -1929,19 +2231,12 @@
if (unhidden) for (var i = 0; i < unhidden.length; ++i)
if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
- var delayed;
- if (!--delayedCallbackDepth) {
- delayed = delayedCallbacks;
- delayedCallbacks = null;
- }
+ if (display.wrapper.offsetHeight)
+ doc.scrollTop = cm.display.scroller.scrollTop;
+
// Fire change events, and delayed event handlers
- if (op.changeObjs) {
- for (var i = 0; i < op.changeObjs.length; i++)
- signal(cm, "change", cm, op.changeObjs[i]);
+ if (op.changeObjs)
signal(cm, "changes", cm, op.changeObjs);
- }
- if (op.cursorActivity) signal(cm, "cursorActivity", cm);
- if (delayed) for (var i = 0; i < delayed.length; ++i) delayed[i]();
}
// Run the given function in an operation
@@ -2113,7 +2408,8 @@
function viewCuttingPoint(cm, oldN, newN, dir) {
var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
- if (!sawCollapsedSpans) return {index: index, lineN: newN};
+ if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
+ return {index: index, lineN: newN};
for (var i = 0, n = cm.display.viewFrom; i < index; i++)
n += view[i].size;
if (n != oldN) {
@@ -2192,6 +2488,11 @@
cm.display.poll.set(20, p);
}
+ // This will be set to an array of strings when copying, so that,
+ // when pasting, we know what kind of selections the copied text
+ // was made out of.
+ var lastCopied = null;
+
// Read input from the textarea, and update the document to match.
// When something is selected, it is present in the textarea, and
// selected (unless it is huge, in which case a placeholder is
@@ -2204,12 +2505,21 @@
// possible when it is clear that nothing happened. hasSelection
// will be the case when there is a lot of text in the textarea,
// in which case reading its value would be expensive.
- if (!cm.state.focused || hasSelection(input) || isReadOnly(cm) || cm.options.disableInput) return false;
+ if (!cm.state.focused || (hasSelection(input) && !prevInput) || isReadOnly(cm) || cm.options.disableInput || cm.state.keySeq)
+ return false;
+ // See paste handler for more on the fakedLastChar kludge
+ if (cm.state.pasteIncoming && cm.state.fakedLastChar) {
+ input.value = input.value.substring(0, input.value.length - 1);
+ cm.state.fakedLastChar = false;
+ }
var text = input.value;
// If nothing changed, bail.
if (text == prevInput && !cm.somethingSelected()) return false;
- // Work around nonsensical selection resetting in IE9/10
- if (ie && !ie_upto8 && cm.display.inputHasSelection === text) {
+ // Work around nonsensical selection resetting in IE9/10, and
+ // inexplicable appearance of private area unicode characters on
+ // some key combos in Mac (#2689).
+ if (ie && ie_version >= 9 && cm.display.inputHasSelection === text ||
+ mac && /[\uf700-\uf7ff]/.test(text)) {
resetInput(cm);
return false;
}
@@ -2218,13 +2528,21 @@
if (withOp) startOperation(cm);
cm.display.shift = false;
+ if (text.charCodeAt(0) == 0x200b && doc.sel == cm.display.selForContextMenu && !prevInput)
+ prevInput = "\u200b";
// Find the part of the input that is actually new
var same = 0, l = Math.min(prevInput.length, text.length);
while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
var inserted = text.slice(same), textLines = splitLines(inserted);
// When pasing N lines into N selections, insert one line per selection
- var multiPaste = cm.state.pasteIncoming && textLines.length > 1 && doc.sel.ranges.length == textLines.length;
+ var multiPaste = null;
+ if (cm.state.pasteIncoming && doc.sel.ranges.length > 1) {
+ if (lastCopied && lastCopied.join("\n") == inserted)
+ multiPaste = doc.sel.ranges.length % lastCopied.length == 0 && map(lastCopied, splitLines);
+ else if (textLines.length == doc.sel.ranges.length)
+ multiPaste = map(textLines, function(l) { return [l]; });
+ }
// Normal behavior is to insert the new text into every selection
for (var i = doc.sel.ranges.length - 1; i >= 0; i--) {
@@ -2237,7 +2555,7 @@
else if (cm.state.overwrite && range.empty() && !cm.state.pasteIncoming)
to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length));
var updateInput = cm.curOp.updateInput;
- var changeEvent = {from: from, to: to, text: multiPaste ? [textLines[i]] : textLines,
+ var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines,
origin: cm.state.pasteIncoming ? "paste" : cm.state.cutIncoming ? "cut" : "+input"};
makeChange(cm.doc, changeEvent);
signalLater(cm, "inputRead", cm, changeEvent);
@@ -2245,12 +2563,18 @@
if (inserted && !cm.state.pasteIncoming && cm.options.electricChars &&
cm.options.smartIndent && range.head.ch < 100 &&
(!i || doc.sel.ranges[i - 1].head.line != range.head.line)) {
- var electric = cm.getModeAt(range.head).electricChars;
- if (electric) for (var j = 0; j < electric.length; j++)
- if (inserted.indexOf(electric.charAt(j)) > -1) {
- indentLine(cm, range.head.line, "smart");
- break;
- }
+ var mode = cm.getModeAt(range.head);
+ var end = changeEnd(changeEvent);
+ if (mode.electricChars) {
+ for (var j = 0; j < mode.electricChars.length; j++)
+ if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
+ indentLine(cm, end.line, "smart");
+ break;
+ }
+ } else if (mode.electricInput) {
+ if (mode.electricInput.test(getLine(doc, end.line).text.slice(0, end.ch)))
+ indentLine(cm, end.line, "smart");
+ }
}
}
ensureCursorVisible(cm);
@@ -2268,6 +2592,7 @@
// Reset the input to correspond to the selection (or to be empty,
// when not typing and nothing is selected)
function resetInput(cm, typing) {
+ if (cm.display.contextMenuPending) return;
var minimal, selected, doc = cm.doc;
if (cm.somethingSelected()) {
cm.display.prevInput = "";
@@ -2277,10 +2602,10 @@
var content = minimal ? "-" : selected || cm.getSelection();
cm.display.input.value = content;
if (cm.state.focused) selectInput(cm.display.input);
- if (ie && !ie_upto8) cm.display.inputHasSelection = content;
+ if (ie && ie_version >= 9) cm.display.inputHasSelection = content;
} else if (!typing) {
cm.display.prevInput = cm.display.input.value = "";
- if (ie && !ie_upto8) cm.display.inputHasSelection = null;
+ if (ie && ie_version >= 9) cm.display.inputHasSelection = null;
}
cm.display.inaccurateSelection = minimal;
}
@@ -2305,13 +2630,13 @@
var d = cm.display;
on(d.scroller, "mousedown", operation(cm, onMouseDown));
// Older IE's will not fire a second mousedown for a double click
- if (ie_upto10)
+ if (ie && ie_version < 11)
on(d.scroller, "dblclick", operation(cm, function(e) {
if (signalDOMEvent(cm, e)) return;
var pos = posFromMouse(cm, e);
if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
e_preventDefault(e);
- var word = findWordAt(cm.doc, pos);
+ var word = cm.findWordAt(pos);
extendSelection(cm.doc, word.anchor, word.head);
}));
else
@@ -2334,48 +2659,18 @@
signal(cm, "scroll", cm);
}
});
- on(d.scrollbarV, "scroll", function() {
- if (d.scroller.clientHeight) setScrollTop(cm, d.scrollbarV.scrollTop);
- });
- on(d.scrollbarH, "scroll", function() {
- if (d.scroller.clientHeight) setScrollLeft(cm, d.scrollbarH.scrollLeft);
- });
// Listen to wheel events in order to try and update the viewport on time.
on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
- // Prevent clicks in the scrollbars from killing focus
- function reFocus() { if (cm.state.focused) setTimeout(bind(focusInput, cm), 0); }
- on(d.scrollbarH, "mousedown", reFocus);
- on(d.scrollbarV, "mousedown", reFocus);
// Prevent wrapper from ever scrolling
on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
- // When the window resizes, we need to refresh active editors.
- var resizeTimer;
- function onResize() {
- if (resizeTimer == null) resizeTimer = setTimeout(function() {
- resizeTimer = null;
- // Might be a text scaling operation, clear size caches.
- d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = knownScrollbarWidth = null;
- cm.setSize();
- }, 100);
- }
- on(window, "resize", onResize);
- // The above handler holds on to the editor and its data
- // structures. Here we poll to unregister it when the editor is no
- // longer in the document, so that it can be garbage-collected.
- function unregister() {
- if (contains(document.body, d.wrapper)) setTimeout(unregister, 5000);
- else off(window, "resize", onResize);
- }
- setTimeout(unregister, 5000);
-
- on(d.input, "keyup", operation(cm, onKeyUp));
+ on(d.input, "keyup", function(e) { onKeyUp.call(cm, e); });
on(d.input, "input", function() {
- if (ie && !ie_upto8 && cm.display.inputHasSelection) cm.display.inputHasSelection = null;
- fastPoll(cm);
+ if (ie && ie_version >= 9 && cm.display.inputHasSelection) cm.display.inputHasSelection = null;
+ readInput(cm);
});
on(d.input, "keydown", operation(cm, onKeyDown));
on(d.input, "keypress", operation(cm, onKeyPress));
@@ -2398,21 +2693,54 @@
fastPoll(cm);
});
on(d.input, "paste", function() {
+ // Workaround for webkit bug https://bugs.webkit.org/show_bug.cgi?id=90206
+ // Add a char to the end of textarea before paste occur so that
+ // selection doesn't span to the end of textarea.
+ if (webkit && !cm.state.fakedLastChar && !(new Date - cm.state.lastMiddleDown < 200)) {
+ var start = d.input.selectionStart, end = d.input.selectionEnd;
+ d.input.value += "$";
+ // The selection end needs to be set before the start, otherwise there
+ // can be an intermediate non-empty selection between the two, which
+ // can override the middle-click paste buffer on linux and cause the
+ // wrong thing to get pasted.
+ d.input.selectionEnd = end;
+ d.input.selectionStart = start;
+ cm.state.fakedLastChar = true;
+ }
cm.state.pasteIncoming = true;
fastPoll(cm);
});
- function prepareCopy(e) {
- if (d.inaccurateSelection) {
- d.prevInput = "";
- d.inaccurateSelection = false;
- d.input.value = cm.getSelection();
- selectInput(d.input);
+ function prepareCopyCut(e) {
+ if (cm.somethingSelected()) {
+ lastCopied = cm.getSelections();
+ if (d.inaccurateSelection) {
+ d.prevInput = "";
+ d.inaccurateSelection = false;
+ d.input.value = lastCopied.join("\n");
+ selectInput(d.input);
+ }
+ } else {
+ var text = [], ranges = [];
+ for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
+ var line = cm.doc.sel.ranges[i].head.line;
+ var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
+ ranges.push(lineRange);
+ text.push(cm.getRange(lineRange.anchor, lineRange.head));
+ }
+ if (e.type == "cut") {
+ cm.setSelections(ranges, null, sel_dontScroll);
+ } else {
+ d.prevInput = "";
+ d.input.value = text.join("\n");
+ selectInput(d.input);
+ }
+ lastCopied = text;
}
if (e.type == "cut") cm.state.cutIncoming = true;
}
- on(d.input, "cut", prepareCopy);
- on(d.input, "copy", prepareCopy);
+ on(d.input, "cut", prepareCopyCut);
+ on(d.input, "copy", prepareCopyCut);
// Needed to handle Tab key in KHTML
if (khtml) on(d.sizer, "mouseup", function() {
@@ -2421,12 +2749,25 @@
});
}
+ // Called when the window resizes
+ function onResize(cm) {
+ var d = cm.display;
+ if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
+ return;
+ // Might be a text scaling operation, clear size caches.
+ d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
+ d.scrollbarsClipped = false;
+ cm.setSize();
+ }
+
// MOUSE EVENTS
// Return true when the given mouse event happened in a widget
function eventInWidget(display, e) {
for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
- if (!n || n.ignoreEvents || n.parentNode == display.sizer && n != display.mover) return true;
+ if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
+ (n.parentNode == display.sizer && n != display.mover))
+ return true;
}
}
@@ -2437,11 +2778,8 @@
// coordinates beyond the right of the text.
function posFromMouse(cm, e, liberal, forRect) {
var display = cm.display;
- if (!liberal) {
- var target = e_target(e);
- if (target == display.scrollbarH || target == display.scrollbarV ||
- target == display.scrollbarFiller || target == display.gutterFiller) return null;
- }
+ if (!liberal && e_target(e).getAttribute("not-content") == "true") return null;
+
var x, y, space = display.lineSpace.getBoundingClientRect();
// Fails unpredictably on IE[67] when mouse is dragged around quickly.
try { x = e.clientX - space.left; y = e.clientY - space.top; }
@@ -2449,7 +2787,7 @@
var coords = coordsChar(cm, x, y), line;
if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
- coords = Pos(coords.line, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff);
+ coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
}
return coords;
}
@@ -2511,17 +2849,18 @@
lastClick = {time: now, pos: start};
}
- var sel = cm.doc.sel, addNew = mac ? e.metaKey : e.ctrlKey;
- if (cm.options.dragDrop && dragAndDrop && !addNew && !isReadOnly(cm) &&
- type == "single" && sel.contains(start) > -1 && sel.somethingSelected())
- leftButtonStartDrag(cm, e, start);
+ var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained;
+ if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) &&
+ type == "single" && (contained = sel.contains(start)) > -1 &&
+ !sel.ranges[contained].empty())
+ leftButtonStartDrag(cm, e, start, modifier);
else
- leftButtonSelect(cm, e, start, type, addNew);
+ leftButtonSelect(cm, e, start, type, modifier);
}
// Start a text drag. When it ends, see if any dragging actually
// happen, and treat as a click if it didn't.
- function leftButtonStartDrag(cm, e, start) {
+ function leftButtonStartDrag(cm, e, start, modifier) {
var display = cm.display;
var dragEnd = operation(cm, function(e2) {
if (webkit) display.scroller.draggable = false;
@@ -2530,10 +2869,11 @@
off(display.scroller, "drop", dragEnd);
if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
e_preventDefault(e2);
- extendSelection(cm.doc, start);
+ if (!modifier)
+ extendSelection(cm.doc, start);
focusInput(cm);
// Work around unexplainable focus problem in IE9 (#2127)
- if (ie_upto10 && !ie_upto8)
+ if (ie && ie_version == 9)
setTimeout(function() {document.body.focus(); focusInput(cm);}, 20);
}
});
@@ -2551,11 +2891,11 @@
var display = cm.display, doc = cm.doc;
e_preventDefault(e);
- var ourRange, ourIndex, startSel = doc.sel;
- if (addNew) {
+ var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
+ if (addNew && !e.shiftKey) {
ourIndex = doc.sel.contains(start);
if (ourIndex > -1)
- ourRange = doc.sel.ranges[ourIndex];
+ ourRange = ranges[ourIndex];
else
ourRange = new Range(start, start);
} else {
@@ -2568,7 +2908,7 @@
start = posFromMouse(cm, e, true, true);
ourIndex = -1;
} else if (type == "double") {
- var word = findWordAt(doc, start);
+ var word = cm.findWordAt(start);
if (cm.display.shift || doc.extend)
ourRange = extendRange(doc, ourRange, word.anchor, word.head);
else
@@ -2586,12 +2926,16 @@
if (!addNew) {
ourIndex = 0;
setSelection(doc, new Selection([ourRange], 0), sel_mouse);
- } else if (ourIndex > -1) {
- replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
- } else {
- ourIndex = doc.sel.ranges.length;
- setSelection(doc, normalizeSelection(doc.sel.ranges.concat([ourRange]), ourIndex),
+ startSel = doc.sel;
+ } else if (ourIndex == -1) {
+ ourIndex = ranges.length;
+ setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
{scroll: false, origin: "*mouse"});
+ } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single") {
+ setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0));
+ startSel = doc.sel;
+ } else {
+ replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
}
var lastPos = start;
@@ -2613,13 +2957,15 @@
ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize))));
}
if (!ranges.length) ranges.push(new Range(start, start));
- setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), sel_mouse);
+ setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
+ {origin: "*mouse", scroll: false});
+ cm.scrollIntoView(pos);
} else {
var oldRange = ourRange;
var anchor = oldRange.anchor, head = pos;
if (type != "single") {
if (type == "double")
- var range = findWordAt(doc, pos);
+ var range = cm.findWordAt(pos);
else
var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0)));
if (cmp(range.anchor, anchor) > 0) {
@@ -2673,7 +3019,7 @@
}
var move = operation(cm, function(e) {
- if ((ie && !ie_upto9) ? !e.buttons : !e_button(e)) done(e);
+ if (!e_button(e)) done(e);
else extend(e);
});
var up = operation(cm, done);
@@ -2719,7 +3065,7 @@
if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
return;
e_preventDefault(e);
- if (ie_upto10) lastDrop = +new Date;
+ if (ie) lastDrop = +new Date;
var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
if (!pos || isReadOnly(cm)) return;
// Might be a file drop, in which case we simply extract the text
@@ -2728,7 +3074,7 @@
var n = files.length, text = Array(n), read = 0;
var loadFile = function(file, i) {
var reader = new FileReader;
- reader.onload = function() {
+ reader.onload = operation(cm, function() {
text[i] = reader.result;
if (++read == n) {
pos = clipPos(cm.doc, pos);
@@ -2736,7 +3082,7 @@
makeChange(cm.doc, change);
setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
}
- };
+ });
reader.readAsText(file);
};
for (var i = 0; i < n; ++i) loadFile(files[i], i);
@@ -2751,7 +3097,8 @@
try {
var text = e.dataTransfer.getData("Text");
if (text) {
- var selected = cm.state.draggingText && cm.listSelections();
+ if (cm.state.draggingText && !(mac ? e.metaKey : e.ctrlKey))
+ var selected = cm.listSelections();
setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
if (selected) for (var i = 0; i < selected.length; ++i)
replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag");
@@ -2764,7 +3111,7 @@
}
function onDragStart(cm, e) {
- if (ie_upto10 && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; }
+ if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; }
if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
e.dataTransfer.setData("Text", cm.getSelection());
@@ -2792,10 +3139,10 @@
function setScrollTop(cm, val) {
if (Math.abs(cm.doc.scrollTop - val) < 2) return;
cm.doc.scrollTop = val;
- if (!gecko) updateDisplay(cm, {top: val});
+ if (!gecko) updateDisplaySimple(cm, {top: val});
if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
- if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
- if (gecko) updateDisplay(cm);
+ cm.display.scrollbars.setScrollTop(val);
+ if (gecko) updateDisplaySimple(cm);
startWorker(cm, 100);
}
// Sync scroller and scrollbar, ensure the gutter elements are
@@ -2806,7 +3153,7 @@
cm.doc.scrollLeft = val;
alignHorizontally(cm);
if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
- if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val;
+ cm.display.scrollbars.setScrollLeft(val);
}
// Since the delta values reported on mouse wheel events are
@@ -2830,11 +3177,22 @@
else if (chrome) wheelPixelsPerUnit = -.7;
else if (safari) wheelPixelsPerUnit = -1/3;
- function onScrollWheel(cm, e) {
+ var wheelEventDelta = function(e) {
var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
else if (dy == null) dy = e.wheelDelta;
+ return {x: dx, y: dy};
+ };
+ CodeMirror.wheelEventPixels = function(e) {
+ var delta = wheelEventDelta(e);
+ delta.x *= wheelPixelsPerUnit;
+ delta.y *= wheelPixelsPerUnit;
+ return delta;
+ };
+
+ function onScrollWheel(cm, e) {
+ var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
var display = cm.display, scroll = display.scroller;
// Quit if there's nothing to scroll here
@@ -2878,7 +3236,7 @@
var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
if (pixels < 0) top = Math.max(0, top + pixels - 50);
else bot = Math.min(cm.doc.height, bot + pixels + 50);
- updateDisplay(cm, {top: top, bottom: bot});
+ updateDisplaySimple(cm, {top: top, bottom: bot});
}
if (wheelSamples < 20) {
@@ -2925,62 +3283,70 @@
return done;
}
- // Collect the currently active keymaps.
- function allKeyMaps(cm) {
- var maps = cm.state.keyMaps.slice(0);
- if (cm.options.extraKeys) maps.push(cm.options.extraKeys);
- maps.push(cm.options.keyMap);
- return maps;
+ function lookupKeyForEditor(cm, name, handle) {
+ for (var i = 0; i < cm.state.keyMaps.length; i++) {
+ var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
+ if (result) return result;
+ }
+ return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
+ || lookupKey(name, cm.options.keyMap, handle, cm);
+ }
+
+ var stopSeq = new Delayed;
+ function dispatchKey(cm, name, e, handle) {
+ var seq = cm.state.keySeq;
+ if (seq) {
+ if (isModifierKey(name)) return "handled";
+ stopSeq.set(50, function() {
+ if (cm.state.keySeq == seq) {
+ cm.state.keySeq = null;
+ resetInput(cm);
+ }
+ });
+ name = seq + " " + name;
+ }
+ var result = lookupKeyForEditor(cm, name, handle);
+
+ if (result == "multi")
+ cm.state.keySeq = name;
+ if (result == "handled")
+ signalLater(cm, "keyHandled", cm, name, e);
+
+ if (result == "handled" || result == "multi") {
+ e_preventDefault(e);
+ restartBlink(cm);
+ }
+
+ if (seq && !result && /\'$/.test(name)) {
+ e_preventDefault(e);
+ return true;
+ }
+ return !!result;
}
- var maybeTransition;
// Handle a key from the keydown event.
function handleKeyBinding(cm, e) {
- // Handle automatic keymap transitions
- var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
- clearTimeout(maybeTransition);
- if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
- if (getKeyMap(cm.options.keyMap) == startMap) {
- cm.options.keyMap = (next.call ? next.call(null, cm) : next);
- keyMapChanged(cm);
- }
- }, 50);
-
- var name = keyName(e, true), handled = false;
+ var name = keyName(e, true);
if (!name) return false;
- var keymaps = allKeyMaps(cm);
- if (e.shiftKey) {
+ if (e.shiftKey && !cm.state.keySeq) {
// First try to resolve full name (including 'Shift-'). Failing
// that, see if there is a cursor-motion command (starting with
// 'go') bound to the keyname without 'Shift-'.
- handled = lookupKey("Shift-" + name, keymaps, function(b) {return doHandleBinding(cm, b, true);})
- || lookupKey(name, keymaps, function(b) {
- if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
- return doHandleBinding(cm, b);
- });
+ return dispatchKey(cm, "Shift-" + name, e, function(b) {return doHandleBinding(cm, b, true);})
+ || dispatchKey(cm, name, e, function(b) {
+ if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
+ return doHandleBinding(cm, b);
+ });
} else {
- handled = lookupKey(name, keymaps, function(b) { return doHandleBinding(cm, b); });
+ return dispatchKey(cm, name, e, function(b) { return doHandleBinding(cm, b); });
}
-
- if (handled) {
- e_preventDefault(e);
- restartBlink(cm);
- signalLater(cm, "keyHandled", cm, name, e);
- }
- return handled;
}
// Handle a key from the keypress event
function handleCharBinding(cm, e, ch) {
- var handled = lookupKey("'" + ch + "'", allKeyMaps(cm),
- function(b) { return doHandleBinding(cm, b, true); });
- if (handled) {
- e_preventDefault(e);
- restartBlink(cm);
- signalLater(cm, "keyHandled", cm, "'" + ch + "'", e);
- }
- return handled;
+ return dispatchKey(cm, "'" + ch + "'", e,
+ function(b) { return doHandleBinding(cm, b, true); });
}
var lastStoppedKey = null;
@@ -2989,7 +3355,7 @@
ensureFocus(cm);
if (signalDOMEvent(cm, e)) return;
// IE does strange things with escape.
- if (ie_upto10 && e.keyCode == 27) e.returnValue = false;
+ if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false;
var code = e.keyCode;
cm.display.shift = code == 16 || e.shiftKey;
var handled = handleKeyBinding(cm, e);
@@ -2999,22 +3365,41 @@
if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
cm.replaceSelection("", null, "cut");
}
+
+ // Turn mouse into crosshair when Alt is held on Mac.
+ if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
+ showCrossHair(cm);
+ }
+
+ function showCrossHair(cm) {
+ var lineDiv = cm.display.lineDiv;
+ addClass(lineDiv, "CodeMirror-crosshair");
+
+ function up(e) {
+ if (e.keyCode == 18 || !e.altKey) {
+ rmClass(lineDiv, "CodeMirror-crosshair");
+ off(document, "keyup", up);
+ off(document, "mouseover", up);
+ }
+ }
+ on(document, "keyup", up);
+ on(document, "mouseover", up);
}
function onKeyUp(e) {
- if (signalDOMEvent(this, e)) return;
if (e.keyCode == 16) this.doc.sel.shift = false;
+ signalDOMEvent(this, e);
}
function onKeyPress(e) {
var cm = this;
- if (signalDOMEvent(cm, e)) return;
+ if (signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) return;
var keyCode = e.keyCode, charCode = e.charCode;
if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
if (((presto && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
if (handleCharBinding(cm, e, ch)) return;
- if (ie && !ie_upto8) cm.display.inputHasSelection = null;
+ if (ie && ie_version >= 9) cm.display.inputHasSelection = null;
fastPoll(cm);
}
@@ -3025,9 +3410,11 @@
if (!cm.state.focused) {
signal(cm, "focus", cm);
cm.state.focused = true;
- if (cm.display.wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
- cm.display.wrapper.className += " CodeMirror-focused";
- if (!cm.curOp) {
+ addClass(cm.display.wrapper, "CodeMirror-focused");
+ // The prevInput test prevents this from firing when a context
+ // menu is closed (since the resetInput would kill the
+ // select-all detection hack)
+ if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
resetInput(cm);
if (webkit) setTimeout(bind(resetInput, cm, true), 0); // Issue #1730
}
@@ -3039,7 +3426,7 @@
if (cm.state.focused) {
signal(cm, "blur", cm);
cm.state.focused = false;
- cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-focused", "");
+ rmClass(cm.display.wrapper, "CodeMirror-focused");
}
clearInterval(cm.display.blinker);
setTimeout(function() {if (!cm.state.focused) cm.display.shift = false;}, 150);
@@ -3047,7 +3434,6 @@
// CONTEXT MENU HANDLING
- var detectingSelectAll;
// To make the context menu work, we need to briefly unhide the
// textarea (making it as unobtrusive as possible) to let the
// right-click take effect on it.
@@ -3071,42 +3457,51 @@
"px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: " +
(ie ? "rgba(255, 255, 255, .05)" : "transparent") +
"; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
+ if (webkit) var oldScrollY = window.scrollY; // Work around Chrome issue (#2712)
focusInput(cm);
+ if (webkit) window.scrollTo(null, oldScrollY);
resetInput(cm);
// Adds "Select all" to context menu in FF
if (!cm.somethingSelected()) display.input.value = display.prevInput = " ";
+ display.contextMenuPending = true;
+ display.selForContextMenu = cm.doc.sel;
+ clearTimeout(display.detectingSelectAll);
// Select-all will be greyed out if there's nothing to select, so
// this adds a zero-width space so that we can later check whether
// it got selected.
function prepareSelectAllHack() {
if (display.input.selectionStart != null) {
- var extval = display.input.value = "\u200b" + (cm.somethingSelected() ? display.input.value : "");
- display.prevInput = "\u200b";
+ var selected = cm.somethingSelected();
+ var extval = display.input.value = "\u200b" + (selected ? display.input.value : "");
+ display.prevInput = selected ? "" : "\u200b";
display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
+ // Re-set this, in case some other handler touched the
+ // selection in the meantime.
+ display.selForContextMenu = cm.doc.sel;
}
}
function rehide() {
+ display.contextMenuPending = false;
display.inputDiv.style.position = "relative";
display.input.style.cssText = oldCSS;
- if (ie_upto8) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
+ if (ie && ie_version < 9) display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos);
slowPoll(cm);
// Try to detect the user choosing select-all
if (display.input.selectionStart != null) {
- if (!ie || ie_upto8) prepareSelectAllHack();
- clearTimeout(detectingSelectAll);
- var i = 0, poll = function(){
- if (display.prevInput == "\u200b" && display.input.selectionStart == 0)
+ if (!ie || (ie && ie_version < 9)) prepareSelectAllHack();
+ var i = 0, poll = function() {
+ if (display.selForContextMenu == cm.doc.sel && display.input.selectionStart == 0)
operation(cm, commands.selectAll)(cm);
- else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
+ else if (i++ < 10) display.detectingSelectAll = setTimeout(poll, 500);
else resetInput(cm);
};
- detectingSelectAll = setTimeout(poll, 200);
+ display.detectingSelectAll = setTimeout(poll, 200);
}
}
- if (ie && !ie_upto8) prepareSelectAllHack();
+ if (ie && ie_version >= 9) prepareSelectAllHack();
if (captureRightClick) {
e_stop(e);
var mouseup = function() {
@@ -3296,9 +3691,9 @@
antiChanges.push(historyChangeFromChange(doc, change));
- var after = i ? computeSelAfterChange(doc, change, null) : lst(source);
+ var after = i ? computeSelAfterChange(doc, change) : lst(source);
makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
- if (doc.cm) ensureCursorVisible(doc.cm);
+ if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)});
var rebased = [];
// Propagate to the linked documents
@@ -3315,12 +3710,17 @@
// Sub-views need their line numbers shifted when text is added
// above or below them in the parent document.
function shiftDoc(doc, distance) {
+ if (distance == 0) return;
doc.first += distance;
doc.sel = new Selection(map(doc.sel.ranges, function(range) {
return new Range(Pos(range.anchor.line + distance, range.anchor.ch),
Pos(range.head.line + distance, range.head.ch));
}), doc.sel.primIndex);
- if (doc.cm) regChange(doc.cm, doc.first, doc.first - distance, distance);
+ if (doc.cm) {
+ regChange(doc.cm, doc.first, doc.first - distance, distance);
+ for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
+ regLineChange(doc.cm, l, "gutter");
+ }
}
// More lower-level change function, handling only a single document
@@ -3350,7 +3750,7 @@
change.removed = getBetween(doc, change.from, change.to);
- if (!selAfter) selAfter = computeSelAfterChange(doc, change, null);
+ if (!selAfter) selAfter = computeSelAfterChange(doc, change);
if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans);
else updateDoc(doc, change, spans);
setSelectionNoUndo(doc, selAfter, sel_dontScroll);
@@ -3373,7 +3773,7 @@
}
if (doc.sel.contains(change.from, change.to) > -1)
- cm.curOp.cursorActivity = true;
+ signalCursorActivity(cm);
updateDoc(doc, change, spans, estimateHeight(cm));
@@ -3396,18 +3796,25 @@
var lendiff = change.text.length - (to.line - from.line) - 1;
// Remember that these lines changed, for updating the display
- if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
+ if (change.full)
+ regChange(cm);
+ else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
regLineChange(cm, from.line, "text");
else
regChange(cm, from.line, to.line + 1, lendiff);
- if (hasHandler(cm, "change") || hasHandler(cm, "changes"))
- (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push({
+ var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
+ if (changeHandler || changesHandler) {
+ var obj = {
from: from, to: to,
text: change.text,
removed: change.removed,
origin: change.origin
- });
+ };
+ if (changeHandler) signalLater(cm, "change", cm, obj);
+ if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj);
+ }
+ cm.display.selForContextMenu = null;
}
function replaceRange(doc, code, from, to, origin) {
@@ -3422,13 +3829,15 @@
// If an editor sits on the top or bottom of the window, partially
// scrolled out of view, this ensures that the cursor is visible.
function maybeScrollWindow(cm, coords) {
+ if (signalDOMEvent(cm, "scrollCursorIntoView")) return;
+
var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
if (coords.top + box.top < 0) doScroll = true;
else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
if (doScroll != null && !phantom) {
var scrollNode = elt("div", "\u200b", null, "position: absolute; top: " +
(coords.top - display.viewOffset - paddingTop(cm.display)) + "px; height: " +
- (coords.bottom - coords.top + scrollerCutOff) + "px; left: " +
+ (coords.bottom - coords.top + scrollGap(cm) + display.barHeight) + "px; left: " +
coords.left + "px; width: 2px;");
cm.display.lineSpace.appendChild(scrollNode);
scrollNode.scrollIntoView(doScroll);
@@ -3441,7 +3850,7 @@
// measured, the position of something may 'drift' during drawing).
function scrollPosIntoView(cm, pos, end, margin) {
if (margin == null) margin = 0;
- for (;;) {
+ for (var limit = 0; limit < 5; limit++) {
var changed = false, coords = cursorCoords(cm, pos);
var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left),
@@ -3457,8 +3866,9 @@
setScrollLeft(cm, scrollPos.scrollLeft);
if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true;
}
- if (!changed) return coords;
+ if (!changed) break;
}
+ return coords;
}
// Scroll a given set of coordinates into view (immediately).
@@ -3476,7 +3886,8 @@
var display = cm.display, snapMargin = textHeight(cm.display);
if (y1 < 0) y1 = 0;
var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
- var screen = display.scroller.clientHeight - scrollerCutOff, result = {};
+ var screen = displayHeight(cm), result = {};
+ if (y2 - y1 > screen) y2 = y1 + screen;
var docBottom = cm.doc.height + paddingVert(display);
var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
if (y1 < screentop) {
@@ -3487,16 +3898,15 @@
}
var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
- var screenw = display.scroller.clientWidth - scrollerCutOff;
- x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth;
- var gutterw = display.gutters.offsetWidth;
- var atLeft = x1 < gutterw + 10;
- if (x1 < screenleft + gutterw || atLeft) {
- if (atLeft) x1 = 0;
- result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
- } else if (x2 > screenw + screenleft - 3) {
- result.scrollLeft = x2 + 10 - screenw;
- }
+ var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
+ var tooWide = x2 - x1 > screenw;
+ if (tooWide) x2 = x1 + screenw;
+ if (x1 < 10)
+ result.scrollLeft = 0;
+ else if (x1 < screenleft)
+ result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10));
+ else if (x2 > screenw + screenleft - 3)
+ result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw;
return result;
}
@@ -3552,7 +3962,7 @@
if (how == "smart") {
// Fall back to "prev" when the mode doesn't have an indentation
// method.
- if (!cm.doc.mode.indent) how = "prev";
+ if (!doc.mode.indent) how = "prev";
else state = getStateBefore(cm, n);
}
@@ -3564,8 +3974,8 @@
indentation = 0;
how = "not";
} else if (how == "smart") {
- indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
- if (indentation == Pass) {
+ indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
+ if (indentation == Pass || indentation > 150) {
if (!aggressive) return;
how = "prev";
}
@@ -3588,7 +3998,7 @@
if (pos < indentation) indentString += spaceStr(indentation - pos);
if (indentString != curSpaceString) {
- replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
+ replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
} else {
// Ensure that, if the cursor was in the whitespace at the start
// of the line, it is moved to the end of that space.
@@ -3607,13 +4017,12 @@
// Utility for applying a change to a line by handle or number,
// returning the number and optionally registering the line as
// changed.
- function changeLine(cm, handle, changeType, op) {
- var no = handle, line = handle, doc = cm.doc;
+ function changeLine(doc, handle, changeType, op) {
+ var no = handle, line = handle;
if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
else no = lineNo(handle);
if (no == null) return null;
- if (op(line, no)) regLineChange(cm, no, changeType);
- else return null;
+ if (op(line, no) && doc.cm) regLineChange(doc.cm, no, changeType);
return line;
}
@@ -3676,10 +4085,11 @@
else if (unit == "column") moveOnce(true);
else if (unit == "word" || unit == "group") {
var sawType = null, group = unit == "group";
+ var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
for (var first = true;; first = false) {
if (dir < 0 && !moveOnce(!first)) break;
var cur = lineObj.text.charAt(ch) || "\n";
- var type = isWordChar(cur) ? "w"
+ var type = isWordChar(cur, helper) ? "w"
: group && cur == "\n" ? "n"
: !group || /\s/.test(cur) ? null
: "p";
@@ -3718,22 +4128,6 @@
return target;
}
- // Find the word at the given position (as returned by coordsChar).
- function findWordAt(doc, pos) {
- var line = getLine(doc, pos.line).text;
- var start = pos.ch, end = pos.ch;
- if (line) {
- if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end;
- var startChar = line.charAt(start);
- var check = isWordChar(startChar) ? isWordChar
- : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
- : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
- while (start > 0 && check(line.charAt(start - 1))) --start;
- while (end < line.length && check(line.charAt(end))) ++end;
- }
- return new Range(Pos(pos.line, start), Pos(pos.line, end));
- }
-
// EDITOR METHODS
// The publicly visible API. Note that methodOp(f) means
@@ -3760,12 +4154,12 @@
getDoc: function() {return this.doc;},
addKeyMap: function(map, bottom) {
- this.state.keyMaps[bottom ? "push" : "unshift"](map);
+ this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map));
},
removeKeyMap: function(map) {
var maps = this.state.keyMaps;
for (var i = 0; i < maps.length; ++i)
- if (maps[i] == map || (typeof maps[i] != "string" && maps[i].name == map)) {
+ if (maps[i] == map || maps[i].name == map) {
maps.splice(i, 1);
return true;
}
@@ -3803,11 +4197,14 @@
for (var i = 0; i < ranges.length; i++) {
var range = ranges[i];
if (!range.empty()) {
- var start = Math.max(end, range.from().line);
- var to = range.to();
+ var from = range.from(), to = range.to();
+ var start = Math.max(end, from.line);
end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
for (var j = start; j < end; ++j)
indentLine(this, j, how);
+ var newRanges = this.doc.sel.ranges;
+ if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
+ replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll);
} else if (range.head.line > end) {
indentLine(this, range.head.line, how, true);
end = range.head.line;
@@ -3819,33 +4216,27 @@
// Fetch the parser token for a given character. Useful for hacks
// that want to inspect the mode state (say, for completion).
getTokenAt: function(pos, precise) {
- var doc = this.doc;
- pos = clipPos(doc, pos);
- var state = getStateBefore(this, pos.line, precise), mode = this.doc.mode;
- var line = getLine(doc, pos.line);
- var stream = new StringStream(line.text, this.options.tabSize);
- while (stream.pos < pos.ch && !stream.eol()) {
- stream.start = stream.pos;
- var style = mode.token(stream, state);
- }
- return {start: stream.start,
- end: stream.pos,
- string: stream.current(),
- type: style || null,
- state: state};
+ return takeToken(this, pos, precise);
+ },
+
+ getLineTokens: function(line, precise) {
+ return takeToken(this, Pos(line), precise, true);
},
getTokenTypeAt: function(pos) {
pos = clipPos(this.doc, pos);
var styles = getLineStyles(this, getLine(this.doc, pos.line));
var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
- if (ch == 0) return styles[2];
- for (;;) {
+ var type;
+ if (ch == 0) type = styles[2];
+ else for (;;) {
var mid = (before + after) >> 1;
if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid;
else if (styles[mid * 2 + 1] < ch) before = mid + 1;
- else return styles[mid * 2 + 2];
+ else { type = styles[mid * 2 + 2]; break; }
}
+ var cut = type ? type.indexOf("cm-overlay ") : -1;
+ return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1);
},
getModeAt: function(pos) {
@@ -3922,7 +4313,7 @@
defaultCharWidth: function() { return charWidth(this.display); },
setGutterMarker: methodOp(function(line, gutterID, value) {
- return changeLine(this, line, "gutter", function(line) {
+ return changeLine(this.doc, line, "gutter", function(line) {
var markers = line.gutterMarkers || (line.gutterMarkers = {});
markers[gutterID] = value;
if (!value && isEmpty(markers)) line.gutterMarkers = null;
@@ -3942,32 +4333,6 @@
});
}),
- addLineClass: methodOp(function(handle, where, cls) {
- return changeLine(this, handle, "class", function(line) {
- var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
- if (!line[prop]) line[prop] = cls;
- else if (new RegExp("(?:^|\\s)" + cls + "(?:$|\\s)").test(line[prop])) return false;
- else line[prop] += " " + cls;
- return true;
- });
- }),
-
- removeLineClass: methodOp(function(handle, where, cls) {
- return changeLine(this, handle, "class", function(line) {
- var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
- var cur = line[prop];
- if (!cur) return false;
- else if (cls == null) line[prop] = null;
- else {
- var found = cur.match(new RegExp("(?:^|\\s+)" + cls + "(?:$|\\s+)"));
- if (!found) return false;
- var end = found.index + found[0].length;
- line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
- }
- return true;
- });
- }),
-
addLineWidget: methodOp(function(handle, node, options) {
return addLineWidget(this, handle, node, options);
}),
@@ -3996,6 +4361,7 @@
pos = cursorCoords(this, clipPos(this.doc, pos));
var top = pos.bottom, left = pos.left;
node.style.position = "absolute";
+ node.setAttribute("cm-ignore-events", "true");
display.sizer.appendChild(node);
if (vert == "over") {
top = pos.top;
@@ -4026,7 +4392,7 @@
triggerOnKeyDown: methodOp(onKeyDown),
triggerOnKeyPress: methodOp(onKeyPress),
- triggerOnKeyUp: methodOp(onKeyUp),
+ triggerOnKeyUp: onKeyUp,
execCommand: function(cmd) {
if (commands.hasOwnProperty(cmd))
@@ -4095,12 +4461,30 @@
doc.sel.ranges[i].goalColumn = goals[i];
}),
+ // Find the word at the given position (as returned by coordsChar).
+ findWordAt: function(pos) {
+ var doc = this.doc, line = getLine(doc, pos.line).text;
+ var start = pos.ch, end = pos.ch;
+ if (line) {
+ var helper = this.getHelper(pos, "wordChars");
+ if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end;
+ var startChar = line.charAt(start);
+ var check = isWordChar(startChar, helper)
+ ? function(ch) { return isWordChar(ch, helper); }
+ : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
+ : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
+ while (start > 0 && check(line.charAt(start - 1))) --start;
+ while (end < line.length && check(line.charAt(end))) ++end;
+ }
+ return new Range(Pos(pos.line, start), Pos(pos.line, end));
+ },
+
toggleOverwrite: function(value) {
if (value != null && value == this.state.overwrite) return;
if (this.state.overwrite = !this.state.overwrite)
- this.display.cursorDiv.className += " CodeMirror-overwrite";
+ addClass(this.display.cursorDiv, "CodeMirror-overwrite");
else
- this.display.cursorDiv.className = this.display.cursorDiv.className.replace(" CodeMirror-overwrite", "");
+ rmClass(this.display.cursorDiv, "CodeMirror-overwrite");
signal(this, "overwriteToggle", this, this.state.overwrite);
},
@@ -4112,10 +4496,11 @@
if (y != null) this.curOp.scrollTop = y;
}),
getScrollInfo: function() {
- var scroller = this.display.scroller, co = scrollerCutOff;
+ var scroller = this.display.scroller;
return {left: scroller.scrollLeft, top: scroller.scrollTop,
- height: scroller.scrollHeight - co, width: scroller.scrollWidth - co,
- clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
+ height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
+ width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
+ clientHeight: displayHeight(this), clientWidth: displayWidth(this)};
},
scrollIntoView: methodOp(function(range, margin) {
@@ -4143,14 +4528,21 @@
}),
setSize: methodOp(function(width, height) {
+ var cm = this;
function interpret(val) {
return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
}
- if (width != null) this.display.wrapper.style.width = interpret(width);
- if (height != null) this.display.wrapper.style.height = interpret(height);
- if (this.options.lineWrapping) clearLineMeasurementCache(this);
- this.curOp.forceUpdate = true;
- signal(this, "refresh", this);
+ if (width != null) cm.display.wrapper.style.width = interpret(width);
+ if (height != null) cm.display.wrapper.style.height = interpret(height);
+ if (cm.options.lineWrapping) clearLineMeasurementCache(this);
+ var lineNo = cm.display.viewFrom;
+ cm.doc.iter(lineNo, cm.display.viewTo, function(line) {
+ if (line.widgets) for (var i = 0; i < line.widgets.length; i++)
+ if (line.widgets[i].noHScroll) { regLineChange(cm, lineNo, "widget"); break; }
+ ++lineNo;
+ });
+ cm.curOp.forceUpdate = true;
+ signal(cm, "refresh", this);
}),
operation: function(f){return runInOp(this, f);},
@@ -4158,8 +4550,10 @@
refresh: methodOp(function() {
var oldHeight = this.display.cachedTextHeight;
regChange(this);
+ this.curOp.forceUpdate = true;
clearCaches(this);
this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop);
+ updateGutterSpace(this);
if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
estimateLineHeights(this);
signal(this, "refresh", this);
@@ -4172,6 +4566,7 @@
clearCaches(this);
resetInput(this);
this.scrollTo(doc.scrollLeft, doc.scrollTop);
+ this.curOp.forceScroll = true;
signalLater(this, "swapDoc", this, old);
return old;
}),
@@ -4217,7 +4612,7 @@
clearCaches(cm);
regChange(cm);
}, true);
- option("specialChars", /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\ufeff]/g, function(cm, val) {
+ option("specialChars", /[\t\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val) {
cm.options.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
cm.refresh();
}, true);
@@ -4230,7 +4625,12 @@
themeChanged(cm);
guttersChanged(cm);
}, true);
- option("keyMap", "default", keyMapChanged);
+ option("keyMap", "default", function(cm, val, old) {
+ var next = getKeyMap(val);
+ var prev = old != CodeMirror.Init && getKeyMap(old);
+ if (prev && prev.detach) prev.detach(cm, next);
+ if (next.attach) next.attach(cm, prev || null);
+ });
option("extraKeys", null);
option("lineWrapping", false, wrappingChanged, true);
@@ -4242,7 +4642,13 @@
cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
cm.refresh();
}, true);
- option("coverGutterNextToScrollbar", false, updateScrollbars, true);
+ option("coverGutterNextToScrollbar", false, function(cm) {updateScrollbars(cm);}, true);
+ option("scrollbarStyle", "native", function(cm) {
+ initScrollbars(cm);
+ updateScrollbars(cm);
+ cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
+ cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
+ }, true);
option("lineNumbers", false, function(cm) {
setGuttersForLineNumbers(cm.options);
guttersChanged(cm);
@@ -4268,7 +4674,8 @@
option("cursorBlinkRate", 530);
option("cursorScrollMargin", 0);
- option("cursorHeight", 1);
+ option("cursorHeight", 1, updateSelection, true);
+ option("singleCursorHeightPerLine", true, updateSelection, true);
option("workTime", 100);
option("workDelay", 100);
option("flattenSpans", true, resetModeState, true);
@@ -4297,10 +4704,8 @@
// load a mode. (Preferred mechanism is the require/define calls.)
CodeMirror.defineMode = function(name, mode) {
if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
- if (arguments.length > 2) {
- mode.dependencies = [];
- for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
- }
+ if (arguments.length > 2)
+ mode.dependencies = Array.prototype.slice.call(arguments, 2);
modes[name] = mode;
};
@@ -4451,6 +4856,20 @@
return {from: Pos(range.from().line, 0), to: range.from()};
});
},
+ delWrappedLineLeft: function(cm) {
+ deleteNearSelection(cm, function(range) {
+ var top = cm.charCoords(range.head, "div").top + 5;
+ var leftPos = cm.coordsChar({left: 0, top: top}, "div");
+ return {from: leftPos, to: range.from()};
+ });
+ },
+ delWrappedLineRight: function(cm) {
+ deleteNearSelection(cm, function(range) {
+ var top = cm.charCoords(range.head, "div").top + 5;
+ var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
+ return {from: range.from(), to: rightPos };
+ });
+ },
undo: function(cm) {cm.undo();},
redo: function(cm) {cm.redo();},
undoSelection: function(cm) {cm.undoSelection();},
@@ -4458,23 +4877,17 @@
goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
goLineStart: function(cm) {
- cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.line); }, sel_move);
+ cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.line); },
+ {origin: "+move", bias: 1});
},
goLineStartSmart: function(cm) {
cm.extendSelectionsBy(function(range) {
- var start = lineStart(cm, range.head.line);
- var line = cm.getLineHandle(start.line);
- var order = getOrder(line);
- if (!order || order[0].level == 0) {
- var firstNonWS = Math.max(0, line.text.search(/\S/));
- var inWS = range.head.line == start.line && range.head.ch <= firstNonWS && range.head.ch;
- return Pos(start.line, inWS ? 0 : firstNonWS);
- }
- return start;
- }, sel_move);
+ return lineStartSmart(cm, range.head);
+ }, {origin: "+move", bias: 1});
},
goLineEnd: function(cm) {
- cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line); }, sel_move);
+ cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line); },
+ {origin: "+move", bias: -1});
},
goLineRight: function(cm) {
cm.extendSelectionsBy(function(range) {
@@ -4488,6 +4901,14 @@
return cm.coordsChar({left: 0, top: top}, "div");
}, sel_move);
},
+ goLineLeftSmart: function(cm) {
+ cm.extendSelectionsBy(function(range) {
+ var top = cm.charCoords(range.head, "div").top + 5;
+ var pos = cm.coordsChar({left: 0, top: top}, "div");
+ if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head);
+ return pos;
+ }, sel_move);
+ },
goLineUp: function(cm) {cm.moveV(-1, "line");},
goLineDown: function(cm) {cm.moveV(1, "line");},
goPageUp: function(cm) {cm.moveV(-1, "page");},
@@ -4510,19 +4931,40 @@
indentMore: function(cm) {cm.indentSelection("add");},
indentLess: function(cm) {cm.indentSelection("subtract");},
insertTab: function(cm) {cm.replaceSelection("\t");},
+ insertSoftTab: function(cm) {
+ var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
+ for (var i = 0; i < ranges.length; i++) {
+ var pos = ranges[i].from();
+ var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
+ spaces.push(new Array(tabSize - col % tabSize + 1).join(" "));
+ }
+ cm.replaceSelections(spaces);
+ },
defaultTab: function(cm) {
if (cm.somethingSelected()) cm.indentSelection("add");
else cm.execCommand("insertTab");
},
transposeChars: function(cm) {
runInOp(cm, function() {
- var ranges = cm.listSelections();
+ var ranges = cm.listSelections(), newSel = [];
for (var i = 0; i < ranges.length; i++) {
var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
- if (cur.ch > 0 && cur.ch < line.length - 1)
- cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
- Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1));
+ if (line) {
+ if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1);
+ if (cur.ch > 0) {
+ cur = new Pos(cur.line, cur.ch + 1);
+ cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
+ Pos(cur.line, cur.ch - 2), cur, "+transpose");
+ } else if (cur.line > cm.doc.first) {
+ var prev = getLine(cm.doc, cur.line - 1).text;
+ if (prev)
+ cm.replaceRange(line.charAt(0) + "\n" + prev.charAt(prev.length - 1),
+ Pos(cur.line - 1, prev.length - 1), Pos(cur.line, 1), "+transpose");
+ }
+ }
+ newSel.push(new Range(cur, cur));
}
+ cm.setSelections(newSel);
});
},
newlineAndIndent: function(cm) {
@@ -4539,9 +4981,11 @@
toggleOverwrite: function(cm) {cm.toggleOverwrite();}
};
+
// STANDARD KEYMAPS
var keyMap = CodeMirror.keyMap = {};
+
keyMap.basic = {
"Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
"End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
@@ -4555,7 +4999,7 @@
// are simply ignored.
keyMap.pcDefault = {
"Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
- "Ctrl-Home": "goDocStart", "Ctrl-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
+ "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
"Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
"Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
"Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
@@ -4563,16 +5007,6 @@
"Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
fallthrough: "basic"
};
- keyMap.macDefault = {
- "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
- "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
- "Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore",
- "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
- "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
- "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delLineLeft",
- "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection",
- fallthrough: ["basic", "emacsy"]
- };
// Very basic readline/emacs-style bindings, which are standard on Mac.
keyMap.emacsy = {
"Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
@@ -4580,63 +5014,110 @@
"Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
"Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
};
+ keyMap.macDefault = {
+ "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
+ "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
+ "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
+ "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
+ "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
+ "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
+ "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
+ fallthrough: ["basic", "emacsy"]
+ };
keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
// KEYMAP DISPATCH
- function getKeyMap(val) {
- if (typeof val == "string") return keyMap[val];
- else return val;
+ function normalizeKeyName(name) {
+ var parts = name.split(/-(?!$)/), name = parts[parts.length - 1];
+ var alt, ctrl, shift, cmd;
+ for (var i = 0; i < parts.length - 1; i++) {
+ var mod = parts[i];
+ if (/^(cmd|meta|m)$/i.test(mod)) cmd = true;
+ else if (/^a(lt)?$/i.test(mod)) alt = true;
+ else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true;
+ else if (/^s(hift)$/i.test(mod)) shift = true;
+ else throw new Error("Unrecognized modifier name: " + mod);
+ }
+ if (alt) name = "Alt-" + name;
+ if (ctrl) name = "Ctrl-" + name;
+ if (cmd) name = "Cmd-" + name;
+ if (shift) name = "Shift-" + name;
+ return name;
}
- // Given an array of keymaps and a key name, call handle on any
- // bindings found, until that returns a truthy value, at which point
- // we consider the key handled. Implements things like binding a key
- // to false stopping further handling and keymap fallthrough.
- var lookupKey = CodeMirror.lookupKey = function(name, maps, handle) {
- function lookup(map) {
- map = getKeyMap(map);
- var found = map[name];
- if (found === false) return "stop";
- if (found != null && handle(found)) return true;
- if (map.nofallthrough) return "stop";
+ // This is a kludge to keep keymaps mostly working as raw objects
+ // (backwards compatibility) while at the same time support features
+ // like normalization and multi-stroke key bindings. It compiles a
+ // new normalized keymap, and then updates the old object to reflect
+ // this.
+ CodeMirror.normalizeKeyMap = function(keymap) {
+ var copy = {};
+ for (var keyname in keymap) if (keymap.hasOwnProperty(keyname)) {
+ var value = keymap[keyname];
+ if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue;
+ if (value == "...") { delete keymap[keyname]; continue; }
- var fallthrough = map.fallthrough;
- if (fallthrough == null) return false;
- if (Object.prototype.toString.call(fallthrough) != "[object Array]")
- return lookup(fallthrough);
- for (var i = 0; i < fallthrough.length; ++i) {
- var done = lookup(fallthrough[i]);
- if (done) return done;
+ var keys = map(keyname.split(" "), normalizeKeyName);
+ for (var i = 0; i < keys.length; i++) {
+ var val, name;
+ if (i == keys.length - 1) {
+ name = keyname;
+ val = value;
+ } else {
+ name = keys.slice(0, i + 1).join(" ");
+ val = "...";
+ }
+ var prev = copy[name];
+ if (!prev) copy[name] = val;
+ else if (prev != val) throw new Error("Inconsistent bindings for " + name);
}
- return false;
+ delete keymap[keyname];
}
+ for (var prop in copy) keymap[prop] = copy[prop];
+ return keymap;
+ };
- for (var i = 0; i < maps.length; ++i) {
- var done = lookup(maps[i]);
- if (done) return done != "stop";
+ var lookupKey = CodeMirror.lookupKey = function(key, map, handle, context) {
+ map = getKeyMap(map);
+ var found = map.call ? map.call(key, context) : map[key];
+ if (found === false) return "nothing";
+ if (found === "...") return "multi";
+ if (found != null && handle(found)) return "handled";
+
+ if (map.fallthrough) {
+ if (Object.prototype.toString.call(map.fallthrough) != "[object Array]")
+ return lookupKey(key, map.fallthrough, handle, context);
+ for (var i = 0; i < map.fallthrough.length; i++) {
+ var result = lookupKey(key, map.fallthrough[i], handle, context);
+ if (result) return result;
+ }
}
};
// Modifier key presses don't count as 'real' key presses for the
// purpose of keymap fallthrough.
- var isModifierKey = CodeMirror.isModifierKey = function(event) {
- var name = keyNames[event.keyCode];
+ var isModifierKey = CodeMirror.isModifierKey = function(value) {
+ var name = typeof value == "string" ? value : keyNames[value.keyCode];
return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
};
// Look up the name of a key as indicated by an event object.
var keyName = CodeMirror.keyName = function(event, noShift) {
if (presto && event.keyCode == 34 && event["char"]) return false;
- var name = keyNames[event.keyCode];
+ var base = keyNames[event.keyCode], name = base;
if (name == null || event.altGraphKey) return false;
- if (event.altKey) name = "Alt-" + name;
- if (flipCtrlCmd ? event.metaKey : event.ctrlKey) name = "Ctrl-" + name;
- if (flipCtrlCmd ? event.ctrlKey : event.metaKey) name = "Cmd-" + name;
- if (!noShift && event.shiftKey) name = "Shift-" + name;
+ if (event.altKey && base != "Alt") name = "Alt-" + name;
+ if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") name = "Ctrl-" + name;
+ if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") name = "Cmd-" + name;
+ if (!noShift && event.shiftKey && base != "Shift") name = "Shift-" + name;
return name;
};
+ function getKeyMap(val) {
+ return typeof val == "string" ? keyMap[val] : val;
+ }
+
// FROMTEXTAREA
CodeMirror.fromTextArea = function(textarea, options) {
@@ -4678,6 +5159,7 @@
cm.save = save;
cm.getTextArea = function() { return textarea; };
cm.toTextArea = function() {
+ cm.toTextArea = isNaN; // Prevent this from being ran twice
save();
textarea.parentNode.removeChild(cm.getWrapperElement());
textarea.style.display = "";
@@ -4826,6 +5308,7 @@
}
if (cm) signalLater(cm, "markerCleared", cm, this);
if (withOp) endOperation(cm);
+ if (this.parent) this.parent.clear();
};
// Find the position of the marker in the document. Returns a {from,
@@ -4905,7 +5388,7 @@
if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
var marker = new TextMarker(doc, type), diff = cmp(from, to);
- if (options) copyObj(options, marker);
+ if (options) copyObj(options, marker, false);
// Don't connect empty markers unless clearWhenEmpty is false
if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
return marker;
@@ -4913,7 +5396,7 @@
// Showing up as a widget implies collapsed (widget replaces text)
marker.collapsed = true;
marker.widgetNode = elt("span", [marker.replacedWith], "CodeMirror-widget");
- if (!options.handleMouseEvents) marker.widgetNode.ignoreEvents = true;
+ if (!options.handleMouseEvents) marker.widgetNode.setAttribute("cm-ignore-events", "true");
if (options.insertLeft) marker.widgetNode.insertLeft = true;
}
if (marker.collapsed) {
@@ -4957,7 +5440,7 @@
if (updateMaxLine) cm.curOp.updateMaxLine = true;
if (marker.collapsed)
regChange(cm, from.line, to.line + 1);
- else if (marker.className || marker.title || marker.startStyle || marker.endStyle)
+ else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css)
for (var i = from.line; i <= to.line; i++) regLineChange(cm, i, "text");
if (marker.atomic) reCheckSelection(cm.doc);
signalLater(cm, "markerAdded", cm, marker);
@@ -4973,10 +5456,8 @@
var SharedTextMarker = CodeMirror.SharedTextMarker = function(markers, primary) {
this.markers = markers;
this.primary = primary;
- for (var i = 0, me = this; i < markers.length; ++i) {
+ for (var i = 0; i < markers.length; ++i)
markers[i].parent = this;
- on(markers[i], "clear", function(){me.clear();});
- }
};
eventMixin(SharedTextMarker);
@@ -5006,6 +5487,37 @@
return new SharedTextMarker(markers, primary);
}
+ function findSharedMarkers(doc) {
+ return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())),
+ function(m) { return m.parent; });
+ }
+
+ function copySharedMarkers(doc, markers) {
+ for (var i = 0; i < markers.length; i++) {
+ var marker = markers[i], pos = marker.find();
+ var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
+ if (cmp(mFrom, mTo)) {
+ var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
+ marker.markers.push(subMark);
+ subMark.parent = marker;
+ }
+ }
+ }
+
+ function detachSharedMarkers(markers) {
+ for (var i = 0; i < markers.length; i++) {
+ var marker = markers[i], linked = [marker.primary.doc];;
+ linkedDocs(marker.primary.doc, function(d) { linked.push(d); });
+ for (var j = 0; j < marker.markers.length; j++) {
+ var subMarker = marker.markers[j];
+ if (indexOf(linked, subMarker.doc) == -1) {
+ subMarker.parent = null;
+ marker.markers.splice(j--, 1);
+ }
+ }
+ }
+ }
+
// TEXTMARKER SPANS
function MarkedSpan(marker, from, to) {
@@ -5068,6 +5580,7 @@
// spans partially within the change. Returns an array of span
// arrays with one element for each line in (after) the change.
function stretchSpansOverChange(doc, change) {
+ if (change.full) return null;
var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
if (!oldFirst && !oldLast) return null;
@@ -5255,8 +5768,8 @@
var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue;
- if (fromCmp <= 0 && (cmp(found.to, from) || extraRight(sp.marker) - extraLeft(marker)) > 0 ||
- fromCmp >= 0 && (cmp(found.from, to) || extraLeft(sp.marker) - extraRight(marker)) < 0)
+ if (fromCmp <= 0 && (cmp(found.to, from) > 0 || (sp.marker.inclusiveRight && marker.inclusiveLeft)) ||
+ fromCmp >= 0 && (cmp(found.from, to) < 0 || (sp.marker.inclusiveLeft && marker.inclusiveRight)))
return true;
}
}
@@ -5374,15 +5887,21 @@
function widgetHeight(widget) {
if (widget.height != null) return widget.height;
- if (!contains(document.body, widget.node))
- removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, "position: relative"));
+ if (!contains(document.body, widget.node)) {
+ var parentStyle = "position: relative;";
+ if (widget.coverGutter)
+ parentStyle += "margin-left: -" + widget.cm.display.gutters.offsetWidth + "px;";
+ if (widget.noHScroll)
+ parentStyle += "width: " + widget.cm.display.wrapper.clientWidth + "px;";
+ removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, parentStyle));
+ }
return widget.height = widget.node.offsetHeight;
}
function addLineWidget(cm, handle, node, options) {
var widget = new LineWidget(cm, node, options);
if (widget.noHScroll) cm.display.alignWidgets = true;
- changeLine(cm, handle, "widget", function(line) {
+ changeLine(cm.doc, handle, "widget", function(line) {
var widgets = line.widgets || (line.widgets = []);
if (widget.insertAt == null) widgets.push(widget);
else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget);
@@ -5430,13 +5949,66 @@
detachMarkedSpans(line);
}
+ function extractLineClasses(type, output) {
+ if (type) for (;;) {
+ var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
+ if (!lineClass) break;
+ type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
+ var prop = lineClass[1] ? "bgClass" : "textClass";
+ if (output[prop] == null)
+ output[prop] = lineClass[2];
+ else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
+ output[prop] += " " + lineClass[2];
+ }
+ return type;
+ }
+
+ function callBlankLine(mode, state) {
+ if (mode.blankLine) return mode.blankLine(state);
+ if (!mode.innerMode) return;
+ var inner = CodeMirror.innerMode(mode, state);
+ if (inner.mode.blankLine) return inner.mode.blankLine(inner.state);
+ }
+
+ function readToken(mode, stream, state, inner) {
+ for (var i = 0; i < 10; i++) {
+ if (inner) inner[0] = CodeMirror.innerMode(mode, state).mode;
+ var style = mode.token(stream, state);
+ if (stream.pos > stream.start) return style;
+ }
+ throw new Error("Mode " + mode.name + " failed to advance stream.");
+ }
+
+ // Utility for getTokenAt and getLineTokens
+ function takeToken(cm, pos, precise, asArray) {
+ function getObj(copy) {
+ return {start: stream.start, end: stream.pos,
+ string: stream.current(),
+ type: style || null,
+ state: copy ? copyState(doc.mode, state) : state};
+ }
+
+ var doc = cm.doc, mode = doc.mode, style;
+ pos = clipPos(doc, pos);
+ var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise);
+ var stream = new StringStream(line.text, cm.options.tabSize), tokens;
+ if (asArray) tokens = [];
+ while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
+ stream.start = stream.pos;
+ style = readToken(mode, stream, state);
+ if (asArray) tokens.push(getObj(true));
+ }
+ return asArray ? tokens : getObj();
+ }
+
// Run the given mode's parser over a line, calling f for each token.
- function runMode(cm, text, mode, state, f, forceToEnd) {
+ function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) {
var flattenSpans = mode.flattenSpans;
if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
var curStart = 0, curStyle = null;
var stream = new StringStream(text, cm.options.tabSize), style;
- if (text == "" && mode.blankLine) mode.blankLine(state);
+ var inner = cm.options.addModeClass && [null];
+ if (text == "") extractLineClasses(callBlankLine(mode, state), lineClasses);
while (!stream.eol()) {
if (stream.pos > cm.options.maxHighlightLength) {
flattenSpans = false;
@@ -5444,15 +6016,18 @@
stream.pos = text.length;
style = null;
} else {
- style = mode.token(stream, state);
+ style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses);
}
- if (cm.options.addModeClass) {
- var mName = CodeMirror.innerMode(mode, state).mode.name;
+ if (inner) {
+ var mName = inner[0].name;
if (mName) style = "m-" + (style ? mName + " " + style : mName);
}
if (!flattenSpans || curStyle != style) {
- if (curStart < stream.start) f(stream.start, curStyle);
- curStart = stream.start; curStyle = style;
+ while (curStart < stream.start) {
+ curStart = Math.min(stream.start, curStart + 50000);
+ f(curStart, curStyle);
+ }
+ curStyle = style;
}
stream.start = stream.pos;
}
@@ -5471,11 +6046,11 @@
function highlightLine(cm, line, state, forceToEnd) {
// A styles array always starts with a number identifying the
// mode/overlays that it is based on (for easy invalidation).
- var st = [cm.state.modeGen];
+ var st = [cm.state.modeGen], lineClasses = {};
// Compute the base array of styles
runMode(cm, line.text, cm.doc.mode, state, function(end, style) {
st.push(end, style);
- }, forceToEnd);
+ }, lineClasses, forceToEnd);
// Run overlays, adjust style array.
for (var o = 0; o < cm.state.overlays.length; ++o) {
@@ -5492,23 +6067,28 @@
}
if (!style) return;
if (overlay.opaque) {
- st.splice(start, i - start, end, style);
+ st.splice(start, i - start, end, "cm-overlay " + style);
i = start + 2;
} else {
for (; start < i; start += 2) {
var cur = st[start+1];
- st[start+1] = cur ? cur + " " + style : style;
+ st[start+1] = (cur ? cur + " " : "") + "cm-overlay " + style;
}
}
- });
+ }, lineClasses);
}
- return st;
+ return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null};
}
- function getLineStyles(cm, line) {
- if (!line.styles || line.styles[0] != cm.state.modeGen)
- line.styles = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
+ function getLineStyles(cm, line, updateFrontier) {
+ if (!line.styles || line.styles[0] != cm.state.modeGen) {
+ var result = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
+ line.styles = result.styles;
+ if (result.classes) line.styleClasses = result.classes;
+ else if (line.styleClasses) line.styleClasses = null;
+ if (updateFrontier === cm.doc.frontier) cm.doc.frontier++;
+ }
return line.styles;
}
@@ -5519,9 +6099,9 @@
var mode = cm.doc.mode;
var stream = new StringStream(text, cm.options.tabSize);
stream.start = stream.pos = startAt || 0;
- if (text == "" && mode.blankLine) mode.blankLine(state);
+ if (text == "") callBlankLine(mode, state);
while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) {
- mode.token(stream, state);
+ readToken(mode, stream, state);
stream.start = stream.pos;
}
}
@@ -5530,20 +6110,9 @@
// containing one or more styles) to a CSS style. This is cached,
// and also looks for line-wide styles.
var styleToClassCache = {}, styleToClassCacheWithMode = {};
- function interpretTokenStyle(style, builder) {
- if (!style) return null;
- for (;;) {
- var lineClass = style.match(/(?:^|\s+)line-(background-)?(\S+)/);
- if (!lineClass) break;
- style = style.slice(0, lineClass.index) + style.slice(lineClass.index + lineClass[0].length);
- var prop = lineClass[1] ? "bgClass" : "textClass";
- if (builder[prop] == null)
- builder[prop] = lineClass[2];
- else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(builder[prop]))
- builder[prop] += " " + lineClass[2];
- }
- if (/^\s*$/.test(style)) return null;
- var cache = builder.cm.options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
+ function interpretTokenStyle(style, options) {
+ if (!style || /^\s*$/.test(style)) return null;
+ var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
return cache[style] ||
(cache[style] = style.replace(/\S+/g, "cm-$&"));
}
@@ -5573,7 +6142,14 @@
if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line)))
builder.addToken = buildTokenBadBidi(builder.addToken, order);
builder.map = [];
- insertLineContent(line, builder, getLineStyles(cm, line));
+ var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
+ insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
+ if (line.styleClasses) {
+ if (line.styleClasses.bgClass)
+ builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "");
+ if (line.styleClasses.textClass)
+ builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || "");
+ }
// Ensure at least a single node is present, for measuring.
if (builder.map.length == 0)
@@ -5589,7 +6165,14 @@
}
}
+ // See issue #2901
+ if (webkit && /\bcm-tab\b/.test(builder.content.lastChild.className))
+ builder.content.className = "cm-tab-wrap-hack";
+
signal(cm, "renderLine", cm, lineView.line, builder.pre);
+ if (builder.pre.className)
+ builder.textClass = joinClasses(builder.pre.className, builder.textClass || "");
+
return builder;
}
@@ -5601,14 +6184,14 @@
// Build up the DOM representation for a single token, and add it to
// the line map. Takes care to render special characters separately.
- function buildToken(builder, text, style, startStyle, endStyle, title) {
+ function buildToken(builder, text, style, startStyle, endStyle, title, css) {
if (!text) return;
var special = builder.cm.options.specialChars, mustWrap = false;
if (!special.test(text)) {
builder.col += text.length;
var content = document.createTextNode(text);
builder.map.push(builder.pos, builder.pos + text.length, content);
- if (ie_upto8) mustWrap = true;
+ if (ie && ie_version < 9) mustWrap = true;
builder.pos += text.length;
} else {
var content = document.createDocumentFragment(), pos = 0;
@@ -5618,7 +6201,7 @@
var skipped = m ? m.index - pos : text.length - pos;
if (skipped) {
var txt = document.createTextNode(text.slice(pos, pos + skipped));
- if (ie_upto8) content.appendChild(elt("span", [txt]));
+ if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
else content.appendChild(txt);
builder.map.push(builder.pos, builder.pos + skipped, txt);
builder.col += skipped;
@@ -5632,7 +6215,7 @@
builder.col += tabWidth;
} else {
var txt = builder.cm.options.specialCharPlaceholder(m[0]);
- if (ie_upto8) content.appendChild(elt("span", [txt]));
+ if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
else content.appendChild(txt);
builder.col += 1;
}
@@ -5640,11 +6223,11 @@
builder.pos++;
}
}
- if (style || startStyle || endStyle || mustWrap) {
+ if (style || startStyle || endStyle || mustWrap || css) {
var fullStyle = style || "";
if (startStyle) fullStyle += startStyle;
if (endStyle) fullStyle += endStyle;
- var token = elt("span", [content], fullStyle);
+ var token = elt("span", [content], fullStyle, css);
if (title) token.title = title;
return builder.content.appendChild(token);
}
@@ -5699,15 +6282,15 @@
var spans = line.markedSpans, allText = line.text, at = 0;
if (!spans) {
for (var i = 1; i < styles.length; i+=2)
- builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder));
+ builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options));
return;
}
- var len = allText.length, pos = 0, i = 1, text = "", style;
+ var len = allText.length, pos = 0, i = 1, text = "", style, css;
var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed;
for (;;) {
if (nextChange == pos) { // Update current marker set
- spanStyle = spanEndStyle = spanStartStyle = title = "";
+ spanStyle = spanEndStyle = spanStartStyle = title = css = "";
collapsed = null; nextChange = Infinity;
var foundBookmarks = [];
for (var j = 0; j < spans.length; ++j) {
@@ -5715,6 +6298,7 @@
if (sp.from <= pos && (sp.to == null || sp.to > pos)) {
if (sp.to != null && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; }
if (m.className) spanStyle += " " + m.className;
+ if (m.css) css = m.css;
if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
if (m.title && !title) title = m.title;
@@ -5742,14 +6326,14 @@
if (!collapsed) {
var tokenText = end > upto ? text.slice(0, upto - pos) : text;
builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
- spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title);
+ spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css);
}
if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
pos = end;
spanStartStyle = "";
}
text = allText.slice(at, at = styles[i++]);
- style = interpretTokenStyle(styles[i++], builder);
+ style = interpretTokenStyle(styles[i++], builder.cm.options);
}
}
}
@@ -5771,17 +6355,24 @@
updateLine(line, text, spans, estimateHeight);
signalLater(line, "change", line, change);
}
+ function linesFor(start, end) {
+ for (var i = start, result = []; i < end; ++i)
+ result.push(new Line(text[i], spansFor(i), estimateHeight));
+ return result;
+ }
var from = change.from, to = change.to, text = change.text;
var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
// Adjust the line structure
- if (isWholeLineUpdate(doc, change)) {
+ if (change.full) {
+ doc.insert(0, linesFor(0, text.length));
+ doc.remove(text.length, doc.size - text.length);
+ } else if (isWholeLineUpdate(doc, change)) {
// This is a whole-line replace. Treated specially to make
// sure line objects move the way they are supposed to.
- for (var i = 0, added = []; i < text.length - 1; ++i)
- added.push(new Line(text[i], spansFor(i), estimateHeight));
+ var added = linesFor(0, text.length - 1);
update(lastLine, lastLine.text, lastSpans);
if (nlines) doc.remove(from.line, nlines);
if (added.length) doc.insert(from.line, added);
@@ -5789,8 +6380,7 @@
if (text.length == 1) {
update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
} else {
- for (var added = [], i = 1; i < text.length - 1; ++i)
- added.push(new Line(text[i], spansFor(i), estimateHeight));
+ var added = linesFor(1, text.length - 1);
added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
doc.insert(from.line + 1, added);
@@ -5801,8 +6391,7 @@
} else {
update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
- for (var i = 1, added = []; i < text.length - 1; ++i)
- added.push(new Line(text[i], spansFor(i), estimateHeight));
+ var added = linesFor(1, text.length - 1);
if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
doc.insert(from.line + 1, added);
}
@@ -6013,7 +6602,7 @@
setValue: docMethodOp(function(code) {
var top = Pos(this.first, 0), last = this.first + this.size - 1;
makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
- text: splitLines(code), origin: "setValue"}, true);
+ text: splitLines(code), origin: "setValue", full: true}, true);
setSelection(this, simpleSelection(top));
}),
replaceRange: function(code, from, to, origin) {
@@ -6101,13 +6690,13 @@
}
return parts;
},
- replaceSelection: docMethodOp(function(code, collapse, origin) {
+ replaceSelection: function(code, collapse, origin) {
var dup = [];
for (var i = 0; i < this.sel.ranges.length; i++)
dup[i] = code;
this.replaceSelections(dup, collapse, origin || "+input");
- }),
- replaceSelections: function(code, collapse, origin) {
+ },
+ replaceSelections: docMethodOp(function(code, collapse, origin) {
var changes = [], sel = this.sel;
for (var i = 0; i < sel.ranges.length; i++) {
var range = sel.ranges[i];
@@ -6118,7 +6707,7 @@
makeChange(this, changes[i]);
if (newSel) setSelectionReplaceHistory(this, newSel);
else if (this.cm) ensureCursorVisible(this.cm);
- },
+ }),
undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
@@ -6140,7 +6729,7 @@
},
changeGeneration: function(forceSplit) {
if (forceSplit)
- this.history.lastOp = this.history.lastOrigin = null;
+ this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null;
return this.history.generation;
},
isClean: function (gen) {
@@ -6157,6 +6746,35 @@
hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
},
+ addLineClass: docMethodOp(function(handle, where, cls) {
+ return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) {
+ var prop = where == "text" ? "textClass"
+ : where == "background" ? "bgClass"
+ : where == "gutter" ? "gutterClass" : "wrapClass";
+ if (!line[prop]) line[prop] = cls;
+ else if (classTest(cls).test(line[prop])) return false;
+ else line[prop] += " " + cls;
+ return true;
+ });
+ }),
+ removeLineClass: docMethodOp(function(handle, where, cls) {
+ return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) {
+ var prop = where == "text" ? "textClass"
+ : where == "background" ? "bgClass"
+ : where == "gutter" ? "gutterClass" : "wrapClass";
+ var cur = line[prop];
+ if (!cur) return false;
+ else if (cls == null) line[prop] = null;
+ else {
+ var found = cur.match(classTest(cls));
+ if (!found) return false;
+ var end = found.index + found[0].length;
+ line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
+ }
+ return true;
+ });
+ }),
+
markText: function(from, to, options) {
return markText(this, clipPos(this, from), clipPos(this, to), options, "range");
},
@@ -6178,7 +6796,7 @@
}
return markers;
},
- findMarks: function(from, to) {
+ findMarks: function(from, to, filter) {
from = clipPos(this, from); to = clipPos(this, to);
var found = [], lineNo = from.line;
this.iter(from.line, to.line + 1, function(line) {
@@ -6187,7 +6805,8 @@
var span = spans[i];
if (!(lineNo == from.line && from.ch > span.to ||
span.from == null && lineNo != from.line||
- lineNo == to.line && span.from > to.ch))
+ lineNo == to.line && span.from > to.ch) &&
+ (!filter || filter(span.marker)))
found.push(span.marker.parent || span.marker);
}
++lineNo;
@@ -6245,6 +6864,7 @@
if (options.sharedHist) copy.history = this.history;
(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
+ copySharedMarkers(copy, findSharedMarkers(this));
return copy;
},
unlinkDoc: function(other) {
@@ -6254,6 +6874,7 @@
if (link.doc != other) continue;
this.linked.splice(i, 1);
other.unlinkDoc(this);
+ detachSharedMarkers(findSharedMarkers(this));
break;
}
// If the histories were shared, split them again
@@ -6429,7 +7050,7 @@
// Used to track when changes can be merged into a single undo
// event
this.lastModTime = this.lastSelTime = 0;
- this.lastOp = null;
+ this.lastOp = this.lastSelOp = null;
this.lastOrigin = this.lastSelOrigin = null;
// Used by the isClean() method
this.generation = this.maxGeneration = startGen || 1;
@@ -6507,7 +7128,7 @@
hist.done.push(selAfter);
hist.generation = ++hist.maxGeneration;
hist.lastModTime = hist.lastSelTime = time;
- hist.lastOp = opId;
+ hist.lastOp = hist.lastSelOp = opId;
hist.lastOrigin = hist.lastSelOrigin = change.origin;
if (!last) signal(doc, "historyAdded");
@@ -6533,7 +7154,7 @@
// the current, or the origins don't allow matching. Origins
// starting with * are always merged, those starting with + are
// merged when similar and close together in time.
- if (opId == hist.lastOp ||
+ if (opId == hist.lastSelOp ||
(origin && hist.lastSelOrigin == origin &&
(hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
@@ -6543,7 +7164,7 @@
hist.lastSelTime = +new Date;
hist.lastSelOrigin = origin;
- hist.lastOp = opId;
+ hist.lastSelOp = opId;
if (options && options.clearRedo !== false)
clearSelectionEvents(hist.undone);
}
@@ -6728,6 +7349,8 @@
for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
};
+ var orphanDelayedCallbacks = null;
+
// Often, we want to signal events at a point where we are in the
// middle of some work, but don't want the handler to start calling
// other methods on the editor, which might be in an inconsistent
@@ -6735,25 +7358,26 @@
// signalLater looks whether there are any handlers, and schedules
// them to be executed when the last operation ends, or, if no
// operation is active, when a timeout fires.
- var delayedCallbacks, delayedCallbackDepth = 0;
function signalLater(emitter, type /*, values...*/) {
var arr = emitter._handlers && emitter._handlers[type];
if (!arr) return;
- var args = Array.prototype.slice.call(arguments, 2);
- if (!delayedCallbacks) {
- ++delayedCallbackDepth;
- delayedCallbacks = [];
- setTimeout(fireDelayed, 0);
+ var args = Array.prototype.slice.call(arguments, 2), list;
+ if (operationGroup) {
+ list = operationGroup.delayedCallbacks;
+ } else if (orphanDelayedCallbacks) {
+ list = orphanDelayedCallbacks;
+ } else {
+ list = orphanDelayedCallbacks = [];
+ setTimeout(fireOrphanDelayed, 0);
}
function bnd(f) {return function(){f.apply(null, args);};};
for (var i = 0; i < arr.length; ++i)
- delayedCallbacks.push(bnd(arr[i]));
+ list.push(bnd(arr[i]));
}
- function fireDelayed() {
- --delayedCallbackDepth;
- var delayed = delayedCallbacks;
- delayedCallbacks = null;
+ function fireOrphanDelayed() {
+ var delayed = orphanDelayedCallbacks;
+ orphanDelayedCallbacks = null;
for (var i = 0; i < delayed.length; ++i) delayed[i]();
}
@@ -6761,10 +7385,20 @@
// registering a (non-DOM) handler on the editor for the event name,
// and preventDefault-ing the event in that handler.
function signalDOMEvent(cm, e, override) {
+ if (typeof e == "string")
+ e = {type: e, preventDefault: function() { this.defaultPrevented = true; }};
signal(cm, override || e.type, cm, e);
return e_defaultPrevented(e) || e.codemirrorIgnore;
}
+ function signalCursorActivity(cm) {
+ var arr = cm._handlers && cm._handlers.cursorActivity;
+ if (!arr) return;
+ var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
+ for (var i = 0; i < arr.length; ++i) if (indexOf(set, arr[i]) == -1)
+ set.push(arr[i]);
+ }
+
function hasHandler(emitter, type) {
var arr = emitter._handlers && emitter._handlers[type];
return arr && arr.length > 0;
@@ -6780,7 +7414,7 @@
// MISC UTILITIES
// Number of pixels added to scroller and sizer to hide scrollbar
- var scrollerCutOff = 30;
+ var scrollerGap = 30;
// Returned or thrown by various protocols to signal 'I'm not
// handling this'.
@@ -6848,13 +7482,11 @@
if (array[i] == elt) return i;
return -1;
}
- if ([].indexOf) indexOf = function(array, elt) { return array.indexOf(elt); };
function map(array, f) {
var out = [];
for (var i = 0; i < array.length; i++) out[i] = f(array[i], i);
return out;
}
- if ([].map) map = function(array, f) { return array.map(f); };
function createObj(base, props) {
var inst;
@@ -6869,9 +7501,11 @@
return inst;
};
- function copyObj(obj, target) {
+ function copyObj(obj, target, overwrite) {
if (!target) target = {};
- for (var prop in obj) if (obj.hasOwnProperty(prop)) target[prop] = obj[prop];
+ for (var prop in obj)
+ if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
+ target[prop] = obj[prop];
return target;
}
@@ -6880,11 +7514,16 @@
return function(){return f.apply(null, args);};
}
- var nonASCIISingleCaseWordChar = /[\u00df\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
- var isWordChar = CodeMirror.isWordChar = function(ch) {
+ var nonASCIISingleCaseWordChar = /[\u00df\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
+ var isWordCharBasic = CodeMirror.isWordChar = function(ch) {
return /\w/.test(ch) || ch > "\x80" &&
(ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
};
+ function isWordChar(ch, helper) {
+ if (!helper) return isWordCharBasic(ch);
+ if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) return true;
+ return helper.test(ch);
+ }
function isEmpty(obj) {
for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false;
@@ -6919,7 +7558,8 @@
};
else range = function(node, start, end) {
var r = document.body.createTextRange();
- r.moveToElementText(node.parentNode);
+ try { r.moveToElementText(node.parentNode); }
+ catch(e) { return r; }
r.collapse(true);
r.moveEnd("character", end);
r.moveStart("character", start);
@@ -6946,39 +7586,85 @@
function activeElt() { return document.activeElement; }
// Older versions of IE throws unspecified error when touching
// document.activeElement in some cases (during loading, in iframe)
- if (ie_upto10) activeElt = function() {
+ if (ie && ie_version < 11) activeElt = function() {
try { return document.activeElement; }
catch(e) { return document.body; }
};
+ function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*"); }
+ var rmClass = CodeMirror.rmClass = function(node, cls) {
+ var current = node.className;
+ var match = classTest(cls).exec(current);
+ if (match) {
+ var after = current.slice(match.index + match[0].length);
+ node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
+ }
+ };
+ var addClass = CodeMirror.addClass = function(node, cls) {
+ var current = node.className;
+ if (!classTest(cls).test(current)) node.className += (current ? " " : "") + cls;
+ };
+ function joinClasses(a, b) {
+ var as = a.split(" ");
+ for (var i = 0; i < as.length; i++)
+ if (as[i] && !classTest(as[i]).test(b)) b += " " + as[i];
+ return b;
+ }
+
+ // WINDOW-WIDE EVENTS
+
+ // These must be handled carefully, because naively registering a
+ // handler for each editor will cause the editors to never be
+ // garbage collected.
+
+ function forEachCodeMirror(f) {
+ if (!document.body.getElementsByClassName) return;
+ var byClass = document.body.getElementsByClassName("CodeMirror");
+ for (var i = 0; i < byClass.length; i++) {
+ var cm = byClass[i].CodeMirror;
+ if (cm) f(cm);
+ }
+ }
+
+ var globalsRegistered = false;
+ function ensureGlobalHandlers() {
+ if (globalsRegistered) return;
+ registerGlobalHandlers();
+ globalsRegistered = true;
+ }
+ function registerGlobalHandlers() {
+ // When the window resizes, we need to refresh active editors.
+ var resizeTimer;
+ on(window, "resize", function() {
+ if (resizeTimer == null) resizeTimer = setTimeout(function() {
+ resizeTimer = null;
+ forEachCodeMirror(onResize);
+ }, 100);
+ });
+ // When the window loses focus, we want to show the editor as blurred
+ on(window, "blur", function() {
+ forEachCodeMirror(onBlur);
+ });
+ }
+
// FEATURE DETECTION
// Detect drag-and-drop
var dragAndDrop = function() {
// There is *some* kind of drag-and-drop support in IE6-8, but I
// couldn't get it to work yet.
- if (ie_upto8) return false;
+ if (ie && ie_version < 9) return false;
var div = elt('div');
return "draggable" in div || "dragDrop" in div;
}();
- var knownScrollbarWidth;
- function scrollbarWidth(measure) {
- if (knownScrollbarWidth != null) return knownScrollbarWidth;
- var test = elt("div", null, null, "width: 50px; height: 50px; overflow-x: scroll");
- removeChildrenAndAdd(measure, test);
- if (test.offsetWidth)
- knownScrollbarWidth = test.offsetHeight - test.clientHeight;
- return knownScrollbarWidth || 0;
- }
-
var zwspSupported;
function zeroWidthElement(measure) {
if (zwspSupported == null) {
var test = elt("span", "\u200b");
removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
if (measure.firstChild.offsetHeight != 0)
- zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !ie_upto7;
+ zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8);
}
if (zwspSupported) return elt("span", "\u200b");
else return elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
@@ -6990,7 +7676,7 @@
if (badBidiRects != null) return badBidiRects;
var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
var r0 = range(txt, 0, 1).getBoundingClientRect();
- if (r0.left == r0.right) return false;
+ if (!r0 || r0.left == r0.right) return false; // Safari returns null in some cases (#2780)
var r1 = range(txt, 1, 2).getBoundingClientRect();
return badBidiRects = (r1.right - r0.right < 3);
}
@@ -7032,6 +7718,15 @@
return typeof e.oncopy == "function";
})();
+ var badZoomedRects = null;
+ function hasBadZoomedRects(measure) {
+ if (badZoomedRects != null) return badZoomedRects;
+ var node = removeChildrenAndAdd(measure, elt("span", "x"));
+ var normal = node.getBoundingClientRect();
+ var fromRange = range(node, 0, 1).getBoundingClientRect();
+ return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1;
+ }
+
// KEY NAMES
var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
@@ -7094,6 +7789,17 @@
var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
return Pos(lineN == null ? lineNo(line) : lineN, ch);
}
+ function lineStartSmart(cm, pos) {
+ var start = lineStart(cm, pos.line);
+ var line = getLine(cm.doc, start.line);
+ var order = getOrder(line);
+ if (!order || order[0].level == 0) {
+ var firstNonWS = Math.max(0, line.text.search(/\S/));
+ var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
+ return Pos(start.line, inWS ? 0 : firstNonWS);
+ }
+ return start;
+ }
function compareBidiLevel(order, a, b) {
var linedir = order[0].level;
@@ -7333,7 +8039,7 @@
// THE END
- CodeMirror.version = "4.0.3";
+ CodeMirror.version = "4.12.0";
return CodeMirror;
});
diff --git a/applications/admin/static/codemirror/mode/clike/clike.js b/applications/admin/static/codemirror/mode/clike/clike.js
index f6626cd0..b04b22b1 100644
--- a/applications/admin/static/codemirror/mode/clike/clike.js
+++ b/applications/admin/static/codemirror/mode/clike/clike.js
@@ -1,3 +1,16 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
CodeMirror.defineMode("clike", function(config, parserConfig) {
var indentUnit = config.indentUnit,
statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
@@ -7,7 +20,8 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
blockKeywords = parserConfig.blockKeywords || {},
atoms = parserConfig.atoms || {},
hooks = parserConfig.hooks || {},
- multiLineStrings = parserConfig.multiLineStrings;
+ multiLineStrings = parserConfig.multiLineStrings,
+ indentStatements = parserConfig.indentStatements !== false;
var isOperatorChar = /[+\-*&%=<>!?|\/]/;
var curPunc;
@@ -44,7 +58,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
stream.eatWhile(isOperatorChar);
return "operator";
}
- stream.eatWhile(/[\w\$_]/);
+ stream.eatWhile(/[\w\$_\xa1-\uffff]/);
var cur = stream.current();
if (keywords.propertyIsEnumerable(cur)) {
if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
@@ -138,7 +152,9 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
while (ctx.type == "statement") ctx = popContext(state);
}
else if (curPunc == ctx.type) popContext(state);
- else if (((ctx.type == "}" || ctx.type == "top") && curPunc != ';') || (ctx.type == "statement" && curPunc == "newstatement"))
+ else if (indentStatements &&
+ (((ctx.type == "}" || ctx.type == "top") && curPunc != ';') ||
+ (ctx.type == "statement" && curPunc == "newstatement")))
pushContext(state, stream.column(), "statement");
state.startOfLine = false;
return style;
@@ -163,7 +179,6 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
};
});
-(function() {
function words(str) {
var obj = {}, words = str.split(" ");
for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
@@ -191,6 +206,30 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
return "meta";
}
+ function cpp11StringHook(stream, state) {
+ stream.backUp(1);
+ // Raw strings.
+ if (stream.match(/(R|u8R|uR|UR|LR)/)) {
+ var match = stream.match(/"([^\s\\()]{0,16})\(/);
+ if (!match) {
+ return false;
+ }
+ state.cpp11RawStringDelim = match[1];
+ state.tokenize = tokenRawString;
+ return tokenRawString(stream, state);
+ }
+ // Unicode strings/chars.
+ if (stream.match(/(u8|u|U|L)/)) {
+ if (stream.match(/["']/, /* eat */ false)) {
+ return "string";
+ }
+ return false;
+ }
+ // Ignore this hook.
+ stream.next();
+ return false;
+ }
+
// C#-style strings where "" escapes a quote.
function tokenAtString(stream, state) {
var next;
@@ -203,28 +242,67 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
return "string";
}
- function mimes(ms, mode) {
- for (var i = 0; i < ms.length; ++i) CodeMirror.defineMIME(ms[i], mode);
+ // C++11 raw string literal is "( anything )", where
+ // can be a string up to 16 characters long.
+ function tokenRawString(stream, state) {
+ // Escape characters that have special regex meanings.
+ var delim = state.cpp11RawStringDelim.replace(/[^\w\s]/g, '\\$&');
+ var match = stream.match(new RegExp(".*?\\)" + delim + '"'));
+ if (match)
+ state.tokenize = null;
+ else
+ stream.skipToEnd();
+ return "string";
}
- mimes(["text/x-csrc", "text/x-c", "text/x-chdr"], {
+ function def(mimes, mode) {
+ if (typeof mimes == "string") mimes = [mimes];
+ var words = [];
+ function add(obj) {
+ if (obj) for (var prop in obj) if (obj.hasOwnProperty(prop))
+ words.push(prop);
+ }
+ add(mode.keywords);
+ add(mode.builtin);
+ add(mode.atoms);
+ if (words.length) {
+ mode.helperType = mimes[0];
+ CodeMirror.registerHelper("hintWords", mimes[0], words);
+ }
+
+ for (var i = 0; i < mimes.length; ++i)
+ CodeMirror.defineMIME(mimes[i], mode);
+ }
+
+ def(["text/x-csrc", "text/x-c", "text/x-chdr"], {
name: "clike",
keywords: words(cKeywords),
blockKeywords: words("case do else for if switch while struct"),
atoms: words("null"),
- hooks: {"#": cppHook}
+ hooks: {"#": cppHook},
+ modeProps: {fold: ["brace", "include"]}
});
- mimes(["text/x-c++src", "text/x-c++hdr"], {
+
+ def(["text/x-c++src", "text/x-c++hdr"], {
name: "clike",
keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try bool explicit new " +
"static_cast typeid catch operator template typename class friend private " +
"this using const_cast inline public throw virtual delete mutable protected " +
- "wchar_t"),
+ "wchar_t alignas alignof constexpr decltype nullptr noexcept thread_local final " +
+ "static_assert override"),
blockKeywords: words("catch class do else finally for if struct switch try while"),
atoms: words("true false null"),
- hooks: {"#": cppHook}
+ hooks: {
+ "#": cppHook,
+ "u": cpp11StringHook,
+ "U": cpp11StringHook,
+ "L": cpp11StringHook,
+ "R": cpp11StringHook
+ },
+ modeProps: {fold: ["brace", "include"]}
});
- CodeMirror.defineMIME("text/x-java", {
+
+ def("text/x-java", {
name: "clike",
keywords: words("abstract assert boolean break byte case catch char class const continue default " +
"do double else enum extends final finally float for goto if implements import " +
@@ -238,9 +316,11 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
stream.eatWhile(/[\w\$_]/);
return "meta";
}
- }
+ },
+ modeProps: {fold: ["brace", "import"]}
});
- CodeMirror.defineMIME("text/x-csharp", {
+
+ def("text/x-csharp", {
name: "clike",
keywords: words("abstract as base break case catch checked class const continue" +
" default delegate do else enum event explicit extern finally fixed for" +
@@ -266,7 +346,20 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
}
}
});
- CodeMirror.defineMIME("text/x-scala", {
+
+ function tokenTripleString(stream, state) {
+ var escaped = false;
+ while (!stream.eol()) {
+ if (!escaped && stream.match('"""')) {
+ state.tokenize = null;
+ break;
+ }
+ escaped = stream.next() != "\\" && !escaped;
+ }
+ return "string";
+ }
+
+ def("text/x-scala", {
name: "clike",
keywords: words(
@@ -291,25 +384,31 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
"Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
"Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
"StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
-
-
),
+ multiLineStrings: true,
blockKeywords: words("catch class do else finally for forSome if match switch try while"),
atoms: words("true false null"),
+ indentStatements: false,
hooks: {
"@": function(stream) {
stream.eatWhile(/[\w\$_]/);
return "meta";
+ },
+ '"': function(stream, state) {
+ if (!stream.match('""')) return false;
+ state.tokenize = tokenTripleString;
+ return state.tokenize(stream, state);
}
}
});
- mimes(["x-shader/x-vertex", "x-shader/x-fragment"], {
+
+ def(["x-shader/x-vertex", "x-shader/x-fragment"], {
name: "clike",
keywords: words("float int bool void " +
"vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 " +
"mat2 mat3 mat4 " +
"sampler1D sampler2D sampler3D samplerCube " +
- "sampler1DShadow sampler2DShadow" +
+ "sampler1DShadow sampler2DShadow " +
"const attribute uniform varying " +
"break continue discard return " +
"for while do if else struct " +
@@ -317,7 +416,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
blockKeywords: words("for while do if else struct"),
builtin: words("radians degrees sin cos tan asin acos atan " +
"pow exp log exp2 sqrt inversesqrt " +
- "abs sign floor ceil fract mod min max clamp mix step smootstep " +
+ "abs sign floor ceil fract mod min max clamp mix step smoothstep " +
"length distance dot cross normalize ftransform faceforward " +
"reflect refract matrixCompMult " +
"lessThan lessThanEqual greaterThan greaterThanEqual " +
@@ -334,12 +433,12 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
"gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex " +
"gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 " +
"gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 " +
- "gl_FogCoord " +
+ "gl_FogCoord gl_PointCoord " +
"gl_Position gl_PointSize gl_ClipVertex " +
"gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor " +
"gl_TexCoord gl_FogFragCoord " +
"gl_FragCoord gl_FrontFacing " +
- "gl_FragColor gl_FragData gl_FragDepth " +
+ "gl_FragData gl_FragDepth " +
"gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix " +
"gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse " +
"gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse " +
@@ -357,6 +456,34 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
"gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits " +
"gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits " +
"gl_MaxDrawBuffers"),
- hooks: {"#": cppHook}
+ hooks: {"#": cppHook},
+ modeProps: {fold: ["brace", "include"]}
});
-}());
+
+ def("text/x-nesc", {
+ name: "clike",
+ keywords: words(cKeywords + "as atomic async call command component components configuration event generic " +
+ "implementation includes interface module new norace nx_struct nx_union post provides " +
+ "signal task uses abstract extends"),
+ blockKeywords: words("case do else for if switch while struct"),
+ atoms: words("null"),
+ hooks: {"#": cppHook},
+ modeProps: {fold: ["brace", "include"]}
+ });
+
+ def("text/x-objectivec", {
+ name: "clike",
+ keywords: words(cKeywords + "inline restrict _Bool _Complex _Imaginery BOOL Class bycopy byref id IMP in " +
+ "inout nil oneway out Protocol SEL self super atomic nonatomic retain copy readwrite readonly"),
+ atoms: words("YES NO NULL NILL ON OFF"),
+ hooks: {
+ "@": function(stream) {
+ stream.eatWhile(/[\w\$]/);
+ return "keyword";
+ },
+ "#": cppHook
+ },
+ modeProps: {fold: "brace"}
+ });
+
+});
diff --git a/applications/admin/static/codemirror/mode/clike/index.html b/applications/admin/static/codemirror/mode/clike/index.html
index 45add491..8b386d22 100644
--- a/applications/admin/static/codemirror/mode/clike/index.html
+++ b/applications/admin/static/codemirror/mode/clike/index.html
@@ -7,15 +7,17 @@
+
+
-
+
CodeMirror
Language modes
@@ -105,6 +107,11 @@ enum Enum {
VAL1, VAL2, VAL3
};
+char32_t unicode_string = U"\U0010FFFF";
+string raw_string = R"delim(anything
+you
+want)delim";
+
int Helper(const MyType& param) {
return 0;
}
@@ -132,6 +139,26 @@ void Class::Method2(MyType* value) {
}
+Objective-C example
+
+
+
Java example
+
+Scala example
+
+
Simple mode that tries to handle C-like languages as well as it
@@ -189,7 +243,9 @@ public class Class implements MyInterface {
directives are recognized.
MIME types defined: text/x-csrc
- (C code), text/x-c++src (C++
- code), text/x-java (Java
- code), text/x-csharp (C#).
+ (C), text/x-c++src (C++), text/x-java
+ (Java), text/x-csharp (C#),
+ text/x-objectivec (Objective-C),
+ text/x-scala (Scala), text/x-vertex
+ and x-shader/x-fragment (shader programs).
diff --git a/applications/admin/static/codemirror/mode/clike/scala.html b/applications/admin/static/codemirror/mode/clike/scala.html
index e9acc049..aa04cf0f 100644
--- a/applications/admin/static/codemirror/mode/clike/scala.html
+++ b/applications/admin/static/codemirror/mode/clike/scala.html
@@ -10,12 +10,12 @@
-
+
CodeMirror
Language modes
diff --git a/applications/admin/static/codemirror/mode/clojure/clojure.js b/applications/admin/static/codemirror/mode/clojure/clojure.js
index ee22a12f..c334de73 100644
--- a/applications/admin/static/codemirror/mode/clojure/clojure.js
+++ b/applications/admin/static/codemirror/mode/clojure/clojure.js
@@ -1,11 +1,26 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
/**
* Author: Hans Engel
* Branched from CodeMirror's Scheme mode (by Koh Zi Han, based on implementation by Koh Zi Chun)
*/
-CodeMirror.defineMode("clojure", function () {
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.defineMode("clojure", function (options) {
var BUILTIN = "builtin", COMMENT = "comment", STRING = "string", CHARACTER = "string-2",
- ATOM = "atom", NUMBER = "number", BRACKET = "bracket", KEYWORD = "keyword";
- var INDENT_WORD_SKIP = 2;
+ ATOM = "atom", NUMBER = "number", BRACKET = "bracket", KEYWORD = "keyword", VAR = "variable";
+ var INDENT_WORD_SKIP = options.indentUnit || 2;
+ var NORMAL_INDENT_UNIT = options.indentUnit || 2;
function makeKeywords(str) {
var obj = {}, words = str.split(" ");
@@ -44,7 +59,7 @@ CodeMirror.defineMode("clojure", function () {
sign: /[+-]/,
exponent: /e/i,
keyword_char: /[^\s\(\[\;\)\]]/,
- symbol: /[\w*+!\-\._?:\/]/
+ symbol: /[\w*+!\-\._?:<>\/\xa1-\uffff]/
};
function stateStack(indent, type, prev) { // represents a state stack object
@@ -99,7 +114,7 @@ CodeMirror.defineMode("clojure", function () {
var first = stream.next();
// Read special literals: backspace, newline, space, return.
// Just read all lowercase letters.
- if (first.match(/[a-z]/) && stream.match(/[a-z]+/, true)) {
+ if (first && first.match(/[a-z]/) && stream.match(/[a-z]+/, true)) {
return;
}
// Read unicode character: \u1000 \uA0a1
@@ -179,8 +194,8 @@ CodeMirror.defineMode("clojure", function () {
stream.eatSpace();
if (stream.eol() || stream.peek() == ";") {
// nothing significant after
- // we restart indentation 1 space after
- pushStack(state, indentTemp + 1, ch);
+ // we restart indentation the user defined spaces after
+ pushStack(state, indentTemp + NORMAL_INDENT_UNIT, ch);
} else {
pushStack(state, indentTemp + stream.current().length, ch); // else we match
}
@@ -205,7 +220,9 @@ CodeMirror.defineMode("clojure", function () {
returnType = BUILTIN;
} else if (atoms && atoms.propertyIsEnumerable(stream.current())) {
returnType = ATOM;
- } else returnType = null;
+ } else {
+ returnType = VAR;
+ }
}
}
@@ -222,3 +239,5 @@ CodeMirror.defineMode("clojure", function () {
});
CodeMirror.defineMIME("text/x-clojure", "clojure");
+
+});
diff --git a/applications/admin/static/codemirror/mode/clojure/index.html b/applications/admin/static/codemirror/mode/clojure/index.html
index 5a50c566..3ecf4c48 100644
--- a/applications/admin/static/codemirror/mode/clojure/index.html
+++ b/applications/admin/static/codemirror/mode/clojure/index.html
@@ -9,12 +9,12 @@
-
+
CodeMirror
Language modes
diff --git a/applications/admin/static/codemirror/mode/coffeescript/LICENSE b/applications/admin/static/codemirror/mode/coffeescript/LICENSE
deleted file mode 100644
index 977e284e..00000000
--- a/applications/admin/static/codemirror/mode/coffeescript/LICENSE
+++ /dev/null
@@ -1,22 +0,0 @@
-The MIT License
-
-Copyright (c) 2011 Jeff Pickhardt
-Modified from the Python CodeMirror mode, Copyright (c) 2010 Timothy Farrell
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
\ No newline at end of file
diff --git a/applications/admin/static/codemirror/mode/coffeescript/coffeescript.js b/applications/admin/static/codemirror/mode/coffeescript/coffeescript.js
index b7203f12..da0eb2d5 100644
--- a/applications/admin/static/codemirror/mode/coffeescript/coffeescript.js
+++ b/applications/admin/static/codemirror/mode/coffeescript/coffeescript.js
@@ -1,348 +1,369 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
/**
* Link to the project's GitHub page:
* https://github.com/pickhardt/coffeescript-codemirror-mode
*/
-CodeMirror.defineMode('coffeescript', function(conf) {
- var ERRORCLASS = 'error';
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
- function wordRegexp(words) {
- return new RegExp("^((" + words.join(")|(") + "))\\b");
+CodeMirror.defineMode("coffeescript", function(conf, parserConf) {
+ var ERRORCLASS = "error";
+
+ function wordRegexp(words) {
+ return new RegExp("^((" + words.join(")|(") + "))\\b");
+ }
+
+ var operators = /^(?:->|=>|\+[+=]?|-[\-=]?|\*[\*=]?|\/[\/=]?|[=!]=|<[><]?=?|>>?=?|%=?|&=?|\|=?|\^=?|\~|!|\?|(or|and|\|\||&&|\?)=)/;
+ var delimiters = /^(?:[()\[\]{},:`=;]|\.\.?\.?)/;
+ var identifiers = /^[_A-Za-z$][_A-Za-z$0-9]*/;
+ var properties = /^(@|this\.)[_A-Za-z$][_A-Za-z$0-9]*/;
+
+ var wordOperators = wordRegexp(["and", "or", "not",
+ "is", "isnt", "in",
+ "instanceof", "typeof"]);
+ var indentKeywords = ["for", "while", "loop", "if", "unless", "else",
+ "switch", "try", "catch", "finally", "class"];
+ var commonKeywords = ["break", "by", "continue", "debugger", "delete",
+ "do", "in", "of", "new", "return", "then",
+ "this", "@", "throw", "when", "until", "extends"];
+
+ var keywords = wordRegexp(indentKeywords.concat(commonKeywords));
+
+ indentKeywords = wordRegexp(indentKeywords);
+
+
+ var stringPrefixes = /^('{3}|\"{3}|['\"])/;
+ var regexPrefixes = /^(\/{3}|\/)/;
+ var commonConstants = ["Infinity", "NaN", "undefined", "null", "true", "false", "on", "off", "yes", "no"];
+ var constants = wordRegexp(commonConstants);
+
+ // Tokenizers
+ function tokenBase(stream, state) {
+ // Handle scope changes
+ if (stream.sol()) {
+ if (state.scope.align === null) state.scope.align = false;
+ var scopeOffset = state.scope.offset;
+ if (stream.eatSpace()) {
+ var lineOffset = stream.indentation();
+ if (lineOffset > scopeOffset && state.scope.type == "coffee") {
+ return "indent";
+ } else if (lineOffset < scopeOffset) {
+ return "dedent";
+ }
+ return null;
+ } else {
+ if (scopeOffset > 0) {
+ dedent(stream, state);
+ }
+ }
+ }
+ if (stream.eatSpace()) {
+ return null;
}
- var singleOperators = new RegExp("^[\\+\\-\\*/%&|\\^~<>!\?]");
- var singleDelimiters = new RegExp('^[\\(\\)\\[\\]\\{\\},:`=;\\.]');
- var doubleOperators = new RegExp("^((\->)|(\=>)|(\\+\\+)|(\\+\\=)|(\\-\\-)|(\\-\\=)|(\\*\\*)|(\\*\\=)|(\\/\\/)|(\\/\\=)|(==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//))");
- var doubleDelimiters = new RegExp("^((\\.\\.)|(\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))");
- var tripleDelimiters = new RegExp("^((\\.\\.\\.)|(//=)|(>>=)|(<<=)|(\\*\\*=))");
- var identifiers = new RegExp("^[_A-Za-z$][_A-Za-z$0-9]*");
- var properties = new RegExp("^(@|this\.)[_A-Za-z$][_A-Za-z$0-9]*");
+ var ch = stream.peek();
- var wordOperators = wordRegexp(['and', 'or', 'not',
- 'is', 'isnt', 'in',
- 'instanceof', 'typeof']);
- var indentKeywords = ['for', 'while', 'loop', 'if', 'unless', 'else',
- 'switch', 'try', 'catch', 'finally', 'class'];
- var commonKeywords = ['break', 'by', 'continue', 'debugger', 'delete',
- 'do', 'in', 'of', 'new', 'return', 'then',
- 'this', 'throw', 'when', 'until'];
-
- var keywords = wordRegexp(indentKeywords.concat(commonKeywords));
-
- indentKeywords = wordRegexp(indentKeywords);
-
-
- var stringPrefixes = new RegExp("^('{3}|\"{3}|['\"])");
- var regexPrefixes = new RegExp("^(/{3}|/)");
- var commonConstants = ['Infinity', 'NaN', 'undefined', 'null', 'true', 'false', 'on', 'off', 'yes', 'no'];
- var constants = wordRegexp(commonConstants);
-
- // Tokenizers
- function tokenBase(stream, state) {
- // Handle scope changes
- if (stream.sol()) {
- var scopeOffset = state.scopes[0].offset;
- if (stream.eatSpace()) {
- var lineOffset = stream.indentation();
- if (lineOffset > scopeOffset) {
- return 'indent';
- } else if (lineOffset < scopeOffset) {
- return 'dedent';
- }
- return null;
- } else {
- if (scopeOffset > 0) {
- dedent(stream, state);
- }
- }
- }
- if (stream.eatSpace()) {
- return null;
- }
-
- var ch = stream.peek();
-
- // Handle docco title comment (single line)
- if (stream.match("####")) {
- stream.skipToEnd();
- return 'comment';
- }
-
- // Handle multi line comments
- if (stream.match("###")) {
- state.tokenize = longComment;
- return state.tokenize(stream, state);
- }
-
- // Single line comment
- if (ch === '#') {
- stream.skipToEnd();
- return 'comment';
- }
-
- // Handle number literals
- if (stream.match(/^-?[0-9\.]/, false)) {
- var floatLiteral = false;
- // Floats
- if (stream.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i)) {
- floatLiteral = true;
- }
- if (stream.match(/^-?\d+\.\d*/)) {
- floatLiteral = true;
- }
- if (stream.match(/^-?\.\d+/)) {
- floatLiteral = true;
- }
-
- if (floatLiteral) {
- // prevent from getting extra . on 1..
- if (stream.peek() == "."){
- stream.backUp(1);
- }
- return 'number';
- }
- // Integers
- var intLiteral = false;
- // Hex
- if (stream.match(/^-?0x[0-9a-f]+/i)) {
- intLiteral = true;
- }
- // Decimal
- if (stream.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/)) {
- intLiteral = true;
- }
- // Zero by itself with no other piece of number.
- if (stream.match(/^-?0(?![\dx])/i)) {
- intLiteral = true;
- }
- if (intLiteral) {
- return 'number';
- }
- }
-
- // Handle strings
- if (stream.match(stringPrefixes)) {
- state.tokenize = tokenFactory(stream.current(), 'string');
- return state.tokenize(stream, state);
- }
- // Handle regex literals
- if (stream.match(regexPrefixes)) {
- if (stream.current() != '/' || stream.match(/^.*\//, false)) { // prevent highlight of division
- state.tokenize = tokenFactory(stream.current(), 'string-2');
- return state.tokenize(stream, state);
- } else {
- stream.backUp(1);
- }
- }
-
- // Handle operators and delimiters
- if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) {
- return 'punctuation';
- }
- if (stream.match(doubleOperators)
- || stream.match(singleOperators)
- || stream.match(wordOperators)) {
- return 'operator';
- }
- if (stream.match(singleDelimiters)) {
- return 'punctuation';
- }
-
- if (stream.match(constants)) {
- return 'atom';
- }
-
- if (stream.match(keywords)) {
- return 'keyword';
- }
-
- if (stream.match(identifiers)) {
- return 'variable';
- }
-
- if (stream.match(properties)) {
- return 'property';
- }
-
- // Handle non-detected items
- stream.next();
- return ERRORCLASS;
+ // Handle docco title comment (single line)
+ if (stream.match("####")) {
+ stream.skipToEnd();
+ return "comment";
}
- function tokenFactory(delimiter, outclass) {
- var singleline = delimiter.length == 1;
- return function(stream, state) {
- while (!stream.eol()) {
- stream.eatWhile(/[^'"\/\\]/);
- if (stream.eat('\\')) {
- stream.next();
- if (singleline && stream.eol()) {
- return outclass;
- }
- } else if (stream.match(delimiter)) {
- state.tokenize = tokenBase;
- return outclass;
- } else {
- stream.eat(/['"\/]/);
- }
- }
- if (singleline) {
- if (conf.mode.singleLineStringErrors) {
- outclass = ERRORCLASS;
- } else {
- state.tokenize = tokenBase;
- }
- }
+ // Handle multi line comments
+ if (stream.match("###")) {
+ state.tokenize = longComment;
+ return state.tokenize(stream, state);
+ }
+
+ // Single line comment
+ if (ch === "#") {
+ stream.skipToEnd();
+ return "comment";
+ }
+
+ // Handle number literals
+ if (stream.match(/^-?[0-9\.]/, false)) {
+ var floatLiteral = false;
+ // Floats
+ if (stream.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i)) {
+ floatLiteral = true;
+ }
+ if (stream.match(/^-?\d+\.\d*/)) {
+ floatLiteral = true;
+ }
+ if (stream.match(/^-?\.\d+/)) {
+ floatLiteral = true;
+ }
+
+ if (floatLiteral) {
+ // prevent from getting extra . on 1..
+ if (stream.peek() == "."){
+ stream.backUp(1);
+ }
+ return "number";
+ }
+ // Integers
+ var intLiteral = false;
+ // Hex
+ if (stream.match(/^-?0x[0-9a-f]+/i)) {
+ intLiteral = true;
+ }
+ // Decimal
+ if (stream.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/)) {
+ intLiteral = true;
+ }
+ // Zero by itself with no other piece of number.
+ if (stream.match(/^-?0(?![\dx])/i)) {
+ intLiteral = true;
+ }
+ if (intLiteral) {
+ return "number";
+ }
+ }
+
+ // Handle strings
+ if (stream.match(stringPrefixes)) {
+ state.tokenize = tokenFactory(stream.current(), false, "string");
+ return state.tokenize(stream, state);
+ }
+ // Handle regex literals
+ if (stream.match(regexPrefixes)) {
+ if (stream.current() != "/" || stream.match(/^.*\//, false)) { // prevent highlight of division
+ state.tokenize = tokenFactory(stream.current(), true, "string-2");
+ return state.tokenize(stream, state);
+ } else {
+ stream.backUp(1);
+ }
+ }
+
+ // Handle operators and delimiters
+ if (stream.match(operators) || stream.match(wordOperators)) {
+ return "operator";
+ }
+ if (stream.match(delimiters)) {
+ return "punctuation";
+ }
+
+ if (stream.match(constants)) {
+ return "atom";
+ }
+
+ if (stream.match(keywords)) {
+ return "keyword";
+ }
+
+ if (stream.match(identifiers)) {
+ return "variable";
+ }
+
+ if (stream.match(properties)) {
+ return "property";
+ }
+
+ // Handle non-detected items
+ stream.next();
+ return ERRORCLASS;
+ }
+
+ function tokenFactory(delimiter, singleline, outclass) {
+ return function(stream, state) {
+ while (!stream.eol()) {
+ stream.eatWhile(/[^'"\/\\]/);
+ if (stream.eat("\\")) {
+ stream.next();
+ if (singleline && stream.eol()) {
return outclass;
- };
- }
-
- function longComment(stream, state) {
- while (!stream.eol()) {
- stream.eatWhile(/[^#]/);
- if (stream.match("###")) {
- state.tokenize = tokenBase;
- break;
- }
- stream.eatWhile("#");
- }
- return "comment";
- }
-
- function indent(stream, state, type) {
- type = type || 'coffee';
- var indentUnit = 0;
- if (type === 'coffee') {
- for (var i = 0; i < state.scopes.length; i++) {
- if (state.scopes[i].type === 'coffee') {
- indentUnit = state.scopes[i].offset + conf.indentUnit;
- break;
- }
- }
+ }
+ } else if (stream.match(delimiter)) {
+ state.tokenize = tokenBase;
+ return outclass;
} else {
- indentUnit = stream.column() + stream.current().length;
+ stream.eat(/['"\/]/);
}
- state.scopes.unshift({
- offset: indentUnit,
- type: type
- });
- }
-
- function dedent(stream, state) {
- if (state.scopes.length == 1) return;
- if (state.scopes[0].type === 'coffee') {
- var _indent = stream.indentation();
- var _indent_index = -1;
- for (var i = 0; i < state.scopes.length; ++i) {
- if (_indent === state.scopes[i].offset) {
- _indent_index = i;
- break;
- }
- }
- if (_indent_index === -1) {
- return true;
- }
- while (state.scopes[0].offset !== _indent) {
- state.scopes.shift();
- }
- return false;
+ }
+ if (singleline) {
+ if (parserConf.singleLineStringErrors) {
+ outclass = ERRORCLASS;
} else {
- state.scopes.shift();
- return false;
+ state.tokenize = tokenBase;
}
- }
-
- function tokenLexer(stream, state) {
- var style = state.tokenize(stream, state);
- var current = stream.current();
-
- // Handle '.' connected identifiers
- if (current === '.') {
- style = state.tokenize(stream, state);
- current = stream.current();
- if (/^\.[\w$]+$/.test(current)) {
- return 'variable';
- } else {
- return ERRORCLASS;
- }
- }
-
- // Handle scope changes.
- if (current === 'return') {
- state.dedent += 1;
- }
- if (((current === '->' || current === '=>') &&
- !state.lambda &&
- state.scopes[0].type == 'coffee' &&
- stream.peek() === '')
- || style === 'indent') {
- indent(stream, state);
- }
- var delimiter_index = '[({'.indexOf(current);
- if (delimiter_index !== -1) {
- indent(stream, state, '])}'.slice(delimiter_index, delimiter_index+1));
- }
- if (indentKeywords.exec(current)){
- indent(stream, state);
- }
- if (current == 'then'){
- dedent(stream, state);
- }
-
-
- if (style === 'dedent') {
- if (dedent(stream, state)) {
- return ERRORCLASS;
- }
- }
- delimiter_index = '])}'.indexOf(current);
- if (delimiter_index !== -1) {
- if (dedent(stream, state)) {
- return ERRORCLASS;
- }
- }
- if (state.dedent > 0 && stream.eol() && state.scopes[0].type == 'coffee') {
- if (state.scopes.length > 1) state.scopes.shift();
- state.dedent -= 1;
- }
-
- return style;
- }
-
- var external = {
- startState: function(basecolumn) {
- return {
- tokenize: tokenBase,
- scopes: [{offset:basecolumn || 0, type:'coffee'}],
- lastToken: null,
- lambda: false,
- dedent: 0
- };
- },
-
- token: function(stream, state) {
- var style = tokenLexer(stream, state);
-
- state.lastToken = {style:style, content: stream.current()};
-
- if (stream.eol() && stream.lambda) {
- state.lambda = false;
- }
-
- return style;
- },
-
- indent: function(state) {
- if (state.tokenize != tokenBase) {
- return 0;
- }
-
- return state.scopes[0].offset;
- },
-
- lineComment: "#",
- fold: "indent"
+ }
+ return outclass;
};
- return external;
+ }
+
+ function longComment(stream, state) {
+ while (!stream.eol()) {
+ stream.eatWhile(/[^#]/);
+ if (stream.match("###")) {
+ state.tokenize = tokenBase;
+ break;
+ }
+ stream.eatWhile("#");
+ }
+ return "comment";
+ }
+
+ function indent(stream, state, type) {
+ type = type || "coffee";
+ var offset = 0, align = false, alignOffset = null;
+ for (var scope = state.scope; scope; scope = scope.prev) {
+ if (scope.type === "coffee" || scope.type == "}") {
+ offset = scope.offset + conf.indentUnit;
+ break;
+ }
+ }
+ if (type !== "coffee") {
+ align = null;
+ alignOffset = stream.column() + stream.current().length;
+ } else if (state.scope.align) {
+ state.scope.align = false;
+ }
+ state.scope = {
+ offset: offset,
+ type: type,
+ prev: state.scope,
+ align: align,
+ alignOffset: alignOffset
+ };
+ }
+
+ function dedent(stream, state) {
+ if (!state.scope.prev) return;
+ if (state.scope.type === "coffee") {
+ var _indent = stream.indentation();
+ var matched = false;
+ for (var scope = state.scope; scope; scope = scope.prev) {
+ if (_indent === scope.offset) {
+ matched = true;
+ break;
+ }
+ }
+ if (!matched) {
+ return true;
+ }
+ while (state.scope.prev && state.scope.offset !== _indent) {
+ state.scope = state.scope.prev;
+ }
+ return false;
+ } else {
+ state.scope = state.scope.prev;
+ return false;
+ }
+ }
+
+ function tokenLexer(stream, state) {
+ var style = state.tokenize(stream, state);
+ var current = stream.current();
+
+ // Handle "." connected identifiers
+ if (current === ".") {
+ style = state.tokenize(stream, state);
+ current = stream.current();
+ if (/^\.[\w$]+$/.test(current)) {
+ return "variable";
+ } else {
+ return ERRORCLASS;
+ }
+ }
+
+ // Handle scope changes.
+ if (current === "return") {
+ state.dedent = true;
+ }
+ if (((current === "->" || current === "=>") &&
+ !state.lambda &&
+ !stream.peek())
+ || style === "indent") {
+ indent(stream, state);
+ }
+ var delimiter_index = "[({".indexOf(current);
+ if (delimiter_index !== -1) {
+ indent(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
+ }
+ if (indentKeywords.exec(current)){
+ indent(stream, state);
+ }
+ if (current == "then"){
+ dedent(stream, state);
+ }
+
+
+ if (style === "dedent") {
+ if (dedent(stream, state)) {
+ return ERRORCLASS;
+ }
+ }
+ delimiter_index = "])}".indexOf(current);
+ if (delimiter_index !== -1) {
+ while (state.scope.type == "coffee" && state.scope.prev)
+ state.scope = state.scope.prev;
+ if (state.scope.type == current)
+ state.scope = state.scope.prev;
+ }
+ if (state.dedent && stream.eol()) {
+ if (state.scope.type == "coffee" && state.scope.prev)
+ state.scope = state.scope.prev;
+ state.dedent = false;
+ }
+
+ return style;
+ }
+
+ var external = {
+ startState: function(basecolumn) {
+ return {
+ tokenize: tokenBase,
+ scope: {offset:basecolumn || 0, type:"coffee", prev: null, align: false},
+ lastToken: null,
+ lambda: false,
+ dedent: 0
+ };
+ },
+
+ token: function(stream, state) {
+ var fillAlign = state.scope.align === null && state.scope;
+ if (fillAlign && stream.sol()) fillAlign.align = false;
+
+ var style = tokenLexer(stream, state);
+ if (fillAlign && style && style != "comment") fillAlign.align = true;
+
+ state.lastToken = {style:style, content: stream.current()};
+
+ if (stream.eol() && stream.lambda) {
+ state.lambda = false;
+ }
+
+ return style;
+ },
+
+ indent: function(state, text) {
+ if (state.tokenize != tokenBase) return 0;
+ var scope = state.scope;
+ var closer = text && "])}".indexOf(text.charAt(0)) > -1;
+ if (closer) while (scope.type == "coffee" && scope.prev) scope = scope.prev;
+ var closes = closer && scope.type === text.charAt(0);
+ if (scope.align)
+ return scope.alignOffset - (closes ? 1 : 0);
+ else
+ return (closes ? scope.prev : scope).offset;
+ },
+
+ lineComment: "#",
+ fold: "indent"
+ };
+ return external;
});
-CodeMirror.defineMIME('text/x-coffeescript', 'coffeescript');
+CodeMirror.defineMIME("text/x-coffeescript", "coffeescript");
+
+});
diff --git a/applications/admin/static/codemirror/mode/coffeescript/index.html b/applications/admin/static/codemirror/mode/coffeescript/index.html
index 6e6fde52..93a5f4f3 100644
--- a/applications/admin/static/codemirror/mode/coffeescript/index.html
+++ b/applications/admin/static/codemirror/mode/coffeescript/index.html
@@ -9,12 +9,12 @@
-
+
CodeMirror
Language modes
@@ -735,6 +735,6 @@ wrapper::value = -> this._wrapped
MIME types defined: text/x-coffeescript.
- The CoffeeScript mode was written by Jeff Pickhardt (license ).
+ The CoffeeScript mode was written by Jeff Pickhardt.
diff --git a/applications/admin/static/codemirror/mode/css/css.js b/applications/admin/static/codemirror/mode/css/css.js
index f8fc5cea..3f02907e 100644
--- a/applications/admin/static/codemirror/mode/css/css.js
+++ b/applications/admin/static/codemirror/mode/css/css.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
@@ -16,6 +19,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
mediaTypes = parserConfig.mediaTypes || {},
mediaFeatures = parserConfig.mediaFeatures || {},
propertyKeywords = parserConfig.propertyKeywords || {},
+ nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
colorKeywords = parserConfig.colorKeywords || {},
valueKeywords = parserConfig.valueKeywords || {},
fontProperties = parserConfig.fontProperties || {},
@@ -53,7 +57,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
if (/[\d.]/.test(stream.peek())) {
stream.eatWhile(/[\w.%]/);
return ret("number", "unit");
- } else if (stream.match(/^[^-]+-/)) {
+ } else if (stream.match(/^\w+-/)) {
return ret("meta", "meta");
}
} else if (/[,+>*\/]/.test(ch)) {
@@ -91,7 +95,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
function tokenParenthesized(stream, state) {
stream.next(); // Must be '('
- if (!stream.match(/\s*[\"\']/, false))
+ if (!stream.match(/\s*[\"\')]/, false))
state.tokenize = tokenString(")");
else
state.tokenize = null;
@@ -163,18 +167,22 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
} else if (type == ":") {
return "pseudo";
} else if (allowNested && type == "(") {
- return pushContext(state, stream, "params");
+ return pushContext(state, stream, "parens");
}
return state.context.type;
};
states.block = function(type, stream, state) {
if (type == "word") {
- if (propertyKeywords.hasOwnProperty(stream.current().toLowerCase())) {
+ var word = stream.current().toLowerCase();
+ if (propertyKeywords.hasOwnProperty(word)) {
override = "property";
return "maybeprop";
+ } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
+ override = "string-2";
+ return "maybeprop";
} else if (allowNested) {
- override = stream.match(/^\s*:/, false) ? "property" : "tag";
+ override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag";
return "block";
} else {
override += " error";
@@ -220,6 +228,8 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
states.parens = function(type, stream, state) {
if (type == "{" || type == "}") return popAndPass(type, stream, state);
if (type == ")") return popContext(state);
+ if (type == "(") return pushContext(state, stream, "parens");
+ if (type == "word") wordAsValue(stream);
return "parens";
};
@@ -295,13 +305,6 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
return "interpolation";
};
- states.params = function(type, stream, state) {
- if (type == ")") return popContext(state);
- if (type == "{" || type == "}") return popAndPass(type, stream, state);
- if (type == "word") wordAsValue(stream);
- return "params";
- };
-
return {
startState: function(base) {
return {tokenize: null,
@@ -324,10 +327,10 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
indent: function(state, textAfter) {
var cx = state.context, ch = textAfter && textAfter.charAt(0);
var indent = cx.indent;
- if (cx.type == "prop" && ch == "}") cx = cx.prev;
+ if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev;
if (cx.prev &&
(ch == "}" && (cx.type == "block" || cx.type == "top" || cx.type == "interpolation" || cx.type == "font_face") ||
- ch == ")" && (cx.type == "parens" || cx.type == "params" || cx.type == "media_parens") ||
+ ch == ")" && (cx.type == "parens" || cx.type == "media_parens") ||
ch == "{" && (cx.type == "at" || cx.type == "media"))) {
indent = cx.indent - indentUnit;
cx = cx.prev;
@@ -417,7 +420,8 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"marker-offset", "marks", "marquee-direction", "marquee-loop",
"marquee-play-count", "marquee-speed", "marquee-style", "max-height",
"max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
- "nav-left", "nav-right", "nav-up", "opacity", "order", "orphans", "outline",
+ "nav-left", "nav-right", "nav-up", "object-fit", "object-position",
+ "opacity", "order", "orphans", "outline",
"outline-color", "outline-offset", "outline-style", "outline-width",
"overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
"padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
@@ -428,8 +432,8 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"region-break-before", "region-break-inside", "region-fragment",
"rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness",
"right", "rotation", "rotation-point", "ruby-align", "ruby-overhang",
- "ruby-position", "ruby-span", "shape-inside", "shape-outside", "size",
- "speak", "speak-as", "speak-header",
+ "ruby-position", "ruby-span", "shape-image-threshold", "shape-inside", "shape-margin",
+ "shape-outside", "size", "speak", "speak-as", "speak-header",
"speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
"tab-size", "table-layout", "target", "target-name", "target-new",
"target-position", "text-align", "text-align-last", "text-decoration",
@@ -444,19 +448,27 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"vertical-align", "visibility", "voice-balance", "voice-duration",
"voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
"voice-volume", "volume", "white-space", "widows", "width", "word-break",
- "word-spacing", "word-wrap", "z-index", "zoom",
+ "word-spacing", "word-wrap", "z-index",
// SVG-specific
"clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
"flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
- "color-interpolation", "color-interpolation-filters", "color-profile",
+ "color-interpolation", "color-interpolation-filters",
"color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
"marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
"stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
"stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
"baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
- "glyph-orientation-vertical", "kerning", "text-anchor", "writing-mode"
+ "glyph-orientation-vertical", "text-anchor", "writing-mode"
], propertyKeywords = keySet(propertyKeywords_);
+ var nonStandardPropertyKeywords_ = [
+ "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color",
+ "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color",
+ "scrollbar-3d-light-color", "scrollbar-track-color", "shape-inside",
+ "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button",
+ "searchfield-results-decoration", "zoom"
+ ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);
+
var colorKeywords_ = [
"aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
"bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
@@ -479,8 +491,8 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
"orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
"papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
- "purple", "red", "rosybrown", "royalblue", "saddlebrown", "salmon",
- "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
+ "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown",
+ "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
"slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
"teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
"whitesmoke", "yellow", "yellowgreen"
@@ -513,14 +525,14 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
"ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et",
"ethiopic-halehame-tig", "ew-resize", "expanded", "extra-condensed",
- "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes",
+ "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "footnotes",
"forwards", "from", "geometricPrecision", "georgian", "graytext", "groove",
"gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew",
"help", "hidden", "hide", "higher", "highlight", "highlighttext",
"hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore",
"inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
"infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
- "inline-block", "inline-table", "inset", "inside", "intrinsic", "invert",
+ "inline-block", "inline-flex", "inline-table", "inset", "inside", "intrinsic", "invert",
"italic", "justify", "kannada", "katakana", "katakana-iroha", "keep-all", "khmer",
"landscape", "lao", "large", "larger", "left", "level", "lighter",
"line-through", "linear", "lines", "list-item", "listbox", "listitem",
@@ -576,7 +588,8 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"font-stretch", "font-weight", "font-style"
], fontProperties = keySet(fontProperties_);
- var allWords = mediaTypes_.concat(mediaFeatures_).concat(propertyKeywords_).concat(colorKeywords_).concat(valueKeywords_);
+ var allWords = mediaTypes_.concat(mediaFeatures_).concat(propertyKeywords_)
+ .concat(nonStandardPropertyKeywords_).concat(colorKeywords_).concat(valueKeywords_);
CodeMirror.registerHelper("hintWords", "css", allWords);
function tokenCComment(stream, state) {
@@ -605,6 +618,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
mediaTypes: mediaTypes,
mediaFeatures: mediaFeatures,
propertyKeywords: propertyKeywords,
+ nonStandardPropertyKeywords: nonStandardPropertyKeywords,
colorKeywords: colorKeywords,
valueKeywords: valueKeywords,
fontProperties: fontProperties,
@@ -627,6 +641,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
mediaTypes: mediaTypes,
mediaFeatures: mediaFeatures,
propertyKeywords: propertyKeywords,
+ nonStandardPropertyKeywords: nonStandardPropertyKeywords,
colorKeywords: colorKeywords,
valueKeywords: valueKeywords,
fontProperties: fontProperties,
@@ -644,7 +659,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
}
},
":": function(stream) {
- if (stream.match(/\s*{/))
+ if (stream.match(/\s*\{/))
return [null, "{"];
return false;
},
@@ -667,6 +682,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
mediaTypes: mediaTypes,
mediaFeatures: mediaFeatures,
propertyKeywords: propertyKeywords,
+ nonStandardPropertyKeywords: nonStandardPropertyKeywords,
colorKeywords: colorKeywords,
valueKeywords: valueKeywords,
fontProperties: fontProperties,
diff --git a/applications/admin/static/codemirror/mode/css/index.html b/applications/admin/static/codemirror/mode/css/index.html
new file mode 100644
index 00000000..2d2b9b07
--- /dev/null
+++ b/applications/admin/static/codemirror/mode/css/index.html
@@ -0,0 +1,75 @@
+
+
+CodeMirror: CSS mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+CSS mode
+
+
+
+ MIME types defined: text/css, text/x-scss (demo ), text/x-less (demo ).
+
+ Parsing/Highlighting Tests: normal , verbose .
+
+
diff --git a/applications/admin/static/codemirror/mode/css/less.html b/applications/admin/static/codemirror/mode/css/less.html
new file mode 100644
index 00000000..6ccb721e
--- /dev/null
+++ b/applications/admin/static/codemirror/mode/css/less.html
@@ -0,0 +1,152 @@
+
+
+CodeMirror: LESS mode
+
+
+
+
+
+
+
+
+
+
+
+LESS mode
+@media screen and (device-aspect-ratio: 16/9) { … }
+@media screen and (device-aspect-ratio: 1280/720) { … }
+@media screen and (device-aspect-ratio: 2560/1440) { … }
+
+html:lang(fr-be)
+
+tr:nth-child(2n+1) /* represents every odd row of an HTML table */
+
+img:nth-of-type(2n+1) { float: right; }
+img:nth-of-type(2n) { float: left; }
+
+body > h2:not(:first-of-type):not(:last-of-type)
+
+html|*:not(:link):not(:visited)
+*|*:not(:hover)
+p::first-line { text-transform: uppercase }
+
+@namespace foo url(http://www.example.com);
+foo|h1 { color: blue } /* first rule */
+
+span[hello="Ocean"][goodbye="Land"]
+
+E[foo]{
+ padding:65px;
+}
+
+input[type="search"]::-webkit-search-decoration,
+input[type="search"]::-webkit-search-cancel-button {
+ -webkit-appearance: none; // Inner-padding issues in Chrome OSX, Safari 5
+}
+button::-moz-focus-inner,
+input::-moz-focus-inner { // Inner padding and border oddities in FF3/4
+ padding: 0;
+ border: 0;
+}
+.btn {
+ // reset here as of 2.0.3 due to Recess property order
+ border-color: #ccc;
+ border-color: rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);
+}
+fieldset span button, fieldset span input[type="file"] {
+ font-size:12px;
+ font-family:Arial, Helvetica, sans-serif;
+}
+
+.rounded-corners (@radius: 5px) {
+ border-radius: @radius;
+ -webkit-border-radius: @radius;
+ -moz-border-radius: @radius;
+}
+
+@import url("something.css");
+
+@light-blue: hsl(190, 50%, 65%);
+
+#menu {
+ position: absolute;
+ width: 100%;
+ z-index: 3;
+ clear: both;
+ display: block;
+ background-color: @blue;
+ height: 42px;
+ border-top: 2px solid lighten(@alpha-blue, 20%);
+ border-bottom: 2px solid darken(@alpha-blue, 25%);
+ .box-shadow(0, 1px, 8px, 0.6);
+ -moz-box-shadow: 0 0 0 #000; // Because firefox sucks.
+
+ &.docked {
+ background-color: hsla(210, 60%, 40%, 0.4);
+ }
+ &:hover {
+ background-color: @blue;
+ }
+
+ #dropdown {
+ margin: 0 0 0 117px;
+ padding: 0;
+ padding-top: 5px;
+ display: none;
+ width: 190px;
+ border-top: 2px solid @medium;
+ color: @highlight;
+ border: 2px solid darken(@medium, 25%);
+ border-left-color: darken(@medium, 15%);
+ border-right-color: darken(@medium, 15%);
+ border-top-width: 0;
+ background-color: darken(@medium, 10%);
+ ul {
+ padding: 0px;
+ }
+ li {
+ font-size: 14px;
+ display: block;
+ text-align: left;
+ padding: 0;
+ border: 0;
+ a {
+ display: block;
+ padding: 0px 15px;
+ text-decoration: none;
+ color: white;
+ &:hover {
+ background-color: darken(@medium, 15%);
+ text-decoration: none;
+ }
+ }
+ }
+ .border-radius(5px, bottom);
+ .box-shadow(0, 6px, 8px, 0.5);
+ }
+}
+
+
+
+ The LESS mode is a sub-mode of the CSS mode (defined in css.js.
+
+ Parsing/Highlighting Tests: normal , verbose .
+
diff --git a/applications/admin/static/codemirror/mode/css/less_test.js b/applications/admin/static/codemirror/mode/css/less_test.js
new file mode 100644
index 00000000..2ba69984
--- /dev/null
+++ b/applications/admin/static/codemirror/mode/css/less_test.js
@@ -0,0 +1,51 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function() {
+ "use strict";
+
+ var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-less");
+ function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "less"); }
+
+ MT("variable",
+ "[variable-2 @base]: [atom #f04615];",
+ "[qualifier .class] {",
+ " [property width]: [variable percentage]([number 0.5]); [comment // returns `50%`]",
+ " [property color]: [variable saturate]([variable-2 @base], [number 5%]);",
+ "}");
+
+ MT("amp",
+ "[qualifier .child], [qualifier .sibling] {",
+ " [qualifier .parent] [atom &] {",
+ " [property color]: [keyword black];",
+ " }",
+ " [atom &] + [atom &] {",
+ " [property color]: [keyword red];",
+ " }",
+ "}");
+
+ MT("mixin",
+ "[qualifier .mixin] ([variable dark]; [variable-2 @color]) {",
+ " [property color]: [variable darken]([variable-2 @color], [number 10%]);",
+ "}",
+ "[qualifier .mixin] ([variable light]; [variable-2 @color]) {",
+ " [property color]: [variable lighten]([variable-2 @color], [number 10%]);",
+ "}",
+ "[qualifier .mixin] ([variable-2 @_]; [variable-2 @color]) {",
+ " [property display]: [atom block];",
+ "}",
+ "[variable-2 @switch]: [variable light];",
+ "[qualifier .class] {",
+ " [qualifier .mixin]([variable-2 @switch]; [atom #888]);",
+ "}");
+
+ MT("nest",
+ "[qualifier .one] {",
+ " [def @media] ([property width]: [number 400px]) {",
+ " [property font-size]: [number 1.2em];",
+ " [def @media] [attribute print] [keyword and] [property color] {",
+ " [property color]: [keyword blue];",
+ " }",
+ " }",
+ "}");
+})();
diff --git a/applications/admin/static/codemirror/mode/css/scss.html b/applications/admin/static/codemirror/mode/css/scss.html
index 0677d08f..21f20e0d 100644
--- a/applications/admin/static/codemirror/mode/css/scss.html
+++ b/applications/admin/static/codemirror/mode/css/scss.html
@@ -9,12 +9,12 @@
-
+
CodeMirror
Language modes
diff --git a/applications/admin/static/codemirror/mode/css/scss_test.js b/applications/admin/static/codemirror/mode/css/scss_test.js
index c51cb42b..8dcea9e8 100644
--- a/applications/admin/static/codemirror/mode/css/scss_test.js
+++ b/applications/admin/static/codemirror/mode/css/scss_test.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function() {
var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-scss");
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "scss"); }
diff --git a/applications/admin/static/codemirror/mode/css/test.js b/applications/admin/static/codemirror/mode/css/test.js
index b3f47767..d236e2a7 100644
--- a/applications/admin/static/codemirror/mode/css/test.js
+++ b/applications/admin/static/codemirror/mode/css/test.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function() {
var mode = CodeMirror.getMode({indentUnit: 2}, "css");
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
@@ -116,4 +119,17 @@
" [property src]: [atom url]([string http://blah]),",
" [atom url]([string http://foo]);",
"}");
+
+ MT("empty_url",
+ "[def @import] [tag url]() [tag screen];");
+
+ MT("parens",
+ "[qualifier .foo] {",
+ " [property background-image]: [variable fade]([atom #000], [number 20%]);",
+ " [property border-image]: [variable linear-gradient](",
+ " [atom to] [atom bottom],",
+ " [variable fade]([atom #000], [number 20%]) [number 0%],",
+ " [variable fade]([atom #000], [number 20%]) [number 100%]",
+ " );",
+ "}");
})();
diff --git a/applications/admin/static/codemirror/mode/haml/haml.js b/applications/admin/static/codemirror/mode/haml/haml.js
index 793308f6..8fe63b02 100644
--- a/applications/admin/static/codemirror/mode/haml/haml.js
+++ b/applications/admin/static/codemirror/mode/haml/haml.js
@@ -1,5 +1,15 @@
-(function() {
- "use strict";
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), require("../ruby/ruby"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror", "../htmlmixed/htmlmixed", "../ruby/ruby"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
// full haml mode. This handled embeded ruby and html fragments too
CodeMirror.defineMode("haml", function(config) {
@@ -65,7 +75,7 @@
// donot handle --> as valid ruby, make it HTML close comment instead
if (state.startOfLine && !stream.match("-->", false) && (ch == "=" || ch == "-" )) {
state.tokenize = ruby;
- return null;
+ return state.tokenize(stream, state);
}
if (state.previousToken.style == "hamlTag" ||
@@ -73,10 +83,10 @@
state.previousToken.style == "hamlAttribute") {
if (ch == "(") {
state.tokenize = rubyInQuote(")");
- return null;
+ return state.tokenize(stream, state);
} else if (ch == "{") {
state.tokenize = rubyInQuote("}");
- return null;
+ return state.tokenize(stream, state);
}
}
@@ -141,13 +151,9 @@
style = null;
}
return style;
- },
-
- indent: function(state) {
- return state.indented;
}
};
}, "htmlmixed", "ruby");
CodeMirror.defineMIME("text/x-haml", "haml");
-})();
+});
diff --git a/applications/admin/static/codemirror/mode/haml/index.html b/applications/admin/static/codemirror/mode/haml/index.html
index a7378755..2894a938 100644
--- a/applications/admin/static/codemirror/mode/haml/index.html
+++ b/applications/admin/static/codemirror/mode/haml/index.html
@@ -13,12 +13,12 @@
-
+
CodeMirror
Language modes
diff --git a/applications/admin/static/codemirror/mode/haml/test.js b/applications/admin/static/codemirror/mode/haml/test.js
index b7178d40..508458a4 100644
--- a/applications/admin/static/codemirror/mode/haml/test.js
+++ b/applications/admin/static/codemirror/mode/haml/test.js
@@ -1,5 +1,8 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function() {
- var mode = CodeMirror.getMode({tabSize: 4}, "haml");
+ var mode = CodeMirror.getMode({tabSize: 4, indentUnit: 2}, "haml");
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
// Requires at least one media query
@@ -28,7 +31,7 @@
"[tag %a]([variable title][operator =][string \"test\"]){[atom :title] [operator =>] [string \"test\"]}");
MT("htmlCode",
- "[tag ]Title[tag ]");
+ "[tag&bracket <][tag h1][tag&bracket >]Title[tag&bracket ][tag h1][tag&bracket >]");
MT("rubyBlock",
"[operator =][variable-2 @item]");
@@ -50,9 +53,9 @@
" [comment -# this is a comment]",
" [comment and this is a comment too]",
" Date/Time",
- " [operator -] [variable now] [operator =] [tag DateTime][operator .][variable now]",
+ " [operator -] [variable now] [operator =] [tag DateTime][operator .][property now]",
" [tag %strong=] [variable now]",
- " [operator -] [keyword if] [variable now] [operator >] [tag DateTime][operator .][variable parse]([string \"December 31, 2006\"])",
+ " [operator -] [keyword if] [variable now] [operator >] [tag DateTime][operator .][property parse]([string \"December 31, 2006\"])",
" [operator =][string \"Happy\"]",
" [operator =][string \"Belated\"]",
" [operator =][string \"Birthday\"]");
diff --git a/applications/admin/static/codemirror/mode/htmlembedded/htmlembedded.js b/applications/admin/static/codemirror/mode/htmlembedded/htmlembedded.js
index 3a07c343..e8f7ba80 100644
--- a/applications/admin/static/codemirror/mode/htmlembedded/htmlembedded.js
+++ b/applications/admin/static/codemirror/mode/htmlembedded/htmlembedded.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"));
diff --git a/applications/admin/static/codemirror/mode/htmlembedded/index.html b/applications/admin/static/codemirror/mode/htmlembedded/index.html
new file mode 100644
index 00000000..93d01c45
--- /dev/null
+++ b/applications/admin/static/codemirror/mode/htmlembedded/index.html
@@ -0,0 +1,58 @@
+
+
+CodeMirror: Html Embedded Scripts mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Html Embedded Scripts mode
+
+<%
+function hello(who) {
+ return "Hello " + who;
+}
+%>
+This is an example of EJS (embedded javascript)
+The program says <%= hello("world") %>.
+
+
+
+
+
+ Mode for html embedded scripts like JSP and ASP.NET. Depends on HtmlMixed which in turn depends on
+ JavaScript, CSS and XML. Other dependancies include those of the scriping language chosen.
+
+ MIME types defined: application/x-aspx (ASP.NET),
+ application/x-ejs (Embedded Javascript), application/x-jsp (JavaServer Pages)
+
diff --git a/applications/admin/static/codemirror/mode/htmlmixed/htmlmixed.js b/applications/admin/static/codemirror/mode/htmlmixed/htmlmixed.js
index d80ef9c6..1cc438f0 100644
--- a/applications/admin/static/codemirror/mode/htmlmixed/htmlmixed.js
+++ b/applications/admin/static/codemirror/mode/htmlmixed/htmlmixed.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css"));
@@ -27,6 +30,7 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
function html(stream, state) {
var tagName = state.htmlState.tagName;
+ if (tagName) tagName = tagName.toLowerCase();
var style = htmlMode.token(stream, state.htmlState);
if (tagName == "script" && /\btag\b/.test(style) && stream.current() == ">") {
// Script block: mode to change to depends on type attribute
@@ -65,7 +69,7 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
if (stream.match(/^<\/\s*script\s*>/i, false)) {
state.token = html;
state.localState = state.localMode = null;
- return html(stream, state);
+ return null;
}
return maybeBackup(stream, /<\/\s*script\s*>/,
state.localMode.token(stream, state.localState));
@@ -74,7 +78,7 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
if (stream.match(/^<\/\s*style\s*>/i, false)) {
state.token = html;
state.localState = state.localMode = null;
- return html(stream, state);
+ return null;
}
return maybeBackup(stream, /<\/\s*style\s*>/,
cssMode.token(stream, state.localState));
diff --git a/applications/admin/static/codemirror/mode/htmlmixed/index.html b/applications/admin/static/codemirror/mode/htmlmixed/index.html
new file mode 100644
index 00000000..f94df9e2
--- /dev/null
+++ b/applications/admin/static/codemirror/mode/htmlmixed/index.html
@@ -0,0 +1,89 @@
+
+
+CodeMirror: HTML mixed mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+HTML mixed mode
+
+
+
+
+ Mixed HTML Example
+
+
+
+ Mixed HTML Example
+
+
+
+
+
+
+ The HTML mixed mode depends on the XML, JavaScript, and CSS modes.
+
+ It takes an optional mode configuration
+ option, scriptTypes, which can be used to add custom
+ behavior for specific <script type="..."> tags. If
+ given, it should hold an array of {matches, mode}
+ objects, where matches is a string or regexp that
+ matches the script type, and mode is
+ either null, for script types that should stay in
+ HTML mode, or a mode
+ spec corresponding to the mode that should be used for the
+ script.
+
+ MIME types defined: text/html
+ (redefined, only takes effect if you load this parser after the
+ XML parser).
+
+
diff --git a/applications/admin/static/codemirror/mode/http/http.js b/applications/admin/static/codemirror/mode/http/http.js
index 5a516360..9a3c5f9f 100644
--- a/applications/admin/static/codemirror/mode/http/http.js
+++ b/applications/admin/static/codemirror/mode/http/http.js
@@ -1,3 +1,16 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
CodeMirror.defineMode("http", function() {
function failFirstLine(stream, state) {
stream.skipToEnd();
@@ -96,3 +109,5 @@ CodeMirror.defineMode("http", function() {
});
CodeMirror.defineMIME("message/http", "http");
+
+});
diff --git a/applications/admin/static/codemirror/mode/http/index.html b/applications/admin/static/codemirror/mode/http/index.html
index 705085e2..0b8d5315 100644
--- a/applications/admin/static/codemirror/mode/http/index.html
+++ b/applications/admin/static/codemirror/mode/http/index.html
@@ -9,12 +9,12 @@
-
+
CodeMirror
Language modes
diff --git a/applications/admin/static/codemirror/mode/index.html b/applications/admin/static/codemirror/mode/index.html
new file mode 100644
index 00000000..c933e1e9
--- /dev/null
+++ b/applications/admin/static/codemirror/mode/index.html
@@ -0,0 +1,132 @@
+
+
+CodeMirror: Language Modes
+
+
+
+
+
+
+
+ Language modes
+
+ This is a list of every mode in the distribution. Each mode lives
+in a subdirectory of the mode/ directory, and typically
+defines a single JavaScript file that implements the mode. Loading
+such file will make the language available to CodeMirror, through
+the mode
+option.
+
+
+
+
diff --git a/applications/admin/static/codemirror/mode/javascript/index.html b/applications/admin/static/codemirror/mode/javascript/index.html
new file mode 100644
index 00000000..592a133d
--- /dev/null
+++ b/applications/admin/static/codemirror/mode/javascript/index.html
@@ -0,0 +1,114 @@
+
+
+CodeMirror: JavaScript mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript mode
+
+
+
+// Demo code (the actual new parser character stream implementation)
+
+function StringStream(string) {
+ this.pos = 0;
+ this.string = string;
+}
+
+StringStream.prototype = {
+ done: function() {return this.pos >= this.string.length;},
+ peek: function() {return this.string.charAt(this.pos);},
+ next: function() {
+ if (this.pos < this.string.length)
+ return this.string.charAt(this.pos++);
+ },
+ eat: function(match) {
+ var ch = this.string.charAt(this.pos);
+ if (typeof match == "string") var ok = ch == match;
+ else var ok = ch && match.test ? match.test(ch) : match(ch);
+ if (ok) {this.pos++; return ch;}
+ },
+ eatWhile: function(match) {
+ var start = this.pos;
+ while (this.eat(match));
+ if (this.pos > start) return this.string.slice(start, this.pos);
+ },
+ backUp: function(n) {this.pos -= n;},
+ column: function() {return this.pos;},
+ eatSpace: function() {
+ var start = this.pos;
+ while (/\s/.test(this.string.charAt(this.pos))) this.pos++;
+ return this.pos - start;
+ },
+ match: function(pattern, consume, caseInsensitive) {
+ if (typeof pattern == "string") {
+ function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
+ if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
+ if (consume !== false) this.pos += str.length;
+ return true;
+ }
+ }
+ else {
+ var match = this.string.slice(this.pos).match(pattern);
+ if (match && consume !== false) this.pos += match[0].length;
+ return match;
+ }
+ }
+};
+
+
+
+
+
+ JavaScript mode supports several configuration options:
+
+ json which will set the mode to expect JSON
+ data rather than a JavaScript program.
+ jsonld which will set the mode to expect
+ JSON-LD linked data rather
+ than a JavaScript program (demo ).
+ typescript which will activate additional
+ syntax highlighting and some other things for TypeScript code
+ (demo ).
+ statementIndent which (given a number) will
+ determine the amount of indentation to use for statements
+ continued on a new line.
+ wordCharacters, a regexp that indicates which
+ characters should be considered part of an identifier.
+ Defaults to /[\w$]/, which does not handle
+ non-ASCII identifiers. Can be set to something more elaborate
+ to improve Unicode support.
+
+
+
+ MIME types defined: text/javascript, application/json, application/ld+json, text/typescript, application/typescript.
+
diff --git a/applications/admin/static/codemirror/mode/javascript/javascript.js b/applications/admin/static/codemirror/mode/javascript/javascript.js
index 15eccf7e..93df06d1 100644
--- a/applications/admin/static/codemirror/mode/javascript/javascript.js
+++ b/applications/admin/static/codemirror/mode/javascript/javascript.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
// TODO actually recognize syntax of TypeScript constructs
(function(mod) {
@@ -16,6 +19,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
var jsonldMode = parserConfig.jsonld;
var jsonMode = parserConfig.json || jsonldMode;
var isTS = parserConfig.typescript;
+ var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
// Tokenizer
@@ -129,8 +133,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
} else if (isOperatorChar.test(ch)) {
stream.eatWhile(isOperatorChar);
return ret("operator", "operator", stream.current());
- } else {
- stream.eatWhile(/[\w\$_]/);
+ } else if (wordRE.test(ch)) {
+ stream.eatWhile(wordRE);
var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
ret("variable", "variable", word);
@@ -199,8 +203,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (--depth == 0) break;
} else if (bracket >= 3 && bracket < 6) {
++depth;
- } else if (/[$\w]/.test(ch)) {
+ } else if (wordRE.test(ch)) {
sawSomething = true;
+ } else if (/["'\/]/.test(ch)) {
+ return;
} else if (sawSomething && !depth) {
++pos;
break;
@@ -235,7 +241,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
var cc = state.cc;
// Communicate our context to the combinators.
// (Less wasteful than consing up a hundred closures on every call.)
- cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc;
+ cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
if (!state.lexical.hasOwnProperty("align"))
state.lexical.align = true;
@@ -295,6 +301,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
var result = function() {
var state = cx.state, indent = state.indented;
if (state.lexical.type == "stat") indent = state.lexical.indented;
+ else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
+ indent = outer.indented;
state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
};
result.lex = true;
@@ -325,7 +333,11 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
if (type == "{") return cont(pushlex("}"), block, poplex);
if (type == ";") return cont();
- if (type == "if") return cont(pushlex("form"), expression, statement, poplex, maybeelse);
+ if (type == "if") {
+ if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
+ cx.state.cc.pop()();
+ return cont(pushlex("form"), expression, statement, poplex, maybeelse);
+ }
if (type == "function") return cont(functiondef);
if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
if (type == "variable") return cont(pushlex("stat"), maybelabel);
@@ -336,7 +348,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
statement, poplex, popcontext);
if (type == "module") return cont(pushlex("form"), pushcontext, afterModule, popcontext, poplex);
- if (type == "class") return cont(pushlex("form"), className, objlit, poplex);
+ if (type == "class") return cont(pushlex("form"), className, poplex);
if (type == "export") return cont(pushlex("form"), afterExport, poplex);
if (type == "import") return cont(pushlex("form"), afterImport, poplex);
return pass(pushlex("stat"), expression, expect(";"), poplex);
@@ -356,12 +368,13 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
- if (type == "function") return cont(functiondef);
+ if (type == "function") return cont(functiondef, maybeop);
if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
if (type == "(") return cont(pushlex(")"), maybeexpression, comprehension, expect(")"), poplex, maybeop);
if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
if (type == "{") return contCommasep(objprop, "}", null, maybeop);
+ if (type == "quasi") { return pass(quasi, maybeop); }
return cont();
}
function maybeexpression(type) {
@@ -380,38 +393,37 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
function maybeoperatorNoComma(type, value, noComma) {
var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
var expr = noComma == false ? expression : expressionNoComma;
- if (value == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
+ if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
if (type == "operator") {
if (/\+\+|--/.test(value)) return cont(me);
if (value == "?") return cont(expression, expect(":"), expr);
return cont(expr);
}
- if (type == "quasi") { cx.cc.push(me); return quasi(value); }
+ if (type == "quasi") { return pass(quasi, me); }
if (type == ";") return;
if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
if (type == ".") return cont(property, me);
if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
}
- function quasi(value) {
- if (value.slice(value.length - 2) != "${") return cont();
+ function quasi(type, value) {
+ if (type != "quasi") return pass();
+ if (value.slice(value.length - 2) != "${") return cont(quasi);
return cont(expression, continueQuasi);
}
function continueQuasi(type) {
if (type == "}") {
cx.marked = "string-2";
cx.state.tokenize = tokenQuasi;
- return cont();
+ return cont(quasi);
}
}
function arrowBody(type) {
findFatArrow(cx.stream, cx.state);
- if (type == "{") return pass(statement);
- return pass(expression);
+ return pass(type == "{" ? statement : expression);
}
function arrowBodyNoComma(type) {
findFatArrow(cx.stream, cx.state);
- if (type == "{") return pass(statement);
- return pass(expressionNoComma);
+ return pass(type == "{" ? statement : expressionNoComma);
}
function maybelabel(type) {
if (type == ":") return cont(poplex, statement);
@@ -421,15 +433,18 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == "variable") {cx.marked = "property"; return cont();}
}
function objprop(type, value) {
- if (type == "variable") {
+ if (type == "variable" || cx.style == "keyword") {
cx.marked = "property";
if (value == "get" || value == "set") return cont(getterSetter);
+ return cont(afterprop);
} else if (type == "number" || type == "string") {
- cx.marked = jsonldMode ? "property" : (type + " property");
+ cx.marked = jsonldMode ? "property" : (cx.style + " property");
+ return cont(afterprop);
+ } else if (type == "jsonld-keyword") {
+ return cont(afterprop);
} else if (type == "[") {
return cont(expression, expect("]"), afterprop);
}
- if (atomicTypes.hasOwnProperty(type)) return cont(afterprop);
}
function getterSetter(type) {
if (type != "variable") return pass(afterprop);
@@ -493,7 +508,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == ",") return cont(vardef);
}
function maybeelse(type, value) {
- if (type == "keyword b" && value == "else") return cont(pushlex("form"), statement, poplex);
+ if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
}
function forspec(type) {
if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
@@ -528,11 +543,27 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
function className(type, value) {
if (type == "variable") {register(value); return cont(classNameAfter);}
}
- function classNameAfter(_type, value) {
- if (value == "extends") return cont(expression);
+ function classNameAfter(type, value) {
+ if (value == "extends") return cont(expression, classNameAfter);
+ if (type == "{") return cont(pushlex("}"), classBody, poplex);
}
- function objlit(type) {
- if (type == "{") return contCommasep(objprop, "}");
+ function classBody(type, value) {
+ if (type == "variable" || cx.style == "keyword") {
+ cx.marked = "property";
+ if (value == "get" || value == "set") return cont(classGetterSetter, functiondef, classBody);
+ return cont(functiondef, classBody);
+ }
+ if (value == "*") {
+ cx.marked = "keyword";
+ return cont(classBody);
+ }
+ if (type == ";") return cont(classBody);
+ if (type == "}") return cont();
+ }
+ function classGetterSetter(type) {
+ if (type != "variable") return pass();
+ cx.marked = "property";
+ return cont();
}
function afterModule(type, value) {
if (type == "string") return cont(statement);
@@ -561,7 +592,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
}
function maybeArrayComprehension(type) {
if (type == "for") return pass(comprehension, expect("]"));
- if (type == ",") return cont(commasep(expressionNoComma, "]"));
+ if (type == ",") return cont(commasep(maybeexpressionNoComma, "]"));
return pass(commasep(expressionNoComma, "]"));
}
function comprehension(type) {
@@ -569,6 +600,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == "if") return cont(expression, comprehension);
}
+ function isContinuedStatement(state, textAfter) {
+ return state.lastType == "operator" || state.lastType == "," ||
+ isOperatorChar.test(textAfter.charAt(0)) ||
+ /[,.]/.test(textAfter.charAt(0));
+ }
+
// Interface
return {
@@ -606,7 +643,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (state.tokenize != tokenBase) return 0;
var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
// Kludge to prevent 'maybelse' from blocking lexical scope pops
- for (var i = state.cc.length - 1; i >= 0; --i) {
+ if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
var c = state.cc[i];
if (c == poplex) lexical = lexical.prev;
else if (c != maybeelse) break;
@@ -620,14 +657,14 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
else if (type == "form" && firstChar == "{") return lexical.indented;
else if (type == "form") return lexical.indented + indentUnit;
else if (type == "stat")
- return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? statementIndent || indentUnit : 0);
+ return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
else if (lexical.align) return lexical.column + (closing ? 0 : 1);
else return lexical.indented + (closing ? 0 : indentUnit);
},
- electricChars: ":{}",
+ electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
blockCommentStart: jsonMode ? null : "/*",
blockCommentEnd: jsonMode ? null : "*/",
lineComment: jsonMode ? null : "//",
@@ -639,9 +676,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
};
});
+CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
+
CodeMirror.defineMIME("text/javascript", "javascript");
CodeMirror.defineMIME("text/ecmascript", "javascript");
CodeMirror.defineMIME("application/javascript", "javascript");
+CodeMirror.defineMIME("application/x-javascript", "javascript");
CodeMirror.defineMIME("application/ecmascript", "javascript");
CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
diff --git a/applications/admin/static/codemirror/mode/javascript/json-ld.html b/applications/admin/static/codemirror/mode/javascript/json-ld.html
new file mode 100644
index 00000000..3a37f0bc
--- /dev/null
+++ b/applications/admin/static/codemirror/mode/javascript/json-ld.html
@@ -0,0 +1,72 @@
+
+
+CodeMirror: JSON-LD mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+JSON-LD mode
+
+
+
+{
+ "@context": {
+ "name": "http://schema.org/name",
+ "description": "http://schema.org/description",
+ "image": {
+ "@id": "http://schema.org/image",
+ "@type": "@id"
+ },
+ "geo": "http://schema.org/geo",
+ "latitude": {
+ "@id": "http://schema.org/latitude",
+ "@type": "xsd:float"
+ },
+ "longitude": {
+ "@id": "http://schema.org/longitude",
+ "@type": "xsd:float"
+ },
+ "xsd": "http://www.w3.org/2001/XMLSchema#"
+ },
+ "name": "The Empire State Building",
+ "description": "The Empire State Building is a 102-story landmark in New York City.",
+ "image": "http://www.civil.usherbrooke.ca/cours/gci215a/empire-state-building.jpg",
+ "geo": {
+ "latitude": "40.75",
+ "longitude": "73.98"
+ }
+}
+
+
+
+
+ This is a specialization of the JavaScript mode .
+
diff --git a/applications/admin/static/codemirror/mode/javascript/test.js b/applications/admin/static/codemirror/mode/javascript/test.js
new file mode 100644
index 00000000..91b0e89a
--- /dev/null
+++ b/applications/admin/static/codemirror/mode/javascript/test.js
@@ -0,0 +1,200 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function() {
+ var mode = CodeMirror.getMode({indentUnit: 2}, "javascript");
+ function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
+
+ MT("locals",
+ "[keyword function] [variable foo]([def a], [def b]) { [keyword var] [def c] [operator =] [number 10]; [keyword return] [variable-2 a] [operator +] [variable-2 c] [operator +] [variable d]; }");
+
+ MT("comma-and-binop",
+ "[keyword function](){ [keyword var] [def x] [operator =] [number 1] [operator +] [number 2], [def y]; }");
+
+ MT("destructuring",
+ "([keyword function]([def a], [[[def b], [def c] ]]) {",
+ " [keyword let] {[def d], [property foo]: [def c][operator =][number 10], [def x]} [operator =] [variable foo]([variable-2 a]);",
+ " [[[variable-2 c], [variable y] ]] [operator =] [variable-2 c];",
+ "})();");
+
+ MT("class_body",
+ "[keyword class] [variable Foo] {",
+ " [property constructor]() {}",
+ " [property sayName]() {",
+ " [keyword return] [string-2 `foo${][variable foo][string-2 }oo`];",
+ " }",
+ "}");
+
+ MT("class",
+ "[keyword class] [variable Point] [keyword extends] [variable SuperThing] {",
+ " [property get] [property prop]() { [keyword return] [number 24]; }",
+ " [property constructor]([def x], [def y]) {",
+ " [keyword super]([string 'something']);",
+ " [keyword this].[property x] [operator =] [variable-2 x];",
+ " }",
+ "}");
+
+ MT("module",
+ "[keyword module] [string 'foo'] {",
+ " [keyword export] [keyword let] [def x] [operator =] [number 42];",
+ " [keyword export] [keyword *] [keyword from] [string 'somewhere'];",
+ "}");
+
+ MT("import",
+ "[keyword function] [variable foo]() {",
+ " [keyword import] [def $] [keyword from] [string 'jquery'];",
+ " [keyword module] [def crypto] [keyword from] [string 'crypto'];",
+ " [keyword import] { [def encrypt], [def decrypt] } [keyword from] [string 'crypto'];",
+ "}");
+
+ MT("const",
+ "[keyword function] [variable f]() {",
+ " [keyword const] [[ [def a], [def b] ]] [operator =] [[ [number 1], [number 2] ]];",
+ "}");
+
+ MT("for/of",
+ "[keyword for]([keyword let] [variable of] [keyword of] [variable something]) {}");
+
+ MT("generator",
+ "[keyword function*] [variable repeat]([def n]) {",
+ " [keyword for]([keyword var] [def i] [operator =] [number 0]; [variable-2 i] [operator <] [variable-2 n]; [operator ++][variable-2 i])",
+ " [keyword yield] [variable-2 i];",
+ "}");
+
+ MT("quotedStringAddition",
+ "[keyword let] [variable f] [operator =] [variable a] [operator +] [string 'fatarrow'] [operator +] [variable c];");
+
+ MT("quotedFatArrow",
+ "[keyword let] [variable f] [operator =] [variable a] [operator +] [string '=>'] [operator +] [variable c];");
+
+ MT("fatArrow",
+ "[variable array].[property filter]([def a] [operator =>] [variable-2 a] [operator +] [number 1]);",
+ "[variable a];", // No longer in scope
+ "[keyword let] [variable f] [operator =] ([[ [def a], [def b] ]], [def c]) [operator =>] [variable-2 a] [operator +] [variable-2 c];",
+ "[variable c];");
+
+ MT("spread",
+ "[keyword function] [variable f]([def a], [meta ...][def b]) {",
+ " [variable something]([variable-2 a], [meta ...][variable-2 b]);",
+ "}");
+
+ MT("comprehension",
+ "[keyword function] [variable f]() {",
+ " [[([variable x] [operator +] [number 1]) [keyword for] ([keyword var] [def x] [keyword in] [variable y]) [keyword if] [variable pred]([variable-2 x]) ]];",
+ " ([variable u] [keyword for] ([keyword var] [def u] [keyword of] [variable generateValues]()) [keyword if] ([variable-2 u].[property color] [operator ===] [string 'blue']));",
+ "}");
+
+ MT("quasi",
+ "[variable re][string-2 `fofdlakj${][variable x] [operator +] ([variable re][string-2 `foo`]) [operator +] [number 1][string-2 }fdsa`] [operator +] [number 2]");
+
+ MT("quasi_no_function",
+ "[variable x] [operator =] [string-2 `fofdlakj${][variable x] [operator +] [string-2 `foo`] [operator +] [number 1][string-2 }fdsa`] [operator +] [number 2]");
+
+ MT("indent_statement",
+ "[keyword var] [variable x] [operator =] [number 10]",
+ "[variable x] [operator +=] [variable y] [operator +]",
+ " [atom Infinity]",
+ "[keyword debugger];");
+
+ MT("indent_if",
+ "[keyword if] ([number 1])",
+ " [keyword break];",
+ "[keyword else] [keyword if] ([number 2])",
+ " [keyword continue];",
+ "[keyword else]",
+ " [number 10];",
+ "[keyword if] ([number 1]) {",
+ " [keyword break];",
+ "} [keyword else] [keyword if] ([number 2]) {",
+ " [keyword continue];",
+ "} [keyword else] {",
+ " [number 10];",
+ "}");
+
+ MT("indent_for",
+ "[keyword for] ([keyword var] [variable i] [operator =] [number 0];",
+ " [variable i] [operator <] [number 100];",
+ " [variable i][operator ++])",
+ " [variable doSomething]([variable i]);",
+ "[keyword debugger];");
+
+ MT("indent_c_style",
+ "[keyword function] [variable foo]()",
+ "{",
+ " [keyword debugger];",
+ "}");
+
+ MT("indent_else",
+ "[keyword for] (;;)",
+ " [keyword if] ([variable foo])",
+ " [keyword if] ([variable bar])",
+ " [number 1];",
+ " [keyword else]",
+ " [number 2];",
+ " [keyword else]",
+ " [number 3];");
+
+ MT("indent_funarg",
+ "[variable foo]([number 10000],",
+ " [keyword function]([def a]) {",
+ " [keyword debugger];",
+ "};");
+
+ MT("indent_below_if",
+ "[keyword for] (;;)",
+ " [keyword if] ([variable foo])",
+ " [number 1];",
+ "[number 2];");
+
+ MT("multilinestring",
+ "[keyword var] [variable x] [operator =] [string 'foo\\]",
+ "[string bar'];");
+
+ MT("scary_regexp",
+ "[string-2 /foo[[/]]bar/];");
+
+ MT("indent_strange_array",
+ "[keyword var] [variable x] [operator =] [[",
+ " [number 1],,",
+ " [number 2],",
+ "]];",
+ "[number 10];");
+
+ var jsonld_mode = CodeMirror.getMode(
+ {indentUnit: 2},
+ {name: "javascript", jsonld: true}
+ );
+ function LD(name) {
+ test.mode(name, jsonld_mode, Array.prototype.slice.call(arguments, 1));
+ }
+
+ LD("json_ld_keywords",
+ '{',
+ ' [meta "@context"]: {',
+ ' [meta "@base"]: [string "http://example.com"],',
+ ' [meta "@vocab"]: [string "http://xmlns.com/foaf/0.1/"],',
+ ' [property "likesFlavor"]: {',
+ ' [meta "@container"]: [meta "@list"]',
+ ' [meta "@reverse"]: [string "@beFavoriteOf"]',
+ ' },',
+ ' [property "nick"]: { [meta "@container"]: [meta "@set"] },',
+ ' [property "nick"]: { [meta "@container"]: [meta "@index"] }',
+ ' },',
+ ' [meta "@graph"]: [[ {',
+ ' [meta "@id"]: [string "http://dbpedia.org/resource/John_Lennon"],',
+ ' [property "name"]: [string "John Lennon"],',
+ ' [property "modified"]: {',
+ ' [meta "@value"]: [string "2010-05-29T14:17:39+02:00"],',
+ ' [meta "@type"]: [string "http://www.w3.org/2001/XMLSchema#dateTime"]',
+ ' }',
+ ' } ]]',
+ '}');
+
+ LD("json_ld_fake",
+ '{',
+ ' [property "@fake"]: [string "@fake"],',
+ ' [property "@contextual"]: [string "@identifier"],',
+ ' [property "user@domain.com"]: [string "@graphical"],',
+ ' [property "@ID"]: [string "@@ID"]',
+ '}');
+})();
diff --git a/applications/admin/static/codemirror/mode/javascript/typescript.html b/applications/admin/static/codemirror/mode/javascript/typescript.html
index 9cc5f493..2cfc5381 100644
--- a/applications/admin/static/codemirror/mode/javascript/typescript.html
+++ b/applications/admin/static/codemirror/mode/javascript/typescript.html
@@ -9,12 +9,12 @@
-
+
CodeMirror
Language modes
diff --git a/applications/admin/static/codemirror/mode/jinja2/index.html b/applications/admin/static/codemirror/mode/jinja2/index.html
index 66bf2ec6..5a70e915 100644
--- a/applications/admin/static/codemirror/mode/jinja2/index.html
+++ b/applications/admin/static/codemirror/mode/jinja2/index.html
@@ -9,12 +9,12 @@
-
+
CodeMirror
Language modes
@@ -25,22 +25,26 @@
Jinja2 mode
-<html style="color: green">
- <!-- this is a comment -->
- <head>
- <title>Jinja2 Example</title>
- </head>
- <body>
- <ul>
- {# this is a comment #}
- {%- for item in li -%}
- <li>
- {{ item.label }}
- </li>
- {% endfor -%}
- </ul>
- </body>
-</html>
+{# this is a comment #}
+{%- for item in li -%}
+ <li>{{ item.label }}</li>
+{% endfor -%}
+{{ item.sand == true and item.keyword == false ? 1 : 0 }}
+{{ app.get(55, 1.2, true) }}
+{% if app.get('_route') == ('_home') %}home{% endif %}
+{% if app.session.flashbag.has('message') %}
+ {% for message in app.session.flashbag.get('message') %}
+ {{ message.content }}
+ {% endfor %}
+{% endif %}
+{{ path('_home', {'section': app.request.get('section')}) }}
+{{ path('_home', {
+ 'section': app.request.get('section'),
+ 'boolean': true,
+ 'number': 55.33
+ })
+}}
+{% include ('test.incl.html.twig') %}
-
-
-
-
-
-
-
-
-
-
-
-LESS mode
-@media screen and (device-aspect-ratio: 16/9) { … }
-@media screen and (device-aspect-ratio: 32/18) { … }
-@media screen and (device-aspect-ratio: 1280/720) { … }
-@media screen and (device-aspect-ratio: 2560/1440) { … }
-
-html:lang(fr-be)
-html:lang(de)
-:lang(fr-be) > q
-:lang(de) > q
-
-tr:nth-child(2n+1) /* represents every odd row of an HTML table */
-tr:nth-child(odd) /* same */
-tr:nth-child(2n+0) /* represents every even row of an HTML table */
-tr:nth-child(even) /* same */
-
-/* Alternate paragraph colours in CSS */
-p:nth-child(4n+1) { color: navy; }
-p:nth-child(4n+2) { color: green; }
-p:nth-child(4n+3) { color: maroon; }
-p:nth-child(4n+4) { color: purple; }
-
-:nth-child(10n-1) /* represents the 9th, 19th, 29th, etc, element */
-:nth-child(10n+9) /* Same */
-:nth-child(10n+-1) /* Syntactically invalid, and would be ignored */
-
-:nth-child( 3n + 1 )
-:nth-child( +3n - 2 )
-:nth-child( -n+ 6)
-:nth-child( +6 )
-
-html|tr:nth-child(-n+6) /* represents the 6 first rows of XHTML tables */
-
-img:nth-of-type(2n+1) { float: right; }
-img:nth-of-type(2n) { float: left; }
-
-body > h2:nth-of-type(n+2):nth-last-of-type(n+2)
-body > h2:not(:first-of-type):not(:last-of-type)
-
-html|*:not(:link):not(:visited)
-*|*:not(:hover)
-p::first-line { text-transform: uppercase }
-
-p { color: red; font-size: 12pt }
-p::first-letter { color: green; font-size: 200% }
-p::first-line { color: blue }
-
-p { line-height: 1.1 }
-p::first-letter { font-size: 3em; font-weight: normal }
-span { font-weight: bold }
-
-* /* a=0 b=0 c=0 -> specificity = 0 */
-LI /* a=0 b=0 c=1 -> specificity = 1 */
-UL LI /* a=0 b=0 c=2 -> specificity = 2 */
-UL OL+LI /* a=0 b=0 c=3 -> specificity = 3 */
-H1 + *[REL=up] /* a=0 b=1 c=1 -> specificity = 11 */
-UL OL LI.red /* a=0 b=1 c=3 -> specificity = 13 */
-LI.red.level /* a=0 b=2 c=1 -> specificity = 21 */
-#x34y /* a=1 b=0 c=0 -> specificity = 100 */
-#s12:not(FOO) /* a=1 b=0 c=1 -> specificity = 101 */
-
-@namespace foo url(http://www.example.com);
-foo|h1 { color: blue } /* first rule */
-foo|* { color: yellow } /* second rule */
-|h1 { color: red } /* ...*/
-*|h1 { color: green }
-h1 { color: green }
-
-span[hello="Ocean"][goodbye="Land"]
-
-a[rel~="copyright"] { ... }
-a[href="http://www.w3.org/"] { ... }
-
-DIALOGUE[character=romeo]
-DIALOGUE[character=juliet]
-
-[att^=val]
-[att$=val]
-[att*=val]
-
-@namespace foo "http://www.example.com";
-[foo|att=val] { color: blue }
-[*|att] { color: yellow }
-[|att] { color: green }
-[att] { color: green }
-
-
-*:target { color : red }
-*:target::before { content : url(target.png) }
-
-E[foo]{
- padding:65px;
-}
-E[foo] ~ F{
- padding:65px;
-}
-E#myid{
- padding:65px;
-}
-input[type="search"]::-webkit-search-decoration,
-input[type="search"]::-webkit-search-cancel-button {
- -webkit-appearance: none; // Inner-padding issues in Chrome OSX, Safari 5
-}
-button::-moz-focus-inner,
-input::-moz-focus-inner { // Inner padding and border oddities in FF3/4
- padding: 0;
- border: 0;
-}
-.btn {
- // reset here as of 2.0.3 due to Recess property order
- border-color: #ccc;
- border-color: rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);
-}
-fieldset span button, fieldset span input[type="file"] {
- font-size:12px;
- font-family:Arial, Helvetica, sans-serif;
-}
-.el tr:nth-child(even):last-child td:first-child{
- -moz-border-radius-bottomleft:3px;
- -webkit-border-bottom-left-radius:3px;
- border-bottom-left-radius:3px;
-}
-
-/* Some LESS code */
-
-button {
- width: 32px;
- height: 32px;
- border: 0;
- margin: 4px;
- cursor: pointer;
-}
-button.icon-plus { background: url(http://dahlström.net/tmp/sharp-icons/svg-icon-target.svg#plus) no-repeat; }
-button.icon-chart { background: url(http://dahlström.net/tmp/sharp-icons/svg-icon-target.svg#chart) no-repeat; }
-
-button:hover { background-color: #999; }
-button:active { background-color: #666; }
-
-@test_a: #eeeQQQ;//this is not a valid hex value and thus parsed as an element id
-@test_b: #eeeFFF //this is a valid hex value but the declaration doesn't end with a semicolon and thus parsed as an element id
-
-#eee aaa .box
-{
- #test bbb {
- width: 500px;
- height: 250px;
- background-image: url(dir/output/sheep.png), url( betweengrassandsky.png );
- background-position: center bottom, left top;
- background-repeat: no-repeat;
- }
-}
-
-@base: #f938ab;
-
-.box-shadow(@style, @c) when (iscolor(@c)) {
- box-shadow: @style @c;
- -webkit-box-shadow: @style @c;
- -moz-box-shadow: @style @c;
-}
-.box-shadow(@style, @alpha: 50%) when (isnumber(@alpha)) {
- .box-shadow(@style, rgba(0, 0, 0, @alpha));
-}
-
-@color: #4D926F;
-
-#header {
- color: @color;
- color: #000000;
-}
-h2 {
- color: @color;
-}
-
-.rounded-corners (@radius: 5px) {
- border-radius: @radius;
- -webkit-border-radius: @radius;
- -moz-border-radius: @radius;
-}
-
-#header {
- .rounded-corners;
-}
-#footer {
- .rounded-corners(10px);
-}
-
-.box-shadow (@x: 0, @y: 0, @blur: 1px, @alpha) {
- @val: @x @y @blur rgba(0, 0, 0, @alpha);
-
- box-shadow: @val;
- -webkit-box-shadow: @val;
- -moz-box-shadow: @val;
-}
-.box { @base: #f938ab;
- color: saturate(@base, 5%);
- border-color: lighten(@base, 30%);
- div { .box-shadow(0, 0, 5px, 0.4) }
-}
-
-@import url("something.css");
-
-@light-blue: hsl(190, 50%, 65%);
-@light-yellow: desaturate(#fefec8, 10%);
-@dark-yellow: desaturate(darken(@light-yellow, 10%), 40%);
-@darkest: hsl(20, 0%, 15%);
-@dark: hsl(190, 20%, 30%);
-@medium: hsl(10, 60%, 30%);
-@light: hsl(90, 40%, 20%);
-@lightest: hsl(90, 20%, 90%);
-@highlight: hsl(80, 50%, 90%);
-@blue: hsl(210, 60%, 20%);
-@alpha-blue: hsla(210, 60%, 40%, 0.5);
-
-.box-shadow (@x, @y, @blur, @alpha) {
- @value: @x @y @blur rgba(0, 0, 0, @alpha);
- box-shadow: @value;
- -moz-box-shadow: @value;
- -webkit-box-shadow: @value;
-}
-.border-radius (@radius) {
- border-radius: @radius;
- -moz-border-radius: @radius;
- -webkit-border-radius: @radius;
-}
-
-.border-radius (@radius, bottom) {
- border-top-right-radius: 0;
- border-top-left-radius: 0;
- -moz-border-top-right-radius: 0;
- -moz-border-top-left-radius: 0;
- -webkit-border-top-left-radius: 0;
- -webkit-border-top-right-radius: 0;
-}
-.border-radius (@radius, right) {
- border-bottom-left-radius: 0;
- border-top-left-radius: 0;
- -moz-border-bottom-left-radius: 0;
- -moz-border-top-left-radius: 0;
- -webkit-border-bottom-left-radius: 0;
- -webkit-border-top-left-radius: 0;
-}
-.box-shadow-inset (@x, @y, @blur, @color) {
- box-shadow: @x @y @blur @color inset;
- -moz-box-shadow: @x @y @blur @color inset;
- -webkit-box-shadow: @x @y @blur @color inset;
-}
-.code () {
- font-family: 'Bitstream Vera Sans Mono',
- 'DejaVu Sans Mono',
- 'Monaco',
- Courier,
- monospace !important;
-}
-.wrap () {
- text-wrap: wrap;
- white-space: pre-wrap; /* css-3 */
- white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
- white-space: -pre-wrap; /* Opera 4-6 */
- white-space: -o-pre-wrap; /* Opera 7 */
- word-wrap: break-word; /* Internet Explorer 5.5+ */
-}
-
-html { margin: 0 }
-body {
- background-color: @darkest;
- margin: 0 auto;
- font-family: Arial, sans-serif;
- font-size: 100%;
- overflow-x: hidden;
-}
-nav, header, footer, section, article {
- display: block;
-}
-a {
- color: #b83000;
-}
-h1 a {
- color: black;
- text-decoration: none;
-}
-a:hover {
- text-decoration: underline;
-}
-h1, h2, h3, h4 {
- margin: 0;
- font-weight: normal;
-}
-ul, li {
- list-style-type: none;
-}
-code { .code; }
-code {
- .string, .regexp { color: @dark }
- .keyword { font-weight: bold }
- .comment { color: rgba(0, 0, 0, 0.5) }
- .number { color: @blue }
- .class, .special { color: rgba(0, 50, 100, 0.8) }
-}
-pre {
- padding: 0 30px;
- .wrap;
-}
-blockquote {
- font-style: italic;
-}
-body > footer {
- text-align: left;
- margin-left: 10px;
- font-style: italic;
- font-size: 18px;
- color: #888;
-}
-
-#logo {
- margin-top: 30px;
- margin-bottom: 30px;
- display: block;
- width: 199px;
- height: 81px;
- background: url(/images/logo.png) no-repeat;
-}
-nav {
- margin-left: 15px;
-}
-nav a, #dropdown li {
- display: inline-block;
- color: white;
- line-height: 42px;
- margin: 0;
- padding: 0px 15px;
- text-shadow: -1px -1px 1px rgba(0, 0, 0, 0.5);
- text-decoration: none;
- border: 2px solid transparent;
- border-width: 0 2px;
- &:hover {
- .dark-red;
- text-decoration: none;
- }
-}
-.dark-red {
- @red: @medium;
- border: 2px solid darken(@red, 25%);
- border-left-color: darken(@red, 15%);
- border-right-color: darken(@red, 15%);
- border-bottom: 0;
- border-top: 0;
- background-color: darken(@red, 10%);
-}
-
-.content {
- margin: 0 auto;
- width: 980px;
-}
-
-#menu {
- position: absolute;
- width: 100%;
- z-index: 3;
- clear: both;
- display: block;
- background-color: @blue;
- height: 42px;
- border-top: 2px solid lighten(@alpha-blue, 20%);
- border-bottom: 2px solid darken(@alpha-blue, 25%);
- .box-shadow(0, 1px, 8px, 0.6);
- -moz-box-shadow: 0 0 0 #000; // Because firefox sucks.
-
- &.docked {
- background-color: hsla(210, 60%, 40%, 0.4);
- }
- &:hover {
- background-color: @blue;
- }
-
- #dropdown {
- margin: 0 0 0 117px;
- padding: 0;
- padding-top: 5px;
- display: none;
- width: 190px;
- border-top: 2px solid @medium;
- color: @highlight;
- border: 2px solid darken(@medium, 25%);
- border-left-color: darken(@medium, 15%);
- border-right-color: darken(@medium, 15%);
- border-top-width: 0;
- background-color: darken(@medium, 10%);
- ul {
- padding: 0px;
- }
- li {
- font-size: 14px;
- display: block;
- text-align: left;
- padding: 0;
- border: 0;
- a {
- display: block;
- padding: 0px 15px;
- text-decoration: none;
- color: white;
- &:hover {
- background-color: darken(@medium, 15%);
- text-decoration: none;
- }
- }
- }
- .border-radius(5px, bottom);
- .box-shadow(0, 6px, 8px, 0.5);
- }
-}
-
-#main {
- margin: 0 auto;
- width: 100%;
- background-color: @light-blue;
- border-top: 8px solid darken(@light-blue, 5%);
-
- #intro {
- background-color: lighten(@light-blue, 25%);
- float: left;
- margin-top: -8px;
- margin-right: 5px;
-
- height: 380px;
- position: relative;
- z-index: 2;
- font-family: 'Droid Serif', 'Georgia';
- width: 395px;
- padding: 45px 20px 23px 30px;
- border: 2px dashed darken(@light-blue, 10%);
- .box-shadow(1px, 0px, 6px, 0.5);
- border-bottom: 0;
- border-top: 0;
- #download { color: transparent; border: 0; float: left; display: inline-block; margin: 15px 0 15px -5px; }
- #download img { display: inline-block}
- #download-info {
- code {
- font-size: 13px;
- }
- color: @blue + #333; display: inline; float: left; margin: 36px 0 0 15px }
- }
- h2 {
- span {
- color: @medium;
- }
- color: @blue;
- margin: 20px 0;
- font-size: 24px;
- line-height: 1.2em;
- }
- h3 {
- color: @blue;
- line-height: 1.4em;
- margin: 30px 0 15px 0;
- font-size: 1em;
- text-shadow: 0px 0px 0px @lightest;
- span { color: @medium }
- }
- #example {
- p {
- font-size: 18px;
- color: @blue;
- font-weight: bold;
- text-shadow: 0px 1px 1px @lightest;
- }
- pre {
- margin: 0;
- text-shadow: 0 -1px 1px @darkest;
- margin-top: 20px;
- background-color: desaturate(@darkest, 8%);
- border: 0;
- width: 450px;
- color: lighten(@lightest, 2%);
- background-repeat: repeat;
- padding: 15px;
- border: 1px dashed @lightest;
- line-height: 15px;
- .box-shadow(0, 0px, 15px, 0.5);
- .code;
- .border-radius(2px);
- code .attribute { color: hsl(40, 50%, 70%) }
- code .variable { color: hsl(120, 10%, 50%) }
- code .element { color: hsl(170, 20%, 50%) }
-
- code .string, .regexp { color: hsl(75, 50%, 65%) }
- code .class { color: hsl(40, 40%, 60%); font-weight: normal }
- code .id { color: hsl(50, 40%, 60%); font-weight: normal }
- code .comment { color: rgba(255, 255, 255, 0.2) }
- code .number, .color { color: hsl(10, 40%, 50%) }
- code .class, code .mixin, .special { color: hsl(190, 20%, 50%) }
- #time { color: #aaa }
- }
- float: right;
- font-size: 12px;
- margin: 0;
- margin-top: 15px;
- padding: 0;
- width: 500px;
- }
-}
-
-
-.page {
- .content {
- width: 870px;
- padding: 45px;
- }
- margin: 0 auto;
- font-family: 'Georgia', serif;
- font-size: 18px;
- line-height: 26px;
- padding: 0 60px;
- code {
- font-size: 16px;
- }
- pre {
- border-width: 1px;
- border-style: dashed;
- padding: 15px;
- margin: 15px 0;
- }
- h1 {
- text-align: left;
- font-size: 40px;
- margin-top: 15px;
- margin-bottom: 35px;
- }
- p + h1 { margin-top: 60px }
- h2, h3 {
- margin: 30px 0 15px 0;
- }
- p + h2, pre + h2, code + h2 {
- border-top: 6px solid rgba(255, 255, 255, 0.1);
- padding-top: 30px;
- }
- h3 {
- margin: 15px 0;
- }
-}
-
-
-#docs {
- @bg: lighten(@light-blue, 5%);
- border-top: 2px solid lighten(@bg, 5%);
- color: @blue;
- background-color: @light-blue;
- .box-shadow(0, -2px, 5px, 0.2);
-
- h1 {
- font-family: 'Droid Serif', 'Georgia', serif;
- padding-top: 30px;
- padding-left: 45px;
- font-size: 44px;
- text-align: left;
- margin: 30px 0 !important;
- text-shadow: 0px 1px 1px @lightest;
- font-weight: bold;
- }
- .content {
- clear: both;
- border-color: transparent;
- background-color: lighten(@light-blue, 25%);
- .box-shadow(0, 5px, 5px, 0.4);
- }
- pre {
- @background: lighten(@bg, 30%);
- color: lighten(@blue, 10%);
- background-color: @background;
- border-color: lighten(@light-blue, 25%);
- border-width: 2px;
- code .attribute { color: hsl(40, 50%, 30%) }
- code .variable { color: hsl(120, 10%, 30%) }
- code .element { color: hsl(170, 20%, 30%) }
-
- code .string, .regexp { color: hsl(75, 50%, 35%) }
- code .class { color: hsl(40, 40%, 30%); font-weight: normal }
- code .id { color: hsl(50, 40%, 30%); font-weight: normal }
- code .comment { color: rgba(0, 0, 0, 0.4) }
- code .number, .color { color: hsl(10, 40%, 30%) }
- code .class, code .mixin, .special { color: hsl(190, 20%, 30%) }
- }
- pre code { font-size: 15px }
- p + h2, pre + h2, code + h2 { border-top-color: rgba(0, 0, 0, 0.1) }
-}
-
-td {
- padding-right: 30px;
-}
-#synopsis {
- .box-shadow(0, 5px, 5px, 0.2);
-}
-#synopsis, #about {
- h2 {
- font-size: 30px;
- padding: 10px 0;
- }
- h1 + h2 {
- margin-top: 15px;
- }
- h3 { font-size: 22px }
-
- .code-example {
- border-spacing: 0;
- border-width: 1px;
- border-style: dashed;
- padding: 0;
- pre { border: 0; margin: 0 }
- td {
- border: 0;
- margin: 0;
- background-color: desaturate(darken(@darkest, 5%), 20%);
- vertical-align: top;
- padding: 0;
- }
- tr { padding: 0 }
- }
- .css-output {
- td {
- border-left: 0;
- }
- }
- .less-example {
- //border-right: 1px dotted rgba(255, 255, 255, 0.5) !important;
- }
- .css-output, .less-example {
- width: 390px;
- }
- pre {
- padding: 20px;
- line-height: 20px;
- font-size: 14px;
- }
-}
-#about, #synopsis, #guide {
- a {
- text-decoration: none;
- color: @light-yellow;
- border-bottom: 1px dashed rgba(255, 255, 255, 0.2);
- &:hover {
- text-decoration: none;
- border-bottom: 1px dashed @light-yellow;
- }
- }
- @bg: desaturate(darken(@darkest, 5%), 20%);
- text-shadow: 0 -1px 1px lighten(@bg, 5%);
- color: @highlight;
- background-color: @bg;
- .content {
- background-color: desaturate(@darkest, 20%);
- clear: both;
- .box-shadow(0, 5px, 5px, 0.4);
- }
- h1, h2, h3 {
- color: @dark-yellow;
- }
- pre {
- code .attribute { color: hsl(40, 50%, 70%) }
- code .variable { color: hsl(120, 10%, 50%) }
- code .element { color: hsl(170, 20%, 50%) }
-
- code .string, .regexp { color: hsl(75, 50%, 65%) }
- code .class { color: hsl(40, 40%, 60%); font-weight: normal }
- code .id { color: hsl(50, 40%, 60%); font-weight: normal }
- code .comment { color: rgba(255, 255, 255, 0.2) }
- code .number, .color { color: hsl(10, 40%, 50%) }
- code .class, code .mixin, .special { color: hsl(190, 20%, 50%) }
- background-color: @bg;
- border-color: darken(@light-yellow, 5%);
- }
- code {
- color: darken(@dark-yellow, 5%);
- .string, .regexp { color: desaturate(@light-blue, 15%) }
- .keyword { color: hsl(40, 40%, 60%); font-weight: normal }
- .comment { color: rgba(255, 255, 255, 0.2) }
- .number { color: lighten(@blue, 10%) }
- .class, .special { color: hsl(190, 20%, 50%) }
- }
-}
-#guide {
- background-color: @darkest;
- .content {
- background-color: transparent;
- }
-
-}
-
-#about {
- background-color: @darkest !important;
- .content {
- background-color: desaturate(lighten(@darkest, 3%), 5%);
- }
-}
-#synopsis {
- background-color: desaturate(lighten(@darkest, 3%), 5%) !important;
- .content {
- background-color: desaturate(lighten(@darkest, 3%), 5%);
- }
- pre {}
-}
-#synopsis, #guide {
- .content {
- .box-shadow(0, 0px, 0px, 0.0);
- }
-}
-#about footer {
- margin-top: 30px;
- padding-top: 30px;
- border-top: 6px solid rgba(0, 0, 0, 0.1);
- text-align: center;
- font-size: 16px;
- color: rgba(255, 255, 255, 0.35);
- #copy { font-size: 12px }
- text-shadow: -1px -1px 1px rgba(0, 0, 0, 0.02);
-}
-
-
-
- MIME types defined: text/x-less, text/css (if not previously defined).
-
diff --git a/applications/admin/static/codemirror/mode/less/less.js b/applications/admin/static/codemirror/mode/less/less.js
deleted file mode 100644
index 8384b3cd..00000000
--- a/applications/admin/static/codemirror/mode/less/less.js
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
- LESS mode - http://www.lesscss.org/
- Ported to CodeMirror by Peter Kroon
- Report bugs/issues here: https://github.com/marijnh/CodeMirror/issues
- GitHub: @peterkroon
-*/
-
-CodeMirror.defineMode("less", function(config) {
- var indentUnit = config.indentUnit, type;
- function ret(style, tp) {type = tp; return style;}
-
- var selectors = /(^\:root$|^\:nth\-child$|^\:nth\-last\-child$|^\:nth\-of\-type$|^\:nth\-last\-of\-type$|^\:first\-child$|^\:last\-child$|^\:first\-of\-type$|^\:last\-of\-type$|^\:only\-child$|^\:only\-of\-type$|^\:empty$|^\:link|^\:visited$|^\:active$|^\:hover$|^\:focus$|^\:target$|^\:lang$|^\:enabled^\:disabled$|^\:checked$|^\:first\-line$|^\:first\-letter$|^\:before$|^\:after$|^\:not$|^\:required$|^\:invalid$)/;
-
- function tokenBase(stream, state) {
- var ch = stream.next();
-
- if (ch == "@") {stream.eatWhile(/[\w\-]/); return ret("meta", stream.current());}
- else if (ch == "/" && stream.eat("*")) {
- state.tokenize = tokenCComment;
- return tokenCComment(stream, state);
- } else if (ch == "<" && stream.eat("!")) {
- state.tokenize = tokenSGMLComment;
- return tokenSGMLComment(stream, state);
- } else if (ch == "=") ret(null, "compare");
- else if (ch == "|" && stream.eat("=")) return ret(null, "compare");
- else if (ch == "\"" || ch == "'") {
- state.tokenize = tokenString(ch);
- return state.tokenize(stream, state);
- } else if (ch == "/") { // e.g.: .png will not be parsed as a class
- if(stream.eat("/")){
- state.tokenize = tokenSComment;
- return tokenSComment(stream, state);
- } else {
- if(type == "string" || type == "(") return ret("string", "string");
- if(state.stack[state.stack.length-1] != undefined) return ret(null, ch);
- stream.eatWhile(/[\a-zA-Z0-9\-_.\s]/);
- if( /\/|\)|#/.test(stream.peek() || (stream.eatSpace() && stream.peek() == ")")) || stream.eol() )return ret("string", "string"); // let url(/images/logo.png) without quotes return as string
- }
- } else if (ch == "!") {
- stream.match(/^\s*\w*/);
- return ret("keyword", "important");
- } else if (/\d/.test(ch)) {
- stream.eatWhile(/[\w.%]/);
- return ret("number", "unit");
- } else if (/[,+<>*\/]/.test(ch)) {
- if(stream.peek() == "=" || type == "a")return ret("string", "string");
- if(ch === ",")return ret(null, ch);
- return ret(null, "select-op");
- } else if (/[;{}:\[\]()~\|]/.test(ch)) {
- if(ch == ":"){
- stream.eatWhile(/[a-z\\\-]/);
- if( selectors.test(stream.current()) ){
- return ret("tag", "tag");
- } else if(stream.peek() == ":"){//::-webkit-search-decoration
- stream.next();
- stream.eatWhile(/[a-z\\\-]/);
- if(stream.current().match(/\:\:\-(o|ms|moz|webkit)\-/))return ret("string", "string");
- if( selectors.test(stream.current().substring(1)) )return ret("tag", "tag");
- return ret(null, ch);
- } else {
- return ret(null, ch);
- }
- } else if(ch == "~"){
- if(type == "r")return ret("string", "string");
- } else {
- return ret(null, ch);
- }
- } else if (ch == ".") {
- if(type == "(" || type == "string")return ret("string", "string"); // allow url(../image.png)
- stream.eatWhile(/[\a-zA-Z0-9\-_]/);
- if(stream.peek() == " ")stream.eatSpace();
- if(stream.peek() == ")")return ret("number", "unit");//rgba(0,0,0,.25);
- return ret("tag", "tag");
- } else if (ch == "#") {
- //we don't eat white-space, we want the hex color and or id only
- stream.eatWhile(/[A-Za-z0-9]/);
- //check if there is a proper hex color length e.g. #eee || #eeeEEE
- if(stream.current().length == 4 || stream.current().length == 7){
- if(stream.current().match(/[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}/,false) != null){//is there a valid hex color value present in the current stream
- //when not a valid hex value, parse as id
- if(stream.current().substring(1) != stream.current().match(/[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}/,false))return ret("atom", "tag");
- //eat white-space
- stream.eatSpace();
- //when hex value declaration doesn't end with [;,] but is does with a slash/cc comment treat it as an id, just like the other hex values that don't end with[;,]
- if( /[\/<>.(){!$%^&*_\-\\?=+\|#'~`]/.test(stream.peek()) )return ret("atom", "tag");
- //#time { color: #aaa }
- else if(stream.peek() == "}" )return ret("number", "unit");
- //we have a valid hex color value, parse as id whenever an element/class is defined after the hex(id) value e.g. #eee aaa || #eee .aaa
- else if( /[a-zA-Z\\]/.test(stream.peek()) )return ret("atom", "tag");
- //when a hex value is on the end of a line, parse as id
- else if(stream.eol())return ret("atom", "tag");
- //default
- else return ret("number", "unit");
- } else {//when not a valid hexvalue in the current stream e.g. #footer
- stream.eatWhile(/[\w\\\-]/);
- return ret("atom", "tag");
- }
- } else {//when not a valid hexvalue length
- stream.eatWhile(/[\w\\\-]/);
- return ret("atom", "tag");
- }
- } else if (ch == "&") {
- stream.eatWhile(/[\w\-]/);
- return ret(null, ch);
- } else {
- stream.eatWhile(/[\w\\\-_%.{]/);
- if(type == "string"){
- return ret("string", "string");
- } else if(stream.current().match(/(^http$|^https$)/) != null){
- stream.eatWhile(/[\w\\\-_%.{:\/]/);
- return ret("string", "string");
- } else if(stream.peek() == "<" || stream.peek() == ">" || stream.peek() == "+"){
- return ret("tag", "tag");
- } else if( /\(/.test(stream.peek()) ){
- return ret(null, ch);
- } else if (stream.peek() == "/" && state.stack[state.stack.length-1] != undefined){ // url(dir/center/image.png)
- return ret("string", "string");
- } else if( stream.current().match(/\-\d|\-.\d/) ){ // match e.g.: -5px -0.4 etc... only colorize the minus sign
- //commment out these 2 comment if you want the minus sign to be parsed as null -500px
- //stream.backUp(stream.current().length-1);
- //return ret(null, ch);
- return ret("number", "unit");
- } else if( /\/|[\s\)]/.test(stream.peek() || stream.eol() || (stream.eatSpace() && stream.peek() == "/")) && stream.current().indexOf(".") !== -1){
- if(stream.current().substring(stream.current().length-1,stream.current().length) == "{"){
- stream.backUp(1);
- return ret("tag", "tag");
- }//end if
- stream.eatSpace();
- if( /[{<>.a-zA-Z\/]/.test(stream.peek()) || stream.eol() )return ret("tag", "tag"); // e.g. button.icon-plus
- return ret("string", "string"); // let url(/images/logo.png) without quotes return as string
- } else if( stream.eol() || stream.peek() == "[" || stream.peek() == "#" || type == "tag" ){
- if(stream.current().substring(stream.current().length-1,stream.current().length) == "{")stream.backUp(1);
- return ret("tag", "tag");
- } else if(type == "compare" || type == "a" || type == "("){
- return ret("string", "string");
- } else if(type == "|" || stream.current() == "-" || type == "["){
- if(type == "|" )return ret("tag", "tag");
- return ret(null, ch);
- } else if(stream.peek() == ":") {
- stream.next();
- var t_v = stream.peek() == ":" ? true : false;
- if(!t_v){
- var old_pos = stream.pos;
- var sc = stream.current().length;
- stream.eatWhile(/[a-z\\\-]/);
- var new_pos = stream.pos;
- if(stream.current().substring(sc-1).match(selectors) != null){
- stream.backUp(new_pos-(old_pos-1));
- return ret("tag", "tag");
- } else stream.backUp(new_pos-(old_pos-1));
- } else {
- stream.backUp(1);
- }
- if(t_v)return ret("tag", "tag"); else return ret("variable", "variable");
- } else if(state.stack[state.stack.length-1] === "font-family"){
- return ret(null, null);
- } else {
- if(state.stack[state.stack.length-1] === "{" || type === "select-op" || (state.stack[state.stack.length-1] === "rule" && type === ",") )return ret("tag", "tag");
- return ret("variable", "variable");
- }
- }
- }
-
- function tokenSComment(stream, state) { // SComment = Slash comment
- stream.skipToEnd();
- state.tokenize = tokenBase;
- return ret("comment", "comment");
- }
-
- function tokenCComment(stream, state) {
- var maybeEnd = false, ch;
- while ((ch = stream.next()) != null) {
- if (maybeEnd && ch == "/") {
- state.tokenize = tokenBase;
- break;
- }
- maybeEnd = (ch == "*");
- }
- return ret("comment", "comment");
- }
-
- function tokenSGMLComment(stream, state) {
- var dashes = 0, ch;
- while ((ch = stream.next()) != null) {
- if (dashes >= 2 && ch == ">") {
- state.tokenize = tokenBase;
- break;
- }
- dashes = (ch == "-") ? dashes + 1 : 0;
- }
- return ret("comment", "comment");
- }
-
- function tokenString(quote) {
- return function(stream, state) {
- var escaped = false, ch;
- while ((ch = stream.next()) != null) {
- if (ch == quote && !escaped)
- break;
- escaped = !escaped && ch == "\\";
- }
- if (!escaped) state.tokenize = tokenBase;
- return ret("string", "string");
- };
- }
-
- return {
- startState: function(base) {
- return {tokenize: tokenBase,
- baseIndent: base || 0,
- stack: []};
- },
-
- token: function(stream, state) {
- if (stream.eatSpace()) return null;
- var style = state.tokenize(stream, state);
-
- var context = state.stack[state.stack.length-1];
- if (type == "hash" && context == "rule") style = "atom";
- else if (style == "variable") {
- if (context == "rule") style = null; //"tag"
- else if (!context || context == "@media{") {
- style = stream.current() == "when" ? "variable" :
- /[\s,|\s\)|\s]/.test(stream.peek()) ? "tag" : type;
- }
- }
-
- if (context == "rule" && /^[\{\};]$/.test(type))
- state.stack.pop();
- if (type == "{") {
- if (context == "@media") state.stack[state.stack.length-1] = "@media{";
- else state.stack.push("{");
- }
- else if (type == "}") state.stack.pop();
- else if (type == "@media") state.stack.push("@media");
- else if (stream.current() === "font-family") state.stack[state.stack.length-1] = "font-family";
- else if (context == "{" && type != "comment" && type !== "tag") state.stack.push("rule");
- else if (stream.peek() === ":" && stream.current().match(/@|#/) === null) style = type;
- return style;
- },
-
- indent: function(state, textAfter) {
- var n = state.stack.length;
-
- if (/^\}/.test(textAfter))
- n -= state.stack[state.stack.length-1] == "rule" ? 2 : 1;
- return state.baseIndent + n * indentUnit;
- },
-
- electricChars: "}"
- };
-});
-
-CodeMirror.defineMIME("text/x-less", "less");
-if (!CodeMirror.mimeModes.hasOwnProperty("text/css"))
- CodeMirror.defineMIME("text/css", "less");
diff --git a/applications/admin/static/codemirror/mode/livescript/LICENSE b/applications/admin/static/codemirror/mode/livescript/LICENSE
deleted file mode 100644
index a675c402..00000000
--- a/applications/admin/static/codemirror/mode/livescript/LICENSE
+++ /dev/null
@@ -1,23 +0,0 @@
-The MIT License
-
-Copyright (c) 2013 Kenneth Bentley
-Modified from the CoffeeScript CodeMirror mode, Copyright (c) 2011 Jeff Pickhardt
-Modified from the Python CodeMirror mode, Copyright (c) 2010 Timothy Farrell
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
diff --git a/applications/admin/static/codemirror/mode/livescript/index.html b/applications/admin/static/codemirror/mode/livescript/index.html
index b5944697..f4154798 100644
--- a/applications/admin/static/codemirror/mode/livescript/index.html
+++ b/applications/admin/static/codemirror/mode/livescript/index.html
@@ -10,12 +10,12 @@
-
+
CodeMirror
Language modes
@@ -454,6 +454,6 @@ export prelude = out$
MIME types defined: text/x-livescript.
- The LiveScript mode was written by Kenneth Bentley (license ).
+ The LiveScript mode was written by Kenneth Bentley.
diff --git a/applications/admin/static/codemirror/mode/livescript/livescript.js b/applications/admin/static/codemirror/mode/livescript/livescript.js
index c000324b..55882efc 100644
--- a/applications/admin/static/codemirror/mode/livescript/livescript.js
+++ b/applications/admin/static/codemirror/mode/livescript/livescript.js
@@ -1,19 +1,32 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
/**
* Link to the project's GitHub page:
* https://github.com/duralog/CodeMirror
*/
-(function() {
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
CodeMirror.defineMode('livescript', function(){
- var tokenBase, external;
- tokenBase = function(stream, state){
- var next_rule, nr, i$, len$, r, m;
- if (next_rule = state.next || 'start') {
+ var tokenBase = function(stream, state) {
+ var next_rule = state.next || "start";
+ if (next_rule) {
state.next = state.next;
- if (Array.isArray(nr = Rules[next_rule])) {
- for (i$ = 0, len$ = nr.length; i$ < len$; ++i$) {
- r = nr[i$];
+ var nr = Rules[next_rule];
+ if (nr.splice) {
+ for (var i$ = 0; i$ < nr.length; ++i$) {
+ var r = nr[i$], m;
if (r.regex && (m = stream.match(r.regex))) {
- state.next = r.next;
+ state.next = r.next || state.next;
return r.token;
}
}
@@ -33,7 +46,7 @@
stream.next();
return 'error';
};
- external = {
+ var external = {
startState: function(){
return {
next: 'start',
@@ -41,8 +54,8 @@
};
},
token: function(stream, state){
- var style;
- style = tokenBase(stream, state);
+ while (stream.pos == stream.start)
+ var style = tokenBase(stream, state);
state.lastToken = {
style: style,
indent: stream.indentation(),
@@ -51,8 +64,7 @@
return style.replace(/\./g, ' ');
},
indent: function(state){
- var indentation;
- indentation = state.lastToken.indent;
+ var indentation = state.lastToken.indent;
if (state.lastToken.content.match(indenter)) {
indentation += 2;
}
@@ -192,7 +204,7 @@
next: 'start'
}, {
token: 'text',
- regex: '.',
+ regex: '',
next: 'start'
}
],
@@ -251,17 +263,18 @@
};
for (var idx in Rules) {
var r = Rules[idx];
- if (Array.isArray(r)) {
+ if (r.splice) {
for (var i = 0, len = r.length; i < len; ++i) {
var rr = r[i];
- if (rr.regex) {
+ if (typeof rr.regex === 'string') {
Rules[idx][i].regex = new RegExp('^' + rr.regex);
}
}
- } else if (r.regex) {
+ } else if (typeof rr.regex === 'string') {
Rules[idx].regex = new RegExp('^' + r.regex);
}
}
-})();
-CodeMirror.defineMIME('text/x-livescript', 'livescript');
+ CodeMirror.defineMIME('text/x-livescript', 'livescript');
+
+});
diff --git a/applications/admin/static/codemirror/mode/livescript/livescript.ls b/applications/admin/static/codemirror/mode/livescript/livescript.ls
deleted file mode 100644
index 06524231..00000000
--- a/applications/admin/static/codemirror/mode/livescript/livescript.ls
+++ /dev/null
@@ -1,266 +0,0 @@
-/**
- * Link to the project's GitHub page:
- * https://github.com/duralog/CodeMirror
- */
-CodeMirror.defineMode 'livescript', (conf) ->
- tokenBase = (stream, state) ->
- #indent =
- if next_rule = state.next or \start
- state.next = state.next
- if Array.isArray nr = Rules[next_rule]
- for r in nr
- if r.regex and m = stream.match r.regex
- state.next = r.next
- return r.token
- stream.next!
- return \error
- if stream.match r = Rules[next_rule]
- if r.regex and stream.match r.regex
- state.next = r.next
- return r.token
- else
- stream.next!
- return \error
- stream.next!
- return 'error'
- external = {
- startState: (basecolumn) ->
- {
- next: \start
- lastToken: null
- }
- token: (stream, state) ->
- style = tokenBase stream, state #tokenLexer stream, state
- state.lastToken = {
- style: style
- indent: stream.indentation!
- content: stream.current!
- }
- style.replace /\./g, ' '
- indent: (state, textAfter) ->
- # XXX this won't work with backcalls
- indentation = state.lastToken.indent
- if state.lastToken.content.match indenter then indentation += 2
- return indentation
- }
- external
-
-### Highlight Rules
-# taken from mode-ls.ls
-
-indenter = // (?
- : [({[=:]
- | [-~]>
- | \b (?: e(?:lse|xport) | d(?:o|efault) | t(?:ry|hen) | finally |
- import (?:\s* all)? | const | var |
- let | new | catch (?:\s* #identifier)? )
- ) \s* $ //
-
-identifier = /(?![\d\s])[$\w\xAA-\uFFDC](?:(?!\s)[$\w\xAA-\uFFDC]|-[A-Za-z])*/$
-keywordend = /(?![$\w]|-[A-Za-z]|\s*:(?![:=]))/$
-stringfill = token: \string, regex: '.+'
-
-Rules =
- start:
- * token: \comment.doc
- regex: '/\\*'
- next : \comment
-
- * token: \comment
- regex: '#.*'
-
- * token: \keyword
- regex: //(?
- :t(?:h(?:is|row|en)|ry|ypeof!?)
- |c(?:on(?:tinue|st)|a(?:se|tch)|lass)
- |i(?:n(?:stanceof)?|mp(?:ort(?:\s+all)?|lements)|[fs])
- |d(?:e(?:fault|lete|bugger)|o)
- |f(?:or(?:\s+own)?|inally|unction)
- |s(?:uper|witch)
- |e(?:lse|x(?:tends|port)|val)
- |a(?:nd|rguments)
- |n(?:ew|ot)
- |un(?:less|til)
- |w(?:hile|ith)
- |o[fr]|return|break|let|var|loop
- )//$ + keywordend
-
- * token: \constant.language
- regex: '(?:true|false|yes|no|on|off|null|void|undefined)' + keywordend
-
- * token: \invalid.illegal
- regex: '(?
- :p(?:ackage|r(?:ivate|otected)|ublic)
- |i(?:mplements|nterface)
- |enum|static|yield
- )' + keywordend
-
- * token: \language.support.class
- regex: '(?
- :R(?:e(?:gExp|ferenceError)|angeError)
- |S(?:tring|yntaxError)
- |E(?:rror|valError)
- |Array|Boolean|Date|Function|Number|Object|TypeError|URIError
- )' + keywordend
-
- * token: \language.support.function
- regex: '(?
- :is(?:NaN|Finite)
- |parse(?:Int|Float)
- |Math|JSON
- |(?:en|de)codeURI(?:Component)?
- )' + keywordend
-
- * token: \variable.language
- regex: '(?:t(?:hat|il|o)|f(?:rom|allthrough)|it|by|e)' + keywordend
-
- * token: \identifier
- regex: identifier + /\s*:(?![:=])/$
-
- * token: \variable
- regex: identifier
-
- * token: \keyword.operator
- regex: /(?:\.{3}|\s+\?)/$
-
- * token: \keyword.variable
- regex: /(?:@+|::|\.\.)/$
- next : \key
-
- * token: \keyword.operator
- regex: /\.\s*/$
- next : \key
-
- * token: \string
- regex: /\\\S[^\s,;)}\]]*/$
-
- * token: \string.doc
- regex: \'''
- next : \qdoc
-
- * token: \string.doc
- regex: \"""
- next : \qqdoc
-
- * token: \string
- regex: \'
- next : \qstring
-
- * token: \string
- regex: \"
- next : \qqstring
-
- * token: \string
- regex: \`
- next : \js
-
- * token: \string
- regex: '<\\['
- next : \words
-
- * token: \string.regex
- regex: \//
- next : \heregex
-
- * token: \string.regex
- regex: //
- /(?: [^ [ / \n \\ ]*
- (?: (?: \\.
- | \[ [^\]\n\\]* (?:\\.[^\]\n\\]*)* \]
- ) [^ [ / \n \\ ]*
- )*
- )/ [gimy$]{0,4}
- //$
- next : \key
-
- * token: \constant.numeric
- regex: '(?:0x[\\da-fA-F][\\da-fA-F_]*
- |(?:[2-9]|[12]\\d|3[0-6])r[\\da-zA-Z][\\da-zA-Z_]*
- |(?:\\d[\\d_]*(?:\\.\\d[\\d_]*)?|\\.\\d[\\d_]*)
- (?:e[+-]?\\d[\\d_]*)?[\\w$]*)'
-
- * token: \lparen
- regex: '[({[]'
-
- * token: \rparen
- regex: '[)}\\]]'
- next : \key
-
- * token: \keyword.operator
- regex: \\\S+
-
- * token: \text
- regex: \\\s+
-
- heregex:
- * token: \string.regex
- regex: '.*?//[gimy$?]{0,4}'
- next : \start
- * token: \string.regex
- regex: '\\s*#{'
- * token: \comment.regex
- regex: '\\s+(?:#.*)?'
- * token: \string.regex
- regex: '\\S+'
-
- key:
- * token: \keyword.operator
- regex: '[.?@!]+'
- * token: \identifier
- regex: identifier
- next : \start
- * token: \text
- regex: '.'
- next : \start
-
- comment:
- * token: \comment.doc
- regex: '.*?\\*/'
- next : \start
- * token: \comment.doc
- regex: '.+'
-
- qdoc:
- token: \string
- regex: ".*?'''"
- next : \key
- stringfill
-
- qqdoc:
- token: \string
- regex: '.*?"""'
- next : \key
- stringfill
-
- qstring:
- token: \string
- regex: /[^\\']*(?:\\.[^\\']*)*'/$
- next : \key
- stringfill
-
- qqstring:
- token: \string
- regex: /[^\\"]*(?:\\.[^\\"]*)*"/$
- next : \key
- stringfill
-
- js:
- token: \string
- regex: /[^\\`]*(?:\\.[^\\`]*)*`/$
- next : \key
- stringfill
-
- words:
- token: \string
- regex: '.*?\\]>'
- next : \key
- stringfill
-
-# for optimization, precompile the regexps
-for idx, r of Rules
- if Array.isArray r
- for rr, i in r
- if rr.regex then Rules[idx][i].regex = new RegExp '^'+rr.regex
- else if r.regex then Rules[idx].regex = new RegExp '^'+r.regex
-
-CodeMirror.defineMIME 'text/x-livescript', 'livescript'
diff --git a/applications/admin/static/codemirror/mode/markdown/index.html b/applications/admin/static/codemirror/mode/markdown/index.html
index a6b541e2..c3bb8df9 100644
--- a/applications/admin/static/codemirror/mode/markdown/index.html
+++ b/applications/admin/static/codemirror/mode/markdown/index.html
@@ -16,12 +16,12 @@
.cm-s-default .cm-trailing-space-new-line:before {position: absolute; content: "\21B5"; color: #777;}
-
+
CodeMirror
Language modes
diff --git a/applications/admin/static/codemirror/mode/markdown/markdown.js b/applications/admin/static/codemirror/mode/markdown/markdown.js
index bf1750d5..3c803110 100644
--- a/applications/admin/static/codemirror/mode/markdown/markdown.js
+++ b/applications/admin/static/codemirror/mode/markdown/markdown.js
@@ -1,46 +1,39 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"), require("../xml/xml"), require("../meta"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror", "../xml/xml", "../meta"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
var htmlFound = CodeMirror.modes.hasOwnProperty("xml");
var htmlMode = CodeMirror.getMode(cmCfg, htmlFound ? {name: "xml", htmlMode: true} : "text/plain");
- var aliases = {
- html: "htmlmixed",
- js: "javascript",
- json: "application/json",
- c: "text/x-csrc",
- "c++": "text/x-c++src",
- java: "text/x-java",
- csharp: "text/x-csharp",
- "c#": "text/x-csharp",
- scala: "text/x-scala"
- };
- var getMode = (function () {
- var i, modes = {}, mimes = {}, mime;
-
- var list = [];
- for (var m in CodeMirror.modes)
- if (CodeMirror.modes.propertyIsEnumerable(m)) list.push(m);
- for (i = 0; i < list.length; i++) {
- modes[list[i]] = list[i];
- }
- var mimesList = [];
- for (var m in CodeMirror.mimeModes)
- if (CodeMirror.mimeModes.propertyIsEnumerable(m))
- mimesList.push({mime: m, mode: CodeMirror.mimeModes[m]});
- for (i = 0; i < mimesList.length; i++) {
- mime = mimesList[i].mime;
- mimes[mime] = mimesList[i].mime;
+ function getMode(name) {
+ if (CodeMirror.findModeByName) {
+ var found = CodeMirror.findModeByName(name);
+ if (found) name = found.mime || found.mimes[0];
}
+ var mode = CodeMirror.getMode(cmCfg, name);
+ return mode.name == "null" ? null : mode;
+ }
- for (var a in aliases) {
- if (aliases[a] in modes || aliases[a] in mimes)
- modes[a] = aliases[a];
- }
+ // Should characters that affect highlighting be highlighted separate?
+ // Does not include characters that will be output (such as `1.` and `-` for lists)
+ if (modeCfg.highlightFormatting === undefined)
+ modeCfg.highlightFormatting = false;
- return function (lang) {
- return modes[lang] ? CodeMirror.getMode(cmCfg, modes[lang]) : null;
- };
- }());
+ // Maximum number of nested blockquotes. Set to 0 for infinite nesting.
+ // Excess `>` will emit `error` token.
+ if (modeCfg.maxBlockquoteDepth === undefined)
+ modeCfg.maxBlockquoteDepth = 0;
// Should underscores in words open/close em/strong?
if (modeCfg.underscoresBreakWords === undefined)
@@ -52,30 +45,36 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
// Turn on task lists? ("- [ ] " and "- [x] ")
if (modeCfg.taskLists === undefined) modeCfg.taskLists = false;
+ // Turn on strikethrough syntax
+ if (modeCfg.strikethrough === undefined)
+ modeCfg.strikethrough = false;
+
var codeDepth = 0;
var header = 'header'
, code = 'comment'
- , quote1 = 'atom'
- , quote2 = 'number'
+ , quote = 'quote'
, list1 = 'variable-2'
, list2 = 'variable-3'
, list3 = 'keyword'
, hr = 'hr'
, image = 'tag'
+ , formatting = 'formatting'
, linkinline = 'link'
, linkemail = 'link'
, linktext = 'link'
, linkhref = 'string'
, em = 'em'
- , strong = 'strong';
+ , strong = 'strong'
+ , strikethrough = 'strikethrough';
var hrRE = /^([*\-=_])(?:\s*\1){2,}\s*$/
, ulRE = /^[*\-+]\s+/
, olRE = /^[0-9]+\.\s+/
, taskListRE = /^\[(x| )\](?=\s)/ // Must follow ulRE or olRE
- , headerRE = /^(?:\={1,}|-{1,})$/
- , textRE = /^[^!\[\]*_\\<>` "'(]+/;
+ , atxHeaderRE = /^#+/
+ , setextHeaderRE = /^(?:\={1,}|-{1,})$/
+ , textRE = /^[^#!\[\]*_\\<>` "'(~]+/;
function switchInline(stream, state, f) {
state.f = state.inline = f;
@@ -97,6 +96,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
state.em = false;
// Reset STRONG state
state.strong = false;
+ // Reset strikethrough state
+ state.strikethrough = false;
// Reset state.quote
state.quote = 0;
if (!htmlFound && state.f == htmlBlock) {
@@ -113,6 +114,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
function blockNormal(stream, state) {
+ var sol = stream.sol();
+
var prevLineIsList = (state.list !== false);
if (state.list !== false && state.indentationDiff >= 0) { // Continued list
if (state.indentationDiff < 4) { // Only adjust indentation if *not* a code block
@@ -127,39 +130,58 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
state.listDepth = 0;
}
+ var match = null;
if (state.indentationDiff >= 4) {
state.indentation -= 4;
stream.skipToEnd();
return code;
} else if (stream.eatSpace()) {
return null;
- } else if (stream.peek() === '#' || (state.prevLineHasContent && stream.match(headerRE)) ) {
- state.header = true;
+ } else if (match = stream.match(atxHeaderRE)) {
+ state.header = match[0].length <= 6 ? match[0].length : 6;
+ if (modeCfg.highlightFormatting) state.formatting = "header";
+ state.f = state.inline;
+ return getType(state);
+ } else if (state.prevLineHasContent && (match = stream.match(setextHeaderRE))) {
+ state.header = match[0].charAt(0) == '=' ? 1 : 2;
+ if (modeCfg.highlightFormatting) state.formatting = "header";
+ state.f = state.inline;
+ return getType(state);
} else if (stream.eat('>')) {
state.indentation++;
- state.quote = 1;
+ state.quote = sol ? 1 : state.quote + 1;
+ if (modeCfg.highlightFormatting) state.formatting = "quote";
stream.eatSpace();
- while (stream.eat('>')) {
- stream.eatSpace();
- state.quote++;
- }
+ return getType(state);
} else if (stream.peek() === '[') {
return switchInline(stream, state, footnoteLink);
} else if (stream.match(hrRE, true)) {
return hr;
- } else if ((!state.prevLineHasContent || prevLineIsList) && (stream.match(ulRE, true) || stream.match(olRE, true))) {
+ } else if ((!state.prevLineHasContent || prevLineIsList) && (stream.match(ulRE, false) || stream.match(olRE, false))) {
+ var listType = null;
+ if (stream.match(ulRE, true)) {
+ listType = 'ul';
+ } else {
+ stream.match(olRE, true);
+ listType = 'ol';
+ }
state.indentation += 4;
state.list = true;
state.listDepth++;
if (modeCfg.taskLists && stream.match(taskListRE, false)) {
state.taskList = true;
}
- } else if (modeCfg.fencedCodeBlocks && stream.match(/^```([\w+#]*)/, true)) {
+ state.f = state.inline;
+ if (modeCfg.highlightFormatting) state.formatting = ["list", "list-" + listType];
+ return getType(state);
+ } else if (modeCfg.fencedCodeBlocks && stream.match(/^```[ \t]*([\w+#]*)/, true)) {
// try switching mode
state.localMode = getMode(RegExp.$1);
if (state.localMode) state.localState = state.localMode.startState();
- switchBlock(stream, state, local);
- return code;
+ state.f = state.block = local;
+ if (modeCfg.highlightFormatting) state.formatting = "code-block";
+ state.code = true;
+ return getType(state);
}
return switchInline(stream, state, state.inline);
@@ -167,24 +189,20 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
function htmlBlock(stream, state) {
var style = htmlMode.token(stream, state.htmlState);
- if (htmlFound && style === 'tag' && state.htmlState.type !== 'openTag' && !state.htmlState.context) {
+ if ((htmlFound && state.htmlState.tagStart === null && !state.htmlState.context) ||
+ (state.md_inside && stream.current().indexOf(">") > -1)) {
state.f = inlineNormal;
state.block = blockNormal;
- }
- if (state.md_inside && stream.current().indexOf(">")!=-1) {
- state.f = inlineNormal;
- state.block = blockNormal;
- state.htmlState.context = undefined;
+ state.htmlState = null;
}
return style;
}
function local(stream, state) {
- if (stream.sol() && stream.match(/^```/, true)) {
+ if (stream.sol() && stream.match("```", false)) {
state.localMode = state.localState = null;
- state.f = inlineNormal;
- state.block = blockNormal;
- return code;
+ state.f = state.block = leavingLocal;
+ return null;
} else if (state.localMode) {
return state.localMode.token(stream, state.localState);
} else {
@@ -193,22 +211,80 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
}
}
+ function leavingLocal(stream, state) {
+ stream.match("```");
+ state.block = blockNormal;
+ state.f = inlineNormal;
+ if (modeCfg.highlightFormatting) state.formatting = "code-block";
+ state.code = true;
+ var returnType = getType(state);
+ state.code = false;
+ return returnType;
+ }
+
// Inline
function getType(state) {
var styles = [];
- if (state.taskOpen) { return "meta"; }
- if (state.taskClosed) { return "property"; }
+ if (state.formatting) {
+ styles.push(formatting);
+
+ if (typeof state.formatting === "string") state.formatting = [state.formatting];
+
+ for (var i = 0; i < state.formatting.length; i++) {
+ styles.push(formatting + "-" + state.formatting[i]);
+
+ if (state.formatting[i] === "header") {
+ styles.push(formatting + "-" + state.formatting[i] + "-" + state.header);
+ }
+
+ // Add `formatting-quote` and `formatting-quote-#` for blockquotes
+ // Add `error` instead if the maximum blockquote nesting depth is passed
+ if (state.formatting[i] === "quote") {
+ if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
+ styles.push(formatting + "-" + state.formatting[i] + "-" + state.quote);
+ } else {
+ styles.push("error");
+ }
+ }
+ }
+ }
+
+ if (state.taskOpen) {
+ styles.push("meta");
+ return styles.length ? styles.join(' ') : null;
+ }
+ if (state.taskClosed) {
+ styles.push("property");
+ return styles.length ? styles.join(' ') : null;
+ }
+
+ if (state.linkHref) {
+ styles.push(linkhref);
+ return styles.length ? styles.join(' ') : null;
+ }
if (state.strong) { styles.push(strong); }
if (state.em) { styles.push(em); }
+ if (state.strikethrough) { styles.push(strikethrough); }
if (state.linkText) { styles.push(linktext); }
if (state.code) { styles.push(code); }
- if (state.header) { styles.push(header); }
- if (state.quote) { styles.push(state.quote % 2 ? quote1 : quote2); }
+ if (state.header) { styles.push(header); styles.push(header + "-" + state.header); }
+
+ if (state.quote) {
+ styles.push(quote);
+
+ // Add `quote-#` where the maximum for `#` is modeCfg.maxBlockquoteDepth
+ if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
+ styles.push(quote + "-" + state.quote);
+ } else {
+ styles.push(quote + "-" + modeCfg.maxBlockquoteDepth);
+ }
+ }
+
if (state.list !== false) {
var listMod = (state.listDepth - 1) % 3;
if (!listMod) {
@@ -250,6 +326,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
var taskOpen = stream.match(taskListRE, true)[1] !== "x";
if (taskOpen) state.taskOpen = true;
else state.taskClosed = true;
+ if (modeCfg.highlightFormatting) state.formatting = "task";
state.taskList = false;
return getType(state);
}
@@ -257,11 +334,22 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
state.taskOpen = false;
state.taskClosed = false;
+ if (state.header && stream.match(/^#+$/, true)) {
+ if (modeCfg.highlightFormatting) state.formatting = "header";
+ return getType(state);
+ }
+
+ // Get sol() value now, before character is consumed
+ var sol = stream.sol();
+
var ch = stream.next();
if (ch === '\\') {
stream.next();
- return getType(state);
+ if (modeCfg.highlightFormatting) {
+ var type = getType(state);
+ return type ? type + " formatting-escape" : "formatting-escape";
+ }
}
// Matches link titles present on next line
@@ -280,6 +368,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
// If this block is changed, it may need to be updated in GFM mode
if (ch === '`') {
+ var previousFormatting = state.formatting;
+ if (modeCfg.highlightFormatting) state.formatting = "code";
var t = getType(state);
var before = stream.pos;
stream.eatWhile('`');
@@ -293,6 +383,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
state.code = false;
return t;
}
+ state.formatting = previousFormatting;
return getType(state);
}
} else if (state.code) {
@@ -305,12 +396,14 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
return image;
}
- if (ch === '[' && stream.match(/.*\](\(| ?\[)/, false)) {
+ if (ch === '[' && stream.match(/.*\](\(.*\)| ?\[.*\])/, false)) {
state.linkText = true;
+ if (modeCfg.highlightFormatting) state.formatting = "link";
return getType(state);
}
- if (ch === ']' && state.linkText) {
+ if (ch === ']' && state.linkText && stream.match(/\(.*\)| ?\[.*\]/, false)) {
+ if (modeCfg.highlightFormatting) state.formatting = "link";
var type = getType(state);
state.linkText = false;
state.inline = state.f = linkHref;
@@ -318,21 +411,38 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
}
if (ch === '<' && stream.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/, false)) {
- return switchInline(stream, state, inlineElement(linkinline, '>'));
+ state.f = state.inline = linkInline;
+ if (modeCfg.highlightFormatting) state.formatting = "link";
+ var type = getType(state);
+ if (type){
+ type += " ";
+ } else {
+ type = "";
+ }
+ return type + linkinline;
}
if (ch === '<' && stream.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/, false)) {
- return switchInline(stream, state, inlineElement(linkemail, '>'));
+ state.f = state.inline = linkInline;
+ if (modeCfg.highlightFormatting) state.formatting = "link";
+ var type = getType(state);
+ if (type){
+ type += " ";
+ } else {
+ type = "";
+ }
+ return type + linkemail;
}
if (ch === '<' && stream.match(/^\w/, false)) {
- if (stream.string.indexOf(">")!=-1) {
+ if (stream.string.indexOf(">") != -1) {
var atts = stream.string.substring(1,stream.string.indexOf(">"));
if (/markdown\s*=\s*('|"){0,1}1('|"){0,1}/.test(atts)) {
state.md_inside = true;
}
}
stream.backUp(1);
+ state.htmlState = CodeMirror.startState(htmlMode);
return switchBlock(stream, state, htmlBlock);
}
@@ -353,19 +463,26 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
}
}
}
- var t = getType(state);
if (ch === '*' || (ch === '_' && !ignoreUnderscore)) {
- if (state.strong === ch && stream.eat(ch)) { // Remove STRONG
+ if (sol && stream.peek() === ' ') {
+ // Do nothing, surrounded by newline and space
+ } else if (state.strong === ch && stream.eat(ch)) { // Remove STRONG
+ if (modeCfg.highlightFormatting) state.formatting = "strong";
+ var t = getType(state);
state.strong = false;
return t;
} else if (!state.strong && stream.eat(ch)) { // Add STRONG
state.strong = ch;
+ if (modeCfg.highlightFormatting) state.formatting = "strong";
return getType(state);
} else if (state.em === ch) { // Remove EM
+ if (modeCfg.highlightFormatting) state.formatting = "em";
+ var t = getType(state);
state.em = false;
return t;
} else if (!state.em) { // Add EM
state.em = ch;
+ if (modeCfg.highlightFormatting) state.formatting = "em";
return getType(state);
}
} else if (ch === ' ') {
@@ -378,6 +495,29 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
}
}
+ if (modeCfg.strikethrough) {
+ if (ch === '~' && stream.eatWhile(ch)) {
+ if (state.strikethrough) {// Remove strikethrough
+ if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
+ var t = getType(state);
+ state.strikethrough = false;
+ return t;
+ } else if (stream.match(/^[^\s]/, false)) {// Add strikethrough
+ state.strikethrough = true;
+ if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
+ return getType(state);
+ }
+ } else if (ch === ' ') {
+ if (stream.match(/^~~/, true)) { // Probably surrounded by space
+ if (stream.peek() === ' ') { // Surrounded by spaces, ignore
+ return getType(state);
+ } else { // Not surrounded by spaces, back up pointer
+ stream.backUp(2);
+ }
+ }
+ }
+ }
+
if (ch === ' ') {
if (stream.match(/ +$/, false)) {
state.trailingSpace++;
@@ -389,6 +529,26 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
return getType(state);
}
+ function linkInline(stream, state) {
+ var ch = stream.next();
+
+ if (ch === ">") {
+ state.f = state.inline = inlineNormal;
+ if (modeCfg.highlightFormatting) state.formatting = "link";
+ var type = getType(state);
+ if (type){
+ type += " ";
+ } else {
+ type = "";
+ }
+ return type + linkinline;
+ }
+
+ stream.match(/^[^>]+/, true);
+
+ return linkinline;
+ }
+
function linkHref(stream, state) {
// Check if space, and return NULL if so (to avoid marking the space)
if(stream.eatSpace()){
@@ -396,19 +556,60 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
}
var ch = stream.next();
if (ch === '(' || ch === '[') {
- return switchInline(stream, state, inlineElement(linkhref, ch === '(' ? ')' : ']'));
+ state.f = state.inline = getLinkHrefInside(ch === "(" ? ")" : "]");
+ if (modeCfg.highlightFormatting) state.formatting = "link-string";
+ state.linkHref = true;
+ return getType(state);
}
return 'error';
}
+ function getLinkHrefInside(endChar) {
+ return function(stream, state) {
+ var ch = stream.next();
+
+ if (ch === endChar) {
+ state.f = state.inline = inlineNormal;
+ if (modeCfg.highlightFormatting) state.formatting = "link-string";
+ var returnState = getType(state);
+ state.linkHref = false;
+ return returnState;
+ }
+
+ if (stream.match(inlineRE(endChar), true)) {
+ stream.backUp(1);
+ }
+
+ state.linkHref = true;
+ return getType(state);
+ };
+ }
+
function footnoteLink(stream, state) {
- if (stream.match(/^[^\]]*\]:/, true)) {
- state.f = footnoteUrl;
- return linktext;
+ if (stream.match(/^[^\]]*\]:/, false)) {
+ state.f = footnoteLinkInside;
+ stream.next(); // Consume [
+ if (modeCfg.highlightFormatting) state.formatting = "link";
+ state.linkText = true;
+ return getType(state);
}
return switchInline(stream, state, inlineNormal);
}
+ function footnoteLinkInside(stream, state) {
+ if (stream.match(/^\]:/, true)) {
+ state.f = state.inline = footnoteUrl;
+ if (modeCfg.highlightFormatting) state.formatting = "link";
+ var returnType = getType(state);
+ state.linkText = false;
+ return returnType;
+ }
+
+ stream.match(/^[^\]]+/, true);
+
+ return linktext;
+ }
+
function footnoteUrl(stream, state) {
// Check if space, and return NULL if so (to avoid marking the space)
if(stream.eatSpace()){
@@ -438,16 +639,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
return savedInlineRE[endChar];
}
- function inlineElement(type, endChar, next) {
- next = next || inlineNormal;
- return function(stream, state) {
- stream.match(inlineRE(endChar));
- state.inline = state.f = next;
- return type;
- };
- }
-
- return {
+ var mode = {
startState: function() {
return {
f: blockNormal,
@@ -456,23 +648,26 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
thisLineHasContent: false,
block: blockNormal,
- htmlState: CodeMirror.startState(htmlMode),
+ htmlState: null,
indentation: 0,
inline: inlineNormal,
text: handleText,
+ formatting: false,
linkText: false,
+ linkHref: false,
linkTitle: false,
em: false,
strong: false,
- header: false,
+ header: 0,
taskList: false,
list: false,
listDepth: 0,
quote: 0,
trailingSpace: 0,
- trailingSpaceNewLine: false
+ trailingSpaceNewLine: false,
+ strikethrough: false
};
},
@@ -484,7 +679,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
thisLineHasContent: s.thisLineHasContent,
block: s.block,
- htmlState: CodeMirror.copyState(htmlMode, s.htmlState),
+ htmlState: s.htmlState && CodeMirror.copyState(htmlMode, s.htmlState),
indentation: s.indentation,
localMode: s.localMode,
@@ -492,9 +687,11 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
inline: s.inline,
text: s.text,
+ formatting: false,
linkTitle: s.linkTitle,
em: s.em,
strong: s.strong,
+ strikethrough: s.strikethrough,
header: s.header,
taskList: s.taskList,
list: s.list,
@@ -507,18 +704,25 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
},
token: function(stream, state) {
+
+ // Reset state.formatting
+ state.formatting = false;
+
if (stream.sol()) {
- if (stream.match(/^\s*$/, true)) {
+ var forceBlankLine = !!state.header;
+
+ // Reset state.header
+ state.header = 0;
+
+ if (stream.match(/^\s*$/, true) || forceBlankLine) {
state.prevLineHasContent = false;
- return blankLine(state);
+ blankLine(state);
+ return forceBlankLine ? this.token(stream, state) : null;
} else {
state.prevLineHasContent = state.thisLineHasContent;
state.thisLineHasContent = true;
}
- // Reset state.header
- state.header = false;
-
// Reset state.taskList
state.taskList = false;
@@ -541,11 +745,21 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
return state.f(stream, state);
},
+ innerMode: function(state) {
+ if (state.block == htmlBlock) return {state: state.htmlState, mode: htmlMode};
+ if (state.localState) return {state: state.localState, mode: state.localMode};
+ return {state: state, mode: mode};
+ },
+
blankLine: blankLine,
- getType: getType
- };
+ getType: getType,
+ fold: "markdown"
+ };
+ return mode;
}, "xml");
CodeMirror.defineMIME("text/x-markdown", "markdown");
+
+});
diff --git a/applications/admin/static/codemirror/mode/markdown/test.js b/applications/admin/static/codemirror/mode/markdown/test.js
index f1679172..96ca1aef 100644
--- a/applications/admin/static/codemirror/mode/markdown/test.js
+++ b/applications/admin/static/codemirror/mode/markdown/test.js
@@ -1,6 +1,60 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function() {
var mode = CodeMirror.getMode({tabSize: 4}, "markdown");
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
+ var modeHighlightFormatting = CodeMirror.getMode({tabSize: 4}, {name: "markdown", highlightFormatting: true});
+ function FT(name) { test.mode(name, modeHighlightFormatting, Array.prototype.slice.call(arguments, 1)); }
+
+ FT("formatting_emAsterisk",
+ "[em&formatting&formatting-em *][em foo][em&formatting&formatting-em *]");
+
+ FT("formatting_emUnderscore",
+ "[em&formatting&formatting-em _][em foo][em&formatting&formatting-em _]");
+
+ FT("formatting_strongAsterisk",
+ "[strong&formatting&formatting-strong **][strong foo][strong&formatting&formatting-strong **]");
+
+ FT("formatting_strongUnderscore",
+ "[strong&formatting&formatting-strong __][strong foo][strong&formatting&formatting-strong __]");
+
+ FT("formatting_codeBackticks",
+ "[comment&formatting&formatting-code `][comment foo][comment&formatting&formatting-code `]");
+
+ FT("formatting_doubleBackticks",
+ "[comment&formatting&formatting-code ``][comment foo ` bar][comment&formatting&formatting-code ``]");
+
+ FT("formatting_atxHeader",
+ "[header&header-1&formatting&formatting-header&formatting-header-1 #][header&header-1 foo # bar ][header&header-1&formatting&formatting-header&formatting-header-1 #]");
+
+ FT("formatting_setextHeader",
+ "foo",
+ "[header&header-1&formatting&formatting-header&formatting-header-1 =]");
+
+ FT("formatting_blockquote",
+ "[quote"e-1&formatting&formatting-quote&formatting-quote-1 > ][quote"e-1 foo]");
+
+ FT("formatting_list",
+ "[variable-2&formatting&formatting-list&formatting-list-ul - ][variable-2 foo]");
+ FT("formatting_list",
+ "[variable-2&formatting&formatting-list&formatting-list-ol 1. ][variable-2 foo]");
+
+ FT("formatting_link",
+ "[link&formatting&formatting-link [][link foo][link&formatting&formatting-link ]]][string&formatting&formatting-link-string (][string http://example.com/][string&formatting&formatting-link-string )]");
+
+ FT("formatting_linkReference",
+ "[link&formatting&formatting-link [][link foo][link&formatting&formatting-link ]]][string&formatting&formatting-link-string [][string bar][string&formatting&formatting-link-string ]]]",
+ "[link&formatting&formatting-link [][link bar][link&formatting&formatting-link ]]:] [string http://example.com/]");
+
+ FT("formatting_linkWeb",
+ "[link&formatting&formatting-link <][link http://example.com/][link&formatting&formatting-link >]");
+
+ FT("formatting_linkEmail",
+ "[link&formatting&formatting-link <][link user@example.com][link&formatting&formatting-link >]");
+
+ FT("formatting_escape",
+ "[formatting-escape \\*]");
MT("plainText",
"foo");
@@ -38,6 +92,14 @@
" [comment hello]",
" [comment world]");
+ // Code blocks should end even after extra indented lines
+ MT("codeBlocksWithTrailingIndentedLine",
+ " [comment foo]",
+ " [comment bar]",
+ " [comment baz]",
+ " ",
+ "hello");
+
// Code blocks using 1 tab (regardless of CodeMirror.indentWithTabs value)
MT("codeBlocksUsing1Tab",
"\t[comment foo]");
@@ -87,27 +149,31 @@
// http://daringfireball.net/projects/markdown/syntax#header
MT("atxH1",
- "[header # foo]");
+ "[header&header-1 # foo]");
MT("atxH2",
- "[header ## foo]");
+ "[header&header-2 ## foo]");
MT("atxH3",
- "[header ### foo]");
+ "[header&header-3 ### foo]");
MT("atxH4",
- "[header #### foo]");
+ "[header&header-4 #### foo]");
MT("atxH5",
- "[header ##### foo]");
+ "[header&header-5 ##### foo]");
MT("atxH6",
- "[header ###### foo]");
+ "[header&header-6 ###### foo]");
// H6 - 7x '#' should still be H6, per Dingus
// http://daringfireball.net/projects/markdown/dingus
MT("atxH6NotH7",
- "[header ####### foo]");
+ "[header&header-6 ####### foo]");
+
+ // Inline styles should be parsed inside headers
+ MT("atxH1inline",
+ "[header&header-1 # foo ][header&header-1&em *bar*]");
// Setext headers - H1, H2
// Per documentation, "Any number of underlining =’s or -’s will work."
@@ -119,69 +185,69 @@
// Check if single underlining = works
MT("setextH1",
"foo",
- "[header =]");
+ "[header&header-1 =]");
// Check if 3+ ='s work
MT("setextH1",
"foo",
- "[header ===]");
+ "[header&header-1 ===]");
// Check if single underlining - works
MT("setextH2",
"foo",
- "[header -]");
+ "[header&header-2 -]");
// Check if 3+ -'s work
MT("setextH2",
"foo",
- "[header ---]");
+ "[header&header-2 ---]");
// Single-line blockquote with trailing space
MT("blockquoteSpace",
- "[atom > foo]");
+ "[quote"e-1 > foo]");
// Single-line blockquote
MT("blockquoteNoSpace",
- "[atom >foo]");
+ "[quote"e-1 >foo]");
// No blank line before blockquote
MT("blockquoteNoBlankLine",
"foo",
- "[atom > bar]");
+ "[quote"e-1 > bar]");
// Nested blockquote
MT("blockquoteSpace",
- "[atom > foo]",
- "[number > > foo]",
- "[atom > > > foo]");
+ "[quote"e-1 > foo]",
+ "[quote"e-1 >][quote"e-2 > foo]",
+ "[quote"e-1 >][quote"e-2 >][quote"e-3 > foo]");
// Single-line blockquote followed by normal paragraph
MT("blockquoteThenParagraph",
- "[atom >foo]",
+ "[quote"e-1 >foo]",
"",
"bar");
// Multi-line blockquote (lazy mode)
MT("multiBlockquoteLazy",
- "[atom >foo]",
- "[atom bar]");
+ "[quote"e-1 >foo]",
+ "[quote"e-1 bar]");
// Multi-line blockquote followed by normal paragraph (lazy mode)
MT("multiBlockquoteLazyThenParagraph",
- "[atom >foo]",
- "[atom bar]",
+ "[quote"e-1 >foo]",
+ "[quote"e-1 bar]",
"",
"hello");
// Multi-line blockquote (non-lazy mode)
MT("multiBlockquote",
- "[atom >foo]",
- "[atom >bar]");
+ "[quote"e-1 >foo]",
+ "[quote"e-1 >bar]");
// Multi-line blockquote followed by normal paragraph (non-lazy mode)
MT("multiBlockquoteThenParagraph",
- "[atom >foo]",
- "[atom >bar]",
+ "[quote"e-1 >foo]",
+ "[quote"e-1 >bar]",
"",
"hello");
@@ -221,6 +287,11 @@
"1. bar",
"2. hello");
+ // List after header
+ MT("listAfterHeader",
+ "[header&header-1 # foo]",
+ "[variable-2 - bar]");
+
// Formatting in lists (*)
MT("listAsteriskFormatting",
"[variable-2 * ][variable-2&em *foo*][variable-2 bar]",
@@ -306,7 +377,7 @@
"",
"[variable-2 * bar]",
"",
- " [variable-2&atom > hello]");
+ " [variable-2"e"e-1 > hello]");
// Code block
MT("blockquoteCode",
@@ -364,7 +435,7 @@
"",
" [variable-3 + bar]",
"",
- " [atom&variable-3 > hello]");
+ " [quote"e-1&variable-3 > hello]");
MT("listCode",
"[variable-2 * foo]",
@@ -577,6 +648,10 @@
MT("emEscapedBySpaceOut",
"foo _ bar[em _hello_]world");
+ MT("emEscapedByNewline",
+ "foo",
+ "_ bar[em _hello_]world");
+
// Unclosed emphasis characters
// Instead of simply marking as EM / STRONG, it would be nice to have an
// incomplete flag for EM and STRONG, that is styled slightly different.
@@ -643,6 +718,10 @@
MT("doubleEscapeHash",
"\\\\# foo");
+ MT("escapeNewline",
+ "\\",
+ "[em *foo*]");
+
// Tests to make sure GFM-specific things aren't getting through
@@ -653,4 +732,23 @@
"[comment ```]",
"foo",
"[comment ```]");
+
+ // Tests that require XML mode
+
+ MT("xmlMode",
+ "[tag&bracket <][tag div][tag&bracket >]",
+ "*foo*",
+ "[tag&bracket <][tag http://github.com][tag&bracket />]",
+ "[tag&bracket ][tag div][tag&bracket >]",
+ "[link ]");
+
+ MT("xmlModeWithMarkdownInside",
+ "[tag&bracket <][tag div] [attribute markdown]=[string 1][tag&bracket >]",
+ "[em *foo*]",
+ "[link ]",
+ "[tag ]",
+ "[link ]",
+ "[tag&bracket <][tag div][tag&bracket >]",
+ "[tag&bracket ][tag div][tag&bracket >]");
+
})();
diff --git a/applications/admin/static/codemirror/mode/meta.js b/applications/admin/static/codemirror/mode/meta.js
index 142d9595..8d91df7d 100644
--- a/applications/admin/static/codemirror/mode/meta.js
+++ b/applications/admin/static/codemirror/mode/meta.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../lib/codemirror"));
@@ -8,99 +11,166 @@
})(function(CodeMirror) {
"use strict";
-CodeMirror.modeInfo = [
- {name: 'APL', mime: 'text/apl', mode: 'apl'},
- {name: 'Asterisk', mime: 'text/x-asterisk', mode: 'asterisk'},
- {name: 'C', mime: 'text/x-csrc', mode: 'clike'},
- {name: 'C++', mime: 'text/x-c++src', mode: 'clike'},
- {name: 'Cobol', mime: 'text/x-cobol', mode: 'cobol'},
- {name: 'Java', mime: 'text/x-java', mode: 'clike'},
- {name: 'C#', mime: 'text/x-csharp', mode: 'clike'},
- {name: 'Scala', mime: 'text/x-scala', mode: 'clike'},
- {name: 'Clojure', mime: 'text/x-clojure', mode: 'clojure'},
- {name: 'CoffeeScript', mime: 'text/x-coffeescript', mode: 'coffeescript'},
- {name: 'Common Lisp', mime: 'text/x-common-lisp', mode: 'commonlisp'},
- {name: 'CSS', mime: 'text/css', mode: 'css'},
- {name: 'D', mime: 'text/x-d', mode: 'd'},
- {name: 'diff', mime: 'text/x-diff', mode: 'diff'},
- {name: 'DTD', mime: 'application/xml-dtd', mode: 'dtd'},
- {name: 'ECL', mime: 'text/x-ecl', mode: 'ecl'},
- {name: 'Eiffel', mime: 'text/x-eiffel', mode: 'eiffel'},
- {name: 'Erlang', mime: 'text/x-erlang', mode: 'erlang'},
- {name: 'Fortran', mime: 'text/x-fortran', mode: 'fortran'},
- {name: 'F#', mime: 'text/x-fsharp', mode: 'mllike'},
- {name: 'Gas', mime: 'text/x-gas', mode: 'gas'},
- {name: 'Gherkin', mime: 'text/x-feature', mode: 'gherkin'},
- {name: 'GitHub Flavored Markdown', mime: 'text/x-gfm', mode: 'gfm'},
- {name: 'Go', mime: 'text/x-go', mode: 'go'},
- {name: 'Groovy', mime: 'text/x-groovy', mode: 'groovy'},
- {name: 'HAML', mime: 'text/x-haml', mode: 'haml'},
- {name: 'Haskell', mime: 'text/x-haskell', mode: 'haskell'},
- {name: 'Haxe', mime: 'text/x-haxe', mode: 'haxe'},
- {name: 'ASP.NET', mime: 'application/x-aspx', mode: 'htmlembedded'},
- {name: 'Embedded Javascript', mime: 'application/x-ejs', mode: 'htmlembedded'},
- {name: 'JavaServer Pages', mime: 'application/x-jsp', mode: 'htmlembedded'},
- {name: 'HTML', mime: 'text/html', mode: 'htmlmixed'},
- {name: 'HTTP', mime: 'message/http', mode: 'http'},
- {name: 'Jade', mime: 'text/x-jade', mode: 'jade'},
- {name: 'JavaScript', mime: 'text/javascript', mode: 'javascript'},
- {name: 'JSON', mime: 'application/x-json', mode: 'javascript'},
- {name: 'JSON', mime: 'application/json', mode: 'javascript'},
- {name: 'JSON-LD', mime: 'application/ld+json', mode: 'javascript'},
- {name: 'TypeScript', mime: 'application/typescript', mode: 'javascript'},
- {name: 'Jinja2', mime: null, mode: 'jinja2'},
- {name: 'Julia', mime: 'text/x-julia', mode: 'julia'},
- {name: 'LESS', mime: 'text/x-less', mode: 'css'},
- {name: 'LiveScript', mime: 'text/x-livescript', mode: 'livescript'},
- {name: 'Lua', mime: 'text/x-lua', mode: 'lua'},
- {name: 'Markdown (GitHub-flavour)', mime: 'text/x-markdown', mode: 'markdown'},
- {name: 'mIRC', mime: 'text/mirc', mode: 'mirc'},
- {name: 'Nginx', mime: 'text/x-nginx-conf', mode: 'nginx'},
- {name: 'NTriples', mime: 'text/n-triples', mode: 'ntriples'},
- {name: 'OCaml', mime: 'text/x-ocaml', mode: 'mllike'},
- {name: 'Octave', mime: 'text/x-octave', mode: 'octave'},
- {name: 'Pascal', mime: 'text/x-pascal', mode: 'pascal'},
- {name: 'PEG.js', mime: null, mode: 'pegjs'},
- {name: 'Perl', mime: 'text/x-perl', mode: 'perl'},
- {name: 'PHP', mime: 'text/x-php', mode: 'php'},
- {name: 'PHP(HTML)', mime: 'application/x-httpd-php', mode: 'php'},
- {name: 'Pig', mime: 'text/x-pig', mode: 'pig'},
- {name: 'Plain Text', mime: 'text/plain', mode: 'null'},
- {name: 'Properties files', mime: 'text/x-properties', mode: 'properties'},
- {name: 'Python', mime: 'text/x-python', mode: 'python'},
- {name: 'Puppet', mime: 'text/x-puppet', mode: 'puppet'},
- {name: 'Cython', mime: 'text/x-cython', mode: 'python'},
- {name: 'R', mime: 'text/x-rsrc', mode: 'r'},
- {name: 'reStructuredText', mime: 'text/x-rst', mode: 'rst'},
- {name: 'Ruby', mime: 'text/x-ruby', mode: 'ruby'},
- {name: 'Rust', mime: 'text/x-rustsrc', mode: 'rust'},
- {name: 'Sass', mime: 'text/x-sass', mode: 'sass'},
- {name: 'Scheme', mime: 'text/x-scheme', mode: 'scheme'},
- {name: 'SCSS', mime: 'text/x-scss', mode: 'css'},
- {name: 'Shell', mime: 'text/x-sh', mode: 'shell'},
- {name: 'Sieve', mime: 'application/sieve', mode: 'sieve'},
- {name: 'Smalltalk', mime: 'text/x-stsrc', mode: 'smalltalk'},
- {name: 'Smarty', mime: 'text/x-smarty', mode: 'smarty'},
- {name: 'SmartyMixed', mime: 'text/x-smarty', mode: 'smartymixed'},
- {name: 'Solr', mime: 'text/x-solr', mode: 'solr'},
- {name: 'SPARQL', mime: 'application/x-sparql-query', mode: 'sparql'},
- {name: 'SQL', mime: 'text/x-sql', mode: 'sql'},
- {name: 'MariaDB', mime: 'text/x-mariadb', mode: 'sql'},
- {name: 'sTeX', mime: 'text/x-stex', mode: 'stex'},
- {name: 'LaTeX', mime: 'text/x-latex', mode: 'stex'},
- {name: 'Tcl', mime: 'text/x-tcl', mode: 'tcl'},
- {name: 'TiddlyWiki ', mime: 'text/x-tiddlywiki', mode: 'tiddlywiki'},
- {name: 'Tiki wiki', mime: 'text/tiki', mode: 'tiki'},
- {name: 'TOML', mime: 'text/x-toml', mode: 'toml'},
- {name: 'Turtle', mime: 'text/turtle', mode: 'turtle'},
- {name: 'VB.NET', mime: 'text/x-vb', mode: 'vb'},
- {name: 'VBScript', mime: 'text/vbscript', mode: 'vbscript'},
- {name: 'Velocity', mime: 'text/velocity', mode: 'velocity'},
- {name: 'Verilog', mime: 'text/x-verilog', mode: 'verilog'},
- {name: 'XML', mime: 'application/xml', mode: 'xml'},
- {name: 'XQuery', mime: 'application/xquery', mode: 'xquery'},
- {name: 'YAML', mime: 'text/x-yaml', mode: 'yaml'},
- {name: 'Z80', mime: 'text/x-z80', mode: 'z80'}
-];
+ CodeMirror.modeInfo = [
+ {name: "APL", mime: "text/apl", mode: "apl", ext: ["dyalog", "apl"]},
+ {name: "Asterisk", mime: "text/x-asterisk", mode: "asterisk", file: /^extensions\.conf$/i},
+ {name: "C", mime: "text/x-csrc", mode: "clike", ext: ["c", "h"]},
+ {name: "C++", mime: "text/x-c++src", mode: "clike", ext: ["cpp", "c++", "cc", "cxx", "hpp", "h++", "hh", "hxx"], alias: ["cpp"]},
+ {name: "Cobol", mime: "text/x-cobol", mode: "cobol", ext: ["cob", "cpy"]},
+ {name: "C#", mime: "text/x-csharp", mode: "clike", ext: ["cs"], alias: ["csharp"]},
+ {name: "Clojure", mime: "text/x-clojure", mode: "clojure", ext: ["clj"]},
+ {name: "CoffeeScript", mime: "text/x-coffeescript", mode: "coffeescript", ext: ["coffee"], alias: ["coffee", "coffee-script"]},
+ {name: "Common Lisp", mime: "text/x-common-lisp", mode: "commonlisp", ext: ["cl", "lisp", "el"], alias: ["lisp"]},
+ {name: "Cypher", mime: "application/x-cypher-query", mode: "cypher", ext: ["cyp", "cypher"]},
+ {name: "Cython", mime: "text/x-cython", mode: "python", ext: ["pyx", "pxd", "pxi"]},
+ {name: "CSS", mime: "text/css", mode: "css", ext: ["css"]},
+ {name: "CQL", mime: "text/x-cassandra", mode: "sql", ext: ["cql"]},
+ {name: "D", mime: "text/x-d", mode: "d", ext: ["d"]},
+ {name: "Dart", mimes: ["application/dart", "text/x-dart"], mode: "dart", ext: ["dart"]},
+ {name: "diff", mime: "text/x-diff", mode: "diff", ext: ["diff", "patch"]},
+ {name: "Django", mime: "text/x-django", mode: "django"},
+ {name: "Dockerfile", mime: "text/x-dockerfile", mode: "dockerfile", file: /^Dockerfile$/},
+ {name: "DTD", mime: "application/xml-dtd", mode: "dtd", ext: ["dtd"]},
+ {name: "Dylan", mime: "text/x-dylan", mode: "dylan", ext: ["dylan", "dyl", "intr"]},
+ {name: "EBNF", mime: "text/x-ebnf", mode: "ebnf"},
+ {name: "ECL", mime: "text/x-ecl", mode: "ecl", ext: ["ecl"]},
+ {name: "Eiffel", mime: "text/x-eiffel", mode: "eiffel", ext: ["e"]},
+ {name: "Embedded Javascript", mime: "application/x-ejs", mode: "htmlembedded", ext: ["ejs"]},
+ {name: "Embedded Ruby", mime: "application/x-erb", mode: "htmlembedded", ext: ["erb"]},
+ {name: "Erlang", mime: "text/x-erlang", mode: "erlang", ext: ["erl"]},
+ {name: "Fortran", mime: "text/x-fortran", mode: "fortran", ext: ["f", "for", "f77", "f90"]},
+ {name: "F#", mime: "text/x-fsharp", mode: "mllike", ext: ["fs"], alias: ["fsharp"]},
+ {name: "Gas", mime: "text/x-gas", mode: "gas", ext: ["s"]},
+ {name: "Gherkin", mime: "text/x-feature", mode: "gherkin", ext: ["feature"]},
+ {name: "GitHub Flavored Markdown", mime: "text/x-gfm", mode: "gfm", file: /^(readme|contributing|history).md$/i},
+ {name: "Go", mime: "text/x-go", mode: "go", ext: ["go"]},
+ {name: "Groovy", mime: "text/x-groovy", mode: "groovy", ext: ["groovy"]},
+ {name: "HAML", mime: "text/x-haml", mode: "haml", ext: ["haml"]},
+ {name: "Haskell", mime: "text/x-haskell", mode: "haskell", ext: ["hs"]},
+ {name: "Haxe", mime: "text/x-haxe", mode: "haxe", ext: ["hx"]},
+ {name: "HXML", mime: "text/x-hxml", mode: "haxe", ext: ["hxml"]},
+ {name: "ASP.NET", mime: "application/x-aspx", mode: "htmlembedded", ext: ["aspx"], alias: ["asp", "aspx"]},
+ {name: "HTML", mime: "text/html", mode: "htmlmixed", ext: ["html", "htm"], alias: ["xhtml"]},
+ {name: "HTTP", mime: "message/http", mode: "http"},
+ {name: "IDL", mime: "text/x-idl", mode: "idl", ext: ["pro"]},
+ {name: "Jade", mime: "text/x-jade", mode: "jade", ext: ["jade"]},
+ {name: "Java", mime: "text/x-java", mode: "clike", ext: ["java"]},
+ {name: "Java Server Pages", mime: "application/x-jsp", mode: "htmlembedded", ext: ["jsp"], alias: ["jsp"]},
+ {name: "JavaScript", mimes: ["text/javascript", "text/ecmascript", "application/javascript", "application/x-javascript", "application/ecmascript"],
+ mode: "javascript", ext: ["js"], alias: ["ecmascript", "js", "node"]},
+ {name: "JSON", mimes: ["application/json", "application/x-json"], mode: "javascript", ext: ["json", "map"], alias: ["json5"]},
+ {name: "JSON-LD", mime: "application/ld+json", mode: "javascript", ext: ["jsonld"], alias: ["jsonld"]},
+ {name: "Jinja2", mime: "null", mode: "jinja2"},
+ {name: "Julia", mime: "text/x-julia", mode: "julia", ext: ["jl"]},
+ {name: "Kotlin", mime: "text/x-kotlin", mode: "kotlin", ext: ["kt"]},
+ {name: "LESS", mime: "text/x-less", mode: "css", ext: ["less"]},
+ {name: "LiveScript", mime: "text/x-livescript", mode: "livescript", ext: ["ls"], alias: ["ls"]},
+ {name: "Lua", mime: "text/x-lua", mode: "lua", ext: ["lua"]},
+ {name: "Markdown", mime: "text/x-markdown", mode: "markdown", ext: ["markdown", "md", "mkd"]},
+ {name: "mIRC", mime: "text/mirc", mode: "mirc"},
+ {name: "MariaDB SQL", mime: "text/x-mariadb", mode: "sql"},
+ {name: "Modelica", mime: "text/x-modelica", mode: "modelica", ext: ["mo"]},
+ {name: "MS SQL", mime: "text/x-mssql", mode: "sql"},
+ {name: "MySQL", mime: "text/x-mysql", mode: "sql"},
+ {name: "Nginx", mime: "text/x-nginx-conf", mode: "nginx", file: /nginx.*\.conf$/i},
+ {name: "NTriples", mime: "text/n-triples", mode: "ntriples", ext: ["nt"]},
+ {name: "Objective C", mime: "text/x-objectivec", mode: "clike", ext: ["m", "mm"]},
+ {name: "OCaml", mime: "text/x-ocaml", mode: "mllike", ext: ["ml", "mli", "mll", "mly"]},
+ {name: "Octave", mime: "text/x-octave", mode: "octave", ext: ["m"]},
+ {name: "Pascal", mime: "text/x-pascal", mode: "pascal", ext: ["p", "pas"]},
+ {name: "PEG.js", mime: "null", mode: "pegjs", ext: ["jsonld"]},
+ {name: "Perl", mime: "text/x-perl", mode: "perl", ext: ["pl", "pm"]},
+ {name: "PHP", mime: "application/x-httpd-php", mode: "php", ext: ["php", "php3", "php4", "php5", "phtml"]},
+ {name: "Pig", mime: "text/x-pig", mode: "pig", ext: ["pig"]},
+ {name: "Plain Text", mime: "text/plain", mode: "null", ext: ["txt", "text", "conf", "def", "list", "log"]},
+ {name: "PLSQL", mime: "text/x-plsql", mode: "sql", ext: ["pls"]},
+ {name: "Properties files", mime: "text/x-properties", mode: "properties", ext: ["properties", "ini", "in"], alias: ["ini", "properties"]},
+ {name: "Python", mime: "text/x-python", mode: "python", ext: ["py", "pyw"]},
+ {name: "Puppet", mime: "text/x-puppet", mode: "puppet", ext: ["pp"]},
+ {name: "Q", mime: "text/x-q", mode: "q", ext: ["q"]},
+ {name: "R", mime: "text/x-rsrc", mode: "r", ext: ["r"], alias: ["rscript"]},
+ {name: "reStructuredText", mime: "text/x-rst", mode: "rst", ext: ["rst"], alias: ["rst"]},
+ {name: "RPM Changes", mime: "text/x-rpm-changes", mode: "rpm"},
+ {name: "RPM Spec", mime: "text/x-rpm-spec", mode: "rpm", ext: ["spec"]},
+ {name: "Ruby", mime: "text/x-ruby", mode: "ruby", ext: ["rb"], alias: ["jruby", "macruby", "rake", "rb", "rbx"]},
+ {name: "Rust", mime: "text/x-rustsrc", mode: "rust", ext: ["rs"]},
+ {name: "Sass", mime: "text/x-sass", mode: "sass", ext: ["sass"]},
+ {name: "Scala", mime: "text/x-scala", mode: "clike", ext: ["scala"]},
+ {name: "Scheme", mime: "text/x-scheme", mode: "scheme", ext: ["scm", "ss"]},
+ {name: "SCSS", mime: "text/x-scss", mode: "css", ext: ["scss"]},
+ {name: "Shell", mime: "text/x-sh", mode: "shell", ext: ["sh", "ksh", "bash"], alias: ["bash", "sh", "zsh"]},
+ {name: "Sieve", mime: "application/sieve", mode: "sieve", ext: ["siv", "sieve"]},
+ {name: "Slim", mimes: ["text/x-slim", "application/x-slim"], mode: "slim", ext: ["slim"]},
+ {name: "Smalltalk", mime: "text/x-stsrc", mode: "smalltalk", ext: ["st"]},
+ {name: "Smarty", mime: "text/x-smarty", mode: "smarty", ext: ["tpl"]},
+ {name: "SmartyMixed", mime: "text/x-smarty", mode: "smartymixed"},
+ {name: "Solr", mime: "text/x-solr", mode: "solr"},
+ {name: "Soy", mime: "text/x-soy", mode: "soy", ext: ["soy"], alias: ["closure template"]},
+ {name: "SPARQL", mime: "application/sparql-query", mode: "sparql", ext: ["rq", "sparql"], alias: ["sparul"]},
+ {name: "Spreadsheet", mime: "text/x-spreadsheet", mode: "spreadsheet", alias: ["excel", "formula"]},
+ {name: "SQL", mime: "text/x-sql", mode: "sql", ext: ["sql"]},
+ {name: "MariaDB", mime: "text/x-mariadb", mode: "sql"},
+ {name: "sTeX", mime: "text/x-stex", mode: "stex"},
+ {name: "LaTeX", mime: "text/x-latex", mode: "stex", ext: ["text", "ltx"], alias: ["tex"]},
+ {name: "SystemVerilog", mime: "text/x-systemverilog", mode: "verilog", ext: ["v"]},
+ {name: "Tcl", mime: "text/x-tcl", mode: "tcl", ext: ["tcl"]},
+ {name: "Textile", mime: "text/x-textile", mode: "textile", ext: ["textile"]},
+ {name: "TiddlyWiki ", mime: "text/x-tiddlywiki", mode: "tiddlywiki"},
+ {name: "Tiki wiki", mime: "text/tiki", mode: "tiki"},
+ {name: "TOML", mime: "text/x-toml", mode: "toml", ext: ["toml"]},
+ {name: "Tornado", mime: "text/x-tornado", mode: "tornado"},
+ {name: "Turtle", mime: "text/turtle", mode: "turtle", ext: ["ttl"]},
+ {name: "TypeScript", mime: "application/typescript", mode: "javascript", ext: ["ts"], alias: ["ts"]},
+ {name: "VB.NET", mime: "text/x-vb", mode: "vb", ext: ["vb"]},
+ {name: "VBScript", mime: "text/vbscript", mode: "vbscript", ext: ["vbs"]},
+ {name: "Velocity", mime: "text/velocity", mode: "velocity", ext: ["vtl"]},
+ {name: "Verilog", mime: "text/x-verilog", mode: "verilog", ext: ["v"]},
+ {name: "XML", mimes: ["application/xml", "text/xml"], mode: "xml", ext: ["xml", "xsl", "xsd"], alias: ["rss", "wsdl", "xsd"]},
+ {name: "XQuery", mime: "application/xquery", mode: "xquery", ext: ["xy", "xquery"]},
+ {name: "YAML", mime: "text/x-yaml", mode: "yaml", ext: ["yaml"], alias: ["yml"]},
+ {name: "Z80", mime: "text/x-z80", mode: "z80", ext: ["z80"]}
+ ];
+ // Ensure all modes have a mime property for backwards compatibility
+ for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
+ var info = CodeMirror.modeInfo[i];
+ if (info.mimes) info.mime = info.mimes[0];
+ }
+ CodeMirror.findModeByMIME = function(mime) {
+ mime = mime.toLowerCase();
+ for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
+ var info = CodeMirror.modeInfo[i];
+ if (info.mime == mime) return info;
+ if (info.mimes) for (var j = 0; j < info.mimes.length; j++)
+ if (info.mimes[j] == mime) return info;
+ }
+ };
+
+ CodeMirror.findModeByExtension = function(ext) {
+ for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
+ var info = CodeMirror.modeInfo[i];
+ if (info.ext) for (var j = 0; j < info.ext.length; j++)
+ if (info.ext[j] == ext) return info;
+ }
+ };
+
+ CodeMirror.findModeByFileName = function(filename) {
+ for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
+ var info = CodeMirror.modeInfo[i];
+ if (info.file && info.file.test(filename)) return info;
+ }
+ var dot = filename.lastIndexOf(".");
+ var ext = dot > -1 && filename.substring(dot + 1, filename.length);
+ if (ext) return CodeMirror.findModeByExtension(ext);
+ };
+
+ CodeMirror.findModeByName = function(name) {
+ name = name.toLowerCase();
+ for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
+ var info = CodeMirror.modeInfo[i];
+ if (info.name.toLowerCase() == name) return info;
+ if (info.alias) for (var j = 0; j < info.alias.length; j++)
+ if (info.alias[j].toLowerCase() == name) return info;
+ }
+ };
});
diff --git a/applications/admin/static/codemirror/mode/properties/index.html b/applications/admin/static/codemirror/mode/properties/index.html
index 40ee1a37..f885302d 100644
--- a/applications/admin/static/codemirror/mode/properties/index.html
+++ b/applications/admin/static/codemirror/mode/properties/index.html
@@ -9,12 +9,12 @@
-
+
CodeMirror
Language modes
diff --git a/applications/admin/static/codemirror/mode/properties/properties.js b/applications/admin/static/codemirror/mode/properties/properties.js
index d3a13c76..07400842 100644
--- a/applications/admin/static/codemirror/mode/properties/properties.js
+++ b/applications/admin/static/codemirror/mode/properties/properties.js
@@ -1,3 +1,16 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
CodeMirror.defineMode("properties", function() {
return {
token: function(stream, state) {
@@ -61,3 +74,5 @@ CodeMirror.defineMode("properties", function() {
CodeMirror.defineMIME("text/x-properties", "properties");
CodeMirror.defineMIME("text/x-ini", "properties");
+
+});
diff --git a/applications/admin/static/codemirror/mode/python/index.html b/applications/admin/static/codemirror/mode/python/index.html
new file mode 100644
index 00000000..86eb3d52
--- /dev/null
+++ b/applications/admin/static/codemirror/mode/python/index.html
@@ -0,0 +1,198 @@
+
+
+CodeMirror: Python mode
+
+
+
+
+
+
+
+
+
+
+
+Python mode
+
+
+# Literals
+1234
+0.0e101
+.123
+0b01010011100
+0o01234567
+0x0987654321abcdef
+7
+2147483647
+3L
+79228162514264337593543950336L
+0x100000000L
+79228162514264337593543950336
+0xdeadbeef
+3.14j
+10.j
+10j
+.001j
+1e100j
+3.14e-10j
+
+
+# String Literals
+'For\''
+"God\""
+"""so loved
+the world"""
+'''that he gave
+his only begotten\' '''
+'that whosoever believeth \
+in him'
+''
+
+# Identifiers
+__a__
+a.b
+a.b.c
+
+#Unicode identifiers on Python3
+# a = x\ddot
+a⃗ = ẍ
+# a = v\dot
+a⃗ = v̇
+
+#F\vec = m \cdot a\vec
+F⃗ = m•a⃗
+
+# Operators
++ - * / % & | ^ ~ < >
+== != <= >= <> << >> // **
+and or not in is
+
+#infix matrix multiplication operator (PEP 465)
+A @ B
+
+# Delimiters
+() [] {} , : ` = ; @ . # Note that @ and . require the proper context on Python 2.
++= -= *= /= %= &= |= ^=
+//= >>= <<= **=
+
+# Keywords
+as assert break class continue def del elif else except
+finally for from global if import lambda pass raise
+return try while with yield
+
+# Python 2 Keywords (otherwise Identifiers)
+exec print
+
+# Python 3 Keywords (otherwise Identifiers)
+nonlocal
+
+# Types
+bool classmethod complex dict enumerate float frozenset int list object
+property reversed set slice staticmethod str super tuple type
+
+# Python 2 Types (otherwise Identifiers)
+basestring buffer file long unicode xrange
+
+# Python 3 Types (otherwise Identifiers)
+bytearray bytes filter map memoryview open range zip
+
+# Some Example code
+import os
+from package import ParentClass
+
+@nonsenseDecorator
+def doesNothing():
+ pass
+
+class ExampleClass(ParentClass):
+ @staticmethod
+ def example(inputStr):
+ a = list(inputStr)
+ a.reverse()
+ return ''.join(a)
+
+ def __init__(self, mixin = 'Hello'):
+ self.mixin = mixin
+
+
+
+
+Cython mode
+
+
+
+import numpy as np
+cimport cython
+from libc.math cimport sqrt
+
+@cython.boundscheck(False)
+@cython.wraparound(False)
+def pairwise_cython(double[:, ::1] X):
+ cdef int M = X.shape[0]
+ cdef int N = X.shape[1]
+ cdef double tmp, d
+ cdef double[:, ::1] D = np.empty((M, M), dtype=np.float64)
+ for i in range(M):
+ for j in range(M):
+ d = 0.0
+ for k in range(N):
+ tmp = X[i, k] - X[j, k]
+ d += tmp * tmp
+ D[i, j] = sqrt(d)
+ return np.asarray(D)
+
+
+
+
+ Configuration Options for Python mode:
+
+ version - 2/3 - The version of Python to recognize. Default is 2.
+ singleLineStringErrors - true/false - If you have a single-line string that is not terminated at the end of the line, this will show subsequent lines as errors if true, otherwise it will consider the newline as the end of the string. Default is false.
+ hangingIndent - int - If you want to write long arguments to a function starting on a new line, how much that line should be indented. Defaults to one normal indentation unit.
+
+ Advanced Configuration Options:
+ Usefull for superset of python syntax like Enthought enaml, IPython magics and questionmark help
+
+ singleOperators - RegEx - Regular Expression for single operator matching, default : ^[\\+\\-\\*/%&|\\^~<>!] including @ on Python 3
+ singleDelimiters - RegEx - Regular Expression for single delimiter matching, default : ^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]
+ doubleOperators - RegEx - Regular Expression for double operators matching, default : ^((==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\*\\*))
+ doubleDelimiters - RegEx - Regular Expressoin for double delimiters matching, default : ^((\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))
+ tripleDelimiters - RegEx - Regular Expression for triple delimiters matching, default : ^((//=)|(>>=)|(<<=)|(\\*\\*=))
+ identifiers - RegEx - Regular Expression for identifier, default : ^[_A-Za-z][_A-Za-z0-9]* on Python 2 and ^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]* on Python 3.
+ extra_keywords - list of string - List of extra words ton consider as keywords
+ extra_builtins - list of string - List of extra words ton consider as builtins
+
+
+
+ MIME types defined: text/x-python and text/x-cython.
+
diff --git a/applications/admin/static/codemirror/mode/python/python.js b/applications/admin/static/codemirror/mode/python/python.js
index f5530bcf..98c0409a 100644
--- a/applications/admin/static/codemirror/mode/python/python.js
+++ b/applications/admin/static/codemirror/mode/python/python.js
@@ -1,3 +1,6 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
@@ -6,383 +9,351 @@
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
-"use strict";
+ "use strict";
+ function wordRegexp(words) {
+ return new RegExp("^((" + words.join(")|(") + "))\\b");
+ }
-CodeMirror.defineMode("python", function(conf, parserConf) {
- var ERRORCLASS = 'error';
+ var wordOperators = wordRegexp(["and", "or", "not", "is"]);
+ var commonKeywords = ["as", "assert", "break", "class", "continue",
+ "def", "del", "elif", "else", "except", "finally",
+ "for", "from", "global", "if", "import",
+ "lambda", "pass", "raise", "return",
+ "try", "while", "with", "yield", "in"];
+ var commonBuiltins = ["abs", "all", "any", "bin", "bool", "bytearray", "callable", "chr",
+ "classmethod", "compile", "complex", "delattr", "dict", "dir", "divmod",
+ "enumerate", "eval", "filter", "float", "format", "frozenset",
+ "getattr", "globals", "hasattr", "hash", "help", "hex", "id",
+ "input", "int", "isinstance", "issubclass", "iter", "len",
+ "list", "locals", "map", "max", "memoryview", "min", "next",
+ "object", "oct", "open", "ord", "pow", "property", "range",
+ "repr", "reversed", "round", "set", "setattr", "slice",
+ "sorted", "staticmethod", "str", "sum", "super", "tuple",
+ "type", "vars", "zip", "__import__", "NotImplemented",
+ "Ellipsis", "__debug__"];
+ var py2 = {builtins: ["apply", "basestring", "buffer", "cmp", "coerce", "execfile",
+ "file", "intern", "long", "raw_input", "reduce", "reload",
+ "unichr", "unicode", "xrange", "False", "True", "None"],
+ keywords: ["exec", "print"]};
+ var py3 = {builtins: ["ascii", "bytes", "exec", "print"],
+ keywords: ["nonlocal", "False", "True", "None"]};
- function wordRegexp(words) {
- return new RegExp("^((" + words.join(")|(") + "))\\b");
- }
+ CodeMirror.registerHelper("hintWords", "python", commonKeywords.concat(commonBuiltins));
- var singleOperators = parserConf.singleOperators || new RegExp("^[\\+\\-\\*/%&|\\^~<>!]");
- var singleDelimiters = parserConf.singleDelimiters || new RegExp('^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]');
+ function top(state) {
+ return state.scopes[state.scopes.length - 1];
+ }
+
+ CodeMirror.defineMode("python", function(conf, parserConf) {
+ var ERRORCLASS = "error";
+
+ var singleDelimiters = parserConf.singleDelimiters || new RegExp("^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]");
var doubleOperators = parserConf.doubleOperators || new RegExp("^((==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\*\\*))");
var doubleDelimiters = parserConf.doubleDelimiters || new RegExp("^((\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))");
var tripleDelimiters = parserConf.tripleDelimiters || new RegExp("^((//=)|(>>=)|(<<=)|(\\*\\*=))");
- var identifiers = parserConf.identifiers|| new RegExp("^[_A-Za-z][_A-Za-z0-9]*");
- var hangingIndent = parserConf.hangingIndent || parserConf.indentUnit;
- var wordOperators = wordRegexp(['and', 'or', 'not', 'is', 'in']);
- var commonkeywords = ['as', 'assert', 'break', 'class', 'continue',
- 'def', 'del', 'elif', 'else', 'except', 'finally',
- 'for', 'from', 'global', 'if', 'import',
- 'lambda', 'pass', 'raise', 'return',
- 'try', 'while', 'with', 'yield'];
- var commonBuiltins = ['abs', 'all', 'any', 'bin', 'bool', 'bytearray', 'callable', 'chr',
- 'classmethod', 'compile', 'complex', 'delattr', 'dict', 'dir', 'divmod',
- 'enumerate', 'eval', 'filter', 'float', 'format', 'frozenset',
- 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id',
- 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len',
- 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next',
- 'object', 'oct', 'open', 'ord', 'pow', 'property', 'range',
- 'repr', 'reversed', 'round', 'set', 'setattr', 'slice',
- 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple',
- 'type', 'vars', 'zip', '__import__', 'NotImplemented',
- 'Ellipsis', '__debug__'];
- var py2 = {'builtins': ['apply', 'basestring', 'buffer', 'cmp', 'coerce', 'execfile',
- 'file', 'intern', 'long', 'raw_input', 'reduce', 'reload',
- 'unichr', 'unicode', 'xrange', 'False', 'True', 'None'],
- 'keywords': ['exec', 'print']};
- var py3 = {'builtins': ['ascii', 'bytes', 'exec', 'print'],
- 'keywords': ['nonlocal', 'False', 'True', 'None']};
+ if (parserConf.version && parseInt(parserConf.version, 10) == 3){
+ // since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator
+ var singleOperators = parserConf.singleOperators || new RegExp("^[\\+\\-\\*/%&|\\^~<>!@]");
+ var identifiers = parserConf.identifiers|| new RegExp("^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*");
+ } else {
+ var singleOperators = parserConf.singleOperators || new RegExp("^[\\+\\-\\*/%&|\\^~<>!]");
+ var identifiers = parserConf.identifiers|| new RegExp("^[_A-Za-z][_A-Za-z0-9]*");
+ }
+ var hangingIndent = parserConf.hangingIndent || conf.indentUnit;
+
+ var myKeywords = commonKeywords, myBuiltins = commonBuiltins;
if(parserConf.extra_keywords != undefined){
- commonkeywords = commonkeywords.concat(parserConf.extra_keywords);
+ myKeywords = myKeywords.concat(parserConf.extra_keywords);
}
if(parserConf.extra_builtins != undefined){
- commonBuiltins = commonBuiltins.concat(parserConf.extra_builtins);
+ myBuiltins = myBuiltins.concat(parserConf.extra_builtins);
}
- if (!!parserConf.version && parseInt(parserConf.version, 10) === 3) {
- commonkeywords = commonkeywords.concat(py3.keywords);
- commonBuiltins = commonBuiltins.concat(py3.builtins);
- var stringPrefixes = new RegExp("^(([rb]|(br))?('{3}|\"{3}|['\"]))", "i");
+ if (parserConf.version && parseInt(parserConf.version, 10) == 3) {
+ myKeywords = myKeywords.concat(py3.keywords);
+ myBuiltins = myBuiltins.concat(py3.builtins);
+ var stringPrefixes = new RegExp("^(([rb]|(br))?('{3}|\"{3}|['\"]))", "i");
} else {
- commonkeywords = commonkeywords.concat(py2.keywords);
- commonBuiltins = commonBuiltins.concat(py2.builtins);
- var stringPrefixes = new RegExp("^(([rub]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i");
+ myKeywords = myKeywords.concat(py2.keywords);
+ myBuiltins = myBuiltins.concat(py2.builtins);
+ var stringPrefixes = new RegExp("^(([rub]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i");
}
- var keywords = wordRegexp(commonkeywords);
- var builtins = wordRegexp(commonBuiltins);
-
- var indentInfo = null;
+ var keywords = wordRegexp(myKeywords);
+ var builtins = wordRegexp(myBuiltins);
// tokenizers
function tokenBase(stream, state) {
- // Handle scope changes
- if (stream.sol()) {
- var scopeOffset = state.scopes[0].offset;
- if (stream.eatSpace()) {
- var lineOffset = stream.indentation();
- if (lineOffset > scopeOffset) {
- indentInfo = 'indent';
- } else if (lineOffset < scopeOffset) {
- indentInfo = 'dedent';
- }
- return null;
- } else {
- if (scopeOffset > 0) {
- dedent(stream, state);
- }
- }
- }
+ // Handle scope changes
+ if (stream.sol() && top(state).type == "py") {
+ var scopeOffset = top(state).offset;
if (stream.eatSpace()) {
- return null;
+ var lineOffset = stream.indentation();
+ if (lineOffset > scopeOffset)
+ pushScope(stream, state, "py");
+ else if (lineOffset < scopeOffset && dedent(stream, state))
+ state.errorToken = true;
+ return null;
+ } else {
+ var style = tokenBaseInner(stream, state);
+ if (scopeOffset > 0 && dedent(stream, state))
+ style += " " + ERRORCLASS;
+ return style;
}
+ }
+ return tokenBaseInner(stream, state);
+ }
- var ch = stream.peek();
+ function tokenBaseInner(stream, state) {
+ if (stream.eatSpace()) return null;
- // Handle Comments
- if (ch === '#') {
- stream.skipToEnd();
- return 'comment';
- }
+ var ch = stream.peek();
- // Handle Number Literals
- if (stream.match(/^[0-9\.]/, false)) {
- var floatLiteral = false;
- // Floats
- if (stream.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; }
- if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; }
- if (stream.match(/^\.\d+/)) { floatLiteral = true; }
- if (floatLiteral) {
- // Float literals may be "imaginary"
- stream.eat(/J/i);
- return 'number';
- }
- // Integers
- var intLiteral = false;
- // Hex
- if (stream.match(/^0x[0-9a-f]+/i)) { intLiteral = true; }
- // Binary
- if (stream.match(/^0b[01]+/i)) { intLiteral = true; }
- // Octal
- if (stream.match(/^0o[0-7]+/i)) { intLiteral = true; }
- // Decimal
- if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) {
- // Decimal literals may be "imaginary"
- stream.eat(/J/i);
- // TODO - Can you have imaginary longs?
- intLiteral = true;
- }
- // Zero by itself with no other piece of number.
- if (stream.match(/^0(?![\dx])/i)) { intLiteral = true; }
- if (intLiteral) {
- // Integer literals may be "long"
- stream.eat(/L/i);
- return 'number';
- }
- }
+ // Handle Comments
+ if (ch == "#") {
+ stream.skipToEnd();
+ return "comment";
+ }
- // Handle Strings
- if (stream.match(stringPrefixes)) {
- state.tokenize = tokenStringFactory(stream.current());
- return state.tokenize(stream, state);
+ // Handle Number Literals
+ if (stream.match(/^[0-9\.]/, false)) {
+ var floatLiteral = false;
+ // Floats
+ if (stream.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; }
+ if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; }
+ if (stream.match(/^\.\d+/)) { floatLiteral = true; }
+ if (floatLiteral) {
+ // Float literals may be "imaginary"
+ stream.eat(/J/i);
+ return "number";
}
+ // Integers
+ var intLiteral = false;
+ // Hex
+ if (stream.match(/^0x[0-9a-f]+/i)) intLiteral = true;
+ // Binary
+ if (stream.match(/^0b[01]+/i)) intLiteral = true;
+ // Octal
+ if (stream.match(/^0o[0-7]+/i)) intLiteral = true;
+ // Decimal
+ if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) {
+ // Decimal literals may be "imaginary"
+ stream.eat(/J/i);
+ // TODO - Can you have imaginary longs?
+ intLiteral = true;
+ }
+ // Zero by itself with no other piece of number.
+ if (stream.match(/^0(?![\dx])/i)) intLiteral = true;
+ if (intLiteral) {
+ // Integer literals may be "long"
+ stream.eat(/L/i);
+ return "number";
+ }
+ }
- // Handle operators and Delimiters
- if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) {
- return null;
- }
- if (stream.match(doubleOperators)
- || stream.match(singleOperators)
- || stream.match(wordOperators)) {
- return 'operator';
- }
- if (stream.match(singleDelimiters)) {
- return null;
- }
+ // Handle Strings
+ if (stream.match(stringPrefixes)) {
+ state.tokenize = tokenStringFactory(stream.current());
+ return state.tokenize(stream, state);
+ }
- if (stream.match(keywords)) {
- return 'keyword';
- }
+ // Handle operators and Delimiters
+ if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters))
+ return null;
- if (stream.match(builtins)) {
- return 'builtin';
- }
+ if (stream.match(doubleOperators)
+ || stream.match(singleOperators)
+ || stream.match(wordOperators))
+ return "operator";
- if (stream.match(/^(self|cls)\b/)) {
- return "variable-2";
- }
+ if (stream.match(singleDelimiters))
+ return null;
- if (stream.match(identifiers)) {
- if (state.lastToken == 'def' || state.lastToken == 'class') {
- return 'def';
- }
- return 'variable';
- }
+ if (stream.match(keywords))
+ return "keyword";
- // Handle non-detected items
- stream.next();
- return ERRORCLASS;
+ if (stream.match(builtins))
+ return "builtin";
+
+ if (stream.match(/^(self|cls)\b/))
+ return "variable-2";
+
+ if (stream.match(identifiers)) {
+ if (state.lastToken == "def" || state.lastToken == "class")
+ return "def";
+ return "variable";
+ }
+
+ // Handle non-detected items
+ stream.next();
+ return ERRORCLASS;
}
function tokenStringFactory(delimiter) {
- while ('rub'.indexOf(delimiter.charAt(0).toLowerCase()) >= 0) {
- delimiter = delimiter.substr(1);
- }
- var singleline = delimiter.length == 1;
- var OUTCLASS = 'string';
+ while ("rub".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)
+ delimiter = delimiter.substr(1);
- function tokenString(stream, state) {
- while (!stream.eol()) {
- stream.eatWhile(/[^'"\\]/);
- if (stream.eat('\\')) {
- stream.next();
- if (singleline && stream.eol()) {
- return OUTCLASS;
- }
- } else if (stream.match(delimiter)) {
- state.tokenize = tokenBase;
- return OUTCLASS;
- } else {
- stream.eat(/['"]/);
- }
- }
- if (singleline) {
- if (parserConf.singleLineStringErrors) {
- return ERRORCLASS;
- } else {
- state.tokenize = tokenBase;
- }
- }
+ var singleline = delimiter.length == 1;
+ var OUTCLASS = "string";
+
+ function tokenString(stream, state) {
+ while (!stream.eol()) {
+ stream.eatWhile(/[^'"\\]/);
+ if (stream.eat("\\")) {
+ stream.next();
+ if (singleline && stream.eol())
+ return OUTCLASS;
+ } else if (stream.match(delimiter)) {
+ state.tokenize = tokenBase;
return OUTCLASS;
+ } else {
+ stream.eat(/['"]/);
+ }
}
- tokenString.isString = true;
- return tokenString;
+ if (singleline) {
+ if (parserConf.singleLineStringErrors)
+ return ERRORCLASS;
+ else
+ state.tokenize = tokenBase;
+ }
+ return OUTCLASS;
+ }
+ tokenString.isString = true;
+ return tokenString;
}
- function indent(stream, state, type) {
- type = type || 'py';
- var indentUnit = 0;
- if (type === 'py') {
- if (state.scopes[0].type !== 'py') {
- state.scopes[0].offset = stream.indentation();
- return;
- }
- for (var i = 0; i < state.scopes.length; ++i) {
- if (state.scopes[i].type === 'py') {
- indentUnit = state.scopes[i].offset + conf.indentUnit;
- break;
- }
- }
- } else if (stream.match(/\s*($|#)/, false)) {
- // An open paren/bracket/brace with only space or comments after it
- // on the line will indent the next line a fixed amount, to make it
- // easier to put arguments, list items, etc. on their own lines.
- indentUnit = stream.indentation() + hangingIndent;
- } else {
- indentUnit = stream.column() + stream.current().length;
- }
- state.scopes.unshift({
- offset: indentUnit,
- type: type
- });
+ function pushScope(stream, state, type) {
+ var offset = 0, align = null;
+ if (type == "py") {
+ while (top(state).type != "py")
+ state.scopes.pop();
+ }
+ offset = top(state).offset + (type == "py" ? conf.indentUnit : hangingIndent);
+ if (type != "py" && !stream.match(/^(\s|#.*)*$/, false))
+ align = stream.column() + 1;
+ state.scopes.push({offset: offset, type: type, align: align});
}
- function dedent(stream, state, type) {
- type = type || 'py';
- if (state.scopes.length == 1) return;
- if (state.scopes[0].type === 'py') {
- var _indent = stream.indentation();
- var _indent_index = -1;
- for (var i = 0; i < state.scopes.length; ++i) {
- if (_indent === state.scopes[i].offset) {
- _indent_index = i;
- break;
- }
- }
- if (_indent_index === -1) {
- return true;
- }
- while (state.scopes[0].offset !== _indent) {
- state.scopes.shift();
- }
- return false;
- } else {
- if (type === 'py') {
- state.scopes[0].offset = stream.indentation();
- return false;
- } else {
- if (state.scopes[0].type != type) {
- return true;
- }
- state.scopes.shift();
- return false;
- }
- }
+ function dedent(stream, state) {
+ var indented = stream.indentation();
+ while (top(state).offset > indented) {
+ if (top(state).type != "py") return true;
+ state.scopes.pop();
+ }
+ return top(state).offset != indented;
}
function tokenLexer(stream, state) {
- indentInfo = null;
- var style = state.tokenize(stream, state);
- var current = stream.current();
+ var style = state.tokenize(stream, state);
+ var current = stream.current();
- // Handle '.' connected identifiers
- if (current === '.') {
- style = stream.match(identifiers, false) ? null : ERRORCLASS;
- if (style === null && state.lastStyle === 'meta') {
- // Apply 'meta' style to '.' connected identifiers when
- // appropriate.
- style = 'meta';
- }
- return style;
+ // Handle '.' connected identifiers
+ if (current == ".") {
+ style = stream.match(identifiers, false) ? null : ERRORCLASS;
+ if (style == null && state.lastStyle == "meta") {
+ // Apply 'meta' style to '.' connected identifiers when
+ // appropriate.
+ style = "meta";
}
-
- // Handle decorators
- if (current === '@') {
- return stream.match(identifiers, false) ? 'meta' : ERRORCLASS;
- }
-
- if ((style === 'variable' || style === 'builtin')
- && state.lastStyle === 'meta') {
- style = 'meta';
- }
-
- // Handle scope changes.
- if (current === 'pass' || current === 'return') {
- state.dedent += 1;
- }
- if (current === 'lambda') state.lambda = true;
- if ((current === ':' && !state.lambda && state.scopes[0].type == 'py')
- || indentInfo === 'indent') {
- indent(stream, state);
- }
- var delimiter_index = '[({'.indexOf(current);
- if (delimiter_index !== -1) {
- indent(stream, state, '])}'.slice(delimiter_index, delimiter_index+1));
- }
- if (indentInfo === 'dedent') {
- if (dedent(stream, state)) {
- return ERRORCLASS;
- }
- }
- delimiter_index = '])}'.indexOf(current);
- if (delimiter_index !== -1) {
- if (dedent(stream, state, current)) {
- return ERRORCLASS;
- }
- }
- if (state.dedent > 0 && stream.eol() && state.scopes[0].type == 'py') {
- if (state.scopes.length > 1) state.scopes.shift();
- state.dedent -= 1;
- }
-
return style;
+ }
+
+ // Handle decorators
+ if (current == "@"){
+ if(parserConf.version && parseInt(parserConf.version, 10) == 3){
+ return stream.match(identifiers, false) ? "meta" : "operator";
+ } else {
+ return stream.match(identifiers, false) ? "meta" : ERRORCLASS;
+ }
+ }
+
+ if ((style == "variable" || style == "builtin")
+ && state.lastStyle == "meta")
+ style = "meta";
+
+ // Handle scope changes.
+ if (current == "pass" || current == "return")
+ state.dedent += 1;
+
+ if (current == "lambda") state.lambda = true;
+ if (current == ":" && !state.lambda && top(state).type == "py")
+ pushScope(stream, state, "py");
+
+ var delimiter_index = current.length == 1 ? "[({".indexOf(current) : -1;
+ if (delimiter_index != -1)
+ pushScope(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
+
+ delimiter_index = "])}".indexOf(current);
+ if (delimiter_index != -1) {
+ if (top(state).type == current) state.scopes.pop();
+ else return ERRORCLASS;
+ }
+ if (state.dedent > 0 && stream.eol() && top(state).type == "py") {
+ if (state.scopes.length > 1) state.scopes.pop();
+ state.dedent -= 1;
+ }
+
+ return style;
}
var external = {
- startState: function(basecolumn) {
- return {
- tokenize: tokenBase,
- scopes: [{offset:basecolumn || 0, type:'py'}],
- lastStyle: null,
- lastToken: null,
- lambda: false,
- dedent: 0
- };
- },
+ startState: function(basecolumn) {
+ return {
+ tokenize: tokenBase,
+ scopes: [{offset: basecolumn || 0, type: "py", align: null}],
+ lastStyle: null,
+ lastToken: null,
+ lambda: false,
+ dedent: 0
+ };
+ },
- token: function(stream, state) {
- var style = tokenLexer(stream, state);
+ token: function(stream, state) {
+ var addErr = state.errorToken;
+ if (addErr) state.errorToken = false;
+ var style = tokenLexer(stream, state);
- state.lastStyle = style;
+ state.lastStyle = style;
- var current = stream.current();
- if (current && style) {
- state.lastToken = current;
- }
+ var current = stream.current();
+ if (current && style)
+ state.lastToken = current;
- if (stream.eol() && state.lambda) {
- state.lambda = false;
- }
- return style;
- },
+ if (stream.eol() && state.lambda)
+ state.lambda = false;
+ return addErr ? style + " " + ERRORCLASS : style;
+ },
- indent: function(state) {
- if (state.tokenize != tokenBase) {
- return state.tokenize.isString ? CodeMirror.Pass : 0;
- }
+ indent: function(state, textAfter) {
+ if (state.tokenize != tokenBase)
+ return state.tokenize.isString ? CodeMirror.Pass : 0;
- return state.scopes[0].offset;
- },
+ var scope = top(state);
+ var closing = textAfter && textAfter.charAt(0) == scope.type;
+ if (scope.align != null)
+ return scope.align - (closing ? 1 : 0);
+ else if (closing && state.scopes.length > 1)
+ return state.scopes[state.scopes.length - 2].offset;
+ else
+ return scope.offset;
+ },
- lineComment: "#",
- fold: "indent"
+ lineComment: "#",
+ fold: "indent"
};
return external;
-});
+ });
-CodeMirror.defineMIME("text/x-python", "python");
+ CodeMirror.defineMIME("text/x-python", "python");
-var words = function(str){return str.split(' ');};
+ var words = function(str) { return str.split(" "); };
-CodeMirror.defineMIME("text/x-cython", {
- name: "python",
- extra_keywords: words("by cdef cimport cpdef ctypedef enum except"+
- "extern gil include nogil property public"+
- "readonly struct union DEF IF ELIF ELSE")
-});
+ CodeMirror.defineMIME("text/x-cython", {
+ name: "python",
+ extra_keywords: words("by cdef cimport cpdef ctypedef enum except"+
+ "extern gil include nogil property public"+
+ "readonly struct union DEF IF ELIF ELSE")
+ });
});
diff --git a/applications/admin/static/codemirror/mode/r/LICENSE b/applications/admin/static/codemirror/mode/r/LICENSE
deleted file mode 100644
index 2510ae16..00000000
--- a/applications/admin/static/codemirror/mode/r/LICENSE
+++ /dev/null
@@ -1,24 +0,0 @@
-Copyright (c) 2011, Ubalo, Inc.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- * Neither the name of the Ubalo, Inc nor the names of its
- contributors may be used to endorse or promote products derived
- from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL UBALO, INC BE LIABLE FOR ANY
-DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/applications/admin/static/codemirror/mode/r/index.html b/applications/admin/static/codemirror/mode/r/index.html
index f73e13d6..6dd96346 100644
--- a/applications/admin/static/codemirror/mode/r/index.html
+++ b/applications/admin/static/codemirror/mode/r/index.html
@@ -15,12 +15,12 @@
.cm-s-default span.cm-arg-is { color: brown; }
-
+
CodeMirror
Language modes
@@ -80,7 +80,6 @@ powerful <- function(x) {list(x2=x*x, x3=x*x*x, x4=x*x*x*x)}
MIME types defined: text/x-rsrc.
Development of the CodeMirror R mode was kindly sponsored
- by Ubalo , who hold
- the license .
+ by Ubalo .
diff --git a/applications/admin/static/codemirror/mode/r/r.js b/applications/admin/static/codemirror/mode/r/r.js
index 6410efbb..1ab4a956 100644
--- a/applications/admin/static/codemirror/mode/r/r.js
+++ b/applications/admin/static/codemirror/mode/r/r.js
@@ -1,3 +1,16 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
CodeMirror.defineMode("r", function(config) {
function wordObj(str) {
var words = str.split(" "), res = {};
@@ -36,7 +49,11 @@ CodeMirror.defineMode("r", function(config) {
var word = stream.current();
if (atoms.propertyIsEnumerable(word)) return "atom";
if (keywords.propertyIsEnumerable(word)) {
- if (blockkeywords.propertyIsEnumerable(word)) curPunc = "block";
+ // Block keywords start new blocks, except 'else if', which only starts
+ // one new block for the 'if', no block for the 'else'.
+ if (blockkeywords.propertyIsEnumerable(word) &&
+ !stream.match(/\s*if(\s+|$)/, false))
+ curPunc = "block";
return "keyword";
}
if (builtins.propertyIsEnumerable(word)) return "builtin";
@@ -134,8 +151,12 @@ CodeMirror.defineMode("r", function(config) {
if (ctx.type == "block") return ctx.indent + (firstChar == "{" ? 0 : config.indentUnit);
else if (ctx.align) return ctx.column + (closing ? 0 : 1);
else return ctx.indent + (closing ? 0 : config.indentUnit);
- }
+ },
+
+ lineComment: "#"
};
});
CodeMirror.defineMIME("text/x-rsrc", "r");
+
+});
diff --git a/applications/admin/static/codemirror/mode/rpm/changes/changes.js b/applications/admin/static/codemirror/mode/rpm/changes/changes.js
deleted file mode 100644
index 14a08d97..00000000
--- a/applications/admin/static/codemirror/mode/rpm/changes/changes.js
+++ /dev/null
@@ -1,19 +0,0 @@
-CodeMirror.defineMode("changes", function() {
- var headerSeperator = /^-+$/;
- var headerLine = /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ?\d{1,2} \d{2}:\d{2}(:\d{2})? [A-Z]{3,4} \d{4} - /;
- var simpleEmail = /^[\w+.-]+@[\w.-]+/;
-
- return {
- token: function(stream) {
- if (stream.sol()) {
- if (stream.match(headerSeperator)) { return 'tag'; }
- if (stream.match(headerLine)) { return 'tag'; }
- }
- if (stream.match(simpleEmail)) { return 'string'; }
- stream.next();
- return null;
- }
- };
-});
-
-CodeMirror.defineMIME("text/x-rpm-changes", "changes");
diff --git a/applications/admin/static/codemirror/mode/rpm/changes/index.html b/applications/admin/static/codemirror/mode/rpm/changes/index.html
index 18fe7ab7..6e5031bd 100644
--- a/applications/admin/static/codemirror/mode/rpm/changes/index.html
+++ b/applications/admin/static/codemirror/mode/rpm/changes/index.html
@@ -11,12 +11,12 @@
-
+
CodeMirror
Language modes
@@ -58,8 +58,7 @@ Wed Oct 5 14:34:10 UTC 2011 - misterx@example.com
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
mode: {name: "changes"},
lineNumbers: true,
- indentUnit: 4,
- tabMode: "shift"
+ indentUnit: 4
});
diff --git a/applications/admin/static/codemirror/mode/rpm/spec/index.html b/applications/admin/static/codemirror/mode/rpm/index.html
similarity index 59%
rename from applications/admin/static/codemirror/mode/rpm/spec/index.html
rename to applications/admin/static/codemirror/mode/rpm/index.html
index 127b72ee..9a34e6df 100644
--- a/applications/admin/static/codemirror/mode/rpm/spec/index.html
+++ b/applications/admin/static/codemirror/mode/rpm/index.html
@@ -1,34 +1,70 @@
-CodeMirror: RPM spec mode
+CodeMirror: RPM changes mode
-
-
-
-
-
+
+
+
+
+RPM changes mode
+
+
+-------------------------------------------------------------------
+Tue Oct 18 13:58:40 UTC 2011 - misterx@example.com
+
+- Update to r60.3
+- Fixes bug in the reflect package
+ * disallow Interface method on Value obtained via unexported name
+
+-------------------------------------------------------------------
+Thu Oct 6 08:14:24 UTC 2011 - misterx@example.com
+
+- Update to r60.2
+- Fixes memory leak in certain map types
+
+-------------------------------------------------------------------
+Wed Oct 5 14:34:10 UTC 2011 - misterx@example.com
+
+- Tweaks for gdb debugging
+- go.spec changes:
+ - move %go_arch definition to %prep section
+ - pass correct location of go specific gdb pretty printer and
+ functions to cpp as HOST_EXTRA_CFLAGS macro
+ - install go gdb functions & printer
+- gdb-printer.patch
+ - patch linker (src/cmd/ld/dwarf.c) to emit correct location of go
+ gdb functions and pretty printer
+
+
+
RPM spec mode
-
+
#
# spec file for package minidlna
#
@@ -102,13 +138,12 @@ find %{buildroot} -type f -name '*.la' -exec rm -f {} ';'
%changelog
- MIME types defined: text/x-rpm-spec.
-
+ MIME types defined: text/x-rpm-spec, text/x-rpm-changes.
diff --git a/applications/admin/static/codemirror/mode/rpm/spec/spec.js b/applications/admin/static/codemirror/mode/rpm/rpm.js
similarity index 63%
rename from applications/admin/static/codemirror/mode/rpm/spec/spec.js
rename to applications/admin/static/codemirror/mode/rpm/rpm.js
index 9f339c21..3bb7cd2f 100644
--- a/applications/admin/static/codemirror/mode/rpm/spec/spec.js
+++ b/applications/admin/static/codemirror/mode/rpm/rpm.js
@@ -1,10 +1,43 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.defineMode("rpm-changes", function() {
+ var headerSeperator = /^-+$/;
+ var headerLine = /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ?\d{1,2} \d{2}:\d{2}(:\d{2})? [A-Z]{3,4} \d{4} - /;
+ var simpleEmail = /^[\w+.-]+@[\w.-]+/;
+
+ return {
+ token: function(stream) {
+ if (stream.sol()) {
+ if (stream.match(headerSeperator)) { return 'tag'; }
+ if (stream.match(headerLine)) { return 'tag'; }
+ }
+ if (stream.match(simpleEmail)) { return 'string'; }
+ stream.next();
+ return null;
+ }
+ };
+});
+
+CodeMirror.defineMIME("text/x-rpm-changes", "rpm-changes");
+
// Quick and dirty spec file highlighting
-CodeMirror.defineMode("spec", function() {
+CodeMirror.defineMode("rpm-spec", function() {
var arch = /^(i386|i586|i686|x86_64|ppc64|ppc|ia64|s390x|s390|sparc64|sparcv9|sparc|noarch|alphaev6|alpha|hppa|mipsel)/;
var preamble = /^(Name|Version|Release|License|Summary|Url|Group|Source|BuildArch|BuildRequires|BuildRoot|AutoReqProv|Provides|Requires(\(\w+\))?|Obsoletes|Conflicts|Recommends|Source\d*|Patch\d*|ExclusiveArch|NoSource|Supplements):/;
- var section = /^%(debug_package|package|description|prep|build|install|files|clean|changelog|preun|postun|pre|post|triggerin|triggerun|pretrans|posttrans|verifyscript|check|triggerpostun|triggerprein|trigger)/;
+ var section = /^%(debug_package|package|description|prep|build|install|files|clean|changelog|preinstall|preun|postinstall|postun|pre|post|triggerin|triggerun|pretrans|posttrans|verifyscript|check|triggerpostun|triggerprein|trigger)/;
var control_flow_complex = /^%(ifnarch|ifarch|if)/; // rpm control flow macros
var control_flow_simple = /^%(else|endif)/; // rpm control flow macros
var operators = /^(\!|\?|\<\=|\<|\>\=|\>|\=\=|\&\&|\|\|)/; // operators in control flow macros
@@ -63,4 +96,6 @@ CodeMirror.defineMode("spec", function() {
};
});
-CodeMirror.defineMIME("text/x-rpm-spec", "spec");
+CodeMirror.defineMIME("text/x-rpm-spec", "rpm-spec");
+
+});
diff --git a/applications/admin/static/codemirror/mode/rpm/spec/spec.css b/applications/admin/static/codemirror/mode/rpm/spec/spec.css
deleted file mode 100644
index d0a5d430..00000000
--- a/applications/admin/static/codemirror/mode/rpm/spec/spec.css
+++ /dev/null
@@ -1,5 +0,0 @@
-.cm-s-default span.cm-preamble {color: #b26818; font-weight: bold;}
-.cm-s-default span.cm-macro {color: #b218b2;}
-.cm-s-default span.cm-section {color: green; font-weight: bold;}
-.cm-s-default span.cm-script {color: red;}
-.cm-s-default span.cm-issue {color: yellow;}
diff --git a/applications/admin/static/codemirror/mode/rst/LICENSE.txt b/applications/admin/static/codemirror/mode/rst/LICENSE.txt
deleted file mode 100644
index 39484fab..00000000
--- a/applications/admin/static/codemirror/mode/rst/LICENSE.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License
-
-Copyright (c) 2013 Hasan Karahan
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
\ No newline at end of file
diff --git a/applications/admin/static/codemirror/mode/rst/index.html b/applications/admin/static/codemirror/mode/rst/index.html
index 78030ebe..2902dea2 100644
--- a/applications/admin/static/codemirror/mode/rst/index.html
+++ b/applications/admin/static/codemirror/mode/rst/index.html
@@ -6,15 +6,16 @@
+
-
+
CodeMirror
Language modes
diff --git a/applications/admin/static/codemirror/mode/rst/rst.js b/applications/admin/static/codemirror/mode/rst/rst.js
index 75563ba9..bcf110c1 100644
--- a/applications/admin/static/codemirror/mode/rst/rst.js
+++ b/applications/admin/static/codemirror/mode/rst/rst.js
@@ -1,554 +1,549 @@
-CodeMirror.defineMode('rst-base', function (config) {
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
- ///////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"), require("../python/python"), require("../stex/stex"), require("../../addon/mode/overlay"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror", "../python/python", "../stex/stex", "../../addon/mode/overlay"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
- function format(string) {
- var args = Array.prototype.slice.call(arguments, 1);
- return string.replace(/{(\d+)}/g, function (match, n) {
- return typeof args[n] != 'undefined' ? args[n] : match;
- });
+CodeMirror.defineMode('rst', function (config, options) {
+
+ var rx_strong = /^\*\*[^\*\s](?:[^\*]*[^\*\s])?\*\*/;
+ var rx_emphasis = /^\*[^\*\s](?:[^\*]*[^\*\s])?\*/;
+ var rx_literal = /^``[^`\s](?:[^`]*[^`\s])``/;
+
+ var rx_number = /^(?:[\d]+(?:[\.,]\d+)*)/;
+ var rx_positive = /^(?:\s\+[\d]+(?:[\.,]\d+)*)/;
+ var rx_negative = /^(?:\s\-[\d]+(?:[\.,]\d+)*)/;
+
+ var rx_uri_protocol = "[Hh][Tt][Tt][Pp][Ss]?://";
+ var rx_uri_domain = "(?:[\\d\\w.-]+)\\.(?:\\w{2,6})";
+ var rx_uri_path = "(?:/[\\d\\w\\#\\%\\&\\-\\.\\,\\/\\:\\=\\?\\~]+)*";
+ var rx_uri = new RegExp("^" + rx_uri_protocol + rx_uri_domain + rx_uri_path);
+
+ var overlay = {
+ token: function (stream) {
+
+ if (stream.match(rx_strong) && stream.match (/\W+|$/, false))
+ return 'strong';
+ if (stream.match(rx_emphasis) && stream.match (/\W+|$/, false))
+ return 'em';
+ if (stream.match(rx_literal) && stream.match (/\W+|$/, false))
+ return 'string-2';
+ if (stream.match(rx_number))
+ return 'number';
+ if (stream.match(rx_positive))
+ return 'positive';
+ if (stream.match(rx_negative))
+ return 'negative';
+ if (stream.match(rx_uri))
+ return 'link';
+
+ while (stream.next() != null) {
+ if (stream.match(rx_strong, false)) break;
+ if (stream.match(rx_emphasis, false)) break;
+ if (stream.match(rx_literal, false)) break;
+ if (stream.match(rx_number, false)) break;
+ if (stream.match(rx_positive, false)) break;
+ if (stream.match(rx_negative, false)) break;
+ if (stream.match(rx_uri, false)) break;
+ }
+
+ return null;
}
+ };
- function AssertException(message) {
- this.message = message;
- }
+ var mode = CodeMirror.getMode(
+ config, options.backdrop || 'rst-base'
+ );
- AssertException.prototype.toString = function () {
- return 'AssertException: ' + this.message;
- };
-
- function assert(expression, message) {
- if (!expression) throw new AssertException(message);
- return expression;
- }
-
- ///////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////
-
- var mode_python = CodeMirror.getMode(config, 'python');
- var mode_stex = CodeMirror.getMode(config, 'stex');
-
- ///////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////
-
- var SEPA = "\\s+";
- var TAIL = "(?:\\s*|\\W|$)",
- rx_TAIL = new RegExp(format('^{0}', TAIL));
-
- var NAME =
- "(?:[^\\W\\d_](?:[\\w!\"#$%&'()\\*\\+,\\-\\.\/:;<=>\\?]*[^\\W_])?)",
- rx_NAME = new RegExp(format('^{0}', NAME));
- var NAME_WWS =
- "(?:[^\\W\\d_](?:[\\w\\s!\"#$%&'()\\*\\+,\\-\\.\/:;<=>\\?]*[^\\W_])?)";
- var REF_NAME = format('(?:{0}|`{1}`)', NAME, NAME_WWS);
-
- var TEXT1 = "(?:[^\\s\\|](?:[^\\|]*[^\\s\\|])?)";
- var TEXT2 = "(?:[^\\`]+)",
- rx_TEXT2 = new RegExp(format('^{0}', TEXT2));
-
- var rx_section = new RegExp(
- "^([!'#$%&\"()*+,-./:;<=>?@\\[\\\\\\]^_`{|}~])\\1{3,}\\s*$");
- var rx_explicit = new RegExp(
- format('^\\.\\.{0}', SEPA));
- var rx_link = new RegExp(
- format('^_{0}:{1}|^__:{1}', REF_NAME, TAIL));
- var rx_directive = new RegExp(
- format('^{0}::{1}', REF_NAME, TAIL));
- var rx_substitution = new RegExp(
- format('^\\|{0}\\|{1}{2}::{3}', TEXT1, SEPA, REF_NAME, TAIL));
- var rx_footnote = new RegExp(
- format('^\\[(?:\\d+|#{0}?|\\*)]{1}', REF_NAME, TAIL));
- var rx_citation = new RegExp(
- format('^\\[{0}\\]{1}', REF_NAME, TAIL));
-
- var rx_substitution_ref = new RegExp(
- format('^\\|{0}\\|', TEXT1));
- var rx_footnote_ref = new RegExp(
- format('^\\[(?:\\d+|#{0}?|\\*)]_', REF_NAME));
- var rx_citation_ref = new RegExp(
- format('^\\[{0}\\]_', REF_NAME));
- var rx_link_ref1 = new RegExp(
- format('^{0}__?', REF_NAME));
- var rx_link_ref2 = new RegExp(
- format('^`{0}`_', TEXT2));
-
- var rx_role_pre = new RegExp(
- format('^:{0}:`{1}`{2}', NAME, TEXT2, TAIL));
- var rx_role_suf = new RegExp(
- format('^`{1}`:{0}:{2}', NAME, TEXT2, TAIL));
- var rx_role = new RegExp(
- format('^:{0}:{1}', NAME, TAIL));
-
- var rx_directive_name = new RegExp(format('^{0}', REF_NAME));
- var rx_directive_tail = new RegExp(format('^::{0}', TAIL));
- var rx_substitution_text = new RegExp(format('^\\|{0}\\|', TEXT1));
- var rx_substitution_sepa = new RegExp(format('^{0}', SEPA));
- var rx_substitution_name = new RegExp(format('^{0}', REF_NAME));
- var rx_substitution_tail = new RegExp(format('^::{0}', TAIL));
- var rx_link_head = new RegExp("^_");
- var rx_link_name = new RegExp(format('^{0}|_', REF_NAME));
- var rx_link_tail = new RegExp(format('^:{0}', TAIL));
-
- var rx_verbatim = new RegExp('^::\\s*$');
- var rx_examples = new RegExp('^\\s+(?:>>>|In \\[\\d+\\]:)\\s');
-
- ///////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////
-
- function to_normal(stream, state) {
- var token = null;
-
- if (stream.sol() && stream.match(rx_examples, false)) {
- change(state, to_mode, {
- mode: mode_python, local: mode_python.startState()
- });
- } else if (stream.sol() && stream.match(rx_explicit)) {
- change(state, to_explicit);
- token = 'meta';
- } else if (stream.sol() && stream.match(rx_section)) {
- change(state, to_normal);
- token = 'header';
- } else if (phase(state) == rx_role_pre ||
- stream.match(rx_role_pre, false)) {
-
- switch (stage(state)) {
- case 0:
- change(state, to_normal, context(rx_role_pre, 1));
- assert(stream.match(/^:/));
- token = 'meta';
- break;
- case 1:
- change(state, to_normal, context(rx_role_pre, 2));
- assert(stream.match(rx_NAME));
- token = 'keyword';
-
- if (stream.current().match(/^(?:math|latex)/)) {
- state.tmp_stex = true;
- }
- break;
- case 2:
- change(state, to_normal, context(rx_role_pre, 3));
- assert(stream.match(/^:`/));
- token = 'meta';
- break;
- case 3:
- if (state.tmp_stex) {
- state.tmp_stex = undefined; state.tmp = {
- mode: mode_stex, local: mode_stex.startState()
- };
- }
-
- if (state.tmp) {
- if (stream.peek() == '`') {
- change(state, to_normal, context(rx_role_pre, 4));
- state.tmp = undefined;
- break;
- }
-
- token = state.tmp.mode.token(stream, state.tmp.local);
- break;
- }
-
- change(state, to_normal, context(rx_role_pre, 4));
- assert(stream.match(rx_TEXT2));
- token = 'string';
- break;
- case 4:
- change(state, to_normal, context(rx_role_pre, 5));
- assert(stream.match(/^`/));
- token = 'meta';
- break;
- case 5:
- change(state, to_normal, context(rx_role_pre, 6));
- assert(stream.match(rx_TAIL));
- break;
- default:
- change(state, to_normal);
- assert(stream.current() == '');
- }
- } else if (phase(state) == rx_role_suf ||
- stream.match(rx_role_suf, false)) {
-
- switch (stage(state)) {
- case 0:
- change(state, to_normal, context(rx_role_suf, 1));
- assert(stream.match(/^`/));
- token = 'meta';
- break;
- case 1:
- change(state, to_normal, context(rx_role_suf, 2));
- assert(stream.match(rx_TEXT2));
- token = 'string';
- break;
- case 2:
- change(state, to_normal, context(rx_role_suf, 3));
- assert(stream.match(/^`:/));
- token = 'meta';
- break;
- case 3:
- change(state, to_normal, context(rx_role_suf, 4));
- assert(stream.match(rx_NAME));
- token = 'keyword';
- break;
- case 4:
- change(state, to_normal, context(rx_role_suf, 5));
- assert(stream.match(/^:/));
- token = 'meta';
- break;
- case 5:
- change(state, to_normal, context(rx_role_suf, 6));
- assert(stream.match(rx_TAIL));
- break;
- default:
- change(state, to_normal);
- assert(stream.current() == '');
- }
- } else if (phase(state) == rx_role || stream.match(rx_role, false)) {
-
- switch (stage(state)) {
- case 0:
- change(state, to_normal, context(rx_role, 1));
- assert(stream.match(/^:/));
- token = 'meta';
- break;
- case 1:
- change(state, to_normal, context(rx_role, 2));
- assert(stream.match(rx_NAME));
- token = 'keyword';
- break;
- case 2:
- change(state, to_normal, context(rx_role, 3));
- assert(stream.match(/^:/));
- token = 'meta';
- break;
- case 3:
- change(state, to_normal, context(rx_role, 4));
- assert(stream.match(rx_TAIL));
- break;
- default:
- change(state, to_normal);
- assert(stream.current() == '');
- }
- } else if (phase(state) == rx_substitution_ref ||
- stream.match(rx_substitution_ref, false)) {
-
- switch (stage(state)) {
- case 0:
- change(state, to_normal, context(rx_substitution_ref, 1));
- assert(stream.match(rx_substitution_text));
- token = 'variable-2';
- break;
- case 1:
- change(state, to_normal, context(rx_substitution_ref, 2));
- if (stream.match(/^_?_?/)) token = 'link';
- break;
- default:
- change(state, to_normal);
- assert(stream.current() == '');
- }
- } else if (stream.match(rx_footnote_ref)) {
- change(state, to_normal);
- token = 'quote';
- } else if (stream.match(rx_citation_ref)) {
- change(state, to_normal);
- token = 'quote';
- } else if (stream.match(rx_link_ref1)) {
- change(state, to_normal);
- if (!stream.peek() || stream.peek().match(/^\W$/)) {
- token = 'link';
- }
- } else if (phase(state) == rx_link_ref2 ||
- stream.match(rx_link_ref2, false)) {
-
- switch (stage(state)) {
- case 0:
- if (!stream.peek() || stream.peek().match(/^\W$/)) {
- change(state, to_normal, context(rx_link_ref2, 1));
- } else {
- stream.match(rx_link_ref2);
- }
- break;
- case 1:
- change(state, to_normal, context(rx_link_ref2, 2));
- assert(stream.match(/^`/));
- token = 'link';
- break;
- case 2:
- change(state, to_normal, context(rx_link_ref2, 3));
- assert(stream.match(rx_TEXT2));
- break;
- case 3:
- change(state, to_normal, context(rx_link_ref2, 4));
- assert(stream.match(/^`_/));
- token = 'link';
- break;
- default:
- change(state, to_normal);
- assert(stream.current() == '');
- }
- } else if (stream.match(rx_verbatim)) {
- change(state, to_verbatim);
- }
-
- else {
- if (stream.next()) change(state, to_normal);
- }
-
- return token;
- }
-
- ///////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////
-
- function to_explicit(stream, state) {
- var token = null;
-
- if (phase(state) == rx_substitution ||
- stream.match(rx_substitution, false)) {
-
- switch (stage(state)) {
- case 0:
- change(state, to_explicit, context(rx_substitution, 1));
- assert(stream.match(rx_substitution_text));
- token = 'variable-2';
- break;
- case 1:
- change(state, to_explicit, context(rx_substitution, 2));
- assert(stream.match(rx_substitution_sepa));
- break;
- case 2:
- change(state, to_explicit, context(rx_substitution, 3));
- assert(stream.match(rx_substitution_name));
- token = 'keyword';
- break;
- case 3:
- change(state, to_explicit, context(rx_substitution, 4));
- assert(stream.match(rx_substitution_tail));
- token = 'meta';
- break;
- default:
- change(state, to_normal);
- assert(stream.current() == '');
- }
- } else if (phase(state) == rx_directive ||
- stream.match(rx_directive, false)) {
-
- switch (stage(state)) {
- case 0:
- change(state, to_explicit, context(rx_directive, 1));
- assert(stream.match(rx_directive_name));
- token = 'keyword';
-
- if (stream.current().match(/^(?:math|latex)/))
- state.tmp_stex = true;
- else if (stream.current().match(/^python/))
- state.tmp_py = true;
- break;
- case 1:
- change(state, to_explicit, context(rx_directive, 2));
- assert(stream.match(rx_directive_tail));
- token = 'meta';
-
- if (stream.match(/^latex\s*$/) || state.tmp_stex) {
- state.tmp_stex = undefined; change(state, to_mode, {
- mode: mode_stex, local: mode_stex.startState()
- });
- }
- break;
- case 2:
- change(state, to_explicit, context(rx_directive, 3));
- if (stream.match(/^python\s*$/) || state.tmp_py) {
- state.tmp_py = undefined; change(state, to_mode, {
- mode: mode_python, local: mode_python.startState()
- });
- }
- break;
- default:
- change(state, to_normal);
- assert(stream.current() == '');
- }
- } else if (phase(state) == rx_link || stream.match(rx_link, false)) {
-
- switch (stage(state)) {
- case 0:
- change(state, to_explicit, context(rx_link, 1));
- assert(stream.match(rx_link_head));
- assert(stream.match(rx_link_name));
- token = 'link';
- break;
- case 1:
- change(state, to_explicit, context(rx_link, 2));
- assert(stream.match(rx_link_tail));
- token = 'meta';
- break;
- default:
- change(state, to_normal);
- assert(stream.current() == '');
- }
- } else if (stream.match(rx_footnote)) {
- change(state, to_normal);
- token = 'quote';
- } else if (stream.match(rx_citation)) {
- change(state, to_normal);
- token = 'quote';
- }
-
- else {
- stream.eatSpace();
- if (stream.eol()) {
- change(state, to_normal);
- } else {
- stream.skipToEnd();
- change(state, to_comment);
- token = 'comment';
- }
- }
-
- return token;
- }
-
- ///////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////
-
- function to_comment(stream, state) {
- return as_block(stream, state, 'comment');
- }
-
- function to_verbatim(stream, state) {
- return as_block(stream, state, 'meta');
- }
-
- function as_block(stream, state, token) {
- if (stream.eol() || stream.eatSpace()) {
- stream.skipToEnd();
- return token;
- } else {
- change(state, to_normal);
- return null;
- }
- }
-
- ///////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////
-
- function to_mode(stream, state) {
-
- if (state.ctx.mode && state.ctx.local) {
-
- if (stream.sol()) {
- if (!stream.eatSpace()) change(state, to_normal);
- return null;
- }
-
- return state.ctx.mode.token(stream, state.ctx.local);
- }
-
- change(state, to_normal);
- return null;
- }
-
- ///////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////
-
- function context(phase, stage, mode, local) {
- return {phase: phase, stage: stage, mode: mode, local: local};
- }
-
- function change(state, tok, ctx) {
- state.tok = tok;
- state.ctx = ctx || {};
- }
-
- function stage(state) {
- return state.ctx.stage || 0;
- }
-
- function phase(state) {
- return state.ctx.phase;
- }
-
- ///////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////
-
- return {
- startState: function () {
- return {tok: to_normal, ctx: context(undefined, 0)};
- },
-
- copyState: function (state) {
- return {tok: state.tok, ctx: state.ctx};
- },
-
- innerMode: function (state) {
- return state.tmp ? {state: state.tmp.local, mode: state.tmp.mode}
- : state.ctx ? {state: state.ctx.local, mode: state.ctx.mode}
- : null;
- },
-
- token: function (stream, state) {
- return state.tok(stream, state);
- }
- };
+ return CodeMirror.overlayMode(mode, overlay, true); // combine
}, 'python', 'stex');
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
-CodeMirror.defineMode('rst', function (config, options) {
+CodeMirror.defineMode('rst-base', function (config) {
- var rx_strong = /^\*\*[^\*\s](?:[^\*]*[^\*\s])?\*\*/;
- var rx_emphasis = /^\*[^\*\s](?:[^\*]*[^\*\s])?\*/;
- var rx_literal = /^``[^`\s](?:[^`]*[^`\s])``/;
+ ///////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////
- var rx_number = /^(?:[\d]+(?:[\.,]\d+)*)/;
- var rx_positive = /^(?:\s\+[\d]+(?:[\.,]\d+)*)/;
- var rx_negative = /^(?:\s\-[\d]+(?:[\.,]\d+)*)/;
+ function format(string) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ return string.replace(/{(\d+)}/g, function (match, n) {
+ return typeof args[n] != 'undefined' ? args[n] : match;
+ });
+ }
- var rx_uri_protocol = "[Hh][Tt][Tt][Pp][Ss]?://";
- var rx_uri_domain = "(?:[\\d\\w.-]+)\\.(?:\\w{2,6})";
- var rx_uri_path = "(?:/[\\d\\w\\#\\%\\&\\-\\.\\,\\/\\:\\=\\?\\~]+)*";
- var rx_uri = new RegExp("^" +
- rx_uri_protocol + rx_uri_domain + rx_uri_path
- );
+ ///////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////
- var overlay = {
- token: function (stream) {
+ var mode_python = CodeMirror.getMode(config, 'python');
+ var mode_stex = CodeMirror.getMode(config, 'stex');
- if (stream.match(rx_strong) && stream.match (/\W+|$/, false))
- return 'strong';
- if (stream.match(rx_emphasis) && stream.match (/\W+|$/, false))
- return 'em';
- if (stream.match(rx_literal) && stream.match (/\W+|$/, false))
- return 'string-2';
- if (stream.match(rx_number))
- return 'number';
- if (stream.match(rx_positive))
- return 'positive';
- if (stream.match(rx_negative))
- return 'negative';
- if (stream.match(rx_uri))
- return 'link';
+ ///////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////
- while (stream.next() != null) {
- if (stream.match(rx_strong, false)) break;
- if (stream.match(rx_emphasis, false)) break;
- if (stream.match(rx_literal, false)) break;
- if (stream.match(rx_number, false)) break;
- if (stream.match(rx_positive, false)) break;
- if (stream.match(rx_negative, false)) break;
- if (stream.match(rx_uri, false)) break;
- }
+ var SEPA = "\\s+";
+ var TAIL = "(?:\\s*|\\W|$)",
+ rx_TAIL = new RegExp(format('^{0}', TAIL));
- return null;
+ var NAME =
+ "(?:[^\\W\\d_](?:[\\w!\"#$%&'()\\*\\+,\\-\\.\/:;<=>\\?]*[^\\W_])?)",
+ rx_NAME = new RegExp(format('^{0}', NAME));
+ var NAME_WWS =
+ "(?:[^\\W\\d_](?:[\\w\\s!\"#$%&'()\\*\\+,\\-\\.\/:;<=>\\?]*[^\\W_])?)";
+ var REF_NAME = format('(?:{0}|`{1}`)', NAME, NAME_WWS);
+
+ var TEXT1 = "(?:[^\\s\\|](?:[^\\|]*[^\\s\\|])?)";
+ var TEXT2 = "(?:[^\\`]+)",
+ rx_TEXT2 = new RegExp(format('^{0}', TEXT2));
+
+ var rx_section = new RegExp(
+ "^([!'#$%&\"()*+,-./:;<=>?@\\[\\\\\\]^_`{|}~])\\1{3,}\\s*$");
+ var rx_explicit = new RegExp(
+ format('^\\.\\.{0}', SEPA));
+ var rx_link = new RegExp(
+ format('^_{0}:{1}|^__:{1}', REF_NAME, TAIL));
+ var rx_directive = new RegExp(
+ format('^{0}::{1}', REF_NAME, TAIL));
+ var rx_substitution = new RegExp(
+ format('^\\|{0}\\|{1}{2}::{3}', TEXT1, SEPA, REF_NAME, TAIL));
+ var rx_footnote = new RegExp(
+ format('^\\[(?:\\d+|#{0}?|\\*)]{1}', REF_NAME, TAIL));
+ var rx_citation = new RegExp(
+ format('^\\[{0}\\]{1}', REF_NAME, TAIL));
+
+ var rx_substitution_ref = new RegExp(
+ format('^\\|{0}\\|', TEXT1));
+ var rx_footnote_ref = new RegExp(
+ format('^\\[(?:\\d+|#{0}?|\\*)]_', REF_NAME));
+ var rx_citation_ref = new RegExp(
+ format('^\\[{0}\\]_', REF_NAME));
+ var rx_link_ref1 = new RegExp(
+ format('^{0}__?', REF_NAME));
+ var rx_link_ref2 = new RegExp(
+ format('^`{0}`_', TEXT2));
+
+ var rx_role_pre = new RegExp(
+ format('^:{0}:`{1}`{2}', NAME, TEXT2, TAIL));
+ var rx_role_suf = new RegExp(
+ format('^`{1}`:{0}:{2}', NAME, TEXT2, TAIL));
+ var rx_role = new RegExp(
+ format('^:{0}:{1}', NAME, TAIL));
+
+ var rx_directive_name = new RegExp(format('^{0}', REF_NAME));
+ var rx_directive_tail = new RegExp(format('^::{0}', TAIL));
+ var rx_substitution_text = new RegExp(format('^\\|{0}\\|', TEXT1));
+ var rx_substitution_sepa = new RegExp(format('^{0}', SEPA));
+ var rx_substitution_name = new RegExp(format('^{0}', REF_NAME));
+ var rx_substitution_tail = new RegExp(format('^::{0}', TAIL));
+ var rx_link_head = new RegExp("^_");
+ var rx_link_name = new RegExp(format('^{0}|_', REF_NAME));
+ var rx_link_tail = new RegExp(format('^:{0}', TAIL));
+
+ var rx_verbatim = new RegExp('^::\\s*$');
+ var rx_examples = new RegExp('^\\s+(?:>>>|In \\[\\d+\\]:)\\s');
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////
+
+ function to_normal(stream, state) {
+ var token = null;
+
+ if (stream.sol() && stream.match(rx_examples, false)) {
+ change(state, to_mode, {
+ mode: mode_python, local: CodeMirror.startState(mode_python)
+ });
+ } else if (stream.sol() && stream.match(rx_explicit)) {
+ change(state, to_explicit);
+ token = 'meta';
+ } else if (stream.sol() && stream.match(rx_section)) {
+ change(state, to_normal);
+ token = 'header';
+ } else if (phase(state) == rx_role_pre ||
+ stream.match(rx_role_pre, false)) {
+
+ switch (stage(state)) {
+ case 0:
+ change(state, to_normal, context(rx_role_pre, 1));
+ stream.match(/^:/);
+ token = 'meta';
+ break;
+ case 1:
+ change(state, to_normal, context(rx_role_pre, 2));
+ stream.match(rx_NAME);
+ token = 'keyword';
+
+ if (stream.current().match(/^(?:math|latex)/)) {
+ state.tmp_stex = true;
+ }
+ break;
+ case 2:
+ change(state, to_normal, context(rx_role_pre, 3));
+ stream.match(/^:`/);
+ token = 'meta';
+ break;
+ case 3:
+ if (state.tmp_stex) {
+ state.tmp_stex = undefined; state.tmp = {
+ mode: mode_stex, local: CodeMirror.startState(mode_stex)
+ };
}
- };
- var mode = CodeMirror.getMode(
- config, options.backdrop || 'rst-base'
- );
+ if (state.tmp) {
+ if (stream.peek() == '`') {
+ change(state, to_normal, context(rx_role_pre, 4));
+ state.tmp = undefined;
+ break;
+ }
- return CodeMirror.overlayMode(mode, overlay, true); // combine
+ token = state.tmp.mode.token(stream, state.tmp.local);
+ break;
+ }
+
+ change(state, to_normal, context(rx_role_pre, 4));
+ stream.match(rx_TEXT2);
+ token = 'string';
+ break;
+ case 4:
+ change(state, to_normal, context(rx_role_pre, 5));
+ stream.match(/^`/);
+ token = 'meta';
+ break;
+ case 5:
+ change(state, to_normal, context(rx_role_pre, 6));
+ stream.match(rx_TAIL);
+ break;
+ default:
+ change(state, to_normal);
+ }
+ } else if (phase(state) == rx_role_suf ||
+ stream.match(rx_role_suf, false)) {
+
+ switch (stage(state)) {
+ case 0:
+ change(state, to_normal, context(rx_role_suf, 1));
+ stream.match(/^`/);
+ token = 'meta';
+ break;
+ case 1:
+ change(state, to_normal, context(rx_role_suf, 2));
+ stream.match(rx_TEXT2);
+ token = 'string';
+ break;
+ case 2:
+ change(state, to_normal, context(rx_role_suf, 3));
+ stream.match(/^`:/);
+ token = 'meta';
+ break;
+ case 3:
+ change(state, to_normal, context(rx_role_suf, 4));
+ stream.match(rx_NAME);
+ token = 'keyword';
+ break;
+ case 4:
+ change(state, to_normal, context(rx_role_suf, 5));
+ stream.match(/^:/);
+ token = 'meta';
+ break;
+ case 5:
+ change(state, to_normal, context(rx_role_suf, 6));
+ stream.match(rx_TAIL);
+ break;
+ default:
+ change(state, to_normal);
+ }
+ } else if (phase(state) == rx_role || stream.match(rx_role, false)) {
+
+ switch (stage(state)) {
+ case 0:
+ change(state, to_normal, context(rx_role, 1));
+ stream.match(/^:/);
+ token = 'meta';
+ break;
+ case 1:
+ change(state, to_normal, context(rx_role, 2));
+ stream.match(rx_NAME);
+ token = 'keyword';
+ break;
+ case 2:
+ change(state, to_normal, context(rx_role, 3));
+ stream.match(/^:/);
+ token = 'meta';
+ break;
+ case 3:
+ change(state, to_normal, context(rx_role, 4));
+ stream.match(rx_TAIL);
+ break;
+ default:
+ change(state, to_normal);
+ }
+ } else if (phase(state) == rx_substitution_ref ||
+ stream.match(rx_substitution_ref, false)) {
+
+ switch (stage(state)) {
+ case 0:
+ change(state, to_normal, context(rx_substitution_ref, 1));
+ stream.match(rx_substitution_text);
+ token = 'variable-2';
+ break;
+ case 1:
+ change(state, to_normal, context(rx_substitution_ref, 2));
+ if (stream.match(/^_?_?/)) token = 'link';
+ break;
+ default:
+ change(state, to_normal);
+ }
+ } else if (stream.match(rx_footnote_ref)) {
+ change(state, to_normal);
+ token = 'quote';
+ } else if (stream.match(rx_citation_ref)) {
+ change(state, to_normal);
+ token = 'quote';
+ } else if (stream.match(rx_link_ref1)) {
+ change(state, to_normal);
+ if (!stream.peek() || stream.peek().match(/^\W$/)) {
+ token = 'link';
+ }
+ } else if (phase(state) == rx_link_ref2 ||
+ stream.match(rx_link_ref2, false)) {
+
+ switch (stage(state)) {
+ case 0:
+ if (!stream.peek() || stream.peek().match(/^\W$/)) {
+ change(state, to_normal, context(rx_link_ref2, 1));
+ } else {
+ stream.match(rx_link_ref2);
+ }
+ break;
+ case 1:
+ change(state, to_normal, context(rx_link_ref2, 2));
+ stream.match(/^`/);
+ token = 'link';
+ break;
+ case 2:
+ change(state, to_normal, context(rx_link_ref2, 3));
+ stream.match(rx_TEXT2);
+ break;
+ case 3:
+ change(state, to_normal, context(rx_link_ref2, 4));
+ stream.match(/^`_/);
+ token = 'link';
+ break;
+ default:
+ change(state, to_normal);
+ }
+ } else if (stream.match(rx_verbatim)) {
+ change(state, to_verbatim);
+ }
+
+ else {
+ if (stream.next()) change(state, to_normal);
+ }
+
+ return token;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////
+
+ function to_explicit(stream, state) {
+ var token = null;
+
+ if (phase(state) == rx_substitution ||
+ stream.match(rx_substitution, false)) {
+
+ switch (stage(state)) {
+ case 0:
+ change(state, to_explicit, context(rx_substitution, 1));
+ stream.match(rx_substitution_text);
+ token = 'variable-2';
+ break;
+ case 1:
+ change(state, to_explicit, context(rx_substitution, 2));
+ stream.match(rx_substitution_sepa);
+ break;
+ case 2:
+ change(state, to_explicit, context(rx_substitution, 3));
+ stream.match(rx_substitution_name);
+ token = 'keyword';
+ break;
+ case 3:
+ change(state, to_explicit, context(rx_substitution, 4));
+ stream.match(rx_substitution_tail);
+ token = 'meta';
+ break;
+ default:
+ change(state, to_normal);
+ }
+ } else if (phase(state) == rx_directive ||
+ stream.match(rx_directive, false)) {
+
+ switch (stage(state)) {
+ case 0:
+ change(state, to_explicit, context(rx_directive, 1));
+ stream.match(rx_directive_name);
+ token = 'keyword';
+
+ if (stream.current().match(/^(?:math|latex)/))
+ state.tmp_stex = true;
+ else if (stream.current().match(/^python/))
+ state.tmp_py = true;
+ break;
+ case 1:
+ change(state, to_explicit, context(rx_directive, 2));
+ stream.match(rx_directive_tail);
+ token = 'meta';
+
+ if (stream.match(/^latex\s*$/) || state.tmp_stex) {
+ state.tmp_stex = undefined; change(state, to_mode, {
+ mode: mode_stex, local: CodeMirror.startState(mode_stex)
+ });
+ }
+ break;
+ case 2:
+ change(state, to_explicit, context(rx_directive, 3));
+ if (stream.match(/^python\s*$/) || state.tmp_py) {
+ state.tmp_py = undefined; change(state, to_mode, {
+ mode: mode_python, local: CodeMirror.startState(mode_python)
+ });
+ }
+ break;
+ default:
+ change(state, to_normal);
+ }
+ } else if (phase(state) == rx_link || stream.match(rx_link, false)) {
+
+ switch (stage(state)) {
+ case 0:
+ change(state, to_explicit, context(rx_link, 1));
+ stream.match(rx_link_head);
+ stream.match(rx_link_name);
+ token = 'link';
+ break;
+ case 1:
+ change(state, to_explicit, context(rx_link, 2));
+ stream.match(rx_link_tail);
+ token = 'meta';
+ break;
+ default:
+ change(state, to_normal);
+ }
+ } else if (stream.match(rx_footnote)) {
+ change(state, to_normal);
+ token = 'quote';
+ } else if (stream.match(rx_citation)) {
+ change(state, to_normal);
+ token = 'quote';
+ }
+
+ else {
+ stream.eatSpace();
+ if (stream.eol()) {
+ change(state, to_normal);
+ } else {
+ stream.skipToEnd();
+ change(state, to_comment);
+ token = 'comment';
+ }
+ }
+
+ return token;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////
+
+ function to_comment(stream, state) {
+ return as_block(stream, state, 'comment');
+ }
+
+ function to_verbatim(stream, state) {
+ return as_block(stream, state, 'meta');
+ }
+
+ function as_block(stream, state, token) {
+ if (stream.eol() || stream.eatSpace()) {
+ stream.skipToEnd();
+ return token;
+ } else {
+ change(state, to_normal);
+ return null;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////
+
+ function to_mode(stream, state) {
+
+ if (state.ctx.mode && state.ctx.local) {
+
+ if (stream.sol()) {
+ if (!stream.eatSpace()) change(state, to_normal);
+ return null;
+ }
+
+ return state.ctx.mode.token(stream, state.ctx.local);
+ }
+
+ change(state, to_normal);
+ return null;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////
+
+ function context(phase, stage, mode, local) {
+ return {phase: phase, stage: stage, mode: mode, local: local};
+ }
+
+ function change(state, tok, ctx) {
+ state.tok = tok;
+ state.ctx = ctx || {};
+ }
+
+ function stage(state) {
+ return state.ctx.stage || 0;
+ }
+
+ function phase(state) {
+ return state.ctx.phase;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////
+
+ return {
+ startState: function () {
+ return {tok: to_normal, ctx: context(undefined, 0)};
+ },
+
+ copyState: function (state) {
+ var ctx = state.ctx, tmp = state.tmp;
+ if (ctx.local)
+ ctx = {mode: ctx.mode, local: CodeMirror.copyState(ctx.mode, ctx.local)};
+ if (tmp)
+ tmp = {mode: tmp.mode, local: CodeMirror.copyState(tmp.mode, tmp.local)};
+ return {tok: state.tok, ctx: ctx, tmp: tmp};
+ },
+
+ innerMode: function (state) {
+ return state.tmp ? {state: state.tmp.local, mode: state.tmp.mode}
+ : state.ctx.mode ? {state: state.ctx.local, mode: state.ctx.mode}
+ : null;
+ },
+
+ token: function (stream, state) {
+ return state.tok(stream, state);
+ }
+ };
}, 'python', 'stex');
///////////////////////////////////////////////////////////////////////////////
@@ -558,3 +553,5 @@ CodeMirror.defineMIME('text/x-rst', 'rst');
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
+
+});
diff --git a/applications/admin/static/codemirror/mode/sass/index.html b/applications/admin/static/codemirror/mode/sass/index.html
index 66d46778..9f4a7902 100644
--- a/applications/admin/static/codemirror/mode/sass/index.html
+++ b/applications/admin/static/codemirror/mode/sass/index.html
@@ -10,12 +10,12 @@
-
+
CodeMirror
Language modes
diff --git a/applications/admin/static/codemirror/mode/sass/sass.js b/applications/admin/static/codemirror/mode/sass/sass.js
index 9c9a0dae..52a66829 100644
--- a/applications/admin/static/codemirror/mode/sass/sass.js
+++ b/applications/admin/static/codemirror/mode/sass/sass.js
@@ -1,300 +1,379 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
CodeMirror.defineMode("sass", function(config) {
- var tokenRegexp = function(words){
+ function tokenRegexp(words) {
return new RegExp("^" + words.join("|"));
- };
+ }
var keywords = ["true", "false", "null", "auto"];
var keywordsRegexp = new RegExp("^" + keywords.join("|"));
- var operators = ["\\(", "\\)", "=", ">", "<", "==", ">=", "<=", "\\+", "-", "\\!=", "/", "\\*", "%", "and", "or", "not"];
+ var operators = ["\\(", "\\)", "=", ">", "<", "==", ">=", "<=", "\\+", "-",
+ "\\!=", "/", "\\*", "%", "and", "or", "not", ";","\\{","\\}",":"];
var opRegexp = tokenRegexp(operators);
- var pseudoElementsRegexp = /^::?[\w\-]+/;
+ var pseudoElementsRegexp = /^::?[a-zA-Z_][\w\-]*/;
- var urlTokens = function(stream, state){
+ function urlTokens(stream, state) {
var ch = stream.peek();
- if (ch === ")"){
+ if (ch === ")") {
stream.next();
state.tokenizer = tokenBase;
return "operator";
- }else if (ch === "("){
+ } else if (ch === "(") {
stream.next();
stream.eatSpace();
return "operator";
- }else if (ch === "'" || ch === '"'){
+ } else if (ch === "'" || ch === '"') {
state.tokenizer = buildStringTokenizer(stream.next());
return "string";
- }else{
+ } else {
state.tokenizer = buildStringTokenizer(")", false);
return "string";
}
- };
- var multilineComment = function(stream, state) {
- if (stream.skipTo("*/")){
- stream.next();
- stream.next();
- state.tokenizer = tokenBase;
- }else {
- stream.next();
- }
+ }
+ function comment(indentation, multiLine) {
+ return function(stream, state) {
+ if (stream.sol() && stream.indentation() <= indentation) {
+ state.tokenizer = tokenBase;
+ return tokenBase(stream, state);
+ }
- return "comment";
- };
+ if (multiLine && stream.skipTo("*/")) {
+ stream.next();
+ stream.next();
+ state.tokenizer = tokenBase;
+ } else {
+ stream.skipToEnd();
+ }
- var buildStringTokenizer = function(quote, greedy){
- if(greedy == null){ greedy = true; }
+ return "comment";
+ };
+ }
- function stringTokenizer(stream, state){
+ function buildStringTokenizer(quote, greedy) {
+ if (greedy == null) { greedy = true; }
+
+ function stringTokenizer(stream, state) {
var nextChar = stream.next();
var peekChar = stream.peek();
var previousChar = stream.string.charAt(stream.pos-2);
var endingString = ((nextChar !== "\\" && peekChar === quote) || (nextChar === quote && previousChar !== "\\"));
- /*
- console.log("previousChar: " + previousChar);
- console.log("nextChar: " + nextChar);
- console.log("peekChar: " + peekChar);
- console.log("ending: " + endingString);
- */
-
- if (endingString){
+ if (endingString) {
if (nextChar !== quote && greedy) { stream.next(); }
state.tokenizer = tokenBase;
return "string";
- }else if (nextChar === "#" && peekChar === "{"){
+ } else if (nextChar === "#" && peekChar === "{") {
state.tokenizer = buildInterpolationTokenizer(stringTokenizer);
stream.next();
return "operator";
- }else {
+ } else {
return "string";
}
}
return stringTokenizer;
- };
+ }
- var buildInterpolationTokenizer = function(currentTokenizer){
- return function(stream, state){
- if (stream.peek() === "}"){
+ function buildInterpolationTokenizer(currentTokenizer) {
+ return function(stream, state) {
+ if (stream.peek() === "}") {
stream.next();
state.tokenizer = currentTokenizer;
return "operator";
- }else{
+ } else {
return tokenBase(stream, state);
}
};
- };
+ }
- var indent = function(state){
- if (state.indentCount == 0){
+ function indent(state) {
+ if (state.indentCount == 0) {
state.indentCount++;
var lastScopeOffset = state.scopes[0].offset;
var currentOffset = lastScopeOffset + config.indentUnit;
state.scopes.unshift({ offset:currentOffset });
}
- };
+ }
- var dedent = function(state){
- if (state.scopes.length == 1) { return; }
+ function dedent(state) {
+ if (state.scopes.length == 1) return;
state.scopes.shift();
- };
+ }
- var tokenBase = function(stream, state) {
+ function tokenBase(stream, state) {
var ch = stream.peek();
- // Single line Comment
- if (stream.match('//')) {
- stream.skipToEnd();
- return "comment";
+ // Comment
+ if (stream.match("/*")) {
+ state.tokenizer = comment(stream.indentation(), true);
+ return state.tokenizer(stream, state);
}
-
- // Multiline Comment
- if (stream.match('/*')){
- state.tokenizer = multilineComment;
+ if (stream.match("//")) {
+ state.tokenizer = comment(stream.indentation(), false);
return state.tokenizer(stream, state);
}
// Interpolation
- if (stream.match('#{')){
- state.tokenizer = buildInterpolationTokenizer(tokenBase);
+ if (stream.match("#{")) {
+ state.tokenizer = buildInterpolationTokenizer(tokenBase);
return "operator";
}
- if (ch === "."){
- stream.next();
-
- // Match class selectors
- if (stream.match(/^[\w-]+/)){
- indent(state);
- return "atom";
- }else if (stream.peek() === "#"){
- indent(state);
- return "atom";
- }else{
- return "operator";
- }
- }
-
- if (ch === "#"){
- stream.next();
-
- // Hex numbers
- if (stream.match(/[0-9a-fA-F]{6}|[0-9a-fA-F]{3}/)){
- return "number";
- }
-
- // ID selectors
- if (stream.match(/^[\w-]+/)){
- indent(state);
- return "atom";
- }
-
- if (stream.peek() === "#"){
- indent(state);
- return "atom";
- }
- }
-
- // Numbers
- if (stream.match(/^-?[0-9\.]+/)){
- return "number";
- }
-
- // Units
- if (stream.match(/^(px|em|in)\b/)){
- return "unit";
- }
-
- if (stream.match(keywordsRegexp)){
- return "keyword";
- }
-
- if (stream.match(/^url/) && stream.peek() === "("){
- state.tokenizer = urlTokens;
- return "atom";
- }
-
- // Variables
- if (ch === "$"){
- stream.next();
- stream.eatWhile(/[\w-]/);
-
- if (stream.peek() === ":"){
- stream.next();
- return "variable-2";
- }else{
- return "variable-3";
- }
- }
-
- if (ch === "!"){
- stream.next();
-
- if (stream.match(/^[\w]+/)){
- return "keyword";
- }
-
- return "operator";
- }
-
- if (ch === "="){
- stream.next();
-
- // Match shortcut mixin definition
- if (stream.match(/^[\w-]+/)){
- indent(state);
- return "meta";
- }else {
- return "operator";
- }
- }
-
- if (ch === "+"){
- stream.next();
-
- // Match shortcut mixin definition
- if (stream.match(/^[\w-]+/)){
- return "variable-3";
- }else {
- return "operator";
- }
- }
-
- // Indent Directives
- if (stream.match(/^@(else if|if|media|else|for|each|while|mixin|function)/)){
- indent(state);
- return "meta";
- }
-
- // Other Directives
- if (ch === "@"){
- stream.next();
- stream.eatWhile(/[\w-]/);
- return "meta";
- }
-
// Strings
- if (ch === '"' || ch === "'"){
+ if (ch === '"' || ch === "'") {
stream.next();
state.tokenizer = buildStringTokenizer(ch);
return "string";
}
- // Pseudo element selectors
- if (ch == ':' && stream.match(pseudoElementsRegexp)){
- return "keyword";
- }
+ if(!state.cursorHalf){// state.cursorHalf === 0
+ // first half i.e. before : for key-value pairs
+ // including selectors
- // atoms
- if (stream.eatWhile(/[\w-&]/)){
- // matches a property definition
- if (stream.peek() === ":" && !stream.match(pseudoElementsRegexp, false))
- return "property";
- else
+ if (ch === ".") {
+ stream.next();
+ if (stream.match(/^[\w-]+/)) {
+ indent(state);
+ return "atom";
+ } else if (stream.peek() === "#") {
+ indent(state);
+ return "atom";
+ }
+ }
+
+ if (ch === "#") {
+ stream.next();
+ // ID selectors
+ if (stream.match(/^[\w-]+/)) {
+ indent(state);
+ return "atom";
+ }
+ if (stream.peek() === "#") {
+ indent(state);
+ return "atom";
+ }
+ }
+
+ // Variables
+ if (ch === "$") {
+ stream.next();
+ stream.eatWhile(/[\w-]/);
+ return "variable-2";
+ }
+
+ // Numbers
+ if (stream.match(/^-?[0-9\.]+/))
+ return "number";
+
+ // Units
+ if (stream.match(/^(px|em|in)\b/))
+ return "unit";
+
+ if (stream.match(keywordsRegexp))
+ return "keyword";
+
+ if (stream.match(/^url/) && stream.peek() === "(") {
+ state.tokenizer = urlTokens;
return "atom";
- }
+ }
- if (stream.match(opRegexp)){
+ if (ch === "=") {
+ // Match shortcut mixin definition
+ if (stream.match(/^=[\w-]+/)) {
+ indent(state);
+ return "meta";
+ }
+ }
+
+ if (ch === "+") {
+ // Match shortcut mixin definition
+ if (stream.match(/^\+[\w-]+/)){
+ return "variable-3";
+ }
+ }
+
+ if(ch === "@"){
+ if(stream.match(/@extend/)){
+ if(!stream.match(/\s*[\w]/))
+ dedent(state);
+ }
+ }
+
+
+ // Indent Directives
+ if (stream.match(/^@(else if|if|media|else|for|each|while|mixin|function)/)) {
+ indent(state);
+ return "meta";
+ }
+
+ // Other Directives
+ if (ch === "@") {
+ stream.next();
+ stream.eatWhile(/[\w-]/);
+ return "meta";
+ }
+
+ if (stream.eatWhile(/[\w-]/)){
+ if(stream.match(/ *: *[\w-\+\$#!\("']/,false)){
+ return "propery";
+ }
+ else if(stream.match(/ *:/,false)){
+ indent(state);
+ state.cursorHalf = 1;
+ return "atom";
+ }
+ else if(stream.match(/ *,/,false)){
+ return "atom";
+ }
+ else{
+ indent(state);
+ return "atom";
+ }
+ }
+
+ if(ch === ":"){
+ if (stream.match(pseudoElementsRegexp)){ // could be a pseudo-element
+ return "keyword";
+ }
+ stream.next();
+ state.cursorHalf=1;
+ return "operator";
+ }
+
+ } // cursorHalf===0 ends here
+ else{
+
+ if (ch === "#") {
+ stream.next();
+ // Hex numbers
+ if (stream.match(/[0-9a-fA-F]{6}|[0-9a-fA-F]{3}/)){
+ if(!stream.peek()){
+ state.cursorHalf = 0;
+ }
+ return "number";
+ }
+ }
+
+ // Numbers
+ if (stream.match(/^-?[0-9\.]+/)){
+ if(!stream.peek()){
+ state.cursorHalf = 0;
+ }
+ return "number";
+ }
+
+ // Units
+ if (stream.match(/^(px|em|in)\b/)){
+ if(!stream.peek()){
+ state.cursorHalf = 0;
+ }
+ return "unit";
+ }
+
+ if (stream.match(keywordsRegexp)){
+ if(!stream.peek()){
+ state.cursorHalf = 0;
+ }
+ return "keyword";
+ }
+
+ if (stream.match(/^url/) && stream.peek() === "(") {
+ state.tokenizer = urlTokens;
+ if(!stream.peek()){
+ state.cursorHalf = 0;
+ }
+ return "atom";
+ }
+
+ // Variables
+ if (ch === "$") {
+ stream.next();
+ stream.eatWhile(/[\w-]/);
+ if(!stream.peek()){
+ state.cursorHalf = 0;
+ }
+ return "variable-3";
+ }
+
+ // bang character for !important, !default, etc.
+ if (ch === "!") {
+ stream.next();
+ if(!stream.peek()){
+ state.cursorHalf = 0;
+ }
+ return stream.match(/^[\w]+/) ? "keyword": "operator";
+ }
+
+ if (stream.match(opRegexp)){
+ if(!stream.peek()){
+ state.cursorHalf = 0;
+ }
+ return "operator";
+ }
+
+ // attributes
+ if (stream.eatWhile(/[\w-]/)) {
+ if(!stream.peek()){
+ state.cursorHalf = 0;
+ }
+ return "attribute";
+ }
+
+ //stream.eatSpace();
+ if(!stream.peek()){
+ state.cursorHalf = 0;
+ return null;
+ }
+
+ } // else ends here
+
+ if (stream.match(opRegexp))
return "operator";
- }
// If we haven't returned by now, we move 1 character
// and return an error
stream.next();
return null;
- };
+ }
- var tokenLexer = function(stream, state) {
- if (stream.sol()){
- state.indentCount = 0;
- }
+ function tokenLexer(stream, state) {
+ if (stream.sol()) state.indentCount = 0;
var style = state.tokenizer(stream, state);
var current = stream.current();
- if (current === "@return"){
+ if (current === "@return" || current === "}"){
dedent(state);
}
- if (style === "atom"){
- indent(state);
- }
-
- if (style !== null){
+ if (style !== null) {
var startOfToken = stream.pos - current.length;
+
var withCurrentIndent = startOfToken + (config.indentUnit * state.indentCount);
var newScopes = [];
- for (var i = 0; i < state.scopes.length; i++){
+ for (var i = 0; i < state.scopes.length; i++) {
var scope = state.scopes[i];
- if (scope.offset <= withCurrentIndent){
+ if (scope.offset <= withCurrentIndent)
newScopes.push(scope);
- }
}
state.scopes = newScopes;
@@ -302,13 +381,16 @@ CodeMirror.defineMode("sass", function(config) {
return style;
- };
+ }
return {
startState: function() {
return {
tokenizer: tokenBase,
- scopes: [{offset: 0, type: 'sass'}],
+ scopes: [{offset: 0, type: "sass"}],
+ indentCount: 0,
+ cursorHalf: 0, // cursor half tells us if cursor lies after (1)
+ // or before (0) colon (well... more or less)
definedVars: [],
definedMixins: []
};
@@ -328,3 +410,5 @@ CodeMirror.defineMode("sass", function(config) {
});
CodeMirror.defineMIME("text/x-sass", "sass");
+
+});
diff --git a/applications/admin/static/codemirror/mode/shell/index.html b/applications/admin/static/codemirror/mode/shell/index.html
index cf415e83..0b56300b 100644
--- a/applications/admin/static/codemirror/mode/shell/index.html
+++ b/applications/admin/static/codemirror/mode/shell/index.html
@@ -12,12 +12,12 @@
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
-
+
CodeMirror
Language modes
diff --git a/applications/admin/static/codemirror/mode/shell/shell.js b/applications/admin/static/codemirror/mode/shell/shell.js
index abfd2144..a684e8c2 100644
--- a/applications/admin/static/codemirror/mode/shell/shell.js
+++ b/applications/admin/static/codemirror/mode/shell/shell.js
@@ -1,3 +1,16 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
CodeMirror.defineMode('shell', function() {
var words = {};
@@ -23,10 +36,15 @@ CodeMirror.defineMode('shell', function() {
'touch vi vim wall wc wget who write yes zsh');
function tokenBase(stream, state) {
+ if (stream.eatSpace()) return null;
var sol = stream.sol();
var ch = stream.next();
+ if (ch === '\\') {
+ stream.next();
+ return null;
+ }
if (ch === '\'' || ch === '"' || ch === '`') {
state.tokens.unshift(tokenString(ch));
return tokenize(stream, state);
@@ -53,7 +71,7 @@ CodeMirror.defineMode('shell', function() {
}
if (/\d/.test(ch)) {
stream.eatWhile(/\d/);
- if(!/\w/.test(stream.peek())) {
+ if(stream.eol() || !/\w/.test(stream.peek())) {
return 'number';
}
}
@@ -109,10 +127,13 @@ CodeMirror.defineMode('shell', function() {
return {
startState: function() {return {tokens:[]};},
token: function(stream, state) {
- if (stream.eatSpace()) return null;
return tokenize(stream, state);
- }
+ },
+ lineComment: '#',
+ fold: "brace"
};
});
CodeMirror.defineMIME('text/x-sh', 'shell');
+
+});
diff --git a/applications/admin/static/codemirror/mode/shell/test.js b/applications/admin/static/codemirror/mode/shell/test.js
new file mode 100644
index 00000000..a413b5a4
--- /dev/null
+++ b/applications/admin/static/codemirror/mode/shell/test.js
@@ -0,0 +1,58 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function() {
+ var mode = CodeMirror.getMode({}, "shell");
+ function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
+
+ MT("var",
+ "text [def $var] text");
+ MT("varBraces",
+ "text[def ${var}]text");
+ MT("varVar",
+ "text [def $a$b] text");
+ MT("varBracesVarBraces",
+ "text[def ${a}${b}]text");
+
+ MT("singleQuotedVar",
+ "[string 'text $var text']");
+ MT("singleQuotedVarBraces",
+ "[string 'text ${var} text']");
+
+ MT("doubleQuotedVar",
+ '[string "text ][def $var][string text"]');
+ MT("doubleQuotedVarBraces",
+ '[string "text][def ${var}][string text"]');
+ MT("doubleQuotedVarPunct",
+ '[string "text ][def $@][string text"]');
+ MT("doubleQuotedVarVar",
+ '[string "][def $a$b][string "]');
+ MT("doubleQuotedVarBracesVarBraces",
+ '[string "][def ${a}${b}][string "]');
+
+ MT("notAString",
+ "text\\'text");
+ MT("escapes",
+ "outside\\'\\\"\\`\\\\[string \"inside\\`\\'\\\"\\\\`\\$notAVar\"]outside\\$\\(notASubShell\\)");
+
+ MT("subshell",
+ "[builtin echo] [quote $(whoami)] s log, stardate [quote `date`].");
+ MT("doubleQuotedSubshell",
+ "[builtin echo] [string \"][quote $(whoami)][string 's log, stardate `date`.\"]");
+
+ MT("hashbang",
+ "[meta #!/bin/bash]");
+ MT("comment",
+ "text [comment # Blurb]");
+
+ MT("numbers",
+ "[number 0] [number 1] [number 2]");
+ MT("keywords",
+ "[keyword while] [atom true]; [keyword do]",
+ " [builtin sleep] [number 3]",
+ "[keyword done]");
+ MT("options",
+ "[builtin ls] [attribute -l] [attribute --human-readable]");
+ MT("operator",
+ "[def var][operator =]value");
+})();
diff --git a/applications/admin/static/codemirror/mode/sparql/index.html b/applications/admin/static/codemirror/mode/sparql/index.html
index 7c41e17b..84ef4d36 100644
--- a/applications/admin/static/codemirror/mode/sparql/index.html
+++ b/applications/admin/static/codemirror/mode/sparql/index.html
@@ -10,12 +10,12 @@