Code Snippets - Version 1.7

Version Description

  • Improved plugin API
  • Fixed a bug with saving snippets per page option (#)
  • Updated CodeMirror to version 3.11
  • Allow plugin to be activated on individual sites on multisite (#)
  • Slimmed down the description visual editor
  • Added icon for the new MP6 admin UI (#)
  • Strip PHP tags from the beginning and end of a snippet in case someone forgets
  • Changed to MIT license
  • Removed HTML, CSS and JavaScript CodeMirror modes that were messing things up
  • Made everything leaner, faster, and better
Download this release

Release Info

Developer bungeshea
Plugin Icon Code Snippets
Version 1.7
Comparing to
See all releases

Code changes from version 1.6.1 to 1.7

assets/admin-single.css ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Custom styling for the single snippet admin page
3
+ *
4
+ * @package Code Snippets
5
+ * @subpackage Assets
6
+ */
7
+
8
+ h3 {
9
+ /* Provide some decent space between the fields and titles */
10
+ margin: 1em 0;
11
+ }
12
+
13
+ label {
14
+ /* What's with the pointer mouse on labels? */
15
+ cursor: auto;
16
+ }
17
+
18
+ /**
19
+ * Customise the CodeMirror editor
20
+ * to look pretty and support auto-resizing
21
+ */
22
+ .CodeMirror {
23
+ height: auto;
24
+ min-height: 300px;
25
+ border: 1px solid #eee;
26
+ -moz-border-radius: 3px; -webkit-border-radius: 3px; -o-border-radius: 3px; border-radius: 3px;
27
+ }
28
+
29
+ .CodeMirror-scroll {
30
+ overflow-y: hidden;
31
+ overflow-x: auto;
32
+ }
33
+
34
+ .CodeMirror-sizer {
35
+ min-height: 300px !important;
36
+ }
37
+
38
+ /**
39
+ * Tweak things for the MP6 interface
40
+ */
41
+ .admin-color-mp6 .CodeMirror {
42
+ width: 100%;
43
+ background-color: #fff;
44
+ border-color: #dfdfdf;
45
+ }
46
+
47
+ .admin-color-mp6 .CodeMirror * {
48
+ /* Override Open Sans font */
49
+ font-family: monospace !important;
50
+ }
assets/{util → codemirror/addon}/matchbrackets.js RENAMED
@@ -1,11 +1,18 @@
1
  (function() {
 
 
 
 
 
 
 
2
  var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
3
  function findMatchingBracket(cm) {
4
  var cur = cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1;
5
  var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
6
  if (!match) return null;
7
  var forward = match.charAt(1) == ">", d = forward ? 1 : -1;
8
- var style = cm.getTokenAt({line: cur.line, ch: pos + 1}).type;
9
 
10
  var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
11
  function scan(line, lineNo, start) {
@@ -14,7 +21,7 @@
14
  if (start != null) pos = start + d;
15
  for (; pos != end; pos += d) {
16
  var ch = line.text.charAt(pos);
17
- if (re.test(ch) && cm.getTokenAt({line: lineNo, ch: pos + 1}).type == style) {
18
  var match = matching[ch];
19
  if (match.charAt(1) == ">" == forward) stack.push(ch);
20
  else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
@@ -27,17 +34,21 @@
27
  else found = scan(cm.getLineHandle(i), i);
28
  if (found) break;
29
  }
30
- return {from: {line: cur.line, ch: pos}, to: found && {line: i, ch: found.pos}, match: found && found.match};
31
  }
32
 
33
  function matchBrackets(cm, autoclear) {
34
  var found = findMatchingBracket(cm);
35
- if (!found) return;
 
 
 
36
  var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
37
- var one = cm.markText(found.from, {line: found.from.line, ch: found.from.ch + 1},
38
- {className: style});
39
- var two = found.to && cm.markText(found.to, {line: found.to.line, ch: found.to.ch + 1},
40
- {className: style});
 
41
  var clear = function() {
42
  cm.operation(function() { one.clear(); two && two.clear(); });
43
  };
1
  (function() {
2
+ var ie_lt8 = /MSIE \d/.test(navigator.userAgent) &&
3
+ (document.documentMode == null || document.documentMode < 8);
4
+
5
+ var Pos = CodeMirror.Pos;
6
+ // Disable brace matching in long lines, since it'll cause hugely slow updates
7
+ var maxLineLen = 1000;
8
+
9
  var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
10
  function findMatchingBracket(cm) {
11
  var cur = cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1;
12
  var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
13
  if (!match) return null;
14
  var forward = match.charAt(1) == ">", d = forward ? 1 : -1;
15
+ var style = cm.getTokenAt(Pos(cur.line, pos + 1)).type;
16
 
17
  var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
18
  function scan(line, lineNo, start) {
21
  if (start != null) pos = start + d;
22
  for (; pos != end; pos += d) {
23
  var ch = line.text.charAt(pos);
24
+ if (re.test(ch) && cm.getTokenAt(Pos(lineNo, pos + 1)).type == style) {
25
  var match = matching[ch];
26
  if (match.charAt(1) == ">" == forward) stack.push(ch);
27
  else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
34
  else found = scan(cm.getLineHandle(i), i);
35
  if (found) break;
36
  }
37
+ return {from: Pos(cur.line, pos), to: found && Pos(i, found.pos), match: found && found.match};
38
  }
39
 
40
  function matchBrackets(cm, autoclear) {
41
  var found = findMatchingBracket(cm);
42
+ if (!found || cm.getLine(found.from.line).length > maxLineLen ||
43
+ found.to && cm.getLine(found.to.line).length > maxLineLen)
44
+ return;
45
+
46
  var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
47
+ var one = cm.markText(found.from, Pos(found.from.line, found.from.ch + 1), {className: style});
48
+ var two = found.to && cm.markText(found.to, Pos(found.to.line, found.to.ch + 1), {className: style});
49
+ // Kludge to work around the IE bug from issue #1193, where text
50
+ // input stops going to the textare whever this fires.
51
+ if (ie_lt8 && cm.state.focused) cm.display.input.focus();
52
  var clear = function() {
53
  cm.operation(function() { one.clear(); two && two.clear(); });
54
  };
assets/{util → codemirror/addon}/search.js RENAMED
@@ -7,9 +7,24 @@
7
  // Ctrl-G.
8
 
9
  (function() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  function SearchState() {
11
  this.posFrom = this.posTo = this.query = null;
12
- this.marked = [];
13
  }
14
  function getSearchState(cm) {
15
  return cm._searchState || (cm._searchState = new SearchState());
@@ -39,11 +54,9 @@
39
  cm.operation(function() {
40
  if (!query || state.query) return;
41
  state.query = parseQuery(query);
42
- if (cm.lineCount() < 2000) { // This is too expensive on big documents.
43
- for (var cursor = getSearchCursor(cm, state.query); cursor.findNext();)
44
- state.marked.push(cm.markText(cursor.from(), cursor.to(),
45
- {className: "CodeMirror-searching"}));
46
- }
47
  state.posFrom = state.posTo = cm.getCursor();
48
  findNext(cm, rev);
49
  });
@@ -53,7 +66,7 @@
53
  var state = getSearchState(cm);
54
  var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
55
  if (!cursor.find(rev)) {
56
- cursor = getSearchCursor(cm, state.query, rev ? {line: cm.lineCount() - 1} : {line: 0, ch: 0});
57
  if (!cursor.find(rev)) return;
58
  }
59
  cm.setSelection(cursor.from(), cursor.to());
@@ -63,8 +76,7 @@
63
  var state = getSearchState(cm);
64
  if (!state.query) return;
65
  state.query = null;
66
- for (var i = 0; i < state.marked.length; ++i) state.marked[i].clear();
67
- state.marked.length = 0;
68
  });}
69
 
70
  var replaceQueryDialog =
@@ -88,7 +100,7 @@
88
  } else {
89
  clearSearch(cm);
90
  var cursor = getSearchCursor(cm, query, cm.getCursor());
91
- function advance() {
92
  var start = cursor.from(), match;
93
  if (!(match = cursor.findNext())) {
94
  cursor = getSearchCursor(cm, query);
@@ -98,12 +110,12 @@
98
  cm.setSelection(cursor.from(), cursor.to());
99
  confirmDialog(cm, doReplaceConfirm, "Replace?",
100
  [function() {doReplace(match);}, advance]);
101
- }
102
- function doReplace(match) {
103
  cursor.replace(typeof query == "string" ? text :
104
  text.replace(/\$(\d)/, function(_, i) {return match[i];}));
105
  advance();
106
- }
107
  advance();
108
  }
109
  });
7
  // Ctrl-G.
8
 
9
  (function() {
10
+ function searchOverlay(query) {
11
+ if (typeof query == "string") return {token: function(stream) {
12
+ if (stream.match(query)) return "searching";
13
+ stream.next();
14
+ stream.skipTo(query.charAt(0)) || stream.skipToEnd();
15
+ }};
16
+ return {token: function(stream) {
17
+ if (stream.match(query)) return "searching";
18
+ while (!stream.eol()) {
19
+ stream.next();
20
+ if (stream.match(query, false)) break;
21
+ }
22
+ }};
23
+ }
24
+
25
  function SearchState() {
26
  this.posFrom = this.posTo = this.query = null;
27
+ this.overlay = null;
28
  }
29
  function getSearchState(cm) {
30
  return cm._searchState || (cm._searchState = new SearchState());
54
  cm.operation(function() {
55
  if (!query || state.query) return;
56
  state.query = parseQuery(query);
57
+ cm.removeOverlay(state.overlay);
58
+ state.overlay = searchOverlay(query);
59
+ cm.addOverlay(state.overlay);
 
 
60
  state.posFrom = state.posTo = cm.getCursor();
61
  findNext(cm, rev);
62
  });
66
  var state = getSearchState(cm);
67
  var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
68
  if (!cursor.find(rev)) {
69
+ cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));
70
  if (!cursor.find(rev)) return;
71
  }
72
  cm.setSelection(cursor.from(), cursor.to());
76
  var state = getSearchState(cm);
77
  if (!state.query) return;
78
  state.query = null;
79
+ cm.removeOverlay(state.overlay);
 
80
  });}
81
 
82
  var replaceQueryDialog =
100
  } else {
101
  clearSearch(cm);
102
  var cursor = getSearchCursor(cm, query, cm.getCursor());
103
+ var advance = function() {
104
  var start = cursor.from(), match;
105
  if (!(match = cursor.findNext())) {
106
  cursor = getSearchCursor(cm, query);
110
  cm.setSelection(cursor.from(), cursor.to());
111
  confirmDialog(cm, doReplaceConfirm, "Replace?",
112
  [function() {doReplace(match);}, advance]);
113
+ };
114
+ var doReplace = function(match) {
115
  cursor.replace(typeof query == "string" ? text :
116
  text.replace(/\$(\d)/, function(_, i) {return match[i];}));
117
  advance();
118
+ };
119
  advance();
120
  }
121
  });
assets/{util → codemirror/addon}/searchcursor.js RENAMED
@@ -1,9 +1,11 @@
1
  (function(){
 
 
2
  function SearchCursor(cm, query, pos, caseFold) {
3
  this.atOccurrence = false; this.cm = cm;
4
  if (caseFold == null && typeof query == "string") caseFold = false;
5
 
6
- pos = pos ? cm.clipPos(pos) : {line: 0, ch: 0};
7
  this.pos = {from: pos, to: pos};
8
 
9
  // The matches method is filled in based on the type of query.
@@ -15,24 +17,23 @@
15
  this.matches = function(reverse, pos) {
16
  if (reverse) {
17
  query.lastIndex = 0;
18
- var line = cm.getLine(pos.line).slice(0, pos.ch), match = query.exec(line), start = 0;
19
- while (match) {
20
- start += match.index + 1;
21
- line = line.slice(start);
22
- query.lastIndex = 0;
23
- var newmatch = query.exec(line);
24
- if (newmatch) match = newmatch;
25
- else break;
26
  }
27
- start--;
28
  } else {
29
  query.lastIndex = pos.ch;
30
  var line = cm.getLine(pos.line), match = query.exec(line),
31
  start = match && match.index;
32
  }
33
- if (match)
34
- return {from: {line: pos.line, ch: start},
35
- to: {line: pos.line, ch: start + match[0].length},
36
  match: match};
37
  };
38
  } else { // String query
@@ -40,15 +41,21 @@
40
  var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;};
41
  var target = query.split("\n");
42
  // Different methods for single-line and multi-line queries
43
- if (target.length == 1)
44
- this.matches = function(reverse, pos) {
45
- var line = fold(cm.getLine(pos.line)), len = query.length, match;
46
- if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1)
47
- : (match = line.indexOf(query, pos.ch)) != -1)
48
- return {from: {line: pos.line, ch: match},
49
- to: {line: pos.line, ch: match + len}};
50
- };
51
- else
 
 
 
 
 
 
52
  this.matches = function(reverse, pos) {
53
  var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(cm.getLine(ln));
54
  var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
@@ -66,10 +73,11 @@
66
  var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length);
67
  if (reverse ? offsetB != line.length - match.length : offsetB != match.length)
68
  return;
69
- var start = {line: pos.line, ch: offsetA}, end = {line: ln, ch: offsetB};
70
  return {from: reverse ? end : start, to: reverse ? start : end};
71
  }
72
  };
 
73
  }
74
  }
75
 
@@ -80,7 +88,7 @@
80
  find: function(reverse) {
81
  var self = this, pos = this.cm.clipPos(reverse ? this.pos.from : this.pos.to);
82
  function savePosAndFail(line) {
83
- var pos = {line: line, ch: 0};
84
  self.pos = {from: pos, to: pos};
85
  self.atOccurrence = false;
86
  return false;
@@ -88,17 +96,18 @@
88
 
89
  for (;;) {
90
  if (this.pos = this.matches(reverse, pos)) {
 
91
  this.atOccurrence = true;
92
  return this.pos.match || true;
93
  }
94
  if (reverse) {
95
  if (!pos.line) return savePosAndFail(0);
96
- pos = {line: pos.line-1, ch: this.cm.getLine(pos.line-1).length};
97
  }
98
  else {
99
  var maxLine = this.cm.lineCount();
100
  if (pos.line == maxLine - 1) return savePosAndFail(maxLine);
101
- pos = {line: pos.line+1, ch: 0};
102
  }
103
  }
104
  },
@@ -107,9 +116,11 @@
107
  to: function() {if (this.atOccurrence) return this.pos.to;},
108
 
109
  replace: function(newText) {
110
- var self = this;
111
- if (this.atOccurrence)
112
- self.pos.to = this.cm.replaceRange(newText, self.pos.from, self.pos.to);
 
 
113
  }
114
  };
115
 
1
  (function(){
2
+ var Pos = CodeMirror.Pos;
3
+
4
  function SearchCursor(cm, query, pos, caseFold) {
5
  this.atOccurrence = false; this.cm = cm;
6
  if (caseFold == null && typeof query == "string") caseFold = false;
7
 
8
+ pos = pos ? cm.clipPos(pos) : Pos(0, 0);
9
  this.pos = {from: pos, to: pos};
10
 
11
  // The matches method is filled in based on the type of query.
17
  this.matches = function(reverse, pos) {
18
  if (reverse) {
19
  query.lastIndex = 0;
20
+ var line = cm.getLine(pos.line).slice(0, pos.ch), cutOff = 0, match, start;
21
+ for (;;) {
22
+ query.lastIndex = cutOff;
23
+ var newMatch = query.exec(line);
24
+ if (!newMatch) break;
25
+ match = newMatch;
26
+ start = match.index;
27
+ cutOff = match.index + 1;
28
  }
 
29
  } else {
30
  query.lastIndex = pos.ch;
31
  var line = cm.getLine(pos.line), match = query.exec(line),
32
  start = match && match.index;
33
  }
34
+ if (match && match[0])
35
+ return {from: Pos(pos.line, start),
36
+ to: Pos(pos.line, start + match[0].length),
37
  match: match};
38
  };
39
  } else { // String query
41
  var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;};
42
  var target = query.split("\n");
43
  // Different methods for single-line and multi-line queries
44
+ if (target.length == 1) {
45
+ if (!query.length) {
46
+ // Empty string would match anything and never progress, so
47
+ // we define it to match nothing instead.
48
+ this.matches = function() {};
49
+ } else {
50
+ this.matches = function(reverse, pos) {
51
+ var line = fold(cm.getLine(pos.line)), len = query.length, match;
52
+ if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1)
53
+ : (match = line.indexOf(query, pos.ch)) != -1)
54
+ return {from: Pos(pos.line, match),
55
+ to: Pos(pos.line, match + len)};
56
+ };
57
+ }
58
+ } else {
59
  this.matches = function(reverse, pos) {
60
  var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(cm.getLine(ln));
61
  var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
73
  var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length);
74
  if (reverse ? offsetB != line.length - match.length : offsetB != match.length)
75
  return;
76
+ var start = Pos(pos.line, offsetA), end = Pos(ln, offsetB);
77
  return {from: reverse ? end : start, to: reverse ? start : end};
78
  }
79
  };
80
+ }
81
  }
82
  }
83
 
88
  find: function(reverse) {
89
  var self = this, pos = this.cm.clipPos(reverse ? this.pos.from : this.pos.to);
90
  function savePosAndFail(line) {
91
+ var pos = Pos(line, 0);
92
  self.pos = {from: pos, to: pos};
93
  self.atOccurrence = false;
94
  return false;
96
 
97
  for (;;) {
98
  if (this.pos = this.matches(reverse, pos)) {
99
+ if (!this.pos.from || !this.pos.to) { console.log(this.matches, this.pos); }
100
  this.atOccurrence = true;
101
  return this.pos.match || true;
102
  }
103
  if (reverse) {
104
  if (!pos.line) return savePosAndFail(0);
105
+ pos = Pos(pos.line-1, this.cm.getLine(pos.line-1).length);
106
  }
107
  else {
108
  var maxLine = this.cm.lineCount();
109
  if (pos.line == maxLine - 1) return savePosAndFail(maxLine);
110
+ pos = Pos(pos.line + 1, 0);
111
  }
112
  }
113
  },
116
  to: function() {if (this.atOccurrence) return this.pos.to;},
117
 
118
  replace: function(newText) {
119
+ if (!this.atOccurrence) return;
120
+ var lines = CodeMirror.splitLines(newText);
121
+ this.cm.replaceRange(lines, this.pos.from, this.pos.to);
122
+ this.pos.to = Pos(this.pos.from.line + lines.length - 1,
123
+ lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0));
124
  }
125
  };
126
 
assets/{codemirror.css → codemirror/lib/codemirror.css} RENAMED
@@ -3,19 +3,11 @@
3
  .CodeMirror {
4
  /* Set height, width, borders, and global font properties here */
5
  font-family: monospace;
6
- height: auto;
7
- min-height: 300px;
8
- border: 1px solid #eee;
9
- -moz-border-radius: 3px; -webkit-border-radius: 3px; -o-border-radius: 3px; border-radius: 3px;
10
  }
11
  .CodeMirror-scroll {
12
  /* Set scrolling behaviour here */
13
- overflow-y: hidden;
14
- overflow-x: auto;
15
- }
16
-
17
- .CodeMirror-sizer {
18
- min-height: 300px !important;
19
  }
20
 
21
  /* PADDING */
@@ -47,26 +39,24 @@
47
 
48
  /* CURSOR */
49
 
50
- .CodeMirror pre.CodeMirror-cursor {
51
  border-left: 1px solid black;
 
52
  }
53
  /* Shown when moving in bi-directional text */
54
- .CodeMirror pre.CodeMirror-secondarycursor {
55
  border-left: 1px solid silver;
56
  }
57
- .cm-keymap-fat-cursor pre.CodeMirror-cursor {
58
  width: auto;
59
  border: 0;
60
- background: transparent;
61
- background: rgba(0, 200, 0, .4);
62
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#6600c800, endColorstr=#4c00c800);
63
- }
64
- /* Kludge to turn off filter in ie9+, which also accepts rgba */
65
- .cm-keymap-fat-cursor pre.CodeMirror-cursor:not(#nonsense_id) {
66
- filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
67
  }
68
  /* Can style cursor different in overwrite (non-insert) mode */
69
- .CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {}
 
 
70
 
71
  /* DEFAULT THEME */
72
 
@@ -98,7 +88,6 @@
98
  .cm-positive {color: #292;}
99
  .cm-header, .cm-strong {font-weight: bold;}
100
  .cm-em {font-style: italic;}
101
- .cm-emstrong {font-style: italic; font-weight: bold;}
102
  .cm-link {text-decoration: underline;}
103
 
104
  .cm-invalidchar {color: #f00;}
@@ -106,11 +95,6 @@
106
  div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
107
  div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
108
 
109
- .activeline { background: #e8f2ff !important; }
110
-
111
- span.CodeMirror-matchhighlight { background: #e9e9e9 }
112
- .CodeMirror-focused span.CodeMirror-matchhighlight { background: #e7e4ff; !important }
113
-
114
  /* STOP */
115
 
116
  /* The rest of this file contains styles related to the mechanics of
@@ -120,6 +104,8 @@ span.CodeMirror-matchhighlight { background: #e9e9e9 }
120
  line-height: 1;
121
  position: relative;
122
  overflow: hidden;
 
 
123
  }
124
 
125
  .CodeMirror-scroll {
@@ -161,10 +147,13 @@ span.CodeMirror-matchhighlight { background: #e9e9e9 }
161
  .CodeMirror-gutters {
162
  position: absolute; left: 0; top: 0;
163
  height: 100%;
 
164
  z-index: 3;
165
  }
166
  .CodeMirror-gutter {
167
  height: 100%;
 
 
168
  display: inline-block;
169
  /* Hack to make IE7 behave */
170
  *zoom:1;
@@ -181,7 +170,7 @@ span.CodeMirror-matchhighlight { background: #e9e9e9 }
181
  }
182
  .CodeMirror pre {
183
  /* Reset some styles that the rest of the page might have set */
184
- -moz-border-radius: 0; -webkit-border-radius: 0; -o-border-radius: 0; border-radius: 0;
185
  border-width: 0;
186
  background: transparent;
187
  font-family: inherit;
@@ -209,6 +198,11 @@ span.CodeMirror-matchhighlight { background: #e9e9e9 }
209
  .CodeMirror-linewidget {
210
  position: relative;
211
  z-index: 2;
 
 
 
 
 
212
  }
213
 
214
  .CodeMirror-wrap .CodeMirror-scroll {
@@ -223,20 +217,20 @@ span.CodeMirror-matchhighlight { background: #e9e9e9 }
223
  }
224
  .CodeMirror-measure pre { position: static; }
225
 
226
- .CodeMirror pre.CodeMirror-cursor {
227
  position: absolute;
228
  visibility: hidden;
229
  border-right: none;
230
  width: 0;
231
  }
232
- .CodeMirror-focused pre.CodeMirror-cursor {
233
  visibility: visible;
234
  }
235
 
236
  .CodeMirror-selected { background: #d9d9d9; }
237
  .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
238
 
239
- .CodeMirror-searching {
240
  background: #ffa;
241
  background: rgba(255, 255, 0, .4);
242
  }
@@ -246,7 +240,7 @@ span.CodeMirror-matchhighlight { background: #e9e9e9 }
246
 
247
  @media print {
248
  /* Hide the cursor when printing */
249
- .CodeMirror pre.CodeMirror-cursor {
250
  visibility: hidden;
251
  }
252
  }
3
  .CodeMirror {
4
  /* Set height, width, borders, and global font properties here */
5
  font-family: monospace;
6
+ height: 300px;
 
 
 
7
  }
8
  .CodeMirror-scroll {
9
  /* Set scrolling behaviour here */
10
+ overflow: auto;
 
 
 
 
 
11
  }
12
 
13
  /* PADDING */
39
 
40
  /* CURSOR */
41
 
42
+ .CodeMirror div.CodeMirror-cursor {
43
  border-left: 1px solid black;
44
+ z-index: 3;
45
  }
46
  /* Shown when moving in bi-directional text */
47
+ .CodeMirror div.CodeMirror-secondarycursor {
48
  border-left: 1px solid silver;
49
  }
50
+ .CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor {
51
  width: auto;
52
  border: 0;
53
+ background: #7e7;
54
+ z-index: 1;
 
 
 
 
 
55
  }
56
  /* Can style cursor different in overwrite (non-insert) mode */
57
+ .CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {}
58
+
59
+ .cm-tab { display: inline-block; }
60
 
61
  /* DEFAULT THEME */
62
 
88
  .cm-positive {color: #292;}
89
  .cm-header, .cm-strong {font-weight: bold;}
90
  .cm-em {font-style: italic;}
 
91
  .cm-link {text-decoration: underline;}
92
 
93
  .cm-invalidchar {color: #f00;}
95
  div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
96
  div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
97
 
 
 
 
 
 
98
  /* STOP */
99
 
100
  /* The rest of this file contains styles related to the mechanics of
104
  line-height: 1;
105
  position: relative;
106
  overflow: hidden;
107
+ background: white;
108
+ color: black;
109
  }
110
 
111
  .CodeMirror-scroll {
147
  .CodeMirror-gutters {
148
  position: absolute; left: 0; top: 0;
149
  height: 100%;
150
+ padding-bottom: 30px;
151
  z-index: 3;
152
  }
153
  .CodeMirror-gutter {
154
  height: 100%;
155
+ padding-bottom: 30px;
156
+ margin-bottom: -32px;
157
  display: inline-block;
158
  /* Hack to make IE7 behave */
159
  *zoom:1;
170
  }
171
  .CodeMirror pre {
172
  /* Reset some styles that the rest of the page might have set */
173
+ -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
174
  border-width: 0;
175
  background: transparent;
176
  font-family: inherit;
198
  .CodeMirror-linewidget {
199
  position: relative;
200
  z-index: 2;
201
+ overflow: auto;
202
+ }
203
+
204
+ .CodeMirror-widget {
205
+ display: inline-block;
206
  }
207
 
208
  .CodeMirror-wrap .CodeMirror-scroll {
217
  }
218
  .CodeMirror-measure pre { position: static; }
219
 
220
+ .CodeMirror div.CodeMirror-cursor {
221
  position: absolute;
222
  visibility: hidden;
223
  border-right: none;
224
  width: 0;
225
  }
226
+ .CodeMirror-focused div.CodeMirror-cursor {
227
  visibility: visible;
228
  }
229
 
230
  .CodeMirror-selected { background: #d9d9d9; }
231
  .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
232
 
233
+ .cm-searching {
234
  background: #ffa;
235
  background: rgba(255, 255, 0, .4);
236
  }
240
 
241
  @media print {
242
  /* Hide the cursor when printing */
243
+ .CodeMirror div.CodeMirror-cursor {
244
  visibility: hidden;
245
  }
246
  }
assets/{codemirror.js → codemirror/lib/codemirror.js} RENAMED
@@ -1,4 +1,4 @@
1
- // CodeMirror version 3.0
2
  //
3
  // CodeMirror is the only global var we claim
4
  window.CodeMirror = (function() {
@@ -10,8 +10,8 @@ window.CodeMirror = (function() {
10
  // bugs and behavior differences.
11
  var gecko = /gecko\/\d/i.test(navigator.userAgent);
12
  var ie = /MSIE \d/.test(navigator.userAgent);
13
- var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent);
14
- var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent);
15
  var webkit = /WebKit\//.test(navigator.userAgent);
16
  var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
17
  var chrome = /Chrome\//.test(navigator.userAgent);
@@ -24,8 +24,15 @@ window.CodeMirror = (function() {
24
 
25
  var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
26
  // This is woefully incomplete. Suggestions for alternative methods welcome.
27
- var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|IEMobile/i.test(navigator.userAgent);
28
  var mac = ios || /Mac/.test(navigator.platform);
 
 
 
 
 
 
 
29
 
30
  // Optimize some code when these features are not used
31
  var sawReadOnlySpans = false, sawCollapsedSpans = false;
@@ -34,31 +41,38 @@ window.CodeMirror = (function() {
34
 
35
  function CodeMirror(place, options) {
36
  if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
37
-
38
  this.options = options = options || {};
39
  // Determine effective options based on given values and defaults.
40
  for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt))
41
  options[opt] = defaults[opt];
42
  setGuttersForLineNumbers(options);
43
 
44
- var display = this.display = makeDisplay(place);
 
45
  display.wrapper.CodeMirror = this;
46
  updateGutters(this);
47
  if (options.autofocus && !mobile) focusInput(this);
48
 
49
- this.view = makeView(new BranchChunk([new LeafChunk([makeLine("", null, textHeight(display))])]));
50
- this.nextOpId = 0;
51
- loadMode(this);
 
 
 
 
 
52
  themeChanged(this);
53
  if (options.lineWrapping)
54
  this.display.wrapper.className += " CodeMirror-wrap";
55
 
56
- // Initialize the content.
57
- this.setValue(options.value || "");
 
 
58
  // Override magic textarea content restore that IE sometimes does
59
  // on our hidden textarea on reload
60
  if (ie) setTimeout(bind(resetInput, this, true), 20);
61
- this.view.history = makeHistory();
62
 
63
  registerEventHandlers(this);
64
  // IE throws unspecified error in certain cases, when
@@ -77,10 +91,16 @@ window.CodeMirror = (function() {
77
 
78
  // DISPLAY CONSTRUCTOR
79
 
80
- function makeDisplay(place) {
81
  var d = {};
 
82
  var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;");
83
- input.setAttribute("wrap", "off"); input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off");
 
 
 
 
 
84
  // Wraps and hides input textarea
85
  d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
86
  // The actual fake scrollbars.
@@ -91,9 +111,9 @@ window.CodeMirror = (function() {
91
  d.lineDiv = elt("div");
92
  d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
93
  // Blinky cursor, and element used to ensure cursor fits at the end of a line
94
- d.cursor = elt("pre", "\u00a0", "CodeMirror-cursor");
95
  // Secondary cursor, shown when on a 'jump' in bi-directional text
96
- d.otherCursor = elt("pre", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor");
97
  // Used to measure text size
98
  d.measure = elt("div", null, "CodeMirror-measure");
99
  // Wraps everything that needs to exist inside the vertically-padded coordinate system
@@ -104,7 +124,7 @@ window.CodeMirror = (function() {
104
  // Set to the height of the text, causes scrolling
105
  d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
106
  // D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
107
- d.heightForcer = elt("div", "\u00a0", null, "position: absolute; height: " + scrollerCutOff + "px");
108
  // Will contain the gutters, if any
109
  d.gutters = elt("div", null, "CodeMirror-gutters");
110
  d.lineGutter = null;
@@ -129,7 +149,8 @@ window.CodeMirror = (function() {
129
  else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = "18px";
130
 
131
  // Current visible range (may be bigger than the view window).
132
- d.viewOffset = d.showingFrom = d.showingTo = d.lastSizeC = 0;
 
133
 
134
  // Used to only resize the line number gutter when necessary (when
135
  // the amount of lines crosses a boundary that makes its width change)
@@ -156,35 +177,16 @@ window.CodeMirror = (function() {
156
  // string instead of the (large) selection.
157
  d.inaccurateSelection = false;
158
 
159
- // Used to adjust overwrite behaviour when a paste has been
160
- // detected
161
- d.pasteIncoming = false;
162
-
163
- return d;
164
- }
165
 
166
- // VIEW CONSTRUCTOR
 
167
 
168
- function makeView(doc) {
169
- var selPos = {line: 0, ch: 0};
170
- return {
171
- doc: doc,
172
- // frontier is the point up to which the content has been parsed,
173
- frontier: 0, highlight: new Delayed(),
174
- sel: {from: selPos, to: selPos, head: selPos, anchor: selPos, shift: false, extend: false},
175
- scrollTop: 0, scrollLeft: 0,
176
- overwrite: false, focused: false,
177
- // Tracks the maximum line length so that
178
- // the horizontal scrollbar can be kept
179
- // static when scrolling.
180
- maxLine: getLine(doc, 0),
181
- maxLineLength: 0,
182
- maxLineChanged: false,
183
- suppressEdits: false,
184
- goalColumn: null,
185
- cantEdit: false,
186
- keyMaps: []
187
- };
188
  }
189
 
190
  // STATE UPDATES
@@ -192,34 +194,50 @@ window.CodeMirror = (function() {
192
  // Used to get the editor into a consistent state again when options change.
193
 
194
  function loadMode(cm) {
195
- var doc = cm.view.doc;
196
- cm.view.mode = CodeMirror.getMode(cm.options, cm.options.mode);
197
- doc.iter(0, doc.size, function(line) { line.stateAfter = null; });
198
- cm.view.frontier = 0;
 
 
199
  startWorker(cm, 100);
 
 
200
  }
201
 
202
  function wrappingChanged(cm) {
203
- var doc = cm.view.doc, th = textHeight(cm.display);
204
  if (cm.options.lineWrapping) {
205
  cm.display.wrapper.className += " CodeMirror-wrap";
206
- var perLine = cm.display.scroller.clientWidth / charWidth(cm.display) - 3;
207
- doc.iter(0, doc.size, function(line) {
208
- if (line.height == 0) return;
209
- var guess = Math.ceil(line.text.length / perLine) || 1;
210
- if (guess != 1) updateLineHeight(line, guess * th);
211
- });
212
  cm.display.sizer.style.minWidth = "";
213
  } else {
214
  cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", "");
215
- computeMaxLength(cm.view);
216
- doc.iter(0, doc.size, function(line) {
217
- if (line.height != 0) updateLineHeight(line, th);
218
- });
219
  }
220
- regChange(cm, 0, doc.size);
 
221
  clearCaches(cm);
222
- setTimeout(function(){updateScrollbars(cm.display, cm.view.doc.height);}, 100);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  }
224
 
225
  function keyMapChanged(cm) {
@@ -236,7 +254,7 @@ window.CodeMirror = (function() {
236
 
237
  function guttersChanged(cm) {
238
  updateGutters(cm);
239
- updateDisplay(cm, true);
240
  }
241
 
242
  function updateGutters(cm) {
@@ -271,15 +289,16 @@ window.CodeMirror = (function() {
271
  return len;
272
  }
273
 
274
- function computeMaxLength(view) {
275
- view.maxLine = getLine(view.doc, 0);
276
- view.maxLineLength = lineLength(view.doc, view.maxLine);
277
- view.maxLineChanged = true;
278
- view.doc.iter(1, view.doc.size, function(line) {
279
- var len = lineLength(view.doc, line);
280
- if (len > view.maxLineLength) {
281
- view.maxLineLength = len;
282
- view.maxLine = line;
 
283
  }
284
  });
285
  }
@@ -303,7 +322,7 @@ window.CodeMirror = (function() {
303
  // Re-synchronize the fake scrollbars with the actual size of the
304
  // content. Optionally force a scrollTop.
305
  function updateScrollbars(d /* display */, docHeight) {
306
- var totalHeight = docHeight + 2 * paddingTop(d);
307
  d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
308
  var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
309
  var needsH = d.scroller.scrollWidth > d.scroller.clientWidth;
@@ -311,7 +330,7 @@ window.CodeMirror = (function() {
311
  if (needsV) {
312
  d.scrollbarV.style.display = "block";
313
  d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
314
- d.scrollbarV.firstChild.style.height =
315
  (scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px";
316
  } else d.scrollbarV.style.display = "";
317
  if (needsH) {
@@ -342,18 +361,19 @@ window.CodeMirror = (function() {
342
 
343
  function alignHorizontally(cm) {
344
  var display = cm.display;
345
- if (!display.alignWidgets && !display.gutters.firstChild) return;
346
- var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.view.scrollLeft;
347
  var gutterW = display.gutters.offsetWidth, l = comp + "px";
348
  for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) {
349
  for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l;
350
  }
351
- display.gutters.style.left = (comp + gutterW) + "px";
 
352
  }
353
 
354
  function maybeUpdateLineNumberWidth(cm) {
355
  if (!cm.options.lineNumbers) return false;
356
- var doc = cm.view.doc, last = lineNumberFor(cm.options, doc.size - 1), display = cm.display;
357
  if (last.length != display.lineNumChars) {
358
  var test = display.measure.appendChild(elt("div", [elt("div", last)],
359
  "CodeMirror-linenumber CodeMirror-gutter-elt"));
@@ -372,7 +392,7 @@ window.CodeMirror = (function() {
372
  return String(options.lineNumberFormatter(i + options.firstLineNumber));
373
  }
374
  function compensateForHScroll(display) {
375
- return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left;
376
  }
377
 
378
  // DISPLAY DRAWING
@@ -381,12 +401,12 @@ window.CodeMirror = (function() {
381
  var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo;
382
  var updated = updateDisplayInner(cm, changes, viewPort);
383
  if (updated) {
384
- signalLater(cm, cm, "update", cm);
385
  if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
386
- signalLater(cm, cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
387
  }
388
  updateSelection(cm);
389
- updateScrollbars(cm.display, cm.view.doc.height);
390
 
391
  return updated;
392
  }
@@ -395,9 +415,10 @@ window.CodeMirror = (function() {
395
  // determine which DOM updates have to be made, and makes the
396
  // updates.
397
  function updateDisplayInner(cm, changes, viewPort) {
398
- var display = cm.display, doc = cm.view.doc;
399
  if (!display.wrapper.clientWidth) {
400
- display.showingFrom = display.showingTo = display.viewOffset = 0;
 
401
  return;
402
  }
403
 
@@ -406,45 +427,50 @@ window.CodeMirror = (function() {
406
  // to render instead of the current scrollbar position.
407
  var visible = visibleLines(display, doc, viewPort);
408
  // Bail out if the visible area is already rendered and nothing changed.
409
- if (changes !== true && changes.length == 0 &&
410
  visible.from > display.showingFrom && visible.to < display.showingTo)
411
  return;
412
 
413
- if (changes && maybeUpdateLineNumberWidth(cm))
414
- changes = true;
415
- display.sizer.style.marginLeft = display.scrollbarH.style.left = display.gutters.offsetWidth + "px";
416
-
417
- // When merged lines are present, the line that needs to be
418
- // redrawn might not be the one that was changed.
419
- if (changes !== true && sawCollapsedSpans)
420
- for (var i = 0; i < changes.length; ++i) {
421
- var ch = changes[i], merged;
422
- while (merged = collapsedSpanAtStart(getLine(doc, ch.from))) {
423
- var from = merged.find().from.line;
424
- if (ch.diff) ch.diff -= ch.from - from;
425
- ch.from = from;
426
- }
427
- }
428
 
429
  // Used to determine which lines need their line numbers updated
430
- var positionsChangedFrom = changes === true ? 0 : Infinity;
431
- if (cm.options.lineNumbers && changes && changes !== true)
432
  for (var i = 0; i < changes.length; ++i)
433
  if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; }
434
 
435
- var from = Math.max(visible.from - cm.options.viewportMargin, 0);
436
- var to = Math.min(doc.size, visible.to + cm.options.viewportMargin);
437
- if (display.showingFrom < from && from - display.showingFrom < 20) from = display.showingFrom;
438
- if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(doc.size, display.showingTo);
 
439
  if (sawCollapsedSpans) {
440
  from = lineNo(visualLine(doc, getLine(doc, from)));
441
- while (to < doc.size && lineIsHidden(getLine(doc, to))) ++to;
442
  }
443
 
444
  // Create a range of theoretically intact lines, and punch holes
445
  // in that using the change info.
446
- var intact = changes === true ? [] :
447
- computeIntact([{from: display.showingFrom, to: display.showingTo}], changes);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
448
  // Clip off the parts that won't be visible
449
  var intactLines = 0;
450
  for (var i = 0; i < intact.length; ++i) {
@@ -454,13 +480,17 @@ window.CodeMirror = (function() {
454
  if (range.from >= range.to) intact.splice(i--, 1);
455
  else intactLines += range.to - range.from;
456
  }
457
- if (intactLines == to - from && from == display.showingFrom && to == display.showingTo)
 
458
  return;
 
459
  intact.sort(function(a, b) {return a.from - b.from;});
460
 
 
461
  if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
462
  patchDisplay(cm, from, to, intact, positionsChangedFrom);
463
  display.lineDiv.style.display = "";
 
464
 
465
  var different = from != display.showingFrom || to != display.showingTo ||
466
  display.lastSizeC != display.wrapper.clientHeight;
@@ -477,20 +507,31 @@ window.CodeMirror = (function() {
477
  height = bot - prevBottom;
478
  prevBottom = bot;
479
  } else {
480
- var box = node.getBoundingClientRect();
481
  height = box.bottom - box.top;
482
  }
483
  var diff = node.lineObj.height - height;
484
  if (height < 2) height = textHeight(display);
485
- if (diff > .001 || diff < -.001)
486
  updateLineHeight(node.lineObj, height);
 
 
 
 
487
  }
488
- display.viewOffset = heightAtLine(cm, getLine(doc, from));
489
- // Position the mover div to align with the current virtual scroll position
490
- display.mover.style.top = display.viewOffset + "px";
 
491
  return true;
492
  }
493
 
 
 
 
 
 
 
494
  function computeIntact(intact, changes) {
495
  for (var i = 0, l = changes.length || 0; i < l; ++i) {
496
  var change = changes[i], intact2 = [], diff = change.diff || 0;
@@ -528,9 +569,7 @@ window.CodeMirror = (function() {
528
  function patchDisplay(cm, from, to, intact, updateNumbersFrom) {
529
  var dims = getDimensions(cm);
530
  var display = cm.display, lineNumbers = cm.options.lineNumbers;
531
- // IE does bad things to nodes when .innerHTML = "" is used on a parent
532
- // we still need widgets and markers intact to add back to the new content later
533
- if (!intact.length && !ie && (!webkit || !cm.display.currentWheelTarget))
534
  removeChildren(display.lineDiv);
535
  var container = display.lineDiv, cur = container.firstChild;
536
 
@@ -540,50 +579,106 @@ window.CodeMirror = (function() {
540
  node.style.display = "none";
541
  node.lineObj = null;
542
  } else {
543
- container.removeChild(node);
544
  }
545
  return next;
546
  }
547
 
548
- var nextIntact = intact.shift(), lineNo = from;
549
- cm.view.doc.iter(from, to, function(line) {
550
- if (nextIntact && nextIntact.to == lineNo) nextIntact = intact.shift();
551
- if (lineIsHidden(line)) {
552
  if (line.height != 0) updateLineHeight(line, 0);
553
- } else if (nextIntact && nextIntact.from <= lineNo && nextIntact.to > lineNo) {
 
 
 
 
 
 
 
 
 
 
 
 
554
  // This line is intact. Skip to the actual node. Update its
555
  // line number if needed.
556
  while (cur.lineObj != line) cur = rm(cur);
557
- if (lineNumbers && updateNumbersFrom <= lineNo && cur.lineNumber)
558
- setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineNo));
559
  cur = cur.nextSibling;
560
  } else {
 
 
 
 
 
561
  // This line needs to be generated.
562
- var lineNode = buildLineElement(cm, line, lineNo, dims);
563
- container.insertBefore(lineNode, cur);
 
 
 
 
 
 
564
  lineNode.lineObj = line;
565
  }
566
- ++lineNo;
567
  });
568
  while (cur) cur = rm(cur);
569
  }
570
 
571
- function buildLineElement(cm, line, lineNo, dims) {
572
  var lineElement = lineContent(cm, line);
573
- var markers = line.gutterMarkers, display = cm.display;
574
-
575
- if (!cm.options.lineNumbers && !markers && !line.bgClass && !line.wrapClass &&
576
- (!line.widgets || !line.widgets.length)) return lineElement;
577
-
578
- // Lines with gutter elements or a background class need
579
- // to be wrapped again, and have the extra elements added
580
- // to the wrapper div
581
-
582
- var wrap = elt("div", null, line.wrapClass, "position: relative");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
583
  if (cm.options.lineNumbers || markers) {
584
- var gutterWrap = wrap.appendChild(elt("div", null, null, "position: absolute; left: " +
585
- dims.fixedPos + "px"));
586
- wrap.alignable = [gutterWrap];
 
587
  if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
588
  wrap.lineNumber = gutterWrap.appendChild(
589
  elt("div", lineNumberFor(cm.options, lineNo),
@@ -598,44 +693,42 @@ window.CodeMirror = (function() {
598
  dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
599
  }
600
  }
601
- // Kludge to make sure the styled element lies behind the selection (by z-index)
602
- if (line.bgClass)
603
- wrap.appendChild(elt("div", "\u00a0", line.bgClass + " CodeMirror-linebackground"));
604
- wrap.appendChild(lineElement);
605
- if (line.widgets)
606
- for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
607
- var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
608
- node.widget = widget;
609
- if (widget.noHScroll) {
610
- (wrap.alignable || (wrap.alignable = [])).push(node);
611
- var width = dims.wrapperWidth;
612
- node.style.left = dims.fixedPos + "px";
613
- if (!widget.coverGutter) {
614
- width -= dims.gutterTotalWidth;
615
- node.style.paddingLeft = dims.gutterTotalWidth + "px";
616
- }
617
- node.style.width = width + "px";
618
- }
619
- if (widget.coverGutter) {
620
- node.style.zIndex = 5;
621
- node.style.position = "relative";
622
- if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
623
- }
624
- if (widget.above)
625
- wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
626
- else
627
- wrap.appendChild(node);
628
- }
629
-
630
  if (ie_lt8) wrap.style.zIndex = 2;
 
 
 
 
 
 
 
 
 
631
  return wrap;
632
  }
633
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
634
  // SELECTION / CURSOR
635
 
636
  function updateSelection(cm) {
637
  var display = cm.display;
638
- var collapsed = posEq(cm.view.sel.from, cm.view.sel.to);
639
  if (collapsed || cm.options.showCursorWhenSelecting)
640
  updateSelectionCursor(cm);
641
  else
@@ -646,8 +739,8 @@ window.CodeMirror = (function() {
646
  display.selectionDiv.style.display = "none";
647
 
648
  // Move the hidden textarea near the cursor to prevent scrolling artifacts
649
- var headPos = cursorCoords(cm, cm.view.sel.head, "div");
650
- var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
651
  display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
652
  headPos.top + lineOff.top - wrapOff.top)) + "px";
653
  display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
@@ -656,7 +749,7 @@ window.CodeMirror = (function() {
656
 
657
  // No selection, plain cursor
658
  function updateSelectionCursor(cm) {
659
- var display = cm.display, pos = cursorCoords(cm, cm.view.sel.head, "div");
660
  display.cursor.style.left = pos.left + "px";
661
  display.cursor.style.top = pos.top + "px";
662
  display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
@@ -672,7 +765,7 @@ window.CodeMirror = (function() {
672
 
673
  // Highlight selection
674
  function updateSelectionRange(cm) {
675
- var display = cm.display, doc = cm.view.doc, sel = cm.view.sel;
676
  var fragment = document.createDocumentFragment();
677
  var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display);
678
 
@@ -687,7 +780,7 @@ window.CodeMirror = (function() {
687
  var lineObj = getLine(doc, line);
688
  var lineLen = lineObj.text.length, rVal = retTop ? Infinity : -Infinity;
689
  function coords(ch) {
690
- return charCoords(cm, {line: line, ch: ch}, "div", lineObj);
691
  }
692
 
693
  iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
@@ -765,28 +858,33 @@ window.CodeMirror = (function() {
765
  // HIGHLIGHT WORKER
766
 
767
  function startWorker(cm, time) {
768
- if (cm.view.frontier < cm.display.showingTo)
769
- cm.view.highlight.set(time, bind(highlightWorker, cm));
770
  }
771
 
772
  function highlightWorker(cm) {
773
- var view = cm.view, doc = view.doc;
774
- if (view.frontier >= cm.display.showingTo) return;
 
775
  var end = +new Date + cm.options.workTime;
776
- var state = copyState(view.mode, getStateBefore(cm, view.frontier));
777
  var changed = [], prevChange;
778
- doc.iter(view.frontier, Math.min(doc.size, cm.display.showingTo + 500), function(line) {
779
- if (view.frontier >= cm.display.showingFrom) { // Visible
780
- if (highlightLine(cm, line, state) && view.frontier >= cm.display.showingFrom) {
781
- if (prevChange && prevChange.end == view.frontier) prevChange.end++;
782
- else changed.push(prevChange = {start: view.frontier, end: view.frontier + 1});
 
 
 
 
783
  }
784
- line.stateAfter = copyState(view.mode, state);
785
  } else {
786
  processLine(cm, line, state);
787
- line.stateAfter = view.frontier % 5 == 0 ? copyState(view.mode, state) : null;
788
  }
789
- ++view.frontier;
790
  if (+new Date > end) {
791
  startWorker(cm, cm.options.workDelay);
792
  return true;
@@ -805,10 +903,10 @@ window.CodeMirror = (function() {
805
  // smallest indentation, which tends to need the least context to
806
  // parse correctly.
807
  function findStartLine(cm, n) {
808
- var minindent, minline, doc = cm.view.doc;
809
  for (var search = n, lim = n - 100; search > lim; --search) {
810
- if (search == 0) return 0;
811
- var line = getLine(doc, search-1);
812
  if (line.stateAfter) return search;
813
  var indented = countColumn(line.text, null, cm.options.tabSize);
814
  if (minline == null || minindent > indented) {
@@ -820,29 +918,33 @@ window.CodeMirror = (function() {
820
  }
821
 
822
  function getStateBefore(cm, n) {
823
- var view = cm.view;
824
- var pos = findStartLine(cm, n), state = pos && getLine(view.doc, pos-1).stateAfter;
825
- if (!state) state = startState(view.mode);
826
- else state = copyState(view.mode, state);
827
- view.doc.iter(pos, n, function(line) {
 
828
  processLine(cm, line, state);
829
- var save = pos == n - 1 || pos % 5 == 0 || pos >= view.showingFrom && pos < view.showingTo;
830
- line.stateAfter = save ? copyState(view.mode, state) : null;
831
  ++pos;
832
  });
833
  return state;
834
  }
835
 
836
  // POSITION MEASUREMENT
837
-
838
  function paddingTop(display) {return display.lineSpace.offsetTop;}
 
839
  function paddingLeft(display) {
840
- var e = removeChildrenAndAdd(display.measure, elt("pre")).appendChild(elt("span", "x"));
841
  return e.offsetLeft;
842
  }
843
 
844
  function measureChar(cm, line, ch, data) {
845
- var data = data || measureLine(cm, line), dir = -1;
 
 
846
  for (var pos = ch;; pos += dir) {
847
  var r = data[pos];
848
  if (r) break;
@@ -853,22 +955,30 @@ window.CodeMirror = (function() {
853
  top: r.top, bottom: r.bottom};
854
  }
855
 
856
- function measureLine(cm, line) {
857
- // First look in the cache
858
- var display = cm.display, cache = cm.display.measureLineCache;
859
  for (var i = 0; i < cache.length; ++i) {
860
  var memo = cache[i];
861
  if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
862
- display.scroller.clientWidth == memo.width)
 
863
  return memo.measure;
864
  }
865
-
866
- var measure = measureLineInner(cm, line);
867
- // Store result in the cache
868
- var memo = {text: line.text, width: display.scroller.clientWidth,
869
- markedSpans: line.markedSpans, measure: measure};
870
- if (cache.length == 16) cache[++display.measureLineCachePos % 16] = memo;
871
- else cache.push(memo);
 
 
 
 
 
 
 
 
872
  return measure;
873
  }
874
 
@@ -903,10 +1013,15 @@ window.CodeMirror = (function() {
903
 
904
  removeChildrenAndAdd(display.measure, pre);
905
 
906
- var outer = display.lineDiv.getBoundingClientRect();
907
  var vranges = [], data = emptyArray(line.text.length), maxBot = pre.offsetHeight;
 
 
 
 
 
908
  for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
909
- var size = cur.getBoundingClientRect();
910
  var top = Math.max(0, size.top - outer.top), bot = Math.min(size.bottom - outer.top, maxBot);
911
  for (var j = 0; j < vranges.length; j += 2) {
912
  var rtop = vranges[j], rbot = vranges[j+1];
@@ -920,25 +1035,44 @@ window.CodeMirror = (function() {
920
  }
921
  }
922
  if (j == vranges.length) vranges.push(top, bot);
923
- data[i] = {left: size.left - outer.left, right: size.right - outer.left, top: j};
 
 
924
  }
925
  for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
926
  var vr = cur.top;
927
  cur.top = vranges[vr]; cur.bottom = vranges[vr+1];
928
  }
 
929
  return data;
930
  }
931
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
932
  function clearCaches(cm) {
933
  cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
934
  cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
935
- cm.view.maxLineChanged = true;
 
936
  }
937
 
938
  // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
939
  function intoCoordSystem(cm, lineObj, rect, context) {
940
  if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
941
- var size = lineObj.widgets[i].node.offsetHeight;
942
  rect.top += size; rect.bottom += size;
943
  }
944
  if (context == "line") return rect;
@@ -946,7 +1080,7 @@ window.CodeMirror = (function() {
946
  var yOff = heightAtLine(cm, lineObj);
947
  if (context != "local") yOff -= cm.display.viewOffset;
948
  if (context == "page") {
949
- var lOff = cm.display.lineSpace.getBoundingClientRect();
950
  yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop);
951
  var xOff = lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft);
952
  rect.left += xOff; rect.right += xOff;
@@ -955,13 +1089,33 @@ window.CodeMirror = (function() {
955
  return rect;
956
  }
957
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
958
  function charCoords(cm, pos, context, lineObj) {
959
- if (!lineObj) lineObj = getLine(cm.view.doc, pos.line);
960
  return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context);
961
  }
962
 
963
  function cursorCoords(cm, pos, context, lineObj, measurement) {
964
- lineObj = lineObj || getLine(cm.view.doc, pos.line);
965
  if (!measurement) measurement = measureLine(cm, lineObj);
966
  function get(ch, right) {
967
  var m = measureChar(cm, lineObj, ch, measurement);
@@ -976,10 +1130,9 @@ window.CodeMirror = (function() {
976
  if (part.from < ch && part.to > ch) return get(ch, rtl);
977
  var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to;
978
  if (left == ch) {
979
- // Opera and IE return bogus offsets and widths for edges
980
- // where the direction flips, but only for the side with the
981
- // lower level. So we try to use the side with the higher
982
- // level.
983
  if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true);
984
  else here = get(rtl && part.from != part.to ? ch - 1 : ch);
985
  if (rtl == linedir) main = here; else other = here;
@@ -997,21 +1150,29 @@ window.CodeMirror = (function() {
997
  return main;
998
  }
999
 
 
 
 
 
 
 
1000
  // Coords must be lineSpace-local
1001
  function coordsChar(cm, x, y) {
1002
- var doc = cm.view.doc;
1003
  y += cm.display.viewOffset;
1004
- if (y < 0) return {line: 0, ch: 0, outside: true};
1005
- var lineNo = lineAtHeight(doc, y);
1006
- if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc, doc.size - 1).text.length};
 
1007
  if (x < 0) x = 0;
1008
 
1009
  for (;;) {
1010
  var lineObj = getLine(doc, lineNo);
1011
  var found = coordsCharInner(cm, lineObj, lineNo, x, y);
1012
  var merged = collapsedSpanAtEnd(lineObj);
1013
- if (merged && found.ch == lineRight(lineObj))
1014
- lineNo = merged.find().to.line;
 
1015
  else
1016
  return found;
1017
  }
@@ -1019,30 +1180,32 @@ window.CodeMirror = (function() {
1019
 
1020
  function coordsCharInner(cm, lineObj, lineNo, x, y) {
1021
  var innerOff = y - heightAtLine(cm, lineObj);
1022
- var wrongLine = false, cWidth = cm.display.wrapper.clientWidth;
1023
  var measurement = measureLine(cm, lineObj);
1024
 
1025
  function getX(ch) {
1026
- var sp = cursorCoords(cm, {line: lineNo, ch: ch}, "line",
1027
  lineObj, measurement);
1028
  wrongLine = true;
1029
- if (innerOff > sp.bottom) return Math.max(0, sp.left - cWidth);
1030
- else if (innerOff < sp.top) return sp.left + cWidth;
1031
  else wrongLine = false;
1032
  return sp.left;
1033
  }
1034
 
1035
  var bidi = getOrder(lineObj), dist = lineObj.text.length;
1036
  var from = lineLeft(lineObj), to = lineRight(lineObj);
1037
- var fromX = paddingLeft(cm.display), toX = getX(to);
1038
 
1039
- if (x > toX) return {line: lineNo, ch: to, outside: wrongLine};
1040
  // Do a binary search between these bounds.
1041
  for (;;) {
1042
  if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
1043
  var after = x - fromX < toX - x, ch = after ? from : to;
1044
  while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
1045
- return {line: lineNo, ch: ch, after: after, outside: wrongLine};
 
 
1046
  }
1047
  var step = Math.ceil(dist / 2), middle = from + step;
1048
  if (bidi) {
@@ -1050,8 +1213,8 @@ window.CodeMirror = (function() {
1050
  for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
1051
  }
1052
  var middleX = getX(middle);
1053
- if (middleX > x) {to = middle; toX = middleX; if (wrongLine) toX += 1000; dist -= step;}
1054
- else {from = middle; fromX = middleX; dist = step;}
1055
  }
1056
  }
1057
 
@@ -1092,78 +1255,117 @@ window.CodeMirror = (function() {
1092
  // be awkward, slow, and error-prone), but instead updates are
1093
  // batched and then all combined and executed at once.
1094
 
 
1095
  function startOperation(cm) {
1096
- if (cm.curOp) ++cm.curOp.depth;
1097
- else cm.curOp = {
1098
- // Nested operations delay update until the outermost one
1099
- // finishes.
1100
- depth: 1,
1101
  // An array of ranges of lines that have to be updated. See
1102
  // updateDisplay.
1103
  changes: [],
1104
- delayedCallbacks: [],
1105
  updateInput: null,
1106
  userSelChange: null,
1107
  textChanged: null,
1108
  selectionChanged: false,
1109
  updateMaxLine: false,
1110
- id: ++cm.nextOpId
 
1111
  };
 
1112
  }
1113
 
1114
  function endOperation(cm) {
1115
- var op = cm.curOp;
1116
- if (--op.depth) return;
1117
  cm.curOp = null;
1118
- var view = cm.view, display = cm.display;
1119
- if (op.updateMaxLine) computeMaxLength(view);
1120
- if (view.maxLineChanged && !cm.options.lineWrapping) {
1121
- var width = measureChar(cm, view.maxLine, view.maxLine.text.length).right;
1122
- display.sizer.style.minWidth = (width + 3 + scrollerCutOff) + "px";
1123
- view.maxLineChanged = false;
 
 
 
1124
  }
1125
  var newScrollPos, updated;
1126
- if (op.selectionChanged) {
1127
- var coords = cursorCoords(cm, view.sel.head);
 
 
1128
  newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
1129
  }
1130
- if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null)
1131
  updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop);
 
 
1132
  if (!updated && op.selectionChanged) updateSelection(cm);
1133
- if (newScrollPos) scrollCursorIntoView(cm);
 
 
 
 
 
 
1134
  if (op.selectionChanged) restartBlink(cm);
1135
 
1136
- if (view.focused && op.updateInput)
1137
  resetInput(cm, op.userSelChange);
1138
 
 
 
 
 
 
 
 
 
 
 
 
1139
  if (op.textChanged)
1140
  signal(cm, "change", cm, op.textChanged);
1141
  if (op.selectionChanged) signal(cm, "cursorActivity", cm);
1142
- for (var i = 0; i < op.delayedCallbacks.length; ++i) op.delayedCallbacks[i](cm);
1143
  }
1144
 
1145
  // Wraps a function in an operation. Returns the wrapped function.
1146
  function operation(cm1, f) {
1147
  return function() {
1148
- var cm = cm1 || this;
1149
- startOperation(cm);
1150
- try {var result = f.apply(cm, arguments);}
1151
- finally {endOperation(cm);}
 
 
 
 
 
 
 
 
 
1152
  return result;
1153
  };
1154
  }
 
 
 
 
 
 
 
1155
 
1156
  function regChange(cm, from, to, lendiff) {
 
 
1157
  cm.curOp.changes.push({from: from, to: to, diff: lendiff});
1158
  }
1159
 
1160
  // INPUT HANDLING
1161
 
1162
  function slowPoll(cm) {
1163
- if (cm.view.pollingFast) return;
1164
  cm.display.poll.set(cm.options.pollInterval, function() {
1165
  readInput(cm);
1166
- if (cm.view.focused) slowPoll(cm);
1167
  });
1168
  }
1169
 
@@ -1184,50 +1386,59 @@ window.CodeMirror = (function() {
1184
  // events that indicate IME taking place, but these are not widely
1185
  // supported or compatible enough yet to rely on.)
1186
  function readInput(cm) {
1187
- var input = cm.display.input, prevInput = cm.display.prevInput, view = cm.view, sel = view.sel;
1188
- if (!view.focused || hasSelection(input) || isReadOnly(cm)) return false;
1189
  var text = input.value;
1190
  if (text == prevInput && posEq(sel.from, sel.to)) return false;
1191
- startOperation(cm);
1192
- view.sel.shift = false;
 
 
 
 
 
 
 
 
1193
  var same = 0, l = Math.min(prevInput.length, text.length);
1194
  while (same < l && prevInput[same] == text[same]) ++same;
1195
  var from = sel.from, to = sel.to;
1196
  if (same < prevInput.length)
1197
- from = {line: from.line, ch: from.ch - (prevInput.length - same)};
1198
- else if (view.overwrite && posEq(from, to) && !cm.display.pasteIncoming)
1199
- to = {line: to.line, ch: Math.min(getLine(cm.view.doc, to.line).text.length, to.ch + (text.length - same))};
1200
  var updateInput = cm.curOp.updateInput;
1201
- updateDoc(cm, from, to, splitLines(text.slice(same)), "end",
1202
- cm.display.pasteIncoming ? "paste" : "input", {from: from, to: to});
 
1203
  cm.curOp.updateInput = updateInput;
1204
- if (text.length > 1000) input.value = cm.display.prevInput = "";
1205
  else cm.display.prevInput = text;
1206
- endOperation(cm);
1207
- cm.display.pasteIncoming = false;
1208
  return true;
1209
  }
1210
 
1211
  function resetInput(cm, user) {
1212
- var view = cm.view, minimal, selected;
1213
- if (!posEq(view.sel.from, view.sel.to)) {
1214
  cm.display.prevInput = "";
1215
  minimal = hasCopyEvent &&
1216
- (view.sel.to.line - view.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000);
1217
  if (minimal) cm.display.input.value = "-";
1218
  else cm.display.input.value = selected || cm.getSelection();
1219
- if (view.focused) selectInput(cm.display.input);
1220
  } else if (user) cm.display.prevInput = cm.display.input.value = "";
1221
  cm.display.inaccurateSelection = minimal;
1222
  }
1223
 
1224
  function focusInput(cm) {
1225
- if (cm.options.readOnly != "nocursor" && (ie || document.activeElement != cm.display.input))
1226
  cm.display.input.focus();
1227
  }
1228
 
1229
  function isReadOnly(cm) {
1230
- return cm.options.readOnly || cm.view.cantEdit;
1231
  }
1232
 
1233
  // EVENT HANDLERS
@@ -1237,44 +1448,56 @@ window.CodeMirror = (function() {
1237
  on(d.scroller, "mousedown", operation(cm, onMouseDown));
1238
  on(d.scroller, "dblclick", operation(cm, e_preventDefault));
1239
  on(d.lineSpace, "selectstart", function(e) {
1240
- if (!mouseEventInWidget(d, e)) e_preventDefault(e);
1241
  });
1242
  // Gecko browsers fire contextmenu *after* opening the menu, at
1243
  // which point we can't mess with it anymore. Context menu is
1244
  // handled in onMouseDown for Gecko.
1245
- if (!gecko) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
1246
 
1247
  on(d.scroller, "scroll", function() {
1248
- setScrollTop(cm, d.scroller.scrollTop);
1249
- setScrollLeft(cm, d.scroller.scrollLeft, true);
1250
- signal(cm, "scroll", cm);
 
 
1251
  });
1252
  on(d.scrollbarV, "scroll", function() {
1253
- setScrollTop(cm, d.scrollbarV.scrollTop);
1254
  });
1255
  on(d.scrollbarH, "scroll", function() {
1256
- setScrollLeft(cm, d.scrollbarH.scrollLeft);
1257
  });
1258
 
1259
  on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
1260
  on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
1261
 
1262
- function reFocus() { if (cm.view.focused) setTimeout(bind(focusInput, cm), 0); }
1263
  on(d.scrollbarH, "mousedown", reFocus);
1264
  on(d.scrollbarV, "mousedown", reFocus);
1265
  // Prevent wrapper from ever scrolling
1266
  on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
1267
- on(window, "resize", function resizeHandler() {
 
1268
  // Might be a text scaling operation, clear size caches.
1269
  d.cachedCharWidth = d.cachedTextHeight = null;
1270
  clearCaches(cm);
1271
- if (d.wrapper.parentNode) updateDisplay(cm, true);
1272
- else off(window, "resize", resizeHandler);
1273
- });
 
 
 
 
 
 
 
 
 
1274
 
1275
  on(d.input, "keyup", operation(cm, function(e) {
1276
  if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1277
- if (e_prop(e, "keyCode") == 16) cm.view.sel.shift = false;
1278
  }));
1279
  on(d.input, "input", bind(fastPoll, cm));
1280
  on(d.input, "keydown", operation(cm, onKeyDown));
@@ -1292,9 +1515,13 @@ window.CodeMirror = (function() {
1292
  on(d.scroller, "dragover", drag_);
1293
  on(d.scroller, "drop", operation(cm, onDrop));
1294
  }
1295
- on(d.scroller, "paste", function(){focusInput(cm); fastPoll(cm);});
 
 
 
 
1296
  on(d.input, "paste", function() {
1297
- d.pasteIncoming = true;
1298
  fastPoll(cm);
1299
  });
1300
 
@@ -1316,10 +1543,12 @@ window.CodeMirror = (function() {
1316
  });
1317
  }
1318
 
1319
- function mouseEventInWidget(display, e) {
1320
- for (var n = e_target(e); n != display.wrapper; n = n.parentNode)
 
1321
  if (/\bCodeMirror-(?:line)?widget\b/.test(n.className) ||
1322
  n.parentNode == display.sizer && n != display.mover) return true;
 
1323
  }
1324
 
1325
  function posFromMouse(cm, e, liberal) {
@@ -1330,7 +1559,7 @@ window.CodeMirror = (function() {
1330
  target == display.scrollbarV || target == display.scrollbarV.firstChild ||
1331
  target == display.scrollbarFiller) return null;
1332
  }
1333
- var x, y, space = display.lineSpace.getBoundingClientRect();
1334
  // Fails unpredictably on IE[67] when mouse is dragged around quickly.
1335
  try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
1336
  return coordsChar(cm, x - space.left, y - space.top);
@@ -1338,10 +1567,10 @@ window.CodeMirror = (function() {
1338
 
1339
  var lastClick, lastDoubleClick;
1340
  function onMouseDown(e) {
1341
- var cm = this, display = cm.display, view = cm.view, sel = view.sel, doc = view.doc;
1342
- sel.shift = e_prop(e, "shiftKey");
1343
 
1344
- if (mouseEventInWidget(display, e)) {
1345
  if (!webkit) {
1346
  display.scroller.draggable = false;
1347
  setTimeout(function(){display.scroller.draggable = true;}, 100);
@@ -1353,10 +1582,10 @@ window.CodeMirror = (function() {
1353
 
1354
  switch (e_button(e)) {
1355
  case 3:
1356
- if (gecko) onContextMenu.call(cm, cm, e);
1357
  return;
1358
  case 2:
1359
- if (start) extendSelection(cm, start);
1360
  setTimeout(bind(focusInput, cm), 20);
1361
  e_preventDefault(e);
1362
  return;
@@ -1366,7 +1595,7 @@ window.CodeMirror = (function() {
1366
  // selection.
1367
  if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;}
1368
 
1369
- if (!view.focused) onFocus(cm);
1370
 
1371
  var now = +new Date, type = "single";
1372
  if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
@@ -1379,7 +1608,7 @@ window.CodeMirror = (function() {
1379
  lastDoubleClick = {time: now, pos: start};
1380
  e_preventDefault(e);
1381
  var word = findWordAt(getLine(doc, start.line).text, start);
1382
- extendSelection(cm, word.from, word.to);
1383
  } else { lastClick = {time: now, pos: start}; }
1384
 
1385
  var last = start;
@@ -1387,18 +1616,18 @@ window.CodeMirror = (function() {
1387
  !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
1388
  var dragEnd = operation(cm, function(e2) {
1389
  if (webkit) display.scroller.draggable = false;
1390
- view.draggingText = false;
1391
  off(document, "mouseup", dragEnd);
1392
  off(display.scroller, "drop", dragEnd);
1393
  if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
1394
  e_preventDefault(e2);
1395
- extendSelection(cm, start);
1396
  focusInput(cm);
1397
  }
1398
  });
1399
  // Let the drag handler handle this.
1400
  if (webkit) display.scroller.draggable = true;
1401
- view.draggingText = dragEnd;
1402
  // IE's approach to draggable
1403
  if (display.scroller.dragDrop) display.scroller.dragDrop();
1404
  on(document, "mouseup", dragEnd);
@@ -1406,13 +1635,13 @@ window.CodeMirror = (function() {
1406
  return;
1407
  }
1408
  e_preventDefault(e);
1409
- if (type == "single") extendSelection(cm, clipPos(doc, start));
1410
 
1411
  var startstart = sel.from, startend = sel.to;
1412
 
1413
  function doSelect(cur) {
1414
  if (type == "single") {
1415
- extendSelection(cm, clipPos(doc, start), cur);
1416
  return;
1417
  }
1418
 
@@ -1420,15 +1649,15 @@ window.CodeMirror = (function() {
1420
  startend = clipPos(doc, startend);
1421
  if (type == "double") {
1422
  var word = findWordAt(getLine(doc, cur.line).text, cur);
1423
- if (posLess(cur, startstart)) extendSelection(cm, word.from, startend);
1424
- else extendSelection(cm, startstart, word.to);
1425
  } else if (type == "triple") {
1426
- if (posLess(cur, startstart)) extendSelection(cm, startend, clipPos(doc, {line: cur.line, ch: 0}));
1427
- else extendSelection(cm, startstart, clipPos(doc, {line: cur.line + 1, ch: 0}));
1428
  }
1429
  }
1430
 
1431
- var editorSize = display.wrapper.getBoundingClientRect();
1432
  // Used to ensure timeout re-tries don't fire when another extend
1433
  // happened in the meantime (clearTimeout isn't reliable -- at
1434
  // least on Chrome, the timeouts still happen even when cleared,
@@ -1440,7 +1669,7 @@ window.CodeMirror = (function() {
1440
  var cur = posFromMouse(cm, e, true);
1441
  if (!cur) return;
1442
  if (!posEq(cur, last)) {
1443
- if (!view.focused) onFocus(cm);
1444
  last = cur;
1445
  doSelect(cur);
1446
  var visible = visibleLines(display, doc);
@@ -1477,7 +1706,8 @@ window.CodeMirror = (function() {
1477
 
1478
  function onDrop(e) {
1479
  var cm = this;
1480
- if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
 
1481
  e_preventDefault(e);
1482
  var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
1483
  if (!pos || isReadOnly(cm)) return;
@@ -1488,11 +1718,8 @@ window.CodeMirror = (function() {
1488
  reader.onload = function() {
1489
  text[i] = reader.result;
1490
  if (++read == n) {
1491
- pos = clipPos(cm.view.doc, pos);
1492
- operation(cm, function() {
1493
- var end = replaceRange(cm, text.join(""), pos, pos, "paste");
1494
- setSelection(cm, pos, end);
1495
- })();
1496
  }
1497
  };
1498
  reader.readAsText(file);
@@ -1500,17 +1727,18 @@ window.CodeMirror = (function() {
1500
  for (var i = 0; i < n; ++i) loadFile(files[i], i);
1501
  } else {
1502
  // Don't do a replace if the drop happened inside of the selected text.
1503
- if (cm.view.draggingText && !(posLess(pos, cm.view.sel.from) || posLess(cm.view.sel.to, pos))) {
1504
- cm.view.draggingText(e);
1505
- if (ie) setTimeout(bind(focusInput, cm), 50);
 
1506
  return;
1507
  }
1508
  try {
1509
  var text = e.dataTransfer.getData("Text");
1510
  if (text) {
1511
- var curFrom = cm.view.sel.from, curTo = cm.view.sel.to;
1512
- setSelection(cm, pos, pos);
1513
- if (cm.view.draggingText) replaceRange(cm, "", curFrom, curTo, "paste");
1514
  cm.replaceSelection(text, null, "paste");
1515
  focusInput(cm);
1516
  onFocus(cm);
@@ -1525,20 +1753,20 @@ window.CodeMirror = (function() {
1525
  try { var mX = e.clientX, mY = e.clientY; }
1526
  catch(e) { return false; }
1527
 
1528
- if (mX >= Math.floor(display.gutters.getBoundingClientRect().right)) return false;
1529
  e_preventDefault(e);
1530
  if (!hasHandler(cm, "gutterClick")) return true;
1531
 
1532
- var lineBox = display.lineDiv.getBoundingClientRect();
1533
  if (mY > lineBox.bottom) return true;
1534
  mY -= lineBox.top - display.viewOffset;
1535
 
1536
  for (var i = 0; i < cm.options.gutters.length; ++i) {
1537
  var g = display.gutters.childNodes[i];
1538
- if (g && g.getBoundingClientRect().right >= mX) {
1539
- var line = lineAtHeight(cm.view.doc, mY);
1540
  var gutter = cm.options.gutters[i];
1541
- signalLater(cm, cm, "gutterClick", cm, line, gutter, e);
1542
  break;
1543
  }
1544
  }
@@ -1546,26 +1774,47 @@ window.CodeMirror = (function() {
1546
  }
1547
 
1548
  function onDragStart(cm, e) {
 
 
1549
  var txt = cm.getSelection();
1550
  e.dataTransfer.setData("Text", txt);
1551
 
1552
  // Use dummy image instead of default browsers image.
1553
  // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
1554
- if (e.dataTransfer.setDragImage && !safari)
1555
- e.dataTransfer.setDragImage(elt('img'), 0, 0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1556
  }
1557
 
1558
  function setScrollTop(cm, val) {
1559
- if (Math.abs(cm.view.scrollTop - val) < 2) return;
1560
- cm.view.scrollTop = val;
1561
  if (!gecko) updateDisplay(cm, [], val);
1562
  if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
1563
  if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
1564
  if (gecko) updateDisplay(cm, []);
1565
  }
1566
  function setScrollLeft(cm, val, isScroller) {
1567
- if (isScroller ? val == cm.view.scrollLeft : Math.abs(cm.view.scrollLeft - val) < 2) return;
1568
- cm.view.scrollLeft = val;
 
1569
  alignHorizontally(cm);
1570
  if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
1571
  if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val;
@@ -1582,7 +1831,7 @@ window.CodeMirror = (function() {
1582
  // is that it gives us a chance to update the display before the
1583
  // actual scrolling happens, reducing flickering.
1584
 
1585
- var wheelSamples = 0, wheelDX, wheelDY, wheelStartX, wheelStartY, wheelPixelsPerUnit = null;
1586
  // Fill in a browser-detected starting value on browsers where we
1587
  // know one. These don't have to be accurate -- the result of them
1588
  // being wrong would just be a slight flicker on the first wheel
@@ -1611,7 +1860,7 @@ window.CodeMirror = (function() {
1611
  }
1612
  }
1613
 
1614
- var scroll = cm.display.scroller;
1615
  // On some browsers, horizontal scrolling will cause redraws to
1616
  // happen before the gutter has been realigned, causing it to
1617
  // wriggle around in a most unseemly way. When we have an
@@ -1623,35 +1872,35 @@ window.CodeMirror = (function() {
1623
  setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
1624
  setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
1625
  e_preventDefault(e);
1626
- wheelStartX = null; // Abort measurement, if in progress
1627
  return;
1628
  }
1629
 
1630
  if (dy && wheelPixelsPerUnit != null) {
1631
  var pixels = dy * wheelPixelsPerUnit;
1632
- var top = cm.view.scrollTop, bot = top + cm.display.wrapper.clientHeight;
1633
  if (pixels < 0) top = Math.max(0, top + pixels - 50);
1634
- else bot = Math.min(cm.view.doc.height, bot + pixels + 50);
1635
  updateDisplay(cm, [], {top: top, bottom: bot});
1636
  }
1637
 
1638
  if (wheelSamples < 20) {
1639
- if (wheelStartX == null) {
1640
- wheelStartX = scroll.scrollLeft; wheelStartY = scroll.scrollTop;
1641
- wheelDX = dx; wheelDY = dy;
1642
  setTimeout(function() {
1643
- if (wheelStartX == null) return;
1644
- var movedX = scroll.scrollLeft - wheelStartX;
1645
- var movedY = scroll.scrollTop - wheelStartY;
1646
- var sample = (movedY && wheelDY && movedY / wheelDY) ||
1647
- (movedX && wheelDX && movedX / wheelDX);
1648
- wheelStartX = wheelStartY = null;
1649
  if (!sample) return;
1650
  wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
1651
  ++wheelSamples;
1652
  }, 200);
1653
  } else {
1654
- wheelDX += dx; wheelDY += dy;
1655
  }
1656
  }
1657
  }
@@ -1664,25 +1913,22 @@ window.CodeMirror = (function() {
1664
  // Ensure previous input has been read, so that the handler sees a
1665
  // consistent view of the document
1666
  if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false;
1667
- var view = cm.view, prevShift = view.sel.shift;
1668
  try {
1669
- if (isReadOnly(cm)) view.suppressEdits = true;
1670
- if (dropShift) view.sel.shift = false;
1671
- bound(cm);
1672
- } catch(e) {
1673
- if (e != Pass) throw e;
1674
- return false;
1675
  } finally {
1676
- view.sel.shift = prevShift;
1677
- view.suppressEdits = false;
1678
  }
1679
- return true;
1680
  }
1681
 
1682
  function allKeyMaps(cm) {
1683
- var maps = cm.view.keyMaps.slice(0);
 
1684
  maps.push(cm.options.keyMap);
1685
- if (cm.options.extraKeys) maps.unshift(cm.options.extraKeys);
1686
  return maps;
1687
  }
1688
 
@@ -1696,28 +1942,23 @@ window.CodeMirror = (function() {
1696
  cm.options.keyMap = (next.call ? next.call(null, cm) : next);
1697
  }, 50);
1698
 
1699
- var name = keyNames[e_prop(e, "keyCode")], handled = false;
1700
- var flipCtrlCmd = mac && (opera || qtwebkit);
1701
- if (name == null || e.altGraphKey) return false;
1702
- if (e_prop(e, "altKey")) name = "Alt-" + name;
1703
- if (e_prop(e, flipCtrlCmd ? "metaKey" : "ctrlKey")) name = "Ctrl-" + name;
1704
- if (e_prop(e, flipCtrlCmd ? "ctrlKey" : "metaKey")) name = "Cmd-" + name;
1705
-
1706
- var stopped = false;
1707
- function stop() { stopped = true; }
1708
  var keymaps = allKeyMaps(cm);
1709
 
1710
- if (e_prop(e, "shiftKey")) {
1711
- handled = lookupKey("Shift-" + name, keymaps,
1712
- function(b) {return doHandleBinding(cm, b, true);}, stop)
1713
- || lookupKey(name, keymaps, function(b) {
1714
- if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(cm, b);
1715
- }, stop);
 
 
1716
  } else {
1717
- handled = lookupKey(name, keymaps,
1718
- function(b) { return doHandleBinding(cm, b); }, stop);
1719
  }
1720
- if (stopped) handled = false;
 
1721
  if (handled) {
1722
  e_preventDefault(e);
1723
  restartBlink(cm);
@@ -1739,18 +1980,18 @@ window.CodeMirror = (function() {
1739
  var lastStoppedKey = null;
1740
  function onKeyDown(e) {
1741
  var cm = this;
1742
- if (!cm.view.focused) onFocus(cm);
1743
  if (ie && e.keyCode == 27) { e.returnValue = false; }
1744
  if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1745
- var code = e_prop(e, "keyCode");
1746
  // IE does strange things with escape.
1747
- cm.view.sel.shift = code == 16 || e_prop(e, "shiftKey");
1748
  // First give onKeyEvent option a chance to handle this.
1749
  var handled = handleKeyBinding(cm, e);
1750
  if (opera) {
1751
  lastStoppedKey = handled ? code : null;
1752
  // Opera has no cut event... we try to at least catch the key combo
1753
- if (!handled && code == 88 && !hasCopyEvent && e_prop(e, mac ? "metaKey" : "ctrlKey"))
1754
  cm.replaceSelection("");
1755
  }
1756
  }
@@ -1758,53 +1999,55 @@ window.CodeMirror = (function() {
1758
  function onKeyPress(e) {
1759
  var cm = this;
1760
  if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1761
- var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode");
1762
  if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
1763
  if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
1764
  var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
1765
- if (this.options.electricChars && this.view.mode.electricChars &&
1766
  this.options.smartIndent && !isReadOnly(this) &&
1767
- this.view.mode.electricChars.indexOf(ch) > -1)
1768
- setTimeout(operation(cm, function() {indentLine(cm, cm.view.sel.to.line, "smart");}), 75);
1769
  if (handleCharBinding(cm, e, ch)) return;
1770
  fastPoll(cm);
1771
  }
1772
 
1773
  function onFocus(cm) {
1774
  if (cm.options.readOnly == "nocursor") return;
1775
- if (!cm.view.focused) {
1776
  signal(cm, "focus", cm);
1777
- cm.view.focused = true;
1778
- if (cm.display.scroller.className.search(/\bCodeMirror-focused\b/) == -1)
1779
- cm.display.scroller.className += " CodeMirror-focused";
1780
  resetInput(cm, true);
1781
  }
1782
  slowPoll(cm);
1783
  restartBlink(cm);
1784
  }
1785
  function onBlur(cm) {
1786
- if (cm.view.focused) {
1787
  signal(cm, "blur", cm);
1788
- cm.view.focused = false;
1789
- cm.display.scroller.className = cm.display.scroller.className.replace(" CodeMirror-focused", "");
1790
  }
1791
  clearInterval(cm.display.blinker);
1792
- setTimeout(function() {if (!cm.view.focused) cm.view.sel.shift = false;}, 150);
1793
  }
1794
 
1795
  var detectingSelectAll;
1796
  function onContextMenu(cm, e) {
1797
- var display = cm.display, sel = cm.view.sel;
 
 
1798
  var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
1799
  if (!pos || opera) return; // Opera is difficult.
1800
  if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
1801
- operation(cm, setSelection)(cm, pos, pos);
1802
 
1803
  var oldCSS = display.input.style.cssText;
1804
  display.inputDiv.style.position = "absolute";
1805
  display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
1806
  "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" +
1807
- "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
1808
  focusInput(cm);
1809
  resetInput(cm, true);
1810
  // Adds "Select all" to context menu in FF
@@ -1816,27 +2059,29 @@ window.CodeMirror = (function() {
1816
  if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
1817
  slowPoll(cm);
1818
 
1819
- // Try to detect the user choosing select-all
1820
- if (display.input.selectionStart != null) {
1821
  clearTimeout(detectingSelectAll);
1822
  var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0;
1823
  display.prevInput = " ";
1824
  display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
1825
- detectingSelectAll = setTimeout(function poll(){
1826
  if (display.prevInput == " " && display.input.selectionStart == 0)
1827
  operation(cm, commands.selectAll)(cm);
1828
  else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
1829
  else resetInput(cm);
1830
- }, 200);
 
1831
  }
1832
  }
1833
 
1834
- if (gecko) {
1835
  e_stop(e);
1836
- on(window, "mouseup", function mouseup() {
1837
  off(window, "mouseup", mouseup);
1838
  setTimeout(rehide, 20);
1839
- });
 
1840
  } else {
1841
  setTimeout(rehide, 50);
1842
  }
@@ -1844,124 +2089,208 @@ window.CodeMirror = (function() {
1844
 
1845
  // UPDATING
1846
 
1847
- // Replace the range from from to to by the strings in newText.
1848
- // Afterwards, set the selection to selFrom, selTo.
1849
- function updateDoc(cm, from, to, newText, selUpdate, origin) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1850
  // Possibly split or suppress the update based on the presence
1851
  // of read-only spans in its range.
1852
- var split = sawReadOnlySpans &&
1853
- removeReadOnlyRanges(cm.view.doc, from, to);
1854
  if (split) {
1855
  for (var i = split.length - 1; i >= 1; --i)
1856
- updateDocInner(cm, split[i].from, split[i].to, [""], origin);
1857
  if (split.length)
1858
- return updateDocInner(cm, split[0].from, split[0].to, newText, selUpdate, origin);
1859
  } else {
1860
- return updateDocInner(cm, from, to, newText, selUpdate, origin);
1861
  }
1862
  }
1863
 
1864
- function updateDocInner(cm, from, to, newText, selUpdate, origin) {
1865
- if (cm.view.suppressEdits) return;
 
1866
 
1867
- var view = cm.view, doc = view.doc, old = [];
1868
- doc.iter(from.line, to.line + 1, function(line) {
1869
- old.push(newHL(line.text, line.markedSpans));
 
 
 
 
 
 
1870
  });
1871
- var startSelFrom = view.sel.from, startSelTo = view.sel.to;
1872
- var lines = updateMarkedSpans(hlSpans(old[0]), hlSpans(lst(old)), from.ch, to.ch, newText);
1873
- var retval = updateDocNoUndo(cm, from, to, lines, selUpdate, origin);
1874
- if (view.history) addChange(cm, from.line, newText.length, old, origin,
1875
- startSelFrom, startSelTo, view.sel.from, view.sel.to);
1876
- return retval;
1877
- }
1878
-
1879
- function unredoHelper(cm, type) {
1880
- var doc = cm.view.doc, hist = cm.view.history;
1881
- var set = (type == "undo" ? hist.done : hist.undone).pop();
1882
- if (!set) return;
1883
- var anti = {events: [], fromBefore: set.fromAfter, toBefore: set.toAfter,
1884
- fromAfter: set.fromBefore, toAfter: set.toBefore};
1885
- for (var i = set.events.length - 1; i >= 0; i -= 1) {
1886
- hist.dirtyCounter += type == "undo" ? -1 : 1;
1887
- var change = set.events[i];
1888
- var replaced = [], end = change.start + change.added;
1889
- doc.iter(change.start, end, function(line) { replaced.push(newHL(line.text, line.markedSpans)); });
1890
- anti.events.push({start: change.start, added: change.old.length, old: replaced});
1891
- var selPos = i ? null : {from: set.fromBefore, to: set.toBefore};
1892
- updateDocNoUndo(cm, {line: change.start, ch: 0}, {line: end - 1, ch: getLine(doc, end-1).text.length},
1893
- change.old, selPos, type);
1894
- }
1895
  (type == "undo" ? hist.undone : hist.done).push(anti);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1896
  }
1897
 
1898
- function updateDocNoUndo(cm, from, to, lines, selUpdate, origin) {
1899
- var view = cm.view, doc = view.doc, display = cm.display;
1900
- if (view.suppressEdits) return;
1901
 
1902
- var nlines = to.line - from.line, firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
1903
  var recomputeMaxLength = false, checkWidthStart = from.line;
1904
  if (!cm.options.lineWrapping) {
1905
- checkWidthStart = lineNo(visualLine(doc, firstLine));
1906
  doc.iter(checkWidthStart, to.line + 1, function(line) {
1907
- if (lineLength(doc, line) == view.maxLineLength) {
1908
  recomputeMaxLength = true;
1909
  return true;
1910
  }
1911
  });
1912
  }
1913
 
1914
- var lastHL = lst(lines), th = textHeight(display);
1915
-
1916
- // First adjust the line structure
1917
- if (from.ch == 0 && to.ch == 0 && hlText(lastHL) == "") {
1918
- // This is a whole-line replace. Treated specially to make
1919
- // sure line objects move the way they are supposed to.
1920
- var added = [];
1921
- for (var i = 0, e = lines.length - 1; i < e; ++i)
1922
- added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
1923
- updateLine(cm, lastLine, lastLine.text, hlSpans(lastHL));
1924
- if (nlines) doc.remove(from.line, nlines, cm);
1925
- if (added.length) doc.insert(from.line, added);
1926
- } else if (firstLine == lastLine) {
1927
- if (lines.length == 1) {
1928
- updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) +
1929
- firstLine.text.slice(to.ch), hlSpans(lines[0]));
1930
- } else {
1931
- for (var added = [], i = 1, e = lines.length - 1; i < e; ++i)
1932
- added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
1933
- added.push(makeLine(hlText(lastHL) + firstLine.text.slice(to.ch), hlSpans(lastHL), th));
1934
- updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
1935
- doc.insert(from.line + 1, added);
1936
- }
1937
- } else if (lines.length == 1) {
1938
- updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) +
1939
- lastLine.text.slice(to.ch), hlSpans(lines[0]));
1940
- doc.remove(from.line + 1, nlines, cm);
1941
- } else {
1942
- var added = [];
1943
- updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
1944
- updateLine(cm, lastLine, hlText(lastHL) + lastLine.text.slice(to.ch), hlSpans(lastHL));
1945
- for (var i = 1, e = lines.length - 1; i < e; ++i)
1946
- added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
1947
- if (nlines > 1) doc.remove(from.line + 1, nlines - 1, cm);
1948
- doc.insert(from.line + 1, added);
1949
- }
1950
 
1951
- if (cm.options.lineWrapping) {
1952
- var perLine = Math.max(5, display.scroller.clientWidth / charWidth(display) - 3);
1953
- doc.iter(from.line, from.line + lines.length, function(line) {
1954
- if (line.height == 0) return;
1955
- var guess = (Math.ceil(line.text.length / perLine) || 1) * th;
1956
- if (guess != line.height) updateLineHeight(line, guess);
1957
- });
1958
- } else {
1959
- doc.iter(checkWidthStart, from.line + lines.length, function(line) {
1960
  var len = lineLength(doc, line);
1961
- if (len > view.maxLineLength) {
1962
- view.maxLine = line;
1963
- view.maxLineLength = len;
1964
- view.maxLineChanged = true;
1965
  recomputeMaxLength = false;
1966
  }
1967
  });
@@ -1969,82 +2298,66 @@ window.CodeMirror = (function() {
1969
  }
1970
 
1971
  // Adjust frontier, schedule worker
1972
- view.frontier = Math.min(view.frontier, from.line);
1973
  startWorker(cm, 400);
1974
 
1975
- var lendiff = lines.length - nlines - 1;
1976
  // Remember that these lines changed, for updating the display
1977
  regChange(cm, from.line, to.line + 1, lendiff);
 
1978
  if (hasHandler(cm, "change")) {
1979
- // Normalize lines to contain only strings, since that's what
1980
- // the change event handler expects
1981
- for (var i = 0; i < lines.length; ++i)
1982
- if (typeof lines[i] != "string") lines[i] = lines[i].text;
1983
- var changeObj = {from: from, to: to, text: lines, origin: origin};
1984
  if (cm.curOp.textChanged) {
1985
  for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {}
1986
  cur.next = changeObj;
1987
  } else cm.curOp.textChanged = changeObj;
1988
  }
1989
-
1990
- // Update the selection
1991
- var newSelFrom, newSelTo, end = {line: from.line + lines.length - 1,
1992
- ch: hlText(lastHL).length + (lines.length == 1 ? from.ch : 0)};
1993
- if (selUpdate && typeof selUpdate != "string") {
1994
- if (selUpdate.from) { newSelFrom = selUpdate.from; newSelTo = selUpdate.to; }
1995
- else newSelFrom = newSelTo = selUpdate;
1996
- } else if (selUpdate == "end") {
1997
- newSelFrom = newSelTo = end;
1998
- } else if (selUpdate == "start") {
1999
- newSelFrom = newSelTo = from;
2000
- } else if (selUpdate == "around") {
2001
- newSelFrom = from; newSelTo = end;
2002
- } else {
2003
- var adjustPos = function(pos) {
2004
- if (posLess(pos, from)) return pos;
2005
- if (!posLess(to, pos)) return end;
2006
- var line = pos.line + lendiff;
2007
- var ch = pos.ch;
2008
- if (pos.line == to.line)
2009
- ch += hlText(lastHL).length - (to.ch - (to.line == from.line ? from.ch : 0));
2010
- return {line: line, ch: ch};
2011
- };
2012
- newSelFrom = adjustPos(view.sel.from);
2013
- newSelTo = adjustPos(view.sel.to);
2014
- }
2015
- setSelection(cm, newSelFrom, newSelTo, null, true);
2016
- return end;
2017
  }
2018
 
2019
- function replaceRange(cm, code, from, to, origin) {
2020
  if (!to) to = from;
2021
  if (posLess(to, from)) { var tmp = to; to = from; from = tmp; }
2022
- return updateDoc(cm, from, to, splitLines(code), null, origin);
 
2023
  }
2024
 
2025
- // SELECTION
 
 
 
 
 
 
2026
 
2027
  function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
2028
  function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
2029
- function copyPos(x) {return {line: x.line, ch: x.ch};}
 
 
2030
 
2031
- function clipLine(doc, n) {return Math.max(0, Math.min(n, doc.size-1));}
2032
  function clipPos(doc, pos) {
2033
- if (pos.line < 0) return {line: 0, ch: 0};
2034
- if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc, doc.size-1).text.length};
2035
- var ch = pos.ch, linelen = getLine(doc, pos.line).text.length;
2036
- if (ch == null || ch > linelen) return {line: pos.line, ch: linelen};
2037
- else if (ch < 0) return {line: pos.line, ch: 0};
 
 
 
 
2038
  else return pos;
2039
  }
2040
- function isLine(doc, l) {return l >= 0 && l < doc.size;}
2041
 
2042
  // If shift is held, this will move the selection anchor. Otherwise,
2043
  // it'll set the whole selection.
2044
- function extendSelection(cm, pos, other, bias) {
2045
- var sel = cm.view.sel;
2046
- if (sel.shift || sel.extend) {
2047
- var anchor = sel.anchor;
2048
  if (other) {
2049
  var posBefore = posLess(pos, anchor);
2050
  if (posBefore != posLess(other, anchor)) {
@@ -2054,24 +2367,38 @@ window.CodeMirror = (function() {
2054
  pos = other;
2055
  }
2056
  }
2057
- setSelection(cm, anchor, pos, bias);
2058
  } else {
2059
- setSelection(cm, pos, other || pos, bias);
2060
  }
2061
- cm.curOp.userSelChange = true;
 
 
 
 
 
 
 
 
2062
  }
2063
 
2064
  // Update the selection. Last two args are only used by
2065
  // updateDoc, since they have to be expressed in the line
2066
  // numbers before the update.
2067
- function setSelection(cm, anchor, head, bias, checkAtomic) {
2068
- cm.view.goalColumn = null;
2069
- var sel = cm.view.sel;
 
 
 
 
 
 
2070
  // Skip over atomic spans.
2071
  if (checkAtomic || !posEq(anchor, sel.anchor))
2072
- anchor = skipAtomic(cm, anchor, bias, checkAtomic != "push");
2073
  if (checkAtomic || !posEq(head, sel.head))
2074
- head = skipAtomic(cm, head, bias, checkAtomic != "push");
2075
 
2076
  if (posEq(sel.anchor, anchor) && posEq(sel.head, head)) return;
2077
 
@@ -2080,47 +2407,53 @@ window.CodeMirror = (function() {
2080
  sel.from = inv ? head : anchor;
2081
  sel.to = inv ? anchor : head;
2082
 
2083
- cm.curOp.updateInput = true;
2084
- cm.curOp.selectionChanged = true;
 
 
2085
  }
2086
 
2087
  function reCheckSelection(cm) {
2088
- setSelection(cm, cm.view.sel.from, cm.view.sel.to, null, "push");
2089
  }
2090
 
2091
- function skipAtomic(cm, pos, bias, mayClear) {
2092
- var doc = cm.view.doc, flipped = false, curPos = pos;
2093
  var dir = bias || 1;
2094
- cm.view.cantEdit = false;
2095
  search: for (;;) {
2096
- var line = getLine(doc, curPos.line), toClear;
2097
  if (line.markedSpans) {
2098
  for (var i = 0; i < line.markedSpans.length; ++i) {
2099
  var sp = line.markedSpans[i], m = sp.marker;
2100
  if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
2101
  (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
2102
- if (mayClear && m.clearOnEnter) {
2103
- (toClear || (toClear = [])).push(m);
2104
- continue;
2105
- } else if (!m.atomic) continue;
 
 
 
 
2106
  var newPos = m.find()[dir < 0 ? "from" : "to"];
2107
  if (posEq(newPos, curPos)) {
2108
  newPos.ch += dir;
2109
  if (newPos.ch < 0) {
2110
- if (newPos.line) newPos = clipPos(doc, {line: newPos.line - 1});
2111
  else newPos = null;
2112
  } else if (newPos.ch > line.text.length) {
2113
- if (newPos.line < doc.size - 1) newPos = {line: newPos.line + 1, ch: 0};
2114
  else newPos = null;
2115
  }
2116
  if (!newPos) {
2117
  if (flipped) {
2118
  // Driven in a corner -- no valid cursor position found at all
2119
  // -- try again *with* clearing, if we didn't already
2120
- if (!mayClear) return skipAtomic(cm, pos, bias, true);
2121
  // Otherwise, turn off editing until further notice, and return the start of the doc
2122
- cm.view.cantEdit = true;
2123
- return {line: 0, ch: 0};
2124
  }
2125
  flipped = true; newPos = pos; dir = -dir;
2126
  }
@@ -2129,7 +2462,6 @@ window.CodeMirror = (function() {
2129
  continue search;
2130
  }
2131
  }
2132
- if (toClear) for (var i = 0; i < toClear.length; ++i) toClear[i].clear();
2133
  }
2134
  return curPos;
2135
  }
@@ -2138,12 +2470,11 @@ window.CodeMirror = (function() {
2138
  // SCROLLING
2139
 
2140
  function scrollCursorIntoView(cm) {
2141
- var view = cm.view;
2142
- var coords = scrollPosIntoView(cm, view.sel.head);
2143
- if (!view.focused) return;
2144
- var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
2145
- if (coords.top + box.top < 0) doScroll = true;
2146
- else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
2147
  if (doScroll != null && !phantom) {
2148
  var hidden = display.cursor.style.display == "none";
2149
  if (hidden) {
@@ -2156,18 +2487,19 @@ window.CodeMirror = (function() {
2156
  }
2157
  }
2158
 
2159
- function scrollPosIntoView(cm, pos) {
 
2160
  for (;;) {
2161
  var changed = false, coords = cursorCoords(cm, pos);
2162
- var scrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
2163
- var startTop = cm.view.scrollTop, startLeft = cm.view.scrollLeft;
2164
  if (scrollPos.scrollTop != null) {
2165
  setScrollTop(cm, scrollPos.scrollTop);
2166
- if (Math.abs(cm.view.scrollTop - startTop) > 1) changed = true;
2167
  }
2168
  if (scrollPos.scrollLeft != null) {
2169
  setScrollLeft(cm, scrollPos.scrollLeft);
2170
- if (Math.abs(cm.view.scrollLeft - startLeft) > 1) changed = true;
2171
  }
2172
  if (!changed) return coords;
2173
  }
@@ -2183,7 +2515,7 @@ window.CodeMirror = (function() {
2183
  var display = cm.display, pt = paddingTop(display);
2184
  y1 += pt; y2 += pt;
2185
  var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
2186
- var docBottom = cm.view.doc.height + 2 * pt;
2187
  var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
2188
  if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1);
2189
  else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen;
@@ -2201,13 +2533,24 @@ window.CodeMirror = (function() {
2201
  return result;
2202
  }
2203
 
 
 
 
 
 
 
 
 
 
 
 
2204
  // API UTILITIES
2205
 
2206
  function indentLine(cm, n, how, aggressive) {
2207
- var doc = cm.view.doc;
2208
  if (!how) how = "add";
2209
  if (how == "smart") {
2210
- if (!cm.view.mode.indent) how = "prev";
2211
  else var state = getStateBefore(cm, n);
2212
  }
2213
 
@@ -2215,18 +2558,20 @@ window.CodeMirror = (function() {
2215
  var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
2216
  var curSpaceString = line.text.match(/^\s*/)[0], indentation;
2217
  if (how == "smart") {
2218
- indentation = cm.view.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
2219
  if (indentation == Pass) {
2220
  if (!aggressive) return;
2221
  how = "prev";
2222
  }
2223
  }
2224
  if (how == "prev") {
2225
- if (n) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
2226
  else indentation = 0;
 
 
 
 
2227
  }
2228
- else if (how == "add") indentation = curSpace + cm.options.indentUnit;
2229
- else if (how == "subtract") indentation = curSpace - cm.options.indentUnit;
2230
  indentation = Math.max(0, indentation);
2231
 
2232
  var indentString = "", pos = 0;
@@ -2235,12 +2580,12 @@ window.CodeMirror = (function() {
2235
  if (pos < indentation) indentString += spaceStr(indentation - pos);
2236
 
2237
  if (indentString != curSpaceString)
2238
- replaceRange(cm, indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length}, "input");
2239
  line.stateAfter = null;
2240
  }
2241
 
2242
  function changeLine(cm, handle, op) {
2243
- var no = handle, line = handle, doc = cm.view.doc;
2244
  if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
2245
  else no = lineNo(handle);
2246
  if (no == null) return null;
@@ -2249,12 +2594,13 @@ window.CodeMirror = (function() {
2249
  return line;
2250
  }
2251
 
2252
- function findPosH(cm, dir, unit, visually) {
2253
- var doc = cm.view.doc, end = cm.view.sel.head, line = end.line, ch = end.ch;
2254
  var lineObj = getLine(doc, line);
 
2255
  function findNextLine() {
2256
  var l = line + dir;
2257
- if (l < 0 || l == doc.size) return false;
2258
  line = l;
2259
  return lineObj = getLine(doc, l);
2260
  }
@@ -2264,22 +2610,50 @@ window.CodeMirror = (function() {
2264
  if (!boundToLine && findNextLine()) {
2265
  if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
2266
  else ch = dir < 0 ? lineObj.text.length : 0;
2267
- } else return false;
2268
  } else ch = next;
2269
  return true;
2270
  }
 
2271
  if (unit == "char") moveOnce();
2272
  else if (unit == "column") moveOnce(true);
2273
- else if (unit == "word") {
2274
- var sawWord = false;
2275
- for (;;) {
2276
- if (dir < 0) if (!moveOnce()) break;
2277
- if (isWordChar(lineObj.text.charAt(ch))) sawWord = true;
2278
- else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;}
2279
- if (dir > 0) if (!moveOnce()) break;
 
 
 
 
 
 
 
 
2280
  }
2281
  }
2282
- return skipAtomic(cm, {line: line, ch: ch}, dir, true);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2283
  }
2284
 
2285
  function findWordAt(line, pos) {
@@ -2287,17 +2661,17 @@ window.CodeMirror = (function() {
2287
  if (line) {
2288
  if (pos.after === false || end == line.length) --start; else ++end;
2289
  var startChar = line.charAt(start);
2290
- var check = isWordChar(startChar) ? isWordChar :
2291
- /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} :
2292
- function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
2293
  while (start > 0 && check(line.charAt(start - 1))) --start;
2294
  while (end < line.length && check(line.charAt(end))) ++end;
2295
  }
2296
- return {from: {line: pos.line, ch: start}, to: {line: pos.line, ch: end}};
2297
  }
2298
 
2299
  function selectLine(cm, line) {
2300
- extendSelection(cm, {line: line, ch: 0}, clipPos(cm.view.doc, {line: line + 1, ch: 0}));
2301
  }
2302
 
2303
  // PROTOTYPE
@@ -2306,24 +2680,6 @@ window.CodeMirror = (function() {
2306
  // 'wrap f in an operation, performed on its `this` parameter'
2307
 
2308
  CodeMirror.prototype = {
2309
- getValue: function(lineSep) {
2310
- var text = [], doc = this.view.doc;
2311
- doc.iter(0, doc.size, function(line) { text.push(line.text); });
2312
- return text.join(lineSep || "\n");
2313
- },
2314
-
2315
- setValue: operation(null, function(code) {
2316
- var doc = this.view.doc, top = {line: 0, ch: 0}, lastLen = getLine(doc, doc.size-1).text.length;
2317
- updateDocInner(this, top, {line: doc.size - 1, ch: lastLen}, splitLines(code), top, top, "setValue");
2318
- }),
2319
-
2320
- getSelection: function(lineSep) { return this.getRange(this.view.sel.from, this.view.sel.to, lineSep); },
2321
-
2322
- replaceSelection: operation(null, function(code, collapse, origin) {
2323
- var sel = this.view.sel;
2324
- updateDoc(this, sel.from, sel.to, splitLines(code), collapse || "around", origin);
2325
- }),
2326
-
2327
  focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
2328
 
2329
  setOption: function(option, value) {
@@ -2335,15 +2691,13 @@ window.CodeMirror = (function() {
2335
  },
2336
 
2337
  getOption: function(option) {return this.options[option];},
 
2338
 
2339
- getMode: function() {return this.view.mode;},
2340
-
2341
- addKeyMap: function(map) {
2342
- this.view.keyMaps.push(map);
2343
  },
2344
-
2345
  removeKeyMap: function(map) {
2346
- var maps = this.view.keyMaps;
2347
  for (var i = 0; i < maps.length; ++i)
2348
  if ((typeof map == "string" ? maps[i].name : maps[i]) == map) {
2349
  maps.splice(i, 1);
@@ -2351,68 +2705,45 @@ window.CodeMirror = (function() {
2351
  }
2352
  },
2353
 
2354
- undo: operation(null, function() {unredoHelper(this, "undo");}),
2355
- redo: operation(null, function() {unredoHelper(this, "redo");}),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2356
 
2357
  indentLine: operation(null, function(n, dir, aggressive) {
2358
  if (typeof dir != "string") {
2359
  if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
2360
  else dir = dir ? "add" : "subtract";
2361
  }
2362
- if (isLine(this.view.doc, n)) indentLine(this, n, dir, aggressive);
2363
  }),
2364
-
2365
  indentSelection: operation(null, function(how) {
2366
- var sel = this.view.sel;
2367
  if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how);
2368
  var e = sel.to.line - (sel.to.ch ? 0 : 1);
2369
  for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how);
2370
  }),
2371
 
2372
- historySize: function() {
2373
- var hist = this.view.history;
2374
- return {undo: hist.done.length, redo: hist.undone.length};
2375
- },
2376
-
2377
- clearHistory: function() {this.view.history = makeHistory();},
2378
-
2379
- markClean: function() {
2380
- this.view.history.dirtyCounter = 0;
2381
- this.view.history.lastOp = this.view.history.lastOrigin = null;
2382
- },
2383
-
2384
- isClean: function () {return this.view.history.dirtyCounter == 0;},
2385
-
2386
- getHistory: function() {
2387
- var hist = this.view.history;
2388
- function cp(arr) {
2389
- for (var i = 0, nw = [], nwelt; i < arr.length; ++i) {
2390
- var set = arr[i];
2391
- nw.push({events: nwelt = [], fromBefore: set.fromBefore, toBefore: set.toBefore,
2392
- fromAfter: set.fromAfter, toAfter: set.toAfter});
2393
- for (var j = 0, elt = set.events; j < elt.length; ++j) {
2394
- var old = [], cur = elt[j];
2395
- nwelt.push({start: cur.start, added: cur.added, old: old});
2396
- for (var k = 0; k < cur.old.length; ++k) old.push(hlText(cur.old[k]));
2397
- }
2398
- }
2399
- return nw;
2400
- }
2401
- return {done: cp(hist.done), undone: cp(hist.undone)};
2402
- },
2403
-
2404
- setHistory: function(histData) {
2405
- var hist = this.view.history = makeHistory();
2406
- hist.done = histData.done;
2407
- hist.undone = histData.undone;
2408
- },
2409
-
2410
  // Fetch the parser token for a given character. Useful for hacks
2411
  // that want to inspect the mode state (say, for completion).
2412
  getTokenAt: function(pos) {
2413
- var doc = this.view.doc;
2414
  pos = clipPos(doc, pos);
2415
- var state = getStateBefore(this, pos.line), mode = this.view.mode;
2416
  var line = getLine(doc, pos.line);
2417
  var stream = new StringStream(line.text, this.options.tabSize);
2418
  while (stream.pos < pos.ch && !stream.eol()) {
@@ -2428,52 +2759,30 @@ window.CodeMirror = (function() {
2428
  },
2429
 
2430
  getStateAfter: function(line) {
2431
- var doc = this.view.doc;
2432
- line = clipLine(doc, line == null ? doc.size - 1: line);
2433
  return getStateBefore(this, line + 1);
2434
  },
2435
 
2436
  cursorCoords: function(start, mode) {
2437
- var pos, sel = this.view.sel;
2438
  if (start == null) pos = sel.head;
2439
- else if (typeof start == "object") pos = clipPos(this.view.doc, start);
2440
  else pos = start ? sel.from : sel.to;
2441
  return cursorCoords(this, pos, mode || "page");
2442
  },
2443
 
2444
  charCoords: function(pos, mode) {
2445
- return charCoords(this, clipPos(this.view.doc, pos), mode || "page");
2446
  },
2447
 
2448
- coordsChar: function(coords) {
2449
- var off = this.display.lineSpace.getBoundingClientRect();
2450
- return coordsChar(this, coords.left - off.left, coords.top - off.top);
2451
  },
2452
 
2453
  defaultTextHeight: function() { return textHeight(this.display); },
2454
-
2455
- markText: operation(null, function(from, to, options) {
2456
- return markText(this, clipPos(this.view.doc, from), clipPos(this.view.doc, to),
2457
- options, "range");
2458
- }),
2459
-
2460
- setBookmark: operation(null, function(pos, widget) {
2461
- pos = clipPos(this.view.doc, pos);
2462
- return markText(this, pos, pos, widget ? {replacedWith: widget} : {}, "bookmark");
2463
- }),
2464
-
2465
- findMarksAt: function(pos) {
2466
- var doc = this.view.doc;
2467
- pos = clipPos(doc, pos);
2468
- var markers = [], spans = getLine(doc, pos.line).markedSpans;
2469
- if (spans) for (var i = 0; i < spans.length; ++i) {
2470
- var span = spans[i];
2471
- if ((span.from == null || span.from <= pos.ch) &&
2472
- (span.to == null || span.to >= pos.ch))
2473
- markers.push(span.marker);
2474
- }
2475
- return markers;
2476
- },
2477
 
2478
  setGutterMarker: operation(null, function(line, gutterID, value) {
2479
  return changeLine(this, line, function(line) {
@@ -2485,8 +2794,8 @@ window.CodeMirror = (function() {
2485
  }),
2486
 
2487
  clearGutter: operation(null, function(gutterID) {
2488
- var i = 0, cm = this, doc = cm.view.doc;
2489
- doc.iter(0, doc.size, function(line) {
2490
  if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
2491
  line.gutterMarkers[gutterID] = null;
2492
  regChange(cm, i, i + 1);
@@ -2522,29 +2831,16 @@ window.CodeMirror = (function() {
2522
  }),
2523
 
2524
  addLineWidget: operation(null, function(handle, node, options) {
2525
- var widget = options || {};
2526
- widget.node = node;
2527
- if (widget.noHScroll) this.display.alignWidgets = true;
2528
- changeLine(this, handle, function(line) {
2529
- (line.widgets || (line.widgets = [])).push(widget);
2530
- widget.line = line;
2531
- return true;
2532
- });
2533
- return widget;
2534
  }),
2535
 
2536
- removeLineWidget: operation(null, function(widget) {
2537
- var ws = widget.line.widgets, no = lineNo(widget.line);
2538
- if (no == null) return;
2539
- for (var i = 0; i < ws.length; ++i) if (ws[i] == widget) ws.splice(i--, 1);
2540
- regChange(this, no, no + 1);
2541
- }),
2542
 
2543
  lineInfo: function(line) {
2544
  if (typeof line == "number") {
2545
- if (!isLine(this.view.doc, line)) return null;
2546
  var n = line;
2547
- line = getLine(this.view.doc, line);
2548
  if (!line) return null;
2549
  } else {
2550
  var n = lineNo(line);
@@ -2559,16 +2855,20 @@ window.CodeMirror = (function() {
2559
 
2560
  addWidget: function(pos, node, scroll, vert, horiz) {
2561
  var display = this.display;
2562
- pos = cursorCoords(this, clipPos(this.view.doc, pos));
2563
- var top = pos.top, left = pos.left;
2564
  node.style.position = "absolute";
2565
  display.sizer.appendChild(node);
2566
- if (vert == "over") top = pos.top;
2567
- else if (vert == "near") {
2568
- var vspace = Math.max(display.wrapper.clientHeight, this.view.doc.height),
 
2569
  hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
2570
- if (pos.bottom + node.offsetHeight > vspace && pos.top > node.offsetHeight)
 
2571
  top = pos.top - node.offsetHeight;
 
 
2572
  if (left + node.offsetWidth > hspace)
2573
  left = hspace - node.offsetWidth;
2574
  }
@@ -2586,146 +2886,71 @@ window.CodeMirror = (function() {
2586
  scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
2587
  },
2588
 
2589
- lineCount: function() {return this.view.doc.size;},
2590
-
2591
- clipPos: function(pos) {return clipPos(this.view.doc, pos);},
2592
-
2593
- getCursor: function(start) {
2594
- var sel = this.view.sel, pos;
2595
- if (start == null || start == "head") pos = sel.head;
2596
- else if (start == "anchor") pos = sel.anchor;
2597
- else if (start == "end" || start === false) pos = sel.to;
2598
- else pos = sel.from;
2599
- return copyPos(pos);
2600
- },
2601
-
2602
- somethingSelected: function() {return !posEq(this.view.sel.from, this.view.sel.to);},
2603
-
2604
- setCursor: operation(null, function(line, ch, extend) {
2605
- var pos = clipPos(this.view.doc, typeof line == "number" ? {line: line, ch: ch || 0} : line);
2606
- if (extend) extendSelection(this, pos);
2607
- else setSelection(this, pos, pos);
2608
- }),
2609
-
2610
- setSelection: operation(null, function(anchor, head) {
2611
- var doc = this.view.doc;
2612
- setSelection(this, clipPos(doc, anchor), clipPos(doc, head || anchor));
2613
- }),
2614
-
2615
- extendSelection: operation(null, function(from, to) {
2616
- var doc = this.view.doc;
2617
- extendSelection(this, clipPos(doc, from), to && clipPos(doc, to));
2618
- }),
2619
-
2620
- setExtending: function(val) {this.view.sel.extend = val;},
2621
-
2622
- getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
2623
-
2624
- getLineHandle: function(line) {
2625
- var doc = this.view.doc;
2626
- if (isLine(doc, line)) return getLine(doc, line);
2627
- },
2628
-
2629
- getLineNumber: function(line) {return lineNo(line);},
2630
-
2631
- setLine: operation(null, function(line, text) {
2632
- if (isLine(this.view.doc, line))
2633
- replaceRange(this, text, {line: line, ch: 0}, {line: line, ch: getLine(this.view.doc, line).text.length});
2634
- }),
2635
-
2636
- removeLine: operation(null, function(line) {
2637
- if (isLine(this.view.doc, line))
2638
- replaceRange(this, "", {line: line, ch: 0}, clipPos(this.view.doc, {line: line+1, ch: 0}));
2639
- }),
2640
-
2641
- replaceRange: operation(null, function(code, from, to) {
2642
- var doc = this.view.doc;
2643
- from = clipPos(doc, from);
2644
- to = to ? clipPos(doc, to) : from;
2645
- return replaceRange(this, code, from, to);
2646
- }),
2647
-
2648
- getRange: function(from, to, lineSep) {
2649
- var doc = this.view.doc;
2650
- from = clipPos(doc, from); to = clipPos(doc, to);
2651
- var l1 = from.line, l2 = to.line;
2652
- if (l1 == l2) return getLine(doc, l1).text.slice(from.ch, to.ch);
2653
- var code = [getLine(doc, l1).text.slice(from.ch)];
2654
- doc.iter(l1 + 1, l2, function(line) { code.push(line.text); });
2655
- code.push(getLine(doc, l2).text.slice(0, to.ch));
2656
- return code.join(lineSep || "\n");
2657
- },
2658
-
2659
  triggerOnKeyDown: operation(null, onKeyDown),
2660
 
2661
  execCommand: function(cmd) {return commands[cmd](this);},
2662
 
2663
- // Stuff used by commands, probably not much use to outside code.
 
 
 
 
 
 
 
 
 
2664
  moveH: operation(null, function(dir, unit) {
2665
- var sel = this.view.sel, pos = dir < 0 ? sel.from : sel.to;
2666
- if (sel.shift || sel.extend || posEq(sel.from, sel.to)) pos = findPosH(this, dir, unit, true);
2667
- extendSelection(this, pos, pos, dir);
 
 
 
2668
  }),
2669
 
2670
  deleteH: operation(null, function(dir, unit) {
2671
- var sel = this.view.sel;
2672
- if (!posEq(sel.from, sel.to)) replaceRange(this, "", sel.from, sel.to, "delete");
2673
- else replaceRange(this, "", sel.from, findPosH(this, dir, unit, false), "delete");
2674
  this.curOp.userSelChange = true;
2675
  }),
2676
 
2677
- moveV: operation(null, function(dir, unit) {
2678
- var view = this.view, doc = view.doc, display = this.display;
2679
- var cur = view.sel.head, pos = cursorCoords(this, cur, "div");
2680
- var x = pos.left, y;
2681
- if (view.goalColumn != null) x = view.goalColumn;
2682
- if (unit == "page") {
2683
- var pageSize = Math.min(display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
2684
- y = pos.top + dir * pageSize;
2685
- } else if (unit == "line") {
2686
- y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
2687
  }
2688
- do {
2689
- var target = coordsChar(this, x, y);
2690
- y += dir * 5;
2691
- } while (target.outside && (dir < 0 ? y > 0 : y < doc.height));
2692
 
2693
- if (unit == "page") display.scrollbarV.scrollTop += charCoords(this, target, "div").top - pos.top;
2694
- extendSelection(this, target, target, dir);
2695
- view.goalColumn = x;
 
 
 
 
 
 
2696
  }),
2697
 
2698
  toggleOverwrite: function() {
2699
- if (this.view.overwrite = !this.view.overwrite)
2700
  this.display.cursor.className += " CodeMirror-overwrite";
2701
  else
2702
  this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", "");
2703
  },
 
2704
 
2705
- posFromIndex: function(off) {
2706
- var lineNo = 0, ch, doc = this.view.doc;
2707
- doc.iter(0, doc.size, function(line) {
2708
- var sz = line.text.length + 1;
2709
- if (sz > off) { ch = off; return true; }
2710
- off -= sz;
2711
- ++lineNo;
2712
- });
2713
- return clipPos(doc, {line: lineNo, ch: ch});
2714
- },
2715
- indexFromPos: function (coords) {
2716
- if (coords.line < 0 || coords.ch < 0) return 0;
2717
- var index = coords.ch;
2718
- this.view.doc.iter(0, coords.line, function (line) {
2719
- index += line.text.length + 1;
2720
- });
2721
- return index;
2722
- },
2723
-
2724
- scrollTo: function(x, y) {
2725
- if (x != null) this.display.scrollbarH.scrollLeft = this.display.scroller.scrollLeft = x;
2726
- if (y != null) this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = y;
2727
- updateDisplay(this, []);
2728
- },
2729
  getScrollInfo: function() {
2730
  var scroller = this.display.scroller, co = scrollerCutOff;
2731
  return {left: scroller.scrollLeft, top: scroller.scrollTop,
@@ -2733,10 +2958,14 @@ window.CodeMirror = (function() {
2733
  clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
2734
  },
2735
 
2736
- scrollIntoView: function(pos) {
2737
- if (typeof pos == "number") pos = {line: pos, ch: 0};
2738
- pos = pos ? clipPos(this.view.doc, pos) : this.view.sel.head;
2739
- scrollPosIntoView(this, pos);
 
 
 
 
2740
  },
2741
 
2742
  setSize: function(width, height) {
@@ -2751,14 +2980,22 @@ window.CodeMirror = (function() {
2751
  on: function(type, f) {on(this, type, f);},
2752
  off: function(type, f) {off(this, type, f);},
2753
 
2754
- operation: function(f){return operation(this, f)();},
2755
 
2756
- refresh: function() {
2757
  clearCaches(this);
2758
- if (this.display.scroller.scrollHeight > this.view.scrollTop)
2759
- this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = this.view.scrollTop;
2760
- updateDisplay(this, true);
2761
- },
 
 
 
 
 
 
 
 
2762
 
2763
  getInputField: function(){return this.display.input;},
2764
  getWrapperElement: function(){return this.display.wrapper;},
@@ -2783,8 +3020,13 @@ window.CodeMirror = (function() {
2783
 
2784
  // These two are, on init, called from the constructor because they
2785
  // have to be initialized before the editor can start at all.
2786
- option("value", "", function(cm, val) {cm.setValue(val);}, true);
2787
- option("mode", null, loadMode, true);
 
 
 
 
 
2788
 
2789
  option("indentUnit", 2, loadMode, true);
2790
  option("indentWithTabs", false);
@@ -2792,9 +3034,10 @@ window.CodeMirror = (function() {
2792
  option("tabSize", 4, function(cm) {
2793
  loadMode(cm);
2794
  clearCaches(cm);
2795
- updateDisplay(cm, true);
2796
  }, true);
2797
  option("electricChars", true);
 
2798
 
2799
  option("theme", "default", function(cm) {
2800
  themeChanged(cm);
@@ -2811,6 +3054,10 @@ window.CodeMirror = (function() {
2811
  setGuttersForLineNumbers(cm.options);
2812
  guttersChanged(cm);
2813
  }, true);
 
 
 
 
2814
  option("lineNumbers", false, function(cm) {
2815
  setGuttersForLineNumbers(cm.options);
2816
  guttersChanged(cm);
@@ -2818,7 +3065,7 @@ window.CodeMirror = (function() {
2818
  option("firstLineNumber", 1, guttersChanged, true);
2819
  option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
2820
  option("showCursorWhenSelecting", false, updateSelection, true);
2821
-
2822
  option("readOnly", false, function(cm, val) {
2823
  if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
2824
  else if (!val) resetInput(cm, true);
@@ -2831,7 +3078,7 @@ window.CodeMirror = (function() {
2831
  option("workDelay", 100);
2832
  option("flattenSpans", true);
2833
  option("pollInterval", 100);
2834
- option("undoDepth", 40);
2835
  option("viewportMargin", 10, function(cm){cm.refresh();}, true);
2836
 
2837
  option("tabindex", null, function(cm, val) {
@@ -2867,7 +3114,7 @@ window.CodeMirror = (function() {
2867
  };
2868
 
2869
  CodeMirror.getMode = function(options, spec) {
2870
- var spec = CodeMirror.resolveMode(spec);
2871
  var mfactory = modes[spec.name];
2872
  if (!mfactory) return CodeMirror.getMode(options, "text/plain");
2873
  var modeObj = mfactory(options, spec);
@@ -2891,8 +3138,7 @@ window.CodeMirror = (function() {
2891
  var modeExtensions = CodeMirror.modeExtensions = {};
2892
  CodeMirror.extendMode = function(mode, properties) {
2893
  var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
2894
- for (var prop in properties) if (properties.hasOwnProperty(prop))
2895
- exts[prop] = properties[prop];
2896
  };
2897
 
2898
  // EXTENSIONS
@@ -2940,21 +3186,21 @@ window.CodeMirror = (function() {
2940
  // STANDARD COMMANDS
2941
 
2942
  var commands = CodeMirror.commands = {
2943
- selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});},
2944
  killLine: function(cm) {
2945
  var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
2946
  if (!sel && cm.getLine(from.line).length == from.ch)
2947
- cm.replaceRange("", from, {line: from.line + 1, ch: 0}, "delete");
2948
- else cm.replaceRange("", from, sel ? to : {line: from.line}, "delete");
2949
  },
2950
  deleteLine: function(cm) {
2951
  var l = cm.getCursor().line;
2952
- cm.replaceRange("", {line: l, ch: 0}, {line: l}, "delete");
2953
  },
2954
  undo: function(cm) {cm.undo();},
2955
  redo: function(cm) {cm.redo();},
2956
- goDocStart: function(cm) {cm.extendSelection({line: 0, ch: 0});},
2957
- goDocEnd: function(cm) {cm.extendSelection({line: cm.lineCount() - 1});},
2958
  goLineStart: function(cm) {
2959
  cm.extendSelection(lineStart(cm, cm.getCursor().line));
2960
  },
@@ -2965,12 +3211,20 @@ window.CodeMirror = (function() {
2965
  if (!order || order[0].level == 0) {
2966
  var firstNonWS = Math.max(0, line.text.search(/\S/));
2967
  var inWS = cur.line == start.line && cur.ch <= firstNonWS && cur.ch;
2968
- cm.extendSelection({line: start.line, ch: inWS ? 0 : firstNonWS});
2969
  } else cm.extendSelection(start);
2970
  },
2971
  goLineEnd: function(cm) {
2972
  cm.extendSelection(lineEnd(cm, cm.getCursor().line));
2973
  },
 
 
 
 
 
 
 
 
2974
  goLineUp: function(cm) {cm.moveV(-1, "line");},
2975
  goLineDown: function(cm) {cm.moveV(1, "line");},
2976
  goPageUp: function(cm) {cm.moveV(-1, "page");},
@@ -2980,28 +3234,32 @@ window.CodeMirror = (function() {
2980
  goColumnLeft: function(cm) {cm.moveH(-1, "column");},
2981
  goColumnRight: function(cm) {cm.moveH(1, "column");},
2982
  goWordLeft: function(cm) {cm.moveH(-1, "word");},
 
 
2983
  goWordRight: function(cm) {cm.moveH(1, "word");},
2984
  delCharBefore: function(cm) {cm.deleteH(-1, "char");},
2985
  delCharAfter: function(cm) {cm.deleteH(1, "char");},
2986
  delWordBefore: function(cm) {cm.deleteH(-1, "word");},
2987
  delWordAfter: function(cm) {cm.deleteH(1, "word");},
 
 
2988
  indentAuto: function(cm) {cm.indentSelection("smart");},
2989
  indentMore: function(cm) {cm.indentSelection("add");},
2990
  indentLess: function(cm) {cm.indentSelection("subtract");},
2991
- insertTab: function(cm) {cm.replaceSelection("\t", "end", "input");},
2992
  defaultTab: function(cm) {
2993
  if (cm.somethingSelected()) cm.indentSelection("add");
2994
- else cm.replaceSelection("\t", "end", "input");
2995
  },
2996
  transposeChars: function(cm) {
2997
  var cur = cm.getCursor(), line = cm.getLine(cur.line);
2998
  if (cur.ch > 0 && cur.ch < line.length - 1)
2999
  cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
3000
- {line: cur.line, ch: cur.ch - 1}, {line: cur.line, ch: cur.ch + 1});
3001
  },
3002
  newlineAndIndent: function(cm) {
3003
  operation(cm, function() {
3004
- cm.replaceSelection("\n", "end", "input");
3005
  cm.indentLine(cm.getCursor().line, null, true);
3006
  })();
3007
  },
@@ -3022,17 +3280,17 @@ window.CodeMirror = (function() {
3022
  keyMap.pcDefault = {
3023
  "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
3024
  "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
3025
- "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
3026
- "Ctrl-Backspace": "delWordBefore", "Ctrl-Delete": "delWordAfter", "Ctrl-S": "save", "Ctrl-F": "find",
3027
  "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
3028
  "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
3029
  fallthrough: "basic"
3030
  };
3031
  keyMap.macDefault = {
3032
  "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
3033
- "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft",
3034
- "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordBefore",
3035
- "Ctrl-Alt-Backspace": "delWordAfter", "Alt-Delete": "delWordAfter", "Cmd-S": "save", "Cmd-F": "find",
3036
  "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
3037
  "Cmd-[": "indentLess", "Cmd-]": "indentMore",
3038
  fallthrough: ["basic", "emacsy"]
@@ -3052,37 +3310,46 @@ window.CodeMirror = (function() {
3052
  else return val;
3053
  }
3054
 
3055
- function lookupKey(name, maps, handle, stop) {
3056
  function lookup(map) {
3057
  map = getKeyMap(map);
3058
  var found = map[name];
3059
- if (found === false) {
3060
- if (stop) stop();
3061
- return true;
3062
- }
3063
  if (found != null && handle(found)) return true;
3064
- if (map.nofallthrough) {
3065
- if (stop) stop();
3066
- return true;
3067
- }
3068
  var fallthrough = map.fallthrough;
3069
  if (fallthrough == null) return false;
3070
  if (Object.prototype.toString.call(fallthrough) != "[object Array]")
3071
  return lookup(fallthrough);
3072
  for (var i = 0, e = fallthrough.length; i < e; ++i) {
3073
- if (lookup(fallthrough[i])) return true;
 
3074
  }
3075
  return false;
3076
  }
3077
 
3078
- for (var i = 0; i < maps.length; ++i)
3079
- if (lookup(maps[i])) return true;
 
 
3080
  }
3081
  function isModifierKey(event) {
3082
- var name = keyNames[e_prop(event, "keyCode")];
3083
  return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
3084
  }
 
 
 
 
 
 
 
 
 
 
3085
  CodeMirror.isModifierKey = isModifierKey;
 
3086
 
3087
  // FROMTEXTAREA
3088
 
@@ -3091,6 +3358,8 @@ window.CodeMirror = (function() {
3091
  options.value = textarea.value;
3092
  if (!options.tabindex && textarea.tabindex)
3093
  options.tabindex = textarea.tabindex;
 
 
3094
  // Set autofocus to true if this textarea is focused, or if it has
3095
  // autofocus and no other element is focused.
3096
  if (options.autofocus == null) {
@@ -3103,17 +3372,19 @@ window.CodeMirror = (function() {
3103
 
3104
  function save() {textarea.value = cm.getValue();}
3105
  if (textarea.form) {
3106
- // Deplorable hack to make the submit method do the right thing.
3107
  on(textarea.form, "submit", save);
3108
- var form = textarea.form, realSubmit = form.submit;
3109
- try {
3110
- form.submit = function wrappedSubmit() {
3111
- save();
3112
- form.submit = realSubmit;
3113
- form.submit();
3114
- form.submit = wrappedSubmit;
3115
- };
3116
- } catch(e) {}
 
 
 
3117
  }
3118
 
3119
  textarea.style.display = "none";
@@ -3145,6 +3416,7 @@ window.CodeMirror = (function() {
3145
  this.pos = this.start = 0;
3146
  this.string = string;
3147
  this.tabSize = tabSize || 8;
 
3148
  }
3149
 
3150
  StringStream.prototype = {
@@ -3177,12 +3449,19 @@ window.CodeMirror = (function() {
3177
  if (found > -1) {this.pos = found; return true;}
3178
  },
3179
  backUp: function(n) {this.pos -= n;},
3180
- column: function() {return countColumn(this.string, this.start, this.tabSize);},
 
 
 
 
 
 
3181
  indentation: function() {return countColumn(this.string, null, this.tabSize);},
3182
  match: function(pattern, consume, caseInsensitive) {
3183
  if (typeof pattern == "string") {
3184
  var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
3185
- if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
 
3186
  if (consume !== false) this.pos += pattern.length;
3187
  return true;
3188
  }
@@ -3199,15 +3478,17 @@ window.CodeMirror = (function() {
3199
 
3200
  // TEXTMARKERS
3201
 
3202
- function TextMarker(cm, type) {
3203
  this.lines = [];
3204
  this.type = type;
3205
- this.cm = cm;
3206
  }
 
3207
 
3208
  TextMarker.prototype.clear = function() {
3209
  if (this.explicitlyCleared) return;
3210
- startOperation(this.cm);
 
3211
  var min = null, max = null;
3212
  for (var i = 0; i < this.lines.length; ++i) {
3213
  var line = this.lines[i];
@@ -3216,18 +3497,27 @@ window.CodeMirror = (function() {
3216
  line.markedSpans = removeMarkedSpan(line.markedSpans, span);
3217
  if (span.from != null)
3218
  min = lineNo(line);
3219
- else if (this.collapsed && !lineIsHidden(line))
3220
- updateLineHeight(line, textHeight(this.cm.display));
 
 
 
 
 
 
 
 
3221
  }
3222
- if (min != null) regChange(this.cm, min, max + 1);
 
3223
  this.lines.length = 0;
3224
  this.explicitlyCleared = true;
3225
- if (this.collapsed && this.cm.view.cantEdit) {
3226
- this.cm.view.cantEdit = false;
3227
- reCheckSelection(this.cm);
3228
  }
3229
- endOperation(this.cm);
3230
- signalLater(this.cm, this, "clear");
3231
  };
3232
 
3233
  TextMarker.prototype.find = function() {
@@ -3237,28 +3527,58 @@ window.CodeMirror = (function() {
3237
  var span = getMarkedSpanFor(line.markedSpans, this);
3238
  if (span.from != null || span.to != null) {
3239
  var found = lineNo(line);
3240
- if (span.from != null) from = {line: found, ch: span.from};
3241
- if (span.to != null) to = {line: found, ch: span.to};
3242
  }
3243
  }
3244
  if (this.type == "bookmark") return from;
3245
  return from && {from: from, to: to};
3246
  };
3247
 
3248
- function markText(cm, from, to, options, type) {
3249
- var doc = cm.view.doc;
3250
- var marker = new TextMarker(cm, type);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3251
  if (type == "range" && !posLess(from, to)) return marker;
3252
- if (options) for (var opt in options) if (options.hasOwnProperty(opt))
3253
- marker[opt] = options[opt];
3254
  if (marker.replacedWith) {
3255
  marker.collapsed = true;
3256
  marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
3257
  }
3258
  if (marker.collapsed) sawCollapsedSpans = true;
3259
 
3260
- var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd;
3261
  doc.iter(curLine, to.line + 1, function(line) {
 
 
3262
  var span = {from: null, to: null, marker: marker};
3263
  size += line.text.length;
3264
  if (curLine == from.line) {span.from = from.ch; size -= from.ch;}
@@ -3269,15 +3589,18 @@ window.CodeMirror = (function() {
3269
  else updateLineHeight(line, 0);
3270
  }
3271
  addMarkedSpan(line, span);
3272
- if (marker.collapsed && curLine == from.line && lineIsHidden(line))
3273
- updateLineHeight(line, 0);
3274
  ++curLine;
3275
  });
 
 
 
 
 
3276
 
3277
  if (marker.readOnly) {
3278
  sawReadOnlySpans = true;
3279
- if (cm.view.history.done.length || cm.view.history.undone.length)
3280
- cm.clearHistory();
3281
  }
3282
  if (marker.collapsed) {
3283
  if (collapsedAtStart != collapsedAtEnd)
@@ -3285,12 +3608,58 @@ window.CodeMirror = (function() {
3285
  marker.size = size;
3286
  marker.atomic = true;
3287
  }
3288
- if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed)
3289
- regChange(cm, from.line, to.line + 1);
3290
- if (marker.atomic) reCheckSelection(cm);
 
 
 
3291
  return marker;
3292
  }
3293
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3294
  // TEXTMARKER SPANS
3295
 
3296
  function getMarkedSpanFor(spans, marker) {
@@ -3306,14 +3675,14 @@ window.CodeMirror = (function() {
3306
  }
3307
  function addMarkedSpan(line, span) {
3308
  line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
3309
- span.marker.lines.push(line);
3310
  }
3311
 
3312
- function markedSpansBefore(old, startCh) {
3313
  if (old) for (var i = 0, nw; i < old.length; ++i) {
3314
  var span = old[i], marker = span.marker;
3315
  var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
3316
- if (startsBefore || marker.type == "bookmark" && span.from == startCh) {
3317
  var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
3318
  (nw || (nw = [])).push({from: span.from,
3319
  to: endsAfter ? null : span.to,
@@ -3323,11 +3692,11 @@ window.CodeMirror = (function() {
3323
  return nw;
3324
  }
3325
 
3326
- function markedSpansAfter(old, startCh, endCh) {
3327
  if (old) for (var i = 0, nw; i < old.length; ++i) {
3328
  var span = old[i], marker = span.marker;
3329
  var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
3330
- if (endsAfter || marker.type == "bookmark" && span.from == endCh && span.from != startCh) {
3331
  var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
3332
  (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
3333
  to: span.to == null ? null : span.to - endCh,
@@ -3337,14 +3706,18 @@ window.CodeMirror = (function() {
3337
  return nw;
3338
  }
3339
 
3340
- function updateMarkedSpans(oldFirst, oldLast, startCh, endCh, newText) {
3341
- if (!oldFirst && !oldLast) return newText;
 
 
 
 
3342
  // Get the spans that 'stick out' on both sides
3343
- var first = markedSpansBefore(oldFirst, startCh);
3344
- var last = markedSpansAfter(oldLast, startCh, endCh);
3345
 
3346
  // Next, merge those two ends
3347
- var sameLine = newText.length == 1, offset = lst(newText).length + (sameLine ? startCh : 0);
3348
  if (first) {
3349
  // Fix up .to properties of first
3350
  for (var i = 0; i < first.length; ++i) {
@@ -3374,21 +3747,43 @@ window.CodeMirror = (function() {
3374
  }
3375
  }
3376
 
3377
- var newMarkers = [newHL(newText[0], first)];
3378
  if (!sameLine) {
3379
  // Fill gap with whole-line-spans
3380
- var gap = newText.length - 2, gapMarkers;
3381
  if (gap > 0 && first)
3382
  for (var i = 0; i < first.length; ++i)
3383
  if (first[i].to == null)
3384
  (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
3385
  for (var i = 0; i < gap; ++i)
3386
- newMarkers.push(newHL(newText[i+1], gapMarkers));
3387
- newMarkers.push(newHL(lst(newText), last));
3388
  }
3389
  return newMarkers;
3390
  }
3391
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3392
  function removeReadOnlyRanges(doc, from, to) {
3393
  var markers = null;
3394
  doc.iter(from.line, to.line + 1, function(line) {
@@ -3401,13 +3796,15 @@ window.CodeMirror = (function() {
3401
  if (!markers) return null;
3402
  var parts = [{from: from, to: to}];
3403
  for (var i = 0; i < markers.length; ++i) {
3404
- var m = markers[i].find();
3405
  for (var j = 0; j < parts.length; ++j) {
3406
  var p = parts[j];
3407
- if (!posLess(m.from, p.to) || posLess(m.to, p.from)) continue;
3408
  var newParts = [j, 1];
3409
- if (posLess(p.from, m.from)) newParts.push({from: p.from, to: m.from});
3410
- if (posLess(m.to, p.to)) newParts.push({from: m.to, to: p.to});
 
 
3411
  parts.splice.apply(parts, newParts);
3412
  j += newParts.length - 1;
3413
  }
@@ -3437,80 +3834,124 @@ window.CodeMirror = (function() {
3437
  return line;
3438
  }
3439
 
3440
- function lineIsHidden(line) {
3441
  var sps = sawCollapsedSpans && line.markedSpans;
3442
  if (sps) for (var sp, i = 0; i < sps.length; ++i) {
3443
  sp = sps[i];
3444
  if (!sp.marker.collapsed) continue;
3445
  if (sp.from == null) return true;
3446
- if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(line, sp))
3447
  return true;
3448
  }
3449
  }
3450
- window.lineIsHidden = lineIsHidden;
3451
- function lineIsHiddenInner(line, span) {
3452
- if (span.to == null || span.marker.inclusiveRight && span.to == line.text.length)
 
 
 
3453
  return true;
3454
  for (var sp, i = 0; i < line.markedSpans.length; ++i) {
3455
  sp = line.markedSpans[i];
3456
  if (sp.marker.collapsed && sp.from == span.to &&
3457
  (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
3458
- lineIsHiddenInner(line, sp)) return true;
3459
  }
3460
  }
3461
 
3462
- // hl stands for history-line, a data structure that can be either a
3463
- // string (line without markers) or a {text, markedSpans} object.
3464
- function hlText(val) { return typeof val == "string" ? val : val.text; }
3465
- function hlSpans(val) {
3466
- if (typeof val == "string") return null;
3467
- var spans = val.markedSpans, out = null;
3468
- for (var i = 0; i < spans.length; ++i) {
3469
- if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
3470
- else if (out) out.push(spans[i]);
3471
- }
3472
- return !out ? spans : out.length ? out : null;
3473
- }
3474
- function newHL(text, spans) { return spans ? {text: text, markedSpans: spans} : text; }
3475
-
3476
  function detachMarkedSpans(line) {
3477
  var spans = line.markedSpans;
3478
  if (!spans) return;
3479
- for (var i = 0; i < spans.length; ++i) {
3480
- var lines = spans[i].marker.lines;
3481
- var ix = indexOf(lines, line);
3482
- lines.splice(ix, 1);
3483
- }
3484
  line.markedSpans = null;
3485
  }
3486
 
3487
  function attachMarkedSpans(line, spans) {
3488
  if (!spans) return;
3489
  for (var i = 0; i < spans.length; ++i)
3490
- spans[i].marker.lines.push(line);
3491
  line.markedSpans = spans;
3492
  }
3493
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3494
  // LINE DATA STRUCTURE
3495
 
3496
  // Line objects. These hold state related to a line, including
3497
  // highlighting info (the styles array).
3498
- function makeLine(text, markedSpans, height) {
3499
- var line = {text: text, height: height};
3500
  attachMarkedSpans(line, markedSpans);
3501
- if (lineIsHidden(line)) line.height = 0;
3502
  return line;
3503
  }
3504
 
3505
- function updateLine(cm, line, text, markedSpans) {
3506
  line.text = text;
3507
- line.stateAfter = line.styles = null;
 
3508
  if (line.order != null) line.order = null;
3509
  detachMarkedSpans(line);
3510
  attachMarkedSpans(line, markedSpans);
3511
- if (lineIsHidden(line)) line.height = 0;
3512
- else if (!line.height) line.height = textHeight(cm.display);
3513
- signalLater(cm, line, "change");
3514
  }
3515
 
3516
  function cleanUpLine(line) {
@@ -3521,37 +3962,79 @@ window.CodeMirror = (function() {
3521
  // Run the given mode's parser over a line, update the styles
3522
  // array, which contains alternating fragments of text and CSS
3523
  // classes.
3524
- function highlightLine(cm, line, state) {
3525
- var mode = cm.view.mode, flattenSpans = cm.options.flattenSpans;
3526
- var changed = !line.styles, pos = 0, curText = "", curStyle = null;
3527
- var stream = new StringStream(line.text, cm.options.tabSize), st = line.styles || (line.styles = []);
3528
- if (line.text == "" && mode.blankLine) mode.blankLine(state);
 
3529
  while (!stream.eol()) {
3530
- var style = mode.token(stream, state), substr = stream.current();
 
 
 
 
 
 
 
3531
  stream.start = stream.pos;
3532
  if (!flattenSpans || curStyle != style) {
3533
- if (curText) {
3534
- changed = changed || pos >= st.length || curText != st[pos] || curStyle != st[pos+1];
3535
- st[pos++] = curText; st[pos++] = curStyle;
3536
- }
3537
  curText = substr; curStyle = style;
3538
  } else curText = curText + substr;
3539
- // Give up when line is ridiculously long
3540
- if (stream.pos > 5000) break;
3541
  }
3542
- if (curText) {
3543
- changed = changed || pos >= st.length || curText != st[pos] || curStyle != st[pos+1];
3544
- st[pos++] = curText; st[pos++] = curStyle;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3545
  }
3546
- if (stream.pos > 5000) { st[pos++] = line.text.slice(stream.pos); st[pos++] = null; }
3547
- if (pos != st.length) { st.length = pos; changed = true; }
3548
- return changed;
 
 
 
 
 
3549
  }
3550
 
3551
  // Lightweight form of highlight -- proceed over this line and
3552
  // update state, but don't save a style array.
3553
  function processLine(cm, line, state) {
3554
- var mode = cm.view.mode;
3555
  var stream = new StringStream(line.text, cm.options.tabSize);
3556
  if (line.text == "" && mode.blankLine) mode.blankLine(state);
3557
  while (!stream.eol() && stream.pos <= 5000) {
@@ -3571,7 +4054,7 @@ window.CodeMirror = (function() {
3571
  var merged, line = realLine, lineBefore, sawBefore, simple = true;
3572
  while (merged = collapsedSpanAtStart(line)) {
3573
  simple = false;
3574
- line = getLine(cm.view.doc, merged.find().from.line);
3575
  if (!lineBefore) lineBefore = line;
3576
  }
3577
 
@@ -3580,32 +4063,48 @@ window.CodeMirror = (function() {
3580
  if (line.textClass) builder.pre.className = line.textClass;
3581
 
3582
  do {
3583
- if (!line.styles)
3584
- highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
3585
  builder.measure = line == realLine && measure;
3586
  builder.pos = 0;
3587
  builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
 
 
3588
  if (measure && sawBefore && line != realLine && !builder.addedOne) {
3589
  measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
3590
  builder.addedOne = true;
3591
  }
3592
- var next = insertLineContent(line, builder);
3593
  sawBefore = line == lineBefore;
3594
  if (next) {
3595
- line = getLine(cm.view.doc, next.to.line);
3596
  simple = false;
3597
  }
3598
  } while (next);
3599
 
3600
  if (measure && !builder.addedOne)
3601
  measure[0] = builder.pre.appendChild(simple ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
3602
- if (!builder.pre.firstChild && !lineIsHidden(realLine))
3603
  builder.pre.appendChild(document.createTextNode("\u00a0"));
3604
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3605
  return builder.pre;
3606
  }
3607
 
3608
- var tokenSpecialChars = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g;
3609
  function buildToken(builder, text, style, startStyle, endStyle) {
3610
  if (!text) return;
3611
  if (!tokenSpecialChars.test(text)) {
@@ -3645,18 +4144,42 @@ window.CodeMirror = (function() {
3645
  }
3646
 
3647
  function buildTokenMeasure(builder, text, style, startStyle, endStyle) {
 
3648
  for (var i = 0; i < text.length; ++i) {
3649
- if (i && i < text.length - 1 &&
3650
- builder.cm.options.lineWrapping &&
3651
- spanAffectsWrapping.test(text.slice(i - 1, i + 1)))
 
 
 
3652
  builder.pre.appendChild(elt("wbr"));
3653
- builder.measure[builder.pos++] =
3654
- buildToken(builder, text.charAt(i), style,
3655
- i == 0 && startStyle, i == text.length - 1 && endStyle);
 
 
 
 
 
 
 
 
3656
  }
3657
  if (text.length) builder.addedOne = true;
3658
  }
3659
 
 
 
 
 
 
 
 
 
 
 
 
 
3660
  function buildCollapsedSpan(builder, size, widget) {
3661
  if (widget) {
3662
  if (!builder.display) widget = widget.cloneNode(true);
@@ -3671,16 +4194,16 @@ window.CodeMirror = (function() {
3671
 
3672
  // Outputs a number of spans to make up a line, taking highlighting
3673
  // and marked text into account.
3674
- function insertLineContent(line, builder) {
3675
- var st = line.styles, spans = line.markedSpans;
3676
  if (!spans) {
3677
- for (var i = 0; i < st.length; i+=2)
3678
- builder.addToken(builder, st[i], styleToClass(st[i+1]));
3679
  return;
3680
  }
3681
 
3682
  var allText = line.text, len = allText.length;
3683
- var pos = 0, i = 0, text = "", style;
3684
  var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed;
3685
  for (;;) {
3686
  if (nextChange == pos) { // Update current marker set
@@ -3717,20 +4240,66 @@ window.CodeMirror = (function() {
3717
  var end = pos + text.length;
3718
  if (!collapsed) {
3719
  var tokenText = end > upto ? text.slice(0, upto - pos) : text;
3720
- builder.addToken(builder, tokenText, style + spanStyle,
3721
  spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "");
3722
  }
3723
  if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
3724
  pos = end;
3725
  spanStartStyle = "";
3726
  }
3727
- text = st[i++]; style = styleToClass(st[i++]);
3728
  }
3729
  }
3730
  }
3731
 
3732
  // DOCUMENT DATA STRUCTURE
3733
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3734
  function LeafChunk(lines) {
3735
  this.lines = lines;
3736
  this.parent = null;
@@ -3743,19 +4312,19 @@ window.CodeMirror = (function() {
3743
 
3744
  LeafChunk.prototype = {
3745
  chunkSize: function() { return this.lines.length; },
3746
- remove: function(at, n, cm) {
3747
  for (var i = at, e = at + n; i < e; ++i) {
3748
  var line = this.lines[i];
3749
  this.height -= line.height;
3750
  cleanUpLine(line);
3751
- signalLater(cm, line, "delete");
3752
  }
3753
  this.lines.splice(at, n);
3754
  },
3755
  collapse: function(lines) {
3756
  lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
3757
  },
3758
- insertHeight: function(at, lines, height) {
3759
  this.height += height;
3760
  this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
3761
  for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
@@ -3781,13 +4350,13 @@ window.CodeMirror = (function() {
3781
 
3782
  BranchChunk.prototype = {
3783
  chunkSize: function() { return this.size; },
3784
- remove: function(at, n, callbacks) {
3785
  this.size -= n;
3786
  for (var i = 0; i < this.children.length; ++i) {
3787
  var child = this.children[i], sz = child.chunkSize();
3788
  if (at < sz) {
3789
  var rm = Math.min(n, sz - at), oldHeight = child.height;
3790
- child.remove(at, rm, callbacks);
3791
  this.height -= oldHeight - child.height;
3792
  if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
3793
  if ((n -= rm) == 0) break;
@@ -3804,18 +4373,13 @@ window.CodeMirror = (function() {
3804
  collapse: function(lines) {
3805
  for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
3806
  },
3807
- insert: function(at, lines) {
3808
- var height = 0;
3809
- for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
3810
- this.insertHeight(at, lines, height);
3811
- },
3812
- insertHeight: function(at, lines, height) {
3813
  this.size += lines.length;
3814
  this.height += height;
3815
  for (var i = 0, e = this.children.length; i < e; ++i) {
3816
  var child = this.children[i], sz = child.chunkSize();
3817
  if (at <= sz) {
3818
- child.insertHeight(at, lines, height);
3819
  if (child.lines && child.lines.length > 50) {
3820
  while (child.lines.length > 50) {
3821
  var spilled = child.lines.splice(child.lines.length - 25, 25);
@@ -3852,7 +4416,6 @@ window.CodeMirror = (function() {
3852
  } while (me.children.length > 10);
3853
  me.parent.maybeSpill();
3854
  },
3855
- iter: function(from, to, op) { this.iterN(from, to - from, op); },
3856
  iterN: function(at, n, op) {
3857
  for (var i = 0, e = this.children.length; i < e; ++i) {
3858
  var child = this.children[i], sz = child.chunkSize();
@@ -3866,9 +4429,268 @@ window.CodeMirror = (function() {
3866
  }
3867
  };
3868
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3869
  // LINE UTILITIES
3870
 
3871
  function getLine(chunk, n) {
 
3872
  while (!chunk.lines) {
3873
  for (var i = 0;; ++i) {
3874
  var child = chunk.children[i], sz = child.chunkSize();
@@ -3879,6 +4701,23 @@ window.CodeMirror = (function() {
3879
  return chunk.lines[n];
3880
  }
3881
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3882
  function updateLineHeight(line, height) {
3883
  var diff = height - line.height;
3884
  for (var n = line; n; n = n.parent) n.height += diff;
@@ -3893,11 +4732,11 @@ window.CodeMirror = (function() {
3893
  no += chunk.children[i].chunkSize();
3894
  }
3895
  }
3896
- return no;
3897
  }
3898
 
3899
  function lineAtHeight(chunk, h) {
3900
- var n = 0;
3901
  outer: do {
3902
  for (var i = 0, e = chunk.children.length; i < e; ++i) {
3903
  var child = chunk.children[i], ch = child.height;
@@ -3916,7 +4755,7 @@ window.CodeMirror = (function() {
3916
  }
3917
 
3918
  function heightAtLine(cm, lineObj) {
3919
- lineObj = visualLine(cm.view.doc, lineObj);
3920
 
3921
  var h = 0, chunk = lineObj.parent;
3922
  for (var i = 0; i < chunk.lines.length; ++i) {
@@ -3947,7 +4786,7 @@ window.CodeMirror = (function() {
3947
  // Arrays of history events. Doing something adds an event to
3948
  // done and clears undo. Undoing moves events from done to
3949
  // undone, redoing moves them in the other direction.
3950
- done: [], undone: [],
3951
  // Used to track when changes can be merged into a single undo
3952
  // event
3953
  lastTime: 0, lastOp: null, lastOrigin: null,
@@ -3956,47 +4795,151 @@ window.CodeMirror = (function() {
3956
  };
3957
  }
3958
 
3959
- function addChange(cm, start, added, old, origin, fromBefore, toBefore, fromAfter, toAfter) {
3960
- var history = cm.view.history;
3961
- history.undone.length = 0;
3962
- var time = +new Date, cur = lst(history.done);
3963
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3964
  if (cur &&
3965
- (history.lastOp == cm.curOp.id ||
3966
- history.lastOrigin == origin && (origin == "input" || origin == "delete") &&
3967
- history.lastTime > time - 600)) {
3968
  // Merge this change into the last event
3969
- var last = lst(cur.events);
3970
- if (last.start > start + old.length || last.start + last.added < start) {
3971
- // Doesn't intersect with last sub-event, add new sub-event
3972
- cur.events.push({start: start, added: added, old: old});
 
3973
  } else {
3974
- // Patch up the last sub-event
3975
- var startBefore = Math.max(0, last.start - start),
3976
- endAfter = Math.max(0, (start + old.length) - (last.start + last.added));
3977
- for (var i = startBefore; i > 0; --i) last.old.unshift(old[i - 1]);
3978
- for (var i = endAfter; i > 0; --i) last.old.push(old[old.length - i]);
3979
- if (startBefore) last.start = start;
3980
- last.added += added - (old.length - startBefore - endAfter);
3981
  }
3982
- cur.fromAfter = fromAfter; cur.toAfter = toAfter;
3983
  } else {
3984
  // Can not be merged, start a new event.
3985
- cur = {events: [{start: start, added: added, old: old}],
3986
- fromBefore: fromBefore, toBefore: toBefore, fromAfter: fromAfter, toAfter: toAfter};
3987
- history.done.push(cur);
3988
- while (history.done.length > cm.options.undoDepth)
3989
- history.done.shift();
3990
- if (history.dirtyCounter < 0)
3991
- // The user has made a change after undoing past the last clean state.
3992
- // We can never get back to a clean state now until markClean() is called.
3993
- history.dirtyCounter = NaN;
 
3994
  else
3995
- history.dirtyCounter++;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3996
  }
3997
- history.lastTime = time;
3998
- history.lastOp = cm.curOp.id;
3999
- history.lastOrigin = origin;
 
 
 
4000
  }
4001
 
4002
  // EVENT OPERATORS
@@ -4033,13 +4976,6 @@ window.CodeMirror = (function() {
4033
  return b;
4034
  }
4035
 
4036
- // Allow 3rd-party code to override event properties by adding an override
4037
- // object to an event object.
4038
- function e_prop(e, prop) {
4039
- var overridden = e.override && e.override.hasOwnProperty(prop);
4040
- return overridden ? e.override[prop] : e[prop];
4041
- }
4042
-
4043
  // EVENT HANDLING
4044
 
4045
  function on(emitter, type, f) {
@@ -4074,14 +5010,26 @@ window.CodeMirror = (function() {
4074
  for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
4075
  }
4076
 
4077
- function signalLater(cm, emitter, type /*, values...*/) {
 
4078
  var arr = emitter._handlers && emitter._handlers[type];
4079
  if (!arr) return;
4080
- var args = Array.prototype.slice.call(arguments, 3), flist = cm.curOp && cm.curOp.delayedCallbacks;
 
 
 
 
 
4081
  function bnd(f) {return function(){f.apply(null, args);};};
4082
  for (var i = 0; i < arr.length; ++i)
4083
- if (flist) flist.push(bnd(arr[i]));
4084
- else arr[i].apply(null, args);
 
 
 
 
 
 
4085
  }
4086
 
4087
  function hasHandler(emitter, type) {
@@ -4105,12 +5053,12 @@ window.CodeMirror = (function() {
4105
 
4106
  // Counts the column offset in a string, taking tabs into account.
4107
  // Used mostly to find indentation.
4108
- function countColumn(string, end, tabSize) {
4109
  if (end == null) {
4110
  end = string.search(/[^\s\u00a0]/);
4111
  if (end == -1) end = string.length;
4112
  }
4113
- for (var i = 0, n = 0; i < end; ++i) {
4114
  if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
4115
  else ++n;
4116
  }
@@ -4141,6 +5089,20 @@ window.CodeMirror = (function() {
4141
  return -1;
4142
  }
4143
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4144
  function emptyArray(size) {
4145
  for (var a = [], i = 0; i < size; ++i) a.push(undefined);
4146
  return a;
@@ -4158,12 +5120,11 @@ window.CodeMirror = (function() {
4158
  }
4159
 
4160
  function isEmpty(obj) {
4161
- var c = 0;
4162
- for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) ++c;
4163
- return !c;
4164
  }
4165
 
4166
- var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F]/;
4167
 
4168
  // DOM UTILITIES
4169
 
@@ -4177,7 +5138,8 @@ window.CodeMirror = (function() {
4177
  }
4178
 
4179
  function removeChildren(e) {
4180
- e.innerHTML = "";
 
4181
  return e;
4182
  }
4183
 
@@ -4192,6 +5154,11 @@ window.CodeMirror = (function() {
4192
  } else e.textContent = str;
4193
  }
4194
 
 
 
 
 
 
4195
  // FEATURE DETECTION
4196
 
4197
  // Detect drag-and-drop
@@ -4212,8 +5179,8 @@ window.CodeMirror = (function() {
4212
  // various browsers.
4213
  var spanAffectsWrapping = /^$/; // Won't match any two-character string
4214
  if (gecko) spanAffectsWrapping = /$'/;
4215
- else if (safari) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
4216
- else if (chrome) spanAffectsWrapping = /\-[^ \-\.?]|\?[^ \-\.?\]\}:;!'\"\),\/]|[\.!\"#&%\)*+,:;=>\]|\}~][\(\{\[<]|\$'/;
4217
 
4218
  var knownScrollbarWidth;
4219
  function scrollbarWidth(measure) {
@@ -4300,7 +5267,7 @@ window.CodeMirror = (function() {
4300
  if (!order) return f(from, to, "ltr");
4301
  for (var i = 0; i < order.length; ++i) {
4302
  var part = order[i];
4303
- if (part.from < to && part.to > from)
4304
  f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
4305
  }
4306
  }
@@ -4316,20 +5283,20 @@ window.CodeMirror = (function() {
4316
  }
4317
 
4318
  function lineStart(cm, lineN) {
4319
- var line = getLine(cm.view.doc, lineN);
4320
- var visual = visualLine(cm.view.doc, line);
4321
  if (visual != line) lineN = lineNo(visual);
4322
  var order = getOrder(visual);
4323
  var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
4324
- return {line: lineN, ch: ch};
4325
  }
4326
- function lineEnd(cm, lineNo) {
4327
  var merged, line;
4328
- while (merged = collapsedSpanAtEnd(line = getLine(cm.view.doc, lineNo)))
4329
- lineNo = merged.find().to.line;
4330
  var order = getOrder(line);
4331
  var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
4332
- return {line: lineNo, ch: ch};
4333
  }
4334
 
4335
  // This is somewhat involved. It is needed in order to move
@@ -4418,24 +5385,20 @@ window.CodeMirror = (function() {
4418
 
4419
  var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
4420
  var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
 
 
4421
 
4422
- return function charOrdering(str) {
4423
  if (!bidiRE.test(str)) return false;
4424
- var len = str.length, types = [], startType = null;
4425
- for (var i = 0, type; i < len; ++i) {
4426
  types.push(type = charType(str.charCodeAt(i)));
4427
- if (startType == null) {
4428
- if (type == "L") startType = "L";
4429
- else if (type == "R" || type == "r") startType = "R";
4430
- }
4431
- }
4432
- if (startType == null) startType = "L";
4433
 
4434
  // W1. Examine each non-spacing mark (NSM) in the level run, and
4435
  // change the type of the NSM to the type of the previous
4436
  // character. If the NSM is at the start of the level run, it will
4437
  // get the type of sor.
4438
- for (var i = 0, prev = startType; i < len; ++i) {
4439
  var type = types[i];
4440
  if (type == "m") types[i] = prev;
4441
  else prev = type;
@@ -4446,7 +5409,7 @@ window.CodeMirror = (function() {
4446
  // AL is found, change the type of the European number to Arabic
4447
  // number.
4448
  // W3. Change all ALs to R.
4449
- for (var i = 0, cur = startType; i < len; ++i) {
4450
  var type = types[i];
4451
  if (type == "1" && cur == "r") types[i] = "n";
4452
  else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
@@ -4481,7 +5444,7 @@ window.CodeMirror = (function() {
4481
  // W7. Search backwards from each instance of a European number
4482
  // until the first strong type (R, L, or sor) is found. If an L is
4483
  // found, then change the type of the European number to L.
4484
- for (var i = 0, cur = startType; i < len; ++i) {
4485
  var type = types[i];
4486
  if (cur == "L" && type == "1") types[i] = "L";
4487
  else if (isStrong.test(type)) cur = type;
@@ -4496,8 +5459,8 @@ window.CodeMirror = (function() {
4496
  for (var i = 0; i < len; ++i) {
4497
  if (isNeutral.test(types[i])) {
4498
  for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
4499
- var before = (i ? types[i-1] : startType) == "L";
4500
- var after = (end < len - 1 ? types[end] : startType) == "L";
4501
  var replace = before || after ? "L" : "R";
4502
  for (var j = i; j < end; ++j) types[j] = replace;
4503
  i = end - 1;
@@ -4547,7 +5510,7 @@ window.CodeMirror = (function() {
4547
 
4548
  // THE END
4549
 
4550
- CodeMirror.version = "3.0";
4551
 
4552
  return CodeMirror;
4553
  })();
1
+ // CodeMirror version 3.11
2
  //
3
  // CodeMirror is the only global var we claim
4
  window.CodeMirror = (function() {
10
  // bugs and behavior differences.
11
  var gecko = /gecko\/\d/i.test(navigator.userAgent);
12
  var ie = /MSIE \d/.test(navigator.userAgent);
13
+ var ie_lt8 = ie && (document.documentMode == null || document.documentMode < 8);
14
+ var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
15
  var webkit = /WebKit\//.test(navigator.userAgent);
16
  var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
17
  var chrome = /Chrome\//.test(navigator.userAgent);
24
 
25
  var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
26
  // This is woefully incomplete. Suggestions for alternative methods welcome.
27
+ var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
28
  var mac = ios || /Mac/.test(navigator.platform);
29
+ var windows = /windows/i.test(navigator.platform);
30
+
31
+ var opera_version = opera && navigator.userAgent.match(/Version\/(\d*\.\d*)/);
32
+ if (opera_version) opera_version = Number(opera_version[1]);
33
+ // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
34
+ var flipCtrlCmd = mac && (qtwebkit || opera && (opera_version == null || opera_version < 12.11));
35
+ var captureMiddleClick = gecko || (ie && !ie_lt9);
36
 
37
  // Optimize some code when these features are not used
38
  var sawReadOnlySpans = false, sawCollapsedSpans = false;
41
 
42
  function CodeMirror(place, options) {
43
  if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
44
+
45
  this.options = options = options || {};
46
  // Determine effective options based on given values and defaults.
47
  for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt))
48
  options[opt] = defaults[opt];
49
  setGuttersForLineNumbers(options);
50
 
51
+ var docStart = typeof options.value == "string" ? 0 : options.value.first;
52
+ var display = this.display = makeDisplay(place, docStart);
53
  display.wrapper.CodeMirror = this;
54
  updateGutters(this);
55
  if (options.autofocus && !mobile) focusInput(this);
56
 
57
+ this.state = {keyMaps: [],
58
+ overlays: [],
59
+ modeGen: 0,
60
+ overwrite: false, focused: false,
61
+ suppressEdits: false, pasteIncoming: false,
62
+ draggingText: false,
63
+ highlight: new Delayed()};
64
+
65
  themeChanged(this);
66
  if (options.lineWrapping)
67
  this.display.wrapper.className += " CodeMirror-wrap";
68
 
69
+ var doc = options.value;
70
+ if (typeof doc == "string") doc = new Doc(options.value, options.mode);
71
+ operation(this, attachDoc)(this, doc);
72
+
73
  // Override magic textarea content restore that IE sometimes does
74
  // on our hidden textarea on reload
75
  if (ie) setTimeout(bind(resetInput, this, true), 20);
 
76
 
77
  registerEventHandlers(this);
78
  // IE throws unspecified error in certain cases, when
91
 
92
  // DISPLAY CONSTRUCTOR
93
 
94
+ function makeDisplay(place, docStart) {
95
  var d = {};
96
+
97
  var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;");
98
+ if (webkit) input.style.width = "1000px";
99
+ else input.setAttribute("wrap", "off");
100
+ // if border: 0; -- iOS fails to open keyboard (issue #1287)
101
+ if (ios) input.style.border = "1px solid black";
102
+ input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off");
103
+
104
  // Wraps and hides input textarea
105
  d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
106
  // The actual fake scrollbars.
111
  d.lineDiv = elt("div");
112
  d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
113
  // Blinky cursor, and element used to ensure cursor fits at the end of a line
114
+ d.cursor = elt("div", "\u00a0", "CodeMirror-cursor");
115
  // Secondary cursor, shown when on a 'jump' in bi-directional text
116
+ d.otherCursor = elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor");
117
  // Used to measure text size
118
  d.measure = elt("div", null, "CodeMirror-measure");
119
  // Wraps everything that needs to exist inside the vertically-padded coordinate system
124
  // Set to the height of the text, causes scrolling
125
  d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
126
  // D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
127
+ d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerCutOff + "px; width: 1px;");
128
  // Will contain the gutters, if any
129
  d.gutters = elt("div", null, "CodeMirror-gutters");
130
  d.lineGutter = null;
149
  else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = "18px";
150
 
151
  // Current visible range (may be bigger than the view window).
152
+ d.viewOffset = d.lastSizeC = 0;
153
+ d.showingFrom = d.showingTo = docStart;
154
 
155
  // Used to only resize the line number gutter when necessary (when
156
  // the amount of lines crosses a boundary that makes its width change)
177
  // string instead of the (large) selection.
178
  d.inaccurateSelection = false;
179
 
180
+ // Tracks the maximum line length so that the horizontal scrollbar
181
+ // can be kept static when scrolling.
182
+ d.maxLine = null;
183
+ d.maxLineLength = 0;
184
+ d.maxLineChanged = false;
 
185
 
186
+ // Used for measuring wheel scrolling granularity
187
+ d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
188
 
189
+ return d;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
  }
191
 
192
  // STATE UPDATES
194
  // Used to get the editor into a consistent state again when options change.
195
 
196
  function loadMode(cm) {
197
+ cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
198
+ cm.doc.iter(function(line) {
199
+ if (line.stateAfter) line.stateAfter = null;
200
+ if (line.styles) line.styles = null;
201
+ });
202
+ cm.doc.frontier = cm.doc.first;
203
  startWorker(cm, 100);
204
+ cm.state.modeGen++;
205
+ if (cm.curOp) regChange(cm);
206
  }
207
 
208
  function wrappingChanged(cm) {
 
209
  if (cm.options.lineWrapping) {
210
  cm.display.wrapper.className += " CodeMirror-wrap";
 
 
 
 
 
 
211
  cm.display.sizer.style.minWidth = "";
212
  } else {
213
  cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", "");
214
+ computeMaxLength(cm);
 
 
 
215
  }
216
+ estimateLineHeights(cm);
217
+ regChange(cm);
218
  clearCaches(cm);
219
+ setTimeout(function(){updateScrollbars(cm.display, cm.doc.height);}, 100);
220
+ }
221
+
222
+ function estimateHeight(cm) {
223
+ var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
224
+ var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
225
+ return function(line) {
226
+ if (lineIsHidden(cm.doc, line))
227
+ return 0;
228
+ else if (wrapping)
229
+ return (Math.ceil(line.text.length / perLine) || 1) * th;
230
+ else
231
+ return th;
232
+ };
233
+ }
234
+
235
+ function estimateLineHeights(cm) {
236
+ var doc = cm.doc, est = estimateHeight(cm);
237
+ doc.iter(function(line) {
238
+ var estHeight = est(line);
239
+ if (estHeight != line.height) updateLineHeight(line, estHeight);
240
+ });
241
  }
242
 
243
  function keyMapChanged(cm) {
254
 
255
  function guttersChanged(cm) {
256
  updateGutters(cm);
257
+ regChange(cm);
258
  }
259
 
260
  function updateGutters(cm) {
289
  return len;
290
  }
291
 
292
+ function computeMaxLength(cm) {
293
+ var d = cm.display, doc = cm.doc;
294
+ d.maxLine = getLine(doc, doc.first);
295
+ d.maxLineLength = lineLength(doc, d.maxLine);
296
+ d.maxLineChanged = true;
297
+ doc.iter(function(line) {
298
+ var len = lineLength(doc, line);
299
+ if (len > d.maxLineLength) {
300
+ d.maxLineLength = len;
301
+ d.maxLine = line;
302
  }
303
  });
304
  }
322
  // Re-synchronize the fake scrollbars with the actual size of the
323
  // content. Optionally force a scrollTop.
324
  function updateScrollbars(d /* display */, docHeight) {
325
+ var totalHeight = docHeight + paddingVert(d);
326
  d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
327
  var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
328
  var needsH = d.scroller.scrollWidth > d.scroller.clientWidth;
330
  if (needsV) {
331
  d.scrollbarV.style.display = "block";
332
  d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
333
+ d.scrollbarV.firstChild.style.height =
334
  (scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px";
335
  } else d.scrollbarV.style.display = "";
336
  if (needsH) {
361
 
362
  function alignHorizontally(cm) {
363
  var display = cm.display;
364
+ if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
365
+ var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
366
  var gutterW = display.gutters.offsetWidth, l = comp + "px";
367
  for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) {
368
  for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l;
369
  }
370
+ if (cm.options.fixedGutter)
371
+ display.gutters.style.left = (comp + gutterW) + "px";
372
  }
373
 
374
  function maybeUpdateLineNumberWidth(cm) {
375
  if (!cm.options.lineNumbers) return false;
376
+ var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
377
  if (last.length != display.lineNumChars) {
378
  var test = display.measure.appendChild(elt("div", [elt("div", last)],
379
  "CodeMirror-linenumber CodeMirror-gutter-elt"));
392
  return String(options.lineNumberFormatter(i + options.firstLineNumber));
393
  }
394
  function compensateForHScroll(display) {
395
+ return getRect(display.scroller).left - getRect(display.sizer).left;
396
  }
397
 
398
  // DISPLAY DRAWING
401
  var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo;
402
  var updated = updateDisplayInner(cm, changes, viewPort);
403
  if (updated) {
404
+ signalLater(cm, "update", cm);
405
  if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
406
+ signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
407
  }
408
  updateSelection(cm);
409
+ updateScrollbars(cm.display, cm.doc.height);
410
 
411
  return updated;
412
  }
415
  // determine which DOM updates have to be made, and makes the
416
  // updates.
417
  function updateDisplayInner(cm, changes, viewPort) {
418
+ var display = cm.display, doc = cm.doc;
419
  if (!display.wrapper.clientWidth) {
420
+ display.showingFrom = display.showingTo = doc.first;
421
+ display.viewOffset = 0;
422
  return;
423
  }
424
 
427
  // to render instead of the current scrollbar position.
428
  var visible = visibleLines(display, doc, viewPort);
429
  // Bail out if the visible area is already rendered and nothing changed.
430
+ if (changes.length == 0 &&
431
  visible.from > display.showingFrom && visible.to < display.showingTo)
432
  return;
433
 
434
+ if (maybeUpdateLineNumberWidth(cm))
435
+ changes = [{from: doc.first, to: doc.first + doc.size}];
436
+ var gutterW = display.sizer.style.marginLeft = display.gutters.offsetWidth + "px";
437
+ display.scrollbarH.style.left = cm.options.fixedGutter ? gutterW : "0";
 
 
 
 
 
 
 
 
 
 
 
438
 
439
  // Used to determine which lines need their line numbers updated
440
+ var positionsChangedFrom = Infinity;
441
+ if (cm.options.lineNumbers)
442
  for (var i = 0; i < changes.length; ++i)
443
  if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; }
444
 
445
+ var end = doc.first + doc.size;
446
+ var from = Math.max(visible.from - cm.options.viewportMargin, doc.first);
447
+ var to = Math.min(end, visible.to + cm.options.viewportMargin);
448
+ if (display.showingFrom < from && from - display.showingFrom < 20) from = Math.max(doc.first, display.showingFrom);
449
+ if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(end, display.showingTo);
450
  if (sawCollapsedSpans) {
451
  from = lineNo(visualLine(doc, getLine(doc, from)));
452
+ while (to < end && lineIsHidden(doc, getLine(doc, to))) ++to;
453
  }
454
 
455
  // Create a range of theoretically intact lines, and punch holes
456
  // in that using the change info.
457
+ var intact = [{from: Math.max(display.showingFrom, doc.first),
458
+ to: Math.min(display.showingTo, end)}];
459
+ if (intact[0].from >= intact[0].to) intact = [];
460
+ else intact = computeIntact(intact, changes);
461
+ // When merged lines are present, we might have to reduce the
462
+ // intact ranges because changes in continued fragments of the
463
+ // intact lines do require the lines to be redrawn.
464
+ if (sawCollapsedSpans)
465
+ for (var i = 0; i < intact.length; ++i) {
466
+ var range = intact[i], merged;
467
+ while (merged = collapsedSpanAtEnd(getLine(doc, range.to - 1))) {
468
+ var newTo = merged.find().from.line;
469
+ if (newTo > range.from) range.to = newTo;
470
+ else { intact.splice(i--, 1); break; }
471
+ }
472
+ }
473
+
474
  // Clip off the parts that won't be visible
475
  var intactLines = 0;
476
  for (var i = 0; i < intact.length; ++i) {
480
  if (range.from >= range.to) intact.splice(i--, 1);
481
  else intactLines += range.to - range.from;
482
  }
483
+ if (intactLines == to - from && from == display.showingFrom && to == display.showingTo) {
484
+ updateViewOffset(cm);
485
  return;
486
+ }
487
  intact.sort(function(a, b) {return a.from - b.from;});
488
 
489
+ var focused = document.activeElement;
490
  if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
491
  patchDisplay(cm, from, to, intact, positionsChangedFrom);
492
  display.lineDiv.style.display = "";
493
+ if (document.activeElement != focused && focused.offsetHeight) focused.focus();
494
 
495
  var different = from != display.showingFrom || to != display.showingTo ||
496
  display.lastSizeC != display.wrapper.clientHeight;
507
  height = bot - prevBottom;
508
  prevBottom = bot;
509
  } else {
510
+ var box = getRect(node);
511
  height = box.bottom - box.top;
512
  }
513
  var diff = node.lineObj.height - height;
514
  if (height < 2) height = textHeight(display);
515
+ if (diff > .001 || diff < -.001) {
516
  updateLineHeight(node.lineObj, height);
517
+ var widgets = node.lineObj.widgets;
518
+ if (widgets) for (var i = 0; i < widgets.length; ++i)
519
+ widgets[i].height = widgets[i].node.offsetHeight;
520
+ }
521
  }
522
+ updateViewOffset(cm);
523
+
524
+ if (visibleLines(display, doc, viewPort).to > to)
525
+ updateDisplayInner(cm, [], viewPort);
526
  return true;
527
  }
528
 
529
+ function updateViewOffset(cm) {
530
+ var off = cm.display.viewOffset = heightAtLine(cm, getLine(cm.doc, cm.display.showingFrom));
531
+ // Position the mover div to align with the current virtual scroll position
532
+ cm.display.mover.style.top = off + "px";
533
+ }
534
+
535
  function computeIntact(intact, changes) {
536
  for (var i = 0, l = changes.length || 0; i < l; ++i) {
537
  var change = changes[i], intact2 = [], diff = change.diff || 0;
569
  function patchDisplay(cm, from, to, intact, updateNumbersFrom) {
570
  var dims = getDimensions(cm);
571
  var display = cm.display, lineNumbers = cm.options.lineNumbers;
572
+ if (!intact.length && (!webkit || !cm.display.currentWheelTarget))
 
 
573
  removeChildren(display.lineDiv);
574
  var container = display.lineDiv, cur = container.firstChild;
575
 
579
  node.style.display = "none";
580
  node.lineObj = null;
581
  } else {
582
+ node.parentNode.removeChild(node);
583
  }
584
  return next;
585
  }
586
 
587
+ var nextIntact = intact.shift(), lineN = from;
588
+ cm.doc.iter(from, to, function(line) {
589
+ if (nextIntact && nextIntact.to == lineN) nextIntact = intact.shift();
590
+ if (lineIsHidden(cm.doc, line)) {
591
  if (line.height != 0) updateLineHeight(line, 0);
592
+ if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i)
593
+ if (line.widgets[i].showIfHidden) {
594
+ var prev = cur.previousSibling;
595
+ if (/pre/i.test(prev.nodeName)) {
596
+ var wrap = elt("div", null, null, "position: relative");
597
+ prev.parentNode.replaceChild(wrap, prev);
598
+ wrap.appendChild(prev);
599
+ prev = wrap;
600
+ }
601
+ var wnode = prev.appendChild(elt("div", [line.widgets[i].node], "CodeMirror-linewidget"));
602
+ positionLineWidget(line.widgets[i], wnode, prev, dims);
603
+ }
604
+ } else if (nextIntact && nextIntact.from <= lineN && nextIntact.to > lineN) {
605
  // This line is intact. Skip to the actual node. Update its
606
  // line number if needed.
607
  while (cur.lineObj != line) cur = rm(cur);
608
+ if (lineNumbers && updateNumbersFrom <= lineN && cur.lineNumber)
609
+ setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineN));
610
  cur = cur.nextSibling;
611
  } else {
612
+ // For lines with widgets, make an attempt to find and reuse
613
+ // the existing element, so that widgets aren't needlessly
614
+ // removed and re-inserted into the dom
615
+ if (line.widgets) for (var j = 0, search = cur, reuse; search && j < 20; ++j, search = search.nextSibling)
616
+ if (search.lineObj == line && /div/i.test(search.nodeName)) { reuse = search; break; }
617
  // This line needs to be generated.
618
+ var lineNode = buildLineElement(cm, line, lineN, dims, reuse);
619
+ if (lineNode != reuse) {
620
+ container.insertBefore(lineNode, cur);
621
+ } else {
622
+ while (cur != reuse) cur = rm(cur);
623
+ cur = cur.nextSibling;
624
+ }
625
+
626
  lineNode.lineObj = line;
627
  }
628
+ ++lineN;
629
  });
630
  while (cur) cur = rm(cur);
631
  }
632
 
633
+ function buildLineElement(cm, line, lineNo, dims, reuse) {
634
  var lineElement = lineContent(cm, line);
635
+ var markers = line.gutterMarkers, display = cm.display, wrap;
636
+
637
+ if (!cm.options.lineNumbers && !markers && !line.bgClass && !line.wrapClass && !line.widgets)
638
+ return lineElement;
639
+
640
+ // Lines with gutter elements, widgets or a background class need
641
+ // to be wrapped again, and have the extra elements added to the
642
+ // wrapper div
643
+
644
+ if (reuse) {
645
+ reuse.alignable = null;
646
+ var isOk = true, widgetsSeen = 0;
647
+ for (var n = reuse.firstChild, next; n; n = next) {
648
+ next = n.nextSibling;
649
+ if (!/\bCodeMirror-linewidget\b/.test(n.className)) {
650
+ reuse.removeChild(n);
651
+ } else {
652
+ for (var i = 0, first = true; i < line.widgets.length; ++i) {
653
+ var widget = line.widgets[i], isFirst = false;
654
+ if (!widget.above) { isFirst = first; first = false; }
655
+ if (widget.node == n.firstChild) {
656
+ positionLineWidget(widget, n, reuse, dims);
657
+ ++widgetsSeen;
658
+ if (isFirst) reuse.insertBefore(lineElement, n);
659
+ break;
660
+ }
661
+ }
662
+ if (i == line.widgets.length) { isOk = false; break; }
663
+ }
664
+ }
665
+ if (isOk && widgetsSeen == line.widgets.length) {
666
+ wrap = reuse;
667
+ reuse.className = line.wrapClass || "";
668
+ }
669
+ }
670
+ if (!wrap) {
671
+ wrap = elt("div", null, line.wrapClass, "position: relative");
672
+ wrap.appendChild(lineElement);
673
+ }
674
+ // Kludge to make sure the styled element lies behind the selection (by z-index)
675
+ if (line.bgClass)
676
+ wrap.insertBefore(elt("div", null, line.bgClass + " CodeMirror-linebackground"), wrap.firstChild);
677
  if (cm.options.lineNumbers || markers) {
678
+ var gutterWrap = wrap.insertBefore(elt("div", null, null, "position: absolute; left: " +
679
+ (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"),
680
+ wrap.firstChild);
681
+ if (cm.options.fixedGutter) (wrap.alignable || (wrap.alignable = [])).push(gutterWrap);
682
  if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
683
  wrap.lineNumber = gutterWrap.appendChild(
684
  elt("div", lineNumberFor(cm.options, lineNo),
693
  dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
694
  }
695
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
696
  if (ie_lt8) wrap.style.zIndex = 2;
697
+ if (line.widgets && wrap != reuse) for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
698
+ var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
699
+ positionLineWidget(widget, node, wrap, dims);
700
+ if (widget.above)
701
+ wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
702
+ else
703
+ wrap.appendChild(node);
704
+ signalLater(widget, "redraw");
705
+ }
706
  return wrap;
707
  }
708
 
709
+ function positionLineWidget(widget, node, wrap, dims) {
710
+ if (widget.noHScroll) {
711
+ (wrap.alignable || (wrap.alignable = [])).push(node);
712
+ var width = dims.wrapperWidth;
713
+ node.style.left = dims.fixedPos + "px";
714
+ if (!widget.coverGutter) {
715
+ width -= dims.gutterTotalWidth;
716
+ node.style.paddingLeft = dims.gutterTotalWidth + "px";
717
+ }
718
+ node.style.width = width + "px";
719
+ }
720
+ if (widget.coverGutter) {
721
+ node.style.zIndex = 5;
722
+ node.style.position = "relative";
723
+ if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
724
+ }
725
+ }
726
+
727
  // SELECTION / CURSOR
728
 
729
  function updateSelection(cm) {
730
  var display = cm.display;
731
+ var collapsed = posEq(cm.doc.sel.from, cm.doc.sel.to);
732
  if (collapsed || cm.options.showCursorWhenSelecting)
733
  updateSelectionCursor(cm);
734
  else
739
  display.selectionDiv.style.display = "none";
740
 
741
  // Move the hidden textarea near the cursor to prevent scrolling artifacts
742
+ var headPos = cursorCoords(cm, cm.doc.sel.head, "div");
743
+ var wrapOff = getRect(display.wrapper), lineOff = getRect(display.lineDiv);
744
  display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
745
  headPos.top + lineOff.top - wrapOff.top)) + "px";
746
  display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
749
 
750
  // No selection, plain cursor
751
  function updateSelectionCursor(cm) {
752
+ var display = cm.display, pos = cursorCoords(cm, cm.doc.sel.head, "div");
753
  display.cursor.style.left = pos.left + "px";
754
  display.cursor.style.top = pos.top + "px";
755
  display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
765
 
766
  // Highlight selection
767
  function updateSelectionRange(cm) {
768
+ var display = cm.display, doc = cm.doc, sel = cm.doc.sel;
769
  var fragment = document.createDocumentFragment();
770
  var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display);
771
 
780
  var lineObj = getLine(doc, line);
781
  var lineLen = lineObj.text.length, rVal = retTop ? Infinity : -Infinity;
782
  function coords(ch) {
783
+ return charCoords(cm, Pos(line, ch), "div", lineObj);
784
  }
785
 
786
  iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
858
  // HIGHLIGHT WORKER
859
 
860
  function startWorker(cm, time) {
861
+ if (cm.doc.mode.startState && cm.doc.frontier < cm.display.showingTo)
862
+ cm.state.highlight.set(time, bind(highlightWorker, cm));
863
  }
864
 
865
  function highlightWorker(cm) {
866
+ var doc = cm.doc;
867
+ if (doc.frontier < doc.first) doc.frontier = doc.first;
868
+ if (doc.frontier >= cm.display.showingTo) return;
869
  var end = +new Date + cm.options.workTime;
870
+ var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
871
  var changed = [], prevChange;
872
+ doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.showingTo + 500), function(line) {
873
+ if (doc.frontier >= cm.display.showingFrom) { // Visible
874
+ var oldStyles = line.styles;
875
+ line.styles = highlightLine(cm, line, state);
876
+ var ischange = !oldStyles || oldStyles.length != line.styles.length;
877
+ for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
878
+ if (ischange) {
879
+ if (prevChange && prevChange.end == doc.frontier) prevChange.end++;
880
+ else changed.push(prevChange = {start: doc.frontier, end: doc.frontier + 1});
881
  }
882
+ line.stateAfter = copyState(doc.mode, state);
883
  } else {
884
  processLine(cm, line, state);
885
+ line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
886
  }
887
+ ++doc.frontier;
888
  if (+new Date > end) {
889
  startWorker(cm, cm.options.workDelay);
890
  return true;
903
  // smallest indentation, which tends to need the least context to
904
  // parse correctly.
905
  function findStartLine(cm, n) {
906
+ var minindent, minline, doc = cm.doc;
907
  for (var search = n, lim = n - 100; search > lim; --search) {
908
+ if (search <= doc.first) return doc.first;
909
+ var line = getLine(doc, search - 1);
910
  if (line.stateAfter) return search;
911
  var indented = countColumn(line.text, null, cm.options.tabSize);
912
  if (minline == null || minindent > indented) {
918
  }
919
 
920
  function getStateBefore(cm, n) {
921
+ var doc = cm.doc, display = cm.display;
922
+ if (!doc.mode.startState) return true;
923
+ var pos = findStartLine(cm, n), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
924
+ if (!state) state = startState(doc.mode);
925
+ else state = copyState(doc.mode, state);
926
+ doc.iter(pos, n, function(line) {
927
  processLine(cm, line, state);
928
+ var save = pos == n - 1 || pos % 5 == 0 || pos >= display.showingFrom && pos < display.showingTo;
929
+ line.stateAfter = save ? copyState(doc.mode, state) : null;
930
  ++pos;
931
  });
932
  return state;
933
  }
934
 
935
  // POSITION MEASUREMENT
936
+
937
  function paddingTop(display) {return display.lineSpace.offsetTop;}
938
+ function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
939
  function paddingLeft(display) {
940
+ var e = removeChildrenAndAdd(display.measure, elt("pre", null, null, "text-align: left")).appendChild(elt("span", "x"));
941
  return e.offsetLeft;
942
  }
943
 
944
  function measureChar(cm, line, ch, data) {
945
+ var dir = -1;
946
+ data = data || measureLine(cm, line);
947
+
948
  for (var pos = ch;; pos += dir) {
949
  var r = data[pos];
950
  if (r) break;
955
  top: r.top, bottom: r.bottom};
956
  }
957
 
958
+ function findCachedMeasurement(cm, line) {
959
+ var cache = cm.display.measureLineCache;
 
960
  for (var i = 0; i < cache.length; ++i) {
961
  var memo = cache[i];
962
  if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
963
+ cm.display.scroller.clientWidth == memo.width &&
964
+ memo.classes == line.textClass + "|" + line.bgClass + "|" + line.wrapClass)
965
  return memo.measure;
966
  }
967
+ }
968
+
969
+ function measureLine(cm, line) {
970
+ // First look in the cache
971
+ var measure = findCachedMeasurement(cm, line);
972
+ if (!measure) {
973
+ // Failing that, recompute and store result in cache
974
+ measure = measureLineInner(cm, line);
975
+ var cache = cm.display.measureLineCache;
976
+ var memo = {text: line.text, width: cm.display.scroller.clientWidth,
977
+ markedSpans: line.markedSpans, measure: measure,
978
+ classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass};
979
+ if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo;
980
+ else cache.push(memo);
981
+ }
982
  return measure;
983
  }
984
 
1013
 
1014
  removeChildrenAndAdd(display.measure, pre);
1015
 
1016
+ var outer = getRect(display.lineDiv);
1017
  var vranges = [], data = emptyArray(line.text.length), maxBot = pre.offsetHeight;
1018
+ // Work around an IE7/8 bug where it will sometimes have randomly
1019
+ // replaced our pre with a clone at this point.
1020
+ if (ie_lt9 && display.measure.first != pre)
1021
+ removeChildrenAndAdd(display.measure, pre);
1022
+
1023
  for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
1024
+ var size = getRect(cur);
1025
  var top = Math.max(0, size.top - outer.top), bot = Math.min(size.bottom - outer.top, maxBot);
1026
  for (var j = 0; j < vranges.length; j += 2) {
1027
  var rtop = vranges[j], rbot = vranges[j+1];
1035
  }
1036
  }
1037
  if (j == vranges.length) vranges.push(top, bot);
1038
+ var right = size.right;
1039
+ if (cur.measureRight) right = getRect(cur.measureRight).left;
1040
+ data[i] = {left: size.left - outer.left, right: right - outer.left, top: j};
1041
  }
1042
  for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
1043
  var vr = cur.top;
1044
  cur.top = vranges[vr]; cur.bottom = vranges[vr+1];
1045
  }
1046
+
1047
  return data;
1048
  }
1049
 
1050
+ function measureLineWidth(cm, line) {
1051
+ var hasBadSpan = false;
1052
+ if (line.markedSpans) for (var i = 0; i < line.markedSpans; ++i) {
1053
+ var sp = line.markedSpans[i];
1054
+ if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true;
1055
+ }
1056
+ var cached = !hasBadSpan && findCachedMeasurement(cm, line);
1057
+ if (cached) return measureChar(cm, line, line.text.length, cached).right;
1058
+
1059
+ var pre = lineContent(cm, line);
1060
+ var end = pre.appendChild(zeroWidthElement(cm.display.measure));
1061
+ removeChildrenAndAdd(cm.display.measure, pre);
1062
+ return getRect(end).right - getRect(cm.display.lineDiv).left;
1063
+ }
1064
+
1065
  function clearCaches(cm) {
1066
  cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
1067
  cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
1068
+ cm.display.maxLineChanged = true;
1069
+ cm.display.lineNumChars = null;
1070
  }
1071
 
1072
  // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
1073
  function intoCoordSystem(cm, lineObj, rect, context) {
1074
  if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
1075
+ var size = widgetHeight(lineObj.widgets[i]);
1076
  rect.top += size; rect.bottom += size;
1077
  }
1078
  if (context == "line") return rect;
1080
  var yOff = heightAtLine(cm, lineObj);
1081
  if (context != "local") yOff -= cm.display.viewOffset;
1082
  if (context == "page") {
1083
+ var lOff = getRect(cm.display.lineSpace);
1084
  yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop);
1085
  var xOff = lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft);
1086
  rect.left += xOff; rect.right += xOff;
1089
  return rect;
1090
  }
1091
 
1092
+ // Context may be "window", "page", "div", or "local"/null
1093
+ // Result is in local coords
1094
+ function fromCoordSystem(cm, coords, context) {
1095
+ if (context == "div") return coords;
1096
+ var left = coords.left, top = coords.top;
1097
+ if (context == "page") {
1098
+ left -= window.pageXOffset || (document.documentElement || document.body).scrollLeft;
1099
+ top -= window.pageYOffset || (document.documentElement || document.body).scrollTop;
1100
+ }
1101
+ var lineSpaceBox = getRect(cm.display.lineSpace);
1102
+ left -= lineSpaceBox.left;
1103
+ top -= lineSpaceBox.top;
1104
+ if (context == "local" || !context) {
1105
+ var editorBox = getRect(cm.display.wrapper);
1106
+ left -= editorBox.left;
1107
+ top -= editorBox.top;
1108
+ }
1109
+ return {left: left, top: top};
1110
+ }
1111
+
1112
  function charCoords(cm, pos, context, lineObj) {
1113
+ if (!lineObj) lineObj = getLine(cm.doc, pos.line);
1114
  return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context);
1115
  }
1116
 
1117
  function cursorCoords(cm, pos, context, lineObj, measurement) {
1118
+ lineObj = lineObj || getLine(cm.doc, pos.line);
1119
  if (!measurement) measurement = measureLine(cm, lineObj);
1120
  function get(ch, right) {
1121
  var m = measureChar(cm, lineObj, ch, measurement);
1130
  if (part.from < ch && part.to > ch) return get(ch, rtl);
1131
  var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to;
1132
  if (left == ch) {
1133
+ // IE returns bogus offsets and widths for edges where the
1134
+ // direction flips, but only for the side with the lower
1135
+ // level. So we try to use the side with the higher level.
 
1136
  if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true);
1137
  else here = get(rtl && part.from != part.to ? ch - 1 : ch);
1138
  if (rtl == linedir) main = here; else other = here;
1150
  return main;
1151
  }
1152
 
1153
+ function PosMaybeOutside(line, ch, outside) {
1154
+ var pos = new Pos(line, ch);
1155
+ if (outside) pos.outside = true;
1156
+ return pos;
1157
+ }
1158
+
1159
  // Coords must be lineSpace-local
1160
  function coordsChar(cm, x, y) {
1161
+ var doc = cm.doc;
1162
  y += cm.display.viewOffset;
1163
+ if (y < 0) return PosMaybeOutside(doc.first, 0, true);
1164
+ var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
1165
+ if (lineNo > last)
1166
+ return PosMaybeOutside(doc.first + doc.size - 1, getLine(doc, last).text.length, true);
1167
  if (x < 0) x = 0;
1168
 
1169
  for (;;) {
1170
  var lineObj = getLine(doc, lineNo);
1171
  var found = coordsCharInner(cm, lineObj, lineNo, x, y);
1172
  var merged = collapsedSpanAtEnd(lineObj);
1173
+ var mergedPos = merged && merged.find();
1174
+ if (merged && found.ch >= mergedPos.from.ch)
1175
+ lineNo = mergedPos.to.line;
1176
  else
1177
  return found;
1178
  }
1180
 
1181
  function coordsCharInner(cm, lineObj, lineNo, x, y) {
1182
  var innerOff = y - heightAtLine(cm, lineObj);
1183
+ var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
1184
  var measurement = measureLine(cm, lineObj);
1185
 
1186
  function getX(ch) {
1187
+ var sp = cursorCoords(cm, Pos(lineNo, ch), "line",
1188
  lineObj, measurement);
1189
  wrongLine = true;
1190
+ if (innerOff > sp.bottom) return sp.left - adjust;
1191
+ else if (innerOff < sp.top) return sp.left + adjust;
1192
  else wrongLine = false;
1193
  return sp.left;
1194
  }
1195
 
1196
  var bidi = getOrder(lineObj), dist = lineObj.text.length;
1197
  var from = lineLeft(lineObj), to = lineRight(lineObj);
1198
+ var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
1199
 
1200
+ if (x > toX) return PosMaybeOutside(lineNo, to, toOutside);
1201
  // Do a binary search between these bounds.
1202
  for (;;) {
1203
  if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
1204
  var after = x - fromX < toX - x, ch = after ? from : to;
1205
  while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
1206
+ var pos = PosMaybeOutside(lineNo, ch, after ? fromOutside : toOutside);
1207
+ pos.after = after;
1208
+ return pos;
1209
  }
1210
  var step = Math.ceil(dist / 2), middle = from + step;
1211
  if (bidi) {
1213
  for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
1214
  }
1215
  var middleX = getX(middle);
1216
+ if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist -= step;}
1217
+ else {from = middle; fromX = middleX; fromOutside = wrongLine; dist = step;}
1218
  }
1219
  }
1220
 
1255
  // be awkward, slow, and error-prone), but instead updates are
1256
  // batched and then all combined and executed at once.
1257
 
1258
+ var nextOpId = 0;
1259
  function startOperation(cm) {
1260
+ cm.curOp = {
 
 
 
 
1261
  // An array of ranges of lines that have to be updated. See
1262
  // updateDisplay.
1263
  changes: [],
 
1264
  updateInput: null,
1265
  userSelChange: null,
1266
  textChanged: null,
1267
  selectionChanged: false,
1268
  updateMaxLine: false,
1269
+ updateScrollPos: false,
1270
+ id: ++nextOpId
1271
  };
1272
+ if (!delayedCallbackDepth++) delayedCallbacks = [];
1273
  }
1274
 
1275
  function endOperation(cm) {
1276
+ var op = cm.curOp, doc = cm.doc, display = cm.display;
 
1277
  cm.curOp = null;
1278
+
1279
+ if (op.updateMaxLine) computeMaxLength(cm);
1280
+ if (display.maxLineChanged && !cm.options.lineWrapping) {
1281
+ var width = measureLineWidth(cm, display.maxLine);
1282
+ display.sizer.style.minWidth = Math.max(0, width + 3 + scrollerCutOff) + "px";
1283
+ display.maxLineChanged = false;
1284
+ var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + display.sizer.offsetWidth - display.scroller.clientWidth);
1285
+ if (maxScrollLeft < doc.scrollLeft && !op.updateScrollPos)
1286
+ setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true);
1287
  }
1288
  var newScrollPos, updated;
1289
+ if (op.updateScrollPos) {
1290
+ newScrollPos = op.updateScrollPos;
1291
+ } else if (op.selectionChanged && display.scroller.clientHeight) { // don't rescroll if not visible
1292
+ var coords = cursorCoords(cm, doc.sel.head);
1293
  newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
1294
  }
1295
+ if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null) {
1296
  updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop);
1297
+ if (cm.display.scroller.offsetHeight) cm.doc.scrollTop = cm.display.scroller.scrollTop;
1298
+ }
1299
  if (!updated && op.selectionChanged) updateSelection(cm);
1300
+ if (op.updateScrollPos) {
1301
+ display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = newScrollPos.scrollTop;
1302
+ display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = newScrollPos.scrollLeft;
1303
+ alignHorizontally(cm);
1304
+ } else if (newScrollPos) {
1305
+ scrollCursorIntoView(cm);
1306
+ }
1307
  if (op.selectionChanged) restartBlink(cm);
1308
 
1309
+ if (cm.state.focused && op.updateInput)
1310
  resetInput(cm, op.userSelChange);
1311
 
1312
+ var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
1313
+ if (hidden) for (var i = 0; i < hidden.length; ++i)
1314
+ if (!hidden[i].lines.length) signal(hidden[i], "hide");
1315
+ if (unhidden) for (var i = 0; i < unhidden.length; ++i)
1316
+ if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
1317
+
1318
+ var delayed;
1319
+ if (!--delayedCallbackDepth) {
1320
+ delayed = delayedCallbacks;
1321
+ delayedCallbacks = null;
1322
+ }
1323
  if (op.textChanged)
1324
  signal(cm, "change", cm, op.textChanged);
1325
  if (op.selectionChanged) signal(cm, "cursorActivity", cm);
1326
+ if (delayed) for (var i = 0; i < delayed.length; ++i) delayed[i]();
1327
  }
1328
 
1329
  // Wraps a function in an operation. Returns the wrapped function.
1330
  function operation(cm1, f) {
1331
  return function() {
1332
+ var cm = cm1 || this, withOp = !cm.curOp;
1333
+ if (withOp) startOperation(cm);
1334
+ try { var result = f.apply(cm, arguments); }
1335
+ finally { if (withOp) endOperation(cm); }
1336
+ return result;
1337
+ };
1338
+ }
1339
+ function docOperation(f) {
1340
+ return function() {
1341
+ var withOp = this.cm && !this.cm.curOp, result;
1342
+ if (withOp) startOperation(this.cm);
1343
+ try { result = f.apply(this, arguments); }
1344
+ finally { if (withOp) endOperation(this.cm); }
1345
  return result;
1346
  };
1347
  }
1348
+ function runInOp(cm, f) {
1349
+ var withOp = !cm.curOp, result;
1350
+ if (withOp) startOperation(cm);
1351
+ try { result = f(); }
1352
+ finally { if (withOp) endOperation(cm); }
1353
+ return result;
1354
+ }
1355
 
1356
  function regChange(cm, from, to, lendiff) {
1357
+ if (from == null) from = cm.doc.first;
1358
+ if (to == null) to = cm.doc.first + cm.doc.size;
1359
  cm.curOp.changes.push({from: from, to: to, diff: lendiff});
1360
  }
1361
 
1362
  // INPUT HANDLING
1363
 
1364
  function slowPoll(cm) {
1365
+ if (cm.display.pollingFast) return;
1366
  cm.display.poll.set(cm.options.pollInterval, function() {
1367
  readInput(cm);
1368
+ if (cm.state.focused) slowPoll(cm);
1369
  });
1370
  }
1371
 
1386
  // events that indicate IME taking place, but these are not widely
1387
  // supported or compatible enough yet to rely on.)
1388
  function readInput(cm) {
1389
+ var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel;
1390
+ if (!cm.state.focused || hasSelection(input) || isReadOnly(cm)) return false;
1391
  var text = input.value;
1392
  if (text == prevInput && posEq(sel.from, sel.to)) return false;
1393
+ // IE enjoys randomly deselecting our input's text when
1394
+ // re-focusing. If the selection is gone but the cursor is at the
1395
+ // start of the input, that's probably what happened.
1396
+ if (ie && text && input.selectionStart === 0) {
1397
+ resetInput(cm, true);
1398
+ return false;
1399
+ }
1400
+ var withOp = !cm.curOp;
1401
+ if (withOp) startOperation(cm);
1402
+ sel.shift = false;
1403
  var same = 0, l = Math.min(prevInput.length, text.length);
1404
  while (same < l && prevInput[same] == text[same]) ++same;
1405
  var from = sel.from, to = sel.to;
1406
  if (same < prevInput.length)
1407
+ from = Pos(from.line, from.ch - (prevInput.length - same));
1408
+ else if (cm.state.overwrite && posEq(from, to) && !cm.state.pasteIncoming)
1409
+ to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same)));
1410
  var updateInput = cm.curOp.updateInput;
1411
+ makeChange(cm.doc, {from: from, to: to, text: splitLines(text.slice(same)),
1412
+ origin: cm.state.pasteIncoming ? "paste" : "+input"}, "end");
1413
+
1414
  cm.curOp.updateInput = updateInput;
1415
+ if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = "";
1416
  else cm.display.prevInput = text;
1417
+ if (withOp) endOperation(cm);
1418
+ cm.state.pasteIncoming = false;
1419
  return true;
1420
  }
1421
 
1422
  function resetInput(cm, user) {
1423
+ var minimal, selected, doc = cm.doc;
1424
+ if (!posEq(doc.sel.from, doc.sel.to)) {
1425
  cm.display.prevInput = "";
1426
  minimal = hasCopyEvent &&
1427
+ (doc.sel.to.line - doc.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000);
1428
  if (minimal) cm.display.input.value = "-";
1429
  else cm.display.input.value = selected || cm.getSelection();
1430
+ if (cm.state.focused) selectInput(cm.display.input);
1431
  } else if (user) cm.display.prevInput = cm.display.input.value = "";
1432
  cm.display.inaccurateSelection = minimal;
1433
  }
1434
 
1435
  function focusInput(cm) {
1436
+ if (cm.options.readOnly != "nocursor" && (!mobile || document.activeElement != cm.display.input))
1437
  cm.display.input.focus();
1438
  }
1439
 
1440
  function isReadOnly(cm) {
1441
+ return cm.options.readOnly || cm.doc.cantEdit;
1442
  }
1443
 
1444
  // EVENT HANDLERS
1448
  on(d.scroller, "mousedown", operation(cm, onMouseDown));
1449
  on(d.scroller, "dblclick", operation(cm, e_preventDefault));
1450
  on(d.lineSpace, "selectstart", function(e) {
1451
+ if (!eventInWidget(d, e)) e_preventDefault(e);
1452
  });
1453
  // Gecko browsers fire contextmenu *after* opening the menu, at
1454
  // which point we can't mess with it anymore. Context menu is
1455
  // handled in onMouseDown for Gecko.
1456
+ if (!captureMiddleClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
1457
 
1458
  on(d.scroller, "scroll", function() {
1459
+ if (d.scroller.clientHeight) {
1460
+ setScrollTop(cm, d.scroller.scrollTop);
1461
+ setScrollLeft(cm, d.scroller.scrollLeft, true);
1462
+ signal(cm, "scroll", cm);
1463
+ }
1464
  });
1465
  on(d.scrollbarV, "scroll", function() {
1466
+ if (d.scroller.clientHeight) setScrollTop(cm, d.scrollbarV.scrollTop);
1467
  });
1468
  on(d.scrollbarH, "scroll", function() {
1469
+ if (d.scroller.clientHeight) setScrollLeft(cm, d.scrollbarH.scrollLeft);
1470
  });
1471
 
1472
  on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
1473
  on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
1474
 
1475
+ function reFocus() { if (cm.state.focused) setTimeout(bind(focusInput, cm), 0); }
1476
  on(d.scrollbarH, "mousedown", reFocus);
1477
  on(d.scrollbarV, "mousedown", reFocus);
1478
  // Prevent wrapper from ever scrolling
1479
  on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
1480
+
1481
+ function onResize() {
1482
  // Might be a text scaling operation, clear size caches.
1483
  d.cachedCharWidth = d.cachedTextHeight = null;
1484
  clearCaches(cm);
1485
+ runInOp(cm, bind(regChange, cm));
1486
+ }
1487
+ on(window, "resize", onResize);
1488
+ // Above handler holds on to the editor and its data structures.
1489
+ // Here we poll to unregister it when the editor is no longer in
1490
+ // the document, so that it can be garbage-collected.
1491
+ function unregister() {
1492
+ for (var p = d.wrapper.parentNode; p && p != document.body; p = p.parentNode) {}
1493
+ if (p) setTimeout(unregister, 5000);
1494
+ else off(window, "resize", onResize);
1495
+ }
1496
+ setTimeout(unregister, 5000);
1497
 
1498
  on(d.input, "keyup", operation(cm, function(e) {
1499
  if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1500
+ if (e.keyCode == 16) cm.doc.sel.shift = false;
1501
  }));
1502
  on(d.input, "input", bind(fastPoll, cm));
1503
  on(d.input, "keydown", operation(cm, onKeyDown));
1515
  on(d.scroller, "dragover", drag_);
1516
  on(d.scroller, "drop", operation(cm, onDrop));
1517
  }
1518
+ on(d.scroller, "paste", function(e){
1519
+ if (eventInWidget(d, e)) return;
1520
+ focusInput(cm);
1521
+ fastPoll(cm);
1522
+ });
1523
  on(d.input, "paste", function() {
1524
+ cm.state.pasteIncoming = true;
1525
  fastPoll(cm);
1526
  });
1527
 
1543
  });
1544
  }
1545
 
1546
+ function eventInWidget(display, e) {
1547
+ for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
1548
+ if (!n) return true;
1549
  if (/\bCodeMirror-(?:line)?widget\b/.test(n.className) ||
1550
  n.parentNode == display.sizer && n != display.mover) return true;
1551
+ }
1552
  }
1553
 
1554
  function posFromMouse(cm, e, liberal) {
1559
  target == display.scrollbarV || target == display.scrollbarV.firstChild ||
1560
  target == display.scrollbarFiller) return null;
1561
  }
1562
+ var x, y, space = getRect(display.lineSpace);
1563
  // Fails unpredictably on IE[67] when mouse is dragged around quickly.
1564
  try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
1565
  return coordsChar(cm, x - space.left, y - space.top);
1567
 
1568
  var lastClick, lastDoubleClick;
1569
  function onMouseDown(e) {
1570
+ var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel;
1571
+ sel.shift = e.shiftKey;
1572
 
1573
+ if (eventInWidget(display, e)) {
1574
  if (!webkit) {
1575
  display.scroller.draggable = false;
1576
  setTimeout(function(){display.scroller.draggable = true;}, 100);
1582
 
1583
  switch (e_button(e)) {
1584
  case 3:
1585
+ if (captureMiddleClick) onContextMenu.call(cm, cm, e);
1586
  return;
1587
  case 2:
1588
+ if (start) extendSelection(cm.doc, start);
1589
  setTimeout(bind(focusInput, cm), 20);
1590
  e_preventDefault(e);
1591
  return;
1595
  // selection.
1596
  if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;}
1597
 
1598
+ if (!cm.state.focused) onFocus(cm);
1599
 
1600
  var now = +new Date, type = "single";
1601
  if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
1608
  lastDoubleClick = {time: now, pos: start};
1609
  e_preventDefault(e);
1610
  var word = findWordAt(getLine(doc, start.line).text, start);
1611
+ extendSelection(cm.doc, word.from, word.to);
1612
  } else { lastClick = {time: now, pos: start}; }
1613
 
1614
  var last = start;
1616
  !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
1617
  var dragEnd = operation(cm, function(e2) {
1618
  if (webkit) display.scroller.draggable = false;
1619
+ cm.state.draggingText = false;
1620
  off(document, "mouseup", dragEnd);
1621
  off(display.scroller, "drop", dragEnd);
1622
  if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
1623
  e_preventDefault(e2);
1624
+ extendSelection(cm.doc, start);
1625
  focusInput(cm);
1626
  }
1627
  });
1628
  // Let the drag handler handle this.
1629
  if (webkit) display.scroller.draggable = true;
1630
+ cm.state.draggingText = dragEnd;
1631
  // IE's approach to draggable
1632
  if (display.scroller.dragDrop) display.scroller.dragDrop();
1633
  on(document, "mouseup", dragEnd);
1635
  return;
1636
  }
1637
  e_preventDefault(e);
1638
+ if (type == "single") extendSelection(cm.doc, clipPos(doc, start));
1639
 
1640
  var startstart = sel.from, startend = sel.to;
1641
 
1642
  function doSelect(cur) {
1643
  if (type == "single") {
1644
+ extendSelection(cm.doc, clipPos(doc, start), cur);
1645
  return;
1646
  }
1647
 
1649
  startend = clipPos(doc, startend);
1650
  if (type == "double") {
1651
  var word = findWordAt(getLine(doc, cur.line).text, cur);
1652
+ if (posLess(cur, startstart)) extendSelection(cm.doc, word.from, startend);
1653
+ else extendSelection(cm.doc, startstart, word.to);
1654
  } else if (type == "triple") {
1655
+ if (posLess(cur, startstart)) extendSelection(cm.doc, startend, clipPos(doc, Pos(cur.line, 0)));
1656
+ else extendSelection(cm.doc, startstart, clipPos(doc, Pos(cur.line + 1, 0)));
1657
  }
1658
  }
1659
 
1660
+ var editorSize = getRect(display.wrapper);
1661
  // Used to ensure timeout re-tries don't fire when another extend
1662
  // happened in the meantime (clearTimeout isn't reliable -- at
1663
  // least on Chrome, the timeouts still happen even when cleared,
1669
  var cur = posFromMouse(cm, e, true);
1670
  if (!cur) return;
1671
  if (!posEq(cur, last)) {
1672
+ if (!cm.state.focused) onFocus(cm);
1673
  last = cur;
1674
  doSelect(cur);
1675
  var visible = visibleLines(display, doc);
1706
 
1707
  function onDrop(e) {
1708
  var cm = this;
1709
+ if (eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
1710
+ return;
1711
  e_preventDefault(e);
1712
  var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
1713
  if (!pos || isReadOnly(cm)) return;
1718
  reader.onload = function() {
1719
  text[i] = reader.result;
1720
  if (++read == n) {
1721
+ pos = clipPos(cm.doc, pos);
1722
+ makeChange(cm.doc, {from: pos, to: pos, text: splitLines(text.join("\n")), origin: "paste"}, "around");
 
 
 
1723
  }
1724
  };
1725
  reader.readAsText(file);
1727
  for (var i = 0; i < n; ++i) loadFile(files[i], i);
1728
  } else {
1729
  // Don't do a replace if the drop happened inside of the selected text.
1730
+ if (cm.state.draggingText && !(posLess(pos, cm.doc.sel.from) || posLess(cm.doc.sel.to, pos))) {
1731
+ cm.state.draggingText(e);
1732
+ // Ensure the editor is re-focused
1733
+ setTimeout(bind(focusInput, cm), 20);
1734
  return;
1735
  }
1736
  try {
1737
  var text = e.dataTransfer.getData("Text");
1738
  if (text) {
1739
+ var curFrom = cm.doc.sel.from, curTo = cm.doc.sel.to;
1740
+ setSelection(cm.doc, pos, pos);
1741
+ if (cm.state.draggingText) replaceRange(cm.doc, "", curFrom, curTo, "paste");
1742
  cm.replaceSelection(text, null, "paste");
1743
  focusInput(cm);
1744
  onFocus(cm);
1753
  try { var mX = e.clientX, mY = e.clientY; }
1754
  catch(e) { return false; }
1755
 
1756
+ if (mX >= Math.floor(getRect(display.gutters).right)) return false;
1757
  e_preventDefault(e);
1758
  if (!hasHandler(cm, "gutterClick")) return true;
1759
 
1760
+ var lineBox = getRect(display.lineDiv);
1761
  if (mY > lineBox.bottom) return true;
1762
  mY -= lineBox.top - display.viewOffset;
1763
 
1764
  for (var i = 0; i < cm.options.gutters.length; ++i) {
1765
  var g = display.gutters.childNodes[i];
1766
+ if (g && getRect(g).right >= mX) {
1767
+ var line = lineAtHeight(cm.doc, mY);
1768
  var gutter = cm.options.gutters[i];
1769
+ signalLater(cm, "gutterClick", cm, line, gutter, e);
1770
  break;
1771
  }
1772
  }
1774
  }
1775
 
1776
  function onDragStart(cm, e) {
1777
+ if (eventInWidget(cm.display, e)) return;
1778
+
1779
  var txt = cm.getSelection();
1780
  e.dataTransfer.setData("Text", txt);
1781
 
1782
  // Use dummy image instead of default browsers image.
1783
  // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
1784
+ if (e.dataTransfer.setDragImage) {
1785
+ var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
1786
+ if (opera) {
1787
+ img.width = img.height = 1;
1788
+ cm.display.wrapper.appendChild(img);
1789
+ // Force a relayout, or Opera won't use our image for some obscure reason
1790
+ img._top = img.offsetTop;
1791
+ }
1792
+ if (safari) {
1793
+ if (cm.display.dragImg) {
1794
+ img = cm.display.dragImg;
1795
+ } else {
1796
+ cm.display.dragImg = img;
1797
+ img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
1798
+ cm.display.wrapper.appendChild(img);
1799
+ }
1800
+ }
1801
+ e.dataTransfer.setDragImage(img, 0, 0);
1802
+ if (opera) img.parentNode.removeChild(img);
1803
+ }
1804
  }
1805
 
1806
  function setScrollTop(cm, val) {
1807
+ if (Math.abs(cm.doc.scrollTop - val) < 2) return;
1808
+ cm.doc.scrollTop = val;
1809
  if (!gecko) updateDisplay(cm, [], val);
1810
  if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
1811
  if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
1812
  if (gecko) updateDisplay(cm, []);
1813
  }
1814
  function setScrollLeft(cm, val, isScroller) {
1815
+ if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
1816
+ val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
1817
+ cm.doc.scrollLeft = val;
1818
  alignHorizontally(cm);
1819
  if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
1820
  if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val;
1831
  // is that it gives us a chance to update the display before the
1832
  // actual scrolling happens, reducing flickering.
1833
 
1834
+ var wheelSamples = 0, wheelPixelsPerUnit = null;
1835
  // Fill in a browser-detected starting value on browsers where we
1836
  // know one. These don't have to be accurate -- the result of them
1837
  // being wrong would just be a slight flicker on the first wheel
1860
  }
1861
  }
1862
 
1863
+ var display = cm.display, scroll = display.scroller;
1864
  // On some browsers, horizontal scrolling will cause redraws to
1865
  // happen before the gutter has been realigned, causing it to
1866
  // wriggle around in a most unseemly way. When we have an
1872
  setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
1873
  setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
1874
  e_preventDefault(e);
1875
+ display.wheelStartX = null; // Abort measurement, if in progress
1876
  return;
1877
  }
1878
 
1879
  if (dy && wheelPixelsPerUnit != null) {
1880
  var pixels = dy * wheelPixelsPerUnit;
1881
+ var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
1882
  if (pixels < 0) top = Math.max(0, top + pixels - 50);
1883
+ else bot = Math.min(cm.doc.height, bot + pixels + 50);
1884
  updateDisplay(cm, [], {top: top, bottom: bot});
1885
  }
1886
 
1887
  if (wheelSamples < 20) {
1888
+ if (display.wheelStartX == null) {
1889
+ display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
1890
+ display.wheelDX = dx; display.wheelDY = dy;
1891
  setTimeout(function() {
1892
+ if (display.wheelStartX == null) return;
1893
+ var movedX = scroll.scrollLeft - display.wheelStartX;
1894
+ var movedY = scroll.scrollTop - display.wheelStartY;
1895
+ var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
1896
+ (movedX && display.wheelDX && movedX / display.wheelDX);
1897
+ display.wheelStartX = display.wheelStartY = null;
1898
  if (!sample) return;
1899
  wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
1900
  ++wheelSamples;
1901
  }, 200);
1902
  } else {
1903
+ display.wheelDX += dx; display.wheelDY += dy;
1904
  }
1905
  }
1906
  }
1913
  // Ensure previous input has been read, so that the handler sees a
1914
  // consistent view of the document
1915
  if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false;
1916
+ var doc = cm.doc, prevShift = doc.sel.shift, done = false;
1917
  try {
1918
+ if (isReadOnly(cm)) cm.state.suppressEdits = true;
1919
+ if (dropShift) doc.sel.shift = false;
1920
+ done = bound(cm) != Pass;
 
 
 
1921
  } finally {
1922
+ doc.sel.shift = prevShift;
1923
+ cm.state.suppressEdits = false;
1924
  }
1925
+ return done;
1926
  }
1927
 
1928
  function allKeyMaps(cm) {
1929
+ var maps = cm.state.keyMaps.slice(0);
1930
+ if (cm.options.extraKeys) maps.push(cm.options.extraKeys);
1931
  maps.push(cm.options.keyMap);
 
1932
  return maps;
1933
  }
1934
 
1942
  cm.options.keyMap = (next.call ? next.call(null, cm) : next);
1943
  }, 50);
1944
 
1945
+ var name = keyName(e, true), handled = false;
1946
+ if (!name) return false;
 
 
 
 
 
 
 
1947
  var keymaps = allKeyMaps(cm);
1948
 
1949
+ if (e.shiftKey) {
1950
+ // First try to resolve full name (including 'Shift-'). Failing
1951
+ // that, see if there is a cursor-motion command (starting with
1952
+ // 'go') bound to the keyname without 'Shift-'.
1953
+ handled = lookupKey("Shift-" + name, keymaps, function(b) {return doHandleBinding(cm, b, true);})
1954
+ || lookupKey(name, keymaps, function(b) {
1955
+ if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(cm, b);
1956
+ });
1957
  } else {
1958
+ handled = lookupKey(name, keymaps, function(b) { return doHandleBinding(cm, b); });
 
1959
  }
1960
+ if (handled == "stop") handled = false;
1961
+
1962
  if (handled) {
1963
  e_preventDefault(e);
1964
  restartBlink(cm);
1980
  var lastStoppedKey = null;
1981
  function onKeyDown(e) {
1982
  var cm = this;
1983
+ if (!cm.state.focused) onFocus(cm);
1984
  if (ie && e.keyCode == 27) { e.returnValue = false; }
1985
  if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1986
+ var code = e.keyCode;
1987
  // IE does strange things with escape.
1988
+ cm.doc.sel.shift = code == 16 || e.shiftKey;
1989
  // First give onKeyEvent option a chance to handle this.
1990
  var handled = handleKeyBinding(cm, e);
1991
  if (opera) {
1992
  lastStoppedKey = handled ? code : null;
1993
  // Opera has no cut event... we try to at least catch the key combo
1994
+ if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
1995
  cm.replaceSelection("");
1996
  }
1997
  }
1999
  function onKeyPress(e) {
2000
  var cm = this;
2001
  if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
2002
+ var keyCode = e.keyCode, charCode = e.charCode;
2003
  if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
2004
  if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
2005
  var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
2006
+ if (this.options.electricChars && this.doc.mode.electricChars &&
2007
  this.options.smartIndent && !isReadOnly(this) &&
2008
+ this.doc.mode.electricChars.indexOf(ch) > -1)
2009
+ setTimeout(operation(cm, function() {indentLine(cm, cm.doc.sel.to.line, "smart");}), 75);
2010
  if (handleCharBinding(cm, e, ch)) return;
2011
  fastPoll(cm);
2012
  }
2013
 
2014
  function onFocus(cm) {
2015
  if (cm.options.readOnly == "nocursor") return;
2016
+ if (!cm.state.focused) {
2017
  signal(cm, "focus", cm);
2018
+ cm.state.focused = true;
2019
+ if (cm.display.wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
2020
+ cm.display.wrapper.className += " CodeMirror-focused";
2021
  resetInput(cm, true);
2022
  }
2023
  slowPoll(cm);
2024
  restartBlink(cm);
2025
  }
2026
  function onBlur(cm) {
2027
+ if (cm.state.focused) {
2028
  signal(cm, "blur", cm);
2029
+ cm.state.focused = false;
2030
+ cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-focused", "");
2031
  }
2032
  clearInterval(cm.display.blinker);
2033
+ setTimeout(function() {if (!cm.state.focused) cm.doc.sel.shift = false;}, 150);
2034
  }
2035
 
2036
  var detectingSelectAll;
2037
  function onContextMenu(cm, e) {
2038
+ var display = cm.display, sel = cm.doc.sel;
2039
+ if (eventInWidget(display, e)) return;
2040
+
2041
  var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
2042
  if (!pos || opera) return; // Opera is difficult.
2043
  if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
2044
+ operation(cm, setSelection)(cm.doc, pos, pos);
2045
 
2046
  var oldCSS = display.input.style.cssText;
2047
  display.inputDiv.style.position = "absolute";
2048
  display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
2049
  "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" +
2050
+ "border-width: 0; outline: none; overflow: hidden; opacity: .05; -ms-opacity: .05; filter: alpha(opacity=5);";
2051
  focusInput(cm);
2052
  resetInput(cm, true);
2053
  // Adds "Select all" to context menu in FF
2059
  if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
2060
  slowPoll(cm);
2061
 
2062
+ // Try to detect the user choosing select-all
2063
+ if (display.input.selectionStart != null && (!ie || ie_lt9)) {
2064
  clearTimeout(detectingSelectAll);
2065
  var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0;
2066
  display.prevInput = " ";
2067
  display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
2068
+ var poll = function(){
2069
  if (display.prevInput == " " && display.input.selectionStart == 0)
2070
  operation(cm, commands.selectAll)(cm);
2071
  else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
2072
  else resetInput(cm);
2073
+ };
2074
+ detectingSelectAll = setTimeout(poll, 200);
2075
  }
2076
  }
2077
 
2078
+ if (captureMiddleClick) {
2079
  e_stop(e);
2080
+ var mouseup = function() {
2081
  off(window, "mouseup", mouseup);
2082
  setTimeout(rehide, 20);
2083
+ };
2084
+ on(window, "mouseup", mouseup);
2085
  } else {
2086
  setTimeout(rehide, 50);
2087
  }
2089
 
2090
  // UPDATING
2091
 
2092
+ function changeEnd(change) {
2093
+ return Pos(change.from.line + change.text.length - 1,
2094
+ lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
2095
+ }
2096
+
2097
+ // Make sure a position will be valid after the given change.
2098
+ function clipPostChange(doc, change, pos) {
2099
+ if (!posLess(change.from, pos)) return clipPos(doc, pos);
2100
+ var diff = (change.text.length - 1) - (change.to.line - change.from.line);
2101
+ if (pos.line > change.to.line + diff) {
2102
+ var preLine = pos.line - diff, lastLine = doc.first + doc.size - 1;
2103
+ if (preLine > lastLine) return Pos(lastLine, getLine(doc, lastLine).text.length);
2104
+ return clipToLen(pos, getLine(doc, preLine).text.length);
2105
+ }
2106
+ if (pos.line == change.to.line + diff)
2107
+ return clipToLen(pos, lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0) +
2108
+ getLine(doc, change.to.line).text.length - change.to.ch);
2109
+ var inside = pos.line - change.from.line;
2110
+ return clipToLen(pos, change.text[inside].length + (inside ? 0 : change.from.ch));
2111
+ }
2112
+
2113
+ // Hint can be null|"end"|"start"|"around"|{anchor,head}
2114
+ function computeSelAfterChange(doc, change, hint) {
2115
+ if (hint && typeof hint == "object") // Assumed to be {anchor, head} object
2116
+ return {anchor: clipPostChange(doc, change, hint.anchor),
2117
+ head: clipPostChange(doc, change, hint.head)};
2118
+
2119
+ if (hint == "start") return {anchor: change.from, head: change.from};
2120
+
2121
+ var end = changeEnd(change);
2122
+ if (hint == "around") return {anchor: change.from, head: end};
2123
+ if (hint == "end") return {anchor: end, head: end};
2124
+
2125
+ // hint is null, leave the selection alone as much as possible
2126
+ var adjustPos = function(pos) {
2127
+ if (posLess(pos, change.from)) return pos;
2128
+ if (!posLess(change.to, pos)) return end;
2129
+
2130
+ var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
2131
+ if (pos.line == change.to.line) ch += end.ch - change.to.ch;
2132
+ return Pos(line, ch);
2133
+ };
2134
+ return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)};
2135
+ }
2136
+
2137
+ function filterChange(doc, change) {
2138
+ var obj = {
2139
+ canceled: false,
2140
+ from: change.from,
2141
+ to: change.to,
2142
+ text: change.text,
2143
+ origin: change.origin,
2144
+ update: function(from, to, text, origin) {
2145
+ if (from) this.from = clipPos(doc, from);
2146
+ if (to) this.to = clipPos(doc, to);
2147
+ if (text) this.text = text;
2148
+ if (origin !== undefined) this.origin = origin;
2149
+ },
2150
+ cancel: function() { this.canceled = true; }
2151
+ };
2152
+ signal(doc, "beforeChange", doc, obj);
2153
+ if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
2154
+
2155
+ if (obj.canceled) return null;
2156
+ return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
2157
+ }
2158
+
2159
+ // Replace the range from from to to by the strings in replacement.
2160
+ // change is a {from, to, text [, origin]} object
2161
+ function makeChange(doc, change, selUpdate, ignoreReadOnly) {
2162
+ if (doc.cm) {
2163
+ if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, selUpdate, ignoreReadOnly);
2164
+ if (doc.cm.state.suppressEdits) return;
2165
+ }
2166
+
2167
+ if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
2168
+ change = filterChange(doc, change);
2169
+ if (!change) return;
2170
+ }
2171
+
2172
  // Possibly split or suppress the update based on the presence
2173
  // of read-only spans in its range.
2174
+ var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
 
2175
  if (split) {
2176
  for (var i = split.length - 1; i >= 1; --i)
2177
+ makeChangeNoReadonly(doc, {from: split[i].from, to: split[i].to, text: [""]});
2178
  if (split.length)
2179
+ makeChangeNoReadonly(doc, {from: split[0].from, to: split[0].to, text: change.text}, selUpdate);
2180
  } else {
2181
+ makeChangeNoReadonly(doc, change, selUpdate);
2182
  }
2183
  }
2184
 
2185
+ function makeChangeNoReadonly(doc, change, selUpdate) {
2186
+ var selAfter = computeSelAfterChange(doc, change, selUpdate);
2187
+ addToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
2188
 
2189
+ makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
2190
+ var rebased = [];
2191
+
2192
+ linkedDocs(doc, function(doc, sharedHist) {
2193
+ if (!sharedHist && indexOf(rebased, doc.history) == -1) {
2194
+ rebaseHist(doc.history, change);
2195
+ rebased.push(doc.history);
2196
+ }
2197
+ makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
2198
  });
2199
+ }
2200
+
2201
+ function makeChangeFromHistory(doc, type) {
2202
+ if (doc.cm && doc.cm.state.suppressEdits) return;
2203
+
2204
+ var hist = doc.history;
2205
+ var event = (type == "undo" ? hist.done : hist.undone).pop();
2206
+ if (!event) return;
2207
+ hist.dirtyCounter += type == "undo" ? -1 : 1;
2208
+
2209
+ var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.headAfter,
2210
+ anchorAfter: event.anchorBefore, headAfter: event.headBefore};
 
 
 
 
 
 
 
 
 
 
 
 
2211
  (type == "undo" ? hist.undone : hist.done).push(anti);
2212
+
2213
+ for (var i = event.changes.length - 1; i >= 0; --i) {
2214
+ var change = event.changes[i];
2215
+ change.origin = type;
2216
+ anti.changes.push(historyChangeFromChange(doc, change));
2217
+
2218
+ var after = i ? computeSelAfterChange(doc, change, null)
2219
+ : {anchor: event.anchorBefore, head: event.headBefore};
2220
+ makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
2221
+ var rebased = [];
2222
+
2223
+ linkedDocs(doc, function(doc, sharedHist) {
2224
+ if (!sharedHist && indexOf(rebased, doc.history) == -1) {
2225
+ rebaseHist(doc.history, change);
2226
+ rebased.push(doc.history);
2227
+ }
2228
+ makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
2229
+ });
2230
+ }
2231
+ }
2232
+
2233
+ function shiftDoc(doc, distance) {
2234
+ function shiftPos(pos) {return Pos(pos.line + distance, pos.ch);}
2235
+ doc.first += distance;
2236
+ if (doc.cm) regChange(doc.cm, doc.first, doc.first, distance);
2237
+ doc.sel.head = shiftPos(doc.sel.head); doc.sel.anchor = shiftPos(doc.sel.anchor);
2238
+ doc.sel.from = shiftPos(doc.sel.from); doc.sel.to = shiftPos(doc.sel.to);
2239
+ }
2240
+
2241
+ function makeChangeSingleDoc(doc, change, selAfter, spans) {
2242
+ if (doc.cm && !doc.cm.curOp)
2243
+ return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
2244
+
2245
+ if (change.to.line < doc.first) {
2246
+ shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
2247
+ return;
2248
+ }
2249
+ if (change.from.line > doc.lastLine()) return;
2250
+
2251
+ // Clip the change to the size of this doc
2252
+ if (change.from.line < doc.first) {
2253
+ var shift = change.text.length - 1 - (doc.first - change.from.line);
2254
+ shiftDoc(doc, shift);
2255
+ change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
2256
+ text: [lst(change.text)], origin: change.origin};
2257
+ }
2258
+ var last = doc.lastLine();
2259
+ if (change.to.line > last) {
2260
+ change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
2261
+ text: [change.text[0]], origin: change.origin};
2262
+ }
2263
+
2264
+ change.removed = getBetween(doc, change.from, change.to);
2265
+
2266
+ if (!selAfter) selAfter = computeSelAfterChange(doc, change, null);
2267
+ if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans, selAfter);
2268
+ else updateDoc(doc, change, spans, selAfter);
2269
  }
2270
 
2271
+ function makeChangeSingleDocInEditor(cm, change, spans, selAfter) {
2272
+ var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
 
2273
 
 
2274
  var recomputeMaxLength = false, checkWidthStart = from.line;
2275
  if (!cm.options.lineWrapping) {
2276
+ checkWidthStart = lineNo(visualLine(doc, getLine(doc, from.line)));
2277
  doc.iter(checkWidthStart, to.line + 1, function(line) {
2278
+ if (line == display.maxLine) {
2279
  recomputeMaxLength = true;
2280
  return true;
2281
  }
2282
  });
2283
  }
2284
 
2285
+ updateDoc(doc, change, spans, selAfter, estimateHeight(cm));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2286
 
2287
+ if (!cm.options.lineWrapping) {
2288
+ doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
 
 
 
 
 
 
 
2289
  var len = lineLength(doc, line);
2290
+ if (len > display.maxLineLength) {
2291
+ display.maxLine = line;
2292
+ display.maxLineLength = len;
2293
+ display.maxLineChanged = true;
2294
  recomputeMaxLength = false;
2295
  }
2296
  });
2298
  }
2299
 
2300
  // Adjust frontier, schedule worker
2301
+ doc.frontier = Math.min(doc.frontier, from.line);
2302
  startWorker(cm, 400);
2303
 
2304
+ var lendiff = change.text.length - (to.line - from.line) - 1;
2305
  // Remember that these lines changed, for updating the display
2306
  regChange(cm, from.line, to.line + 1, lendiff);
2307
+
2308
  if (hasHandler(cm, "change")) {
2309
+ var changeObj = {from: from, to: to,
2310
+ text: change.text,
2311
+ removed: change.removed,
2312
+ origin: change.origin};
 
2313
  if (cm.curOp.textChanged) {
2314
  for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {}
2315
  cur.next = changeObj;
2316
  } else cm.curOp.textChanged = changeObj;
2317
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2318
  }
2319
 
2320
+ function replaceRange(doc, code, from, to, origin) {
2321
  if (!to) to = from;
2322
  if (posLess(to, from)) { var tmp = to; to = from; from = tmp; }
2323
+ if (typeof code == "string") code = splitLines(code);
2324
+ makeChange(doc, {from: from, to: to, text: code, origin: origin}, null);
2325
  }
2326
 
2327
+ // POSITION OBJECT
2328
+
2329
+ function Pos(line, ch) {
2330
+ if (!(this instanceof Pos)) return new Pos(line, ch);
2331
+ this.line = line; this.ch = ch;
2332
+ }
2333
+ CodeMirror.Pos = Pos;
2334
 
2335
  function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
2336
  function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
2337
+ function copyPos(x) {return Pos(x.line, x.ch);}
2338
+
2339
+ // SELECTION
2340
 
2341
+ function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
2342
  function clipPos(doc, pos) {
2343
+ if (pos.line < doc.first) return Pos(doc.first, 0);
2344
+ var last = doc.first + doc.size - 1;
2345
+ if (pos.line > last) return Pos(last, getLine(doc, last).text.length);
2346
+ return clipToLen(pos, getLine(doc, pos.line).text.length);
2347
+ }
2348
+ function clipToLen(pos, linelen) {
2349
+ var ch = pos.ch;
2350
+ if (ch == null || ch > linelen) return Pos(pos.line, linelen);
2351
+ else if (ch < 0) return Pos(pos.line, 0);
2352
  else return pos;
2353
  }
2354
+ function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
2355
 
2356
  // If shift is held, this will move the selection anchor. Otherwise,
2357
  // it'll set the whole selection.
2358
+ function extendSelection(doc, pos, other, bias) {
2359
+ if (doc.sel.shift || doc.sel.extend) {
2360
+ var anchor = doc.sel.anchor;
 
2361
  if (other) {
2362
  var posBefore = posLess(pos, anchor);
2363
  if (posBefore != posLess(other, anchor)) {
2367
  pos = other;
2368
  }
2369
  }
2370
+ setSelection(doc, anchor, pos, bias);
2371
  } else {
2372
+ setSelection(doc, pos, other || pos, bias);
2373
  }
2374
+ if (doc.cm) doc.cm.curOp.userSelChange = true;
2375
+ }
2376
+
2377
+ function filterSelectionChange(doc, anchor, head) {
2378
+ var obj = {anchor: anchor, head: head};
2379
+ signal(doc, "beforeSelectionChange", doc, obj);
2380
+ if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
2381
+ obj.anchor = clipPos(doc, obj.anchor); obj.head = clipPos(doc, obj.head);
2382
+ return obj;
2383
  }
2384
 
2385
  // Update the selection. Last two args are only used by
2386
  // updateDoc, since they have to be expressed in the line
2387
  // numbers before the update.
2388
+ function setSelection(doc, anchor, head, bias, checkAtomic) {
2389
+ if (!checkAtomic && hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) {
2390
+ var filtered = filterSelectionChange(doc, anchor, head);
2391
+ head = filtered.head;
2392
+ anchor = filtered.anchor;
2393
+ }
2394
+
2395
+ var sel = doc.sel;
2396
+ sel.goalColumn = null;
2397
  // Skip over atomic spans.
2398
  if (checkAtomic || !posEq(anchor, sel.anchor))
2399
+ anchor = skipAtomic(doc, anchor, bias, checkAtomic != "push");
2400
  if (checkAtomic || !posEq(head, sel.head))
2401
+ head = skipAtomic(doc, head, bias, checkAtomic != "push");
2402
 
2403
  if (posEq(sel.anchor, anchor) && posEq(sel.head, head)) return;
2404
 
2407
  sel.from = inv ? head : anchor;
2408
  sel.to = inv ? anchor : head;
2409
 
2410
+ if (doc.cm)
2411
+ doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true;
2412
+
2413
+ signalLater(doc, "cursorActivity", doc);
2414
  }
2415
 
2416
  function reCheckSelection(cm) {
2417
+ setSelection(cm.doc, cm.doc.sel.from, cm.doc.sel.to, null, "push");
2418
  }
2419
 
2420
+ function skipAtomic(doc, pos, bias, mayClear) {
2421
+ var flipped = false, curPos = pos;
2422
  var dir = bias || 1;
2423
+ doc.cantEdit = false;
2424
  search: for (;;) {
2425
+ var line = getLine(doc, curPos.line);
2426
  if (line.markedSpans) {
2427
  for (var i = 0; i < line.markedSpans.length; ++i) {
2428
  var sp = line.markedSpans[i], m = sp.marker;
2429
  if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
2430
  (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
2431
+ if (mayClear) {
2432
+ signal(m, "beforeCursorEnter");
2433
+ if (m.explicitlyCleared) {
2434
+ if (!line.markedSpans) break;
2435
+ else {--i; continue;}
2436
+ }
2437
+ }
2438
+ if (!m.atomic) continue;
2439
  var newPos = m.find()[dir < 0 ? "from" : "to"];
2440
  if (posEq(newPos, curPos)) {
2441
  newPos.ch += dir;
2442
  if (newPos.ch < 0) {
2443
+ if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
2444
  else newPos = null;
2445
  } else if (newPos.ch > line.text.length) {
2446
+ if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
2447
  else newPos = null;
2448
  }
2449
  if (!newPos) {
2450
  if (flipped) {
2451
  // Driven in a corner -- no valid cursor position found at all
2452
  // -- try again *with* clearing, if we didn't already
2453
+ if (!mayClear) return skipAtomic(doc, pos, bias, true);
2454
  // Otherwise, turn off editing until further notice, and return the start of the doc
2455
+ doc.cantEdit = true;
2456
+ return Pos(doc.first, 0);
2457
  }
2458
  flipped = true; newPos = pos; dir = -dir;
2459
  }
2462
  continue search;
2463
  }
2464
  }
 
2465
  }
2466
  return curPos;
2467
  }
2470
  // SCROLLING
2471
 
2472
  function scrollCursorIntoView(cm) {
2473
+ var coords = scrollPosIntoView(cm, cm.doc.sel.head);
2474
+ if (!cm.state.focused) return;
2475
+ var display = cm.display, box = getRect(display.sizer), doScroll = null, pTop = paddingTop(cm.display);
2476
+ if (coords.top + pTop + box.top < 0) doScroll = true;
2477
+ else if (coords.bottom + pTop + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
 
2478
  if (doScroll != null && !phantom) {
2479
  var hidden = display.cursor.style.display == "none";
2480
  if (hidden) {
2487
  }
2488
  }
2489
 
2490
+ function scrollPosIntoView(cm, pos, margin) {
2491
+ if (margin == null) margin = 0;
2492
  for (;;) {
2493
  var changed = false, coords = cursorCoords(cm, pos);
2494
+ var scrollPos = calculateScrollPos(cm, coords.left, coords.top - margin, coords.left, coords.bottom + margin);
2495
+ var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
2496
  if (scrollPos.scrollTop != null) {
2497
  setScrollTop(cm, scrollPos.scrollTop);
2498
+ if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true;
2499
  }
2500
  if (scrollPos.scrollLeft != null) {
2501
  setScrollLeft(cm, scrollPos.scrollLeft);
2502
+ if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true;
2503
  }
2504
  if (!changed) return coords;
2505
  }
2515
  var display = cm.display, pt = paddingTop(display);
2516
  y1 += pt; y2 += pt;
2517
  var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
2518
+ var docBottom = cm.doc.height + paddingVert(display);
2519
  var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
2520
  if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1);
2521
  else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen;
2533
  return result;
2534
  }
2535
 
2536
+ function updateScrollPos(cm, left, top) {
2537
+ cm.curOp.updateScrollPos = {scrollLeft: left, scrollTop: top};
2538
+ }
2539
+
2540
+ function addToScrollPos(cm, left, top) {
2541
+ var pos = cm.curOp.updateScrollPos || (cm.curOp.updateScrollPos = {scrollLeft: cm.doc.scrollLeft, scrollTop: cm.doc.scrollTop});
2542
+ var scroll = cm.display.scroller;
2543
+ pos.scrollTop = Math.max(0, Math.min(scroll.scrollHeight - scroll.clientHeight, pos.scrollTop + top));
2544
+ pos.scrollLeft = Math.max(0, Math.min(scroll.scrollWidth - scroll.clientWidth, pos.scrollLeft + left));
2545
+ }
2546
+
2547
  // API UTILITIES
2548
 
2549
  function indentLine(cm, n, how, aggressive) {
2550
+ var doc = cm.doc;
2551
  if (!how) how = "add";
2552
  if (how == "smart") {
2553
+ if (!cm.doc.mode.indent) how = "prev";
2554
  else var state = getStateBefore(cm, n);
2555
  }
2556
 
2558
  var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
2559
  var curSpaceString = line.text.match(/^\s*/)[0], indentation;
2560
  if (how == "smart") {
2561
+ indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
2562
  if (indentation == Pass) {
2563
  if (!aggressive) return;
2564
  how = "prev";
2565
  }
2566
  }
2567
  if (how == "prev") {
2568
+ if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
2569
  else indentation = 0;
2570
+ } else if (how == "add") {
2571
+ indentation = curSpace + cm.options.indentUnit;
2572
+ } else if (how == "subtract") {
2573
+ indentation = curSpace - cm.options.indentUnit;
2574
  }
 
 
2575
  indentation = Math.max(0, indentation);
2576
 
2577
  var indentString = "", pos = 0;
2580
  if (pos < indentation) indentString += spaceStr(indentation - pos);
2581
 
2582
  if (indentString != curSpaceString)
2583
+ replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
2584
  line.stateAfter = null;
2585
  }
2586
 
2587
  function changeLine(cm, handle, op) {
2588
+ var no = handle, line = handle, doc = cm.doc;
2589
  if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
2590
  else no = lineNo(handle);
2591
  if (no == null) return null;
2594
  return line;
2595
  }
2596
 
2597
+ function findPosH(doc, pos, dir, unit, visually) {
2598
+ var line = pos.line, ch = pos.ch;
2599
  var lineObj = getLine(doc, line);
2600
+ var possible = true;
2601
  function findNextLine() {
2602
  var l = line + dir;
2603
+ if (l < doc.first || l >= doc.first + doc.size) return (possible = false);
2604
  line = l;
2605
  return lineObj = getLine(doc, l);
2606
  }
2610
  if (!boundToLine && findNextLine()) {
2611
  if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
2612
  else ch = dir < 0 ? lineObj.text.length : 0;
2613
+ } else return (possible = false);
2614
  } else ch = next;
2615
  return true;
2616
  }
2617
+
2618
  if (unit == "char") moveOnce();
2619
  else if (unit == "column") moveOnce(true);
2620
+ else if (unit == "word" || unit == "group") {
2621
+ var sawType = null, group = unit == "group";
2622
+ for (var first = true;; first = false) {
2623
+ if (dir < 0 && !moveOnce(!first)) break;
2624
+ var cur = lineObj.text.charAt(ch) || "\n";
2625
+ var type = isWordChar(cur) ? "w"
2626
+ : !group ? null
2627
+ : /\s/.test(cur) ? null
2628
+ : "p";
2629
+ if (sawType && sawType != type) {
2630
+ if (dir < 0) {dir = 1; moveOnce();}
2631
+ break;
2632
+ }
2633
+ if (type) sawType = type;
2634
+ if (dir > 0 && !moveOnce(!first)) break;
2635
  }
2636
  }
2637
+ var result = skipAtomic(doc, Pos(line, ch), dir, true);
2638
+ if (!possible) result.hitSide = true;
2639
+ return result;
2640
+ }
2641
+
2642
+ function findPosV(cm, pos, dir, unit) {
2643
+ var doc = cm.doc, x = pos.left, y;
2644
+ if (unit == "page") {
2645
+ var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
2646
+ y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));
2647
+ } else if (unit == "line") {
2648
+ y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
2649
+ }
2650
+ for (;;) {
2651
+ var target = coordsChar(cm, x, y);
2652
+ if (!target.outside) break;
2653
+ if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; }
2654
+ y += dir * 5;
2655
+ }
2656
+ return target;
2657
  }
2658
 
2659
  function findWordAt(line, pos) {
2661
  if (line) {
2662
  if (pos.after === false || end == line.length) --start; else ++end;
2663
  var startChar = line.charAt(start);
2664
+ var check = isWordChar(startChar) ? isWordChar
2665
+ : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
2666
+ : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
2667
  while (start > 0 && check(line.charAt(start - 1))) --start;
2668
  while (end < line.length && check(line.charAt(end))) ++end;
2669
  }
2670
+ return {from: Pos(pos.line, start), to: Pos(pos.line, end)};
2671
  }
2672
 
2673
  function selectLine(cm, line) {
2674
+ extendSelection(cm.doc, Pos(line, 0), clipPos(cm.doc, Pos(line + 1, 0)));
2675
  }
2676
 
2677
  // PROTOTYPE
2680
  // 'wrap f in an operation, performed on its `this` parameter'
2681
 
2682
  CodeMirror.prototype = {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2683
  focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
2684
 
2685
  setOption: function(option, value) {
2691
  },
2692
 
2693
  getOption: function(option) {return this.options[option];},
2694
+ getDoc: function() {return this.doc;},
2695
 
2696
+ addKeyMap: function(map, bottom) {
2697
+ this.state.keyMaps[bottom ? "push" : "unshift"](map);
 
 
2698
  },
 
2699
  removeKeyMap: function(map) {
2700
+ var maps = this.state.keyMaps;
2701
  for (var i = 0; i < maps.length; ++i)
2702
  if ((typeof map == "string" ? maps[i].name : maps[i]) == map) {
2703
  maps.splice(i, 1);
2705
  }
2706
  },
2707
 
2708
+ addOverlay: operation(null, function(spec, options) {
2709
+ var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
2710
+ if (mode.startState) throw new Error("Overlays may not be stateful.");
2711
+ this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
2712
+ this.state.modeGen++;
2713
+ regChange(this);
2714
+ }),
2715
+ removeOverlay: operation(null, function(spec) {
2716
+ var overlays = this.state.overlays;
2717
+ for (var i = 0; i < overlays.length; ++i) {
2718
+ if (overlays[i].modeSpec == spec) {
2719
+ overlays.splice(i, 1);
2720
+ this.state.modeGen++;
2721
+ regChange(this);
2722
+ return;
2723
+ }
2724
+ }
2725
+ }),
2726
 
2727
  indentLine: operation(null, function(n, dir, aggressive) {
2728
  if (typeof dir != "string") {
2729
  if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
2730
  else dir = dir ? "add" : "subtract";
2731
  }
2732
+ if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
2733
  }),
 
2734
  indentSelection: operation(null, function(how) {
2735
+ var sel = this.doc.sel;
2736
  if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how);
2737
  var e = sel.to.line - (sel.to.ch ? 0 : 1);
2738
  for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how);
2739
  }),
2740
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2741
  // Fetch the parser token for a given character. Useful for hacks
2742
  // that want to inspect the mode state (say, for completion).
2743
  getTokenAt: function(pos) {
2744
+ var doc = this.doc;
2745
  pos = clipPos(doc, pos);
2746
+ var state = getStateBefore(this, pos.line), mode = this.doc.mode;
2747
  var line = getLine(doc, pos.line);
2748
  var stream = new StringStream(line.text, this.options.tabSize);
2749
  while (stream.pos < pos.ch && !stream.eol()) {
2759
  },
2760
 
2761
  getStateAfter: function(line) {
2762
+ var doc = this.doc;
2763
+ line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
2764
  return getStateBefore(this, line + 1);
2765
  },
2766
 
2767
  cursorCoords: function(start, mode) {
2768
+ var pos, sel = this.doc.sel;
2769
  if (start == null) pos = sel.head;
2770
+ else if (typeof start == "object") pos = clipPos(this.doc, start);
2771
  else pos = start ? sel.from : sel.to;
2772
  return cursorCoords(this, pos, mode || "page");
2773
  },
2774
 
2775
  charCoords: function(pos, mode) {
2776
+ return charCoords(this, clipPos(this.doc, pos), mode || "page");
2777
  },
2778
 
2779
+ coordsChar: function(coords, mode) {
2780
+ coords = fromCoordSystem(this, coords, mode || "page");
2781
+ return coordsChar(this, coords.left, coords.top);
2782
  },
2783
 
2784
  defaultTextHeight: function() { return textHeight(this.display); },
2785
+ defaultCharWidth: function() { return charWidth(this.display); },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2786
 
2787
  setGutterMarker: operation(null, function(line, gutterID, value) {
2788
  return changeLine(this, line, function(line) {
2794
  }),
2795
 
2796
  clearGutter: operation(null, function(gutterID) {
2797
+ var cm = this, doc = cm.doc, i = doc.first;
2798
+ doc.iter(function(line) {
2799
  if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
2800
  line.gutterMarkers[gutterID] = null;
2801
  regChange(cm, i, i + 1);
2831
  }),
2832
 
2833
  addLineWidget: operation(null, function(handle, node, options) {
2834
+ return addLineWidget(this, handle, node, options);
 
 
 
 
 
 
 
 
2835
  }),
2836
 
2837
+ removeLineWidget: function(widget) { widget.clear(); },
 
 
 
 
 
2838
 
2839
  lineInfo: function(line) {
2840
  if (typeof line == "number") {
2841
+ if (!isLine(this.doc, line)) return null;
2842
  var n = line;
2843
+ line = getLine(this.doc, line);
2844
  if (!line) return null;
2845
  } else {
2846
  var n = lineNo(line);
2855
 
2856
  addWidget: function(pos, node, scroll, vert, horiz) {
2857
  var display = this.display;
2858
+ pos = cursorCoords(this, clipPos(this.doc, pos));
2859
+ var top = pos.bottom, left = pos.left;
2860
  node.style.position = "absolute";
2861
  display.sizer.appendChild(node);
2862
+ if (vert == "over") {
2863
+ top = pos.top;
2864
+ } else if (vert == "above" || vert == "near") {
2865
+ var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
2866
  hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
2867
+ // Default to positioning above (if specified and possible); otherwise default to positioning below
2868
+ if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
2869
  top = pos.top - node.offsetHeight;
2870
+ else if (pos.bottom + node.offsetHeight <= vspace)
2871
+ top = pos.bottom;
2872
  if (left + node.offsetWidth > hspace)
2873
  left = hspace - node.offsetWidth;
2874
  }
2886
  scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
2887
  },
2888
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2889
  triggerOnKeyDown: operation(null, onKeyDown),
2890
 
2891
  execCommand: function(cmd) {return commands[cmd](this);},
2892
 
2893
+ findPosH: function(from, amount, unit, visually) {
2894
+ var dir = 1;
2895
+ if (amount < 0) { dir = -1; amount = -amount; }
2896
+ for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
2897
+ cur = findPosH(this.doc, cur, dir, unit, visually);
2898
+ if (cur.hitSide) break;
2899
+ }
2900
+ return cur;
2901
+ },
2902
+
2903
  moveH: operation(null, function(dir, unit) {
2904
+ var sel = this.doc.sel, pos;
2905
+ if (sel.shift || sel.extend || posEq(sel.from, sel.to))
2906
+ pos = findPosH(this.doc, sel.head, dir, unit, this.options.rtlMoveVisually);
2907
+ else
2908
+ pos = dir < 0 ? sel.from : sel.to;
2909
+ extendSelection(this.doc, pos, pos, dir);
2910
  }),
2911
 
2912
  deleteH: operation(null, function(dir, unit) {
2913
+ var sel = this.doc.sel;
2914
+ if (!posEq(sel.from, sel.to)) replaceRange(this.doc, "", sel.from, sel.to, "+delete");
2915
+ else replaceRange(this.doc, "", sel.from, findPosH(this.doc, sel.head, dir, unit, false), "+delete");
2916
  this.curOp.userSelChange = true;
2917
  }),
2918
 
2919
+ findPosV: function(from, amount, unit, goalColumn) {
2920
+ var dir = 1, x = goalColumn;
2921
+ if (amount < 0) { dir = -1; amount = -amount; }
2922
+ for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
2923
+ var coords = cursorCoords(this, cur, "div");
2924
+ if (x == null) x = coords.left;
2925
+ else coords.left = x;
2926
+ cur = findPosV(this, coords, dir, unit);
2927
+ if (cur.hitSide) break;
 
2928
  }
2929
+ return cur;
2930
+ },
 
 
2931
 
2932
+ moveV: operation(null, function(dir, unit) {
2933
+ var sel = this.doc.sel;
2934
+ var pos = cursorCoords(this, sel.head, "div");
2935
+ if (sel.goalColumn != null) pos.left = sel.goalColumn;
2936
+ var target = findPosV(this, pos, dir, unit);
2937
+
2938
+ if (unit == "page") addToScrollPos(this, 0, charCoords(this, target, "div").top - pos.top);
2939
+ extendSelection(this.doc, target, target, dir);
2940
+ sel.goalColumn = pos.left;
2941
  }),
2942
 
2943
  toggleOverwrite: function() {
2944
+ if (this.state.overwrite = !this.state.overwrite)
2945
  this.display.cursor.className += " CodeMirror-overwrite";
2946
  else
2947
  this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", "");
2948
  },
2949
+ hasFocus: function() { return this.state.focused; },
2950
 
2951
+ scrollTo: operation(null, function(x, y) {
2952
+ updateScrollPos(this, x, y);
2953
+ }),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2954
  getScrollInfo: function() {
2955
  var scroller = this.display.scroller, co = scrollerCutOff;
2956
  return {left: scroller.scrollLeft, top: scroller.scrollTop,
2958
  clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
2959
  },
2960
 
2961
+ scrollIntoView: function(pos, margin) {
2962
+ if (typeof pos == "number") pos = Pos(pos, 0);
2963
+ if (!pos || pos.line != null) {
2964
+ pos = pos ? clipPos(this.doc, pos) : this.doc.sel.head;
2965
+ scrollPosIntoView(this, pos, margin);
2966
+ } else {
2967
+ scrollIntoView(this, pos.left, pos.top - margin, pos.right, pos.bottom + margin);
2968
+ }
2969
  },
2970
 
2971
  setSize: function(width, height) {
2980
  on: function(type, f) {on(this, type, f);},
2981
  off: function(type, f) {off(this, type, f);},
2982
 
2983
+ operation: function(f){return runInOp(this, f);},
2984
 
2985
+ refresh: operation(null, function() {
2986
  clearCaches(this);
2987
+ updateScrollPos(this, this.doc.scrollLeft, this.doc.scrollTop);
2988
+ regChange(this);
2989
+ }),
2990
+
2991
+ swapDoc: operation(null, function(doc) {
2992
+ var old = this.doc;
2993
+ old.cm = null;
2994
+ attachDoc(this, doc);
2995
+ clearCaches(this);
2996
+ updateScrollPos(this, doc.scrollLeft, doc.scrollTop);
2997
+ return old;
2998
+ }),
2999
 
3000
  getInputField: function(){return this.display.input;},
3001
  getWrapperElement: function(){return this.display.wrapper;},
3020
 
3021
  // These two are, on init, called from the constructor because they
3022
  // have to be initialized before the editor can start at all.
3023
+ option("value", "", function(cm, val) {
3024
+ cm.setValue(val);
3025
+ }, true);
3026
+ option("mode", null, function(cm, val) {
3027
+ cm.doc.modeOption = val;
3028
+ loadMode(cm);
3029
+ }, true);
3030
 
3031
  option("indentUnit", 2, loadMode, true);
3032
  option("indentWithTabs", false);
3034
  option("tabSize", 4, function(cm) {
3035
  loadMode(cm);
3036
  clearCaches(cm);
3037
+ regChange(cm);
3038
  }, true);
3039
  option("electricChars", true);
3040
+ option("rtlMoveVisually", !windows);
3041
 
3042
  option("theme", "default", function(cm) {
3043
  themeChanged(cm);
3054
  setGuttersForLineNumbers(cm.options);
3055
  guttersChanged(cm);
3056
  }, true);
3057
+ option("fixedGutter", true, function(cm, val) {
3058
+ cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
3059
+ cm.refresh();
3060
+ }, true);
3061
  option("lineNumbers", false, function(cm) {
3062
  setGuttersForLineNumbers(cm.options);
3063
  guttersChanged(cm);
3065
  option("firstLineNumber", 1, guttersChanged, true);
3066
  option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
3067
  option("showCursorWhenSelecting", false, updateSelection, true);
3068
+
3069
  option("readOnly", false, function(cm, val) {
3070
  if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
3071
  else if (!val) resetInput(cm, true);
3078
  option("workDelay", 100);
3079
  option("flattenSpans", true);
3080
  option("pollInterval", 100);
3081
+ option("undoDepth", 40, function(cm, val){cm.doc.history.undoDepth = val;});
3082
  option("viewportMargin", 10, function(cm){cm.refresh();}, true);
3083
 
3084
  option("tabindex", null, function(cm, val) {
3114
  };
3115
 
3116
  CodeMirror.getMode = function(options, spec) {
3117
+ spec = CodeMirror.resolveMode(spec);
3118
  var mfactory = modes[spec.name];
3119
  if (!mfactory) return CodeMirror.getMode(options, "text/plain");
3120
  var modeObj = mfactory(options, spec);
3138
  var modeExtensions = CodeMirror.modeExtensions = {};
3139
  CodeMirror.extendMode = function(mode, properties) {
3140
  var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
3141
+ copyObj(properties, exts);
 
3142
  };
3143
 
3144
  // EXTENSIONS
3186
  // STANDARD COMMANDS
3187
 
3188
  var commands = CodeMirror.commands = {
3189
+ selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()));},
3190
  killLine: function(cm) {
3191
  var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
3192
  if (!sel && cm.getLine(from.line).length == from.ch)
3193
+ cm.replaceRange("", from, Pos(from.line + 1, 0), "+delete");
3194
+ else cm.replaceRange("", from, sel ? to : Pos(from.line), "+delete");
3195
  },
3196
  deleteLine: function(cm) {
3197
  var l = cm.getCursor().line;
3198
+ cm.replaceRange("", Pos(l, 0), Pos(l), "+delete");
3199
  },
3200
  undo: function(cm) {cm.undo();},
3201
  redo: function(cm) {cm.redo();},
3202
+ goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
3203
+ goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
3204
  goLineStart: function(cm) {
3205
  cm.extendSelection(lineStart(cm, cm.getCursor().line));
3206
  },
3211
  if (!order || order[0].level == 0) {
3212
  var firstNonWS = Math.max(0, line.text.search(/\S/));
3213
  var inWS = cur.line == start.line && cur.ch <= firstNonWS && cur.ch;
3214
+ cm.extendSelection(Pos(start.line, inWS ? 0 : firstNonWS));
3215
  } else cm.extendSelection(start);
3216
  },
3217
  goLineEnd: function(cm) {
3218
  cm.extendSelection(lineEnd(cm, cm.getCursor().line));
3219
  },
3220
+ goLineRight: function(cm) {
3221
+ var top = cm.charCoords(cm.getCursor(), "div").top + 5;
3222
+ cm.extendSelection(cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"));
3223
+ },
3224
+ goLineLeft: function(cm) {
3225
+ var top = cm.charCoords(cm.getCursor(), "div").top + 5;
3226
+ cm.extendSelection(cm.coordsChar({left: 0, top: top}, "div"));
3227
+ },
3228
  goLineUp: function(cm) {cm.moveV(-1, "line");},
3229
  goLineDown: function(cm) {cm.moveV(1, "line");},
3230
  goPageUp: function(cm) {cm.moveV(-1, "page");},
3234
  goColumnLeft: function(cm) {cm.moveH(-1, "column");},
3235
  goColumnRight: function(cm) {cm.moveH(1, "column");},
3236
  goWordLeft: function(cm) {cm.moveH(-1, "word");},
3237
+ goGroupRight: function(cm) {cm.moveH(1, "group");},
3238
+ goGroupLeft: function(cm) {cm.moveH(-1, "group");},
3239
  goWordRight: function(cm) {cm.moveH(1, "word");},
3240
  delCharBefore: function(cm) {cm.deleteH(-1, "char");},
3241
  delCharAfter: function(cm) {cm.deleteH(1, "char");},
3242
  delWordBefore: function(cm) {cm.deleteH(-1, "word");},
3243
  delWordAfter: function(cm) {cm.deleteH(1, "word");},
3244
+ delGroupBefore: function(cm) {cm.deleteH(-1, "group");},
3245
+ delGroupAfter: function(cm) {cm.deleteH(1, "group");},
3246
  indentAuto: function(cm) {cm.indentSelection("smart");},
3247
  indentMore: function(cm) {cm.indentSelection("add");},
3248
  indentLess: function(cm) {cm.indentSelection("subtract");},
3249
+ insertTab: function(cm) {cm.replaceSelection("\t", "end", "+input");},
3250
  defaultTab: function(cm) {
3251
  if (cm.somethingSelected()) cm.indentSelection("add");
3252
+ else cm.replaceSelection("\t", "end", "+input");
3253
  },
3254
  transposeChars: function(cm) {
3255
  var cur = cm.getCursor(), line = cm.getLine(cur.line);
3256
  if (cur.ch > 0 && cur.ch < line.length - 1)
3257
  cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
3258
+ Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1));
3259
  },
3260
  newlineAndIndent: function(cm) {
3261
  operation(cm, function() {
3262
+ cm.replaceSelection("\n", "end", "+input");
3263
  cm.indentLine(cm.getCursor().line, null, true);
3264
  })();
3265
  },
3280
  keyMap.pcDefault = {
3281
  "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
3282
  "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
3283
+ "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
3284
+ "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
3285
  "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
3286
  "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
3287
  fallthrough: "basic"
3288
  };
3289
  keyMap.macDefault = {
3290
  "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
3291
+ "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
3292
+ "Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore",
3293
+ "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
3294
  "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
3295
  "Cmd-[": "indentLess", "Cmd-]": "indentMore",
3296
  fallthrough: ["basic", "emacsy"]
3310
  else return val;
3311
  }
3312
 
3313
+ function lookupKey(name, maps, handle) {
3314
  function lookup(map) {
3315
  map = getKeyMap(map);
3316
  var found = map[name];
3317
+ if (found === false) return "stop";
 
 
 
3318
  if (found != null && handle(found)) return true;
3319
+ if (map.nofallthrough) return "stop";
3320
+
 
 
3321
  var fallthrough = map.fallthrough;
3322
  if (fallthrough == null) return false;
3323
  if (Object.prototype.toString.call(fallthrough) != "[object Array]")
3324
  return lookup(fallthrough);
3325
  for (var i = 0, e = fallthrough.length; i < e; ++i) {
3326
+ var done = lookup(fallthrough[i]);
3327
+ if (done) return done;
3328
  }
3329
  return false;
3330
  }
3331
 
3332
+ for (var i = 0; i < maps.length; ++i) {
3333
+ var done = lookup(maps[i]);
3334
+ if (done) return done;
3335
+ }
3336
  }
3337
  function isModifierKey(event) {
3338
+ var name = keyNames[event.keyCode];
3339
  return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
3340
  }
3341
+ function keyName(event, noShift) {
3342
+ var name = keyNames[event.keyCode];
3343
+ if (name == null || event.altGraphKey) return false;
3344
+ if (event.altKey) name = "Alt-" + name;
3345
+ if (flipCtrlCmd ? event.metaKey : event.ctrlKey) name = "Ctrl-" + name;
3346
+ if (flipCtrlCmd ? event.ctrlKey : event.metaKey) name = "Cmd-" + name;
3347
+ if (!noShift && event.shiftKey) name = "Shift-" + name;
3348
+ return name;
3349
+ }
3350
+ CodeMirror.lookupKey = lookupKey;
3351
  CodeMirror.isModifierKey = isModifierKey;
3352
+ CodeMirror.keyName = keyName;
3353
 
3354
  // FROMTEXTAREA
3355
 
3358
  options.value = textarea.value;
3359
  if (!options.tabindex && textarea.tabindex)
3360
  options.tabindex = textarea.tabindex;
3361
+ if (!options.placeholder && textarea.placeholder)
3362
+ options.placeholder = textarea.placeholder;
3363
  // Set autofocus to true if this textarea is focused, or if it has
3364
  // autofocus and no other element is focused.
3365
  if (options.autofocus == null) {
3372
 
3373
  function save() {textarea.value = cm.getValue();}
3374
  if (textarea.form) {
 
3375
  on(textarea.form, "submit", save);
3376
+ // Deplorable hack to make the submit method do the right thing.
3377
+ if (!options.leaveSubmitMethodAlone) {
3378
+ var form = textarea.form, realSubmit = form.submit;
3379
+ try {
3380
+ var wrappedSubmit = form.submit = function() {
3381
+ save();
3382
+ form.submit = realSubmit;
3383
+ form.submit();
3384
+ form.submit = wrappedSubmit;
3385
+ };
3386
+ } catch(e) {}
3387
+ }
3388
  }
3389
 
3390
  textarea.style.display = "none";
3416
  this.pos = this.start = 0;
3417
  this.string = string;
3418
  this.tabSize = tabSize || 8;
3419
+ this.lastColumnPos = this.lastColumnValue = 0;
3420
  }
3421
 
3422
  StringStream.prototype = {
3449
  if (found > -1) {this.pos = found; return true;}
3450
  },
3451
  backUp: function(n) {this.pos -= n;},
3452
+ column: function() {
3453
+ if (this.lastColumnPos < this.start) {
3454
+ this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
3455
+ this.lastColumnPos = this.start;
3456
+ }
3457
+ return this.lastColumnValue;
3458
+ },
3459
  indentation: function() {return countColumn(this.string, null, this.tabSize);},
3460
  match: function(pattern, consume, caseInsensitive) {
3461
  if (typeof pattern == "string") {
3462
  var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
3463
+ var substr = this.string.substr(this.pos, pattern.length);
3464
+ if (cased(substr) == cased(pattern)) {
3465
  if (consume !== false) this.pos += pattern.length;
3466
  return true;
3467
  }
3478
 
3479
  // TEXTMARKERS
3480
 
3481
+ function TextMarker(doc, type) {
3482
  this.lines = [];
3483
  this.type = type;
3484
+ this.doc = doc;
3485
  }
3486
+ CodeMirror.TextMarker = TextMarker;
3487
 
3488
  TextMarker.prototype.clear = function() {
3489
  if (this.explicitlyCleared) return;
3490
+ var cm = this.doc.cm, withOp = cm && !cm.curOp;
3491
+ if (withOp) startOperation(cm);
3492
  var min = null, max = null;
3493
  for (var i = 0; i < this.lines.length; ++i) {
3494
  var line = this.lines[i];
3497
  line.markedSpans = removeMarkedSpan(line.markedSpans, span);
3498
  if (span.from != null)
3499
  min = lineNo(line);
3500
+ else if (this.collapsed && !lineIsHidden(this.doc, line) && cm)
3501
+ updateLineHeight(line, textHeight(cm.display));
3502
+ }
3503
+ if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
3504
+ var visual = visualLine(cm.doc, this.lines[i]), len = lineLength(cm.doc, visual);
3505
+ if (len > cm.display.maxLineLength) {
3506
+ cm.display.maxLine = visual;
3507
+ cm.display.maxLineLength = len;
3508
+ cm.display.maxLineChanged = true;
3509
+ }
3510
  }
3511
+
3512
+ if (min != null && cm) regChange(cm, min, max + 1);
3513
  this.lines.length = 0;
3514
  this.explicitlyCleared = true;
3515
+ if (this.collapsed && this.doc.cantEdit) {
3516
+ this.doc.cantEdit = false;
3517
+ if (cm) reCheckSelection(cm);
3518
  }
3519
+ if (withOp) endOperation(cm);
3520
+ signalLater(this, "clear");
3521
  };
3522
 
3523
  TextMarker.prototype.find = function() {
3527
  var span = getMarkedSpanFor(line.markedSpans, this);
3528
  if (span.from != null || span.to != null) {
3529
  var found = lineNo(line);
3530
+ if (span.from != null) from = Pos(found, span.from);
3531
+ if (span.to != null) to = Pos(found, span.to);
3532
  }
3533
  }
3534
  if (this.type == "bookmark") return from;
3535
  return from && {from: from, to: to};
3536
  };
3537
 
3538
+ TextMarker.prototype.getOptions = function(copyWidget) {
3539
+ var repl = this.replacedWith;
3540
+ return {className: this.className,
3541
+ inclusiveLeft: this.inclusiveLeft, inclusiveRight: this.inclusiveRight,
3542
+ atomic: this.atomic,
3543
+ collapsed: this.collapsed,
3544
+ replacedWith: copyWidget ? repl && repl.cloneNode(true) : repl,
3545
+ readOnly: this.readOnly,
3546
+ startStyle: this.startStyle, endStyle: this.endStyle};
3547
+ };
3548
+
3549
+ TextMarker.prototype.attachLine = function(line) {
3550
+ if (!this.lines.length && this.doc.cm) {
3551
+ var op = this.doc.cm.curOp;
3552
+ if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
3553
+ (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this);
3554
+ }
3555
+ this.lines.push(line);
3556
+ };
3557
+ TextMarker.prototype.detachLine = function(line) {
3558
+ this.lines.splice(indexOf(this.lines, line), 1);
3559
+ if (!this.lines.length && this.doc.cm) {
3560
+ var op = this.doc.cm.curOp;
3561
+ (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
3562
+ }
3563
+ };
3564
+
3565
+ function markText(doc, from, to, options, type) {
3566
+ if (options && options.shared) return markTextShared(doc, from, to, options, type);
3567
+ if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
3568
+
3569
+ var marker = new TextMarker(doc, type);
3570
  if (type == "range" && !posLess(from, to)) return marker;
3571
+ if (options) copyObj(options, marker);
 
3572
  if (marker.replacedWith) {
3573
  marker.collapsed = true;
3574
  marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
3575
  }
3576
  if (marker.collapsed) sawCollapsedSpans = true;
3577
 
3578
+ var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd, cm = doc.cm, updateMaxLine;
3579
  doc.iter(curLine, to.line + 1, function(line) {
3580
+ if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(doc, line) == cm.display.maxLine)
3581
+ updateMaxLine = true;
3582
  var span = {from: null, to: null, marker: marker};
3583
  size += line.text.length;
3584
  if (curLine == from.line) {span.from = from.ch; size -= from.ch;}
3589
  else updateLineHeight(line, 0);
3590
  }
3591
  addMarkedSpan(line, span);
 
 
3592
  ++curLine;
3593
  });
3594
+ if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
3595
+ if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
3596
+ });
3597
+
3598
+ if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); });
3599
 
3600
  if (marker.readOnly) {
3601
  sawReadOnlySpans = true;
3602
+ if (doc.history.done.length || doc.history.undone.length)
3603
+ doc.clearHistory();
3604
  }
3605
  if (marker.collapsed) {
3606
  if (collapsedAtStart != collapsedAtEnd)
3608
  marker.size = size;
3609
  marker.atomic = true;
3610
  }
3611
+ if (cm) {
3612
+ if (updateMaxLine) cm.curOp.updateMaxLine = true;
3613
+ if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed)
3614
+ regChange(cm, from.line, to.line + 1);
3615
+ if (marker.atomic) reCheckSelection(cm);
3616
+ }
3617
  return marker;
3618
  }
3619
 
3620
+ // SHARED TEXTMARKERS
3621
+
3622
+ function SharedTextMarker(markers, primary) {
3623
+ this.markers = markers;
3624
+ this.primary = primary;
3625
+ for (var i = 0, me = this; i < markers.length; ++i) {
3626
+ markers[i].parent = this;
3627
+ on(markers[i], "clear", function(){me.clear();});
3628
+ }
3629
+ }
3630
+ CodeMirror.SharedTextMarker = SharedTextMarker;
3631
+
3632
+ SharedTextMarker.prototype.clear = function() {
3633
+ if (this.explicitlyCleared) return;
3634
+ this.explicitlyCleared = true;
3635
+ for (var i = 0; i < this.markers.length; ++i)
3636
+ this.markers[i].clear();
3637
+ signalLater(this, "clear");
3638
+ };
3639
+ SharedTextMarker.prototype.find = function() {
3640
+ return this.primary.find();
3641
+ };
3642
+ SharedTextMarker.prototype.getOptions = function(copyWidget) {
3643
+ var inner = this.primary.getOptions(copyWidget);
3644
+ inner.shared = true;
3645
+ return inner;
3646
+ };
3647
+
3648
+ function markTextShared(doc, from, to, options, type) {
3649
+ options = copyObj(options);
3650
+ options.shared = false;
3651
+ var markers = [markText(doc, from, to, options, type)], primary = markers[0];
3652
+ var widget = options.replacedWith;
3653
+ linkedDocs(doc, function(doc) {
3654
+ if (widget) options.replacedWith = widget.cloneNode(true);
3655
+ markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
3656
+ for (var i = 0; i < doc.linked.length; ++i)
3657
+ if (doc.linked[i].isParent) return;
3658
+ primary = lst(markers);
3659
+ });
3660
+ return new SharedTextMarker(markers, primary);
3661
+ }
3662
+
3663
  // TEXTMARKER SPANS
3664
 
3665
  function getMarkedSpanFor(spans, marker) {
3675
  }
3676
  function addMarkedSpan(line, span) {
3677
  line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
3678
+ span.marker.attachLine(line);
3679
  }
3680
 
3681
+ function markedSpansBefore(old, startCh, isInsert) {
3682
  if (old) for (var i = 0, nw; i < old.length; ++i) {
3683
  var span = old[i], marker = span.marker;
3684
  var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
3685
+ if (startsBefore || marker.type == "bookmark" && span.from == startCh && (!isInsert || !span.marker.insertLeft)) {
3686
  var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
3687
  (nw || (nw = [])).push({from: span.from,
3688
  to: endsAfter ? null : span.to,
3692
  return nw;
3693
  }
3694
 
3695
+ function markedSpansAfter(old, endCh, isInsert) {
3696
  if (old) for (var i = 0, nw; i < old.length; ++i) {
3697
  var span = old[i], marker = span.marker;
3698
  var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
3699
+ if (endsAfter || marker.type == "bookmark" && span.from == endCh && (!isInsert || span.marker.insertLeft)) {
3700
  var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
3701
  (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
3702
  to: span.to == null ? null : span.to - endCh,
3706
  return nw;
3707
  }
3708
 
3709
+ function stretchSpansOverChange(doc, change) {
3710
+ var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
3711
+ var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
3712
+ if (!oldFirst && !oldLast) return null;
3713
+
3714
+ var startCh = change.from.ch, endCh = change.to.ch, isInsert = posEq(change.from, change.to);
3715
  // Get the spans that 'stick out' on both sides
3716
+ var first = markedSpansBefore(oldFirst, startCh, isInsert);
3717
+ var last = markedSpansAfter(oldLast, endCh, isInsert);
3718
 
3719
  // Next, merge those two ends
3720
+ var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
3721
  if (first) {
3722
  // Fix up .to properties of first
3723
  for (var i = 0; i < first.length; ++i) {
3747
  }
3748
  }
3749
 
3750
+ var newMarkers = [first];
3751
  if (!sameLine) {
3752
  // Fill gap with whole-line-spans
3753
+ var gap = change.text.length - 2, gapMarkers;
3754
  if (gap > 0 && first)
3755
  for (var i = 0; i < first.length; ++i)
3756
  if (first[i].to == null)
3757
  (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
3758
  for (var i = 0; i < gap; ++i)
3759
+ newMarkers.push(gapMarkers);
3760
+ newMarkers.push(last);
3761
  }
3762
  return newMarkers;
3763
  }
3764
 
3765
+ function mergeOldSpans(doc, change) {
3766
+ var old = getOldSpans(doc, change);
3767
+ var stretched = stretchSpansOverChange(doc, change);
3768
+ if (!old) return stretched;
3769
+ if (!stretched) return old;
3770
+
3771
+ for (var i = 0; i < old.length; ++i) {
3772
+ var oldCur = old[i], stretchCur = stretched[i];
3773
+ if (oldCur && stretchCur) {
3774
+ spans: for (var j = 0; j < stretchCur.length; ++j) {
3775
+ var span = stretchCur[j];
3776
+ for (var k = 0; k < oldCur.length; ++k)
3777
+ if (oldCur[k].marker == span.marker) continue spans;
3778
+ oldCur.push(span);
3779
+ }
3780
+ } else if (stretchCur) {
3781
+ old[i] = stretchCur;
3782
+ }
3783
+ }
3784
+ return old;
3785
+ }
3786
+
3787
  function removeReadOnlyRanges(doc, from, to) {
3788
  var markers = null;
3789
  doc.iter(from.line, to.line + 1, function(line) {
3796
  if (!markers) return null;
3797
  var parts = [{from: from, to: to}];
3798
  for (var i = 0; i < markers.length; ++i) {
3799
+ var mk = markers[i], m = mk.find();
3800
  for (var j = 0; j < parts.length; ++j) {
3801
  var p = parts[j];
3802
+ if (posLess(p.to, m.from) || posLess(m.to, p.from)) continue;
3803
  var newParts = [j, 1];
3804
+ if (posLess(p.from, m.from) || !mk.inclusiveLeft && posEq(p.from, m.from))
3805
+ newParts.push({from: p.from, to: m.from});
3806
+ if (posLess(m.to, p.to) || !mk.inclusiveRight && posEq(p.to, m.to))
3807
+ newParts.push({from: m.to, to: p.to});
3808
  parts.splice.apply(parts, newParts);
3809
  j += newParts.length - 1;
3810
  }
3834
  return line;
3835
  }
3836
 
3837
+ function lineIsHidden(doc, line) {
3838
  var sps = sawCollapsedSpans && line.markedSpans;
3839
  if (sps) for (var sp, i = 0; i < sps.length; ++i) {
3840
  sp = sps[i];
3841
  if (!sp.marker.collapsed) continue;
3842
  if (sp.from == null) return true;
3843
+ if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
3844
  return true;
3845
  }
3846
  }
3847
+ function lineIsHiddenInner(doc, line, span) {
3848
+ if (span.to == null) {
3849
+ var end = span.marker.find().to, endLine = getLine(doc, end.line);
3850
+ return lineIsHiddenInner(doc, endLine, getMarkedSpanFor(endLine.markedSpans, span.marker));
3851
+ }
3852
+ if (span.marker.inclusiveRight && span.to == line.text.length)
3853
  return true;
3854
  for (var sp, i = 0; i < line.markedSpans.length; ++i) {
3855
  sp = line.markedSpans[i];
3856
  if (sp.marker.collapsed && sp.from == span.to &&
3857
  (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
3858
+ lineIsHiddenInner(doc, line, sp)) return true;
3859
  }
3860
  }
3861
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3862
  function detachMarkedSpans(line) {
3863
  var spans = line.markedSpans;
3864
  if (!spans) return;
3865
+ for (var i = 0; i < spans.length; ++i)
3866
+ spans[i].marker.detachLine(line);
 
 
 
3867
  line.markedSpans = null;
3868
  }
3869
 
3870
  function attachMarkedSpans(line, spans) {
3871
  if (!spans) return;
3872
  for (var i = 0; i < spans.length; ++i)
3873
+ spans[i].marker.attachLine(line);
3874
  line.markedSpans = spans;
3875
  }
3876
 
3877
+ // LINE WIDGETS
3878
+
3879
+ var LineWidget = CodeMirror.LineWidget = function(cm, node, options) {
3880
+ for (var opt in options) if (options.hasOwnProperty(opt))
3881
+ this[opt] = options[opt];
3882
+ this.cm = cm;
3883
+ this.node = node;
3884
+ };
3885
+ function widgetOperation(f) {
3886
+ return function() {
3887
+ var withOp = !this.cm.curOp;
3888
+ if (withOp) startOperation(this.cm);
3889
+ try {var result = f.apply(this, arguments);}
3890
+ finally {if (withOp) endOperation(this.cm);}
3891
+ return result;
3892
+ };
3893
+ }
3894
+ LineWidget.prototype.clear = widgetOperation(function() {
3895
+ var ws = this.line.widgets, no = lineNo(this.line);
3896
+ if (no == null || !ws) return;
3897
+ for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
3898
+ if (!ws.length) this.line.widgets = null;
3899
+ updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this)));
3900
+ regChange(this.cm, no, no + 1);
3901
+ });
3902
+ LineWidget.prototype.changed = widgetOperation(function() {
3903
+ var oldH = this.height;
3904
+ this.height = null;
3905
+ var diff = widgetHeight(this) - oldH;
3906
+ if (!diff) return;
3907
+ updateLineHeight(this.line, this.line.height + diff);
3908
+ var no = lineNo(this.line);
3909
+ regChange(this.cm, no, no + 1);
3910
+ });
3911
+
3912
+ function widgetHeight(widget) {
3913
+ if (widget.height != null) return widget.height;
3914
+ if (!widget.node.parentNode || widget.node.parentNode.nodeType != 1)
3915
+ removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, "position: relative"));
3916
+ return widget.height = widget.node.offsetHeight;
3917
+ }
3918
+
3919
+ function addLineWidget(cm, handle, node, options) {
3920
+ var widget = new LineWidget(cm, node, options);
3921
+ if (widget.noHScroll) cm.display.alignWidgets = true;
3922
+ changeLine(cm, handle, function(line) {
3923
+ (line.widgets || (line.widgets = [])).push(widget);
3924
+ widget.line = line;
3925
+ if (!lineIsHidden(cm.doc, line) || widget.showIfHidden) {
3926
+ var aboveVisible = heightAtLine(cm, line) < cm.display.scroller.scrollTop;
3927
+ updateLineHeight(line, line.height + widgetHeight(widget));
3928
+ if (aboveVisible) addToScrollPos(cm, 0, widget.height);
3929
+ }
3930
+ return true;
3931
+ });
3932
+ return widget;
3933
+ }
3934
+
3935
  // LINE DATA STRUCTURE
3936
 
3937
  // Line objects. These hold state related to a line, including
3938
  // highlighting info (the styles array).
3939
+ function makeLine(text, markedSpans, estimateHeight) {
3940
+ var line = {text: text};
3941
  attachMarkedSpans(line, markedSpans);
3942
+ line.height = estimateHeight ? estimateHeight(line) : 1;
3943
  return line;
3944
  }
3945
 
3946
+ function updateLine(line, text, markedSpans, estimateHeight) {
3947
  line.text = text;
3948
+ if (line.stateAfter) line.stateAfter = null;
3949
+ if (line.styles) line.styles = null;
3950
  if (line.order != null) line.order = null;
3951
  detachMarkedSpans(line);
3952
  attachMarkedSpans(line, markedSpans);
3953
+ var estHeight = estimateHeight ? estimateHeight(line) : 1;
3954
+ if (estHeight != line.height) updateLineHeight(line, estHeight);
 
3955
  }
3956
 
3957
  function cleanUpLine(line) {
3962
  // Run the given mode's parser over a line, update the styles
3963
  // array, which contains alternating fragments of text and CSS
3964
  // classes.
3965
+ function runMode(cm, text, mode, state, f) {
3966
+ var flattenSpans = mode.flattenSpans;
3967
+ if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
3968
+ var curText = "", curStyle = null;
3969
+ var stream = new StringStream(text, cm.options.tabSize);
3970
+ if (text == "" && mode.blankLine) mode.blankLine(state);
3971
  while (!stream.eol()) {
3972
+ var style = mode.token(stream, state);
3973
+ if (stream.pos > 5000) {
3974
+ flattenSpans = false;
3975
+ // Webkit seems to refuse to render text nodes longer than 57444 characters
3976
+ stream.pos = Math.min(text.length, stream.start + 50000);
3977
+ style = null;
3978
+ }
3979
+ var substr = stream.current();
3980
  stream.start = stream.pos;
3981
  if (!flattenSpans || curStyle != style) {
3982
+ if (curText) f(curText, curStyle);
 
 
 
3983
  curText = substr; curStyle = style;
3984
  } else curText = curText + substr;
 
 
3985
  }
3986
+ if (curText) f(curText, curStyle);
3987
+ }
3988
+
3989
+ function highlightLine(cm, line, state) {
3990
+ // A styles array always starts with a number identifying the
3991
+ // mode/overlays that it is based on (for easy invalidation).
3992
+ var st = [cm.state.modeGen];
3993
+ // Compute the base array of styles
3994
+ runMode(cm, line.text, cm.doc.mode, state, function(txt, style) {st.push(txt, style);});
3995
+
3996
+ // Run overlays, adjust style array.
3997
+ for (var o = 0; o < cm.state.overlays.length; ++o) {
3998
+ var overlay = cm.state.overlays[o], i = 1;
3999
+ runMode(cm, line.text, overlay.mode, true, function(txt, style) {
4000
+ var start = i, len = txt.length;
4001
+ // Ensure there's a token end at the current position, and that i points at it
4002
+ while (len) {
4003
+ var cur = st[i], len_ = cur.length;
4004
+ if (len_ <= len) {
4005
+ len -= len_;
4006
+ } else {
4007
+ st.splice(i, 1, cur.slice(0, len), st[i+1], cur.slice(len));
4008
+ len = 0;
4009
+ }
4010
+ i += 2;
4011
+ }
4012
+ if (!style) return;
4013
+ if (overlay.opaque) {
4014
+ st.splice(start, i - start, txt, style);
4015
+ i = start + 2;
4016
+ } else {
4017
+ for (; start < i; start += 2) {
4018
+ var cur = st[start+1];
4019
+ st[start+1] = cur ? cur + " " + style : style;
4020
+ }
4021
+ }
4022
+ });
4023
  }
4024
+
4025
+ return st;
4026
+ }
4027
+
4028
+ function getLineStyles(cm, line) {
4029
+ if (!line.styles || line.styles[0] != cm.state.modeGen)
4030
+ line.styles = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
4031
+ return line.styles;
4032
  }
4033
 
4034
  // Lightweight form of highlight -- proceed over this line and
4035
  // update state, but don't save a style array.
4036
  function processLine(cm, line, state) {
4037
+ var mode = cm.doc.mode;
4038
  var stream = new StringStream(line.text, cm.options.tabSize);
4039
  if (line.text == "" && mode.blankLine) mode.blankLine(state);
4040
  while (!stream.eol() && stream.pos <= 5000) {
4054
  var merged, line = realLine, lineBefore, sawBefore, simple = true;
4055
  while (merged = collapsedSpanAtStart(line)) {
4056
  simple = false;
4057
+ line = getLine(cm.doc, merged.find().from.line);
4058
  if (!lineBefore) lineBefore = line;
4059
  }
4060
 
4063
  if (line.textClass) builder.pre.className = line.textClass;
4064
 
4065
  do {
 
 
4066
  builder.measure = line == realLine && measure;
4067
  builder.pos = 0;
4068
  builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
4069
+ if ((ie || webkit) && cm.getOption("lineWrapping"))
4070
+ builder.addToken = buildTokenSplitSpaces(builder.addToken);
4071
  if (measure && sawBefore && line != realLine && !builder.addedOne) {
4072
  measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
4073
  builder.addedOne = true;
4074
  }
4075
+ var next = insertLineContent(line, builder, getLineStyles(cm, line));
4076
  sawBefore = line == lineBefore;
4077
  if (next) {
4078
+ line = getLine(cm.doc, next.to.line);
4079
  simple = false;
4080
  }
4081
  } while (next);
4082
 
4083
  if (measure && !builder.addedOne)
4084
  measure[0] = builder.pre.appendChild(simple ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
4085
+ if (!builder.pre.firstChild && !lineIsHidden(cm.doc, realLine))
4086
  builder.pre.appendChild(document.createTextNode("\u00a0"));
4087
 
4088
+ var order;
4089
+ // Work around problem with the reported dimensions of single-char
4090
+ // direction spans on IE (issue #1129). See also the comment in
4091
+ // cursorCoords.
4092
+ if (measure && ie && (order = getOrder(line))) {
4093
+ var l = order.length - 1;
4094
+ if (order[l].from == order[l].to) --l;
4095
+ var last = order[l], prev = order[l - 1];
4096
+ if (last.from + 1 == last.to && prev && last.level < prev.level) {
4097
+ var span = measure[builder.pos - 1];
4098
+ if (span) span.parentNode.insertBefore(span.measureRight = zeroWidthElement(cm.display.measure),
4099
+ span.nextSibling);
4100
+ }
4101
+ }
4102
+
4103
+ signal(cm, "renderLine", cm, realLine, builder.pre);
4104
  return builder.pre;
4105
  }
4106
 
4107
+ var tokenSpecialChars = /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\uFEFF]/g;
4108
  function buildToken(builder, text, style, startStyle, endStyle) {
4109
  if (!text) return;
4110
  if (!tokenSpecialChars.test(text)) {
4144
  }
4145
 
4146
  function buildTokenMeasure(builder, text, style, startStyle, endStyle) {
4147
+ var wrapping = builder.cm.options.lineWrapping;
4148
  for (var i = 0; i < text.length; ++i) {
4149
+ var ch = text.charAt(i), start = i == 0;
4150
+ if (ch >= "\ud800" && ch < "\udbff" && i < text.length - 1) {
4151
+ ch = text.slice(i, i + 2);
4152
+ ++i;
4153
+ } else if (i && wrapping &&
4154
+ spanAffectsWrapping.test(text.slice(i - 1, i + 1))) {
4155
  builder.pre.appendChild(elt("wbr"));
4156
+ }
4157
+ var span = builder.measure[builder.pos] =
4158
+ buildToken(builder, ch, style,
4159
+ start && startStyle, i == text.length - 1 && endStyle);
4160
+ // In IE single-space nodes wrap differently than spaces
4161
+ // embedded in larger text nodes, except when set to
4162
+ // white-space: normal (issue #1268).
4163
+ if (ie && wrapping && ch == " " && i && !/\s/.test(text.charAt(i - 1)) &&
4164
+ i < text.length - 1 && !/\s/.test(text.charAt(i + 1)))
4165
+ span.style.whiteSpace = "normal";
4166
+ builder.pos += ch.length;
4167
  }
4168
  if (text.length) builder.addedOne = true;
4169
  }
4170
 
4171
+ function buildTokenSplitSpaces(inner) {
4172
+ function split(old) {
4173
+ var out = " ";
4174
+ for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0";
4175
+ out += " ";
4176
+ return out;
4177
+ }
4178
+ return function(builder, text, style, startStyle, endStyle) {
4179
+ return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle);
4180
+ };
4181
+ }
4182
+
4183
  function buildCollapsedSpan(builder, size, widget) {
4184
  if (widget) {
4185
  if (!builder.display) widget = widget.cloneNode(true);
4194
 
4195
  // Outputs a number of spans to make up a line, taking highlighting
4196
  // and marked text into account.
4197
+ function insertLineContent(line, builder, styles) {
4198
+ var spans = line.markedSpans;
4199
  if (!spans) {
4200
+ for (var i = 1; i < styles.length; i+=2)
4201
+ builder.addToken(builder, styles[i], styleToClass(styles[i+1]));
4202
  return;
4203
  }
4204
 
4205
  var allText = line.text, len = allText.length;
4206
+ var pos = 0, i = 1, text = "", style;
4207
  var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed;
4208
  for (;;) {
4209
  if (nextChange == pos) { // Update current marker set
4240
  var end = pos + text.length;
4241
  if (!collapsed) {
4242
  var tokenText = end > upto ? text.slice(0, upto - pos) : text;
4243
+ builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
4244
  spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "");
4245
  }
4246
  if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
4247
  pos = end;
4248
  spanStartStyle = "";
4249
  }
4250
+ text = styles[i++]; style = styleToClass(styles[i++]);
4251
  }
4252
  }
4253
  }
4254
 
4255
  // DOCUMENT DATA STRUCTURE
4256
 
4257
+ function updateDoc(doc, change, markedSpans, selAfter, estimateHeight) {
4258
+ function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
4259
+ function update(line, text, spans) {
4260
+ updateLine(line, text, spans, estimateHeight);
4261
+ signalLater(line, "change", line, change);
4262
+ }
4263
+
4264
+ var from = change.from, to = change.to, text = change.text;
4265
+ var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
4266
+ var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
4267
+
4268
+ // First adjust the line structure
4269
+ if (from.ch == 0 && to.ch == 0 && lastText == "") {
4270
+ // This is a whole-line replace. Treated specially to make
4271
+ // sure line objects move the way they are supposed to.
4272
+ for (var i = 0, e = text.length - 1, added = []; i < e; ++i)
4273
+ added.push(makeLine(text[i], spansFor(i), estimateHeight));
4274
+ update(lastLine, lastLine.text, lastSpans);
4275
+ if (nlines) doc.remove(from.line, nlines);
4276
+ if (added.length) doc.insert(from.line, added);
4277
+ } else if (firstLine == lastLine) {
4278
+ if (text.length == 1) {
4279
+ update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
4280
+ } else {
4281
+ for (var added = [], i = 1, e = text.length - 1; i < e; ++i)
4282
+ added.push(makeLine(text[i], spansFor(i), estimateHeight));
4283
+ added.push(makeLine(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
4284
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
4285
+ doc.insert(from.line + 1, added);
4286
+ }
4287
+ } else if (text.length == 1) {
4288
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
4289
+ doc.remove(from.line + 1, nlines);
4290
+ } else {
4291
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
4292
+ update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
4293
+ for (var i = 1, e = text.length - 1, added = []; i < e; ++i)
4294
+ added.push(makeLine(text[i], spansFor(i), estimateHeight));
4295
+ if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
4296
+ doc.insert(from.line + 1, added);
4297
+ }
4298
+
4299
+ signalLater(doc, "change", doc, change);
4300
+ setSelection(doc, selAfter.anchor, selAfter.head, null, true);
4301
+ }
4302
+
4303
  function LeafChunk(lines) {
4304
  this.lines = lines;
4305
  this.parent = null;
4312
 
4313
  LeafChunk.prototype = {
4314
  chunkSize: function() { return this.lines.length; },
4315
+ removeInner: function(at, n) {
4316
  for (var i = at, e = at + n; i < e; ++i) {
4317
  var line = this.lines[i];
4318
  this.height -= line.height;
4319
  cleanUpLine(line);
4320
+ signalLater(line, "delete");
4321
  }
4322
  this.lines.splice(at, n);
4323
  },
4324
  collapse: function(lines) {
4325
  lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
4326
  },
4327
+ insertInner: function(at, lines, height) {
4328
  this.height += height;
4329
  this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
4330
  for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
4350
 
4351
  BranchChunk.prototype = {
4352
  chunkSize: function() { return this.size; },
4353
+ removeInner: function(at, n) {
4354
  this.size -= n;
4355
  for (var i = 0; i < this.children.length; ++i) {
4356
  var child = this.children[i], sz = child.chunkSize();
4357
  if (at < sz) {
4358
  var rm = Math.min(n, sz - at), oldHeight = child.height;
4359
+ child.removeInner(at, rm);
4360
  this.height -= oldHeight - child.height;
4361
  if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
4362
  if ((n -= rm) == 0) break;
4373
  collapse: function(lines) {
4374
  for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
4375
  },
4376
+ insertInner: function(at, lines, height) {
 
 
 
 
 
4377
  this.size += lines.length;
4378
  this.height += height;
4379
  for (var i = 0, e = this.children.length; i < e; ++i) {
4380
  var child = this.children[i], sz = child.chunkSize();
4381
  if (at <= sz) {
4382
+ child.insertInner(at, lines, height);
4383
  if (child.lines && child.lines.length > 50) {
4384
  while (child.lines.length > 50) {
4385
  var spilled = child.lines.splice(child.lines.length - 25, 25);
4416
  } while (me.children.length > 10);
4417
  me.parent.maybeSpill();
4418
  },
 
4419
  iterN: function(at, n, op) {
4420
  for (var i = 0, e = this.children.length; i < e; ++i) {
4421
  var child = this.children[i], sz = child.chunkSize();
4429
  }
4430
  };
4431
 
4432
+ var nextDocId = 0;
4433
+ var Doc = CodeMirror.Doc = function(text, mode, firstLine) {
4434
+ if (!(this instanceof Doc)) return new Doc(text, mode, firstLine);
4435
+ if (firstLine == null) firstLine = 0;
4436
+
4437
+ BranchChunk.call(this, [new LeafChunk([makeLine("", null)])]);
4438
+ this.first = firstLine;
4439
+ this.scrollTop = this.scrollLeft = 0;
4440
+ this.cantEdit = false;
4441
+ this.history = makeHistory();
4442
+ this.frontier = firstLine;
4443
+ var start = Pos(firstLine, 0);
4444
+ this.sel = {from: start, to: start, head: start, anchor: start, shift: false, extend: false, goalColumn: null};
4445
+ this.id = ++nextDocId;
4446
+ this.modeOption = mode;
4447
+
4448
+ if (typeof text == "string") text = splitLines(text);
4449
+ updateDoc(this, {from: start, to: start, text: text}, null, {head: start, anchor: start});
4450
+ };
4451
+
4452
+ Doc.prototype = createObj(BranchChunk.prototype, {
4453
+ iter: function(from, to, op) {
4454
+ if (op) this.iterN(from - this.first, to - from, op);
4455
+ else this.iterN(this.first, this.first + this.size, from);
4456
+ },
4457
+
4458
+ insert: function(at, lines) {
4459
+ var height = 0;
4460
+ for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
4461
+ this.insertInner(at - this.first, lines, height);
4462
+ },
4463
+ remove: function(at, n) { this.removeInner(at - this.first, n); },
4464
+
4465
+ getValue: function(lineSep) {
4466
+ var lines = getLines(this, this.first, this.first + this.size);
4467
+ if (lineSep === false) return lines;
4468
+ return lines.join(lineSep || "\n");
4469
+ },
4470
+ setValue: function(code) {
4471
+ var top = Pos(this.first, 0), last = this.first + this.size - 1;
4472
+ makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
4473
+ text: splitLines(code), origin: "setValue"},
4474
+ {head: top, anchor: top}, true);
4475
+ },
4476
+ replaceRange: function(code, from, to, origin) {
4477
+ from = clipPos(this, from);
4478
+ to = to ? clipPos(this, to) : from;
4479
+ replaceRange(this, code, from, to, origin);
4480
+ },
4481
+ getRange: function(from, to, lineSep) {
4482
+ var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
4483
+ if (lineSep === false) return lines;
4484
+ return lines.join(lineSep || "\n");
4485
+ },
4486
+
4487
+ getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
4488
+ setLine: function(line, text) {
4489
+ if (isLine(this, line))
4490
+ replaceRange(this, text, Pos(line, 0), clipPos(this, Pos(line)));
4491
+ },
4492
+ removeLine: function(line) {
4493
+ if (isLine(this, line))
4494
+ replaceRange(this, "", Pos(line, 0), clipPos(this, Pos(line + 1, 0)));
4495
+ },
4496
+
4497
+ getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
4498
+ getLineNumber: function(line) {return lineNo(line);},
4499
+
4500
+ lineCount: function() {return this.size;},
4501
+ firstLine: function() {return this.first;},
4502
+ lastLine: function() {return this.first + this.size - 1;},
4503
+
4504
+ clipPos: function(pos) {return clipPos(this, pos);},
4505
+
4506
+ getCursor: function(start) {
4507
+ var sel = this.sel, pos;
4508
+ if (start == null || start == "head") pos = sel.head;
4509
+ else if (start == "anchor") pos = sel.anchor;
4510
+ else if (start == "end" || start === false) pos = sel.to;
4511
+ else pos = sel.from;
4512
+ return copyPos(pos);
4513
+ },
4514
+ somethingSelected: function() {return !posEq(this.sel.head, this.sel.anchor);},
4515
+
4516
+ setCursor: docOperation(function(line, ch, extend) {
4517
+ var pos = clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line);
4518
+ if (extend) extendSelection(this, pos);
4519
+ else setSelection(this, pos, pos);
4520
+ }),
4521
+ setSelection: docOperation(function(anchor, head) {
4522
+ setSelection(this, clipPos(this, anchor), clipPos(this, head || anchor));
4523
+ }),
4524
+ extendSelection: docOperation(function(from, to) {
4525
+ extendSelection(this, clipPos(this, from), to && clipPos(this, to));
4526
+ }),
4527
+
4528
+ getSelection: function(lineSep) {return this.getRange(this.sel.from, this.sel.to, lineSep);},
4529
+ replaceSelection: function(code, collapse, origin) {
4530
+ makeChange(this, {from: this.sel.from, to: this.sel.to, text: splitLines(code), origin: origin}, collapse || "around");
4531
+ },
4532
+ undo: docOperation(function() {makeChangeFromHistory(this, "undo");}),
4533
+ redo: docOperation(function() {makeChangeFromHistory(this, "redo");}),
4534
+
4535
+ setExtending: function(val) {this.sel.extend = val;},
4536
+
4537
+ historySize: function() {
4538
+ var hist = this.history;
4539
+ return {undo: hist.done.length, redo: hist.undone.length};
4540
+ },
4541
+ clearHistory: function() {this.history = makeHistory();},
4542
+
4543
+ markClean: function() {
4544
+ this.history.dirtyCounter = 0;
4545
+ this.history.lastOp = this.history.lastOrigin = null;
4546
+ },
4547
+ isClean: function () {return this.history.dirtyCounter == 0;},
4548
+
4549
+ getHistory: function() {
4550
+ return {done: copyHistoryArray(this.history.done),
4551
+ undone: copyHistoryArray(this.history.undone)};
4552
+ },
4553
+ setHistory: function(histData) {
4554
+ var hist = this.history = makeHistory();
4555
+ hist.done = histData.done.slice(0);
4556
+ hist.undone = histData.undone.slice(0);
4557
+ },
4558
+
4559
+ markText: function(from, to, options) {
4560
+ return markText(this, clipPos(this, from), clipPos(this, to), options, "range");
4561
+ },
4562
+ setBookmark: function(pos, options) {
4563
+ var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
4564
+ insertLeft: options && options.insertLeft};
4565
+ pos = clipPos(this, pos);
4566
+ return markText(this, pos, pos, realOpts, "bookmark");
4567
+ },
4568
+ findMarksAt: function(pos) {
4569
+ pos = clipPos(this, pos);
4570
+ var markers = [], spans = getLine(this, pos.line).markedSpans;
4571
+ if (spans) for (var i = 0; i < spans.length; ++i) {
4572
+ var span = spans[i];
4573
+ if ((span.from == null || span.from <= pos.ch) &&
4574
+ (span.to == null || span.to >= pos.ch))
4575
+ markers.push(span.marker.parent || span.marker);
4576
+ }
4577
+ return markers;
4578
+ },
4579
+ getAllMarks: function() {
4580
+ var markers = [];
4581
+ this.iter(function(line) {
4582
+ var sps = line.markedSpans;
4583
+ if (sps) for (var i = 0; i < sps.length; ++i)
4584
+ if (sps[i].from != null) markers.push(sps[i].marker);
4585
+ });
4586
+ return markers;
4587
+ },
4588
+
4589
+ posFromIndex: function(off) {
4590
+ var ch, lineNo = this.first;
4591
+ this.iter(function(line) {
4592
+ var sz = line.text.length + 1;
4593
+ if (sz > off) { ch = off; return true; }
4594
+ off -= sz;
4595
+ ++lineNo;
4596
+ });
4597
+ return clipPos(this, Pos(lineNo, ch));
4598
+ },
4599
+ indexFromPos: function (coords) {
4600
+ coords = clipPos(this, coords);
4601
+ var index = coords.ch;
4602
+ if (coords.line < this.first || coords.ch < 0) return 0;
4603
+ this.iter(this.first, coords.line, function (line) {
4604
+ index += line.text.length + 1;
4605
+ });
4606
+ return index;
4607
+ },
4608
+
4609
+ copy: function(copyHistory) {
4610
+ var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first);
4611
+ doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
4612
+ doc.sel = {from: this.sel.from, to: this.sel.to, head: this.sel.head, anchor: this.sel.anchor,
4613
+ shift: this.sel.shift, extend: false, goalColumn: this.sel.goalColumn};
4614
+ if (copyHistory) {
4615
+ doc.history.undoDepth = this.history.undoDepth;
4616
+ doc.setHistory(this.getHistory());
4617
+ }
4618
+ return doc;
4619
+ },
4620
+
4621
+ linkedDoc: function(options) {
4622
+ if (!options) options = {};
4623
+ var from = this.first, to = this.first + this.size;
4624
+ if (options.from != null && options.from > from) from = options.from;
4625
+ if (options.to != null && options.to < to) to = options.to;
4626
+ var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from);
4627
+ if (options.sharedHist) copy.history = this.history;
4628
+ (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
4629
+ copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
4630
+ return copy;
4631
+ },
4632
+ unlinkDoc: function(other) {
4633
+ if (other instanceof CodeMirror) other = other.doc;
4634
+ if (this.linked) for (var i = 0; i < this.linked.length; ++i) {
4635
+ var link = this.linked[i];
4636
+ if (link.doc != other) continue;
4637
+ this.linked.splice(i, 1);
4638
+ other.unlinkDoc(this);
4639
+ break;
4640
+ }
4641
+ // If the histories were shared, split them again
4642
+ if (other.history == this.history) {
4643
+ var splitIds = [other.id];
4644
+ linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
4645
+ other.history = makeHistory();
4646
+ other.history.done = copyHistoryArray(this.history.done, splitIds);
4647
+ other.history.undone = copyHistoryArray(this.history.undone, splitIds);
4648
+ }
4649
+ },
4650
+ iterLinkedDocs: function(f) {linkedDocs(this, f);},
4651
+
4652
+ getMode: function() {return this.mode;},
4653
+ getEditor: function() {return this.cm;}
4654
+ });
4655
+
4656
+ Doc.prototype.eachLine = Doc.prototype.iter;
4657
+
4658
+ // The Doc methods that should be available on CodeMirror instances
4659
+ var dontDelegate = "iter insert remove copy getEditor".split(" ");
4660
+ for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
4661
+ CodeMirror.prototype[prop] = (function(method) {
4662
+ return function() {return method.apply(this.doc, arguments);};
4663
+ })(Doc.prototype[prop]);
4664
+
4665
+ function linkedDocs(doc, f, sharedHistOnly) {
4666
+ function propagate(doc, skip, sharedHist) {
4667
+ if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
4668
+ var rel = doc.linked[i];
4669
+ if (rel.doc == skip) continue;
4670
+ var shared = sharedHist && rel.sharedHist;
4671
+ if (sharedHistOnly && !shared) continue;
4672
+ f(rel.doc, shared);
4673
+ propagate(rel.doc, doc, shared);
4674
+ }
4675
+ }
4676
+ propagate(doc, null, true);
4677
+ }
4678
+
4679
+ function attachDoc(cm, doc) {
4680
+ if (doc.cm) throw new Error("This document is already in use.");
4681
+ cm.doc = doc;
4682
+ doc.cm = cm;
4683
+ estimateLineHeights(cm);
4684
+ loadMode(cm);
4685
+ if (!cm.options.lineWrapping) computeMaxLength(cm);
4686
+ cm.options.mode = doc.modeOption;
4687
+ regChange(cm);
4688
+ }
4689
+
4690
  // LINE UTILITIES
4691
 
4692
  function getLine(chunk, n) {
4693
+ n -= chunk.first;
4694
  while (!chunk.lines) {
4695
  for (var i = 0;; ++i) {
4696
  var child = chunk.children[i], sz = child.chunkSize();
4701
  return chunk.lines[n];
4702
  }
4703
 
4704
+ function getBetween(doc, start, end) {
4705
+ var out = [], n = start.line;
4706
+ doc.iter(start.line, end.line + 1, function(line) {
4707
+ var text = line.text;
4708
+ if (n == end.line) text = text.slice(0, end.ch);
4709
+ if (n == start.line) text = text.slice(start.ch);
4710
+ out.push(text);
4711
+ ++n;
4712
+ });
4713
+ return out;
4714
+ }
4715
+ function getLines(doc, from, to) {
4716
+ var out = [];
4717
+ doc.iter(from, to, function(line) { out.push(line.text); });
4718
+ return out;
4719
+ }
4720
+
4721
  function updateLineHeight(line, height) {
4722
  var diff = height - line.height;
4723
  for (var n = line; n; n = n.parent) n.height += diff;
4732
  no += chunk.children[i].chunkSize();
4733
  }
4734
  }
4735
+ return no + cur.first;
4736
  }
4737
 
4738
  function lineAtHeight(chunk, h) {
4739
+ var n = chunk.first;
4740
  outer: do {
4741
  for (var i = 0, e = chunk.children.length; i < e; ++i) {
4742
  var child = chunk.children[i], ch = child.height;
4755
  }
4756
 
4757
  function heightAtLine(cm, lineObj) {
4758
+ lineObj = visualLine(cm.doc, lineObj);
4759
 
4760
  var h = 0, chunk = lineObj.parent;
4761
  for (var i = 0; i < chunk.lines.length; ++i) {
4786
  // Arrays of history events. Doing something adds an event to
4787
  // done and clears undo. Undoing moves events from done to
4788
  // undone, redoing moves them in the other direction.
4789
+ done: [], undone: [], undoDepth: Infinity,
4790
  // Used to track when changes can be merged into a single undo
4791
  // event
4792
  lastTime: 0, lastOp: null, lastOrigin: null,
4795
  };
4796
  }
4797
 
4798
+ function attachLocalSpans(doc, change, from, to) {
4799
+ var existing = change["spans_" + doc.id], n = 0;
4800
+ doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
4801
+ if (line.markedSpans)
4802
+ (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans;
4803
+ ++n;
4804
+ });
4805
+ }
4806
+
4807
+ function historyChangeFromChange(doc, change) {
4808
+ var histChange = {from: change.from, to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
4809
+ attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
4810
+ linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
4811
+ return histChange;
4812
+ }
4813
+
4814
+ function addToHistory(doc, change, selAfter, opId) {
4815
+ var hist = doc.history;
4816
+ hist.undone.length = 0;
4817
+ var time = +new Date, cur = lst(hist.done);
4818
+
4819
  if (cur &&
4820
+ (hist.lastOp == opId ||
4821
+ hist.lastOrigin == change.origin && change.origin &&
4822
+ ((change.origin.charAt(0) == "+" && hist.lastTime > time - 600) || change.origin.charAt(0) == "*"))) {
4823
  // Merge this change into the last event
4824
+ var last = lst(cur.changes);
4825
+ if (posEq(change.from, change.to) && posEq(change.from, last.to)) {
4826
+ // Optimized case for simple insertion -- don't want to add
4827
+ // new changesets for every character typed
4828
+ last.to = changeEnd(change);
4829
  } else {
4830
+ // Add new sub-event
4831
+ cur.changes.push(historyChangeFromChange(doc, change));
 
 
 
 
 
4832
  }
4833
+ cur.anchorAfter = selAfter.anchor; cur.headAfter = selAfter.head;
4834
  } else {
4835
  // Can not be merged, start a new event.
4836
+ cur = {changes: [historyChangeFromChange(doc, change)],
4837
+ anchorBefore: doc.sel.anchor, headBefore: doc.sel.head,
4838
+ anchorAfter: selAfter.anchor, headAfter: selAfter.head};
4839
+ hist.done.push(cur);
4840
+ while (hist.done.length > hist.undoDepth)
4841
+ hist.done.shift();
4842
+ if (hist.dirtyCounter < 0)
4843
+ // The user has made a change after undoing past the last clean state.
4844
+ // We can never get back to a clean state now until markClean() is called.
4845
+ hist.dirtyCounter = NaN;
4846
  else
4847
+ hist.dirtyCounter++;
4848
+ }
4849
+ hist.lastTime = time;
4850
+ hist.lastOp = opId;
4851
+ hist.lastOrigin = change.origin;
4852
+ }
4853
+
4854
+ function removeClearedSpans(spans) {
4855
+ if (!spans) return null;
4856
+ for (var i = 0, out; i < spans.length; ++i) {
4857
+ if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
4858
+ else if (out) out.push(spans[i]);
4859
+ }
4860
+ return !out ? spans : out.length ? out : null;
4861
+ }
4862
+
4863
+ function getOldSpans(doc, change) {
4864
+ var found = change["spans_" + doc.id];
4865
+ if (!found) return null;
4866
+ for (var i = 0, nw = []; i < change.text.length; ++i)
4867
+ nw.push(removeClearedSpans(found[i]));
4868
+ return nw;
4869
+ }
4870
+
4871
+ // Used both to provide a JSON-safe object in .getHistory, and, when
4872
+ // detaching a document, to split the history in two
4873
+ function copyHistoryArray(events, newGroup) {
4874
+ for (var i = 0, copy = []; i < events.length; ++i) {
4875
+ var event = events[i], changes = event.changes, newChanges = [];
4876
+ copy.push({changes: newChanges, anchorBefore: event.anchorBefore, headBefore: event.headBefore,
4877
+ anchorAfter: event.anchorAfter, headAfter: event.headAfter});
4878
+ for (var j = 0; j < changes.length; ++j) {
4879
+ var change = changes[j], m;
4880
+ newChanges.push({from: change.from, to: change.to, text: change.text});
4881
+ if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) {
4882
+ if (indexOf(newGroup, Number(m[1])) > -1) {
4883
+ lst(newChanges)[prop] = change[prop];
4884
+ delete change[prop];
4885
+ }
4886
+ }
4887
+ }
4888
+ }
4889
+ return copy;
4890
+ }
4891
+
4892
+ // Rebasing/resetting history to deal with externally-sourced changes
4893
+
4894
+ function rebaseHistSel(pos, from, to, diff) {
4895
+ if (to < pos.line) {
4896
+ pos.line += diff;
4897
+ } else if (from < pos.line) {
4898
+ pos.line = from;
4899
+ pos.ch = 0;
4900
+ }
4901
+ }
4902
+
4903
+ // Tries to rebase an array of history events given a change in the
4904
+ // document. If the change touches the same lines as the event, the
4905
+ // event, and everything 'behind' it, is discarded. If the change is
4906
+ // before the event, the event's positions are updated. Uses a
4907
+ // copy-on-write scheme for the positions, to avoid having to
4908
+ // reallocate them all on every rebase, but also avoid problems with
4909
+ // shared position objects being unsafely updated.
4910
+ function rebaseHistArray(array, from, to, diff) {
4911
+ for (var i = 0; i < array.length; ++i) {
4912
+ var sub = array[i], ok = true;
4913
+ for (var j = 0; j < sub.changes.length; ++j) {
4914
+ var cur = sub.changes[j];
4915
+ if (!sub.copied) { cur.from = copyPos(cur.from); cur.to = copyPos(cur.to); }
4916
+ if (to < cur.from.line) {
4917
+ cur.from.line += diff;
4918
+ cur.to.line += diff;
4919
+ } else if (from <= cur.to.line) {
4920
+ ok = false;
4921
+ break;
4922
+ }
4923
+ }
4924
+ if (!sub.copied) {
4925
+ sub.anchorBefore = copyPos(sub.anchorBefore); sub.headBefore = copyPos(sub.headBefore);
4926
+ sub.anchorAfter = copyPos(sub.anchorAfter); sub.readAfter = copyPos(sub.headAfter);
4927
+ sub.copied = true;
4928
+ }
4929
+ if (!ok) {
4930
+ array.splice(0, i + 1);
4931
+ i = 0;
4932
+ } else {
4933
+ rebaseHistSel(sub.anchorBefore); rebaseHistSel(sub.headBefore);
4934
+ rebaseHistSel(sub.anchorAfter); rebaseHistSel(sub.headAfter);
4935
+ }
4936
  }
4937
+ }
4938
+
4939
+ function rebaseHist(hist, change) {
4940
+ var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
4941
+ rebaseHistArray(hist.done, from, to, diff);
4942
+ rebaseHistArray(hist.undone, from, to, diff);
4943
  }
4944
 
4945
  // EVENT OPERATORS
4976
  return b;
4977
  }
4978
 
 
 
 
 
 
 
 
4979
  // EVENT HANDLING
4980
 
4981
  function on(emitter, type, f) {
5010
  for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
5011
  }
5012
 
5013
+ var delayedCallbacks, delayedCallbackDepth = 0;
5014
+ function signalLater(emitter, type /*, values...*/) {
5015
  var arr = emitter._handlers && emitter._handlers[type];
5016
  if (!arr) return;
5017
+ var args = Array.prototype.slice.call(arguments, 2);
5018
+ if (!delayedCallbacks) {
5019
+ ++delayedCallbackDepth;
5020
+ delayedCallbacks = [];
5021
+ setTimeout(fireDelayed, 0);
5022
+ }
5023
  function bnd(f) {return function(){f.apply(null, args);};};
5024
  for (var i = 0; i < arr.length; ++i)
5025
+ delayedCallbacks.push(bnd(arr[i]));
5026
+ }
5027
+
5028
+ function fireDelayed() {
5029
+ --delayedCallbackDepth;
5030
+ var delayed = delayedCallbacks;
5031
+ delayedCallbacks = null;
5032
+ for (var i = 0; i < delayed.length; ++i) delayed[i]();
5033
  }
5034
 
5035
  function hasHandler(emitter, type) {
5053
 
5054
  // Counts the column offset in a string, taking tabs into account.
5055
  // Used mostly to find indentation.
5056
+ function countColumn(string, end, tabSize, startIndex, startValue) {
5057
  if (end == null) {
5058
  end = string.search(/[^\s\u00a0]/);
5059
  if (end == -1) end = string.length;
5060
  }
5061
+ for (var i = startIndex || 0, n = startValue || 0; i < end; ++i) {
5062
  if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
5063
  else ++n;
5064
  }
5089
  return -1;
5090
  }
5091
 
5092
+ function createObj(base, props) {
5093
+ function Obj() {}
5094
+ Obj.prototype = base;
5095
+ var inst = new Obj();
5096
+ if (props) copyObj(props, inst);
5097
+ return inst;
5098
+ }
5099
+
5100
+ function copyObj(obj, target) {
5101
+ if (!target) target = {};
5102
+ for (var prop in obj) if (obj.hasOwnProperty(prop)) target[prop] = obj[prop];
5103
+ return target;
5104
+ }
5105
+
5106
  function emptyArray(size) {
5107
  for (var a = [], i = 0; i < size; ++i) a.push(undefined);
5108
  return a;
5120
  }
5121
 
5122
  function isEmpty(obj) {
5123
+ for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false;
5124
+ return true;
 
5125
  }
5126
 
5127
+ var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F\udc00-\udfff]/;
5128
 
5129
  // DOM UTILITIES
5130
 
5138
  }
5139
 
5140
  function removeChildren(e) {
5141
+ for (var count = e.childNodes.length; count > 0; --count)
5142
+ e.removeChild(e.firstChild);
5143
  return e;
5144
  }
5145
 
5154
  } else e.textContent = str;
5155
  }
5156
 
5157
+ function getRect(node) {
5158
+ return node.getBoundingClientRect();
5159
+ }
5160
+ CodeMirror.replaceGetRect = function(f) { getRect = f; };
5161
+
5162
  // FEATURE DETECTION
5163
 
5164
  // Detect drag-and-drop
5179
  // various browsers.
5180
  var spanAffectsWrapping = /^$/; // Won't match any two-character string
5181
  if (gecko) spanAffectsWrapping = /$'/;
5182
+ else if (safari && !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent)) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
5183
+ else if (webkit) spanAffectsWrapping = /[~!#%&*)=+}\]|\"\.>,:;][({[<]|-[^\-?\.]|\?[\w~`@#$%\^&*(_=+{[|><]/;
5184
 
5185
  var knownScrollbarWidth;
5186
  function scrollbarWidth(measure) {
5267
  if (!order) return f(from, to, "ltr");
5268
  for (var i = 0; i < order.length; ++i) {
5269
  var part = order[i];
5270
+ if (part.from < to && part.to > from || from == to && part.to == from)
5271
  f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
5272
  }
5273
  }
5283
  }
5284
 
5285
  function lineStart(cm, lineN) {
5286
+ var line = getLine(cm.doc, lineN);
5287
+ var visual = visualLine(cm.doc, line);
5288
  if (visual != line) lineN = lineNo(visual);
5289
  var order = getOrder(visual);
5290
  var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
5291
+ return Pos(lineN, ch);
5292
  }
5293
+ function lineEnd(cm, lineN) {
5294
  var merged, line;
5295
+ while (merged = collapsedSpanAtEnd(line = getLine(cm.doc, lineN)))
5296
+ lineN = merged.find().to.line;
5297
  var order = getOrder(line);
5298
  var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
5299
+ return Pos(lineN, ch);
5300
  }
5301
 
5302
  // This is somewhat involved. It is needed in order to move
5385
 
5386
  var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
5387
  var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
5388
+ // Browsers seem to always treat the boundaries of block elements as being L.
5389
+ var outerType = "L";
5390
 
5391
+ return function(str) {
5392
  if (!bidiRE.test(str)) return false;
5393
+ var len = str.length, types = [];
5394
+ for (var i = 0, type; i < len; ++i)
5395
  types.push(type = charType(str.charCodeAt(i)));
 
 
 
 
 
 
5396
 
5397
  // W1. Examine each non-spacing mark (NSM) in the level run, and
5398
  // change the type of the NSM to the type of the previous
5399
  // character. If the NSM is at the start of the level run, it will
5400
  // get the type of sor.
5401
+ for (var i = 0, prev = outerType; i < len; ++i) {
5402
  var type = types[i];
5403
  if (type == "m") types[i] = prev;
5404
  else prev = type;
5409
  // AL is found, change the type of the European number to Arabic
5410
  // number.
5411
  // W3. Change all ALs to R.
5412
+ for (var i = 0, cur = outerType; i < len; ++i) {
5413
  var type = types[i];
5414
  if (type == "1" && cur == "r") types[i] = "n";
5415
  else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
5444
  // W7. Search backwards from each instance of a European number
5445
  // until the first strong type (R, L, or sor) is found. If an L is
5446
  // found, then change the type of the European number to L.
5447
+ for (var i = 0, cur = outerType; i < len; ++i) {
5448
  var type = types[i];
5449
  if (cur == "L" && type == "1") types[i] = "L";
5450
  else if (isStrong.test(type)) cur = type;
5459
  for (var i = 0; i < len; ++i) {
5460
  if (isNeutral.test(types[i])) {
5461
  for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
5462
+ var before = (i ? types[i-1] : outerType) == "L";
5463
+ var after = (end < len - 1 ? types[end] : outerType) == "L";
5464
  var replace = before || after ? "L" : "R";
5465
  for (var j = i; j < end; ++j) types[j] = replace;
5466
  i = end - 1;
5510
 
5511
  // THE END
5512
 
5513
+ CodeMirror.version = "3.11";
5514
 
5515
  return CodeMirror;
5516
  })();
assets/{mode → codemirror/mode}/clike.js RENAMED
@@ -1,6 +1,7 @@
1
  CodeMirror.defineMode("clike", function(config, parserConfig) {
2
  var indentUnit = config.indentUnit,
3
  statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
 
4
  keywords = parserConfig.keywords || {},
5
  builtin = parserConfig.builtin || {},
6
  blockKeywords = parserConfig.blockKeywords || {},
@@ -149,6 +150,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
149
  if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
150
  var closing = firstChar == ctx.type;
151
  if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
 
152
  else if (ctx.align) return ctx.column + (closing ? 0 : 1);
153
  else return ctx.indented + (closing ? 0 : indentUnit);
154
  },
1
  CodeMirror.defineMode("clike", function(config, parserConfig) {
2
  var indentUnit = config.indentUnit,
3
  statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
4
+ dontAlignCalls = parserConfig.dontAlignCalls,
5
  keywords = parserConfig.keywords || {},
6
  builtin = parserConfig.builtin || {},
7
  blockKeywords = parserConfig.blockKeywords || {},
150
  if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
151
  var closing = firstChar == ctx.type;
152
  if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
153
+ else if (dontAlignCalls && ctx.type == ")" && !closing) return ctx.indented + statementIndentUnit;
154
  else if (ctx.align) return ctx.column + (closing ? 0 : 1);
155
  else return ctx.indented + (closing ? 0 : indentUnit);
156
  },
assets/{mode → codemirror/mode}/php.js RENAMED
@@ -121,7 +121,7 @@
121
 
122
  innerMode: function(state) { return {state: state.curState, mode: state.curMode}; }
123
  };
124
- }, "htmlmixed");
125
 
126
  CodeMirror.defineMIME("application/x-httpd-php", "php");
127
  CodeMirror.defineMIME("application/x-httpd-php-open", {name: "php", startOpen: true});
121
 
122
  innerMode: function(state) { return {state: state.curState, mode: state.curMode}; }
123
  };
124
+ }, "htmlmixed", "clike");
125
 
126
  CodeMirror.defineMIME("application/x-httpd-php", "php");
127
  CodeMirror.defineMIME("application/x-httpd-php-open", {name: "php", startOpen: true});
assets/icon.css DELETED
@@ -1,3 +0,0 @@
1
- #icon-snippets.icon32 {
2
- background: url('../images/icon32.png') no-repeat scroll transparent;
3
- }
 
 
 
assets/menu-icon.mp6.css ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Use our awesome new SVG icon on
3
+ * MP6 admin pages
4
+ *
5
+ * @package Code Snippets
6
+ * @subpackage Assets
7
+ */
8
+
9
+ #toplevel_page_snippets .wp-menu-image {
10
+ background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cgo8c3ZnIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBoZWlnaHQ9IjQ0cHQiIHdpZHRoPSI0OHB0IiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA0OCA0NCI+CgoJPHBhdGggY2xhc3M9InNuaXBwZXRzLWljb24tc3ZnIiBkPSJNMzIuNzIsMS44MWMzLjE3LTIuMjcsOS43MS0xLjE2LDEwLjMyLDMuMTMtMi44LDMuNzItNi40OSw2LjY1LTkuNjQsMTAuMDYsNC44MiwwLjUzLDEwLjE2LTEuMjIsMTQuNiwxLjE2djEuNTVjLTAuMzcsMy4xMS0yLjM1LDYuMTQtNS40OSw2Ljk3LTYuNzksMS4yOS0xNC4wNC0xLjAyLTIwLjYyLDEuMjUsMi42NiwxLjY3LDUuNTYsMy43NCw2LjA3LDcuMSwxLjI0LDUuMTItMi45MSwxMC40My04LjA4LDEwLjgtNS4yNywwLjUtMTAuMzctNC40NS05LjY2LTkuNzksMC4zNi00LjY2LDQuNzMtNy4yMiw3LjQ2LTEwLjQ5LDEuMTUtMC45OCwxLjAyLTMuMTYtMC43My0zLjM3LTMuNzgtMC41Ny03Ljc1LDAuNDMtMTEuNDItMC44OS0zLjA2LTEuMi00Ljk5LTQuMTktNS41My03LjM1di0yLjE1YzAuODMtNS44Nyw3Ljc4LTkuNzQsMTMuMDgtNi43OSw0LjU5LDIuMTcsNS43Nyw3LjgzLDQuMjEsMTIuMzIsMC41OC0wLjIxLDEuNzUtMC42MSwyLjM0LTAuODIsNC41Ny00LDguMzgtOC44NCwxMy4wOS0xMi42OXogTTcuMzMsNy40YzIuNDgtMS4yNyw1Ljc5LDAuODEsNS42OSwzLjYxLDAuMTMsMy4wNi0zLjg4LDUuMTctNi4yOCwzLjIzLTIuNTgtMS40OS0yLjE1LTUuNzcsMC41OS02Ljg0eiBNMTcuMzYsMzEuMzhjMi44MS0xLjc4LDYuNzQsMS40NSw1LjY2LDQuNTYtMC42OCwyLjgzLTQuNjgsMy45LTYuNjUsMS43NC0xLjkzLTEuNzctMS40LTUuMjEsMC45OS02LjN6IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGZpbGw9IiM4ODg4ODgiLz4KCjwvc3ZnPg==) !important;
11
+ background-position: center center !important;
12
+ background-size: 20px 20px !important;
13
+ background-repeat: no-repeat;
14
+ }
15
+
16
+ #toplevel_page_snippets.wp-has-current-submenu .wp-menu-image,
17
+ #toplevel_page_snippets.wp-has-current-submenu:hover .wp-menu-image {
18
+ background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cgo8c3ZnIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBoZWlnaHQ9IjQ0cHQiIHdpZHRoPSI0OHB0IiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA0OCA0NCI+CgoJPHBhdGggY2xhc3M9InNuaXBwZXRzLWljb24tc3ZnIiBkPSJNMzIuNzIsMS44MWMzLjE3LTIuMjcsOS43MS0xLjE2LDEwLjMyLDMuMTMtMi44LDMuNzItNi40OSw2LjY1LTkuNjQsMTAuMDYsNC44MiwwLjUzLDEwLjE2LTEuMjIsMTQuNiwxLjE2djEuNTVjLTAuMzcsMy4xMS0yLjM1LDYuMTQtNS40OSw2Ljk3LTYuNzksMS4yOS0xNC4wNC0xLjAyLTIwLjYyLDEuMjUsMi42NiwxLjY3LDUuNTYsMy43NCw2LjA3LDcuMSwxLjI0LDUuMTItMi45MSwxMC40My04LjA4LDEwLjgtNS4yNywwLjUtMTAuMzctNC40NS05LjY2LTkuNzksMC4zNi00LjY2LDQuNzMtNy4yMiw3LjQ2LTEwLjQ5LDEuMTUtMC45OCwxLjAyLTMuMTYtMC43My0zLjM3LTMuNzgtMC41Ny03Ljc1LDAuNDMtMTEuNDItMC44OS0zLjA2LTEuMi00Ljk5LTQuMTktNS41My03LjM1di0yLjE1YzAuODMtNS44Nyw3Ljc4LTkuNzQsMTMuMDgtNi43OSw0LjU5LDIuMTcsNS43Nyw3LjgzLDQuMjEsMTIuMzIsMC41OC0wLjIxLDEuNzUtMC42MSwyLjM0LTAuODIsNC41Ny00LDguMzgtOC44NCwxMy4wOS0xMi42OXogTTcuMzMsNy40YzIuNDgtMS4yNyw1Ljc5LDAuODEsNS42OSwzLjYxLDAuMTMsMy4wNi0zLjg4LDUuMTctNi4yOCwzLjIzLTIuNTgtMS40OS0yLjE1LTUuNzcsMC41OS02Ljg0eiBNMTcuMzYsMzEuMzhjMi44MS0xLjc4LDYuNzQsMS40NSw1LjY2LDQuNTYtMC42OCwyLjgzLTQuNjgsMy45LTYuNjUsMS43NC0xLjkzLTEuNzctMS40LTUuMjEsMC45OS02LjN6IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGZpbGw9IiNmZmYiLz4KCjwvc3ZnPg==) !important;
19
+ }
20
+
21
+ #toplevel_page_snippets:hover .wp-menu-image {
22
+ background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cgo8c3ZnIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBoZWlnaHQ9IjQ0cHQiIHdpZHRoPSI0OHB0IiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA0OCA0NCI+CgoJPHBhdGggY2xhc3M9InNuaXBwZXRzLWljb24tc3ZnIiBkPSJNMzIuNzIsMS44MWMzLjE3LTIuMjcsOS43MS0xLjE2LDEwLjMyLDMuMTMtMi44LDMuNzItNi40OSw2LjY1LTkuNjQsMTAuMDYsNC44MiwwLjUzLDEwLjE2LTEuMjIsMTQuNiwxLjE2djEuNTVjLTAuMzcsMy4xMS0yLjM1LDYuMTQtNS40OSw2Ljk3LTYuNzksMS4yOS0xNC4wNC0xLjAyLTIwLjYyLDEuMjUsMi42NiwxLjY3LDUuNTYsMy43NCw2LjA3LDcuMSwxLjI0LDUuMTItMi45MSwxMC40My04LjA4LDEwLjgtNS4yNywwLjUtMTAuMzctNC40NS05LjY2LTkuNzksMC4zNi00LjY2LDQuNzMtNy4yMiw3LjQ2LTEwLjQ5LDEuMTUtMC45OCwxLjAyLTMuMTYtMC43My0zLjM3LTMuNzgtMC41Ny03Ljc1LDAuNDMtMTEuNDItMC44OS0zLjA2LTEuMi00Ljk5LTQuMTktNS41My03LjM1di0yLjE1YzAuODMtNS44Nyw3Ljc4LTkuNzQsMTMuMDgtNi43OSw0LjU5LDIuMTcsNS43Nyw3LjgzLDQuMjEsMTIuMzIsMC41OC0wLjIxLDEuNzUtMC42MSwyLjM0LTAuODIsNC41Ny00LDguMzgtOC44NCwxMy4wOS0xMi42OXogTTcuMzMsNy40YzIuNDgtMS4yNyw1Ljc5LDAuODEsNS42OSwzLjYxLDAuMTMsMy4wNi0zLjg4LDUuMTctNi4yOCwzLjIzLTIuNTgtMS40OS0yLjE1LTUuNzcsMC41OS02Ljg0eiBNMTcuMzYsMzEuMzhjMi44MS0xLjc4LDYuNzQsMS40NSw1LjY2LDQuNTYtMC42OCwyLjgzLTQuNjgsMy45LTYuNjUsMS43NC0xLjkzLTEuNzctMS40LTUuMjEsMC45OS02LjN6IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGZpbGw9IiMyZWEyY2MiLz4KCjwvc3ZnPg==) !important;
23
+ }
assets/mode/css.js DELETED
@@ -1,465 +0,0 @@
1
- CodeMirror.defineMode("css", function(config) {
2
- var indentUnit = config.indentUnit, type;
3
-
4
- var atMediaTypes = keySet([
5
- "all", "aural", "braille", "handheld", "print", "projection", "screen",
6
- "tty", "tv", "embossed"
7
- ]);
8
-
9
- var atMediaFeatures = keySet([
10
- "width", "min-width", "max-width", "height", "min-height", "max-height",
11
- "device-width", "min-device-width", "max-device-width", "device-height",
12
- "min-device-height", "max-device-height", "aspect-ratio",
13
- "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
14
- "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
15
- "max-color", "color-index", "min-color-index", "max-color-index",
16
- "monochrome", "min-monochrome", "max-monochrome", "resolution",
17
- "min-resolution", "max-resolution", "scan", "grid"
18
- ]);
19
-
20
- var propertyKeywords = keySet([
21
- "align-content", "align-items", "align-self", "alignment-adjust",
22
- "alignment-baseline", "anchor-point", "animation", "animation-delay",
23
- "animation-direction", "animation-duration", "animation-iteration-count",
24
- "animation-name", "animation-play-state", "animation-timing-function",
25
- "appearance", "azimuth", "backface-visibility", "background",
26
- "background-attachment", "background-clip", "background-color",
27
- "background-image", "background-origin", "background-position",
28
- "background-repeat", "background-size", "baseline-shift", "binding",
29
- "bleed", "bookmark-label", "bookmark-level", "bookmark-state",
30
- "bookmark-target", "border", "border-bottom", "border-bottom-color",
31
- "border-bottom-left-radius", "border-bottom-right-radius",
32
- "border-bottom-style", "border-bottom-width", "border-collapse",
33
- "border-color", "border-image", "border-image-outset",
34
- "border-image-repeat", "border-image-slice", "border-image-source",
35
- "border-image-width", "border-left", "border-left-color",
36
- "border-left-style", "border-left-width", "border-radius", "border-right",
37
- "border-right-color", "border-right-style", "border-right-width",
38
- "border-spacing", "border-style", "border-top", "border-top-color",
39
- "border-top-left-radius", "border-top-right-radius", "border-top-style",
40
- "border-top-width", "border-width", "bottom", "box-decoration-break",
41
- "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
42
- "caption-side", "clear", "clip", "color", "color-profile", "column-count",
43
- "column-fill", "column-gap", "column-rule", "column-rule-color",
44
- "column-rule-style", "column-rule-width", "column-span", "column-width",
45
- "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
46
- "cue-after", "cue-before", "cursor", "direction", "display",
47
- "dominant-baseline", "drop-initial-after-adjust",
48
- "drop-initial-after-align", "drop-initial-before-adjust",
49
- "drop-initial-before-align", "drop-initial-size", "drop-initial-value",
50
- "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
51
- "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
52
- "float", "float-offset", "font", "font-feature-settings", "font-family",
53
- "font-kerning", "font-language-override", "font-size", "font-size-adjust",
54
- "font-stretch", "font-style", "font-synthesis", "font-variant",
55
- "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
56
- "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
57
- "font-weight", "grid-cell", "grid-column", "grid-column-align",
58
- "grid-column-sizing", "grid-column-span", "grid-columns", "grid-flow",
59
- "grid-row", "grid-row-align", "grid-row-sizing", "grid-row-span",
60
- "grid-rows", "grid-template", "hanging-punctuation", "height", "hyphens",
61
- "icon", "image-orientation", "image-rendering", "image-resolution",
62
- "inline-box-align", "justify-content", "left", "letter-spacing",
63
- "line-break", "line-height", "line-stacking", "line-stacking-ruby",
64
- "line-stacking-shift", "line-stacking-strategy", "list-style",
65
- "list-style-image", "list-style-position", "list-style-type", "margin",
66
- "margin-bottom", "margin-left", "margin-right", "margin-top",
67
- "marker-offset", "marks", "marquee-direction", "marquee-loop",
68
- "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
69
- "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
70
- "nav-left", "nav-right", "nav-up", "opacity", "order", "orphans", "outline",
71
- "outline-color", "outline-offset", "outline-style", "outline-width",
72
- "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
73
- "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
74
- "page", "page-break-after", "page-break-before", "page-break-inside",
75
- "page-policy", "pause", "pause-after", "pause-before", "perspective",
76
- "perspective-origin", "pitch", "pitch-range", "play-during", "position",
77
- "presentation-level", "punctuation-trim", "quotes", "rendering-intent",
78
- "resize", "rest", "rest-after", "rest-before", "richness", "right",
79
- "rotation", "rotation-point", "ruby-align", "ruby-overhang",
80
- "ruby-position", "ruby-span", "size", "speak", "speak-as", "speak-header",
81
- "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
82
- "tab-size", "table-layout", "target", "target-name", "target-new",
83
- "target-position", "text-align", "text-align-last", "text-decoration",
84
- "text-decoration-color", "text-decoration-line", "text-decoration-skip",
85
- "text-decoration-style", "text-emphasis", "text-emphasis-color",
86
- "text-emphasis-position", "text-emphasis-style", "text-height",
87
- "text-indent", "text-justify", "text-outline", "text-shadow",
88
- "text-space-collapse", "text-transform", "text-underline-position",
89
- "text-wrap", "top", "transform", "transform-origin", "transform-style",
90
- "transition", "transition-delay", "transition-duration",
91
- "transition-property", "transition-timing-function", "unicode-bidi",
92
- "vertical-align", "visibility", "voice-balance", "voice-duration",
93
- "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
94
- "voice-volume", "volume", "white-space", "widows", "width", "word-break",
95
- "word-spacing", "word-wrap", "z-index"
96
- ]);
97
-
98
- var colorKeywords = keySet([
99
- "black", "silver", "gray", "white", "maroon", "red", "purple", "fuchsia",
100
- "green", "lime", "olive", "yellow", "navy", "blue", "teal", "aqua"
101
- ]);
102
-
103
- var valueKeywords = keySet([
104
- "above", "absolute", "activeborder", "activecaption", "afar",
105
- "after-white-space", "ahead", "alias", "all", "all-scroll", "alternate",
106
- "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
107
- "arabic-indic", "armenian", "asterisks", "auto", "avoid", "background",
108
- "backwards", "baseline", "below", "bidi-override", "binary", "bengali",
109
- "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
110
- "both", "bottom", "break-all", "break-word", "button", "button-bevel",
111
- "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian",
112
- "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
113
- "cell", "center", "checkbox", "circle", "cjk-earthly-branch",
114
- "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
115
- "col-resize", "collapse", "compact", "condensed", "contain", "content",
116
- "content-box", "context-menu", "continuous", "copy", "cover", "crop",
117
- "cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal",
118
- "decimal-leading-zero", "default", "default-button", "destination-atop",
119
- "destination-in", "destination-out", "destination-over", "devanagari",
120
- "disc", "discard", "document", "dot-dash", "dot-dot-dash", "dotted",
121
- "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
122
- "element", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
123
- "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
124
- "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
125
- "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
126
- "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
127
- "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
128
- "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et",
129
- "ethiopic-halehame-tig", "ew-resize", "expanded", "extra-condensed",
130
- "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes",
131
- "forwards", "from", "geometricPrecision", "georgian", "graytext", "groove",
132
- "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew",
133
- "help", "hidden", "hide", "higher", "highlight", "highlighttext",
134
- "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore",
135
- "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
136
- "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
137
- "inline-block", "inline-table", "inset", "inside", "intrinsic", "invert",
138
- "italic", "justify", "kannada", "katakana", "katakana-iroha", "khmer",
139
- "landscape", "lao", "large", "larger", "left", "level", "lighter",
140
- "line-through", "linear", "lines", "list-item", "listbox", "listitem",
141
- "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
142
- "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
143
- "lower-roman", "lowercase", "ltr", "malayalam", "match",
144
- "media-controls-background", "media-current-time-display",
145
- "media-fullscreen-button", "media-mute-button", "media-play-button",
146
- "media-return-to-realtime-button", "media-rewind-button",
147
- "media-seek-back-button", "media-seek-forward-button", "media-slider",
148
- "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
149
- "media-volume-slider-container", "media-volume-sliderthumb", "medium",
150
- "menu", "menulist", "menulist-button", "menulist-text",
151
- "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
152
- "mix", "mongolian", "monospace", "move", "multiple", "myanmar", "n-resize",
153
- "narrower", "navy", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
154
- "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
155
- "ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote",
156
- "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
157
- "outside", "overlay", "overline", "padding", "padding-box", "painted",
158
- "paused", "persian", "plus-darker", "plus-lighter", "pointer", "portrait",
159
- "pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button",
160
- "radio", "read-only", "read-write", "read-write-plaintext-only", "relative",
161
- "repeat", "repeat-x", "repeat-y", "reset", "reverse", "rgb", "rgba",
162
- "ridge", "right", "round", "row-resize", "rtl", "run-in", "running",
163
- "s-resize", "sans-serif", "scroll", "scrollbar", "se-resize", "searchfield",
164
- "searchfield-cancel-button", "searchfield-decoration",
165
- "searchfield-results-button", "searchfield-results-decoration",
166
- "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
167
- "single", "skip-white-space", "slide", "slider-horizontal",
168
- "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
169
- "small", "small-caps", "small-caption", "smaller", "solid", "somali",
170
- "source-atop", "source-in", "source-out", "source-over", "space", "square",
171
- "square-button", "start", "static", "status-bar", "stretch", "stroke",
172
- "sub", "subpixel-antialiased", "super", "sw-resize", "table",
173
- "table-caption", "table-cell", "table-column", "table-column-group",
174
- "table-footer-group", "table-header-group", "table-row", "table-row-group",
175
- "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
176
- "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
177
- "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
178
- "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
179
- "transparent", "ultra-condensed", "ultra-expanded", "underline", "up",
180
- "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
181
- "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
182
- "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
183
- "visibleStroke", "visual", "w-resize", "wait", "wave", "white", "wider",
184
- "window", "windowframe", "windowtext", "x-large", "x-small", "xor",
185
- "xx-large", "xx-small", "yellow"
186
- ]);
187
-
188
- function keySet(array) { var keys = {}; for (var i = 0; i < array.length; ++i) keys[array[i]] = true; return keys; }
189
- function ret(style, tp) {type = tp; return style;}
190
-
191
- function tokenBase(stream, state) {
192
- var ch = stream.next();
193
- if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("def", stream.current());}
194
- else if (ch == "/" && stream.eat("*")) {
195
- state.tokenize = tokenCComment;
196
- return tokenCComment(stream, state);
197
- }
198
- else if (ch == "<" && stream.eat("!")) {
199
- state.tokenize = tokenSGMLComment;
200
- return tokenSGMLComment(stream, state);
201
- }
202
- else if (ch == "=") ret(null, "compare");
203
- else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare");
204
- else if (ch == "\"" || ch == "'") {
205
- state.tokenize = tokenString(ch);
206
- return state.tokenize(stream, state);
207
- }
208
- else if (ch == "#") {
209
- stream.eatWhile(/[\w\\\-]/);
210
- return ret("atom", "hash");
211
- }
212
- else if (ch == "!") {
213
- stream.match(/^\s*\w*/);
214
- return ret("keyword", "important");
215
- }
216
- else if (/\d/.test(ch)) {
217
- stream.eatWhile(/[\w.%]/);
218
- return ret("number", "unit");
219
- }
220
- else if (ch === "-") {
221
- if (/\d/.test(stream.peek())) {
222
- stream.eatWhile(/[\w.%]/);
223
- return ret("number", "unit");
224
- } else if (stream.match(/^[^-]+-/)) {
225
- return ret("meta", type);
226
- }
227
- }
228
- else if (/[,+>*\/]/.test(ch)) {
229
- return ret(null, "select-op");
230
- }
231
- else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
232
- return ret("qualifier", type);
233
- }
234
- else if (ch == ":") {
235
- return ret("operator", ch);
236
- }
237
- else if (/[;{}\[\]\(\)]/.test(ch)) {
238
- return ret(null, ch);
239
- }
240
- else if (ch == "u" && stream.match("rl(")) {
241
- stream.backUp(1);
242
- state.tokenize = tokenParenthesized;
243
- return ret("property", "variable");
244
- }
245
- else {
246
- stream.eatWhile(/[\w\\\-]/);
247
- return ret("property", "variable");
248
- }
249
- }
250
-
251
- function tokenCComment(stream, state) {
252
- var maybeEnd = false, ch;
253
- while ((ch = stream.next()) != null) {
254
- if (maybeEnd && ch == "/") {
255
- state.tokenize = tokenBase;
256
- break;
257
- }
258
- maybeEnd = (ch == "*");
259
- }
260
- return ret("comment", "comment");
261
- }
262
-
263
- function tokenSGMLComment(stream, state) {
264
- var dashes = 0, ch;
265
- while ((ch = stream.next()) != null) {
266
- if (dashes >= 2 && ch == ">") {
267
- state.tokenize = tokenBase;
268
- break;
269
- }
270
- dashes = (ch == "-") ? dashes + 1 : 0;
271
- }
272
- return ret("comment", "comment");
273
- }
274
-
275
- function tokenString(quote, nonInclusive) {
276
- return function(stream, state) {
277
- var escaped = false, ch;
278
- while ((ch = stream.next()) != null) {
279
- if (ch == quote && !escaped)
280
- break;
281
- escaped = !escaped && ch == "\\";
282
- }
283
- if (!escaped) {
284
- if (nonInclusive) stream.backUp(1);
285
- state.tokenize = tokenBase;
286
- }
287
- return ret("string", "string");
288
- };
289
- }
290
-
291
- function tokenParenthesized(stream, state) {
292
- stream.next(); // Must be '('
293
- if (!stream.match(/\s*[\"\']/, false))
294
- state.tokenize = tokenString(")", true);
295
- else
296
- state.tokenize = tokenBase;
297
- return ret(null, "(");
298
- }
299
-
300
- return {
301
- startState: function(base) {
302
- return {tokenize: tokenBase,
303
- baseIndent: base || 0,
304
- stack: []};
305
- },
306
-
307
- token: function(stream, state) {
308
-
309
- // Use these terms when applicable (see http://www.xanthir.com/blog/b4E50)
310
- //
311
- // rule** or **ruleset:
312
- // A selector + braces combo, or an at-rule.
313
- //
314
- // declaration block:
315
- // A sequence of declarations.
316
- //
317
- // declaration:
318
- // A property + colon + value combo.
319
- //
320
- // property value:
321
- // The entire value of a property.
322
- //
323
- // component value:
324
- // A single piece of a property value. Like the 5px in
325
- // text-shadow: 0 0 5px blue;. Can also refer to things that are
326
- // multiple terms, like the 1-4 terms that make up the background-size
327
- // portion of the background shorthand.
328
- //
329
- // term:
330
- // The basic unit of author-facing CSS, like a single number (5),
331
- // dimension (5px), string ("foo"), or function. Officially defined
332
- // by the CSS 2.1 grammar (look for the 'term' production)
333
- //
334
- //
335
- // simple selector:
336
- // A single atomic selector, like a type selector, an attr selector, a
337
- // class selector, etc.
338
- //
339
- // compound selector:
340
- // One or more simple selectors without a combinator. div.example is
341
- // compound, div > .example is not.
342
- //
343
- // complex selector:
344
- // One or more compound selectors chained with combinators.
345
- //
346
- // combinator:
347
- // The parts of selectors that express relationships. There are four
348
- // currently - the space (descendant combinator), the greater-than
349
- // bracket (child combinator), the plus sign (next sibling combinator),
350
- // and the tilda (following sibling combinator).
351
- //
352
- // sequence of selectors:
353
- // One or more of the named type of selector chained with commas.
354
-
355
- if (state.tokenize == tokenBase && stream.eatSpace()) return null;
356
- var style = state.tokenize(stream, state);
357
-
358
- // Changing style returned based on context
359
- var context = state.stack[state.stack.length-1];
360
- if (style == "property") {
361
- if (context == "propertyValue"){
362
- if (valueKeywords[stream.current()]) {
363
- style = "string-2";
364
- } else if (colorKeywords[stream.current()]) {
365
- style = "keyword";
366
- } else {
367
- style = "variable-2";
368
- }
369
- } else if (context == "rule") {
370
- if (!propertyKeywords[stream.current()]) {
371
- style += " error";
372
- }
373
- } else if (!context || context == "@media{") {
374
- style = "tag";
375
- } else if (context == "@media") {
376
- if (atMediaTypes[stream.current()]) {
377
- style = "attribute"; // Known attribute
378
- } else if (/^(only|not)$/i.test(stream.current())) {
379
- style = "keyword";
380
- } else if (stream.current().toLowerCase() == "and") {
381
- style = "error"; // "and" is only allowed in @mediaType
382
- } else if (atMediaFeatures[stream.current()]) {
383
- style = "error"; // Known property, should be in @mediaType(
384
- } else {
385
- // Unknown, expecting keyword or attribute, assuming attribute
386
- style = "attribute error";
387
- }
388
- } else if (context == "@mediaType") {
389
- if (atMediaTypes[stream.current()]) {
390
- style = "attribute";
391
- } else if (stream.current().toLowerCase() == "and") {
392
- style = "operator";
393
- } else if (/^(only|not)$/i.test(stream.current())) {
394
- style = "error"; // Only allowed in @media
395
- } else if (atMediaFeatures[stream.current()]) {
396
- style = "error"; // Known property, should be in parentheses
397
- } else {
398
- // Unknown attribute or property, but expecting property (preceded
399
- // by "and"). Should be in parentheses
400
- style = "error";
401
- }
402
- } else if (context == "@mediaType(") {
403
- if (propertyKeywords[stream.current()]) {
404
- // do nothing, remains "property"
405
- } else if (atMediaTypes[stream.current()]) {
406
- style = "error"; // Known property, should be in parentheses
407
- } else if (stream.current().toLowerCase() == "and") {
408
- style = "operator";
409
- } else if (/^(only|not)$/i.test(stream.current())) {
410
- style = "error"; // Only allowed in @media
411
- } else {
412
- style += " error";
413
- }
414
- } else {
415
- style = "error";
416
- }
417
- } else if (style == "atom") {
418
- if(!context || context == "@media{") {
419
- style = "builtin";
420
- } else if (context == "propertyValue") {
421
- if (!/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) {
422
- style += " error";
423
- }
424
- } else {
425
- style = "error";
426
- }
427
- } else if (context == "@media" && type == "{") {
428
- style = "error";
429
- }
430
-
431
- // Push/pop context stack
432
- if (type == "{") {
433
- if (context == "@media" || context == "@mediaType") {
434
- state.stack.pop();
435
- state.stack[state.stack.length-1] = "@media{";
436
- }
437
- else state.stack.push("rule");
438
- }
439
- else if (type == "}") {
440
- state.stack.pop();
441
- if (context == "propertyValue") state.stack.pop();
442
- }
443
- else if (type == "@media") state.stack.push("@media");
444
- else if (context == "@media" && /\b(keyword|attribute)\b/.test(style))
445
- state.stack.push("@mediaType");
446
- else if (context == "@mediaType" && stream.current() == ",") state.stack.pop();
447
- else if (context == "@mediaType" && type == "(") state.stack.push("@mediaType(");
448
- else if (context == "@mediaType(" && type == ")") state.stack.pop();
449
- else if (context == "rule" && type == ":") state.stack.push("propertyValue");
450
- else if (context == "propertyValue" && type == ";") state.stack.pop();
451
- return style;
452
- },
453
-
454
- indent: function(state, textAfter) {
455
- var n = state.stack.length;
456
- if (/^\}/.test(textAfter))
457
- n -= state.stack[state.stack.length-1] == "propertyValue" ? 2 : 1;
458
- return state.baseIndent + n * indentUnit;
459
- },
460
-
461
- electricChars: "}"
462
- };
463
- });
464
-
465
- CodeMirror.defineMIME("text/css", "css");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/mode/htmlmixed.js DELETED
@@ -1,84 +0,0 @@
1
- CodeMirror.defineMode("htmlmixed", function(config) {
2
- var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true});
3
- var jsMode = CodeMirror.getMode(config, "javascript");
4
- var cssMode = CodeMirror.getMode(config, "css");
5
-
6
- function html(stream, state) {
7
- var style = htmlMode.token(stream, state.htmlState);
8
- if (/(?:^|\s)tag(?:\s|$)/.test(style) && stream.current() == ">" && state.htmlState.context) {
9
- if (/^script$/i.test(state.htmlState.context.tagName)) {
10
- state.token = javascript;
11
- state.localState = jsMode.startState(htmlMode.indent(state.htmlState, ""));
12
- }
13
- else if (/^style$/i.test(state.htmlState.context.tagName)) {
14
- state.token = css;
15
- state.localState = cssMode.startState(htmlMode.indent(state.htmlState, ""));
16
- }
17
- }
18
- return style;
19
- }
20
- function maybeBackup(stream, pat, style) {
21
- var cur = stream.current();
22
- var close = cur.search(pat), m;
23
- if (close > -1) stream.backUp(cur.length - close);
24
- else if (m = cur.match(/<\/?$/)) {
25
- stream.backUp(cur.length);
26
- if (!stream.match(pat, false)) stream.match(cur[0]);
27
- }
28
- return style;
29
- }
30
- function javascript(stream, state) {
31
- if (stream.match(/^<\/\s*script\s*>/i, false)) {
32
- state.token = html;
33
- state.localState = null;
34
- return html(stream, state);
35
- }
36
- return maybeBackup(stream, /<\/\s*script\s*>/,
37
- jsMode.token(stream, state.localState));
38
- }
39
- function css(stream, state) {
40
- if (stream.match(/^<\/\s*style\s*>/i, false)) {
41
- state.token = html;
42
- state.localState = null;
43
- return html(stream, state);
44
- }
45
- return maybeBackup(stream, /<\/\s*style\s*>/,
46
- cssMode.token(stream, state.localState));
47
- }
48
-
49
- return {
50
- startState: function() {
51
- var state = htmlMode.startState();
52
- return {token: html, localState: null, mode: "html", htmlState: state};
53
- },
54
-
55
- copyState: function(state) {
56
- if (state.localState)
57
- var local = CodeMirror.copyState(state.token == css ? cssMode : jsMode, state.localState);
58
- return {token: state.token, localState: local, mode: state.mode,
59
- htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
60
- },
61
-
62
- token: function(stream, state) {
63
- return state.token(stream, state);
64
- },
65
-
66
- indent: function(state, textAfter) {
67
- if (state.token == html || /^\s*<\//.test(textAfter))
68
- return htmlMode.indent(state.htmlState, textAfter);
69
- else if (state.token == javascript)
70
- return jsMode.indent(state.localState, textAfter);
71
- else
72
- return cssMode.indent(state.localState, textAfter);
73
- },
74
-
75
- electricChars: "/{}:",
76
-
77
- innerMode: function(state) {
78
- var mode = state.token == html ? htmlMode : state.token == javascript ? jsMode : cssMode;
79
- return {state: state.localState || state.htmlState, mode: mode};
80
- }
81
- };
82
- }, "xml", "javascript", "css");
83
-
84
- CodeMirror.defineMIME("text/html", "htmlmixed");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/mode/javascript.js DELETED
@@ -1,411 +0,0 @@
1
- // TODO actually recognize syntax of TypeScript constructs
2
-
3
- CodeMirror.defineMode("javascript", function(config, parserConfig) {
4
- var indentUnit = config.indentUnit;
5
- var jsonMode = parserConfig.json;
6
- var isTS = parserConfig.typescript;
7
-
8
- // Tokenizer
9
-
10
- var keywords = function(){
11
- function kw(type) {return {type: type, style: "keyword"};}
12
- var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
13
- var operator = kw("operator"), atom = {type: "atom", style: "atom"};
14
-
15
- var jsKeywords = {
16
- "if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
17
- "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C,
18
- "var": kw("var"), "const": kw("var"), "let": kw("var"),
19
- "function": kw("function"), "catch": kw("catch"),
20
- "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
21
- "in": operator, "typeof": operator, "instanceof": operator,
22
- "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom
23
- };
24
-
25
- // Extend the 'normal' keywords with the TypeScript language extensions
26
- if (isTS) {
27
- var type = {type: "variable", style: "variable-3"};
28
- var tsKeywords = {
29
- // object-like things
30
- "interface": kw("interface"),
31
- "class": kw("class"),
32
- "extends": kw("extends"),
33
- "constructor": kw("constructor"),
34
-
35
- // scope modifiers
36
- "public": kw("public"),
37
- "private": kw("private"),
38
- "protected": kw("protected"),
39
- "static": kw("static"),
40
-
41
- "super": kw("super"),
42
-
43
- // types
44
- "string": type, "number": type, "bool": type, "any": type
45
- };
46
-
47
- for (var attr in tsKeywords) {
48
- jsKeywords[attr] = tsKeywords[attr];
49
- }
50
- }
51
-
52
- return jsKeywords;
53
- }();
54
-
55
- var isOperatorChar = /[+\-*&%=<>!?|]/;
56
-
57
- function chain(stream, state, f) {
58
- state.tokenize = f;
59
- return f(stream, state);
60
- }
61
-
62
- function nextUntilUnescaped(stream, end) {
63
- var escaped = false, next;
64
- while ((next = stream.next()) != null) {
65
- if (next == end && !escaped)
66
- return false;
67
- escaped = !escaped && next == "\\";
68
- }
69
- return escaped;
70
- }
71
-
72
- // Used as scratch variables to communicate multiple values without
73
- // consing up tons of objects.
74
- var type, content;
75
- function ret(tp, style, cont) {
76
- type = tp; content = cont;
77
- return style;
78
- }
79
-
80
- function jsTokenBase(stream, state) {
81
- var ch = stream.next();
82
- if (ch == '"' || ch == "'")
83
- return chain(stream, state, jsTokenString(ch));
84
- else if (/[\[\]{}\(\),;\:\.]/.test(ch))
85
- return ret(ch);
86
- else if (ch == "0" && stream.eat(/x/i)) {
87
- stream.eatWhile(/[\da-f]/i);
88
- return ret("number", "number");
89
- }
90
- else if (/\d/.test(ch) || ch == "-" && stream.eat(/\d/)) {
91
- stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
92
- return ret("number", "number");
93
- }
94
- else if (ch == "/") {
95
- if (stream.eat("*")) {
96
- return chain(stream, state, jsTokenComment);
97
- }
98
- else if (stream.eat("/")) {
99
- stream.skipToEnd();
100
- return ret("comment", "comment");
101
- }
102
- else if (state.lastType == "operator" || state.lastType == "keyword c" ||
103
- /^[\[{}\(,;:]$/.test(state.lastType)) {
104
- nextUntilUnescaped(stream, "/");
105
- stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
106
- return ret("regexp", "string-2");
107
- }
108
- else {
109
- stream.eatWhile(isOperatorChar);
110
- return ret("operator", null, stream.current());
111
- }
112
- }
113
- else if (ch == "#") {
114
- stream.skipToEnd();
115
- return ret("error", "error");
116
- }
117
- else if (isOperatorChar.test(ch)) {
118
- stream.eatWhile(isOperatorChar);
119
- return ret("operator", null, stream.current());
120
- }
121
- else {
122
- stream.eatWhile(/[\w\$_]/);
123
- var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
124
- return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
125
- ret("variable", "variable", word);
126
- }
127
- }
128
-
129
- function jsTokenString(quote) {
130
- return function(stream, state) {
131
- if (!nextUntilUnescaped(stream, quote))
132
- state.tokenize = jsTokenBase;
133
- return ret("string", "string");
134
- };
135
- }
136
-
137
- function jsTokenComment(stream, state) {
138
- var maybeEnd = false, ch;
139
- while (ch = stream.next()) {
140
- if (ch == "/" && maybeEnd) {
141
- state.tokenize = jsTokenBase;
142
- break;
143
- }
144
- maybeEnd = (ch == "*");
145
- }
146
- return ret("comment", "comment");
147
- }
148
-
149
- // Parser
150
-
151
- var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true};
152
-
153
- function JSLexical(indented, column, type, align, prev, info) {
154
- this.indented = indented;
155
- this.column = column;
156
- this.type = type;
157
- this.prev = prev;
158
- this.info = info;
159
- if (align != null) this.align = align;
160
- }
161
-
162
- function inScope(state, varname) {
163
- for (var v = state.localVars; v; v = v.next)
164
- if (v.name == varname) return true;
165
- }
166
-
167
- function parseJS(state, style, type, content, stream) {
168
- var cc = state.cc;
169
- // Communicate our context to the combinators.
170
- // (Less wasteful than consing up a hundred closures on every call.)
171
- cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc;
172
-
173
- if (!state.lexical.hasOwnProperty("align"))
174
- state.lexical.align = true;
175
-
176
- while(true) {
177
- var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
178
- if (combinator(type, content)) {
179
- while(cc.length && cc[cc.length - 1].lex)
180
- cc.pop()();
181
- if (cx.marked) return cx.marked;
182
- if (type == "variable" && inScope(state, content)) return "variable-2";
183
- return style;
184
- }
185
- }
186
- }
187
-
188
- // Combinator utils
189
-
190
- var cx = {state: null, column: null, marked: null, cc: null};
191
- function pass() {
192
- for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
193
- }
194
- function cont() {
195
- pass.apply(null, arguments);
196
- return true;
197
- }
198
- function register(varname) {
199
- var state = cx.state;
200
- if (state.context) {
201
- cx.marked = "def";
202
- for (var v = state.localVars; v; v = v.next)
203
- if (v.name == varname) return;
204
- state.localVars = {name: varname, next: state.localVars};
205
- }
206
- }
207
-
208
- // Combinators
209
-
210
- var defaultVars = {name: "this", next: {name: "arguments"}};
211
- function pushcontext() {
212
- cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
213
- cx.state.localVars = defaultVars;
214
- }
215
- function popcontext() {
216
- cx.state.localVars = cx.state.context.vars;
217
- cx.state.context = cx.state.context.prev;
218
- }
219
- function pushlex(type, info) {
220
- var result = function() {
221
- var state = cx.state;
222
- state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info);
223
- };
224
- result.lex = true;
225
- return result;
226
- }
227
- function poplex() {
228
- var state = cx.state;
229
- if (state.lexical.prev) {
230
- if (state.lexical.type == ")")
231
- state.indented = state.lexical.indented;
232
- state.lexical = state.lexical.prev;
233
- }
234
- }
235
- poplex.lex = true;
236
-
237
- function expect(wanted) {
238
- return function expecting(type) {
239
- if (type == wanted) return cont();
240
- else if (wanted == ";") return pass();
241
- else return cont(arguments.callee);
242
- };
243
- }
244
-
245
- function statement(type) {
246
- if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex);
247
- if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
248
- if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
249
- if (type == "{") return cont(pushlex("}"), block, poplex);
250
- if (type == ";") return cont();
251
- if (type == "function") return cont(functiondef);
252
- if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"),
253
- poplex, statement, poplex);
254
- if (type == "variable") return cont(pushlex("stat"), maybelabel);
255
- if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
256
- block, poplex, poplex);
257
- if (type == "case") return cont(expression, expect(":"));
258
- if (type == "default") return cont(expect(":"));
259
- if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
260
- statement, poplex, popcontext);
261
- return pass(pushlex("stat"), expression, expect(";"), poplex);
262
- }
263
- function expression(type) {
264
- if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator);
265
- if (type == "function") return cont(functiondef);
266
- if (type == "keyword c") return cont(maybeexpression);
267
- if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeoperator);
268
- if (type == "operator") return cont(expression);
269
- if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator);
270
- if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
271
- return cont();
272
- }
273
- function maybeexpression(type) {
274
- if (type.match(/[;\}\)\],]/)) return pass();
275
- return pass(expression);
276
- }
277
-
278
- function maybeoperator(type, value) {
279
- if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator);
280
- if (type == "operator" && value == "?") return cont(expression, expect(":"), expression);
281
- if (type == ";") return;
282
- if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator);
283
- if (type == ".") return cont(property, maybeoperator);
284
- if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator);
285
- }
286
- function maybelabel(type) {
287
- if (type == ":") return cont(poplex, statement);
288
- return pass(maybeoperator, expect(";"), poplex);
289
- }
290
- function property(type) {
291
- if (type == "variable") {cx.marked = "property"; return cont();}
292
- }
293
- function objprop(type) {
294
- if (type == "variable") cx.marked = "property";
295
- if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expression);
296
- }
297
- function commasep(what, end) {
298
- function proceed(type) {
299
- if (type == ",") return cont(what, proceed);
300
- if (type == end) return cont();
301
- return cont(expect(end));
302
- }
303
- return function commaSeparated(type) {
304
- if (type == end) return cont();
305
- else return pass(what, proceed);
306
- };
307
- }
308
- function block(type) {
309
- if (type == "}") return cont();
310
- return pass(statement, block);
311
- }
312
- function maybetype(type) {
313
- if (type == ":") return cont(typedef);
314
- return pass();
315
- }
316
- function typedef(type) {
317
- if (type == "variable"){cx.marked = "variable-3"; return cont();}
318
- return pass();
319
- }
320
- function vardef1(type, value) {
321
- if (type == "variable") {
322
- register(value);
323
- return isTS ? cont(maybetype, vardef2) : cont(vardef2);
324
- }
325
- return pass();
326
- }
327
- function vardef2(type, value) {
328
- if (value == "=") return cont(expression, vardef2);
329
- if (type == ",") return cont(vardef1);
330
- }
331
- function forspec1(type) {
332
- if (type == "var") return cont(vardef1, expect(";"), forspec2);
333
- if (type == ";") return cont(forspec2);
334
- if (type == "variable") return cont(formaybein);
335
- return cont(forspec2);
336
- }
337
- function formaybein(_type, value) {
338
- if (value == "in") return cont(expression);
339
- return cont(maybeoperator, forspec2);
340
- }
341
- function forspec2(type, value) {
342
- if (type == ";") return cont(forspec3);
343
- if (value == "in") return cont(expression);
344
- return cont(expression, expect(";"), forspec3);
345
- }
346
- function forspec3(type) {
347
- if (type != ")") cont(expression);
348
- }
349
- function functiondef(type, value) {
350
- if (type == "variable") {register(value); return cont(functiondef);}
351
- if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
352
- }
353
- function funarg(type, value) {
354
- if (type == "variable") {register(value); return isTS ? cont(maybetype) : cont();}
355
- }
356
-
357
- // Interface
358
-
359
- return {
360
- startState: function(basecolumn) {
361
- return {
362
- tokenize: jsTokenBase,
363
- lastType: null,
364
- cc: [],
365
- lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
366
- localVars: parserConfig.localVars,
367
- context: parserConfig.localVars && {vars: parserConfig.localVars},
368
- indented: 0
369
- };
370
- },
371
-
372
- token: function(stream, state) {
373
- if (stream.sol()) {
374
- if (!state.lexical.hasOwnProperty("align"))
375
- state.lexical.align = false;
376
- state.indented = stream.indentation();
377
- }
378
- if (stream.eatSpace()) return null;
379
- var style = state.tokenize(stream, state);
380
- if (type == "comment") return style;
381
- state.lastType = type;
382
- return parseJS(state, style, type, content, stream);
383
- },
384
-
385
- indent: function(state, textAfter) {
386
- if (state.tokenize == jsTokenComment) return CodeMirror.Pass;
387
- if (state.tokenize != jsTokenBase) return 0;
388
- var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
389
- if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
390
- var type = lexical.type, closing = firstChar == type;
391
- if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0);
392
- else if (type == "form" && firstChar == "{") return lexical.indented;
393
- else if (type == "form") return lexical.indented + indentUnit;
394
- else if (type == "stat")
395
- return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? indentUnit : 0);
396
- else if (lexical.info == "switch" && !closing)
397
- return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
398
- else if (lexical.align) return lexical.column + (closing ? 0 : 1);
399
- else return lexical.indented + (closing ? 0 : indentUnit);
400
- },
401
-
402
- electricChars: ":{}",
403
-
404
- jsonMode: jsonMode
405
- };
406
- });
407
-
408
- CodeMirror.defineMIME("text/javascript", "javascript");
409
- CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
410
- CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
411
- CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/mode/xml.js DELETED
@@ -1,324 +0,0 @@
1
- CodeMirror.defineMode("xml", function(config, parserConfig) {
2
- var indentUnit = config.indentUnit;
3
- var Kludges = parserConfig.htmlMode ? {
4
- autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
5
- 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
6
- 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
7
- 'track': true, 'wbr': true},
8
- implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
9
- 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
10
- 'th': true, 'tr': true},
11
- contextGrabbers: {
12
- 'dd': {'dd': true, 'dt': true},
13
- 'dt': {'dd': true, 'dt': true},
14
- 'li': {'li': true},
15
- 'option': {'option': true, 'optgroup': true},
16
- 'optgroup': {'optgroup': true},
17
- 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
18
- 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
19
- 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
20
- 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
21
- 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
22
- 'rp': {'rp': true, 'rt': true},
23
- 'rt': {'rp': true, 'rt': true},
24
- 'tbody': {'tbody': true, 'tfoot': true},
25
- 'td': {'td': true, 'th': true},
26
- 'tfoot': {'tbody': true},
27
- 'th': {'td': true, 'th': true},
28
- 'thead': {'tbody': true, 'tfoot': true},
29
- 'tr': {'tr': true}
30
- },
31
- doNotIndent: {"pre": true},
32
- allowUnquoted: true,
33
- allowMissing: true
34
- } : {
35
- autoSelfClosers: {},
36
- implicitlyClosed: {},
37
- contextGrabbers: {},
38
- doNotIndent: {},
39
- allowUnquoted: false,
40
- allowMissing: false
41
- };
42
- var alignCDATA = parserConfig.alignCDATA;
43
-
44
- // Return variables for tokenizers
45
- var tagName, type;
46
-
47
- function inText(stream, state) {
48
- function chain(parser) {
49
- state.tokenize = parser;
50
- return parser(stream, state);
51
- }
52
-
53
- var ch = stream.next();
54
- if (ch == "<") {
55
- if (stream.eat("!")) {
56
- if (stream.eat("[")) {
57
- if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
58
- else return null;
59
- }
60
- else if (stream.match("--")) return chain(inBlock("comment", "-->"));
61
- else if (stream.match("DOCTYPE", true, true)) {
62
- stream.eatWhile(/[\w\._\-]/);
63
- return chain(doctype(1));
64
- }
65
- else return null;
66
- }
67
- else if (stream.eat("?")) {
68
- stream.eatWhile(/[\w\._\-]/);
69
- state.tokenize = inBlock("meta", "?>");
70
- return "meta";
71
- }
72
- else {
73
- var isClose = stream.eat("/");
74
- tagName = "";
75
- var c;
76
- while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
77
- if (!tagName) return "error";
78
- type = isClose ? "closeTag" : "openTag";
79
- state.tokenize = inTag;
80
- return "tag";
81
- }
82
- }
83
- else if (ch == "&") {
84
- var ok;
85
- if (stream.eat("#")) {
86
- if (stream.eat("x")) {
87
- ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
88
- } else {
89
- ok = stream.eatWhile(/[\d]/) && stream.eat(";");
90
- }
91
- } else {
92
- ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
93
- }
94
- return ok ? "atom" : "error";
95
- }
96
- else {
97
- stream.eatWhile(/[^&<]/);
98
- return null;
99
- }
100
- }
101
-
102
- function inTag(stream, state) {
103
- var ch = stream.next();
104
- if (ch == ">" || (ch == "/" && stream.eat(">"))) {
105
- state.tokenize = inText;
106
- type = ch == ">" ? "endTag" : "selfcloseTag";
107
- return "tag";
108
- }
109
- else if (ch == "=") {
110
- type = "equals";
111
- return null;
112
- }
113
- else if (/[\'\"]/.test(ch)) {
114
- state.tokenize = inAttribute(ch);
115
- return state.tokenize(stream, state);
116
- }
117
- else {
118
- stream.eatWhile(/[^\s\u00a0=<>\"\']/);
119
- return "word";
120
- }
121
- }
122
-
123
- function inAttribute(quote) {
124
- return function(stream, state) {
125
- while (!stream.eol()) {
126
- if (stream.next() == quote) {
127
- state.tokenize = inTag;
128
- break;
129
- }
130
- }
131
- return "string";
132
- };
133
- }
134
-
135
- function inBlock(style, terminator) {
136
- return function(stream, state) {
137
- while (!stream.eol()) {
138
- if (stream.match(terminator)) {
139
- state.tokenize = inText;
140
- break;
141
- }
142
- stream.next();
143
- }
144
- return style;
145
- };
146
- }
147
- function doctype(depth) {
148
- return function(stream, state) {
149
- var ch;
150
- while ((ch = stream.next()) != null) {
151
- if (ch == "<") {
152
- state.tokenize = doctype(depth + 1);
153
- return state.tokenize(stream, state);
154
- } else if (ch == ">") {
155
- if (depth == 1) {
156
- state.tokenize = inText;
157
- break;
158
- } else {
159
- state.tokenize = doctype(depth - 1);
160
- return state.tokenize(stream, state);
161
- }
162
- }
163
- }
164
- return "meta";
165
- };
166
- }
167
-
168
- var curState, setStyle;
169
- function pass() {
170
- for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);
171
- }
172
- function cont() {
173
- pass.apply(null, arguments);
174
- return true;
175
- }
176
-
177
- function pushContext(tagName, startOfLine) {
178
- var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent);
179
- curState.context = {
180
- prev: curState.context,
181
- tagName: tagName,
182
- indent: curState.indented,
183
- startOfLine: startOfLine,
184
- noIndent: noIndent
185
- };
186
- }
187
- function popContext() {
188
- if (curState.context) curState.context = curState.context.prev;
189
- }
190
-
191
- function element(type) {
192
- if (type == "openTag") {
193
- curState.tagName = tagName;
194
- return cont(attributes, endtag(curState.startOfLine));
195
- } else if (type == "closeTag") {
196
- var err = false;
197
- if (curState.context) {
198
- if (curState.context.tagName != tagName) {
199
- if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) {
200
- popContext();
201
- }
202
- err = !curState.context || curState.context.tagName != tagName;
203
- }
204
- } else {
205
- err = true;
206
- }
207
- if (err) setStyle = "error";
208
- return cont(endclosetag(err));
209
- }
210
- return cont();
211
- }
212
- function endtag(startOfLine) {
213
- return function(type) {
214
- var tagName = curState.tagName;
215
- curState.tagName = null;
216
- if (type == "selfcloseTag" ||
217
- (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase()))) {
218
- maybePopContext(tagName.toLowerCase());
219
- return cont();
220
- }
221
- if (type == "endTag") {
222
- maybePopContext(tagName.toLowerCase());
223
- pushContext(tagName, startOfLine);
224
- return cont();
225
- }
226
- return cont();
227
- };
228
- }
229
- function endclosetag(err) {
230
- return function(type) {
231
- if (err) setStyle = "error";
232
- if (type == "endTag") { popContext(); return cont(); }
233
- setStyle = "error";
234
- return cont(arguments.callee);
235
- };
236
- }
237
- function maybePopContext(nextTagName) {
238
- var parentTagName;
239
- while (true) {
240
- if (!curState.context) {
241
- return;
242
- }
243
- parentTagName = curState.context.tagName.toLowerCase();
244
- if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) ||
245
- !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
246
- return;
247
- }
248
- popContext();
249
- }
250
- }
251
-
252
- function attributes(type) {
253
- if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);}
254
- if (type == "endTag" || type == "selfcloseTag") return pass();
255
- setStyle = "error";
256
- return cont(attributes);
257
- }
258
- function attribute(type) {
259
- if (type == "equals") return cont(attvalue, attributes);
260
- if (!Kludges.allowMissing) setStyle = "error";
261
- else if (type == "word") setStyle = "attribute";
262
- return (type == "endTag" || type == "selfcloseTag") ? pass() : cont();
263
- }
264
- function attvalue(type) {
265
- if (type == "string") return cont(attvaluemaybe);
266
- if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();}
267
- setStyle = "error";
268
- return (type == "endTag" || type == "selfCloseTag") ? pass() : cont();
269
- }
270
- function attvaluemaybe(type) {
271
- if (type == "string") return cont(attvaluemaybe);
272
- else return pass();
273
- }
274
-
275
- return {
276
- startState: function() {
277
- return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null};
278
- },
279
-
280
- token: function(stream, state) {
281
- if (stream.sol()) {
282
- state.startOfLine = true;
283
- state.indented = stream.indentation();
284
- }
285
- if (stream.eatSpace()) return null;
286
-
287
- setStyle = type = tagName = null;
288
- var style = state.tokenize(stream, state);
289
- state.type = type;
290
- if ((style || type) && style != "comment") {
291
- curState = state;
292
- while (true) {
293
- var comb = state.cc.pop() || element;
294
- if (comb(type || style)) break;
295
- }
296
- }
297
- state.startOfLine = false;
298
- return setStyle || style;
299
- },
300
-
301
- indent: function(state, textAfter, fullLine) {
302
- var context = state.context;
303
- if ((state.tokenize != inTag && state.tokenize != inText) ||
304
- context && context.noIndent)
305
- return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
306
- if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
307
- if (context && /^<\//.test(textAfter))
308
- context = context.prev;
309
- while (context && !context.startOfLine)
310
- context = context.prev;
311
- if (context) return context.indent + indentUnit;
312
- else return 0;
313
- },
314
-
315
- electricChars: "/",
316
-
317
- configuration: parserConfig.htmlMode ? "html" : "xml"
318
- };
319
- });
320
-
321
- CodeMirror.defineMIME("text/xml", "xml");
322
- CodeMirror.defineMIME("application/xml", "xml");
323
- if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
324
- CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/screen-icon.css ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Add the snippet screen icon to admin pages
3
+ *
4
+ * @package Code Snippets
5
+ * @subpackage Assets
6
+ */
7
+
8
+ #icon-snippets.icon32 {
9
+ background: url('../images/screen-icon.png') no-repeat scroll transparent;
10
+ }
assets/table.css CHANGED
@@ -1,3 +1,10 @@
 
 
 
 
 
 
 
1
  .snippets .inactive a {
2
  color: #557799;
3
  }
1
+ /**
2
+ * Custom styling for the snippets table
3
+ *
4
+ * @package Code Snippets
5
+ * @subpackage Assets
6
+ */
7
+
8
  .snippets .inactive a {
9
  color: #557799;
10
  }
assets/table.mp6.css ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Custom styling for the snippets table
3
+ * and the MP6 interface
4
+ *
5
+ * @package Code Snippets
6
+ * @subpackage Assets
7
+ */
8
+
9
+ .snippets .row-actions-visible {
10
+ color: #ddd;
11
+ }
12
+
13
+ .snippets tfoot th {
14
+ border-top: none !important;
15
+ }
16
+
17
+ .snippets a.delete:hover,
18
+ #all-snippets-table .snippets a.delete:hover,
19
+ #search-snippets-table .snippets a.delete:hover {
20
+ color: #f00;
21
+ border-bottom: 1px solid #f00;
22
+ }
23
+
24
+ .snippets .column-description p {
25
+ color: #333;
26
+ }
27
+
28
+ .snippets .inactive .column-name strong {
29
+ color: #333;
30
+ font-weight: 400;
31
+ }
32
+
33
+ .snippets,
34
+ .snippets th,
35
+ .snippets td {
36
+ color: #000;
37
+ }
38
+
39
+ .snippets .inactive a {
40
+ color: #579;
41
+ }
42
+
43
+ .snippets tr {
44
+ background: #fff;
45
+ }
46
+
47
+ .snippets .inactive td,
48
+ .snippets .inactive th,
49
+ .snippets .active td,
50
+ .snippets .active th {
51
+ border: none;
52
+ padding: 10px 9px;
53
+ -webkit-box-shadow: inset 0 -1px 0 rgba(0,0,0,0.1);
54
+ box-shadow: inset 0 -1px 0 rgba(0,0,0,0.1);;
55
+ }
56
+
57
+ .snippets tbody th.check-column {
58
+ padding: 13px 0 0 3px;
59
+ }
60
+
61
+ .snippets .active td,
62
+ .snippets .active th {
63
+ background-color: rgba(120,200,230,0.06);
64
+ }
65
+
66
+ .snippets tr.active + tr.inactive th,
67
+ .snippets tr.active + tr.inactive td {
68
+ border-top: 1px solid rgba(0,0,0,0.03);
69
+ -webkit-box-shadow: inset 0px 1px 0 rgba(0,0,0,0.02), inset 0 -1px 0 #e1e1e1;
70
+ box-shadow: inset 0px 1px 0 rgba(0,0,0,0.02), inset 0 -1px 0 #e1e1e1;
71
+ }
72
+
73
+ .snippets thead th.check-column,
74
+ .snippets tfoot th.check-column,
75
+ .snippets .inactive th.check-column {
76
+ padding-left: 5px;
77
+ }
78
+
79
+ .snippets .active th.check-column {
80
+ border-left: 2px solid #2ea2cc;
81
+ }
82
+
83
+ #wpbody-content .snippets .column-name {
84
+ white-space: nowrap; /* prevents wrapping of snippet title */
85
+ }
assets/util/dialog.css DELETED
@@ -1,32 +0,0 @@
1
- .CodeMirror-dialog {
2
- position: absolute;
3
- left: 0; right: 0;
4
- background: white;
5
- z-index: 15;
6
- padding: .1em .8em;
7
- overflow: hidden;
8
- color: #333;
9
- }
10
-
11
- .CodeMirror-dialog-top {
12
- border-bottom: 1px solid #eee;
13
- top: 0;
14
- }
15
-
16
- .CodeMirror-dialog-bottom {
17
- border-top: 1px solid #eee;
18
- bottom: 0;
19
- }
20
-
21
- .CodeMirror-dialog input {
22
- border: none;
23
- outline: none;
24
- background: transparent;
25
- width: 20em;
26
- color: inherit;
27
- font-family: monospace;
28
- }
29
-
30
- .CodeMirror-dialog button {
31
- font-size: 70%;
32
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/util/dialog.js DELETED
@@ -1,75 +0,0 @@
1
- // Open simple dialogs on top of an editor. Relies on dialog.css.
2
-
3
- (function() {
4
- function dialogDiv(cm, template, bottom) {
5
- var wrap = cm.getWrapperElement();
6
- var dialog;
7
- dialog = wrap.appendChild(document.createElement("div"));
8
- if (bottom) {
9
- dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom";
10
- } else {
11
- dialog.className = "CodeMirror-dialog CodeMirror-dialog-top";
12
- }
13
- dialog.innerHTML = template;
14
- return dialog;
15
- }
16
-
17
- CodeMirror.defineExtension("openDialog", function(template, callback, options) {
18
- var dialog = dialogDiv(this, template, options && options.bottom);
19
- var closed = false, me = this;
20
- function close() {
21
- if (closed) return;
22
- closed = true;
23
- dialog.parentNode.removeChild(dialog);
24
- }
25
- var inp = dialog.getElementsByTagName("input")[0], button;
26
- if (inp) {
27
- CodeMirror.on(inp, "keydown", function(e) {
28
- if (e.keyCode == 13 || e.keyCode == 27) {
29
- CodeMirror.e_stop(e);
30
- close();
31
- me.focus();
32
- if (e.keyCode == 13) callback(inp.value);
33
- }
34
- });
35
- inp.focus();
36
- CodeMirror.on(inp, "blur", close);
37
- } else if (button = dialog.getElementsByTagName("button")[0]) {
38
- CodeMirror.on(button, "click", function() {
39
- close();
40
- me.focus();
41
- });
42
- button.focus();
43
- CodeMirror.on(button, "blur", close);
44
- }
45
- return close;
46
- });
47
-
48
- CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) {
49
- var dialog = dialogDiv(this, template, options && options.bottom);
50
- var buttons = dialog.getElementsByTagName("button");
51
- var closed = false, me = this, blurring = 1;
52
- function close() {
53
- if (closed) return;
54
- closed = true;
55
- dialog.parentNode.removeChild(dialog);
56
- me.focus();
57
- }
58
- buttons[0].focus();
59
- for (var i = 0; i < buttons.length; ++i) {
60
- var b = buttons[i];
61
- (function(callback) {
62
- CodeMirror.on(b, "click", function(e) {
63
- CodeMirror.e_preventDefault(e);
64
- close();
65
- if (callback) callback(me);
66
- });
67
- })(callbacks[i]);
68
- CodeMirror.on(b, "blur", function() {
69
- --blurring;
70
- setTimeout(function() { if (blurring <= 0) close(); }, 200);
71
- });
72
- CodeMirror.on(b, "focus", function() { ++blurring; });
73
- }
74
- });
75
- })();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
code-snippets.php CHANGED
@@ -15,9 +15,8 @@
15
  * Description: An easy, clean and simple way to add code snippets to your site. No need to edit to your theme's functions.php file again!
16
  * Author: Shea Bunge
17
  * Author URI: http://bungeshea.com
18
- * Version: 1.6.1
19
- * License: GPLv3 or later
20
- * Network: true
21
  * Text Domain: code-snippets
22
  * Domain Path: /languages/
23
  *
@@ -58,66 +57,53 @@ if ( ! class_exists( 'Code_Snippets' ) ) :
58
  final class Code_Snippets {
59
 
60
  /**
61
- * The base name for the snippets table in the database
62
- * This will later be prepended with the WordPress table prefix
63
- *
64
- * DO NOT EDIT THIS VARIABLE!
65
- * Instead, use the 'code_snippets_table' filter
66
  *
67
- * If you need to use the table name in your own code,
68
- * use the $code_snippets->get_table_name() function
69
  *
70
  * @since Code Snippets 1.0
71
  * @access public
72
  */
73
- private $table = 'snippets';
74
 
75
  /**
76
- * The base name for the network snippets table in the database
77
- * This will later be prepended with the WordPress base table prefix
78
- *
79
- * DO NOT EDIT THIS VARIABLE!
80
- * Instead, use the 'code_snippets_multisite_table' filter
81
  *
82
- * If you need to use the table name in your own code,
83
- * use the $code_snippets->get_table_name() function
84
- *
85
- * @since Code Snippets 1.4
86
- * @access private
87
  */
88
- private $ms_table = 'ms_snippets';
89
 
90
  /**
91
- * The version number for this release of the plugin.
92
- * This will later be used for upgrades and enqueueing files
93
- *
94
- * This should be set to the 'Plugin Version' value,
95
- * as defined above in the plugin header
96
  *
97
  * @since Code Snippets 1.0
98
  * @access public
99
  */
100
- public $version = 1.6;
101
 
102
  /**
103
- * The base URLs for the admin pages
104
  *
105
  * @since Code Snippets 1.0
106
  * @access public
107
  */
108
- public $admin_manage_url, $admin_single_url, $admin_import_url;
109
 
110
  /**
111
- * The hooks for the admin pages
112
- * Used primarily for enqueueing scripts and styles
113
  *
114
- * @since Code Snippets 1.0
115
  * @access public
116
  */
117
- public $admin_manage, $admin_single, $admin_import;
118
 
119
  /**
120
  * The constructor function for our class
 
121
  *
122
  * @since Code Snippets 1.0
123
  * @access private
@@ -125,9 +111,47 @@ final class Code_Snippets {
125
  * @return void
126
  */
127
  function __construct() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  $this->setup_vars(); // initialise the variables
129
  $this->setup_hooks(); // register the action and filter hooks
130
  $this->upgrade(); // check if we need to change some stuff
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  }
132
 
133
  /**
@@ -142,9 +166,6 @@ final class Code_Snippets {
142
  global $wpdb;
143
  $this->file = __FILE__;
144
 
145
- $this->table = apply_filters( 'code_snippets_table', $wpdb->prefix . $this->table );
146
- $this->ms_table = apply_filters( 'code_snippets_multisite_table', $wpdb->base_prefix . $this->ms_table );
147
-
148
  $this->basename = plugin_basename( $this->file );
149
  $this->plugin_dir = plugin_dir_path( $this->file );
150
  $this->plugin_url = plugin_dir_url ( $this->file );
@@ -166,26 +187,50 @@ final class Code_Snippets {
166
  */
167
  function setup_hooks() {
168
 
169
- /* execute the snippets once the plugins are loaded */
170
- add_action( 'plugins_loaded', array( $this, 'run_snippets' ) );
171
-
172
  /* add the administration menus */
173
- add_action( 'admin_menu', array( $this, 'add_admin_menus' ) );
174
- add_action( 'network_admin_menu', array( $this, 'add_network_admin_menus' ) );
175
 
176
  /* register the importer */
177
- add_action( 'admin_init', array( $this, 'load_importer' ), 999 );
178
  add_action( 'network_admin_menu', array( $this, 'add_import_admin_menu' ) );
179
 
180
- /* load the translations */
181
- add_action( 'plugins_loaded', array( $this, 'load_textdomain' ) );
182
-
183
  /* add helpful links to the Plugins menu */
184
  add_filter( 'plugin_action_links_' . $this->basename, array( $this, 'settings_link' ) );
185
  add_filter( 'plugin_row_meta', array( $this, 'plugin_meta' ), 10, 2 );
186
 
187
  /* Add a custom icon to Snippets menu pages */
188
- add_action( 'admin_head', array( $this, 'load_admin_icon_style' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  }
190
 
191
  /**
@@ -236,6 +281,8 @@ final class Code_Snippets {
236
  */
237
  function get_table_name( $scope = '', $check_screen = true ) {
238
 
 
 
239
  $this->create_tables(); // create the snippet tables if they do not exist
240
 
241
  if ( ! is_multisite() ) {
@@ -246,17 +293,17 @@ final class Code_Snippets {
246
  $screen = get_current_screen();
247
  $network = $screen->is_network;
248
  }
249
- elseif ( $scope == 'multisite' || $scope == 'network' ) {
250
  $network = true;
251
  }
252
- elseif ( $scope == 'site' || $scope = 'single' ) {
253
  $network = false;
254
  }
255
  else {
256
  $network = false;
257
  }
258
 
259
- $table = ( $network ? $this->ms_table : $this->table );
260
 
261
  return $table;
262
  }
@@ -268,17 +315,20 @@ final class Code_Snippets {
268
  * @access public
269
  *
270
  * @uses $this->create_table() To create a single snippet table
 
271
  *
272
  * @return void
273
  */
274
  public function create_tables() {
275
 
276
- $this->create_table( $this->table );
 
 
 
277
 
278
- if ( is_multisite() )
279
- $this->create_table( $this->ms_table );
280
 
281
- add_site_option( 'code_snippets_version', $this->version );
282
  }
283
 
284
  /**
@@ -288,25 +338,38 @@ final class Code_Snippets {
288
  * @since Code Snippets 1.6
289
  * @access public
290
  *
 
 
291
  * @param string $table_name The name of the table to create
292
  * @return void
293
  */
294
  function create_table( $table_name ) {
 
295
 
296
  global $wpdb;
297
 
298
- if ( $wpdb->get_var( "SHOW TABLES LIKE '$table_name'" ) != $table_name ) {
299
- $sql = "CREATE TABLE $table_name (
300
- id MEDIUMINT NOT NULL AUTO_INCREMENT,
301
- name VARCHAR(64) NOT NULL,
302
- description TEXT,
303
- code TEXT NOT NULL,
304
- active TINYINT(1) NOT NULL DEFAULT 0,
305
- UNIQUE KEY id (id)
306
- );";
307
- require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
308
- dbDelta( $sql );
309
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
310
  }
311
 
312
  /**
@@ -315,14 +378,10 @@ final class Code_Snippets {
315
  * @since Code Snippets 1.2
316
  * @access private
317
  *
318
- * @return bool True on successful upgrade, false on failure
319
  */
320
  function upgrade() {
321
-
322
- /* add backwards-compatibly for the CS_SAFE_MODE constant */
323
- if ( defined( 'CS_SAFE_MODE' ) && ! defined( 'CODE_SNIPPETS_SAFE_MODE' ) ) {
324
- define( 'CODE_SNIPPETS_SAFE_MODE', CS_SAFE_MODE );
325
- }
326
 
327
  /* get the current plugin version from the database */
328
  if ( get_option( 'cs_db_version' ) ) {
@@ -334,70 +393,44 @@ final class Code_Snippets {
334
  $this->current_version = get_site_option( 'code_snippets_version', $this->version );
335
  }
336
 
337
- /* bail early if we're on the latest version */
338
- if ( $this->current_version < $this->version ) return false;
339
-
340
- if ( ! get_site_option( 'code_snippets_version' ) ) {
341
 
342
  /* This is the first time the plugin has run */
343
 
344
- $this->add_caps(); // register the capabilities ONCE ONLY
345
 
346
  if ( is_multisite() ) {
347
- $this->add_caps( 'multisite' ); // register the multisite capabilities ONCE ONLY
348
  }
349
  }
350
 
351
- /* migrate the recently_network_activated_snippets to the site options */
352
- if ( is_multisite() && get_option( 'recently_network_activated_snippets' ) ) {
353
- add_site_option( 'recently_activated_snippets', get_option( 'recently_network_activated_snippets', array() ) );
354
- delete_option( 'recently_network_activated_snippets' );
355
- }
356
-
357
- /* preform version specific upgrades */
358
-
359
- if ( $this->current_version < 1.5 ) {
360
- global $wpdb;
361
-
362
- /* Let's alter the name column to accept up to 64 characters */
363
- $wpdb->query( "ALTER TABLE $this->table CHANGE COLUMN name name VARCHAR(64) NOT NULL" );
364
 
365
- if ( is_multisite() ) {
366
- /* We must not forget the multisite table! */
367
- $wpdb->query( "ALTER TABLE $this->ms_table CHANGE COLUMN name name VARCHAR(64) NOT NULL" );
 
368
  }
369
 
370
- /* Add the custom capabilities that were introduced in version 1.5 */
371
- $this->add_roles();
372
- }
373
-
374
- if ( $this->current_version < 1.2 ) {
375
- /* The 'Complete Uninstall' option was removed in version 1.2 */
376
- delete_option( 'cs_complete_uninstall' );
377
- }
378
 
379
- if ( $this->current_version < $this->version ) {
380
  /* Update the current version */
381
  update_site_option( 'code_snippets_version', $this->version );
382
  }
383
-
384
- return true;
385
  }
386
 
387
  /**
388
- * Load up the localization file if we're using WordPress in a different language
389
- * Place it in this plugin's "languages" folder and name it "code-snippets-[value in wp-config].mo"
390
- *
391
- * If you wish to contribute a language file to be included in the Code Snippets package,
392
- * please see the plugin's website at http://code-snippets.bungeshea.com
393
  *
394
  * @since Code Snippets 1.5
395
  * @access private
396
- *
397
- * @return void
398
  */
399
- function load_textdomain() {
400
- load_plugin_textdomain( 'code-snippets', false, dirname( $this->basename ) . '/languages/' );
401
  }
402
 
403
  /**
@@ -413,7 +446,7 @@ final class Code_Snippets {
413
  */
414
  public function add_caps( $scope = '' ) {
415
 
416
- $network = ( $scope == 'multisite' || $scope == 'network' ? true : false );
417
 
418
  if ( $network && is_multisite() )
419
  $this->setup_ms_roles( 'add' );
@@ -434,7 +467,7 @@ final class Code_Snippets {
434
  */
435
  public function remove_caps( $scope = '' ) {
436
 
437
- $network = ( $scope == 'multisite' || $scope == 'network' ? true : false );
438
 
439
  if ( $network && is_multisite() )
440
  $this->setup_ms_roles( 'remove' );
@@ -453,10 +486,10 @@ final class Code_Snippets {
453
  */
454
  function setup_roles( $action = 'install' ) {
455
 
456
- if ( $action == 'install' || $action = 'add' )
457
  $install = true;
458
 
459
- elseif ( $action == 'uninstall' || $action = 'remove' )
460
  $install = false;
461
 
462
  else
@@ -492,10 +525,10 @@ final class Code_Snippets {
492
 
493
  if ( ! is_multisite() ) return;
494
 
495
- if ( $action == 'install' || $action = 'add' )
496
  $install = true;
497
 
498
- elseif ( $action == 'uninstall' || $action = 'remove' )
499
  $install = false;
500
 
501
  else
@@ -527,94 +560,65 @@ final class Code_Snippets {
527
  *
528
  * @uses add_menu_page() To register a top-level menu
529
  * @uses add_submenu_page() To register a submenu page
530
- * @uses apply_filters() To retrieve the corrent menu slug
531
  * @uses plugins_url() To retrieve the URL to a resource
532
  * @return void
533
  */
534
  function add_admin_menus() {
535
 
 
 
 
 
 
 
 
536
  $this->admin_manage = add_menu_page(
537
  __('Snippets', 'code-snippets'),
538
  __('Snippets', 'code-snippets'),
539
- 'manage_snippets',
540
- apply_filters( 'code_snippets_manage_url', 'snippets' ),
541
  array( $this, 'display_admin_manage' ),
542
- plugins_url( 'images/icon16.png', $this->file ),
543
- 67
544
  );
545
 
546
  add_submenu_page(
547
- apply_filters( 'code_snippets_manage_url', 'snippets' ),
548
  __('Snippets', 'code-snippets'),
549
- __('Manage Snippets', 'code-snippets'),
550
- 'manage_snippets',
551
- apply_filters( 'code_snippets_manage_url', 'snippets' ),
552
  array( $this, 'display_admin_manage')
553
  );
554
 
555
- $this->admin_single = add_submenu_page(
556
- apply_filters( 'code_snippets_manage_url', 'snippets' ),
557
- __('Add New Snippet', 'code-snippets'),
558
- __('Add New', 'code-snippets'),
559
- 'install_snippets',
560
- apply_filters( 'code_snippets_single_url', 'snippet' ),
561
- array( $this, 'display_admin_single' )
562
- );
563
-
564
- add_action( "load-$this->admin_manage", array( $this, 'load_admin_manage' ) );
565
- add_action( "load-$this->admin_single", array( $this, 'load_admin_single' ) );
566
-
567
- add_action( "admin_print_styles-$this->admin_single", array( $this, 'load_editor_styles' ) );
568
- add_action( "admin_print_scripts-$this->admin_single", array( $this, 'load_editor_scripts' ) );
569
- }
570
-
571
- /**
572
- * Add the network dashboard admin menu and subpages
573
- *
574
- * @since Code Snippets 1.4
575
- * @access private
576
- *
577
- * @uses add_menu_page() To register a top-level menu
578
- * @uses add_submenu_page() To register a submenu page
579
- * @uses apply_filters() To retrieve the corrent menu slug
580
- * @uses plugins_url() To retrieve the URL to a resource
581
- * @return void
582
- */
583
- function add_network_admin_menus() {
584
-
585
- $this->admin_manage = add_menu_page(
586
- __('Snippets', 'code-snippets'),
587
- __('Snippets', 'code-snippets'),
588
- 'manage_network_snippets',
589
- apply_filters( 'code_snippets_manage_url', 'snippets' ),
590
- array( $this, 'display_admin_manage' ),
591
- plugins_url( 'images/icon16.png', $this->file ),
592
- 21
593
- );
594
 
595
- add_submenu_page(
596
- apply_filters( 'code_snippets_manage_url', 'snippets' ),
597
- __('Snippets', 'code-snippets'),
598
- __('Manage Snippets', 'code-snippets'),
599
- 'manage_network_snippets',
600
- apply_filters( 'code_snippets_manage_url', 'snippets' ),
601
- array( $this, 'display_admin_manage' )
602
- );
603
 
604
- $this->admin_single = add_submenu_page(
605
- apply_filters( 'code_snippets_manage_url', 'snippets' ),
606
- __('Add New Snippet', 'code-snippets'),
607
- __('Add New', 'code-snippets'),
608
- 'install_network_snippets',
609
- apply_filters( 'code_snippets_single_url', 'snippet' ),
610
- array( $this, 'display_admin_single' )
611
- );
 
612
 
613
  add_action( "load-$this->admin_manage", array( $this, 'load_admin_manage' ) );
614
  add_action( "load-$this->admin_single", array( $this, 'load_admin_single' ) );
615
-
616
- add_action( "admin_print_styles-$this->admin_single", array( $this, 'load_editor_styles' ) );
617
- add_action( "admin_print_scripts-$this->admin_single", array( $this, 'load_editor_scripts' ) );
618
  }
619
 
620
  /**
@@ -626,14 +630,14 @@ final class Code_Snippets {
626
  * @access private
627
  *
628
  * @uses add_submenu_page() To register the menu page
629
- * @uses apply_filters() To retrieve the corrent menu slug
630
  * @uses add_action() To enqueue scripts and styles
631
  * @return void
632
  */
633
  function add_import_admin_menu() {
634
 
635
  $this->admin_import = add_submenu_page(
636
- apply_filters( 'code_snippets_manage_url', 'snippets' ),
637
  __('Import Snippets', 'code-snippets'),
638
  __('Import', 'code-snippets'),
639
  'import_snippets',
@@ -642,7 +646,6 @@ final class Code_Snippets {
642
  );
643
 
644
  $this->admin_import_url = self_admin_url( 'admin.php?page=import-code-snippets' );
645
- add_action( "admin_print_styles-$this->admin_import", array( $this, 'load_stylesheet' ) );
646
  add_action( "load-$this->admin_import", array( $this, 'load_admin_import' ) );
647
  }
648
 
@@ -658,122 +661,190 @@ final class Code_Snippets {
658
  */
659
  function load_admin_icon_style() {
660
 
661
- wp_enqueue_style(
662
- 'icon-snippets',
663
- plugins_url( 'assets/icon.css', $this->file ),
664
- false,
665
- $this->version
666
- );
 
 
 
 
 
 
 
 
 
 
 
 
667
  }
668
 
669
  /**
670
- * Registers and loads the code editor's scripts
671
  *
672
- * @since Code Snippets 1.4
673
- * @access private
674
- *
675
- * @uses wp_register_script()
676
- * @uses wp_enqueue_style() To add the scripts to the queue
677
  *
678
- * @return void
 
679
  */
680
- function load_editor_scripts() {
681
 
682
- /* CodeMirror package version */
683
- $version = '3.0';
684
 
685
- /* CodeMirror base framework */
 
 
 
 
 
 
686
 
687
- wp_register_script(
688
- 'codemirror',
689
- plugins_url( 'assets/codemirror.js', $this->file ),
690
- false,
691
- $version
692
- );
 
 
693
 
694
- /* CodeMirror modes */
 
 
 
695
 
696
- $modes = array( 'php', 'xml', 'javascript', 'css', 'clike', 'htmlmixed' );
 
 
 
697
 
698
- foreach ( $modes as $mode ) {
 
 
 
699
 
700
- wp_register_script(
701
- "codemirror-mode-$mode",
702
- plugins_url( "assets/mode/$mode.js", $this->file ),
703
- array( 'codemirror' ),
704
- $version
705
- );
 
 
 
 
 
 
706
  }
707
 
708
- /* CodeMirror utilities */
 
709
 
710
- $utils = array( 'dialog', 'searchcursor', 'search', 'matchbrackets' );
 
 
 
 
 
 
 
 
 
 
 
 
 
711
 
712
- foreach ( $utils as $util ) {
 
713
 
714
- wp_register_script(
715
- "codemirror-util-$util",
716
- plugins_url( "assets/util/$util.js", $this->file ),
717
- array( 'codemirror' ),
718
- $version
719
- );
720
  }
721
 
722
- /* Enqueue the registered scripts */
723
-
724
- wp_enqueue_script( array(
725
- 'codemirror-util-matchbrackets',
726
- 'codemirror-mode-htmlmixed',
727
- 'codemirror-mode-xml',
728
- 'codemirror-mode-js',
729
- 'codemirror-mode-css',
730
- 'codemirror-mode-clike',
731
- 'codemirror-mode-php',
732
- 'codemirror-util-search',
733
- ) );
734
  }
735
 
736
  /**
737
- * Registers and loads the code editor's styles
738
- *
739
- * @since Code Snippets 1.4
740
- * @access private
741
  *
742
- * @uses wp_register_style()
743
- * @uses wp_enqueue_style() To add the stylesheets to the queue
744
  *
745
- * @return void
 
746
  */
747
- function load_editor_styles() {
748
 
749
- /* CodeMirror package version */
750
- $version = '3.0';
751
 
752
- /* CodeMirror base framework */
 
 
 
 
753
 
754
- wp_register_style(
755
- 'codemirror',
756
- plugins_url( 'assets/codemirror.css', $this->file ),
757
- false,
758
- $version
759
- );
760
 
761
- /* CodeMirror utilities */
 
762
 
763
- wp_register_style(
764
- 'codemirror-util-dialog',
765
- plugins_url( 'assets/util/dialog.css', $this->file ),
766
- array( 'codemirror' ),
767
- $version
768
- );
 
 
 
 
 
769
 
770
- /* Enqueue the registered stylesheets */
771
 
772
- wp_enqueue_style( array(
773
- 'codemirror',
774
- 'codemirror-util-dialog',
775
- ) );
776
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
777
  }
778
 
779
  /**
@@ -803,7 +874,11 @@ final class Code_Snippets {
803
  array( '%d' ),
804
  array( '%d' )
805
  );
 
 
806
  }
 
 
807
  }
808
 
809
  /**
@@ -835,18 +910,22 @@ final class Code_Snippets {
835
  array( '%d' )
836
  );
837
  $recently_active = array( $id => time() ) + (array) $recently_active;
 
 
838
  }
839
 
840
- if ( $table == $this->ms_table )
841
  update_site_option(
842
  'recently_activated_snippets',
843
  $recently_active + (array) get_site_option( 'recently_activated_snippets' )
844
  );
845
- else
846
  update_option(
847
  'recently_activated_snippets',
848
  $recently_active + (array) get_option( 'recently_activated_snippets' )
849
  );
 
 
850
  }
851
 
852
  /**
@@ -862,9 +941,10 @@ final class Code_Snippets {
862
  global $wpdb;
863
 
864
  $table = $this->get_table_name( $scope );
865
- $id = intval( $id );
866
 
867
- $wpdb->query( "DELETE FROM $table WHERE id='$id' LIMIT 1" );
 
 
868
  }
869
 
870
  /**
@@ -876,34 +956,43 @@ final class Code_Snippets {
876
  * @uses $wpdb To update/add the snippet to the database
877
  * @uses $this->get_table_name() To dynamically retrieve the name of the snippet table
878
  *
879
- * @param array $snippet The snippet to add/update to the database
880
  * @return int|bool The ID of the snippet on success, false on failure
881
  */
882
  public function save_snippet( $snippet, $scope = '' ) {
883
  global $wpdb;
884
 
885
- $name = mysql_real_escape_string( htmlspecialchars( $snippet['name'] ) );
886
- $description = mysql_real_escape_string( htmlspecialchars( $snippet['description'] ) );
887
- $code = mysql_real_escape_string( htmlspecialchars( $snippet['code'] ) );
888
 
889
- if ( empty( $name ) or empty( $code ) )
890
  return false;
891
 
892
  $table = $this->get_table_name( $scope );
893
 
894
- if ( isset( $snippet['id'] ) && ( intval( $snippet['id'] ) != 0 ) ) {
895
- $wpdb->query( "UPDATE $table SET
896
- name='$name',
897
- description='$description',
898
- code='$code' WHERE id=" . intval( $snippet['id'] ) . "
899
- LIMIT 1"
900
- );
901
- return intval( $snippet['id'] );
 
 
 
 
 
 
 
 
 
902
  } else {
903
- $wpdb->query(
904
- "INSERT INTO $table(name,description,code)
905
- VALUES ('$name','$description','$code')"
906
- );
 
 
907
  return $wpdb->insert_id;
908
  }
909
  }
@@ -914,7 +1003,7 @@ final class Code_Snippets {
914
  * @since Code Snippets 1.5
915
  * @access public
916
  *
917
- * @uses $this->save_snippet() To add the snippets to the database
918
  *
919
  * @param file $file The path to the XML file to import
920
  * @param string $scope Import into network-wide table or site-wide table?
@@ -927,13 +1016,14 @@ final class Code_Snippets {
927
 
928
  $xml = simplexml_load_file( $file );
929
 
930
- foreach ( $xml->children() as $child ) {
931
- $this->save_snippet( array(
932
- 'name' => $child->name,
933
- 'description' => $child->description,
934
- 'code' => $child->code,
935
- ), $scope );
936
  }
 
 
 
937
  return $xml->count();
938
  }
939
 
@@ -1000,9 +1090,47 @@ final class Code_Snippets {
1000
  $result = eval( $code );
1001
  $output = ob_get_contents();
1002
  ob_end_clean();
 
1003
  return $result;
1004
  }
1005
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1006
  /**
1007
  * Replaces the text 'Add New Snippet' with 'Edit Snippet'
1008
  *
@@ -1041,25 +1169,31 @@ final class Code_Snippets {
1041
 
1042
  $_SERVER['REQUEST_URI'] = remove_query_arg( array( 'action', 'id' ) );
1043
 
1044
- if ( 'activate' == $_GET['action'] ) {
 
 
1045
  $this->activate( $id );
1046
- wp_redirect( add_query_arg( 'activate', true ) );
1047
  }
1048
- elseif ( 'deactivate' == $_GET['action'] ) {
1049
  $this->deactivate( $id );
1050
- wp_redirect( add_query_arg( 'deactivate', true ) );
1051
  }
1052
- elseif ( 'delete' == $_GET['action'] ) {
1053
  $this->delete_snippet( $id );
1054
- wp_redirect( add_query_arg( 'delete', true ) );
1055
  }
1056
- elseif ( 'export' == $_GET['action'] ) {
1057
  $this->export( $id );
1058
  }
1059
- elseif ( 'export-php' == $_GET['action'] ) {
1060
  $this->export_php( $id );
1061
  }
1062
 
 
 
 
 
 
 
 
1063
  endif;
1064
 
1065
  include $this->plugin_dir . 'includes/help/manage.php'; // Load the help tabs
@@ -1069,9 +1203,8 @@ final class Code_Snippets {
1069
  */
1070
  require_once $this->plugin_dir . 'includes/class-list-table.php';
1071
 
1072
- global $code_snippets_list_table;
1073
- $code_snippets_list_table = new Code_Snippets_List_Table();
1074
- $code_snippets_list_table->prepare_items();
1075
  }
1076
 
1077
  /**
@@ -1087,26 +1220,24 @@ final class Code_Snippets {
1087
  */
1088
  function load_admin_single() {
1089
 
 
 
 
 
 
 
1090
  $this->create_tables(); // create the snippet tables if they do not exist
1091
 
1092
- if ( isset( $_REQUEST['save_snippet'] ) ) {
1093
 
1094
- if ( isset( $_REQUEST['snippet_id'] ) ) {
1095
- $result = $this->save_snippet( array(
1096
- 'name' => $_REQUEST['snippet_name'],
1097
- 'description' => $_REQUEST['snippet_description'],
1098
- 'code' => $_REQUEST['snippet_code'],
1099
- 'id' => $_REQUEST['snippet_id'],
1100
- ) );
1101
- } else {
1102
- $result = $this->save_snippet( array(
1103
- 'name' => $_REQUEST['snippet_name'],
1104
- 'description' => $_REQUEST['snippet_description'],
1105
- 'code' => $_REQUEST['snippet_code'],
1106
- ) );
1107
- }
1108
 
1109
- $_SERVER['REQUEST_URI'] = remove_query_arg( array( 'added', 'updated', 'invalid' ) );
 
 
 
1110
 
1111
  if ( ! $result || $result < 1 ) {
1112
  wp_redirect( add_query_arg( 'invalid', true ) );
@@ -1130,6 +1261,102 @@ final class Code_Snippets {
1130
 
1131
  include $this->plugin_dir . 'includes/help/single.php'; // Load the help tabs
1132
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1133
  }
1134
 
1135
  /**
@@ -1192,6 +1419,37 @@ final class Code_Snippets {
1192
  require_once $this->plugin_dir . 'includes/admin/import.php';
1193
  }
1194
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1195
  /**
1196
  * Adds a link pointing to the Manage Snippets page
1197
  *
@@ -1218,7 +1476,7 @@ final class Code_Snippets {
1218
  */
1219
  function plugin_meta( $links, $file ) {
1220
 
1221
- if ( $file != $this->basename ) return $links;
1222
 
1223
  $format = '<a href="%1$s" title="%2$s">%3$s</a>';
1224
 
@@ -1235,7 +1493,7 @@ final class Code_Snippets {
1235
  ),
1236
  sprintf( $format,
1237
  'http://code-snippets.bungeshea.com/donate/',
1238
- __('Support this plugin\'s development', 'code-snippets'),
1239
  __('Donate', 'code-snippets')
1240
  )
1241
  ) );
@@ -1256,17 +1514,28 @@ final class Code_Snippets {
1256
  */
1257
  function run_snippets() {
1258
 
1259
- if ( defined( 'CODE_SNIPPETS_SAFE_MODE' ) && CODE_SNIPPETS_SAFE_MODE )
1260
- return;
1261
 
1262
  global $wpdb;
1263
 
1264
- // grab the active snippets from the database
1265
- $active_snippets = $wpdb->get_results( "SELECT code FROM $this->table WHERE active=1;" );
 
 
 
 
 
 
 
 
 
 
1266
 
1267
- if ( is_multisite() ) {
 
 
1268
  $active_snippets = array_merge(
1269
- $wpdb->get_results( "SELECT code FROM $this->ms_table WHERE active=1;" ),
1270
  $active_snippets
1271
  );
1272
  }
@@ -1274,7 +1543,7 @@ final class Code_Snippets {
1274
  if ( count( $active_snippets ) ) {
1275
  foreach( $active_snippets as $snippet ) {
1276
  // execute the php code
1277
- $this->execute_snippet( htmlspecialchars_decode( stripslashes( $snippet->code ) ) );
1278
  }
1279
  }
1280
  }
@@ -1293,8 +1562,6 @@ $code_snippets = new Code_Snippets;
1293
  global $cs;
1294
  $cs = &$code_snippets;
1295
 
1296
- endif; // class exists check
1297
-
1298
  register_uninstall_hook( $code_snippets->file, 'code_snippets_uninstall' );
1299
 
1300
  /**
@@ -1315,26 +1582,27 @@ register_uninstall_hook( $code_snippets->file, 'code_snippets_uninstall' );
1315
  function code_snippets_uninstall() {
1316
  global $wpdb, $code_snippets;
1317
  if ( is_multisite() ) {
1318
- $blogs = $wpdb->get_results( "SELECT blog_id FROM $wpdb->blogs", ARRAY_A );
1319
- if ( $blogs ) {
1320
- foreach( $blogs as $blog ) {
1321
- switch_to_blog( $blog['blog_id'] );
1322
- $wpdb->query( 'DROP TABLE IF EXISTS ' . apply_filters( 'code_snippets_table', $wpdb->prefix . 'snippets' ) );
1323
  delete_option( 'cs_db_version' );
1324
  delete_option( 'recently_activated_snippets' );
1325
  $code_snippets->remove_caps();
1326
  }
1327
  restore_current_blog();
1328
  }
1329
- $wpdb->query( 'DROP TABLE IF EXISTS ' . $code_snippets->get_table_name( 'multisite' ) );
1330
  delete_site_option( 'recently_activated_snippets' );
1331
  $code_snippets->remove_caps( 'multisite' );
1332
  } else {
1333
- $wpdb->query( 'DROP TABLE IF EXISTS ' . $code_snippets->get_table_name( 'site' ) );
1334
  delete_option( 'recently_activated_snippets' );
1335
  delete_option( 'cs_db_version' );
1336
  $code_snippets->remove_caps();
1337
  }
1338
-
1339
  delete_site_option( 'code_snippets_version' );
1340
- }
 
 
15
  * Description: An easy, clean and simple way to add code snippets to your site. No need to edit to your theme's functions.php file again!
16
  * Author: Shea Bunge
17
  * Author URI: http://bungeshea.com
18
+ * Version: 1.7
19
+ * License: MIT
 
20
  * Text Domain: code-snippets
21
  * Domain Path: /languages/
22
  *
57
  final class Code_Snippets {
58
 
59
  /**
60
+ * The version number for this release of the plugin.
61
+ * This will later be used for upgrades and enqueueing files
 
 
 
62
  *
63
+ * This should be set to the 'Plugin Version' value,
64
+ * as defined above in the plugin header
65
  *
66
  * @since Code Snippets 1.0
67
  * @access public
68
  */
69
+ public $version = 1.7;
70
 
71
  /**
72
+ * The full URLs to the admin pages
 
 
 
 
73
  *
74
+ * @since Code Snippets 1.0
75
+ * @access public
 
 
 
76
  */
77
+ public $admin_manage_url, $admin_single_url, $admin_import_url;
78
 
79
  /**
80
+ * The hooks for the admin pages
81
+ * Used primarily for enqueueing scripts and styles
 
 
 
82
  *
83
  * @since Code Snippets 1.0
84
  * @access public
85
  */
86
+ public $admin_manage, $admin_single, $admin_import;
87
 
88
  /**
89
+ * Variables to hold plugin paths
90
  *
91
  * @since Code Snippets 1.0
92
  * @access public
93
  */
94
+ public $file, $basename, $plugin_dir, $plugin_url;
95
 
96
  /**
97
+ * Stores an instance of the list table class
 
98
  *
99
+ * @since Code Snippets 1.5
100
  * @access public
101
  */
102
+ public $list_table;
103
 
104
  /**
105
  * The constructor function for our class
106
+ * Hooks our initialize function to the plugins_loaded action
107
  *
108
  * @since Code Snippets 1.0
109
  * @access private
111
  * @return void
112
  */
113
  function __construct() {
114
+ add_action( 'plugins_loaded', array( $this, 'init' ) );
115
+
116
+ /* execute the snippets once the plugins are loaded */
117
+ add_action( 'plugins_loaded', array( $this, 'run_snippets' ), 1 );
118
+
119
+ /* add backwards-compatibly for the CS_SAFE_MODE constant */
120
+ if ( defined( 'CS_SAFE_MODE' ) && ! defined( 'CODE_SNIPPETS_SAFE_MODE' ) ) {
121
+ define( 'CODE_SNIPPETS_SAFE_MODE', CS_SAFE_MODE );
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Call our loader functions
127
+ *
128
+ * @since Code Snippets 1.7
129
+ * @access private
130
+ *
131
+ * @return void
132
+ */
133
+ function init() {
134
+
135
+ /**
136
+ * Call the functions required to load Code Snippets
137
+ */
138
  $this->setup_vars(); // initialise the variables
139
  $this->setup_hooks(); // register the action and filter hooks
140
  $this->upgrade(); // check if we need to change some stuff
141
+
142
+ /*
143
+ * Load up the localization file if we're using WordPress in a different language
144
+ * Place it in this plugin's "languages" folder and name it "code-snippets-[value in wp-config].mo"
145
+ *
146
+ * If you wish to contribute a language file to be included in the Code Snippets package,
147
+ * please see create an issue on GitHub: https://github.com/bungeshea/code-snippets/issues
148
+ */
149
+ load_plugin_textdomain( 'code-snippets', false, dirname( $this->basename ) . '/languages/' );
150
+
151
+ /**
152
+ * Let extention plugins know that it's okay to load
153
+ */
154
+ do_action( 'code_snippets_init' );
155
  }
156
 
157
  /**
166
  global $wpdb;
167
  $this->file = __FILE__;
168
 
 
 
 
169
  $this->basename = plugin_basename( $this->file );
170
  $this->plugin_dir = plugin_dir_path( $this->file );
171
  $this->plugin_url = plugin_dir_url ( $this->file );
187
  */
188
  function setup_hooks() {
189
 
 
 
 
190
  /* add the administration menus */
191
+ add_action( 'admin_menu', array( $this, 'add_admin_menus' ), 5 );
192
+ add_action( 'network_admin_menu', array( $this, 'add_admin_menus' ), 5 );
193
 
194
  /* register the importer */
195
+ add_action( 'admin_init', array( $this, 'load_importer' ) );
196
  add_action( 'network_admin_menu', array( $this, 'add_import_admin_menu' ) );
197
 
 
 
 
198
  /* add helpful links to the Plugins menu */
199
  add_filter( 'plugin_action_links_' . $this->basename, array( $this, 'settings_link' ) );
200
  add_filter( 'plugin_row_meta', array( $this, 'plugin_meta' ), 10, 2 );
201
 
202
  /* Add a custom icon to Snippets menu pages */
203
+ add_action( 'admin_enqueue_scripts', array( $this, 'load_admin_icon_style' ) );
204
+
205
+ /* Register the table name with WordPress */
206
+ add_action( 'init', array( $this, 'set_table_vars' ), 1 );
207
+ add_action( 'switch_blog', array( $this, 'set_table_vars' ), 1 );
208
+
209
+ /* Add the description editor to the Snippets > Add New page */
210
+ add_action( 'code_snippets_admin_single', array( $this, 'description_editor_box' ), 5 );
211
+
212
+ /* Handle saving the user's screen option preferences */
213
+ add_filter( 'set-screen-option', array( $this, 'set_screen_option' ), 10, 3 );
214
+ }
215
+
216
+ /**
217
+ * Initialize the variables holding the table names
218
+ *
219
+ * @since Code Snippets 1.7
220
+ * @access public
221
+ *
222
+ * @uses $wpdb
223
+ *
224
+ * @return void
225
+ */
226
+ public function set_table_vars() {
227
+ global $wpdb;
228
+
229
+ $wpdb->snippets = apply_filters( 'code_snippets_table', $wpdb->prefix . 'snippets' );
230
+ $wpdb->ms_snippets = apply_filters( 'code_snippets_multisite_table', $wpdb->base_prefix . 'ms_snippets' );
231
+
232
+ $this->table = &$wpdb->snippets;
233
+ $this->ms_table = &$wpdb->ms_snippets;
234
  }
235
 
236
  /**
281
  */
282
  function get_table_name( $scope = '', $check_screen = true ) {
283
 
284
+ global $wpdb;
285
+
286
  $this->create_tables(); // create the snippet tables if they do not exist
287
 
288
  if ( ! is_multisite() ) {
293
  $screen = get_current_screen();
294
  $network = $screen->is_network;
295
  }
296
+ elseif ( 'multisite' === $scope || 'network' === $scope ) {
297
  $network = true;
298
  }
299
+ elseif ( 'site' === $scope || 'single' === $scope ) {
300
  $network = false;
301
  }
302
  else {
303
  $network = false;
304
  }
305
 
306
+ $table = ( $network ? $wpdb->ms_snippets : $wpdb->snippets );
307
 
308
  return $table;
309
  }
315
  * @access public
316
  *
317
  * @uses $this->create_table() To create a single snippet table
318
+ * @uses $wpdb->get_var() To test of the table exists
319
  *
320
  * @return void
321
  */
322
  public function create_tables() {
323
 
324
+ global $wpdb;
325
+
326
+ if ( $wpdb->get_var( "SHOW TABLES LIKE '$wpdb->snippets'" ) !== $wpdb->snippets )
327
+ $this->create_table( $wpdb->snippets );
328
 
329
+ if ( is_multisite() && $wpdb->get_var( "SHOW TABLES LIKE '$wpdb->ms_snippets'" ) !== $wpdb->ms_snippets )
330
+ $this->create_table( $wpdb->ms_snippets );
331
 
 
332
  }
333
 
334
  /**
338
  * @since Code Snippets 1.6
339
  * @access public
340
  *
341
+ * @uses dbDelta() To add the table to the database
342
+ *
343
  * @param string $table_name The name of the table to create
344
  * @return void
345
  */
346
  function create_table( $table_name ) {
347
+ require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
348
 
349
  global $wpdb;
350
 
351
+ if ( ! empty( $wpdb->charset ) ) {
352
+ $charset_collate = "DEFAULT CHARACTER SET $wpdb->charset";
353
+ }
354
+
355
+ if ( ! empty( $wpdb->collate ) ) {
356
+ $charset_collate .= " COLLATE $wpdb->collate";
 
 
 
 
 
357
  }
358
+
359
+ $sql = "CREATE TABLE $table_name (
360
+ id bigint(20) auto_increment,
361
+ name tinytext not null,
362
+ description text,
363
+ code longtext not null,
364
+ active tinyint(1) not null default 0,
365
+ PRIMARY KEY (id),
366
+ KEY id (id)
367
+
368
+ ) {$charset_collate};";
369
+
370
+ dbDelta( apply_filters( 'code_snippets_table_sql', $sql ) );
371
+
372
+ do_action( 'code_snippets_create_table', $table_name );
373
  }
374
 
375
  /**
378
  * @since Code Snippets 1.2
379
  * @access private
380
  *
381
+ * @return void
382
  */
383
  function upgrade() {
384
+ global $wpdb;
 
 
 
 
385
 
386
  /* get the current plugin version from the database */
387
  if ( get_option( 'cs_db_version' ) ) {
393
  $this->current_version = get_site_option( 'code_snippets_version', $this->version );
394
  }
395
 
396
+ if ( ! get_site_option( 'code_snippets_version' ) || $this->current_version < 1.5 ) {
 
 
 
397
 
398
  /* This is the first time the plugin has run */
399
 
400
+ $this->add_caps(); // register the capabilities once only
401
 
402
  if ( is_multisite() ) {
403
+ $this->add_caps( 'multisite' ); // register the multisite capabilities once only
404
  }
405
  }
406
 
407
+ /* skip this if we're on the latest version */
408
+ if ( get_site_option( 'code_snippets_version' ) < $this->version ) {
 
 
 
 
 
 
 
 
 
 
 
409
 
410
+ /* migrate the recently_network_activated_snippets to the site options */
411
+ if ( is_multisite() && get_option( 'recently_network_activated_snippets' ) ) {
412
+ add_site_option( 'recently_activated_snippets', get_option( 'recently_network_activated_snippets', array() ) );
413
+ delete_option( 'recently_network_activated_snippets' );
414
  }
415
 
416
+ if ( $this->current_version < 1.2 ) {
417
+ /* The 'Complete Uninstall' option was removed in version 1.2 */
418
+ delete_option( 'cs_complete_uninstall' );
419
+ }
 
 
 
 
420
 
 
421
  /* Update the current version */
422
  update_site_option( 'code_snippets_version', $this->version );
423
  }
 
 
424
  }
425
 
426
  /**
427
+ * Handles saving the user's screen option preference
 
 
 
 
428
  *
429
  * @since Code Snippets 1.5
430
  * @access private
 
 
431
  */
432
+ function set_screen_option( $status, $option, $value ) {
433
+ if ( 'snippets_per_page' === $option ) return $value;
434
  }
435
 
436
  /**
446
  */
447
  public function add_caps( $scope = '' ) {
448
 
449
+ $network = ( 'multisite' === $scope || 'network' === $scope ? true : false );
450
 
451
  if ( $network && is_multisite() )
452
  $this->setup_ms_roles( 'add' );
467
  */
468
  public function remove_caps( $scope = '' ) {
469
 
470
+ $network = ( 'multisite' === $scope || 'network' === $scope ? true : false );
471
 
472
  if ( $network && is_multisite() )
473
  $this->setup_ms_roles( 'remove' );
486
  */
487
  function setup_roles( $action = 'install' ) {
488
 
489
+ if ( 'install' === $action || 'add' === $action )
490
  $install = true;
491
 
492
+ elseif ( 'uninstall' === $action || 'remove' === $action )
493
  $install = false;
494
 
495
  else
525
 
526
  if ( ! is_multisite() ) return;
527
 
528
+ if ( 'install' === $action || 'add' === $action )
529
  $install = true;
530
 
531
+ elseif ( 'uninstall' === $action || 'remove' === $action )
532
  $install = false;
533
 
534
  else
560
  *
561
  * @uses add_menu_page() To register a top-level menu
562
  * @uses add_submenu_page() To register a submenu page
563
+ * @uses apply_filters() To retrieve the current menu slug
564
  * @uses plugins_url() To retrieve the URL to a resource
565
  * @return void
566
  */
567
  function add_admin_menus() {
568
 
569
+ /* Use a different screen icon for the MP6 interface */
570
+ if ( get_user_option( 'admin_color' ) !== 'mp6' )
571
+ $menu_icon = apply_filters( 'code_snippets_menu_icon', plugins_url( 'images/menu-icon.png', $this->file ) );
572
+ else
573
+ $menu_icon = 'div';
574
+
575
+ /* Add the top-level menu and relevant subpage */
576
  $this->admin_manage = add_menu_page(
577
  __('Snippets', 'code-snippets'),
578
  __('Snippets', 'code-snippets'),
579
+ is_network_admin() ? 'manage_network_snippets' : 'manage_snippets',
580
+ $this->admin_manage_slug,
581
  array( $this, 'display_admin_manage' ),
582
+ $menu_icon,
583
+ is_network_admin() ? 21 : 67
584
  );
585
 
586
  add_submenu_page(
587
+ $this->admin_manage_slug,
588
  __('Snippets', 'code-snippets'),
589
+ __('Manage', 'code-snippets'),
590
+ is_network_admin() ? 'manage_network_snippets' : 'manage_snippets',
591
+ $this->admin_manage_slug,
592
  array( $this, 'display_admin_manage')
593
  );
594
 
595
+ /* Add the Edit/Add New Snippet page */
596
+ if ( isset( $_REQUEST['page'], $_REQUEST['edit'] ) &&
597
+ $this->admin_single_slug === $_REQUEST['page'] ) {
598
+
599
+ $this->admin_single = add_submenu_page(
600
+ $this->admin_manage_slug,
601
+ __('Edit Snippet', 'code-snippets'),
602
+ __('Edit', 'code-snippets'),
603
+ is_network_admin() ? 'install_network_snippets' : 'install_snippets',
604
+ $this->admin_single_slug,
605
+ array( $this, 'display_admin_single' )
606
+ );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
607
 
608
+ } else {
 
 
 
 
 
 
 
609
 
610
+ $this->admin_single = add_submenu_page(
611
+ $this->admin_manage_slug,
612
+ __('Add New Snippet', 'code-snippets'),
613
+ __('Add New', 'code-snippets'),
614
+ is_network_admin() ? 'install_network_snippets' : 'install_snippets',
615
+ $this->admin_single_slug,
616
+ array( $this, 'display_admin_single' )
617
+ );
618
+ }
619
 
620
  add_action( "load-$this->admin_manage", array( $this, 'load_admin_manage' ) );
621
  add_action( "load-$this->admin_single", array( $this, 'load_admin_single' ) );
 
 
 
622
  }
623
 
624
  /**
630
  * @access private
631
  *
632
  * @uses add_submenu_page() To register the menu page
633
+ * @uses apply_filters() To retrieve the current menu slug
634
  * @uses add_action() To enqueue scripts and styles
635
  * @return void
636
  */
637
  function add_import_admin_menu() {
638
 
639
  $this->admin_import = add_submenu_page(
640
+ $this->admin_manage_slug,
641
  __('Import Snippets', 'code-snippets'),
642
  __('Import', 'code-snippets'),
643
  'import_snippets',
646
  );
647
 
648
  $this->admin_import_url = self_admin_url( 'admin.php?page=import-code-snippets' );
 
649
  add_action( "load-$this->admin_import", array( $this, 'load_admin_import' ) );
650
  }
651
 
661
  */
662
  function load_admin_icon_style() {
663
 
664
+ if ( 'mp6' === get_user_option( 'admin_color' ) ) {
665
+
666
+ wp_enqueue_style(
667
+ 'icon-snippets',
668
+ plugins_url( 'assets/menu-icon.mp6.css', $this->file ),
669
+ false,
670
+ $this->version
671
+ );
672
+
673
+ } else {
674
+
675
+ wp_enqueue_style(
676
+ 'icon-snippets',
677
+ plugins_url( 'assets/screen-icon.css', $this->file ),
678
+ false,
679
+ $this->version
680
+ );
681
+ }
682
  }
683
 
684
  /**
685
+ * Converts an array of snippet data into a snippet object
686
  *
687
+ * @since Code Snippets 1.7
688
+ * @access public
 
 
 
689
  *
690
+ * @param mixed $data The snippet data to convert
691
+ * @return object The resulting snippet object
692
  */
693
+ public function build_snippet_object( $data = null ) {
694
 
695
+ $snippet = new stdClass;
 
696
 
697
+ // define an empty snippet object (or one with default values )
698
+ $snippet->id = 0;
699
+ $snippet->name = '';
700
+ $snippet->description = '';
701
+ $snippet->code = '';
702
+ $snippet->active = 0;
703
+ $snippet = apply_filters( 'code_snippets_build_default_snippet', $snippet );
704
 
705
+ if ( ! isset( $data ) ) {
706
+ return $snippet;
707
+ }
708
+ elseif ( is_object( $data ) ) {
709
+ /* If we already have a snippet object, merge it with the default */
710
+ return (object) array_merge( (array) $snippet, (array) $data );
711
+ }
712
+ elseif ( is_array( $data ) ) {
713
 
714
+ if ( isset( $data['id'] ) )
715
+ $snippet->id = $data['id'];
716
+ elseif ( isset( $data['snippet_id'] ) )
717
+ $snippet->id = $data['snippet_id'];
718
 
719
+ if ( isset( $data['name'] ) )
720
+ $snippet->name = $data['name'];
721
+ elseif ( isset( $data['snippet_name'] ) )
722
+ $snippet->name = $data['snippet_name'];
723
 
724
+ if ( isset( $data['description'] ) )
725
+ $snippet->description = $data['description'];
726
+ elseif ( isset( $data['snippet_description'] ) )
727
+ $snippet->description = $data['snippet_description'];
728
 
729
+ if ( isset( $data['code'] ) )
730
+ $snippet->code = $data['code'];
731
+ elseif ( isset( $data['snippet_code'] ) )
732
+ $snippet->code = $data['snippet_code'];
733
+
734
+
735
+ if ( isset( $data['active' ] ) )
736
+ $snippet->active = $data['active'];
737
+ elseif ( isset( $data['snippet_active'] ) )
738
+ $snippet->active = $data['snippet_active'];
739
+
740
+ return apply_filters( 'code_snippets_build_snippet_object', $snippet, $data );
741
  }
742
 
743
+ return $snippet;
744
+ }
745
 
746
+ /**
747
+ * Retrieve a list of snippets from the database
748
+ *
749
+ * @since Code Snippets 1.7
750
+ * @access public
751
+ *
752
+ * @uses $wpdb To query the database for snippets
753
+ * @uses $this->get_table_name() To dynamically retrieve the snippet table name
754
+ *
755
+ * @param string $scope Retrieve multisite-wide or site-wide snippets?
756
+ * @return array An array of snippet objects
757
+ */
758
+ public function get_snippets( $scope = '' ) {
759
+ global $wpdb;
760
 
761
+ $table = $this->get_table_name( $scope );
762
+ $snippets = $wpdb->get_results( "SELECT * FROM $table", ARRAY_A );
763
 
764
+ foreach( $snippets as $index => $snippet ) {
765
+ $snippets[ $index ] = $this->unescape_snippet_data( $snippet );
 
 
 
 
766
  }
767
 
768
+ return apply_filters( 'code_snippets_get_snippets', $snippets, $scope );
 
 
 
 
 
 
 
 
 
 
 
769
  }
770
 
771
  /**
772
+ * Escape snippet data for inserting into the database
 
 
 
773
  *
774
+ * @since Code Snippets 1.7
775
+ * @access public
776
  *
777
+ * @param mixed $snippet An object or array containing the data to escape
778
+ * @return object The resulting snippet object, with data escaped
779
  */
780
+ public function escape_snippet_data( $snippet ) {
781
 
782
+ $snippet = $this->build_snippet_object( $snippet );
 
783
 
784
+ /* remove the <?php and ?> tags from the snippet */
785
+ $snippet->code = trim ( $snippet->code );
786
+ $snippet->code = ltrim( $snippet->code, '<?php' );
787
+ $snippet->code = ltrim( $snippet->code, '<?' );
788
+ $snippet->code = rtrim( $snippet->code, '?>' );
789
 
790
+ /* escape the data */
791
+ $snippet->name = mysql_real_escape_string( htmlspecialchars( $snippet->name ) );
792
+ $snippet->description = mysql_real_escape_string( htmlspecialchars( $snippet->description ) );
793
+ $snippet->code = mysql_real_escape_string( htmlspecialchars( $snippet->code ) );
794
+ $snippet->id = intval( $snippet->id );
 
795
 
796
+ return apply_filters( 'code_snippets_escape_snippet_data', $snippet );
797
+ }
798
 
799
+ /**
800
+ * Unescape snippet data after retrieving from the database
801
+ * ready for use
802
+ *
803
+ * @since Code Snippets 1.7
804
+ * @access public
805
+ *
806
+ * @param mixed $snippet An object or array containing the data to unescape
807
+ * @return object The resulting snippet object, with data unescaped
808
+ */
809
+ public function unescape_snippet_data( $snippet ) {
810
 
811
+ $snippet = $this->build_snippet_object( $snippet );
812
 
813
+ $snippet->name = htmlspecialchars_decode( stripslashes( $snippet->name ) );
814
+ $snippet->code = htmlspecialchars_decode( stripslashes( $snippet->code ) );
815
+ $snippet->description = htmlspecialchars_decode( stripslashes( $snippet->description ) );
 
816
 
817
+ return apply_filters( 'code_snippets_unescape_snippet_data', $snippet );
818
+ }
819
+
820
+ /**
821
+ * Retrieve a single snippets from the database
822
+ * Will return empty snippet object if no snippet
823
+ * ID is specified
824
+ *
825
+ * @since Code Snippets 1.7
826
+ * @access public
827
+ *
828
+ * @uses $wpdb To query the database for snippets
829
+ * @uses $this->get_table_name() To dynamically retrieve the snippet table name
830
+ *
831
+ * @param string $scope Retrieve a multisite-wide or site-wide snippet?
832
+ * @return object A single snippet object
833
+ */
834
+ public function get_snippet( $id = 0, $scope = '' ) {
835
+ global $wpdb;
836
+ $table = $this->get_table_name( $scope );
837
+
838
+ if ( ! empty( $id ) && 0 !== intval( $id ) ) {
839
+ /* Retrieve the snippet from the database */
840
+ $snippet = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE id = %d", $id ) );
841
+ /* Unescape the snippet data, ready for use */
842
+ $snippet = $this->unescape_snippet_data( $snippet );
843
+ } else {
844
+ // get an empty snippet object
845
+ $snippet = $this->build_snippet_object();
846
+ }
847
+ return apply_filters( 'code_snippets_get_snippet', $snippet, $id, $scope );
848
  }
849
 
850
  /**
874
  array( '%d' ),
875
  array( '%d' )
876
  );
877
+
878
+ do_action( 'code_snippets_activate_snippet', $id, $scope );
879
  }
880
+
881
+ do_action( 'code_snippets_activate', $ids, $scope );
882
  }
883
 
884
  /**
910
  array( '%d' )
911
  );
912
  $recently_active = array( $id => time() ) + (array) $recently_active;
913
+
914
+ do_action( 'code_snippets_deactivate_snippet', $id, $scope );
915
  }
916
 
917
+ if ( $table === $wpdb->ms_snippets )
918
  update_site_option(
919
  'recently_activated_snippets',
920
  $recently_active + (array) get_site_option( 'recently_activated_snippets' )
921
  );
922
+ elseif ( $table === $wpdb->snippets )
923
  update_option(
924
  'recently_activated_snippets',
925
  $recently_active + (array) get_option( 'recently_activated_snippets' )
926
  );
927
+
928
+ do_action( 'code_snippets_deactivate', $ids, $scope );
929
  }
930
 
931
  /**
941
  global $wpdb;
942
 
943
  $table = $this->get_table_name( $scope );
 
944
 
945
+ $wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE id='%d' LIMIT 1", intval( $id ) ) );
946
+
947
+ do_action( 'code_snippets_delete_snippet', $id, $scope );
948
  }
949
 
950
  /**
956
  * @uses $wpdb To update/add the snippet to the database
957
  * @uses $this->get_table_name() To dynamically retrieve the name of the snippet table
958
  *
959
+ * @param object $snippet The snippet to add/update to the database
960
  * @return int|bool The ID of the snippet on success, false on failure
961
  */
962
  public function save_snippet( $snippet, $scope = '' ) {
963
  global $wpdb;
964
 
965
+ $snippet = $this->escape_snippet_data( $snippet );
 
 
966
 
967
+ if ( empty( $snippet->name ) or empty( $snippet->code ) )
968
  return false;
969
 
970
  $table = $this->get_table_name( $scope );
971
 
972
+ $fields = '';
973
+ foreach ( get_object_vars( $snippet ) as $field => $value ) {
974
+ if ( 'id' !== $field ) {
975
+ $fields .= "{$field}='{$value}',";
976
+ }
977
+ }
978
+ $fields = rtrim( $fields, ',' );
979
+
980
+ if ( isset( $snippet->id ) && 0 !== $snippet->id ) {
981
+
982
+ if ( $fields ) {
983
+ $wpdb->query( $wpdb->prepare( "UPDATE $table SET $fields WHERE id='%d' LIMIT 1", $snippet->id ) );
984
+ }
985
+
986
+ do_action( 'code_snippets_update_snippet', $snippet, $table );
987
+ return $snippet->id;
988
+
989
  } else {
990
+
991
+ if ( $fields ) {
992
+ $wpdb->query( "INSERT INTO $table SET $fields" );
993
+ }
994
+
995
+ do_action( 'code_snippets_create_snippet', $snippet, $table );
996
  return $wpdb->insert_id;
997
  }
998
  }
1003
  * @since Code Snippets 1.5
1004
  * @access public
1005
  *
1006
+ * @uses $this->save_snippet() To add the snippets to the database
1007
  *
1008
  * @param file $file The path to the XML file to import
1009
  * @param string $scope Import into network-wide table or site-wide table?
1016
 
1017
  $xml = simplexml_load_file( $file );
1018
 
1019
+ foreach ( $xml->children() as $snippet ) {
1020
+ /* force manual build of object to strip out unsupported fields
1021
+ by converting snippet object into array */
1022
+ $this->save_snippet( get_object_vars( $snippet ), $scope );
 
 
1023
  }
1024
+
1025
+ do_action( 'code_snippets_import', $xml, $scope );
1026
+
1027
  return $xml->count();
1028
  }
1029
 
1090
  $result = eval( $code );
1091
  $output = ob_get_contents();
1092
  ob_end_clean();
1093
+ do_action( 'code_snippets_execute_snippet', $code );
1094
  return $result;
1095
  }
1096
 
1097
+ /**
1098
+ * Returns the (de)activate link for a snippet
1099
+ *
1100
+ * @since Code Snippets 1.7
1101
+ * @access public
1102
+ *
1103
+ * @param mixed $snippet A snippet object
1104
+ * @return The HTML code for the (de)activation link
1105
+ */
1106
+ public function get_activate_link( $snippet ) {
1107
+
1108
+ $snippet = $this->build_snippet_object( $snippet );
1109
+ $screen = get_current_screen();
1110
+
1111
+ if ( $snippet->active ) {
1112
+ $activate_link = sprintf(
1113
+ '<a href="%1$s">%2$s</a>',
1114
+ add_query_arg( array(
1115
+ 'action' => 'deactivate',
1116
+ 'id' => $snippet->id
1117
+ ), $this->admin_manage_url ),
1118
+ $screen->is_network ? __('Network Deactivate', 'code-snippets') : __('Deactivate', 'code-snippets')
1119
+ );
1120
+ } else {
1121
+ $activate_link = sprintf(
1122
+ '<a href="%1$s">%2$s</a>',
1123
+ add_query_arg( array(
1124
+ 'action' => 'activate',
1125
+ 'id' => $snippet->id
1126
+ ), $this->admin_manage_url ),
1127
+ $screen->is_network ? __('Network Activate', 'code-snippets') : __('Activate', 'code-snippets')
1128
+ );
1129
+ }
1130
+
1131
+ return apply_filters( 'code_snippets_activate_link', $activate_link, $snippet );
1132
+ }
1133
+
1134
  /**
1135
  * Replaces the text 'Add New Snippet' with 'Edit Snippet'
1136
  *
1169
 
1170
  $_SERVER['REQUEST_URI'] = remove_query_arg( array( 'action', 'id' ) );
1171
 
1172
+ $action = sanitize_key( $_GET['action'] );
1173
+
1174
+ if ( 'activate' === $action ) {
1175
  $this->activate( $id );
 
1176
  }
1177
+ elseif ( 'deactivate' === $action ) {
1178
  $this->deactivate( $id );
 
1179
  }
1180
+ elseif ( 'delete' === $action ) {
1181
  $this->delete_snippet( $id );
 
1182
  }
1183
+ elseif ( 'export' === $action ) {
1184
  $this->export( $id );
1185
  }
1186
+ elseif ( 'export-php' === $action ) {
1187
  $this->export_php( $id );
1188
  }
1189
 
1190
+ if ( 'export' !== $action || 'export-php' !== $action ) {
1191
+ wp_redirect( apply_filters(
1192
+ "code_snippets_{$action}_redirect",
1193
+ add_query_arg( $action, true )
1194
+ ) );
1195
+ }
1196
+
1197
  endif;
1198
 
1199
  include $this->plugin_dir . 'includes/help/manage.php'; // Load the help tabs
1203
  */
1204
  require_once $this->plugin_dir . 'includes/class-list-table.php';
1205
 
1206
+ $this->list_table = new Code_Snippets_List_Table();
1207
+ $this->list_table->prepare_items();
 
1208
  }
1209
 
1210
  /**
1220
  */
1221
  function load_admin_single() {
1222
 
1223
+ $screen = get_current_screen();
1224
+ $can_edit = current_user_can( $screen->is_network ? 'edit_network_snippets' : 'edit_snippets' );
1225
+
1226
+ if ( isset( $_REQUEST['edit'] ) && ! $can_edit )
1227
+ wp_die( __("Sorry, you're not allowed to edit snippets", 'code-snippets') );
1228
+
1229
  $this->create_tables(); // create the snippet tables if they do not exist
1230
 
1231
+ if ( isset( $_REQUEST['save_snippet'] ) || isset( $_REQUEST['save_snippet_activate'] ) ) {
1232
 
1233
+ $result = $this->save_snippet( $_POST );
1234
+
1235
+ $_SERVER['REQUEST_URI'] = remove_query_arg( array( 'added', 'updated', 'activated', 'invalid' ) );
 
 
 
 
 
 
 
 
 
 
 
1236
 
1237
+ if ( isset( $_REQUEST['save_snippet_activate'] ) && $result ) {
1238
+ $this->activate( $result );
1239
+ $_SERVER['REQUEST_URI'] = add_query_arg( 'activated', true );
1240
+ }
1241
 
1242
  if ( ! $result || $result < 1 ) {
1243
  wp_redirect( add_query_arg( 'invalid', true ) );
1261
 
1262
  include $this->plugin_dir . 'includes/help/single.php'; // Load the help tabs
1263
 
1264
+ add_filter( 'admin_enqueue_scripts', array( $this, 'admin_single_enqueue_scripts' ) );
1265
+ }
1266
+
1267
+ /**
1268
+ * Registers and loads the code editor's scripts
1269
+ *
1270
+ * @since Code Snippets 1.7
1271
+ * @access private
1272
+ *
1273
+ * @uses wp_register_script()
1274
+ * @uses wp_register_style()
1275
+ * @uses wp_enqueue_script() To add the scripts to the queue
1276
+ * @uses wp_enqueue_style() To add the stylesheets to the queue
1277
+ *
1278
+ * @return void
1279
+ */
1280
+ function admin_single_enqueue_scripts( $hook ) {
1281
+
1282
+ if ( $hook !== $this->admin_single )
1283
+ return;
1284
+
1285
+ /* CodeMirror package version */
1286
+ $codemirror_version = '3.11';
1287
+
1288
+ /* CodeMirror base framework */
1289
+
1290
+ wp_register_script(
1291
+ 'codemirror',
1292
+ plugins_url( 'assets/codemirror/lib/codemirror.js', $this->file ),
1293
+ false,
1294
+ $codemirror_version
1295
+ );
1296
+
1297
+ wp_register_style(
1298
+ 'codemirror',
1299
+ plugins_url( 'assets/codemirror/lib/codemirror.css', $this->file ),
1300
+ false,
1301
+ $codemirror_version
1302
+ );
1303
+
1304
+ /* CodeMirror modes */
1305
+
1306
+ $modes = array( 'php', 'clike' );
1307
+
1308
+ foreach ( $modes as $mode ) {
1309
+
1310
+ wp_register_script(
1311
+ "codemirror-mode-$mode",
1312
+ plugins_url( "assets/codemirror/mode/$mode.js", $this->file ),
1313
+ array( 'codemirror' ),
1314
+ $codemirror_version
1315
+ );
1316
+ }
1317
+
1318
+ /* CodeMirror addons */
1319
+
1320
+ $addons = array( 'dialog', 'searchcursor', 'search', 'matchbrackets' );
1321
+
1322
+ foreach ( $addons as $addon ) {
1323
+
1324
+ wp_register_script(
1325
+ "codemirror-addon-$addon",
1326
+ plugins_url( "assets/codemirror/addon/$addon.js", $this->file ),
1327
+ array( 'codemirror' ),
1328
+ $codemirror_version
1329
+ );
1330
+ }
1331
+
1332
+ wp_register_style(
1333
+ 'codemirror-addon-dialog',
1334
+ plugins_url( 'assets/codemirror/addon/dialog.css', $this->file ),
1335
+ array( 'codemirror' ),
1336
+ $codemirror_version
1337
+ );
1338
+
1339
+ /* Enqueue the registered scripts */
1340
+ wp_enqueue_script( array(
1341
+ 'codemirror-addon-matchbrackets',
1342
+ 'codemirror-mode-clike',
1343
+ 'codemirror-mode-php',
1344
+ 'codemirror-addon-search',
1345
+ ) );
1346
+
1347
+ /* Enqueue the registered stylesheets */
1348
+ wp_enqueue_style( array(
1349
+ 'codemirror',
1350
+ 'codemirror-addon-dialog',
1351
+ ) );
1352
+
1353
+ /* Enqueue custom styling */
1354
+ wp_enqueue_style(
1355
+ 'code-snippets-admin-single',
1356
+ plugins_url( 'assets/admin-single.css', $this->file ),
1357
+ false,
1358
+ $this->version
1359
+ );
1360
  }
1361
 
1362
  /**
1419
  require_once $this->plugin_dir . 'includes/admin/import.php';
1420
  }
1421
 
1422
+ /**
1423
+ * Add a description editor to the Add New/Edit Snippet page
1424
+ *
1425
+ * @since Code Snippets 1.7
1426
+ * @access private
1427
+ *
1428
+ */
1429
+ function description_editor_box( $snippet ) {
1430
+ ?>
1431
+
1432
+ <label for="snippet_description">
1433
+ <h3><div style="position: absolute;"><?php _e('Description', 'code-snippets');
1434
+ ?> <span style="font-weight: normal;"><?php esc_html_e('(Optional)', 'code-snippets'); ?></span></div></h3>
1435
+ </label>
1436
+
1437
+ <?php
1438
+
1439
+ remove_editor_styles(); // stop custom theme styling interfering with the editor
1440
+
1441
+ wp_editor(
1442
+ $snippet->description,
1443
+ 'description',
1444
+ apply_filters( 'code_snippets_description_editor_settings', array(
1445
+ 'textarea_name' => 'snippet_description',
1446
+ 'textarea_rows' => 10,
1447
+ 'teeny' => true,
1448
+ 'media_buttons' => false,
1449
+ ) )
1450
+ );
1451
+ }
1452
+
1453
  /**
1454
  * Adds a link pointing to the Manage Snippets page
1455
  *
1476
  */
1477
  function plugin_meta( $links, $file ) {
1478
 
1479
+ if ( $file !== $this->basename ) return $links;
1480
 
1481
  $format = '<a href="%1$s" title="%2$s">%3$s</a>';
1482
 
1493
  ),
1494
  sprintf( $format,
1495
  'http://code-snippets.bungeshea.com/donate/',
1496
+ __("Support this plugin's development", 'code-snippets'),
1497
  __('Donate', 'code-snippets')
1498
  )
1499
  ) );
1514
  */
1515
  function run_snippets() {
1516
 
1517
+ if ( defined( 'CODE_SNIPPETS_SAFE_MODE' ) && CODE_SNIPPETS_SAFE_MODE ) return;
 
1518
 
1519
  global $wpdb;
1520
 
1521
+ if ( ! isset( $wpdb->table ) )
1522
+ $this->set_table_vars();
1523
+
1524
+ $active_snippets = array();
1525
+
1526
+ // check that the table exists before continuing
1527
+ if ( $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->snippets}';" ) ) {
1528
+
1529
+ // grab the active snippets from the database
1530
+ $active_snippets = $wpdb->get_col( "SELECT code FROM {$wpdb->snippets} WHERE active=1;" );
1531
+
1532
+ }
1533
 
1534
+ if ( is_multisite() && $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->ms_snippets}';" ) ) {
1535
+
1536
+ // grab the network active snippets from the database
1537
  $active_snippets = array_merge(
1538
+ $wpdb->get_col( "SELECT code FROM {$wpdb->ms_snippets} WHERE active=1;" ),
1539
  $active_snippets
1540
  );
1541
  }
1543
  if ( count( $active_snippets ) ) {
1544
  foreach( $active_snippets as $snippet ) {
1545
  // execute the php code
1546
+ $this->execute_snippet( htmlspecialchars_decode( stripslashes( $snippet ) ) );
1547
  }
1548
  }
1549
  }
1562
  global $cs;
1563
  $cs = &$code_snippets;
1564
 
 
 
1565
  register_uninstall_hook( $code_snippets->file, 'code_snippets_uninstall' );
1566
 
1567
  /**
1582
  function code_snippets_uninstall() {
1583
  global $wpdb, $code_snippets;
1584
  if ( is_multisite() ) {
1585
+ $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
1586
+ if ( $blog_ids ) {
1587
+ foreach ( $blog_ids as $blog_id ) {
1588
+ switch_to_blog( $blog_id );
1589
+ $wpdb->query( "DROP TABLE IF EXISTS $wpdb->snippets" );
1590
  delete_option( 'cs_db_version' );
1591
  delete_option( 'recently_activated_snippets' );
1592
  $code_snippets->remove_caps();
1593
  }
1594
  restore_current_blog();
1595
  }
1596
+ $wpdb->query( "DROP TABLE IF EXISTS $wpdb->ms_snippets" );
1597
  delete_site_option( 'recently_activated_snippets' );
1598
  $code_snippets->remove_caps( 'multisite' );
1599
  } else {
1600
+ $wpdb->query( "DROP TABLE IF EXISTS $wpdb->snippets" );
1601
  delete_option( 'recently_activated_snippets' );
1602
  delete_option( 'cs_db_version' );
1603
  $code_snippets->remove_caps();
1604
  }
 
1605
  delete_site_option( 'code_snippets_version' );
1606
+ }
1607
+
1608
+ endif; // class exists check
images/{icon16.png → menu-icon.png} RENAMED
File without changes
images/{icon32.png → screen-icon.png} RENAMED
File without changes
includes/admin/import.php CHANGED
@@ -1,6 +1,17 @@
1
  <?php
2
 
3
- if ( isset( $_REQUEST['imported'] ) && intval( $_REQUEST['imported'] ) != 0 ) {
 
 
 
 
 
 
 
 
 
 
 
4
 
5
  echo '<div id="message" class="updated fade"><p>';
6
 
@@ -29,6 +40,7 @@ if ( isset( $_REQUEST['imported'] ) && intval( $_REQUEST['imported'] ) != 0 ) {
29
  <p><?php printf( __('You will need to go to the <a href="%s">Manage Snippets</a> page to activate the imported snippets.', 'code-snippets'), $this->admin_manage_url ); ?></p>
30
 
31
  <p><?php _e('Choose a Code Snippets (.xml) file to upload, then click Upload file and import.', 'code-snippets'); ?></p>
 
32
  <form enctype="multipart/form-data" id="import-upload-form" method="post" action="" name="code_snippets_import">
33
  <p>
34
  <label for="upload"><?php _e('Choose a file from your computer:', 'code-snippets' ); ?></label> <?php _e('(Maximum size: 8MB)', 'code-snippets'); ?>
@@ -36,6 +48,9 @@ if ( isset( $_REQUEST['imported'] ) && intval( $_REQUEST['imported'] ) != 0 ) {
36
  <input type="hidden" name="action" value="save" />
37
  <input type="hidden" name="max_file_size" value="8388608" />
38
  </p>
 
 
 
39
  <p class="submit">
40
  <input type="submit" name="submit" id="submit" class="button" value="<?php _e('Upload file and import', 'code-snippets'); ?>" />
41
  </p>
1
  <?php
2
 
3
+ /**
4
+ * HTML code for the Import Snippets page
5
+ *
6
+ * @package Code Snippets
7
+ * @subpackage Administration
8
+ */
9
+
10
+ if ( ! class_exists( 'Code_Snippets' ) ) exit;
11
+
12
+ /* Display the admin notice */
13
+
14
+ if ( isset( $_REQUEST['imported'] ) && 0 !== intval( $_REQUEST['imported'] ) ) {
15
 
16
  echo '<div id="message" class="updated fade"><p>';
17
 
40
  <p><?php printf( __('You will need to go to the <a href="%s">Manage Snippets</a> page to activate the imported snippets.', 'code-snippets'), $this->admin_manage_url ); ?></p>
41
 
42
  <p><?php _e('Choose a Code Snippets (.xml) file to upload, then click Upload file and import.', 'code-snippets'); ?></p>
43
+
44
  <form enctype="multipart/form-data" id="import-upload-form" method="post" action="" name="code_snippets_import">
45
  <p>
46
  <label for="upload"><?php _e('Choose a file from your computer:', 'code-snippets' ); ?></label> <?php _e('(Maximum size: 8MB)', 'code-snippets'); ?>
48
  <input type="hidden" name="action" value="save" />
49
  <input type="hidden" name="max_file_size" value="8388608" />
50
  </p>
51
+
52
+ <?php do_action( 'code_snippets_admin_import_form' ); ?>
53
+
54
  <p class="submit">
55
  <input type="submit" name="submit" id="submit" class="button" value="<?php _e('Upload file and import', 'code-snippets'); ?>" />
56
  </p>
includes/admin/manage.php CHANGED
@@ -1,9 +1,16 @@
1
  <?php
 
 
 
 
 
 
 
 
2
  if ( ! class_exists( 'Code_Snippets' ) ) exit;
3
 
4
  require_once $this->plugin_dir . 'includes/class-list-table.php';
5
 
6
- global $code_snippets_list_table;
7
  $screen = get_current_screen();
8
  ?>
9
  <?php if ( defined( 'CODE_SNIPPETS_SAFE_MODE' ) && CODE_SNIPPETS_SAFE_MODE ) : ?>
@@ -26,21 +33,25 @@ $screen = get_current_screen();
26
 
27
  <div class="wrap">
28
  <?php screen_icon(); ?>
29
- <h2><?php _e('Snippets', 'code-snippets'); ?>
30
  <?php if ( current_user_can( $screen->is_network ? 'install_network_snippets' : 'install_snippets' ) ) { ?>
31
  <a href="<?php echo $this->admin_single_url; ?>" class="add-new-h2"><?php echo esc_html_x('Add New', 'snippet', 'code-snippets'); ?></a>
32
  <?php }
33
- if ( isset( $s ) && $s )
34
- printf( '<span class="subtitle">' . __('Search results for &#8220;%s&#8221;', 'code-snippets') . '</span>', esc_html( $s ) ); ?></h2>
35
 
36
- <?php $code_snippets_list_table->views(); ?>
37
 
38
  <form method="get" action="">
39
- <input type="hidden" name="page" value="<?php echo $_REQUEST['page'] ?>" />
40
- <?php $code_snippets_list_table->search_box( __( 'Search Installed Snippets', 'code-snippets' ), 'search_id' ); ?>
 
 
41
  </form>
42
  <form method="post" action="">
43
- <input type="hidden" name="page" value="<?php echo $_REQUEST['page'] ?>" />
44
- <?php $code_snippets_list_table->display(); ?>
45
  </form>
 
 
 
46
  </div>
1
  <?php
2
+
3
+ /**
4
+ * HTML code for the Manage Snippets page
5
+ *
6
+ * @package Code Snippets
7
+ * @subpackage Administration
8
+ */
9
+
10
  if ( ! class_exists( 'Code_Snippets' ) ) exit;
11
 
12
  require_once $this->plugin_dir . 'includes/class-list-table.php';
13
 
 
14
  $screen = get_current_screen();
15
  ?>
16
  <?php if ( defined( 'CODE_SNIPPETS_SAFE_MODE' ) && CODE_SNIPPETS_SAFE_MODE ) : ?>
33
 
34
  <div class="wrap">
35
  <?php screen_icon(); ?>
36
+ <h2><?php esc_html_e('Snippets', 'code-snippets'); ?>
37
  <?php if ( current_user_can( $screen->is_network ? 'install_network_snippets' : 'install_snippets' ) ) { ?>
38
  <a href="<?php echo $this->admin_single_url; ?>" class="add-new-h2"><?php echo esc_html_x('Add New', 'snippet', 'code-snippets'); ?></a>
39
  <?php }
40
+ $this->list_table->search_notice(); ?></h2>
 
41
 
42
+ <?php $this->list_table->views(); ?>
43
 
44
  <form method="get" action="">
45
+ <?php
46
+ $this->list_table->required_form_fields( 'search_box' );
47
+ $this->list_table->search_box( __( 'Search Installed Snippets', 'code-snippets' ), 'search_id' );
48
+ ?>
49
  </form>
50
  <form method="post" action="">
51
+ <?php $this->list_table->required_form_fields(); ?>
52
+ <?php $this->list_table->display(); ?>
53
  </form>
54
+
55
+ <?php do_action( 'code_snippets_admin_manage' ); ?>
56
+
57
  </div>
includes/admin/single.php CHANGED
@@ -1,21 +1,29 @@
1
  <?php
 
 
 
 
 
 
 
 
2
  if ( ! class_exists( 'Code_Snippets' ) ) exit;
3
- global $wpdb;
4
 
5
  $table = $this->get_table_name();
6
  $screen = get_current_screen();
7
- $can_edit = current_user_can( $screen->is_network ? 'edit_network_snippets' : 'edit_snippets' );
8
  $can_install = current_user_can( $screen->is_network ? 'install_network_snippets' : 'install_snippets' );
9
 
10
- if ( isset( $_REQUEST['edit'] ) && ! $can_edit )
11
- wp_die( __('Sorry, you&#8217;re not allowed to edit snippets', 'code-snippets') );
12
 
13
- if ( isset( $_REQUEST['edit'] ) )
14
- $edit_id = intval( $_REQUEST['edit'] );
15
  ?>
16
 
17
  <?php if ( isset( $_REQUEST['invalid'] ) && $_REQUEST['invalid'] ) : ?>
18
  <div id="message" class="error fade"><p><?php _e('Please provide a name for the snippet and its code.', 'code-snippets'); ?></p></div>
 
 
 
 
19
  <?php elseif ( isset( $_REQUEST['updated'] ) && $_REQUEST['updated'] ) : ?>
20
  <div id="message" class="updated fade"><p><?php _e('Snippet <strong>updated</strong>.', 'code-snippets'); ?></p></div>
21
  <?php elseif ( isset( $_REQUEST['added'] ) && $_REQUEST['added'] ) : ?>
@@ -25,78 +33,60 @@ if ( isset( $_REQUEST['edit'] ) )
25
  <div class="wrap">
26
  <?php screen_icon(); ?>
27
  <h2><?php
28
- if ( isset( $edit_id ) ) {
29
  esc_html_e('Edit Snippet', 'code-snippets');
30
 
31
  if ( $can_install )
32
  printf( ' <a href="%1$s" class="add-new-h2">%2$s</a>',
33
  $this->admin_single_url,
34
- esc_html('Add New', 'code-snippets')
35
  );
36
  } else {
37
- _e('Add New Snippet', 'code-snippets');
38
  }
39
  ?></h2>
40
 
41
  <form method="post" action="" style="margin-top: 10px;">
42
- <?php
43
- if ( isset( $edit_id ) ) {
44
- $snippet = $wpdb->get_row( "SELECT * FROM $table WHERE id = $edit_id" );
45
  printf ( '<input type="hidden" name="snippet_id" value="%d" />', $snippet->id );
46
- } else {
47
- // define a empty object (or one with default values)
48
- $snippet = new stdClass();
49
- $snippet->name = '';
50
- $snippet->description = '';
51
- $snippet->code = '';
52
- }
53
  ?>
54
  <div id="titlediv">
55
  <div id="titlewrap">
56
- <label for="title" style="display: none;"><?php esc_html_e('Name (short title)', 'code-snippets'); ?></label>
57
- <input id="title" type="text" autocomplete="off" size="30" maxlength="64" name="snippet_name" value="<?php echo stripslashes( $snippet->name ); ?>" placeholder="<?php _e('Name (short title)', 'code-snippets'); ?>" required="required" />
58
  </div>
59
  </div>
60
 
61
  <label for="snippet_code">
62
- <h3><?php esc_html_e('Code', 'code-snippets'); ?>
63
- <span style="float: right; font-weight: normal;"><?php _e('Enter or paste the snippet code without the <code>&lt;?php</code> and <code>?&gt;</code> tags.', 'code-snippets'); ?></span></h3>
64
  </label>
65
 
66
- <textarea id="snippet_code" name="snippet_code" rows="20" spellcheck="false" style="font-family: monospace; width:100%;"><?php echo stripslashes( $snippet->code ); ?></textarea>
67
 
68
- <label for="snippet_description">
69
- <h3><?php esc_html_e('Description', 'code-snippets'); ?>
70
- <span style="font-weight: normal;"><?php esc_html_e('(Optional)', 'code-snippets'); ?></span></h3>
71
- </label>
72
-
73
- <?php
74
- wp_editor(
75
- htmlspecialchars_decode( stripslashes( $snippet->description ) ),
76
- 'description',
77
- array(
78
- 'textarea_name' => 'snippet_description',
79
- 'textarea_rows' => 10,
80
- // 'media_buttons' => false,
81
- )
82
- );
83
- ?>
84
 
85
  <p class="submit">
86
- <input type="submit" name="save_snippet" class="button-primary" value="<?php _e('Save', 'code-snippets'); ?>" />
87
- <a href="<?php echo $this->admin_manage_url; ?>" class="button"><?php _e('Cancel', 'code-snippets'); ?></a>
 
 
 
 
 
 
88
  </p>
 
89
  </form>
90
  </div>
91
  <script type="text/javascript">
92
- var editor = CodeMirror.fromTextArea(document.getElementById("snippet_code"), {
93
- lineNumbers: true,
94
- matchBrackets: true,
95
- lineWrapping: true,
96
- mode: "application/x-httpd-php-open",
97
- indentUnit: 4,
98
- indentWithTabs: true,
99
- enterMode: "keep",
100
- tabMode: "shift"
101
- });
102
  </script>
1
  <?php
2
+
3
+ /**
4
+ * HTML code for the Add New/Edit Snippet page
5
+ *
6
+ * @package Code Snippets
7
+ * @subpackage Administration
8
+ */
9
+
10
  if ( ! class_exists( 'Code_Snippets' ) ) exit;
 
11
 
12
  $table = $this->get_table_name();
13
  $screen = get_current_screen();
 
14
  $can_install = current_user_can( $screen->is_network ? 'install_network_snippets' : 'install_snippets' );
15
 
16
+ $edit_id = ( isset( $_REQUEST['edit'] ) ? intval( $_REQUEST['edit'] ) : 0 );
17
+ $snippet = $this->get_snippet( $edit_id );
18
 
 
 
19
  ?>
20
 
21
  <?php if ( isset( $_REQUEST['invalid'] ) && $_REQUEST['invalid'] ) : ?>
22
  <div id="message" class="error fade"><p><?php _e('Please provide a name for the snippet and its code.', 'code-snippets'); ?></p></div>
23
+ <?php elseif ( isset( $_REQUEST['activated'], $_REQUEST['updated'] ) && $_REQUEST['activated'] && $_REQUEST['updated'] ) : ?>
24
+ <div id="message" class="updated fade"><p><?php _e('Snippet <strong>updated</strong> and <strong>activated</strong>.', 'code-snippets'); ?></p></div>
25
+ <?php elseif ( isset( $_REQUEST['activated'], $_REQUEST['added'] ) && $_REQUEST['activated'] && $_REQUEST['added'] ) : ?>
26
+ <div id="message" class="updated fade"><p><?php _e('Snippet <strong>added</strong> and <strong>activated</strong>.', 'code-snippets'); ?></p></div>
27
  <?php elseif ( isset( $_REQUEST['updated'] ) && $_REQUEST['updated'] ) : ?>
28
  <div id="message" class="updated fade"><p><?php _e('Snippet <strong>updated</strong>.', 'code-snippets'); ?></p></div>
29
  <?php elseif ( isset( $_REQUEST['added'] ) && $_REQUEST['added'] ) : ?>
33
  <div class="wrap">
34
  <?php screen_icon(); ?>
35
  <h2><?php
36
+ if ( $edit_id ) {
37
  esc_html_e('Edit Snippet', 'code-snippets');
38
 
39
  if ( $can_install )
40
  printf( ' <a href="%1$s" class="add-new-h2">%2$s</a>',
41
  $this->admin_single_url,
42
+ esc_html_x('Add New', 'snippet', 'code-snippets')
43
  );
44
  } else {
45
+ esc_html_e('Add New Snippet', 'code-snippets');
46
  }
47
  ?></h2>
48
 
49
  <form method="post" action="" style="margin-top: 10px;">
50
+ <?php if ( intval( $snippet->id ) > 0 )
 
 
51
  printf ( '<input type="hidden" name="snippet_id" value="%d" />', $snippet->id );
 
 
 
 
 
 
 
52
  ?>
53
  <div id="titlediv">
54
  <div id="titlewrap">
55
+ <label for="title" style="display: none;"><?php _e('Name (short title)', 'code-snippets'); ?></label>
56
+ <input id="title" type="text" autocomplete="off" name="snippet_name" value="<?php echo esc_html( $snippet->name ); ?>" placeholder="<?php _e('Name (short title)', 'code-snippets'); ?>" required="required" />
57
  </div>
58
  </div>
59
 
60
  <label for="snippet_code">
61
+ <h3><?php _e('Code', 'code-snippets'); ?></h3>
 
62
  </label>
63
 
64
+ <textarea id="snippet_code" name="snippet_code" rows="20" spellcheck="false" style="font-family: monospace; width:100%;"><?php echo $snippet->code; ?></textarea>
65
 
66
+ <?php do_action( 'code_snippets_admin_single', $snippet ); ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
  <p class="submit">
69
+ <?php
70
+ submit_button( null, 'primary', 'save_snippet', false );
71
+
72
+ if ( ! $snippet->active ) {
73
+ echo '&nbsp;&nbsp;&nbsp;';
74
+ submit_button( 'Save Changes &amp; Activate', 'secondary', 'save_snippet_activate', false );
75
+ }
76
+ ?>
77
  </p>
78
+
79
  </form>
80
  </div>
81
  <script type="text/javascript">
82
+ var editor = CodeMirror.fromTextArea(document.getElementById("snippet_code"), {
83
+ lineNumbers: true,
84
+ matchBrackets: true,
85
+ lineWrapping: true,
86
+ mode: "text/x-php",
87
+ indentUnit: 4,
88
+ indentWithTabs: true,
89
+ enterMode: "keep",
90
+ tabMode: "shift"
91
+ });
92
  </script>
includes/class-list-table.php CHANGED
@@ -13,13 +13,18 @@ if ( ! class_exists( 'WP_List_Table' ) ) {
13
  */
14
  class Code_Snippets_List_Table extends WP_List_Table {
15
 
 
 
 
 
 
16
  function __construct() {
17
  global $status, $page, $code_snippets;
18
 
19
  $screen = get_current_screen();
20
 
21
  $status = 'all';
22
- if ( isset( $_REQUEST['status'] ) && in_array( $_REQUEST['status'], array( 'active', 'inactive', 'recently_activated', 'search' ) ) )
23
  $status = $_REQUEST['status'];
24
 
25
  if ( isset( $_REQUEST['s'] ) )
@@ -33,9 +38,10 @@ class Code_Snippets_List_Table extends WP_List_Table {
33
  'option' => 'snippets_per_page'
34
  ) );
35
 
36
- add_filter( "get_user_option_manage{$screen->id}columnshidden", array( $this, 'get_default_hidden_columns' ) );
37
- add_filter( 'set-screen-option', array( $this, 'set_screen_option' ), 10, 3 );
38
- add_action( "admin_print_scripts-$code_snippets->admin_manage", array( $this, 'load_table_style' ) );
 
39
 
40
  parent::__construct( array(
41
  'singular' => 'snippet',
@@ -44,16 +50,6 @@ class Code_Snippets_List_Table extends WP_List_Table {
44
  ) );
45
  }
46
 
47
- /**
48
- * Handles saving the user's screen option preference
49
- *
50
- * @since Code Snippets 1.5
51
- * @access private
52
- */
53
- function set_screen_option( $status, $option, $value ) {
54
- if ( 'snippets_per_page' === $option ) return $value;
55
- }
56
-
57
  /**
58
  * Enqueue the table stylesheet
59
  *
@@ -64,39 +60,69 @@ class Code_Snippets_List_Table extends WP_List_Table {
64
  *
65
  * @return void
66
  */
67
- function load_table_style() {
68
  global $code_snippets;
69
- wp_enqueue_style(
70
- 'snippets-table',
71
- plugins_url( 'assets/table.css', $code_snippets->file ),
72
- false,
73
- $code_snippets->version
74
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  }
76
 
77
- function column_default( $item, $column_name ) {
 
 
 
 
 
 
 
 
 
 
 
78
  switch( $column_name ) {
79
  case 'id':
80
- return intval( $item[ $column_name ] );
81
  case 'description':
82
- return stripslashes( html_entity_decode( $item[ $column_name ] ) );
 
 
 
83
  default:
84
- return print_r( $item, true ); // Show the whole array for troubleshooting purposes
85
  }
86
  }
87
 
88
- function column_name( $item ) {
89
  global $code_snippets;
90
  $screen = get_current_screen();
91
  $actions = array(); // Build row actions
92
 
93
- if ( $item['active'] ) {
94
  $actions['deactivate'] = sprintf(
95
  '<a href="%1$s">%2$s</a>',
96
  add_query_arg( array(
97
  'page' => $_REQUEST['page'],
98
  'action' => 'deactivate',
99
- 'id' => $item['id']
100
  ) ),
101
  $screen->is_network ? __('Network Deactivate', 'code-snippets') : __('Deactivate', 'code-snippets')
102
  );
@@ -106,7 +132,7 @@ class Code_Snippets_List_Table extends WP_List_Table {
106
  add_query_arg( array(
107
  'page' => $_REQUEST['page'],
108
  'action' => 'activate',
109
- 'id' => $item['id']
110
  ) ),
111
  $screen->is_network ? __('Network Activate', 'code-snippets') : __('Activate', 'code-snippets')
112
  );
@@ -115,14 +141,14 @@ class Code_Snippets_List_Table extends WP_List_Table {
115
  $actions['edit'] = sprintf(
116
  '<a href="%s&edit=%s">Edit</a>',
117
  $code_snippets->admin_single_url,
118
- $item['id']
119
  );
120
  $actions['export'] = sprintf(
121
  '<a href="%s">Export</a>',
122
  add_query_arg( array(
123
  'page' => $_REQUEST['page'],
124
  'action' => 'export',
125
- 'id' => $item['id']
126
  ) )
127
  );
128
  $actions['delete'] = sprintf(
@@ -130,38 +156,47 @@ class Code_Snippets_List_Table extends WP_List_Table {
130
  add_query_arg( array(
131
  'page' => $_REQUEST['page'],
132
  'action' => 'delete',
133
- 'id' => $item['id']
134
  ) ),
135
- esc_js( 'return confirm( "You are about to permanently delete the selected item.
136
- \'Cancel\' to stop, \'OK\' to delete.");' )
 
 
 
137
  );
138
 
139
  // Return the name contents
140
- return '<strong>' . stripslashes( $item['name'] ) . '</strong>' . $this->row_actions( $actions, true );
 
 
 
 
141
  }
142
 
143
- function column_cb( $item ) {
144
- return sprintf(
145
- '<input type="checkbox" name="%1$s[]" value="%2$s" />',
146
- /*$1%s*/ $this->_args['singular'], // Let's simply repurpose the table's singular label ("snippet")
147
- /*$2%s*/ $item['id'] // The value of the checkbox should be the snippet's id
148
- );
149
  }
150
 
151
  function get_columns() {
152
- return array(
153
  'cb' => '<input type="checkbox" />',
154
  'name' => __('Name', 'code-snippets'),
155
  'id' => __('ID', 'code-snippets'),
156
  'description' => __('Description', 'code-snippets'),
157
  );
 
158
  }
159
 
160
  function get_sortable_columns() {
161
- return array(
162
  'id' => array( 'id', true ),
163
  'name' => array( 'name', false ),
164
  );
 
165
  }
166
 
167
  function get_default_hidden_columns( $result ) {
@@ -180,11 +215,12 @@ class Code_Snippets_List_Table extends WP_List_Table {
180
  'delete-selected' => __('Delete', 'code-snippets'),
181
  'export-php-selected' => __('Export to PHP', 'code-snippets'),
182
  );
183
- return $actions;
184
  }
185
 
186
  function get_table_classes() {
187
- return array( 'widefat', $this->_args['plural'] );
 
188
  }
189
 
190
  function get_views() {
@@ -210,39 +246,69 @@ class Code_Snippets_List_Table extends WP_List_Table {
210
  break;
211
  }
212
 
213
- if ( 'search' != $type ) {
214
- $status_links[$type] = sprintf( '<a href="%s"%s>%s</a>',
215
- add_query_arg('status', $type, '?page=' . $_REQUEST['page'] ),
216
- ( $type == $status ) ? ' class="current"' : '',
217
- sprintf( $text, number_format_i18n( $count ) )
218
- );
219
- }
220
  }
221
 
222
- return $status_links;
223
  }
224
 
225
  function extra_tablenav( $which ) {
226
- global $status;
227
 
228
- if ( ! in_array($status, array('recently_activated') ) )
229
- return;
230
 
231
- echo '<div class="alignleft actions">';
 
 
 
 
 
 
 
 
 
232
 
233
- $screen = get_current_screen();
234
 
235
- if ( 'recently_activated' == $status )
236
  submit_button( __('Clear List', 'code-snippets'), 'secondary', 'clear-recent-list', false );
237
 
 
 
238
  echo '</div>';
239
  }
240
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
  function current_action() {
242
  if ( isset( $_POST['clear-recent-list'] ) )
243
- return 'clear-recent-list';
244
-
245
- return parent::current_action();
 
246
  }
247
 
248
  /**
@@ -257,8 +323,9 @@ class Code_Snippets_List_Table extends WP_List_Table {
257
  */
258
  function process_bulk_actions() {
259
  global $code_snippets;
260
- if ( ! isset( $_POST[ $this->_args['singular'] ] ) ) return;
261
- $ids = $_POST[ $this->_args['singular'] ];
 
262
 
263
  $_SERVER['REQUEST_URI'] = remove_query_arg( array( 'activate', 'deactivate', 'delete', 'activate-multi', 'deactivate-multi', 'delete-multi' ) );
264
 
@@ -299,32 +366,43 @@ class Code_Snippets_List_Table extends WP_List_Table {
299
  }
300
  }
301
 
 
 
 
302
  function no_items() {
303
  global $code_snippets;
304
  printf( __('You do not appear to have any snippets available at this time. <a href="%s">Add New&rarr;</a>', 'code-snippets'), $code_snippets->admin_single_url );
305
  }
306
 
 
 
 
 
 
307
  function prepare_items() {
308
 
309
- global $wpdb, $code_snippets, $status, $snippets, $totals, $page, $orderby, $order, $s;
310
 
311
  wp_reset_vars( array( 'orderby', 'order', 's' ) );
312
 
313
  $screen = get_current_screen();
314
  $user = get_current_user_id();
315
- $table = $code_snippets->get_table_name();
316
 
317
  // first, lets process the bulk actions
318
  $this->process_bulk_actions();
319
 
320
  $snippets = array(
321
- 'all' => $wpdb->get_results( "SELECT * FROM $table", ARRAY_A ),
322
- 'search' => array(),
323
  'active' => array(),
324
  'inactive' => array(),
325
  'recently_activated' => array(),
326
  );
327
 
 
 
 
 
 
328
  if ( $screen->is_network )
329
  $recently_activated = get_site_option( 'recently_activated_snippets', array() );
330
  else
@@ -342,32 +420,27 @@ class Code_Snippets_List_Table extends WP_List_Table {
342
 
343
  foreach ( (array) $snippets['all'] as $snippet ) {
344
  // Filter into individual sections
345
- if ( $snippet['active'] ) {
346
  $snippets['active'][] = $snippet;
347
  } else {
348
- if ( isset( $recently_activated[ $snippet['id'] ] ) ) // Was the snippet recently activated?
349
  $snippets['recently_activated'][] = $snippet;
350
  $snippets['inactive'][] = $snippet;
351
  }
352
  }
353
 
354
- if ( $s ) {
355
- $status = 'search';
356
- $snippets['search'] = array_filter( $snippets['all'], array( &$this, '_search_callback' ) );
357
- }
358
-
359
  $totals = array();
360
  foreach ( $snippets as $type => $list )
361
  $totals[ $type ] = count( $list );
362
 
363
- if ( empty( $snippets[ $status ] ) && !in_array( $status, array( 'all', 'search' ) ) )
364
  $status = 'all';
365
 
366
  $data = $snippets[ $status ];
367
 
368
  /**
369
  * First, lets decide how many records per page to show
370
- * by getting the user's setting in the Screen Opions
371
  * panel.
372
  */
373
  $sort_by = $screen->get_option( 'per_page', 'option' );
@@ -395,13 +468,13 @@ class Code_Snippets_List_Table extends WP_List_Table {
395
  $order = ( ! empty( $_REQUEST['order'] ) ) ? $_REQUEST['order'] : 'asc';
396
 
397
  // Determine sort order
398
- if ( $orderby === 'id' )
399
- $result = $a[$orderby] - $b[$orderby]; // get the result for numerical data
400
  else
401
- $result = strcmp( $a[$orderby], $b[$orderby] ); // get the result for string data
402
 
403
  // Send final sort direction to usort
404
- return ( $order === 'asc' ) ? $result : -$result;
405
  }
406
 
407
  usort($data, 'usort_reorder');
@@ -432,7 +505,6 @@ class Code_Snippets_List_Table extends WP_List_Table {
432
  */
433
  $this->items = $data;
434
 
435
-
436
  /**
437
  * We also have to register our pagination options & calculations.
438
  */
@@ -443,27 +515,53 @@ class Code_Snippets_List_Table extends WP_List_Table {
443
  ) );
444
  }
445
 
446
- function _search_callback( $item ) {
447
  static $term;
448
  if ( is_null( $term ) )
449
  $term = stripslashes( $_REQUEST['s'] );
450
 
451
- foreach ( $item as $value )
452
- if ( stripos( $value, $term ) !== false )
453
- return true;
 
 
 
 
 
 
 
 
454
 
455
  return false;
456
  }
457
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
458
  /**
459
  * Generates content for a single row of the table
460
  */
461
- function single_row( $item ) {
462
  static $row_class = '';
463
- $row_class = ( $item['active'] ? 'active' : 'inactive' );
464
 
465
  echo '<tr class="' . $row_class . '">';
466
- echo $this->single_row_columns( $item );
467
  echo '</tr>';
468
  }
469
  }
13
  */
14
  class Code_Snippets_List_Table extends WP_List_Table {
15
 
16
+ /**
17
+ * The constructor function for our class
18
+ *
19
+ * Adds hooks, initializes variables, setups class
20
+ */
21
  function __construct() {
22
  global $status, $page, $code_snippets;
23
 
24
  $screen = get_current_screen();
25
 
26
  $status = 'all';
27
+ if ( isset( $_REQUEST['status'] ) && in_array( $_REQUEST['status'], array( 'active', 'inactive', 'recently_activated' ) ) )
28
  $status = $_REQUEST['status'];
29
 
30
  if ( isset( $_REQUEST['s'] ) )
38
  'option' => 'snippets_per_page'
39
  ) );
40
 
41
+ add_filter( "get_user_option_manage{$screen->id}columnshidden", array( $this, 'get_default_hidden_columns' ), 15 );
42
+ add_action( 'admin_enqueue_scripts', array( $this, 'load_table_style' ) );
43
+
44
+ $_SERVER['REQUEST_URI'] = remove_query_arg( array( 'activate', 'activate-multi', 'deactivate', 'deactivate-multi', 'delete', 'delete-multi' ) );
45
 
46
  parent::__construct( array(
47
  'singular' => 'snippet',
50
  ) );
51
  }
52
 
 
 
 
 
 
 
 
 
 
 
53
  /**
54
  * Enqueue the table stylesheet
55
  *
60
  *
61
  * @return void
62
  */
63
+ function load_table_style( $hook ) {
64
  global $code_snippets;
65
+
66
+ if ( $hook !== $code_snippets->admin_manage )
67
+ return;
68
+
69
+ if ( 'mp6' === get_user_option( 'admin_color' ) ) {
70
+
71
+ wp_enqueue_style(
72
+ 'snippets-table',
73
+ plugins_url( 'assets/table.mp6.css', $code_snippets->file ),
74
+ false,
75
+ $code_snippets->version
76
+ );
77
+
78
+ } else {
79
+
80
+ wp_enqueue_style(
81
+ 'snippets-table',
82
+ plugins_url( 'assets/table.css', $code_snippets->file ),
83
+ false,
84
+ $code_snippets->version
85
+ );
86
+ }
87
  }
88
 
89
+ function format_description( $desc ) {
90
+ $desc = wptexturize( $desc );
91
+ $desc = convert_smilies( $desc );
92
+ $desc = convert_chars( $desc );
93
+ $desc = wpautop( $desc );
94
+ $desc = shortcode_unautop( $desc );
95
+ $desc = capital_P_dangit( $desc );
96
+ return $desc;
97
+ }
98
+
99
+ function column_default( $snippet, $column_name ) {
100
+
101
  switch( $column_name ) {
102
  case 'id':
103
+ return $snippet->id;
104
  case 'description':
105
+ if ( ! empty( $snippet->description ) )
106
+ return $this->format_description( $snippet->description );
107
+ else
108
+ return '&#8212;';
109
  default:
110
+ return do_action( "code_snippets_list_table_column_{$column_name}", $snippet );
111
  }
112
  }
113
 
114
+ function column_name( $snippet ) {
115
  global $code_snippets;
116
  $screen = get_current_screen();
117
  $actions = array(); // Build row actions
118
 
119
+ if ( $snippet->active ) {
120
  $actions['deactivate'] = sprintf(
121
  '<a href="%1$s">%2$s</a>',
122
  add_query_arg( array(
123
  'page' => $_REQUEST['page'],
124
  'action' => 'deactivate',
125
+ 'id' => $snippet->id
126
  ) ),
127
  $screen->is_network ? __('Network Deactivate', 'code-snippets') : __('Deactivate', 'code-snippets')
128
  );
132
  add_query_arg( array(
133
  'page' => $_REQUEST['page'],
134
  'action' => 'activate',
135
+ 'id' => $snippet->id
136
  ) ),
137
  $screen->is_network ? __('Network Activate', 'code-snippets') : __('Activate', 'code-snippets')
138
  );
141
  $actions['edit'] = sprintf(
142
  '<a href="%s&edit=%s">Edit</a>',
143
  $code_snippets->admin_single_url,
144
+ $snippet->id
145
  );
146
  $actions['export'] = sprintf(
147
  '<a href="%s">Export</a>',
148
  add_query_arg( array(
149
  'page' => $_REQUEST['page'],
150
  'action' => 'export',
151
+ 'id' => $snippet->id
152
  ) )
153
  );
154
  $actions['delete'] = sprintf(
156
  add_query_arg( array(
157
  'page' => $_REQUEST['page'],
158
  'action' => 'delete',
159
+ 'id' => $snippet->id
160
  ) ),
161
+ esc_js( sprintf(
162
+ 'return confirm("%s");',
163
+ __("You are about to permanently delete the selected item.
164
+ 'Cancel' to stop, 'OK' to delete.", 'code-snippets')
165
+ ) )
166
  );
167
 
168
  // Return the name contents
169
+ return apply_filters(
170
+ 'code_snippets_list_table_column_name',
171
+ '<strong>' . stripslashes( $snippet->name ) . '</strong>' . $this->row_actions( $actions, true ),
172
+ $snippet
173
+ );
174
  }
175
 
176
+ function column_cb( $snippet ) {
177
+ return apply_filters(
178
+ 'code_snippets_list_table_column_cb',
179
+ sprintf( '<input type="checkbox" name="ids[]" value="%s" />', $snippet->id ),
180
+ $snippet
181
+ );
182
  }
183
 
184
  function get_columns() {
185
+ $columns = array(
186
  'cb' => '<input type="checkbox" />',
187
  'name' => __('Name', 'code-snippets'),
188
  'id' => __('ID', 'code-snippets'),
189
  'description' => __('Description', 'code-snippets'),
190
  );
191
+ return apply_filters( 'code_snippets_list_table_columns', $columns );
192
  }
193
 
194
  function get_sortable_columns() {
195
+ $sortable_columns = array(
196
  'id' => array( 'id', true ),
197
  'name' => array( 'name', false ),
198
  );
199
+ return apply_filters( 'code_snippets_list_table_sortable_columns', $sortable_columns );
200
  }
201
 
202
  function get_default_hidden_columns( $result ) {
215
  'delete-selected' => __('Delete', 'code-snippets'),
216
  'export-php-selected' => __('Export to PHP', 'code-snippets'),
217
  );
218
+ return apply_filters( 'code_snippets_bulk_actions', $actions );
219
  }
220
 
221
  function get_table_classes() {
222
+ $classes = array( 'widefat', $this->_args['plural'] );
223
+ return apply_filters( 'code_snippets_table_classes', $classes );
224
  }
225
 
226
  function get_views() {
246
  break;
247
  }
248
 
249
+ $status_links[$type] = sprintf( '<a href="%s"%s>%s</a>',
250
+ add_query_arg( 'status', $type ),
251
+ ( $type === $status ) ? ' class="current"' : '',
252
+ sprintf( $text, number_format_i18n( $count ) )
253
+ );
254
+
 
255
  }
256
 
257
+ return apply_filters( 'code_snippets_list_table_views', $status_links );
258
  }
259
 
260
  function extra_tablenav( $which ) {
261
+ global $status, $code_snippets;
262
 
263
+ $screen = get_current_screen();
 
264
 
265
+ if ( 'top' === $which && has_action( 'code_snippets_list_table_filter_controls' ) ) {
266
+ ?>
267
+ <div class="alignleft actions">
268
+ <?php
269
+ do_action( 'code_snippets_list_table_filter_controls' );
270
+ submit_button( __('Filter', 'code-snippets'), 'button', false, false );
271
+ ?>
272
+ </div>
273
+ <?php
274
+ }
275
 
276
+ echo '<div class="alignleft actions">';
277
 
278
+ if ( 'recently_activated' === $status )
279
  submit_button( __('Clear List', 'code-snippets'), 'secondary', 'clear-recent-list', false );
280
 
281
+ do_action( 'code_snippets_list_table_actions', $which );
282
+
283
  echo '</div>';
284
  }
285
 
286
+ function required_form_fields( $context = 'main' ) {
287
+
288
+ $vars = apply_filters( 'code_snippets_list_table_required_form_fields', array( 'page', 's', 'status', 'paged' ), $context );
289
+
290
+ if ( 'search_box' === $context ) {
291
+ // remove the 's' var if we're doing this for the search box
292
+ $vars = array_diff( $vars, array( 's' ) );
293
+ }
294
+
295
+ foreach ( $vars as $var ) {
296
+ if ( ! empty( $_REQUEST[ $var ] ) ) {
297
+ printf ( '<input type="hidden" name="%s" value="%s" />', $var, $_REQUEST[ $var ] );
298
+ print "\n";
299
+ }
300
+ }
301
+
302
+ do_action( 'code_snippets_list_table_print_required_form_fields', $context );
303
+ }
304
+
305
+
306
  function current_action() {
307
  if ( isset( $_POST['clear-recent-list'] ) )
308
+ $action = 'clear-recent-list';
309
+ else
310
+ $action = parent::current_action();
311
+ return apply_filters( 'code_snippets_list_table_current_action', $action );
312
  }
313
 
314
  /**
323
  */
324
  function process_bulk_actions() {
325
  global $code_snippets;
326
+
327
+ if ( ! isset( $_POST['ids'] ) ) return;
328
+ $ids = $_POST['ids'];
329
 
330
  $_SERVER['REQUEST_URI'] = remove_query_arg( array( 'activate', 'deactivate', 'delete', 'activate-multi', 'deactivate-multi', 'delete-multi' ) );
331
 
366
  }
367
  }
368
 
369
+ /**
370
+ * Message to display if no snippets are found
371
+ */
372
  function no_items() {
373
  global $code_snippets;
374
  printf( __('You do not appear to have any snippets available at this time. <a href="%s">Add New&rarr;</a>', 'code-snippets'), $code_snippets->admin_single_url );
375
  }
376
 
377
+ /**
378
+ * Prepares the items to later display in the table
379
+ *
380
+ * Should run before any headers are sent
381
+ */
382
  function prepare_items() {
383
 
384
+ global $code_snippets, $status, $snippets, $totals, $page, $orderby, $order, $s;
385
 
386
  wp_reset_vars( array( 'orderby', 'order', 's' ) );
387
 
388
  $screen = get_current_screen();
389
  $user = get_current_user_id();
 
390
 
391
  // first, lets process the bulk actions
392
  $this->process_bulk_actions();
393
 
394
  $snippets = array(
395
+ 'all' => apply_filters( 'code_snippets_list_table_get_snippets', $code_snippets->get_snippets() ),
 
396
  'active' => array(),
397
  'inactive' => array(),
398
  'recently_activated' => array(),
399
  );
400
 
401
+ // filter snippets based on search query
402
+ if ( $s ) {
403
+ $snippets['all'] = array_filter( $snippets[ 'all' ], array( &$this, '_search_callback' ) );
404
+ }
405
+
406
  if ( $screen->is_network )
407
  $recently_activated = get_site_option( 'recently_activated_snippets', array() );
408
  else
420
 
421
  foreach ( (array) $snippets['all'] as $snippet ) {
422
  // Filter into individual sections
423
+ if ( $snippet->active ) {
424
  $snippets['active'][] = $snippet;
425
  } else {
426
+ if ( isset( $recently_activated[ $snippet->id ] ) ) // Was the snippet recently activated?
427
  $snippets['recently_activated'][] = $snippet;
428
  $snippets['inactive'][] = $snippet;
429
  }
430
  }
431
 
 
 
 
 
 
432
  $totals = array();
433
  foreach ( $snippets as $type => $list )
434
  $totals[ $type ] = count( $list );
435
 
436
+ if ( empty( $snippets[ $status ] ) )
437
  $status = 'all';
438
 
439
  $data = $snippets[ $status ];
440
 
441
  /**
442
  * First, lets decide how many records per page to show
443
+ * by getting the user's setting in the Screen Options
444
  * panel.
445
  */
446
  $sort_by = $screen->get_option( 'per_page', 'option' );
468
  $order = ( ! empty( $_REQUEST['order'] ) ) ? $_REQUEST['order'] : 'asc';
469
 
470
  // Determine sort order
471
+ if ( 'id' === $orderby )
472
+ $result = $a->$orderby - $b->$orderby; // get the result for numerical data
473
  else
474
+ $result = strcmp( $a->$orderby, $b->$orderby ); // get the result for string data
475
 
476
  // Send final sort direction to usort
477
+ return ( 'asc' === $order ) ? $result : -$result;
478
  }
479
 
480
  usort($data, 'usort_reorder');
505
  */
506
  $this->items = $data;
507
 
 
508
  /**
509
  * We also have to register our pagination options & calculations.
510
  */
515
  ) );
516
  }
517
 
518
+ function _search_callback( $snippet ) {
519
  static $term;
520
  if ( is_null( $term ) )
521
  $term = stripslashes( $_REQUEST['s'] );
522
 
523
+ foreach ( $snippet as $value ) {
524
+
525
+ if ( is_string( $value ) ) {
526
+ if ( false !== stripos( $value, $term ) )
527
+ return true;
528
+ }
529
+ elseif ( is_array( $value ) ) {
530
+ if ( false !== in_array( $term, $value ) )
531
+ return true;
532
+ }
533
+ }
534
 
535
  return false;
536
  }
537
 
538
+ function search_notice() {
539
+ if ( ! empty( $_REQUEST['s'] ) || apply_filters( 'code_snippets_list_table_search_notice', '' ) ) {
540
+
541
+ echo '<span class="subtitle">' . __('Search results', 'code-snippets');
542
+
543
+ if ( ! empty ( $_REQUEST['s'] ) )
544
+ echo sprintf ( __(' for &#8220;%s&#8221;'), esc_html( $_REQUEST['s'] ) );
545
+
546
+ echo apply_filters( 'code_snippets_list_table_search_notice', '' );
547
+ echo '</span>';
548
+
549
+ printf (
550
+ '&nbsp;<a class="button" href="%s">' . __('Clear Filters', 'code-snippets') . '</a>',
551
+ remove_query_arg( apply_filters( 'code_snippets_list_table_required_form_fields', array( 's' ), 'clear_filters' ) )
552
+ );
553
+ }
554
+ }
555
+
556
  /**
557
  * Generates content for a single row of the table
558
  */
559
+ function single_row( $snippet ) {
560
  static $row_class = '';
561
+ $row_class = ( $snippet->active ? 'active' : 'inactive' );
562
 
563
  echo '<tr class="' . $row_class . '">';
564
+ echo $this->single_row_columns( $snippet );
565
  echo '</tr>';
566
  }
567
  }
includes/export.php CHANGED
@@ -1,97 +1,146 @@
1
- <?php
2
-
3
- /**
4
- * This file handles the export functions
5
- *
6
- * It's better to call the $code_snippets->export()
7
- * and $code_snippets->export_php() methods then
8
- * directly use those in this file
9
- *
10
- * @package Code Snippets
11
- * @subpackage Export
12
- */
13
-
14
- if ( ! function_exists( 'code_snippets_export' ) ) :
15
-
16
- /**
17
- * Exports seleted snippets to a XML or PHP file.
18
- *
19
- * @package Code Snippets
20
- * @since Code Snippets 1.3
21
- *
22
- * @param array $ids The IDs of the snippets to export
23
- * @param string $format The format of the export file
24
- * @return void
25
- */
26
- function code_snippets_export( $ids, $format = 'xml' ) {
27
-
28
- global $wpdb, $code_snippets;
29
-
30
- $ids = (array) $ids;
31
-
32
- $table = $code_snippets->get_table_name();
33
-
34
- if ( count( $ids ) < 2 ) {
35
- // If there is only snippet to export, use its name instead of the site name
36
- $entry = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE id=%d", $ids ) );
37
- $sitename = sanitize_key( $entry->name );
38
- } else {
39
- // Otherwise, use the site name as set in Settings > General
40
- $sitename = sanitize_key( get_bloginfo( 'name' ) );
41
- }
42
-
43
- $filename = apply_filters( 'code_snippets_export_filename', "{$sitename}.code-snippets.{$format}", $format, $sitename );
44
-
45
- header( 'Content-Disposition: attachment; filename=' . $filename );
46
-
47
- if ( $format === 'xml' ) {
48
- header( 'Content-Type: text/xml; charset=utf-8' );
49
-
50
- echo '<?xml version="1.0"?>' . "\n";
51
- echo '<snippets sitename="' . $sitename . '">';
52
-
53
- foreach( $ids as $id ) {
54
-
55
- if ( ! intval( $id ) > 0 ) continue; // skip this one if we don't have a valid ID
56
-
57
- $snippet = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE id=%d", $id ) );
58
-
59
- echo "\n\t" . '<snippet>';
60
- echo "\n\t\t" . "<name>$snippet->name</name>";
61
- echo "\n\t\t" . "<description>$snippet->description</description>";
62
- echo "\n\t\t" . "<code>$snippet->code</code>";
63
- echo "\n\t" . '</snippet>';
64
- }
65
-
66
- echo "\n</snippets>";
67
-
68
- } elseif ( $format === 'php' ) {
69
-
70
- echo "<?php\n";
71
-
72
- foreach( $ids as $id ) {
73
-
74
- if ( ! intval( $id ) > 0 ) continue; // skip this one if we don't have a valid ID
75
-
76
- $snippet = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE id=%d", $id ) );
77
- ?>
78
-
79
- /**
80
- * <?php echo htmlspecialchars_decode( stripslashes( $snippet->name ) ) . "\n"; ?>
81
- <?php if ( ! empty( $snippet->description ) ) : ?>
82
- *
83
- * <?php echo htmlspecialchars_decode( stripslashes( $snippet->description ) ) . "\n"; ?>
84
- <?php endif; ?>
85
- */
86
- <?php echo htmlspecialchars_decode( stripslashes( $snippet->code ) ) . "\n"; ?>
87
-
88
- <?php
89
- }
90
-
91
- echo '?>';
92
- }
93
-
94
- exit;
95
- }
96
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  endif; // function exists check
1
+ <?php
2
+
3
+ /**
4
+ * This file handles the export functions
5
+ *
6
+ * It's better to call the $code_snippets->export()
7
+ * and $code_snippets->export_php() methods then
8
+ * directly use those in this file
9
+ *
10
+ * @package Code Snippets
11
+ * @subpackage Export
12
+ */
13
+
14
+ if ( ! function_exists( 'code_snippets_export' ) ) :
15
+
16
+ /**
17
+ * Exports seleted snippets to a XML or PHP file.
18
+ *
19
+ * @package Code Snippets
20
+ * @since Code Snippets 1.3
21
+ *
22
+ * @param array $ids The IDs of the snippets to export
23
+ * @param string $format The format of the export file
24
+ * @return void
25
+ */
26
+ function code_snippets_export( $ids, $format = 'xml' ) {
27
+
28
+ global $wpdb, $code_snippets;
29
+ $ids = (array) $ids;
30
+
31
+ if ( 1 === count( $ids ) ) {
32
+ // If there is only snippet to export, use its name instead of the site name
33
+ $entry = $code_snippets->get_snippet( $ids );
34
+ $sitename = strtolower( $entry->name );
35
+ } else {
36
+ // Otherwise, use the site name as set in Settings > General
37
+ $sitename = strtolower( get_bloginfo( 'name' ) );
38
+ }
39
+
40
+ $filename = sanitize_file_name( apply_filters( 'code_snippets_export_filename', "{$sitename}.code-snippets.{$format}", $format, $sitename ) );
41
+
42
+ /* Apply the file headers */
43
+
44
+ header( 'Content-Disposition: attachment; filename=' . $filename );
45
+
46
+ if ( $format === 'xml' ) {
47
+ header( 'Content-Type: text/xml; charset=' . get_bloginfo('charset') );
48
+
49
+ echo '<?xml version="1.0" encoding="' . get_bloginfo('charset') . "\" ?>\n";
50
+
51
+ ?>
52
+ <!-- This is a code snippets export file generated by the Code Snippets WordPress plugin. -->
53
+ <!-- http://wordpress.org/extend/plugins/code-snippets -->
54
+
55
+ <!-- To import these snippets a WordPress site follow these steps: -->
56
+ <!-- 1. Log in to that site as an administrator. -->
57
+ <!-- 2. Install the Code Snippets plugin using the directions provided at the above link. -->
58
+ <!-- 3. Go to 'Tools: Import' in the WordPress admin panel. -->
59
+ <!-- 4. Click on the "Code Snippets" importer in the list -->
60
+ <!-- 5. Upload this file using the form provided on that page. -->
61
+ <!-- 6. Code Snippets will then import all of the snippets and associated information -->
62
+ <!-- contained in this file into your site. -->
63
+ <!-- 7. You will then have to visit the 'Snippets: Manage' admin menu and activate desired snippets -->
64
+
65
+ <?php
66
+
67
+ // run the generator line through the standard WordPress filter
68
+ $gen = '<!-- generator="Code Snippets/' . $code_snippets->version . '" created="' . date('Y-m-d H:i') . '" -->';
69
+ $type = 'code_snippets_export';
70
+ echo apply_filters( "get_the_generator_$type", $gen, $type );
71
+
72
+ // start the XML section
73
+ echo "\n<snippets>";
74
+
75
+ } elseif ( 'php' === $format ) {
76
+
77
+ echo "<?php\n";
78
+
79
+ }
80
+
81
+ do_action( 'code_snippets_export_file_header', $format, $ids, $filename );
82
+
83
+ /* Loop through the snippets */
84
+
85
+ $table = $code_snippets->get_table_name();
86
+ $exclude = apply_filters( 'code_snippets_exclude_from_export', array( 'id', 'active' ) );
87
+
88
+ foreach( $ids as $id ) {
89
+
90
+ // grab the snippet from the database
91
+ $snippet = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE id = %d", $id ), ARRAY_A );
92
+
93
+ // remove slashes
94
+ $snippet = stripslashes_deep( $snippet );
95
+
96
+ if ( 'xml' === $format ) {
97
+
98
+ echo "\n\t" . '<snippet>';
99
+
100
+ foreach ( $snippet as $field => $value ) {
101
+
102
+ // don't export certain fields
103
+ if ( in_array( $field, $exclude ) ) continue;
104
+
105
+ // output the field and value as indented XML
106
+ if ( $value = apply_filters( "code_snippets_export_$field", $value ) )
107
+ echo "\n\t\t<$field>$value</$field>";
108
+ }
109
+ echo "\n\t" . '</snippet>';
110
+ }
111
+ elseif ( 'php' === $format ) {
112
+
113
+ echo "\n/**\n * {$snippet['name']}\n";
114
+
115
+ if ( ! empty( $snippet['description'] ) ) {
116
+
117
+ // convert description to PHP Doc
118
+ $desc = strip_tags( str_replace( "\n", "\n * ", $snippet['description'] ) );
119
+
120
+ echo " *\n * $desc\n";
121
+ }
122
+
123
+ echo " */\n{$snippet['code']}\n";
124
+ }
125
+ }
126
+
127
+ do_action( 'code_snippets_export_file_snippet', $format, $id, $filename );
128
+
129
+ /* Finish off the file */
130
+
131
+ if ( 'xml' === $format ) {
132
+
133
+ echo "\n</snippets>";
134
+
135
+ } elseif ( 'php' === $format ) {
136
+
137
+ echo '?>';
138
+
139
+ }
140
+
141
+ do_action( 'code_snippets_export_file_footer', $format, $ids, $filename );
142
+
143
+ exit;
144
+ }
145
+
146
  endif; // function exists check
includes/help/import.php CHANGED
@@ -11,7 +11,7 @@ $screen->add_help_tab( array(
11
  'id' => 'import',
12
  'title' => __('Importing', 'code-snippets'),
13
  'content' =>
14
- '<p>' . __('You can load your snippets from a Code Snippets (.xml) import file using this page.', 'code-snippets') .
15
  sprintf( __('Snippets will be added to the database along with your existing snippets. Regardless of whether the snippets were active on the previous site, imported snippets are always inactive until activated using the <a href="%s">Manage Snippets</a> page.</p>', 'code-snippets'), $this->admin_manage_url ) . '</p>'
16
  ) );
17
 
@@ -27,4 +27,4 @@ $screen->set_help_sidebar(
27
  '<p>' . __('<a href="http://wordpress.org/extend/plugins/code-snippets" target="_blank">WordPress Extend</a>', 'code-snippets') . '</p>' .
28
  '<p>' . __('<a href="http://wordpress.org/support/plugin/code-snippets" target="_blank">Support Forums</a>', 'code-snippets') . '</p>' .
29
  '<p>' . __('<a href="http://code-snippets.bungeshea.com/" target="_blank">Project Website</a>', 'code-snippets') . '</p>'
30
- );
11
  'id' => 'import',
12
  'title' => __('Importing', 'code-snippets'),
13
  'content' =>
14
+ '<p>' . __('You can load your snippets from a code snippets (.xml) export file using this page.', 'code-snippets') .
15
  sprintf( __('Snippets will be added to the database along with your existing snippets. Regardless of whether the snippets were active on the previous site, imported snippets are always inactive until activated using the <a href="%s">Manage Snippets</a> page.</p>', 'code-snippets'), $this->admin_manage_url ) . '</p>'
16
  ) );
17
 
27
  '<p>' . __('<a href="http://wordpress.org/extend/plugins/code-snippets" target="_blank">WordPress Extend</a>', 'code-snippets') . '</p>' .
28
  '<p>' . __('<a href="http://wordpress.org/support/plugin/code-snippets" target="_blank">Support Forums</a>', 'code-snippets') . '</p>' .
29
  '<p>' . __('<a href="http://code-snippets.bungeshea.com/" target="_blank">Project Website</a>', 'code-snippets') . '</p>'
30
+ );
includes/help/single.php CHANGED
@@ -24,7 +24,6 @@ $screen->add_help_tab( array(
24
  'title' => __('Adding Snippets', 'code-snippets'),
25
  'content' =>
26
  '<p>' . __('You need to fill out the name and code fields for your snippet to be added. While the description field will add more information about how your snippet works, what is does and where you found it, it is completely optional.', 'code-snippets') . '</p>' .
27
- '<p>' . __('Make sure that you don\'t add the <code>&lt;?php</code>, <code>&lt;?</code> or <code>?&gt;</code> the beginning and end of the code. You can however use these tags in the code to stop and start PHP sections', 'code-snippets') . '</p>' .
28
  '<p>' . __('Please be sure to check that your snippet is valid PHP code and will not produce errors before adding it through this page. While doing so will not become active straight away, it will help to minimise the chance of a faulty snippet becoming active on your site.', 'code-snippets') . '</p>'
29
  ) );
30
 
@@ -33,4 +32,4 @@ $screen->set_help_sidebar(
33
  '<p>' . __('<a href="http://wordpress.org/extend/plugins/code-snippets" target="_blank">WordPress Extend</a>', 'code-snippets') . '</p>' .
34
  '<p>' . __('<a href="http://wordpress.org/support/plugin/code-snippets" target="_blank">Support Forums</a>', 'code-snippets') . '</p>' .
35
  '<p>' . __('<a href="http://code-snippets.bungeshea.com/" target="_blank">Project Website</a>', 'code-snippets') . '</p>'
36
- );
24
  'title' => __('Adding Snippets', 'code-snippets'),
25
  'content' =>
26
  '<p>' . __('You need to fill out the name and code fields for your snippet to be added. While the description field will add more information about how your snippet works, what is does and where you found it, it is completely optional.', 'code-snippets') . '</p>' .
 
27
  '<p>' . __('Please be sure to check that your snippet is valid PHP code and will not produce errors before adding it through this page. While doing so will not become active straight away, it will help to minimise the chance of a faulty snippet becoming active on your site.', 'code-snippets') . '</p>'
28
  ) );
29
 
32
  '<p>' . __('<a href="http://wordpress.org/extend/plugins/code-snippets" target="_blank">WordPress Extend</a>', 'code-snippets') . '</p>' .
33
  '<p>' . __('<a href="http://wordpress.org/support/plugin/code-snippets" target="_blank">Support Forums</a>', 'code-snippets') . '</p>' .
34
  '<p>' . __('<a href="http://code-snippets.bungeshea.com/" target="_blank">Project Website</a>', 'code-snippets') . '</p>'
35
+ );
languages/code-snippets.pot CHANGED
@@ -1,268 +1,291 @@
1
- # Copyright (C) 2012 Code Snippets
2
  # This file is distributed under the same license as the Code Snippets package.
3
  msgid ""
4
  msgstr ""
5
- "Project-Id-Version: Code Snippets 1.6.1\n"
6
- "Report-Msgid-Bugs-To: http://wordpress.org/support/plugin/code-snippets\n"
7
- "POT-Creation-Date: 2012-12-29 09:12:37+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=UTF-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
11
- "PO-Revision-Date: 2012-12-29 08:14+11\n"
12
- "Last-Translator: Shea Bunge <code-snippets@bungeshea.com>\n"
 
13
 
14
- #: code-snippets.php:217
 
 
15
  msgid "Code Snippets"
16
  msgstr ""
17
 
18
- #: code-snippets.php:218
19
  msgid "Import snippets from a <strong>Code Snippets</strong> export file"
20
  msgstr ""
21
 
22
- #: code-snippets.php:537 code-snippets.php:538 code-snippets.php:548
23
- #: code-snippets.php:586 code-snippets.php:587 code-snippets.php:597
24
- #: includes/admin/manage.php:29
25
  msgid "Snippets"
26
  msgstr ""
27
 
28
- #: code-snippets.php:549 code-snippets.php:598
29
- msgid "Manage Snippets"
30
  msgstr ""
31
 
32
- #: code-snippets.php:557 code-snippets.php:606 code-snippets.php:1017
33
- #: includes/admin/single.php:37
 
 
 
 
 
 
 
34
  msgid "Add New Snippet"
35
  msgstr ""
36
 
37
- #: code-snippets.php:558 code-snippets.php:607
38
  msgid "Add New"
39
  msgstr ""
40
 
41
- #: code-snippets.php:637 includes/admin/import.php:23
42
  msgid "Import Snippets"
43
  msgstr ""
44
 
45
- #: code-snippets.php:638
46
  msgid "Import"
47
  msgstr ""
48
 
49
- #: code-snippets.php:1018 includes/admin/single.php:29
50
- msgid "Edit Snippet"
 
51
  msgstr ""
52
 
53
- #: code-snippets.php:1207
54
- msgid "Manage your existing snippets"
 
55
  msgstr ""
56
 
57
- #: code-snippets.php:1208
58
- msgid "Manage"
 
 
 
 
 
 
59
  msgstr ""
60
 
61
- #: code-snippets.php:1228
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  msgid "Visit the WordPress.org plugin page"
63
  msgstr ""
64
 
65
- #: code-snippets.php:1229
66
  msgid "About"
67
  msgstr ""
68
 
69
- #: code-snippets.php:1233
70
  msgid "Visit the support forums"
71
  msgstr ""
72
 
73
- #: code-snippets.php:1234
74
  msgid "Support"
75
  msgstr ""
76
 
77
- #: code-snippets.php:1238
78
  msgid "Support this plugin's development"
79
  msgstr ""
80
 
81
- #: code-snippets.php:1239
82
  msgid "Donate"
83
  msgstr ""
84
 
85
- #: includes/admin/import.php:8
86
  msgid "Imported <strong>%d</strong> snippet."
87
  msgid_plural "Imported <strong>%d</strong> snippets."
88
  msgstr[0] ""
89
  msgstr[1] ""
90
 
91
- #: includes/admin/import.php:27
92
- msgid "Howdy! Upload your Code Snippets export file and we&#8217;ll import the snippets to this site."
 
 
93
  msgstr ""
94
 
95
- #: includes/admin/import.php:29
96
- msgid "You will need to go to the <a href=\"%s\">Manage Snippets</a> page to activate the imported snippets."
 
 
97
  msgstr ""
98
 
99
- #: includes/admin/import.php:31
100
- msgid "Choose a Code Snippets (.xml) file to upload, then click Upload file and import."
 
 
101
  msgstr ""
102
 
103
- #: includes/admin/import.php:34
104
  msgid "Choose a file from your computer:"
105
  msgstr ""
106
 
107
- #: includes/admin/import.php:34
108
  msgid "(Maximum size: 8MB)"
109
  msgstr ""
110
 
111
- #: includes/admin/import.php:40
112
  msgid "Upload file and import"
113
  msgstr ""
114
 
115
- #: includes/admin/manage.php:14
116
  msgid "Snippet <strong>activated</strong>."
117
  msgstr ""
118
 
119
- #: includes/admin/manage.php:16
120
  msgid "Selected snippets <strong>activated</strong>."
121
  msgstr ""
122
 
123
- #: includes/admin/manage.php:18
124
  msgid "Snippet <strong>deactivated</strong>."
125
  msgstr ""
126
 
127
- #: includes/admin/manage.php:20
128
  msgid "Selected snippets <strong>deactivated</strong>."
129
  msgstr ""
130
 
131
- #: includes/admin/manage.php:22
132
  msgid "Snippet <strong>deleted</strong>."
133
  msgstr ""
134
 
135
- #: includes/admin/manage.php:24
136
  msgid "Selected snippets <strong>deleted</strong>."
137
  msgstr ""
138
 
139
- #: includes/admin/manage.php:31
140
  msgctxt "snippet"
141
  msgid "Add New"
142
  msgstr ""
143
 
144
- #: includes/admin/manage.php:34
145
- msgid "Search results for &#8220;%s&#8221;"
146
- msgstr ""
147
-
148
- #: includes/admin/manage.php:40
149
  msgid "Search Installed Snippets"
150
  msgstr ""
151
 
152
- #: includes/admin/single.php:11
153
- msgid "Sorry, you&#8217;re not allowed to edit snippets"
154
  msgstr ""
155
 
156
- #: includes/admin/single.php:18
157
- msgid "Please provide a name for the snippet and its code."
158
  msgstr ""
159
 
160
- #: includes/admin/single.php:20
161
  msgid "Snippet <strong>updated</strong>."
162
  msgstr ""
163
 
164
- #: includes/admin/single.php:22
165
  msgid "Snippet <strong>added</strong>."
166
  msgstr ""
167
 
168
- #: includes/admin/single.php:56 includes/admin/single.php:57
169
  msgid "Name (short title)"
170
  msgstr ""
171
 
172
- #: includes/admin/single.php:62
173
  msgid "Code"
174
  msgstr ""
175
 
176
- #: includes/admin/single.php:63
177
- msgid "Enter or paste the snippet code without the <code>&lt;?php</code> and <code>?&gt;</code> tags."
178
- msgstr ""
179
-
180
- #: includes/admin/single.php:69 includes/class-list-table.php:156
181
- msgid "Description"
182
- msgstr ""
183
-
184
- #: includes/admin/single.php:70
185
- msgid "(Optional)"
186
- msgstr ""
187
-
188
- #: includes/admin/single.php:86
189
- msgid "Save"
190
- msgstr ""
191
-
192
- #: includes/admin/single.php:87
193
- msgid "Cancel"
194
- msgstr ""
195
-
196
- #: includes/class-list-table.php:31
197
  msgid "Snippets per page"
198
  msgstr ""
199
 
200
- #: includes/class-list-table.php:101 includes/class-list-table.php:178
201
- msgid "Network Deactivate"
202
- msgstr ""
203
-
204
- #: includes/class-list-table.php:101 includes/class-list-table.php:178
205
- msgid "Deactivate"
206
- msgstr ""
207
-
208
- #: includes/class-list-table.php:111 includes/class-list-table.php:177
209
- msgid "Network Activate"
210
- msgstr ""
211
-
212
- #: includes/class-list-table.php:111 includes/class-list-table.php:177
213
- msgid "Activate"
214
  msgstr ""
215
 
216
- #: includes/class-list-table.php:154
217
  msgid "Name"
218
  msgstr ""
219
 
220
- #: includes/class-list-table.php:155
221
  msgid "ID"
222
  msgstr ""
223
 
224
- #: includes/class-list-table.php:179
225
  msgid "Export"
226
  msgstr ""
227
 
228
- #: includes/class-list-table.php:180
229
  msgid "Delete"
230
  msgstr ""
231
 
232
- #: includes/class-list-table.php:181
233
  msgid "Export to PHP"
234
  msgstr ""
235
 
236
- #: includes/class-list-table.php:200
237
  msgid "All <span class=\"count\">(%s)</span>"
238
  msgid_plural "All <span class=\"count\">(%s)</span>"
239
  msgstr[0] ""
240
  msgstr[1] ""
241
 
242
- #: includes/class-list-table.php:203
243
  msgid "Active <span class=\"count\">(%s)</span>"
244
  msgid_plural "Active <span class=\"count\">(%s)</span>"
245
  msgstr[0] ""
246
  msgstr[1] ""
247
 
248
- #: includes/class-list-table.php:206
249
  msgid "Recently Active <span class=\"count\">(%s)</span>"
250
  msgid_plural "Recently Active <span class=\"count\">(%s)</span>"
251
  msgstr[0] ""
252
  msgstr[1] ""
253
 
254
- #: includes/class-list-table.php:209
255
  msgid "Inactive <span class=\"count\">(%s)</span>"
256
  msgid_plural "Inactive <span class=\"count\">(%s)</span>"
257
  msgstr[0] ""
258
  msgstr[1] ""
259
 
260
- #: includes/class-list-table.php:236
 
 
 
 
261
  msgid "Clear List"
262
  msgstr ""
263
 
264
- #: includes/class-list-table.php:304
265
- msgid "You do not appear to have any snippets available at this time. <a href=\"%s\">Add New&rarr;</a>"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
  msgstr ""
267
 
268
  #: includes/help/import.php:5 includes/help/manage.php:5
@@ -271,7 +294,12 @@ msgid "Overview"
271
  msgstr ""
272
 
273
  #: includes/help/import.php:7
274
- msgid "Snippets are similar to plugins - they both extend and expand the functionality of WordPress. Snippets are more light-weight, just a few lines of code, and do not put as much load on your server. Here you can load snippets from a Code Snippets (.xml) import file into the database with your existing snippets."
 
 
 
 
 
275
  msgstr ""
276
 
277
  #: includes/help/import.php:12
@@ -279,11 +307,17 @@ msgid "Importing"
279
  msgstr ""
280
 
281
  #: includes/help/import.php:14
282
- msgid "You can load your snippets from a Code Snippets (.xml) import file using this page."
 
 
283
  msgstr ""
284
 
285
  #: includes/help/import.php:15
286
- msgid "Snippets will be added to the database along with your existing snippets. Regardless of whether the snippets were active on the previous site, imported snippets are always inactive until activated using the <a href=\"%s\">Manage Snippets</a> page.</p>"
 
 
 
 
287
  msgstr ""
288
 
289
  #: includes/help/import.php:20
@@ -291,30 +325,43 @@ msgid "Exporting"
291
  msgstr ""
292
 
293
  #: includes/help/import.php:22
294
- msgid "You can save your snippets to a Code Snippets (.xml) export file using the <a href=\"%s\">Manage Snippets</a> page."
 
 
295
  msgstr ""
296
 
297
  #: includes/help/import.php:26 includes/help/manage.php:27
298
- #: includes/help/single.php:32
299
  msgid "For more information:"
300
  msgstr ""
301
 
302
- #: includes/help/import.php:27 includes/help/single.php:33
303
- msgid "<a href=\"http://wordpress.org/extend/plugins/code-snippets\" target=\"_blank\">WordPress Extend</a>"
 
 
304
  msgstr ""
305
 
306
  #: includes/help/import.php:28 includes/help/manage.php:29
307
- #: includes/help/single.php:34
308
- msgid "<a href=\"http://wordpress.org/support/plugin/code-snippets\" target=\"_blank\">Support Forums</a>"
 
 
309
  msgstr ""
310
 
311
  #: includes/help/import.php:29 includes/help/manage.php:30
312
- #: includes/help/single.php:35
313
- msgid "<a href=\"http://code-snippets.bungeshea.com/\" target=\"_blank\">Project Website</a>"
 
 
314
  msgstr ""
315
 
316
  #: includes/help/manage.php:7
317
- msgid "Snippets are similar to plugins - they both extend and expand the functionality of WordPress. Snippets are more light-weight, just a few lines of code, and do not put as much load on your server. Here you can manage your existing snippets and preform tasks on them such as activating, deactivating, deleting and exporting."
 
 
 
 
 
318
  msgstr ""
319
 
320
  #: includes/help/manage.php:12
@@ -322,11 +369,20 @@ msgid "Safe Mode"
322
  msgstr ""
323
 
324
  #: includes/help/manage.php:14
325
- msgid "Be sure to check your snippets for errors before you activate them, as a faulty snippet could bring your whole blog down. If your site starts doing strange things, deactivate all your snippets and activate them one at a time."
 
 
 
326
  msgstr ""
327
 
328
  #: includes/help/manage.php:15
329
- msgid "If something goes wrong with a snippet and you can't use WordPress, you can cause all snippets to stop executing by adding <code>define('CODE_SNIPPETS_SAFE_MODE', true);</code> to your <code>wp-config.php</code> file. After you have deactivated the offending snippet, you can turn off safe mode by removing this line or replacing <strong>true</strong> with <strong>false</strong>."
 
 
 
 
 
 
330
  msgstr ""
331
 
332
  #: includes/help/manage.php:20
@@ -334,19 +390,33 @@ msgid "Uninstall"
334
  msgstr ""
335
 
336
  #: includes/help/manage.php:22
337
- msgid "When you delete Code Snippets through the Plugins menu in WordPress it will clear up the <code>%1$s</code> table and a few other bits of data stored in the database. If you want to keep this data (ie: you are only temporally uninstalling Code Snippets) then remove the <code>%2$s</code> folder using FTP."
 
 
 
 
 
338
  msgstr ""
339
 
340
  #: includes/help/manage.php:23
341
- msgid "Even if you're sure that you don't want to use Code Snippets ever again on this WordPress installation, you may want to use the export feature to back up your snippets."
 
 
 
342
  msgstr ""
343
 
344
  #: includes/help/manage.php:28
345
- msgid "<a href=\"http://wordpress.org/extend/plugins/code-snippets\" target=\"_blank\">WordPress Extend</a></p>"
 
 
346
  msgstr ""
347
 
348
  #: includes/help/single.php:7
349
- msgid "Snippets are similar to plugins - they both extend and expand the functionality of WordPress. Snippets are more light-weight, just a few lines of code, and do not put as much load on your server. Here you can add a new snippet, or edit an existing one."
 
 
 
 
350
  msgstr ""
351
 
352
  #: includes/help/single.php:11
@@ -355,17 +425,24 @@ msgstr ""
355
 
356
  #: includes/help/single.php:13
357
  msgid ""
358
- "Here are some links to websites which host a large number of snippets that you can add to your site.
359
- "\t\t<ul>
360
- "\t\t\t<li><a href=\"http://wp-snippets.com\" title=\"WordPress Snippets\">WP-Snippets</a></li>
361
- "\t\t\t<li><a href=\"http://wpsnipp.com\" title=\"WP Snipp\">WP Snipp</a></li>
362
- "\t\t\t<li><a href=\"http://www.catswhocode.com/blog/snippets\" title=\"Cats Who Code Snippet Library\">Cats Who Code</a></li>
363
- "\t\t\t<li><a href=\"http://www.wpfunction.me\">WP Function Me</a></li>
 
 
 
 
364
  "\t\t</ul>"
365
  msgstr ""
366
 
367
  #: includes/help/single.php:20
368
- msgid "More places to find snippets, as well as a selection of example snippets, can be found in the <a href=\"http://code-snippets.bungeshea.com/docs/finding-snippets/\">plugin documentation</a>"
 
 
 
369
  msgstr ""
370
 
371
  #: includes/help/single.php:24
@@ -373,18 +450,18 @@ msgid "Adding Snippets"
373
  msgstr ""
374
 
375
  #: includes/help/single.php:26
376
- msgid "You need to fill out the name and code fields for your snippet to be added. While the description field will add more information about how your snippet works, what is does and where you found it, it is completely optional."
 
 
 
377
  msgstr ""
378
 
379
  #: includes/help/single.php:27
380
- msgid "Make sure that you don't add the <code>&lt;?php</code>, <code>&lt;?</code> or <code>?&gt;</code> the beginning and end of the code. You can however use these tags in the code to stop and start PHP sections"
381
- msgstr ""
382
-
383
- #: includes/help/single.php:28
384
- msgid "Please be sure to check that your snippet is valid PHP code and will not produce errors before adding it through this page. While doing so will not become active straight away, it will help to minimise the chance of a faulty snippet becoming active on your site."
385
- msgstr ""
386
- #. Plugin Name of the plugin/theme
387
- msgid "Code Snippets"
388
  msgstr ""
389
 
390
  #. Plugin URI of the plugin/theme
@@ -392,7 +469,9 @@ msgid "http://code-snippets.bungeshea.com"
392
  msgstr ""
393
 
394
  #. Description of the plugin/theme
395
- msgid "An easy, clean and simple way to add code snippets to your site. No need to edit to your theme's functions.php file again!"
 
 
396
  msgstr ""
397
 
398
  #. Author of the plugin/theme
1
+ # Copyright (C) 2013 Code Snippets
2
  # This file is distributed under the same license as the Code Snippets package.
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: Code Snippets 1.7\n"
6
+ "Report-Msgid-Bugs-To: http://wordpress.org/tag/code-snippets\n"
7
+ "POT-Creation-Date: 2013-03-26 07:01:58+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=UTF-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
11
+ "PO-Revision-Date: 2013-MO-DA HO:MI+ZONE\n"
12
+ "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
+ "Language-Team: LANGUAGE <LL@li.org>\n"
14
 
15
+ #. #-#-#-#-# plugin.pot (Code Snippets 1.7) #-#-#-#-#
16
+ #. Plugin Name of the plugin/theme
17
+ #: code-snippets.php:245
18
  msgid "Code Snippets"
19
  msgstr ""
20
 
21
+ #: code-snippets.php:246
22
  msgid "Import snippets from a <strong>Code Snippets</strong> export file"
23
  msgstr ""
24
 
25
+ #: code-snippets.php:581 code-snippets.php:582 code-snippets.php:592
26
+ #: includes/admin/manage.php:36
 
27
  msgid "Snippets"
28
  msgstr ""
29
 
30
+ #: code-snippets.php:593 code-snippets.php:1467
31
+ msgid "Manage"
32
  msgstr ""
33
 
34
+ #: code-snippets.php:605 code-snippets.php:1150 includes/admin/single.php:37
35
+ msgid "Edit Snippet"
36
+ msgstr ""
37
+
38
+ #: code-snippets.php:606
39
+ msgid "Edit"
40
+ msgstr ""
41
+
42
+ #: code-snippets.php:616 code-snippets.php:1149 includes/admin/single.php:45
43
  msgid "Add New Snippet"
44
  msgstr ""
45
 
46
+ #: code-snippets.php:617
47
  msgid "Add New"
48
  msgstr ""
49
 
50
+ #: code-snippets.php:645 includes/admin/import.php:34
51
  msgid "Import Snippets"
52
  msgstr ""
53
 
54
+ #: code-snippets.php:646
55
  msgid "Import"
56
  msgstr ""
57
 
58
+ #: code-snippets.php:1122 includes/class-list-table.php:127
59
+ #: includes/class-list-table.php:213
60
+ msgid "Network Deactivate"
61
  msgstr ""
62
 
63
+ #: code-snippets.php:1122 includes/class-list-table.php:127
64
+ #: includes/class-list-table.php:213
65
+ msgid "Deactivate"
66
  msgstr ""
67
 
68
+ #: code-snippets.php:1131 includes/class-list-table.php:137
69
+ #: includes/class-list-table.php:212
70
+ msgid "Network Activate"
71
+ msgstr ""
72
+
73
+ #: code-snippets.php:1131 includes/class-list-table.php:137
74
+ #: includes/class-list-table.php:212
75
+ msgid "Activate"
76
  msgstr ""
77
 
78
+ #: code-snippets.php:1231
79
+ msgid "Sorry, you're not allowed to edit snippets"
80
+ msgstr ""
81
+
82
+ #: code-snippets.php:1434 includes/class-list-table.php:189
83
+ msgid "Description"
84
+ msgstr ""
85
+
86
+ #: code-snippets.php:1435
87
+ msgid "(Optional)"
88
+ msgstr ""
89
+
90
+ #: code-snippets.php:1466
91
+ msgid "Manage your existing snippets"
92
+ msgstr ""
93
+
94
+ #: code-snippets.php:1487
95
  msgid "Visit the WordPress.org plugin page"
96
  msgstr ""
97
 
98
+ #: code-snippets.php:1488
99
  msgid "About"
100
  msgstr ""
101
 
102
+ #: code-snippets.php:1492
103
  msgid "Visit the support forums"
104
  msgstr ""
105
 
106
+ #: code-snippets.php:1493
107
  msgid "Support"
108
  msgstr ""
109
 
110
+ #: code-snippets.php:1497
111
  msgid "Support this plugin's development"
112
  msgstr ""
113
 
114
+ #: code-snippets.php:1498
115
  msgid "Donate"
116
  msgstr ""
117
 
118
+ #: includes/admin/import.php:19
119
  msgid "Imported <strong>%d</strong> snippet."
120
  msgid_plural "Imported <strong>%d</strong> snippets."
121
  msgstr[0] ""
122
  msgstr[1] ""
123
 
124
+ #: includes/admin/import.php:38
125
+ msgid ""
126
+ "Howdy! Upload your Code Snippets export file and we&#8217;ll import the "
127
+ "snippets to this site."
128
  msgstr ""
129
 
130
+ #: includes/admin/import.php:40
131
+ msgid ""
132
+ "You will need to go to the <a href=\"%s\">Manage Snippets</a> page to "
133
+ "activate the imported snippets."
134
  msgstr ""
135
 
136
+ #: includes/admin/import.php:42
137
+ msgid ""
138
+ "Choose a Code Snippets (.xml) file to upload, then click Upload file and "
139
+ "import."
140
  msgstr ""
141
 
142
+ #: includes/admin/import.php:46
143
  msgid "Choose a file from your computer:"
144
  msgstr ""
145
 
146
+ #: includes/admin/import.php:46
147
  msgid "(Maximum size: 8MB)"
148
  msgstr ""
149
 
150
+ #: includes/admin/import.php:55
151
  msgid "Upload file and import"
152
  msgstr ""
153
 
154
+ #: includes/admin/manage.php:21
155
  msgid "Snippet <strong>activated</strong>."
156
  msgstr ""
157
 
158
+ #: includes/admin/manage.php:23
159
  msgid "Selected snippets <strong>activated</strong>."
160
  msgstr ""
161
 
162
+ #: includes/admin/manage.php:25
163
  msgid "Snippet <strong>deactivated</strong>."
164
  msgstr ""
165
 
166
+ #: includes/admin/manage.php:27
167
  msgid "Selected snippets <strong>deactivated</strong>."
168
  msgstr ""
169
 
170
+ #: includes/admin/manage.php:29
171
  msgid "Snippet <strong>deleted</strong>."
172
  msgstr ""
173
 
174
+ #: includes/admin/manage.php:31
175
  msgid "Selected snippets <strong>deleted</strong>."
176
  msgstr ""
177
 
178
+ #: includes/admin/manage.php:38 includes/admin/single.php:42
179
  msgctxt "snippet"
180
  msgid "Add New"
181
  msgstr ""
182
 
183
+ #: includes/admin/manage.php:47
 
 
 
 
184
  msgid "Search Installed Snippets"
185
  msgstr ""
186
 
187
+ #: includes/admin/single.php:22
188
+ msgid "Please provide a name for the snippet and its code."
189
  msgstr ""
190
 
191
+ #: includes/admin/single.php:24 includes/admin/single.php:26
192
+ msgid "Snippet <strong>updated</strong> and <strong>activated</strong>."
193
  msgstr ""
194
 
195
+ #: includes/admin/single.php:28
196
  msgid "Snippet <strong>updated</strong>."
197
  msgstr ""
198
 
199
+ #: includes/admin/single.php:30
200
  msgid "Snippet <strong>added</strong>."
201
  msgstr ""
202
 
203
+ #: includes/admin/single.php:55 includes/admin/single.php:56
204
  msgid "Name (short title)"
205
  msgstr ""
206
 
207
+ #: includes/admin/single.php:61
208
  msgid "Code"
209
  msgstr ""
210
 
211
+ #: includes/class-list-table.php:36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  msgid "Snippets per page"
213
  msgstr ""
214
 
215
+ #: includes/class-list-table.php:163
216
+ msgid ""
217
+ "You are about to permanently delete the selected item.\r\n"
218
+ "\t\t\t\t'Cancel' to stop, 'OK' to delete."
 
 
 
 
 
 
 
 
 
 
219
  msgstr ""
220
 
221
+ #: includes/class-list-table.php:187
222
  msgid "Name"
223
  msgstr ""
224
 
225
+ #: includes/class-list-table.php:188
226
  msgid "ID"
227
  msgstr ""
228
 
229
+ #: includes/class-list-table.php:214
230
  msgid "Export"
231
  msgstr ""
232
 
233
+ #: includes/class-list-table.php:215
234
  msgid "Delete"
235
  msgstr ""
236
 
237
+ #: includes/class-list-table.php:216
238
  msgid "Export to PHP"
239
  msgstr ""
240
 
241
+ #: includes/class-list-table.php:236
242
  msgid "All <span class=\"count\">(%s)</span>"
243
  msgid_plural "All <span class=\"count\">(%s)</span>"
244
  msgstr[0] ""
245
  msgstr[1] ""
246
 
247
+ #: includes/class-list-table.php:239
248
  msgid "Active <span class=\"count\">(%s)</span>"
249
  msgid_plural "Active <span class=\"count\">(%s)</span>"
250
  msgstr[0] ""
251
  msgstr[1] ""
252
 
253
+ #: includes/class-list-table.php:242
254
  msgid "Recently Active <span class=\"count\">(%s)</span>"
255
  msgid_plural "Recently Active <span class=\"count\">(%s)</span>"
256
  msgstr[0] ""
257
  msgstr[1] ""
258
 
259
+ #: includes/class-list-table.php:245
260
  msgid "Inactive <span class=\"count\">(%s)</span>"
261
  msgid_plural "Inactive <span class=\"count\">(%s)</span>"
262
  msgstr[0] ""
263
  msgstr[1] ""
264
 
265
+ #: includes/class-list-table.php:270
266
+ msgid "Filter"
267
+ msgstr ""
268
+
269
+ #: includes/class-list-table.php:279
270
  msgid "Clear List"
271
  msgstr ""
272
 
273
+ #: includes/class-list-table.php:374
274
+ msgid ""
275
+ "You do not appear to have any snippets available at this time. <a href=\"%s"
276
+ "\">Add New&rarr;</a>"
277
+ msgstr ""
278
+
279
+ #: includes/class-list-table.php:541
280
+ msgid "Search results"
281
+ msgstr ""
282
+
283
+ #: includes/class-list-table.php:544
284
+ msgid " for &#8220;%s&#8221;"
285
+ msgstr ""
286
+
287
+ #: includes/class-list-table.php:550
288
+ msgid "Clear Filters"
289
  msgstr ""
290
 
291
  #: includes/help/import.php:5 includes/help/manage.php:5
294
  msgstr ""
295
 
296
  #: includes/help/import.php:7
297
+ msgid ""
298
+ "Snippets are similar to plugins - they both extend and expand the "
299
+ "functionality of WordPress. Snippets are more light-weight, just a few lines "
300
+ "of code, and do not put as much load on your server. Here you can load "
301
+ "snippets from a Code Snippets (.xml) import file into the database with your "
302
+ "existing snippets."
303
  msgstr ""
304
 
305
  #: includes/help/import.php:12
307
  msgstr ""
308
 
309
  #: includes/help/import.php:14
310
+ msgid ""
311
+ "You can load your snippets from a code snippets (.xml) export file using "
312
+ "this page."
313
  msgstr ""
314
 
315
  #: includes/help/import.php:15
316
+ msgid ""
317
+ "Snippets will be added to the database along with your existing snippets. "
318
+ "Regardless of whether the snippets were active on the previous site, "
319
+ "imported snippets are always inactive until activated using the <a href=\"%s"
320
+ "\">Manage Snippets</a> page.</p>"
321
  msgstr ""
322
 
323
  #: includes/help/import.php:20
325
  msgstr ""
326
 
327
  #: includes/help/import.php:22
328
+ msgid ""
329
+ "You can save your snippets to a Code Snippets (.xml) export file using the "
330
+ "<a href=\"%s\">Manage Snippets</a> page."
331
  msgstr ""
332
 
333
  #: includes/help/import.php:26 includes/help/manage.php:27
334
+ #: includes/help/single.php:31
335
  msgid "For more information:"
336
  msgstr ""
337
 
338
+ #: includes/help/import.php:27 includes/help/single.php:32
339
+ msgid ""
340
+ "<a href=\"http://wordpress.org/extend/plugins/code-snippets\" target=\"_blank"
341
+ "\">WordPress Extend</a>"
342
  msgstr ""
343
 
344
  #: includes/help/import.php:28 includes/help/manage.php:29
345
+ #: includes/help/single.php:33
346
+ msgid ""
347
+ "<a href=\"http://wordpress.org/support/plugin/code-snippets\" target=\"_blank"
348
+ "\">Support Forums</a>"
349
  msgstr ""
350
 
351
  #: includes/help/import.php:29 includes/help/manage.php:30
352
+ #: includes/help/single.php:34
353
+ msgid ""
354
+ "<a href=\"http://code-snippets.bungeshea.com/\" target=\"_blank\">Project "
355
+ "Website</a>"
356
  msgstr ""
357
 
358
  #: includes/help/manage.php:7
359
+ msgid ""
360
+ "Snippets are similar to plugins - they both extend and expand the "
361
+ "functionality of WordPress. Snippets are more light-weight, just a few lines "
362
+ "of code, and do not put as much load on your server. Here you can manage "
363
+ "your existing snippets and preform tasks on them such as activating, "
364
+ "deactivating, deleting and exporting."
365
  msgstr ""
366
 
367
  #: includes/help/manage.php:12
369
  msgstr ""
370
 
371
  #: includes/help/manage.php:14
372
+ msgid ""
373
+ "Be sure to check your snippets for errors before you activate them, as a "
374
+ "faulty snippet could bring your whole blog down. If your site starts doing "
375
+ "strange things, deactivate all your snippets and activate them one at a time."
376
  msgstr ""
377
 
378
  #: includes/help/manage.php:15
379
+ msgid ""
380
+ "If something goes wrong with a snippet and you can't use WordPress, you can "
381
+ "cause all snippets to stop executing by adding <code>define"
382
+ "('CODE_SNIPPETS_SAFE_MODE', true);</code> to your <code>wp-config.php</code> "
383
+ "file. After you have deactivated the offending snippet, you can turn off "
384
+ "safe mode by removing this line or replacing <strong>true</strong> with "
385
+ "<strong>false</strong>."
386
  msgstr ""
387
 
388
  #: includes/help/manage.php:20
390
  msgstr ""
391
 
392
  #: includes/help/manage.php:22
393
+ msgid ""
394
+ "When you delete Code Snippets through the Plugins menu in WordPress it will "
395
+ "clear up the <code>%1$s</code> table and a few other bits of data stored in "
396
+ "the database. If you want to keep this data (ie: you are only temporally "
397
+ "uninstalling Code Snippets) then remove the <code>%2$s</code> folder using "
398
+ "FTP."
399
  msgstr ""
400
 
401
  #: includes/help/manage.php:23
402
+ msgid ""
403
+ "Even if you're sure that you don't want to use Code Snippets ever again on "
404
+ "this WordPress installation, you may want to use the export feature to back "
405
+ "up your snippets."
406
  msgstr ""
407
 
408
  #: includes/help/manage.php:28
409
+ msgid ""
410
+ "<a href=\"http://wordpress.org/extend/plugins/code-snippets\" target=\"_blank"
411
+ "\">WordPress Extend</a></p>"
412
  msgstr ""
413
 
414
  #: includes/help/single.php:7
415
+ msgid ""
416
+ "Snippets are similar to plugins - they both extend and expand the "
417
+ "functionality of WordPress. Snippets are more light-weight, just a few lines "
418
+ "of code, and do not put as much load on your server. Here you can add a new "
419
+ "snippet, or edit an existing one."
420
  msgstr ""
421
 
422
  #: includes/help/single.php:11
425
 
426
  #: includes/help/single.php:13
427
  msgid ""
428
+ "Here are some links to websites which host a large number of snippets that "
429
+ "you can add to your site.\r\n"
430
+ "\t\t<ul>\r\n"
431
+ "\t\t\t<li><a href=\"http://wp-snippets.com\" title=\"WordPress Snippets\">WP-"
432
+ "Snippets</a></li>\r\n"
433
+ "\t\t\t<li><a href=\"http://wpsnipp.com\" title=\"WP Snipp\">WP Snipp</a></li>"
434
+ "\r\n"
435
+ "\t\t\t<li><a href=\"http://www.catswhocode.com/blog/snippets\" title=\"Cats "
436
+ "Who Code Snippet Library\">Cats Who Code</a></li>\r\n"
437
+ "\t\t\t<li><a href=\"http://www.wpfunction.me\">WP Function Me</a></li>\r\n"
438
  "\t\t</ul>"
439
  msgstr ""
440
 
441
  #: includes/help/single.php:20
442
+ msgid ""
443
+ "More places to find snippets, as well as a selection of example snippets, "
444
+ "can be found in the <a href=\"http://code-snippets.bungeshea.com/docs/"
445
+ "finding-snippets/\">plugin documentation</a>"
446
  msgstr ""
447
 
448
  #: includes/help/single.php:24
450
  msgstr ""
451
 
452
  #: includes/help/single.php:26
453
+ msgid ""
454
+ "You need to fill out the name and code fields for your snippet to be added. "
455
+ "While the description field will add more information about how your snippet "
456
+ "works, what is does and where you found it, it is completely optional."
457
  msgstr ""
458
 
459
  #: includes/help/single.php:27
460
+ msgid ""
461
+ "Please be sure to check that your snippet is valid PHP code and will not "
462
+ "produce errors before adding it through this page. While doing so will not "
463
+ "become active straight away, it will help to minimise the chance of a faulty "
464
+ "snippet becoming active on your site."
 
 
 
465
  msgstr ""
466
 
467
  #. Plugin URI of the plugin/theme
469
  msgstr ""
470
 
471
  #. Description of the plugin/theme
472
+ msgid ""
473
+ "An easy, clean and simple way to add code snippets to your site. No need to "
474
+ "edit to your theme's functions.php file again!"
475
  msgstr ""
476
 
477
  #. Author of the plugin/theme
license.txt CHANGED
@@ -1,674 +1,7 @@
1
- GNU GENERAL PUBLIC LICENSE
2
- Version 3, 29 June 2007
3
 
4
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5
- Everyone is permitted to copy and distribute verbatim copies
6
- of this license document, but changing it is not allowed.
7
 
8
- Preamble
9
 
10
- The GNU General Public License is a free, copyleft license for
11
- software and other kinds of works.
12
-
13
- The licenses for most software and other practical works are designed
14
- to take away your freedom to share and change the works. By contrast,
15
- the GNU General Public License is intended to guarantee your freedom to
16
- share and change all versions of a program--to make sure it remains free
17
- software for all its users. We, the Free Software Foundation, use the
18
- GNU General Public License for most of our software; it applies also to
19
- any other work released this way by its authors. You can apply it to
20
- your programs, too.
21
-
22
- When we speak of free software, we are referring to freedom, not
23
- price. Our General Public Licenses are designed to make sure that you
24
- have the freedom to distribute copies of free software (and charge for
25
- them if you wish), that you receive source code or can get it if you
26
- want it, that you can change the software or use pieces of it in new
27
- free programs, and that you know you can do these things.
28
-
29
- To protect your rights, we need to prevent others from denying you
30
- these rights or asking you to surrender the rights. Therefore, you have
31
- certain responsibilities if you distribute copies of the software, or if
32
- you modify it: responsibilities to respect the freedom of others.
33
-
34
- For example, if you distribute copies of such a program, whether
35
- gratis or for a fee, you must pass on to the recipients the same
36
- freedoms that you received. You must make sure that they, too, receive
37
- or can get the source code. And you must show them these terms so they
38
- know their rights.
39
-
40
- Developers that use the GNU GPL protect your rights with two steps:
41
- (1) assert copyright on the software, and (2) offer you this License
42
- giving you legal permission to copy, distribute and/or modify it.
43
-
44
- For the developers' and authors' protection, the GPL clearly explains
45
- that there is no warranty for this free software. For both users' and
46
- authors' sake, the GPL requires that modified versions be marked as
47
- changed, so that their problems will not be attributed erroneously to
48
- authors of previous versions.
49
-
50
- Some devices are designed to deny users access to install or run
51
- modified versions of the software inside them, although the manufacturer
52
- can do so. This is fundamentally incompatible with the aim of
53
- protecting users' freedom to change the software. The systematic
54
- pattern of such abuse occurs in the area of products for individuals to
55
- use, which is precisely where it is most unacceptable. Therefore, we
56
- have designed this version of the GPL to prohibit the practice for those
57
- products. If such problems arise substantially in other domains, we
58
- stand ready to extend this provision to those domains in future versions
59
- of the GPL, as needed to protect the freedom of users.
60
-
61
- Finally, every program is threatened constantly by software patents.
62
- States should not allow patents to restrict development and use of
63
- software on general-purpose computers, but in those that do, we wish to
64
- avoid the special danger that patents applied to a free program could
65
- make it effectively proprietary. To prevent this, the GPL assures that
66
- patents cannot be used to render the program non-free.
67
-
68
- The precise terms and conditions for copying, distribution and
69
- modification follow.
70
-
71
- TERMS AND CONDITIONS
72
-
73
- 0. Definitions.
74
-
75
- "This License" refers to version 3 of the GNU General Public License.
76
-
77
- "Copyright" also means copyright-like laws that apply to other kinds of
78
- works, such as semiconductor masks.
79
-
80
- "The Program" refers to any copyrightable work licensed under this
81
- License. Each licensee is addressed as "you". "Licensees" and
82
- "recipients" may be individuals or organizations.
83
-
84
- To "modify" a work means to copy from or adapt all or part of the work
85
- in a fashion requiring copyright permission, other than the making of an
86
- exact copy. The resulting work is called a "modified version" of the
87
- earlier work or a work "based on" the earlier work.
88
-
89
- A "covered work" means either the unmodified Program or a work based
90
- on the Program.
91
-
92
- To "propagate" a work means to do anything with it that, without
93
- permission, would make you directly or secondarily liable for
94
- infringement under applicable copyright law, except executing it on a
95
- computer or modifying a private copy. Propagation includes copying,
96
- distribution (with or without modification), making available to the
97
- public, and in some countries other activities as well.
98
-
99
- To "convey" a work means any kind of propagation that enables other
100
- parties to make or receive copies. Mere interaction with a user through
101
- a computer network, with no transfer of a copy, is not conveying.
102
-
103
- An interactive user interface displays "Appropriate Legal Notices"
104
- to the extent that it includes a convenient and prominently visible
105
- feature that (1) displays an appropriate copyright notice, and (2)
106
- tells the user that there is no warranty for the work (except to the
107
- extent that warranties are provided), that licensees may convey the
108
- work under this License, and how to view a copy of this License. If
109
- the interface presents a list of user commands or options, such as a
110
- menu, a prominent item in the list meets this criterion.
111
-
112
- 1. Source Code.
113
-
114
- The "source code" for a work means the preferred form of the work
115
- for making modifications to it. "Object code" means any non-source
116
- form of a work.
117
-
118
- A "Standard Interface" means an interface that either is an official
119
- standard defined by a recognized standards body, or, in the case of
120
- interfaces specified for a particular programming language, one that
121
- is widely used among developers working in that language.
122
-
123
- The "System Libraries" of an executable work include anything, other
124
- than the work as a whole, that (a) is included in the normal form of
125
- packaging a Major Component, but which is not part of that Major
126
- Component, and (b) serves only to enable use of the work with that
127
- Major Component, or to implement a Standard Interface for which an
128
- implementation is available to the public in source code form. A
129
- "Major Component", in this context, means a major essential component
130
- (kernel, window system, and so on) of the specific operating system
131
- (if any) on which the executable work runs, or a compiler used to
132
- produce the work, or an object code interpreter used to run it.
133
-
134
- The "Corresponding Source" for a work in object code form means all
135
- the source code needed to generate, install, and (for an executable
136
- work) run the object code and to modify the work, including scripts to
137
- control those activities. However, it does not include the work's
138
- System Libraries, or general-purpose tools or generally available free
139
- programs which are used unmodified in performing those activities but
140
- which are not part of the work. For example, Corresponding Source
141
- includes interface definition files associated with source files for
142
- the work, and the source code for shared libraries and dynamically
143
- linked subprograms that the work is specifically designed to require,
144
- such as by intimate data communication or control flow between those
145
- subprograms and other parts of the work.
146
-
147
- The Corresponding Source need not include anything that users
148
- can regenerate automatically from other parts of the Corresponding
149
- Source.
150
-
151
- The Corresponding Source for a work in source code form is that
152
- same work.
153
-
154
- 2. Basic Permissions.
155
-
156
- All rights granted under this License are granted for the term of
157
- copyright on the Program, and are irrevocable provided the stated
158
- conditions are met. This License explicitly affirms your unlimited
159
- permission to run the unmodified Program. The output from running a
160
- covered work is covered by this License only if the output, given its
161
- content, constitutes a covered work. This License acknowledges your
162
- rights of fair use or other equivalent, as provided by copyright law.
163
-
164
- You may make, run and propagate covered works that you do not
165
- convey, without conditions so long as your license otherwise remains
166
- in force. You may convey covered works to others for the sole purpose
167
- of having them make modifications exclusively for you, or provide you
168
- with facilities for running those works, provided that you comply with
169
- the terms of this License in conveying all material for which you do
170
- not control copyright. Those thus making or running the covered works
171
- for you must do so exclusively on your behalf, under your direction
172
- and control, on terms that prohibit them from making any copies of
173
- your copyrighted material outside their relationship with you.
174
-
175
- Conveying under any other circumstances is permitted solely under
176
- the conditions stated below. Sublicensing is not allowed; section 10
177
- makes it unnecessary.
178
-
179
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180
-
181
- No covered work shall be deemed part of an effective technological
182
- measure under any applicable law fulfilling obligations under article
183
- 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184
- similar laws prohibiting or restricting circumvention of such
185
- measures.
186
-
187
- When you convey a covered work, you waive any legal power to forbid
188
- circumvention of technological measures to the extent such circumvention
189
- is effected by exercising rights under this License with respect to
190
- the covered work, and you disclaim any intention to limit operation or
191
- modification of the work as a means of enforcing, against the work's
192
- users, your or third parties' legal rights to forbid circumvention of
193
- technological measures.
194
-
195
- 4. Conveying Verbatim Copies.
196
-
197
- You may convey verbatim copies of the Program's source code as you
198
- receive it, in any medium, provided that you conspicuously and
199
- appropriately publish on each copy an appropriate copyright notice;
200
- keep intact all notices stating that this License and any
201
- non-permissive terms added in accord with section 7 apply to the code;
202
- keep intact all notices of the absence of any warranty; and give all
203
- recipients a copy of this License along with the Program.
204
-
205
- You may charge any price or no price for each copy that you convey,
206
- and you may offer support or warranty protection for a fee.
207
-
208
- 5. Conveying Modified Source Versions.
209
-
210
- You may convey a work based on the Program, or the modifications to
211
- produce it from the Program, in the form of source code under the
212
- terms of section 4, provided that you also meet all of these conditions:
213
-
214
- a) The work must carry prominent notices stating that you modified
215
- it, and giving a relevant date.
216
-
217
- b) The work must carry prominent notices stating that it is
218
- released under this License and any conditions added under section
219
- 7. This requirement modifies the requirement in section 4 to
220
- "keep intact all notices".
221
-
222
- c) You must license the entire work, as a whole, under this
223
- License to anyone who comes into possession of a copy. This
224
- License will therefore apply, along with any applicable section 7
225
- additional terms, to the whole of the work, and all its parts,
226
- regardless of how they are packaged. This License gives no
227
- permission to license the work in any other way, but it does not
228
- invalidate such permission if you have separately received it.
229
-
230
- d) If the work has interactive user interfaces, each must display
231
- Appropriate Legal Notices; however, if the Program has interactive
232
- interfaces that do not display Appropriate Legal Notices, your
233
- work need not make them do so.
234
-
235
- A compilation of a covered work with other separate and independent
236
- works, which are not by their nature extensions of the covered work,
237
- and which are not combined with it such as to form a larger program,
238
- in or on a volume of a storage or distribution medium, is called an
239
- "aggregate" if the compilation and its resulting copyright are not
240
- used to limit the access or legal rights of the compilation's users
241
- beyond what the individual works permit. Inclusion of a covered work
242
- in an aggregate does not cause this License to apply to the other
243
- parts of the aggregate.
244
-
245
- 6. Conveying Non-Source Forms.
246
-
247
- You may convey a covered work in object code form under the terms
248
- of sections 4 and 5, provided that you also convey the
249
- machine-readable Corresponding Source under the terms of this License,
250
- in one of these ways:
251
-
252
- a) Convey the object code in, or embodied in, a physical product
253
- (including a physical distribution medium), accompanied by the
254
- Corresponding Source fixed on a durable physical medium
255
- customarily used for software interchange.
256
-
257
- b) Convey the object code in, or embodied in, a physical product
258
- (including a physical distribution medium), accompanied by a
259
- written offer, valid for at least three years and valid for as
260
- long as you offer spare parts or customer support for that product
261
- model, to give anyone who possesses the object code either (1) a
262
- copy of the Corresponding Source for all the software in the
263
- product that is covered by this License, on a durable physical
264
- medium customarily used for software interchange, for a price no
265
- more than your reasonable cost of physically performing this
266
- conveying of source, or (2) access to copy the
267
- Corresponding Source from a network server at no charge.
268
-
269
- c) Convey individual copies of the object code with a copy of the
270
- written offer to provide the Corresponding Source. This
271
- alternative is allowed only occasionally and noncommercially, and
272
- only if you received the object code with such an offer, in accord
273
- with subsection 6b.
274
-
275
- d) Convey the object code by offering access from a designated
276
- place (gratis or for a charge), and offer equivalent access to the
277
- Corresponding Source in the same way through the same place at no
278
- further charge. You need not require recipients to copy the
279
- Corresponding Source along with the object code. If the place to
280
- copy the object code is a network server, the Corresponding Source
281
- may be on a different server (operated by you or a third party)
282
- that supports equivalent copying facilities, provided you maintain
283
- clear directions next to the object code saying where to find the
284
- Corresponding Source. Regardless of what server hosts the
285
- Corresponding Source, you remain obligated to ensure that it is
286
- available for as long as needed to satisfy these requirements.
287
-
288
- e) Convey the object code using peer-to-peer transmission, provided
289
- you inform other peers where the object code and Corresponding
290
- Source of the work are being offered to the general public at no
291
- charge under subsection 6d.
292
-
293
- A separable portion of the object code, whose source code is excluded
294
- from the Corresponding Source as a System Library, need not be
295
- included in conveying the object code work.
296
-
297
- A "User Product" is either (1) a "consumer product", which means any
298
- tangible personal property which is normally used for personal, family,
299
- or household purposes, or (2) anything designed or sold for incorporation
300
- into a dwelling. In determining whether a product is a consumer product,
301
- doubtful cases shall be resolved in favor of coverage. For a particular
302
- product received by a particular user, "normally used" refers to a
303
- typical or common use of that class of product, regardless of the status
304
- of the particular user or of the way in which the particular user
305
- actually uses, or expects or is expected to use, the product. A product
306
- is a consumer product regardless of whether the product has substantial
307
- commercial, industrial or non-consumer uses, unless such uses represent
308
- the only significant mode of use of the product.
309
-
310
- "Installation Information" for a User Product means any methods,
311
- procedures, authorization keys, or other information required to install
312
- and execute modified versions of a covered work in that User Product from
313
- a modified version of its Corresponding Source. The information must
314
- suffice to ensure that the continued functioning of the modified object
315
- code is in no case prevented or interfered with solely because
316
- modification has been made.
317
-
318
- If you convey an object code work under this section in, or with, or
319
- specifically for use in, a User Product, and the conveying occurs as
320
- part of a transaction in which the right of possession and use of the
321
- User Product is transferred to the recipient in perpetuity or for a
322
- fixed term (regardless of how the transaction is characterized), the
323
- Corresponding Source conveyed under this section must be accompanied
324
- by the Installation Information. But this requirement does not apply
325
- if neither you nor any third party retains the ability to install
326
- modified object code on the User Product (for example, the work has
327
- been installed in ROM).
328
-
329
- The requirement to provide Installation Information does not include a
330
- requirement to continue to provide support service, warranty, or updates
331
- for a work that has been modified or installed by the recipient, or for
332
- the User Product in which it has been modified or installed. Access to a
333
- network may be denied when the modification itself materially and
334
- adversely affects the operation of the network or violates the rules and
335
- protocols for communication across the network.
336
-
337
- Corresponding Source conveyed, and Installation Information provided,
338
- in accord with this section must be in a format that is publicly
339
- documented (and with an implementation available to the public in
340
- source code form), and must require no special password or key for
341
- unpacking, reading or copying.
342
-
343
- 7. Additional Terms.
344
-
345
- "Additional permissions" are terms that supplement the terms of this
346
- License by making exceptions from one or more of its conditions.
347
- Additional permissions that are applicable to the entire Program shall
348
- be treated as though they were included in this License, to the extent
349
- that they are valid under applicable law. If additional permissions
350
- apply only to part of the Program, that part may be used separately
351
- under those permissions, but the entire Program remains governed by
352
- this License without regard to the additional permissions.
353
-
354
- When you convey a copy of a covered work, you may at your option
355
- remove any additional permissions from that copy, or from any part of
356
- it. (Additional permissions may be written to require their own
357
- removal in certain cases when you modify the work.) You may place
358
- additional permissions on material, added by you to a covered work,
359
- for which you have or can give appropriate copyright permission.
360
-
361
- Notwithstanding any other provision of this License, for material you
362
- add to a covered work, you may (if authorized by the copyright holders of
363
- that material) supplement the terms of this License with terms:
364
-
365
- a) Disclaiming warranty or limiting liability differently from the
366
- terms of sections 15 and 16 of this License; or
367
-
368
- b) Requiring preservation of specified reasonable legal notices or
369
- author attributions in that material or in the Appropriate Legal
370
- Notices displayed by works containing it; or
371
-
372
- c) Prohibiting misrepresentation of the origin of that material, or
373
- requiring that modified versions of such material be marked in
374
- reasonable ways as different from the original version; or
375
-
376
- d) Limiting the use for publicity purposes of names of licensors or
377
- authors of the material; or
378
-
379
- e) Declining to grant rights under trademark law for use of some
380
- trade names, trademarks, or service marks; or
381
-
382
- f) Requiring indemnification of licensors and authors of that
383
- material by anyone who conveys the material (or modified versions of
384
- it) with contractual assumptions of liability to the recipient, for
385
- any liability that these contractual assumptions directly impose on
386
- those licensors and authors.
387
-
388
- All other non-permissive additional terms are considered "further
389
- restrictions" within the meaning of section 10. If the Program as you
390
- received it, or any part of it, contains a notice stating that it is
391
- governed by this License along with a term that is a further
392
- restriction, you may remove that term. If a license document contains
393
- a further restriction but permits relicensing or conveying under this
394
- License, you may add to a covered work material governed by the terms
395
- of that license document, provided that the further restriction does
396
- not survive such relicensing or conveying.
397
-
398
- If you add terms to a covered work in accord with this section, you
399
- must place, in the relevant source files, a statement of the
400
- additional terms that apply to those files, or a notice indicating
401
- where to find the applicable terms.
402
-
403
- Additional terms, permissive or non-permissive, may be stated in the
404
- form of a separately written license, or stated as exceptions;
405
- the above requirements apply either way.
406
-
407
- 8. Termination.
408
-
409
- You may not propagate or modify a covered work except as expressly
410
- provided under this License. Any attempt otherwise to propagate or
411
- modify it is void, and will automatically terminate your rights under
412
- this License (including any patent licenses granted under the third
413
- paragraph of section 11).
414
-
415
- However, if you cease all violation of this License, then your
416
- license from a particular copyright holder is reinstated (a)
417
- provisionally, unless and until the copyright holder explicitly and
418
- finally terminates your license, and (b) permanently, if the copyright
419
- holder fails to notify you of the violation by some reasonable means
420
- prior to 60 days after the cessation.
421
-
422
- Moreover, your license from a particular copyright holder is
423
- reinstated permanently if the copyright holder notifies you of the
424
- violation by some reasonable means, this is the first time you have
425
- received notice of violation of this License (for any work) from that
426
- copyright holder, and you cure the violation prior to 30 days after
427
- your receipt of the notice.
428
-
429
- Termination of your rights under this section does not terminate the
430
- licenses of parties who have received copies or rights from you under
431
- this License. If your rights have been terminated and not permanently
432
- reinstated, you do not qualify to receive new licenses for the same
433
- material under section 10.
434
-
435
- 9. Acceptance Not Required for Having Copies.
436
-
437
- You are not required to accept this License in order to receive or
438
- run a copy of the Program. Ancillary propagation of a covered work
439
- occurring solely as a consequence of using peer-to-peer transmission
440
- to receive a copy likewise does not require acceptance. However,
441
- nothing other than this License grants you permission to propagate or
442
- modify any covered work. These actions infringe copyright if you do
443
- not accept this License. Therefore, by modifying or propagating a
444
- covered work, you indicate your acceptance of this License to do so.
445
-
446
- 10. Automatic Licensing of Downstream Recipients.
447
-
448
- Each time you convey a covered work, the recipient automatically
449
- receives a license from the original licensors, to run, modify and
450
- propagate that work, subject to this License. You are not responsible
451
- for enforcing compliance by third parties with this License.
452
-
453
- An "entity transaction" is a transaction transferring control of an
454
- organization, or substantially all assets of one, or subdividing an
455
- organization, or merging organizations. If propagation of a covered
456
- work results from an entity transaction, each party to that
457
- transaction who receives a copy of the work also receives whatever
458
- licenses to the work the party's predecessor in interest had or could
459
- give under the previous paragraph, plus a right to possession of the
460
- Corresponding Source of the work from the predecessor in interest, if
461
- the predecessor has it or can get it with reasonable efforts.
462
-
463
- You may not impose any further restrictions on the exercise of the
464
- rights granted or affirmed under this License. For example, you may
465
- not impose a license fee, royalty, or other charge for exercise of
466
- rights granted under this License, and you may not initiate litigation
467
- (including a cross-claim or counterclaim in a lawsuit) alleging that
468
- any patent claim is infringed by making, using, selling, offering for
469
- sale, or importing the Program or any portion of it.
470
-
471
- 11. Patents.
472
-
473
- A "contributor" is a copyright holder who authorizes use under this
474
- License of the Program or a work on which the Program is based. The
475
- work thus licensed is called the contributor's "contributor version".
476
-
477
- A contributor's "essential patent claims" are all patent claims
478
- owned or controlled by the contributor, whether already acquired or
479
- hereafter acquired, that would be infringed by some manner, permitted
480
- by this License, of making, using, or selling its contributor version,
481
- but do not include claims that would be infringed only as a
482
- consequence of further modification of the contributor version. For
483
- purposes of this definition, "control" includes the right to grant
484
- patent sublicenses in a manner consistent with the requirements of
485
- this License.
486
-
487
- Each contributor grants you a non-exclusive, worldwide, royalty-free
488
- patent license under the contributor's essential patent claims, to
489
- make, use, sell, offer for sale, import and otherwise run, modify and
490
- propagate the contents of its contributor version.
491
-
492
- In the following three paragraphs, a "patent license" is any express
493
- agreement or commitment, however denominated, not to enforce a patent
494
- (such as an express permission to practice a patent or covenant not to
495
- sue for patent infringement). To "grant" such a patent license to a
496
- party means to make such an agreement or commitment not to enforce a
497
- patent against the party.
498
-
499
- If you convey a covered work, knowingly relying on a patent license,
500
- and the Corresponding Source of the work is not available for anyone
501
- to copy, free of charge and under the terms of this License, through a
502
- publicly available network server or other readily accessible means,
503
- then you must either (1) cause the Corresponding Source to be so
504
- available, or (2) arrange to deprive yourself of the benefit of the
505
- patent license for this particular work, or (3) arrange, in a manner
506
- consistent with the requirements of this License, to extend the patent
507
- license to downstream recipients. "Knowingly relying" means you have
508
- actual knowledge that, but for the patent license, your conveying the
509
- covered work in a country, or your recipient's use of the covered work
510
- in a country, would infringe one or more identifiable patents in that
511
- country that you have reason to believe are valid.
512
-
513
- If, pursuant to or in connection with a single transaction or
514
- arrangement, you convey, or propagate by procuring conveyance of, a
515
- covered work, and grant a patent license to some of the parties
516
- receiving the covered work authorizing them to use, propagate, modify
517
- or convey a specific copy of the covered work, then the patent license
518
- you grant is automatically extended to all recipients of the covered
519
- work and works based on it.
520
-
521
- A patent license is "discriminatory" if it does not include within
522
- the scope of its coverage, prohibits the exercise of, or is
523
- conditioned on the non-exercise of one or more of the rights that are
524
- specifically granted under this License. You may not convey a covered
525
- work if you are a party to an arrangement with a third party that is
526
- in the business of distributing software, under which you make payment
527
- to the third party based on the extent of your activity of conveying
528
- the work, and under which the third party grants, to any of the
529
- parties who would receive the covered work from you, a discriminatory
530
- patent license (a) in connection with copies of the covered work
531
- conveyed by you (or copies made from those copies), or (b) primarily
532
- for and in connection with specific products or compilations that
533
- contain the covered work, unless you entered into that arrangement,
534
- or that patent license was granted, prior to 28 March 2007.
535
-
536
- Nothing in this License shall be construed as excluding or limiting
537
- any implied license or other defenses to infringement that may
538
- otherwise be available to you under applicable patent law.
539
-
540
- 12. No Surrender of Others' Freedom.
541
-
542
- If conditions are imposed on you (whether by court order, agreement or
543
- otherwise) that contradict the conditions of this License, they do not
544
- excuse you from the conditions of this License. If you cannot convey a
545
- covered work so as to satisfy simultaneously your obligations under this
546
- License and any other pertinent obligations, then as a consequence you may
547
- not convey it at all. For example, if you agree to terms that obligate you
548
- to collect a royalty for further conveying from those to whom you convey
549
- the Program, the only way you could satisfy both those terms and this
550
- License would be to refrain entirely from conveying the Program.
551
-
552
- 13. Use with the GNU Affero General Public License.
553
-
554
- Notwithstanding any other provision of this License, you have
555
- permission to link or combine any covered work with a work licensed
556
- under version 3 of the GNU Affero General Public License into a single
557
- combined work, and to convey the resulting work. The terms of this
558
- License will continue to apply to the part which is the covered work,
559
- but the special requirements of the GNU Affero General Public License,
560
- section 13, concerning interaction through a network will apply to the
561
- combination as such.
562
-
563
- 14. Revised Versions of this License.
564
-
565
- The Free Software Foundation may publish revised and/or new versions of
566
- the GNU General Public License from time to time. Such new versions will
567
- be similar in spirit to the present version, but may differ in detail to
568
- address new problems or concerns.
569
-
570
- Each version is given a distinguishing version number. If the
571
- Program specifies that a certain numbered version of the GNU General
572
- Public License "or any later version" applies to it, you have the
573
- option of following the terms and conditions either of that numbered
574
- version or of any later version published by the Free Software
575
- Foundation. If the Program does not specify a version number of the
576
- GNU General Public License, you may choose any version ever published
577
- by the Free Software Foundation.
578
-
579
- If the Program specifies that a proxy can decide which future
580
- versions of the GNU General Public License can be used, that proxy's
581
- public statement of acceptance of a version permanently authorizes you
582
- to choose that version for the Program.
583
-
584
- Later license versions may give you additional or different
585
- permissions. However, no additional obligations are imposed on any
586
- author or copyright holder as a result of your choosing to follow a
587
- later version.
588
-
589
- 15. Disclaimer of Warranty.
590
-
591
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592
- APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593
- HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594
- OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595
- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596
- PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597
- IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598
- ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599
-
600
- 16. Limitation of Liability.
601
-
602
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603
- WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604
- THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605
- GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606
- USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607
- DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608
- PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609
- EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610
- SUCH DAMAGES.
611
-
612
- 17. Interpretation of Sections 15 and 16.
613
-
614
- If the disclaimer of warranty and limitation of liability provided
615
- above cannot be given local legal effect according to their terms,
616
- reviewing courts shall apply local law that most closely approximates
617
- an absolute waiver of all civil liability in connection with the
618
- Program, unless a warranty or assumption of liability accompanies a
619
- copy of the Program in return for a fee.
620
-
621
- END OF TERMS AND CONDITIONS
622
-
623
- How to Apply These Terms to Your New Programs
624
-
625
- If you develop a new program, and you want it to be of the greatest
626
- possible use to the public, the best way to achieve this is to make it
627
- free software which everyone can redistribute and change under these terms.
628
-
629
- To do so, attach the following notices to the program. It is safest
630
- to attach them to the start of each source file to most effectively
631
- state the exclusion of warranty; and each file should have at least
632
- the "copyright" line and a pointer to where the full notice is found.
633
-
634
- <one line to give the program's name and a brief idea of what it does.>
635
- Copyright (C) <year> <name of author>
636
-
637
- This program is free software: you can redistribute it and/or modify
638
- it under the terms of the GNU General Public License as published by
639
- the Free Software Foundation, either version 3 of the License, or
640
- (at your option) any later version.
641
-
642
- This program is distributed in the hope that it will be useful,
643
- but WITHOUT ANY WARRANTY; without even the implied warranty of
644
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645
- GNU General Public License for more details.
646
-
647
- You should have received a copy of the GNU General Public License
648
- along with this program. If not, see <http://www.gnu.org/licenses/>.
649
-
650
- Also add information on how to contact you by electronic and paper mail.
651
-
652
- If the program does terminal interaction, make it output a short
653
- notice like this when it starts in an interactive mode:
654
-
655
- <program> Copyright (C) <year> <name of author>
656
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657
- This is free software, and you are welcome to redistribute it
658
- under certain conditions; type `show c' for details.
659
-
660
- The hypothetical commands `show w' and `show c' should show the appropriate
661
- parts of the General Public License. Of course, your program's commands
662
- might be different; for a GUI interface, you would use an "about box".
663
-
664
- You should also get your employer (if you work as a programmer) or school,
665
- if any, to sign a "copyright disclaimer" for the program, if necessary.
666
- For more information on this, and how to apply and follow the GNU GPL, see
667
- <http://www.gnu.org/licenses/>.
668
-
669
- The GNU General Public License does not permit incorporating your program
670
- into proprietary programs. If your program is a subroutine library, you
671
- may consider it more useful to permit linking proprietary applications with
672
- the library. If this is what you want to do, use the GNU Lesser General
673
- Public License instead of this License. But first, please read
674
- <http://www.gnu.org/philosophy/why-not-lgpl.html>.
1
+ Copyright (c) 2013 Shea Bunge
 
2
 
3
+ 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:
 
 
4
 
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
 
7
+ 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.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
readme.txt CHANGED
@@ -1,12 +1,12 @@
1
  === Code Snippets ===
2
  Contributors: bungeshea
3
  Donate link: http://code-snippets.bungeshea.com/donate/
4
- Tags: snippets, code, php, network, multisite
5
  Requires at least: 3.3
6
- Tested up to: 3.5
7
- Stable tag: 1.6.1
8
- License: GPLv3 or later
9
- License URI: http://www.gnu.org/copyleft/gpl.html
10
 
11
  An easy, clean and simple way to add code snippets to your site.
12
 
@@ -15,7 +15,7 @@ An easy, clean and simple way to add code snippets to your site.
15
  **Code Snippets** is an easy, clean and simple way to add code snippets to your site. No need to edit to your theme's `functions.php` file again!
16
 
17
  A snippet is a small chunk of PHP code that you can use to extend the functionality of a WordPress-powered website; essentially a mini-plugin with a *lot* less load on your site.
18
- Most snippet-hosting sites tell you to add snippet code to your active theme's `functions.php` file, which can get rather long and messy after a while.
19
  Code Snippets changes that by providing a GUI interface for adding snippets and **actually running them on your site** as if they were in your theme's `functions.php` file.
20
 
21
  You can use a graphical interface, similar to the Plugins menu, to manage, activate, deactivate, edit and delete your snippets. Easily organise your snippets by adding a name and description using the visual editor. Code Snippets includes built-in syntax highlighting and other features to help you write your code. Snippets can be exported for transfer to another side, either in XML for later importing by the Code Snippets plugin, or in PHP for creating your own plugin or theme.
@@ -53,7 +53,7 @@ If you have any feedback, issues, or suggestions for improvements please leave a
53
  Further documentation available on the [plugin website](http://code-snippets.bungeshea.com/docs/).
54
 
55
  = Do I need to include the &lt;?php, &lt;? or ?&gt; tags in my snippet? =
56
- No, just copy all the content inside those tags.
57
 
58
  = Is there a way to add a snippet but not run it right away? =
59
  Yes. Just add it but do not activate it yet.
@@ -85,7 +85,10 @@ Yes! You can individually export a single snippet using the link below the snipp
85
  Yes. Click the checkboxes next to the snippets you want to export, and then choose **Export to PHP** from the Bulk Actions menu and click Apply. The generated PHP file will contain the exported snippets' code, as well as their name and description in comments.
86
 
87
  = Can I run network-wide snippets on a multisite installation? =
88
- You can run snippets across an entire multisite network by **Network Activating** Code Snippets through the Network Dashboard.
 
 
 
89
 
90
  = I need help with Code Snippets =
91
  You can get help with Code Snippets either on the [WordPress Support Forums](http://wordpress.org/support/plugin/code-snippets/), on [GithHub](https://github.com/bungeshea/code-snippets/issues), or on [WordPress Answers](http://wordpress.stackexchange.com).
@@ -99,13 +102,25 @@ That's fantastic! Join me on [GitHub](https://github.com/bungeshea/code-snippets
99
  == Screenshots ==
100
 
101
  1. Managing existing snippets
102
- 2. Managing network-wide snippets
103
- 3. Adding a new snippet
104
- 4. Editing a snippet
105
- 5. Importing snippets from an XML file
106
 
107
  == Changelog ==
108
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  = 1.6.1 =
110
  * Fixed a bug with permissions not being applied on install ([#](http://wordpress.org/support/topic/permissions-problem-after-install))
111
  * Fixed a bug in the uninstall method ([#](http://wordpress.org/support/topic/bug-in-delete-script))
@@ -178,6 +193,9 @@ Plugin updates will be posted on the [plugin's homepage](http://code-snippets.bu
178
 
179
  == Upgrade Notice ==
180
 
 
 
 
181
  = 1.6 =
182
  Improvements and optimization with WordPress 3.5
183
 
@@ -194,8 +212,8 @@ Code Snippets has a new website: http://code-snippets.bungeshea.com/
194
  Added import/export feature
195
 
196
  = 1.2 =
197
- Minor improvements |
198
- Added code highlighting |
199
  Plugin data will now be cleaned up when you delete the plugin.
200
 
201
  = 1.1 =
1
  === Code Snippets ===
2
  Contributors: bungeshea
3
  Donate link: http://code-snippets.bungeshea.com/donate/
4
+ Tags: code-snippets, snippets, code, php, network, multisite
5
  Requires at least: 3.3
6
+ Tested up to: 3.5.1
7
+ Stable tag: 1.7
8
+ License: MIT
9
+ License URI: license.txt
10
 
11
  An easy, clean and simple way to add code snippets to your site.
12
 
15
  **Code Snippets** is an easy, clean and simple way to add code snippets to your site. No need to edit to your theme's `functions.php` file again!
16
 
17
  A snippet is a small chunk of PHP code that you can use to extend the functionality of a WordPress-powered website; essentially a mini-plugin with a *lot* less load on your site.
18
+ Most snippet-hosting sites tell you to add snippet code to your active theme's `functions.php` file, which can get rather long and messy after a while.
19
  Code Snippets changes that by providing a GUI interface for adding snippets and **actually running them on your site** as if they were in your theme's `functions.php` file.
20
 
21
  You can use a graphical interface, similar to the Plugins menu, to manage, activate, deactivate, edit and delete your snippets. Easily organise your snippets by adding a name and description using the visual editor. Code Snippets includes built-in syntax highlighting and other features to help you write your code. Snippets can be exported for transfer to another side, either in XML for later importing by the Code Snippets plugin, or in PHP for creating your own plugin or theme.
53
  Further documentation available on the [plugin website](http://code-snippets.bungeshea.com/docs/).
54
 
55
  = Do I need to include the &lt;?php, &lt;? or ?&gt; tags in my snippet? =
56
+ No, just copy all the content inside those tags. If you accidentally forget (or just like being lazy), the tags will be stripped from the beginning and end of the snippet when you save it. You can, however, use those tags *inside* your snippets to start and end HTML sections.
57
 
58
  = Is there a way to add a snippet but not run it right away? =
59
  Yes. Just add it but do not activate it yet.
85
  Yes. Click the checkboxes next to the snippets you want to export, and then choose **Export to PHP** from the Bulk Actions menu and click Apply. The generated PHP file will contain the exported snippets' code, as well as their name and description in comments.
86
 
87
  = Can I run network-wide snippets on a multisite installation? =
88
+ You can run snippets across an entire multisite network by **Network Activating** Code Snippets through the Network Dashboard. You can also activate Code Snippets just on the main site, and then individually on other sites of your choice.
89
+
90
+ = Is there anyway to add categories to snippets? =
91
+ Users of Code Snippets version 1.7 and later can install the [Code Snippets Tags](http://wordpress.org/extend/plugins/code-snippets-tags) plugin for the ability to add tags to snippets, and then later filter the snippets by tag for easier organization.
92
 
93
  = I need help with Code Snippets =
94
  You can get help with Code Snippets either on the [WordPress Support Forums](http://wordpress.org/support/plugin/code-snippets/), on [GithHub](https://github.com/bungeshea/code-snippets/issues), or on [WordPress Answers](http://wordpress.stackexchange.com).
102
  == Screenshots ==
103
 
104
  1. Managing existing snippets
105
+ 2. Adding a new snippet
106
+ 3. Editing a snippet
107
+ 4. Importing snippets from an XML file
108
+ 5. Managing exiting snippets in the MP6 interface
109
 
110
  == Changelog ==
111
 
112
+ = 1.7 =
113
+ * Improved plugin API
114
+ * Fixed a bug with saving snippets per page option ([#](http://wordpress.org/support/topic/plugin-code-snippets-snippets-per-page-does-not-work#post-3710991))
115
+ * Updated CodeMirror to version 3.11
116
+ * Allow plugin to be activated on individual sites on multisite ([#](http://wordpress.org/support/topic/dont-work-at-multisite))
117
+ * Slimmed down the description visual editor
118
+ * Added icon for the new MP6 admin UI ([#](http://wordpress.org/support/topic/icon-disappears-with-mp6))
119
+ * Strip PHP tags from the beginning and end of a snippet in case someone forgets
120
+ * Changed to [MIT license](http://opensource.org/licenses/mit-license.php)
121
+ * Removed HTML, CSS and JavaScript CodeMirror modes that were messing things up
122
+ * Made everything leaner, faster, and better
123
+
124
  = 1.6.1 =
125
  * Fixed a bug with permissions not being applied on install ([#](http://wordpress.org/support/topic/permissions-problem-after-install))
126
  * Fixed a bug in the uninstall method ([#](http://wordpress.org/support/topic/bug-in-delete-script))
193
 
194
  == Upgrade Notice ==
195
 
196
+ = 1.7 =
197
+ Many improvments and optimization. Download "Code Snippets Tags" plugin to add tags to snippets
198
+
199
  = 1.6 =
200
  Improvements and optimization with WordPress 3.5
201
 
212
  Added import/export feature
213
 
214
  = 1.2 =
215
+ Minor improvements |
216
+ Added code highlighting |
217
  Plugin data will now be cleaned up when you delete the plugin.
218
 
219
  = 1.1 =
screenshot-1.jpg CHANGED
Binary file
screenshot-2.jpg CHANGED
Binary file
screenshot-3.jpg CHANGED
Binary file
screenshot-4.jpg CHANGED
Binary file
screenshot-5.jpg CHANGED
Binary file