From 918e7ad682ba48259cf3974b0cb5124750f14065 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 23 Oct 2012 07:06:50 -0200 Subject: [PATCH 01/32] Fixed "inception" media tree bug due to recycle bin auto initializing when it shouldn't --- .../umbraco.presentation/umbraco/Trees/MediaRecycleBin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/MediaRecycleBin.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/MediaRecycleBin.cs index e225a60c55..ec5c8eb436 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/MediaRecycleBin.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/MediaRecycleBin.cs @@ -29,7 +29,7 @@ using umbraco.cms.presentation.Trees; namespace umbraco.cms.presentation.Trees { - [Tree("media", "mediaRecycleBin", "Recycle Bin", "folder.gif", "folder_o.gif")] + [Tree("media", "mediaRecycleBin", "Recycle Bin", "folder.gif", "folder_o.gif", initialize : false)] public class MediaRecycleBin : BaseMediaTree { public MediaRecycleBin(string application) From 51769847ad76b46b8a73d4c46a4dc0909716522a Mon Sep 17 00:00:00 2001 From: dseefeld Date: Mon, 17 Sep 2012 04:06:53 -0200 Subject: [PATCH 02/32] Error: Delete media item with upload property - empty recycle bin fails Fixed #U4-771 --- src/umbraco.cms/businesslogic/Content.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/umbraco.cms/businesslogic/Content.cs b/src/umbraco.cms/businesslogic/Content.cs index 4c24028e60..1c45fbcfc9 100644 --- a/src/umbraco.cms/businesslogic/Content.cs +++ b/src/umbraco.cms/businesslogic/Content.cs @@ -602,7 +602,8 @@ namespace umbraco.cms.businesslogic // don't want to delete the media folder if not using directories. if (UmbracoSettings.UploadAllowDirectories && parentDirectory != fs.GetRelativePath("/")) { - fs.DeleteDirectory(parentDirectory); + //issue U4-771: if there is a parent directory the recursive parameter should be true + fs.DeleteDirectory(parentDirectory, String.IsNullOrEmpty(parentDirectory) == false); } else { From fbd4669da80c0660ddca93198542a6e853bdb3f2 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Mon, 15 Oct 2012 07:16:32 -0200 Subject: [PATCH 03/32] Unpublished icon in tree with published node #U4-387 Fixed --- src/umbraco.cms/businesslogic/Content.cs | 6 ++-- src/umbraco.cms/businesslogic/web/Document.cs | 31 ++++++++++++------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/umbraco.cms/businesslogic/Content.cs b/src/umbraco.cms/businesslogic/Content.cs index 1c45fbcfc9..af5180fc0d 100644 --- a/src/umbraco.cms/businesslogic/Content.cs +++ b/src/umbraco.cms/businesslogic/Content.cs @@ -498,7 +498,7 @@ namespace umbraco.cms.businesslogic protected void CreateContent(ContentType ct) { SqlHelper.ExecuteNonQuery("insert into cmsContent (nodeId,ContentType) values (" + this.Id + "," + ct.Id + ")"); - createNewVersion(); + createNewVersion(DateTime.Now); } @@ -508,7 +508,7 @@ namespace umbraco.cms.businesslogic /// /// /// The new version Id - protected Guid createNewVersion() + protected Guid createNewVersion(DateTime versionDate) { ClearLoadedProperties(); @@ -517,7 +517,7 @@ namespace umbraco.cms.businesslogic // we need to ensure that a version in the db exist before we add related data SqlHelper.ExecuteNonQuery("Insert into cmsContentVersion (ContentId,versionId,versionDate) values (" + this.Id + ",'" + newVersion + "', @updateDate)", - SqlHelper.CreateParameter("@updateDate", DateTime.Now)); + SqlHelper.CreateParameter("@updateDate", versionDate)); foreach (propertytype.PropertyType pt in this.ContentType.PropertyTypes) { diff --git a/src/umbraco.cms/businesslogic/web/Document.cs b/src/umbraco.cms/businesslogic/web/Document.cs index 8add844687..be8dfee1c5 100644 --- a/src/umbraco.cms/businesslogic/web/Document.cs +++ b/src/umbraco.cms/businesslogic/web/Document.cs @@ -402,10 +402,11 @@ namespace umbraco.cms.businesslogic.web tmp.CreateContent(dct); //now create the document data - SqlHelper.ExecuteNonQuery("insert into cmsDocument (newest, nodeId, published, documentUser, versionId, Text) values (1, " + + SqlHelper.ExecuteNonQuery("insert into cmsDocument (newest, nodeId, published, documentUser, versionId, updateDate, Text) values (1, " + tmp.Id + ", 0, " + - u.Id + ", @versionId, @text)", + u.Id + ", @versionId, @updateDate, @text)", SqlHelper.CreateParameter("@versionId", tmp.Version), + SqlHelper.CreateParameter("@updateDate", DateTime.Now), SqlHelper.CreateParameter("@text", tmp.Text)); //read the whole object from the db @@ -996,18 +997,20 @@ namespace umbraco.cms.businesslogic.web _published = true; string tempVersion = Version.ToString(); - Guid newVersion = createNewVersion(); + DateTime versionDate = DateTime.Now; + Guid newVersion = createNewVersion(versionDate); Log.Add(LogTypes.Publish, u, Id, ""); //PPH make sure that there is only 1 newest node, this is important in regard to schedueled publishing... SqlHelper.ExecuteNonQuery("update cmsDocument set newest = 0 where nodeId = " + Id); - SqlHelper.ExecuteNonQuery("insert into cmsDocument (newest, nodeId, published, documentUser, versionId, Text, TemplateId) values (1,@id, 0, @userId, @versionId, @text, @template)", + SqlHelper.ExecuteNonQuery("insert into cmsDocument (newest, nodeId, published, documentUser, versionId, updateDate, Text, TemplateId) values (1,@id, 0, @userId, @versionId, @updateDate, @text, @template)", SqlHelper.CreateParameter("@id", Id), SqlHelper.CreateParameter("@template", _template > 0 ? (object)_template : (object)DBNull.Value), //pass null in if the template doesn't have a valid id SqlHelper.CreateParameter("@userId", u.Id), SqlHelper.CreateParameter("@versionId", newVersion), + SqlHelper.CreateParameter("@updateDate", versionDate), SqlHelper.CreateParameter("@text", Text)); SqlHelper.ExecuteNonQuery("update cmsDocument set published = 0 where nodeId = " + Id); @@ -1064,23 +1067,26 @@ namespace umbraco.cms.businesslogic.web if (!e.Cancel) { - Guid newVersion = createNewVersion(); + DateTime versionDate = DateTime.Now; + Guid newVersion = createNewVersion(versionDate); if (_template != 0) { - SqlHelper.ExecuteNonQuery("insert into cmsDocument (nodeId, published, documentUser, versionId, Text, TemplateId) values (" + + SqlHelper.ExecuteNonQuery("insert into cmsDocument (nodeId, published, documentUser, versionId, updateDate, Text, TemplateId) values (" + Id + ", 0, " + u.Id + ", @versionId, @text, " + _template + ")", SqlHelper.CreateParameter("@versionId", newVersion), + SqlHelper.CreateParameter("@updateDate", versionDate), SqlHelper.CreateParameter("@text", Text)); } else { - SqlHelper.ExecuteNonQuery("insert into cmsDocument (nodeId, published, documentUser, versionId, Text) values (" + + SqlHelper.ExecuteNonQuery("insert into cmsDocument (nodeId, published, documentUser, versionId, updateDate, Text) values (" + Id + ", 0, " + u.Id + ", @versionId, @text )", SqlHelper.CreateParameter("@versionId", newVersion), + SqlHelper.CreateParameter("@updateDate", versionDate), SqlHelper.CreateParameter("@text", Text)); } @@ -1125,12 +1131,14 @@ namespace umbraco.cms.businesslogic.web { _published = true; string tempVersion = Version.ToString(); - Guid newVersion = createNewVersion(); + DateTime versionDate = DateTime.Now; + Guid newVersion = createNewVersion(versionDate); - SqlHelper.ExecuteNonQuery("insert into cmsDocument (nodeId, published, documentUser, versionId, Text) values (" + + SqlHelper.ExecuteNonQuery("insert into cmsDocument (nodeId, published, documentUser, versionId, updateDate, Text) values (" + Id + ", 0, " + u.Id + ", @versionId, @text)", SqlHelper.CreateParameter("@versionId", newVersion), + SqlHelper.CreateParameter("@updateDate", versionDate), SqlHelper.CreateParameter("@text", Text)); SqlHelper.ExecuteNonQuery("update cmsDocument set published = 0 where nodeId = " + Id); @@ -1201,13 +1209,14 @@ namespace umbraco.cms.businesslogic.web /// /// Pending changes means that there have been property/data changes since the last published version. - /// This is determined by the comparing the version date to the updated date. if they are different by .5 seconds, + /// This is determined by the comparing the version date to the updated date. if they are different by more than 2 seconds, /// then this is considered a change. /// /// public bool HasPendingChanges() { - return new TimeSpan(UpdateDate.Ticks - VersionDate.Ticks).TotalMilliseconds > 2000; + double timeDiff = new TimeSpan(UpdateDate.Ticks - VersionDate.Ticks).TotalMilliseconds; + return timeDiff > 2000; } /// From e389dc39a73ac7f514d346fc4cecca3457b2cba1 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Wed, 10 Oct 2012 11:02:59 -0200 Subject: [PATCH 04/32] Upgrade to CodeMirror 2.34 --- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 10 + .../CodeMirror/js/lib/codemirror.css | 35 +- .../CodeMirror/js/lib/codemirror.js | 1494 ++++++++--------- .../CodeMirror/js/lib/util/closetag.js | 70 +- .../CodeMirror/js/lib/util/dialog.js | 5 +- .../CodeMirror/js/lib/util/formatting.js | 410 ++--- .../CodeMirror/js/lib/util/javascript-hint.js | 6 +- .../CodeMirror/js/lib/util/loadmode.js | 2 +- .../CodeMirror/js/lib/util/multiplex.js | 14 +- .../CodeMirror/js/lib/util/overlay.js | 4 +- .../CodeMirror/js/lib/util/pig-hint.js | 12 +- .../js/lib/util/runmode-standalone.js | 90 + .../CodeMirror/js/lib/util/runmode.js | 12 +- .../CodeMirror/js/lib/util/search.js | 8 +- .../CodeMirror/js/lib/util/searchcursor.js | 24 +- .../CodeMirror/js/lib/util/simple-hint.js | 97 ++ .../CodeMirror/js/lib/util/xml-hint.js | 18 +- .../CodeMirror/js/mode/clike/clike.js | 8 +- .../CodeMirror/js/mode/clike/index.html | 1 + .../CodeMirror/js/mode/clike/scala.html | 1 + .../CodeMirror/js/mode/clojure/clojure.js | 17 +- .../CodeMirror/js/mode/clojure/index.html | 1 + .../js/mode/coffeescript/coffeescript.js | 6 +- .../js/mode/coffeescript/index.html | 1 + .../js/mode/commonlisp/commonlisp.js | 101 ++ .../CodeMirror/js/mode/commonlisp/index.html | 165 ++ .../CodeMirror/js/mode/css/css.js | 354 +++- .../CodeMirror/js/mode/css/index.html | 3 + .../CodeMirror/js/mode/css/test.js | 501 ++++++ .../CodeMirror/js/mode/diff/index.html | 1 + .../CodeMirror/js/mode/ecl/ecl.js | 2 +- .../CodeMirror/js/mode/erlang/index.html | 1 + .../CodeMirror/js/mode/gfm/gfm.js | 14 +- .../CodeMirror/js/mode/gfm/index.html | 1 + .../CodeMirror/js/mode/go/go.js | 4 +- .../CodeMirror/js/mode/go/index.html | 1 + .../CodeMirror/js/mode/groovy/groovy.js | 2 +- .../CodeMirror/js/mode/groovy/index.html | 1 + .../CodeMirror/js/mode/haskell/haskell.js | 4 +- .../CodeMirror/js/mode/haskell/index.html | 1 + .../CodeMirror/js/mode/haxe/haxe.js | 11 +- .../CodeMirror/js/mode/haxe/index.html | 1 + .../js/mode/htmlembedded/htmlembedded.js | 12 +- .../js/mode/htmlembedded/index.html | 1 + .../CodeMirror/js/mode/htmlmixed/htmlmixed.js | 25 +- .../CodeMirror/js/mode/htmlmixed/index.html | 1 + .../CodeMirror/js/mode/javascript/index.html | 1 + .../js/mode/javascript/javascript.js | 6 +- .../CodeMirror/js/mode/jinja2/index.html | 1 + .../CodeMirror/js/mode/less/index.html | 126 +- .../CodeMirror/js/mode/less/less.js | 154 +- .../CodeMirror/js/mode/lua/index.html | 1 + .../CodeMirror/js/mode/markdown/index.html | 3 + .../CodeMirror/js/mode/markdown/markdown.js | 156 +- .../CodeMirror/js/mode/markdown/test.js | 1084 ++++++++++++ .../CodeMirror/js/mode/mysql/index.html | 1 + .../CodeMirror/js/mode/ntriples/index.html | 1 + .../CodeMirror/js/mode/ntriples/ntriples.js | 8 +- .../CodeMirror/js/mode/ocaml/ocaml.js | 2 +- .../CodeMirror/js/mode/pascal/index.html | 1 + .../CodeMirror/js/mode/pascal/pascal.js | 2 +- .../CodeMirror/js/mode/perl/index.html | 1 + .../CodeMirror/js/mode/perl/perl.js | 142 +- .../CodeMirror/js/mode/php/index.html | 1 + .../CodeMirror/js/mode/php/php.js | 16 +- .../CodeMirror/js/mode/pig/index.html | 1 + .../CodeMirror/js/mode/pig/pig.js | 6 +- .../CodeMirror/js/mode/plsql/index.html | 1 + .../CodeMirror/js/mode/properties/index.html | 1 + .../CodeMirror/js/mode/python/index.html | 1 + .../CodeMirror/js/mode/python/python.js | 2 +- .../CodeMirror/js/mode/r/index.html | 1 + .../CodeMirror/js/mode/rpm/changes/index.html | 1 + .../CodeMirror/js/mode/rpm/spec/index.html | 1 + .../CodeMirror/js/mode/rst/index.html | 1 + .../CodeMirror/js/mode/ruby/index.html | 1 + .../CodeMirror/js/mode/rust/index.html | 1 + .../CodeMirror/js/mode/scheme/index.html | 1 + .../CodeMirror/js/mode/scheme/scheme.js | 2 +- .../CodeMirror/js/mode/shell/shell.js | 4 +- .../CodeMirror/js/mode/sieve/LICENSE | 23 + .../CodeMirror/js/mode/sieve/index.html | 81 + .../CodeMirror/js/mode/sieve/sieve.js | 156 ++ .../CodeMirror/js/mode/smalltalk/index.html | 1 + .../CodeMirror/js/mode/smalltalk/smalltalk.js | 4 +- .../CodeMirror/js/mode/smarty/index.html | 1 + .../CodeMirror/js/mode/smarty/smarty.js | 4 +- .../CodeMirror/js/mode/sparql/index.html | 1 + .../CodeMirror/js/mode/stex/index.html | 3 + .../CodeMirror/js/mode/stex/stex.js | 2 +- .../CodeMirror/js/mode/stex/test.js | 343 ++++ .../CodeMirror/js/mode/tiddlywiki/index.html | 1 + .../CodeMirror/js/mode/tiki/index.html | 1 + .../CodeMirror/js/mode/tiki/tiki.js | 15 +- .../CodeMirror/js/mode/vb/index.html | 1 + .../CodeMirror/js/mode/vb/vb.js | 4 +- .../CodeMirror/js/mode/vbscript/index.html | 1 + .../CodeMirror/js/mode/velocity/index.html | 1 + .../CodeMirror/js/mode/velocity/velocity.js | 2 +- .../CodeMirror/js/mode/verilog/index.html | 1 + .../CodeMirror/js/mode/verilog/verilog.js | 2 +- .../CodeMirror/js/mode/xml/index.html | 3 +- .../CodeMirror/js/mode/xml/xml.js | 12 +- .../CodeMirror/js/mode/xquery/index.html | 5 +- .../CodeMirror/js/mode/xquery/xquery.js | 11 +- .../CodeMirror/js/mode/yaml/index.html | 1 + 106 files changed, 4536 insertions(+), 1429 deletions(-) create mode 100644 src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/runmode-standalone.js create mode 100644 src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/simple-hint.js create mode 100644 src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/commonlisp/commonlisp.js create mode 100644 src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/commonlisp/index.html create mode 100644 src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/css/test.js create mode 100644 src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/markdown/test.js create mode 100644 src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/sieve/LICENSE create mode 100644 src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/sieve/index.html create mode 100644 src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/sieve/sieve.js create mode 100644 src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/stex/test.js diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 2217987e19..6c53516393 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -384,11 +384,13 @@ + + @@ -397,8 +399,11 @@ + + + @@ -429,6 +434,7 @@ + @@ -469,6 +475,8 @@ + + @@ -478,6 +486,7 @@ + @@ -1465,6 +1474,7 @@ + Web.Template.config diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/codemirror.css b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/codemirror.css index ccfe1f857f..05ad0ed013 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/codemirror.css +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/codemirror.css @@ -1,20 +1,16 @@ .CodeMirror { line-height: 1em; - font-family: Consolas, monospace; + font-family: monospace; /* Necessary so the scrollbar can be absolutely positioned within the wrapper on Lion. */ position: relative; /* This prevents unwanted scrollbars from showing up on the body and wrapper in IE. */ overflow: hidden; - - width: 100%; - height: 100%; } .CodeMirror-scroll { - overflow-x: auto; - overflow-y: hidden; - height: 100%; + overflow: auto; + height: 300px; /* This is needed to prevent an IE[67] bug where the scrolled content is visible outside of the scrolling box. */ position: relative; @@ -23,13 +19,11 @@ /* Vertical scrollbar */ .CodeMirror-scrollbar { - float: right; + position: absolute; + right: 0; top: 0; overflow-x: hidden; overflow-y: scroll; - - /* This corrects for the 1px gap introduced to the left of the scrollbar - by the rule for .CodeMirror-scrollbar-inner. */ - margin-left: -1px; + z-index: 5; } .CodeMirror-scrollbar-inner { /* This needs to have a nonzero width in order for the scrollbar to appear @@ -72,10 +66,6 @@ white-space: pre; cursor: text; } -.CodeMirror-lines * { - /* Necessary for throw-scrolling to decelerate properly on Safari. */ - pointer-events: none; -} .CodeMirror pre { -moz-border-radius: 0; @@ -155,7 +145,7 @@ div.CodeMirror-selected { background: #d9d9d9; } .cm-s-default span.cm-error {color: #f00;} .cm-s-default span.cm-qualifier {color: #555;} .cm-s-default span.cm-builtin {color: #30a;} -.cm-s-default span.cm-bracket {color: #cc7;} +.cm-s-default span.cm-bracket {color: #997;} .cm-s-default span.cm-tag {color: #170;} .cm-s-default span.cm-attribute {color: #00c;} .cm-s-default span.cm-header {color: blue;} @@ -168,5 +158,16 @@ span.cm-em {font-style: italic;} span.cm-emstrong {font-style: italic; font-weight: bold;} span.cm-link {text-decoration: underline;} +span.cm-invalidchar {color: #f00;} + div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} + +@media print { + + /* Hide the cursor when printing */ + .CodeMirror pre.CodeMirror-cursor { + visibility: hidden; + } + +} diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/codemirror.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/codemirror.js index 532dd8974f..5f9724cab7 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/codemirror.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/codemirror.js @@ -1,11 +1,12 @@ -// CodeMirror version 2.32 -// +// CodeMirror version 2.34 + // All functions that need access to the editor's state live inside // the CodeMirror function. Below that, at the bottom of the file, // some utilities are defined. // CodeMirror is the only global var we claim -var CodeMirror = (function() { +window.CodeMirror = (function() { + "use strict"; // This is the function that produces an editor instance. Its // closure is used to store the editor state. function CodeMirror(place, givenOptions) { @@ -15,38 +16,33 @@ var CodeMirror = (function() { if (defaults.hasOwnProperty(opt)) options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt]; + var input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em"); + input.setAttribute("wrap", "off"); input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off"); + // Wraps and hides input textarea + var inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); + // The empty scrollbar content, used solely for managing the scrollbar thumb. + var scrollbarInner = elt("div", null, "CodeMirror-scrollbar-inner"); + // The vertical scrollbar. Horizontal scrolling is handled by the scroller itself. + var scrollbar = elt("div", [scrollbarInner], "CodeMirror-scrollbar"); + // DIVs containing the selection and the actual code + var lineDiv = elt("div"), selectionDiv = elt("div", null, null, "position: relative; z-index: -1"); + // Blinky cursor, and element used to ensure cursor fits at the end of a line + var cursor = elt("pre", "\u00a0", "CodeMirror-cursor"), widthForcer = elt("pre", "\u00a0", "CodeMirror-cursor", "visibility: hidden"); + // Used to measure text size + var measure = elt("div", null, null, "position: absolute; width: 100%; height: 0px; overflow: hidden; visibility: hidden;"); + var lineSpace = elt("div", [measure, cursor, widthForcer, selectionDiv, lineDiv], null, "position: relative; z-index: 0"); + var gutterText = elt("div", null, "CodeMirror-gutter-text"), gutter = elt("div", [gutterText], "CodeMirror-gutter"); + // Moved around its parent to cover visible view + var mover = elt("div", [gutter, elt("div", [lineSpace], "CodeMirror-lines")], null, "position: relative"); + // Set to the height of the text, causes scrolling + var sizer = elt("div", [mover], null, "position: relative"); + // Provides scrolling + var scroller = elt("div", [sizer], "CodeMirror-scroll"); + scroller.setAttribute("tabIndex", "-1"); // The element in which the editor lives. - var wrapper = document.createElement("div"); - wrapper.className = "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : ""); - // This mess creates the base DOM structure for the editor. - wrapper.innerHTML = - '
' + // Wraps and hides input textarea - '
' + - '
' + // The vertical scrollbar. Horizontal scrolling is handled by the scroller itself. - '
' + // The empty scrollbar content, used solely for managing the scrollbar thumb. - '
' + // This must be before the scroll area because it's float-right. - '
' + - '
' + // Set to the height of the text, causes scrolling - '
' + // Moved around its parent to cover visible view - '
' + - // Provides positioning relative to (visible) text origin - '
' + - // Used to measure text size - '
' + - '
 
' + // Absolutely positioned blinky cursor - '' + // Used to force a width - '
' + // DIVs containing the selection and the actual code - '
'; + var wrapper = elt("div", [inputDiv, scrollbar, scroller], "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : "")); if (place.appendChild) place.appendChild(wrapper); else place(wrapper); - // I've never seen more elegant code in my life. - var inputDiv = wrapper.firstChild, input = inputDiv.firstChild, - scroller = wrapper.lastChild, code = scroller.firstChild, - mover = code.firstChild, gutter = mover.firstChild, gutterText = gutter.firstChild, - lineSpace = gutter.nextSibling.firstChild, measure = lineSpace.firstChild, - cursor = measure.nextSibling, widthForcer = cursor.nextSibling, - selectionDiv = widthForcer.nextSibling, lineDiv = selectionDiv.nextSibling, - scrollbar = inputDiv.nextSibling, scrollbarInner = scrollbar.firstChild; + themeChanged(); keyMapChanged(); // Needed to hide big blue blinking cursor on Mobile Safari if (ios) input.style.width = "0px"; @@ -58,33 +54,21 @@ var CodeMirror = (function() { // Needed to handle Tab key in KHTML if (khtml) inputDiv.style.height = "1px", inputDiv.style.position = "absolute"; - // Check for OS X >= 10.7. If so, we need to force a width on the scrollbar, and - // make it overlap the content. (But we only do this if the scrollbar doesn't already - // have a natural width. If the mouse is plugged in or the user sets the system pref - // to always show scrollbars, the scrollbar shouldn't overlap.) - if (mac_geLion) { - scrollbar.className += (overlapScrollbars() ? " cm-sb-overlap" : " cm-sb-nonoverlap"); - } else if (ie_lt8) { - // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). - scrollbar.className += " cm-sb-ie7"; - } - - // Check for problem with IE innerHTML not working when we have a - // P (or similar) parent node. - try { stringWidth("x"); } - catch (e) { - if (e.message.match(/runtime/i)) - e = new Error("A CodeMirror inside a P-style element does not work in Internet Explorer. (innerHTML bug)"); - throw e; - } + // Check for OS X >= 10.7. This has transparent scrollbars, so the + // overlaying of one scrollbar with another won't work. This is a + // temporary hack to simply turn off the overlay scrollbar. See + // issue #727. + if (mac_geLion) { scrollbar.style.zIndex = -2; scrollbar.style.visibility = "hidden"; } + // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). + else if (ie_lt8) scrollbar.style.minWidth = "18px"; // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval. var poll = new Delayed(), highlight = new Delayed(), blinker; // mode holds a mode API object. doc is the tree of Line objects, - // work an array of lines that should be parsed, and history the - // undo history (instance of History constructor). - var mode, doc = new BranchChunk([new LeafChunk([new Line("")])]), work, focused; + // frontier is the point up to which the content has been parsed, + // and history the undo history (instance of History constructor). + var mode, doc = new BranchChunk([new LeafChunk([new Line("")])]), frontier = 0, focused; loadMode(); // The selection. These are always maintained to point at valid // positions. Inverted is used to remember that the user is @@ -92,11 +76,11 @@ var CodeMirror = (function() { var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false}; // Selection-related flags. shiftSelecting obviously tracks // whether the user is holding shift. - var shiftSelecting, lastClick, lastDoubleClick, lastScrollTop = 0, lastScrollLeft = 0, draggingText, + var shiftSelecting, lastClick, lastDoubleClick, lastScrollTop = 0, draggingText, overwrite = false, suppressEdits = false; // Variables used by startOperation/endOperation to track what // happened during the operation. - var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone, + var updateInput, userSelChange, changes, textChanged, selectionChanged, gutterDirty, callbacks; // Current visible range (may be bigger than the view window). var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0; @@ -105,8 +89,9 @@ var CodeMirror = (function() { var bracketHighlighted; // Tracks the maximum line length so that the horizontal scrollbar // can be kept static when scrolling. - var maxLine = "", updateMaxLine = false, maxLineChanged = true; - var tabCache = {}; + var maxLine = getLine(0), updateMaxLine = false, maxLineChanged = true; + var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll + var goalColumn = null; // Initialize the content. operation(function(){setValue(options.value || ""); updateInput = false;})(); @@ -120,12 +105,13 @@ var CodeMirror = (function() { // which point we can't mess with it anymore. Context menu is // handled in onMouseDown for Gecko. if (!gecko) connect(scroller, "contextmenu", onContextMenu); - connect(scroller, "scroll", onScroll); - connect(scrollbar, "scroll", onScroll); + connect(scroller, "scroll", onScrollMain); + connect(scrollbar, "scroll", onScrollBar); connect(scrollbar, "mousedown", function() {if (focused) setTimeout(focusInput, 0);}); - connect(scroller, "mousewheel", onMouseWheel); - connect(scroller, "DOMMouseScroll", onMouseWheel); - connect(window, "resize", function() {updateDisplay(true);}); + var resizeHandler = connect(window, "resize", function() { + if (wrapper.parentNode) updateDisplay(true); + else resizeHandler(); + }, true); connect(input, "keyup", operation(onKeyUp)); connect(input, "input", fastPoll); connect(input, "keydown", operation(onKeyDown)); @@ -133,12 +119,12 @@ var CodeMirror = (function() { connect(input, "focus", onFocus); connect(input, "blur", onBlur); + function drag_(e) { + if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return; + e_stop(e); + } if (options.dragDrop) { connect(scroller, "dragstart", onDragStart); - function drag_(e) { - if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return; - e_stop(e); - } connect(scroller, "dragenter", drag_); connect(scroller, "dragover", drag_); connect(scroller, "drop", operation(onDrop)); @@ -150,7 +136,7 @@ var CodeMirror = (function() { })); // Needed to handle Tab key in KHTML - if (khtml) connect(code, "mouseup", function() { + if (khtml) connect(sizer, "mouseup", function() { if (document.activeElement == input) input.blur(); focusInput(); }); @@ -183,12 +169,14 @@ var CodeMirror = (function() { else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)(); else if (option == "tabSize") updateDisplay(true); else if (option == "keyMap") keyMapChanged(); - if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || option == "theme") { + if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || + option == "theme" || option == "lineNumberFormatter") { gutterChanged(); updateDisplay(true); } }, getOption: function(option) {return options[option];}, + getMode: function() {return mode;}, undo: operation(undo), redo: operation(redo), indentLine: operation(function(n, dir) { @@ -207,13 +195,23 @@ var CodeMirror = (function() { history.undone = histData.undone; }, getHistory: function() { - history.time = 0; - return {done: history.done.concat([]), undone: history.undone.concat([])}; + function cp(arr) { + for (var i = 0, nw = [], nwelt; i < arr.length; ++i) { + nw.push(nwelt = []); + for (var j = 0, elt = arr[i]; j < elt.length; ++j) { + var old = [], cur = elt[j]; + nwelt.push({start: cur.start, added: cur.added, old: old}); + for (var k = 0; k < cur.old.length; ++k) old.push(hlText(cur.old[k])); + } + } + return nw; + } + return {done: cp(history.done), undone: cp(history.undone)}; }, matchBrackets: operation(function(){matchBrackets(true);}), getTokenAt: operation(function(pos) { pos = clipPos(pos); - return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), pos.ch); + return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), options.tabSize, pos.ch); }), getStateAfter: function(line) { line = clipLine(line == null ? doc.size - 1: line); @@ -250,15 +248,16 @@ var CodeMirror = (function() { return line; }, lineInfo: lineInfo, + getViewport: function() { return {from: showingFrom, to: showingTo};}, addWidget: function(pos, node, scroll, vert, horiz) { pos = localCoords(clipPos(pos)); var top = pos.yBot, left = pos.x; node.style.position = "absolute"; - code.appendChild(node); + sizer.appendChild(node); if (vert == "over") top = pos.y; else if (vert == "near") { var vspace = Math.max(scroller.offsetHeight, doc.height * textHeight()), - hspace = Math.max(code.clientWidth, lineSpace.clientWidth) - paddingLeft(); + hspace = Math.max(sizer.clientWidth, lineSpace.clientWidth) - paddingLeft(); if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight) top = pos.y - node.offsetHeight; if (left + node.offsetWidth > hspace) @@ -267,11 +266,11 @@ var CodeMirror = (function() { node.style.top = (top + paddingTop()) + "px"; node.style.left = node.style.right = ""; if (horiz == "right") { - left = code.clientWidth - node.offsetWidth; + left = sizer.clientWidth - node.offsetWidth; node.style.right = "0px"; } else { if (horiz == "left") left = 0; - else if (horiz == "middle") left = (code.clientWidth - node.offsetWidth) / 2; + else if (horiz == "middle") left = (sizer.clientWidth - node.offsetWidth) / 2; node.style.left = (left + paddingLeft()) + "px"; } if (scroll) @@ -339,7 +338,7 @@ var CodeMirror = (function() { }, scrollTo: function(x, y) { if (x != null) scroller.scrollLeft = x; - if (y != null) scrollbar.scrollTop = y; + if (y != null) scrollbar.scrollTop = scroller.scrollTop = y; updateDisplay([]); }, getScrollInfo: function() { @@ -353,6 +352,7 @@ var CodeMirror = (function() { } if (width != null) wrapper.style.width = interpret(width); if (height != null) scroller.style.height = interpret(height); + instance.refresh(); }, operation: function(f){return operation(f)();}, @@ -375,6 +375,12 @@ var CodeMirror = (function() { for (var n = line; n; n = n.parent) n.height += diff; } + function lineContent(line, wrapAt) { + if (!line.styles) + line.highlight(mode, line.stateAfter = getStateBefore(lineNo(line)), options.tabSize); + return line.getContent(options.tabSize, wrapAt, options.lineWrapping); + } + function setValue(code) { var top = {line: 0, ch: 0}; updateLines(top, {line: doc.size - 1, ch: getLine(doc.size-1).text.length}, @@ -387,25 +393,30 @@ var CodeMirror = (function() { return text.join(lineSep || "\n"); } - function onScroll(e) { - if (scroller.scrollTop) { - scrollbar.scrollTop += scroller.scrollTop; - scroller.scrollTop = 0; - } - if (lastScrollTop != scrollbar.scrollTop || lastScrollLeft != scroller.scrollLeft) { - lastScrollTop = scrollbar.scrollTop; - lastScrollLeft = scroller.scrollLeft; + function onScrollBar(e) { + if (scrollbar.scrollTop != lastScrollTop) { + lastScrollTop = scroller.scrollTop = scrollbar.scrollTop; updateDisplay([]); - if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px"; - if (options.onScroll) options.onScroll(instance); } } + function onScrollMain(e) { + if (options.fixedGutter && gutter.style.left != scroller.scrollLeft + "px") + gutter.style.left = scroller.scrollLeft + "px"; + if (scroller.scrollTop != lastScrollTop) { + lastScrollTop = scroller.scrollTop; + if (scrollbar.scrollTop != lastScrollTop) + scrollbar.scrollTop = lastScrollTop; + updateDisplay([]); + } + if (options.onScroll) options.onScroll(instance); + } + function onMouseDown(e) { setShift(e_prop(e, "shiftKey")); // Check whether this is a click in a widget for (var n = e_target(e); n != wrapper; n = n.parentNode) - if (n.parentNode == code && n != mover) return; + if (n.parentNode == sizer && n != mover) return; // See if this is a click in the gutter for (var n = e_target(e); n != wrapper; n = n.parentNode) @@ -448,21 +459,21 @@ var CodeMirror = (function() { setSelectionUser(word.from, word.to); } else { lastClick = {time: now, pos: start}; } + function dragEnd(e2) { + if (webkit) scroller.draggable = false; + draggingText = false; + up(); drop(); + if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) { + e_preventDefault(e2); + setCursor(start.line, start.ch, true); + focusInput(); + } + } var last = start, going; if (options.dragDrop && dragAndDrop && !options.readOnly && !posEq(sel.from, sel.to) && !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") { // Let the drag handler handle this. if (webkit) scroller.draggable = true; - function dragEnd(e2) { - if (webkit) scroller.draggable = false; - draggingText = false; - up(); drop(); - if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) { - e_preventDefault(e2); - setCursor(start.line, start.ch, true); - focusInput(); - } - } var up = connect(document, "mouseup", operation(dragEnd), true); var drop = connect(scroller, "drop", operation(dragEnd), true); draggingText = true; @@ -525,11 +536,12 @@ var CodeMirror = (function() { } function onDrop(e) { if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return; - e.preventDefault(); + e_preventDefault(e); var pos = posFromMouse(e, true), files = e.dataTransfer.files; if (!pos || options.readOnly) return; if (files && files.length && window.FileReader && window.File) { - function loadFile(file, i) { + var n = files.length, text = Array(n), read = 0; + var loadFile = function(file, i) { var reader = new FileReader; reader.onload = function() { text[i] = reader.result; @@ -542,8 +554,7 @@ var CodeMirror = (function() { } }; reader.readAsText(file); - } - var n = files.length, text = Array(n), read = 0; + }; for (var i = 0; i < n; ++i) loadFile(files[i], i); } else { // Don't do a replace if the drop happened inside of the selected text. @@ -566,13 +577,10 @@ var CodeMirror = (function() { function onDragStart(e) { var txt = getSelection(); e.dataTransfer.setData("Text", txt); - + // Use dummy image instead of default browsers image. - if (gecko || chrome || opera) { - var img = document.createElement('img'); - img.scr = 'data:image/gif;base64,R0lGODdhAgACAIAAAAAAAP///ywAAAAAAgACAAACAoRRADs='; //1x1 image - e.dataTransfer.setDragImage(img, 0, 0); - } + if (e.dataTransfer.setDragImage) + e.dataTransfer.setDragImage(elt('img'), 0, 0); } function doHandleBinding(bound, dropShift) { @@ -594,6 +602,7 @@ var CodeMirror = (function() { } return true; } + var maybeTransition; function handleKeyBinding(e) { // Handle auto keymap transitions var startMap = getKeyMap(options.keyMap), next = startMap.auto; @@ -605,10 +614,11 @@ var CodeMirror = (function() { }, 50); var name = keyNames[e_prop(e, "keyCode")], handled = false; + var flipCtrlCmd = opera && mac; if (name == null || e.altGraphKey) return false; if (e_prop(e, "altKey")) name = "Alt-" + name; - if (e_prop(e, "ctrlKey")) name = "Ctrl-" + name; - if (e_prop(e, "metaKey")) name = "Cmd-" + name; + if (e_prop(e, flipCtrlCmd ? "metaKey" : "ctrlKey")) name = "Ctrl-" + name; + if (e_prop(e, flipCtrlCmd ? "ctrlKey" : "metaKey")) name = "Cmd-" + name; var stopped = false; function stop() { stopped = true; } @@ -640,7 +650,7 @@ var CodeMirror = (function() { return handled; } - var lastStoppedKey = null, maybeTransition; + var lastStoppedKey = null; function onKeyDown(e) { if (!focused) onFocus(); if (ie && e.keyCode == 27) { e.returnValue = false; } @@ -684,7 +694,6 @@ var CodeMirror = (function() { focused = true; if (scroller.className.search(/\bCodeMirror-focused\b/) == -1) scroller.className += " CodeMirror-focused"; - if (!leaveInputAlone) resetInput(true); } slowPoll(); restartBlink(); @@ -703,53 +712,20 @@ var CodeMirror = (function() { setTimeout(function() {if (!focused) shiftSelecting = null;}, 150); } - function chopDelta(delta) { - // Make sure we always scroll a little bit for any nonzero delta. - if (delta > 0.0 && delta < 1.0) return 1; - else if (delta > -1.0 && delta < 0.0) return -1; - else return Math.round(delta); - } - - function onMouseWheel(e) { - var deltaX = 0, deltaY = 0; - if (e.type == "DOMMouseScroll") { // Firefox - var delta = -e.detail * 8.0; - if (e.axis == e.HORIZONTAL_AXIS) deltaX = delta; - else if (e.axis == e.VERTICAL_AXIS) deltaY = delta; - } else if (e.wheelDeltaX !== undefined && e.wheelDeltaY !== undefined) { // WebKit - deltaX = e.wheelDeltaX / 3.0; - deltaY = e.wheelDeltaY / 3.0; - } else if (e.wheelDelta !== undefined) { // IE or Opera - deltaY = e.wheelDelta / 3.0; - } - - var scrolled = false; - deltaX = chopDelta(deltaX); - deltaY = chopDelta(deltaY); - if ((deltaX > 0 && scroller.scrollLeft > 0) || - (deltaX < 0 && scroller.scrollLeft + scroller.clientWidth < scroller.scrollWidth)) { - scroller.scrollLeft -= deltaX; - scrolled = true; - } - if ((deltaY > 0 && scrollbar.scrollTop > 0) || - (deltaY < 0 && scrollbar.scrollTop + scrollbar.clientHeight < scrollbar.scrollHeight)) { - scrollbar.scrollTop -= deltaY; - scrolled = true; - } - if (scrolled) e_stop(e); - } - // Replace the range from from to to by the strings in newText. // Afterwards, set the selection to selFrom, selTo. function updateLines(from, to, newText, selFrom, selTo) { if (suppressEdits) return; + var old = []; + doc.iter(from.line, to.line + 1, function(line) { + old.push(newHL(line.text, line.markedSpans)); + }); if (history) { - var old = []; - doc.iter(from.line, to.line + 1, function(line) { old.push(line.text); }); history.addChange(from.line, newText.length, old); while (history.done.length > options.undoDepth) history.done.shift(); } - updateLinesNoUndo(from, to, newText, selFrom, selTo); + var lines = updateMarkedSpans(hlSpans(old[0]), hlSpans(lst(old)), from.ch, to.ch, newText); + updateLinesNoUndo(from, to, lines, selFrom, selTo); } function unredoHelper(from, to) { if (!from.length) return; @@ -757,11 +733,12 @@ var CodeMirror = (function() { for (var i = set.length - 1; i >= 0; i -= 1) { var change = set[i]; var replaced = [], end = change.start + change.added; - doc.iter(change.start, end, function(line) { replaced.push(line.text); }); + doc.iter(change.start, end, function(line) { replaced.push(newHL(line.text, line.markedSpans)); }); out.push({start: change.start, added: change.old.length, old: replaced}); var pos = {line: change.start + change.old.length - 1, - ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])}; - updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, change.old, pos, pos); + ch: editEnd(hlText(lst(replaced)), hlText(lst(change.old)))}; + updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, + change.old, pos, pos); } updateInput = true; to.push(out); @@ -769,95 +746,86 @@ var CodeMirror = (function() { function undo() {unredoHelper(history.done, history.undone);} function redo() {unredoHelper(history.undone, history.done);} - function updateLinesNoUndo(from, to, newText, selFrom, selTo) { + function updateLinesNoUndo(from, to, lines, selFrom, selTo) { if (suppressEdits) return; - var recomputeMaxLength = false, maxLineLength = maxLine.length; + var recomputeMaxLength = false, maxLineLength = maxLine.text.length; if (!options.lineWrapping) doc.iter(from.line, to.line + 1, function(line) { if (!line.hidden && line.text.length == maxLineLength) {recomputeMaxLength = true; return true;} }); - if (from.line != to.line || newText.length > 1) gutterDirty = true; + if (from.line != to.line || lines.length > 1) gutterDirty = true; var nlines = to.line - from.line, firstLine = getLine(from.line), lastLine = getLine(to.line); - // First adjust the line structure, taking some care to leave highlighting intact. - if (from.ch == 0 && to.ch == 0 && newText[newText.length - 1] == "") { + var lastHL = lst(lines); + + // First adjust the line structure + if (from.ch == 0 && to.ch == 0 && hlText(lastHL) == "") { // This is a whole-line replace. Treated specially to make // sure line objects move the way they are supposed to. var added = [], prevLine = null; - if (from.line) { - prevLine = getLine(from.line - 1); - prevLine.fixMarkEnds(lastLine); - } else lastLine.fixMarkStarts(); - for (var i = 0, e = newText.length - 1; i < e; ++i) - added.push(Line.inheritMarks(newText[i], prevLine)); + for (var i = 0, e = lines.length - 1; i < e; ++i) + added.push(new Line(hlText(lines[i]), hlSpans(lines[i]))); + lastLine.update(lastLine.text, hlSpans(lastHL)); if (nlines) doc.remove(from.line, nlines, callbacks); if (added.length) doc.insert(from.line, added); } else if (firstLine == lastLine) { - if (newText.length == 1) - firstLine.replace(from.ch, to.ch, newText[0]); - else { - lastLine = firstLine.split(to.ch, newText[newText.length-1]); - firstLine.replace(from.ch, null, newText[0]); - firstLine.fixMarkEnds(lastLine); - var added = []; - for (var i = 1, e = newText.length - 1; i < e; ++i) - added.push(Line.inheritMarks(newText[i], firstLine)); - added.push(lastLine); + if (lines.length == 1) { + firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]) + firstLine.text.slice(to.ch), hlSpans(lines[0])); + } else { + for (var added = [], i = 1, e = lines.length - 1; i < e; ++i) + added.push(new Line(hlText(lines[i]), hlSpans(lines[i]))); + added.push(new Line(hlText(lastHL) + firstLine.text.slice(to.ch), hlSpans(lastHL))); + firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0])); doc.insert(from.line + 1, added); } - } else if (newText.length == 1) { - firstLine.replace(from.ch, null, newText[0]); - lastLine.replace(null, to.ch, ""); - firstLine.append(lastLine); + } else if (lines.length == 1) { + firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]) + lastLine.text.slice(to.ch), hlSpans(lines[0])); doc.remove(from.line + 1, nlines, callbacks); } else { var added = []; - firstLine.replace(from.ch, null, newText[0]); - lastLine.replace(null, to.ch, newText[newText.length-1]); - firstLine.fixMarkEnds(lastLine); - for (var i = 1, e = newText.length - 1; i < e; ++i) - added.push(Line.inheritMarks(newText[i], firstLine)); + firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0])); + lastLine.update(hlText(lastHL) + lastLine.text.slice(to.ch), hlSpans(lastHL)); + for (var i = 1, e = lines.length - 1; i < e; ++i) + added.push(new Line(hlText(lines[i]), hlSpans(lines[i]))); if (nlines > 1) doc.remove(from.line + 1, nlines - 1, callbacks); doc.insert(from.line + 1, added); } if (options.lineWrapping) { var perLine = Math.max(5, scroller.clientWidth / charWidth() - 3); - doc.iter(from.line, from.line + newText.length, function(line) { + doc.iter(from.line, from.line + lines.length, function(line) { if (line.hidden) return; var guess = Math.ceil(line.text.length / perLine) || 1; if (guess != line.height) updateLineHeight(line, guess); }); } else { - doc.iter(from.line, from.line + newText.length, function(line) { + doc.iter(from.line, from.line + lines.length, function(line) { var l = line.text; if (!line.hidden && l.length > maxLineLength) { - maxLine = l; maxLineLength = l.length; maxLineChanged = true; + maxLine = line; maxLineLength = l.length; maxLineChanged = true; recomputeMaxLength = false; } }); if (recomputeMaxLength) updateMaxLine = true; } - // Add these lines to the work array, so that they will be - // highlighted. Adjust work lines if lines were added/removed. - var newWork = [], lendiff = newText.length - nlines - 1; - for (var i = 0, l = work.length; i < l; ++i) { - var task = work[i]; - if (task < from.line) newWork.push(task); - else if (task > to.line) newWork.push(task + lendiff); - } - var hlEnd = from.line + Math.min(newText.length, 500); - highlightLines(from.line, hlEnd); - newWork.push(hlEnd); - work = newWork; - startWorker(100); + // Adjust frontier, schedule worker + frontier = Math.min(frontier, from.line); + startWorker(400); + + var lendiff = lines.length - nlines - 1; // Remember that these lines changed, for updating the display changes.push({from: from.line, to: to.line + 1, diff: lendiff}); - var changeObj = {from: from, to: to, text: newText}; - if (textChanged) { - for (var cur = textChanged; cur.next; cur = cur.next) {} - cur.next = changeObj; - } else textChanged = changeObj; + if (options.onChange) { + // Normalize lines to contain only strings, since that's what + // the change event handler expects + for (var i = 0; i < lines.length; ++i) + if (typeof lines[i] != "string") lines[i] = lines[i].text; + var changeObj = {from: from, to: to, text: lines}; + if (textChanged) { + for (var cur = textChanged; cur.next; cur = cur.next) {} + cur.next = changeObj; + } else textChanged = changeObj; + } // Update the selection function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;} @@ -867,46 +835,43 @@ var CodeMirror = (function() { function needsScrollbar() { var realHeight = doc.height * textHeight() + 2 * paddingTop(); - return realHeight - 1 > scroller.offsetHeight ? realHeight : false; + return realHeight * .99 > scroller.offsetHeight ? realHeight : false; } function updateVerticalScroll(scrollTop) { var scrollHeight = needsScrollbar(); scrollbar.style.display = scrollHeight ? "block" : "none"; if (scrollHeight) { - scrollbarInner.style.height = scrollHeight + "px"; - scrollbar.style.height = scroller.offsetHeight + "px"; - if (scrollTop != null) scrollbar.scrollTop = scrollTop; + scrollbarInner.style.height = sizer.style.minHeight = scrollHeight + "px"; + scrollbar.style.height = scroller.clientHeight + "px"; + if (scrollTop != null) { + scrollbar.scrollTop = scroller.scrollTop = scrollTop; + // 'Nudge' the scrollbar to work around a Webkit bug where, + // in some situations, we'd end up with a scrollbar that + // reported its scrollTop (and looked) as expected, but + // *behaved* as if it was still in a previous state (i.e. + // couldn't scroll up, even though it appeared to be at the + // bottom). + if (webkit) setTimeout(function() { + if (scrollbar.scrollTop != scrollTop) return; + scrollbar.scrollTop = scrollTop + (scrollTop ? -1 : 1); + scrollbar.scrollTop = scrollTop; + }, 0); + } + } else { + sizer.style.minHeight = ""; } // Position the mover div to align with the current virtual scroll position - mover.style.top = (displayOffset * textHeight() - scrollbar.scrollTop) + "px"; - } - - // On Mac OS X Lion and up, detect whether the mouse is plugged in by measuring - // the width of a div with a scrollbar in it. If the width is <= 1, then - // the mouse isn't plugged in and scrollbars should overlap the content. - function overlapScrollbars() { - var tmpSb = document.createElement('div'), - tmpSbInner = document.createElement('div'); - tmpSb.className = "CodeMirror-scrollbar"; - tmpSb.style.cssText = "position: absolute; left: -9999px; height: 100px;"; - tmpSbInner.className = "CodeMirror-scrollbar-inner"; - tmpSbInner.style.height = "200px"; - tmpSb.appendChild(tmpSbInner); - - document.body.appendChild(tmpSb); - var result = (tmpSb.offsetWidth <= 1); - document.body.removeChild(tmpSb); - return result; + mover.style.top = displayOffset * textHeight() + "px"; } function computeMaxLength() { - var maxLineLength = 0; - maxLine = ""; maxLineChanged = true; - doc.iter(0, doc.size, function(line) { + maxLine = getLine(0); maxLineChanged = true; + var maxLineLength = maxLine.text.length; + doc.iter(1, doc.size, function(line) { var l = line.text; if (!line.hidden && l.length > maxLineLength) { - maxLineLength = l.length; maxLine = l; + maxLineLength = l.length; maxLine = line; } }); updateMaxLine = false; @@ -922,7 +887,7 @@ var CodeMirror = (function() { var line = pos.line + code.length - (to.line - from.line) - 1; var ch = pos.ch; if (pos.line == to.line) - ch += code[code.length-1].length - (to.ch - (to.line == from.line ? from.ch : 0)); + ch += lst(code).length - (to.ch - (to.line == from.line ? from.ch : 0)); return {line: line, ch: ch}; } var end; @@ -940,7 +905,7 @@ var CodeMirror = (function() { }); } function replaceRange1(code, from, to, computeSel) { - var endch = code.length == 1 ? code[0].length + from.ch : code[code.length-1].length; + var endch = code.length == 1 ? code[0].length + from.ch : lst(code).length; var newSel = computeSel({line: from.line + code.length - 1, ch: endch}); updateLines(from, to, code, newSel.from, newSel.to); } @@ -957,25 +922,20 @@ var CodeMirror = (function() { return getRange(sel.from, sel.to, lineSep); } - var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll function slowPoll() { if (pollingFast) return; poll.set(options.pollInterval, function() { - startOperation(); readInput(); if (focused) slowPoll(); - endOperation(); }); } function fastPoll() { var missed = false; pollingFast = true; function p() { - startOperation(); var changed = readInput(); if (!changed && !missed) {missed = true; poll.set(60, p);} else {pollingFast = false; slowPoll();} - endOperation(); } poll.set(20, p); } @@ -987,9 +947,10 @@ var CodeMirror = (function() { // supported or compatible enough yet to rely on.) var prevInput = ""; function readInput() { - if (leaveInputAlone || !focused || hasSelection(input) || options.readOnly) return false; + if (!focused || hasSelection(input) || options.readOnly) return false; var text = input.value; if (text == prevInput) return false; + if (!nestedOperation) startOperation(); shiftSelecting = null; var same = 0, l = Math.min(prevInput.length, text.length); while (same < l && prevInput[same] == text[same]) ++same; @@ -1000,13 +961,14 @@ var CodeMirror = (function() { replaceSelection(text.slice(same), "end"); if (text.length > 1000) { input.value = prevInput = ""; } else prevInput = text; + if (!nestedOperation) endOperation(); return true; } function resetInput(user) { if (!posEq(sel.from, sel.to)) { prevInput = ""; input.value = getSelection(); - selectInput(input); + if (focused) selectInput(input); } else if (user) prevInput = input.value = ""; } @@ -1014,16 +976,23 @@ var CodeMirror = (function() { if (options.readOnly != "nocursor") input.focus(); } - function scrollEditorIntoView() { - var rect = cursor.getBoundingClientRect(); - // IE returns bogus coordinates when the instance sits inside of an iframe and the cursor is hidden - if (ie && rect.top == rect.bottom) return; - var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight); - if (rect.top < 0 || rect.bottom > winH) scrollCursorIntoView(); - } function scrollCursorIntoView() { var coords = calculateCursorCoords(); - return scrollIntoView(coords.x, coords.y, coords.x, coords.yBot); + scrollIntoView(coords.x, coords.y, coords.x, coords.yBot); + if (!focused) return; + var box = sizer.getBoundingClientRect(), doScroll = null; + if (coords.y + box.top < 0) doScroll = true; + else if (coords.y + box.top + textHeight() > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false; + if (doScroll != null) { + var hidden = cursor.style.display == "none"; + if (hidden) { + cursor.style.display = ""; + cursor.style.left = coords.x + "px"; + cursor.style.top = (coords.y - displayOffset) + "px"; + } + cursor.scrollIntoView(doScroll); + if (hidden) cursor.style.display = "none"; + } } function calculateCursorCoords() { var cursor = localCoords(sel.inverted ? sel.from : sel.to); @@ -1031,17 +1000,16 @@ var CodeMirror = (function() { return {x: x, y: cursor.y, yBot: cursor.yBot}; } function scrollIntoView(x1, y1, x2, y2) { - var scrollPos = calculateScrollPos(x1, y1, x2, y2), scrolled = false; - if (scrollPos.scrollLeft != null) {scroller.scrollLeft = scrollPos.scrollLeft; scrolled = true;} - if (scrollPos.scrollTop != null) {scrollbar.scrollTop = scrollPos.scrollTop; scrolled = true;} - if (scrolled && options.onScroll) options.onScroll(instance); + var scrollPos = calculateScrollPos(x1, y1, x2, y2); + if (scrollPos.scrollLeft != null) {scroller.scrollLeft = scrollPos.scrollLeft;} + if (scrollPos.scrollTop != null) {scrollbar.scrollTop = scroller.scrollTop = scrollPos.scrollTop;} } function calculateScrollPos(x1, y1, x2, y2) { var pl = paddingLeft(), pt = paddingTop(); y1 += pt; y2 += pt; x1 += pl; x2 += pl; var screen = scroller.clientHeight, screentop = scrollbar.scrollTop, result = {}; - var docBottom = scroller.scrollHeight; - var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;; + var docBottom = needsScrollbar() || Infinity; + var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10; if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1); else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen; @@ -1113,8 +1081,13 @@ var CodeMirror = (function() { // This is just a bogus formula that detects when the editor is // resized or the font size changes. if (different) lastSizeC = scroller.clientHeight + th; + if (from != showingFrom || to != showingTo && options.onViewportChange) + setTimeout(function(){ + if (options.onViewportChange) options.onViewportChange(instance, from, to); + }); showingFrom = from; showingTo = to; displayOffset = heightAtLine(doc, from); + startWorker(100); // Since this is all rather error prone, it is honoured with the // only assertion in the whole file. @@ -1125,6 +1098,10 @@ var CodeMirror = (function() { function checkHeights() { var curNode = lineDiv.firstChild, heightChanged = false; doc.iter(showingFrom, showingTo, function(line) { + // Work around bizarro IE7 bug where, sometimes, our curNode + // is magically replaced with a new node in the DOM, leaving + // us with a reference to an orphan (nextSibling-less) node. + if (!curNode) return; if (!line.hidden) { var height = Math.round(curNode.offsetHeight / th) || 1; if (line.height != height) { @@ -1137,16 +1114,7 @@ var CodeMirror = (function() { return heightChanged; } - if (options.lineWrapping) { - checkHeights(); - var scrollHeight = needsScrollbar(); - var shouldHaveScrollbar = scrollHeight ? "block" : "none"; - if (scrollbar.style.display != shouldHaveScrollbar) { - scrollbar.style.display = shouldHaveScrollbar; - if (scrollHeight) scrollbarInner.style.height = scrollHeight + "px"; - checkHeights(); - } - } + if (options.lineWrapping) checkHeights(); gutter.style.display = gutterDisplay; if (different || gutterDirty) { @@ -1183,14 +1151,14 @@ var CodeMirror = (function() { } function patchDisplay(from, to, intact) { + function killNode(node) { + var tmp = node.nextSibling; + node.parentNode.removeChild(node); + return tmp; + } // The first pass removes the DOM nodes that aren't intact. - if (!intact.length) lineDiv.innerHTML = ""; + if (!intact.length) removeChildren(lineDiv); else { - function killNode(node) { - var tmp = node.nextSibling; - node.parentNode.removeChild(node); - return tmp; - } var domPos = 0, curNode = lineDiv.firstChild, n; for (var i = 0; i < intact.length; ++i) { var cur = intact[i]; @@ -1201,21 +1169,20 @@ var CodeMirror = (function() { } // This pass fills in the lines that actually changed. var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from; - var scratch = document.createElement("div"); doc.iter(from, to, function(line) { if (nextIntact && nextIntact.to == j) nextIntact = intact.shift(); if (!nextIntact || nextIntact.from > j) { - if (line.hidden) var html = scratch.innerHTML = "
";
+          if (line.hidden) var lineElement = elt("pre");
           else {
-            var html = ''
-              + line.getHTML(makeTab) + '';
+            var lineElement = lineContent(line);
+            if (line.className) lineElement.className = line.className;
             // Kludge to make sure the styled element lies behind the selection (by z-index)
-            if (line.bgClassName)
-              html = '
 
' + html + "
"; + if (line.bgClassName) { + var pre = elt("pre", "\u00a0", line.bgClassName, "position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: -2"); + lineElement = elt("div", [pre, lineElement], null, "position: relative"); + } } - scratch.innerHTML = html; - lineDiv.insertBefore(scratch.firstChild, curNode); + lineDiv.insertBefore(lineElement, curNode); } else { curNode = curNode.nextSibling; } @@ -1227,10 +1194,10 @@ var CodeMirror = (function() { if (!options.gutter && !options.lineNumbers) return; var hText = mover.offsetHeight, hEditor = scroller.clientHeight; gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px"; - var html = [], i = showingFrom, normalNode; + var fragment = document.createDocumentFragment(), i = showingFrom, normalNode; doc.iter(showingFrom, Math.max(showingTo, showingFrom + 1), function(line) { if (line.hidden) { - html.push("
");
+          fragment.appendChild(elt("pre"));
         } else {
           var marker = line.gutterMarker;
           var text = options.lineNumbers ? options.lineNumberFormatter(i + options.firstLineNumber) : null;
@@ -1238,15 +1205,18 @@ var CodeMirror = (function() {
             text = marker.text.replace("%N%", text != null ? text : "");
           else if (text == null)
             text = "\u00a0";
-          html.push((marker && marker.style ? '
' : "
"), text);
-          for (var j = 1; j < line.height; ++j) html.push("
 "); - html.push("
"); + var markerElement = fragment.appendChild(elt("pre", null, marker && marker.style)); + markerElement.innerHTML = text; + for (var j = 1; j < line.height; ++j) { + markerElement.appendChild(elt("br")); + markerElement.appendChild(document.createTextNode("\u00a0")); + } if (!marker) normalNode = i; } ++i; }); gutter.style.display = "none"; - gutterText.innerHTML = html.join(""); + removeChildrenAndAdd(gutterText, fragment); // Make sure scrolling doesn't cause number gutter size to pop if (normalNode != null && options.lineNumbers) { var node = gutterText.childNodes[normalNode - showingFrom]; @@ -1274,15 +1244,15 @@ var CodeMirror = (function() { cursor.style.display = ""; selectionDiv.style.display = "none"; } else { - var sameLine = fromPos.y == toPos.y, html = ""; + var sameLine = fromPos.y == toPos.y, fragment = document.createDocumentFragment(); var clientWidth = lineSpace.clientWidth || lineSpace.offsetWidth; var clientHeight = lineSpace.clientHeight || lineSpace.offsetHeight; - function add(left, top, right, height) { + var add = function(left, top, right, height) { var rstyle = quirksMode ? "width: " + (!right ? clientWidth : clientWidth - right - left) + "px" : "right: " + right + "px"; - html += '
'; - } + fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left + + "px; top: " + top + "px; " + rstyle + "; height: " + height + "px")); + }; if (sel.from.ch && fromPos.y >= 0) { var right = sameLine ? clientWidth - toPos.x : 0; add(fromPos.x, fromPos.y, right, th); @@ -1293,7 +1263,7 @@ var CodeMirror = (function() { add(0, middleStart, 0, middleHeight); if ((!sameLine || !sel.from.ch) && toPos.y < clientHeight - .5 * th) add(0, toPos.y, clientWidth - toPos.x, th); - selectionDiv.innerHTML = html; + removeChildrenAndAdd(selectionDiv, fragment); cursor.style.display = "none"; selectionDiv.style.display = ""; } @@ -1425,13 +1395,16 @@ var CodeMirror = (function() { else replaceRange("", sel.from, findPosH(dir, unit)); userSelChange = true; } - var goalColumn = null; function moveV(dir, unit) { var dist = 0, pos = localCoords(sel.inverted ? sel.from : sel.to, true); if (goalColumn != null) pos.x = goalColumn; - if (unit == "page") dist = Math.min(scroller.clientHeight, window.innerHeight || document.documentElement.clientHeight); - else if (unit == "line") dist = textHeight(); - var target = coordsChar(pos.x, pos.y + dist * dir + 2); + if (unit == "page") { + var screen = Math.min(scroller.clientHeight, window.innerHeight || document.documentElement.clientHeight); + var target = coordsChar(pos.x, pos.y + screen * dir); + } else if (unit == "line") { + var th = textHeight(); + var target = coordsChar(pos.x, pos.y + .5 * th + dir * th); + } if (unit == "page") scrollbar.scrollTop += localCoords(target, true).y - pos.y; setCursor(target.line, target.ch, true); goalColumn = pos.x; @@ -1440,10 +1413,15 @@ var CodeMirror = (function() { function findWordAt(pos) { var line = getLine(pos.line).text; var start = pos.ch, end = pos.ch; - var check = isWordChar(line.charAt(start < line.length ? start : start - 1)) ? - isWordChar : function(ch) {return !isWordChar(ch);}; - while (start > 0 && check(line.charAt(start - 1))) --start; - while (end < line.length && check(line.charAt(end))) ++end; + if (line) { + if (pos.after === false || end == line.length) --start; else ++end; + var startChar = line.charAt(start); + var check = isWordChar(startChar) ? isWordChar : + /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} : + function(ch) {return !/\s/.test(ch) && !isWordChar(ch);}; + while (start > 0 && check(line.charAt(start - 1))) --start; + while (end < line.length && check(line.charAt(end))) ++end; + } return {from: {line: pos.line, ch: start}, to: {line: pos.line, ch: end}}; } function selectLine(line) { @@ -1480,16 +1458,17 @@ var CodeMirror = (function() { var indentString = "", pos = 0; if (options.indentWithTabs) for (var i = Math.floor(indentation / options.tabSize); i; --i) {pos += options.tabSize; indentString += "\t";} - while (pos < indentation) {++pos; indentString += " ";} + if (pos < indentation) indentString += spaceStr(indentation - pos); - replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length}); + if (indentString != curSpaceString) + replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length}); } function loadMode() { mode = CodeMirror.getMode(options, options.mode); doc.iter(0, doc.size, function(line) { line.stateAfter = null; }); - work = [0]; - startWorker(); + frontier = 0; + startWorker(100); } function gutterChanged() { var visible = options.gutter || options.lineNumbers; @@ -1506,24 +1485,16 @@ var CodeMirror = (function() { var guess = Math.ceil(line.text.length / perLine) || 1; if (guess != 1) updateLineHeight(line, guess); }); - lineSpace.style.width = code.style.width = ""; - widthForcer.style.left = ""; + lineSpace.style.minWidth = widthForcer.style.left = ""; } else { wrapper.className = wrapper.className.replace(" CodeMirror-wrap", ""); - maxLine = ""; maxLineChanged = true; + computeMaxLength(); doc.iter(0, doc.size, function(line) { if (line.height != 1 && !line.hidden) updateLineHeight(line, 1); - if (line.text.length > maxLine.length) maxLine = line.text; }); } changes.push({from: 0, to: doc.size}); } - function makeTab(col) { - var w = options.tabSize - col % options.tabSize, cached = tabCache[w]; - if (cached) return cached; - for (var str = '', i = 0; i < w; ++i) str += " "; - return (tabCache[w] = {html: str + "", width: w}); - } function themeChanged() { scroller.className = scroller.className.replace(/\s*cm-s-\S+/g, "") + options.theme.replace(/(^|\s)\s*/g, " cm-s-"); @@ -1534,74 +1505,71 @@ var CodeMirror = (function() { (style ? " cm-keymap-" + style : ""); } - function TextMarker() { this.set = []; } + function TextMarker(type, style) { this.lines = []; this.type = type; if (style) this.style = style; } TextMarker.prototype.clear = operation(function() { var min = Infinity, max = -Infinity; - for (var i = 0, e = this.set.length; i < e; ++i) { - var line = this.set[i], mk = line.marked; - if (!mk || !line.parent) continue; - var lineN = lineNo(line); - min = Math.min(min, lineN); max = Math.max(max, lineN); - for (var j = 0; j < mk.length; ++j) - if (mk[j].marker == this) mk.splice(j--, 1); + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this, true); + if (span.from != null || span.to != null) { + var lineN = lineNo(line); + min = Math.min(min, lineN); max = Math.max(max, lineN); + } } if (min != Infinity) changes.push({from: min, to: max + 1}); + this.lines.length = 0; }); TextMarker.prototype.find = function() { var from, to; - for (var i = 0, e = this.set.length; i < e; ++i) { - var line = this.set[i], mk = line.marked; - for (var j = 0; j < mk.length; ++j) { - var mark = mk[j]; - if (mark.marker == this) { - if (mark.from != null || mark.to != null) { - var found = lineNo(line); - if (found != null) { - if (mark.from != null) from = {line: found, ch: mark.from}; - if (mark.to != null) to = {line: found, ch: mark.to}; - } - } - } + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (span.from != null || span.to != null) { + var found = lineNo(line); + if (span.from != null) from = {line: found, ch: span.from}; + if (span.to != null) to = {line: found, ch: span.to}; } } - return {from: from, to: to}; + if (this.type == "bookmark") return from; + return from && {from: from, to: to}; }; - function markText(from, to, className) { + function markText(from, to, className, options) { from = clipPos(from); to = clipPos(to); - var tm = new TextMarker(); - if (!posLess(from, to)) return tm; - function add(line, from, to, className) { - getLine(line).addMark(new MarkedText(from, to, className, tm)); - } - if (from.line == to.line) add(from.line, from.ch, to.ch, className); - else { - add(from.line, from.ch, null, className); - for (var i = from.line + 1, e = to.line; i < e; ++i) - add(i, null, null, className); - add(to.line, null, to.ch, className); - } + var marker = new TextMarker("range", className); + if (options) for (var opt in options) if (options.hasOwnProperty(opt)) + marker[opt] = options[opt]; + var curLine = from.line; + doc.iter(curLine, to.line + 1, function(line) { + var span = {from: curLine == from.line ? from.ch : null, + to: curLine == to.line ? to.ch : null, + marker: marker}; + (line.markedSpans || (line.markedSpans = [])).push(span); + marker.lines.push(line); + ++curLine; + }); changes.push({from: from.line, to: to.line + 1}); - return tm; + return marker; } function setBookmark(pos) { pos = clipPos(pos); - var bm = new Bookmark(pos.ch); - getLine(pos.line).addMark(bm); - return bm; + var marker = new TextMarker("bookmark"), line = getLine(pos.line); + var span = {from: pos.ch, to: pos.ch, marker: marker}; + (line.markedSpans || (line.markedSpans = [])).push(span); + marker.lines.push(line); + return marker; } function findMarksAt(pos) { pos = clipPos(pos); - var markers = [], marked = getLine(pos.line).marked; - if (!marked) return markers; - for (var i = 0, e = marked.length; i < e; ++i) { - var m = marked[i]; - if ((m.from == null || m.from <= pos.ch) && - (m.to == null || m.to >= pos.ch)) - markers.push(m.marker || m); + var markers = [], spans = getLine(pos.line).markedSpans; + if (spans) for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if ((span.from == null || span.from <= pos.ch) && + (span.to == null || span.to >= pos.ch)) + markers.push(span.marker); } return markers; } @@ -1641,11 +1609,10 @@ var CodeMirror = (function() { if (line.hidden != hidden) { line.hidden = hidden; if (!options.lineWrapping) { - var l = line.text; - if (hidden && l.length == maxLine.length) { + if (hidden && line.text.length == maxLine.text.length) { updateMaxLine = true; - } else if (!hidden && l.length > maxLine.length) { - maxLine = l; updateMaxLine = false; + } else if (!hidden && line.text.length > maxLine.text.length) { + maxLine = line; updateMaxLine = false; } } updateLineHeight(line, hidden ? 0 : 1); @@ -1677,53 +1644,18 @@ var CodeMirror = (function() { markerClass: marker && marker.style, lineClass: line.className, bgClass: line.bgClassName}; } - function stringWidth(str) { - measure.innerHTML = "
x
"; - measure.firstChild.firstChild.firstChild.nodeValue = str; - return measure.firstChild.firstChild.offsetWidth || 10; - } - // These are used to go from pixel positions to character - // positions, taking varying character widths into account. - function charFromX(line, x) { - if (x <= 0) return 0; - var lineObj = getLine(line), text = lineObj.text; - function getX(len) { - return measureLine(lineObj, len).left; - } - var from = 0, fromX = 0, to = text.length, toX; - // Guess a suitable upper bound for our search. - var estimated = Math.min(to, Math.ceil(x / charWidth())); - for (;;) { - var estX = getX(estimated); - if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2)); - else {toX = estX; to = estimated; break;} - } - if (x > toX) return to; - // Try to guess a suitable lower bound as well. - estimated = Math.floor(to * 0.8); estX = getX(estimated); - if (estX < x) {from = estimated; fromX = estX;} - // Do a binary search between these bounds. - for (;;) { - if (to - from <= 1) return (toX - x > x - fromX) ? from : to; - var middle = Math.ceil((from + to) / 2), middleX = getX(middle); - if (middleX > x) {to = middle; toX = middleX;} - else {from = middle; fromX = middleX;} - } - } - - var tempId = "CodeMirror-temp-" + Math.floor(Math.random() * 0xffffff).toString(16); function measureLine(line, ch) { if (ch == 0) return {top: 0, left: 0}; var wbr = options.lineWrapping && ch < line.text.length && spanAffectsWrapping.test(line.text.slice(ch - 1, ch + 1)); - measure.innerHTML = "
" + line.getHTML(makeTab, ch, tempId, wbr) + "
"; - var elt = document.getElementById(tempId); - var top = elt.offsetTop, left = elt.offsetLeft; + var pre = lineContent(line, ch); + removeChildrenAndAdd(measure, pre); + var anchor = pre.anchor; + var top = anchor.offsetTop, left = anchor.offsetLeft; // Older IEs report zero offsets for spans directly after a wrap if (ie && top == 0 && left == 0) { - var backup = document.createElement("span"); - backup.innerHTML = "x"; - elt.parentNode.insertBefore(backup, elt.nextSibling); + var backup = elt("span", "x"); + anchor.parentNode.insertBefore(backup, anchor.nextSibling); top = backup.offsetTop; } return {top: top, left: left}; @@ -1740,17 +1672,19 @@ var CodeMirror = (function() { } // Coords must be lineSpace-local function coordsChar(x, y) { - if (y < 0) y = 0; var th = textHeight(), cw = charWidth(), heightPos = displayOffset + Math.floor(y / th); + if (heightPos < 0) return {line: 0, ch: 0}; var lineNo = lineAtHeight(doc, heightPos); if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc.size - 1).text.length}; var lineObj = getLine(lineNo), text = lineObj.text; var tw = options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0; if (x <= 0 && innerOff == 0) return {line: lineNo, ch: 0}; + var wrongLine = false; function getX(len) { var sp = measureLine(lineObj, len); if (tw) { var off = Math.round(sp.top / th); + wrongLine = off != innerOff; return Math.max(0, sp.left + (off - innerOff) * scroller.clientWidth); } return sp.left; @@ -1769,9 +1703,12 @@ var CodeMirror = (function() { if (estX < x) {from = estimated; fromX = estX;} // Do a binary search between these bounds. for (;;) { - if (to - from <= 1) return {line: lineNo, ch: (toX - x > x - fromX) ? from : to}; + if (to - from <= 1) { + var after = x - fromX < toX - x; + return {line: lineNo, ch: after ? from : to, after: after}; + } var middle = Math.ceil((from + to) / 2), middleX = getX(middle); - if (middleX > x) {to = middle; toX = middleX;} + if (middleX > x) {to = middle; toX = middleX; if (wrongLine) toX += 1000; } else {from = middle; fromX = middleX;} } } @@ -1780,26 +1717,32 @@ var CodeMirror = (function() { return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot}; } - var cachedHeight, cachedHeightFor, measureText; + var cachedHeight, cachedHeightFor, measurePre; function textHeight() { - if (measureText == null) { - measureText = "
";
-        for (var i = 0; i < 49; ++i) measureText += "x
"; - measureText += "x
"; + if (measurePre == null) { + measurePre = elt("pre"); + for (var i = 0; i < 49; ++i) { + measurePre.appendChild(document.createTextNode("x")); + measurePre.appendChild(elt("br")); + } + measurePre.appendChild(document.createTextNode("x")); } var offsetHeight = lineDiv.clientHeight; if (offsetHeight == cachedHeightFor) return cachedHeight; cachedHeightFor = offsetHeight; - measure.innerHTML = measureText; + removeChildrenAndAdd(measure, measurePre.cloneNode(true)); cachedHeight = measure.firstChild.offsetHeight / 50 || 1; - measure.innerHTML = ""; + removeChildren(measure); return cachedHeight; } var cachedWidth, cachedWidthFor = 0; function charWidth() { if (scroller.clientWidth == cachedWidthFor) return cachedWidth; cachedWidthFor = scroller.clientWidth; - return (cachedWidth = stringWidth("x")); + var anchor = elt("span", "x"); + var pre = elt("pre", [anchor]); + removeChildrenAndAdd(measure, pre); + return (cachedWidth = anchor.offsetWidth || 10); } function paddingTop() {return lineSpace.offsetTop;} function paddingLeft() {return lineSpace.offsetLeft;} @@ -1816,6 +1759,7 @@ var CodeMirror = (function() { var offL = eltOffset(lineSpace, true); return coordsChar(x - offL.left, y - offL.top); } + var detectingSelectAll; function onContextMenu(e) { var pos = posFromMouse(e), scrollPos = scrollbar.scrollTop; if (!pos || opera) return; // Opera is difficult. @@ -1827,19 +1771,30 @@ var CodeMirror = (function() { input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) + "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; " + "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; - leaveInputAlone = true; - var val = input.value = getSelection(); focusInput(); - selectInput(input); + resetInput(true); + // Adds "Select all" to context menu in FF + if (posEq(sel.from, sel.to)) input.value = prevInput = " "; + function rehide() { - var newVal = splitLines(input.value).join("\n"); - if (newVal != val && !options.readOnly) operation(replaceSelection)(newVal, "end"); inputDiv.style.position = "relative"; input.style.cssText = oldCSS; if (ie_lt9) scrollbar.scrollTop = scrollPos; - leaveInputAlone = false; - resetInput(true); slowPoll(); + + // Try to detect the user choosing select-all + if (input.selectionStart != null) { + clearTimeout(detectingSelectAll); + var extval = input.value = " " + (posEq(sel.from, sel.to) ? "" : input.value), i = 0; + prevInput = " "; + input.selectionStart = 1; input.selectionEnd = extval.length; + detectingSelectAll = setTimeout(function poll(){ + if (prevInput == " " && input.selectionStart == 0) + operation(commands.selectAll)(instance); + else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500); + else resetInput(); + }, 200); + } } if (gecko) { @@ -1860,7 +1815,7 @@ var CodeMirror = (function() { cursor.style.visibility = ""; blinker = setInterval(function() { cursor.style.visibility = (on = !on) ? "" : "hidden"; - }, 650); + }, options.cursorBlinkRate); } var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"}; @@ -1923,70 +1878,39 @@ var CodeMirror = (function() { return minline; } function getStateBefore(n) { - var start = findStartLine(n), state = start && getLine(start-1).stateAfter; + var pos = findStartLine(n), state = pos && getLine(pos-1).stateAfter; if (!state) state = startState(mode); else state = copyState(mode, state); - doc.iter(start, n, function(line) { - line.highlight(mode, state, options.tabSize); - line.stateAfter = copyState(mode, state); + doc.iter(pos, n, function(line) { + line.process(mode, state, options.tabSize); + line.stateAfter = (pos == n - 1 || pos % 5 == 0) ? copyState(mode, state) : null; }); - if (start < n) changes.push({from: start, to: n}); - if (n < doc.size && !getLine(n).stateAfter) work.push(n); return state; } - function highlightLines(start, end) { - var state = getStateBefore(start); - doc.iter(start, end, function(line) { - line.highlight(mode, state, options.tabSize); - line.stateAfter = copyState(mode, state); - }); - } function highlightWorker() { - var end = +new Date + options.workTime; - var foundWork = work.length; - while (work.length) { - if (!getLine(showingFrom).stateAfter) var task = showingFrom; - else var task = work.pop(); - if (task >= doc.size) continue; - var start = findStartLine(task), state = start && getLine(start-1).stateAfter; - if (state) state = copyState(mode, state); - else state = startState(mode); - - var unchanged = 0, compare = mode.compareStates, realChange = false, - i = start, bail = false; - doc.iter(i, doc.size, function(line) { - var hadState = line.stateAfter; - if (+new Date > end) { - work.push(i); - startWorker(options.workDelay); - if (realChange) changes.push({from: task, to: i + 1}); - return (bail = true); - } - var changed = line.highlight(mode, state, options.tabSize); - if (changed) realChange = true; + if (frontier >= showingTo) return; + var end = +new Date + options.workTime, state = copyState(mode, getStateBefore(frontier)); + var startFrontier = frontier; + doc.iter(frontier, showingTo, function(line) { + if (frontier >= showingFrom) { // Visible + line.highlight(mode, state, options.tabSize); line.stateAfter = copyState(mode, state); - var done = null; - if (compare) { - var same = hadState && compare(hadState, state); - if (same != Pass) done = !!same; - } - if (done == null) { - if (changed !== false || !hadState) unchanged = 0; - else if (++unchanged > 3 && (!mode.indent || mode.indent(hadState, "") == mode.indent(state, ""))) - done = true; - } - if (done) return true; - ++i; - }); - if (bail) return; - if (realChange) changes.push({from: task, to: i + 1}); - } - if (foundWork && options.onHighlightComplete) - options.onHighlightComplete(instance); + } else { + line.process(mode, state, options.tabSize); + line.stateAfter = frontier % 5 == 0 ? copyState(mode, state) : null; + } + ++frontier; + if (+new Date > end) { + startWorker(options.workDelay); + return true; + } + }); + if (showingTo > startFrontier && frontier >= showingFrom) + operation(function() {changes.push({from: startFrontier, to: frontier});})(); } function startWorker(time) { - if (!work.length) return; - highlight.set(time, operation(highlightWorker)); + if (frontier < showingTo) + highlight.set(time, highlightWorker); } // Operations are used to wrap changes in such a way that each @@ -2000,9 +1924,11 @@ var CodeMirror = (function() { function endOperation() { if (updateMaxLine) computeMaxLength(); if (maxLineChanged && !options.lineWrapping) { - var cursorWidth = widthForcer.offsetWidth, left = stringWidth(maxLine); - widthForcer.style.left = left + "px"; - lineSpace.style.minWidth = (left + cursorWidth) + "px"; + var cursorWidth = widthForcer.offsetWidth, left = measureLine(maxLine, maxLine.text.length).left; + if (!ie_lt8) { + widthForcer.style.left = left + "px"; + lineSpace.style.minWidth = (left + cursorWidth) + "px"; + } maxLineChanged = false; } var newScrollPos, updated; @@ -2010,16 +1936,16 @@ var CodeMirror = (function() { var coords = calculateCursorCoords(); newScrollPos = calculateScrollPos(coords.x, coords.y, coords.x, coords.yBot); } - if (changes.length) updated = updateDisplay(changes, true, (newScrollPos ? newScrollPos.scrollTop : null)); - else { + if (changes.length || newScrollPos && newScrollPos.scrollTop != null) + updated = updateDisplay(changes, true, newScrollPos && newScrollPos.scrollTop); + if (!updated) { if (selectionChanged) updateSelection(); if (gutterDirty) updateGutter(); } if (newScrollPos) scrollCursorIntoView(); - if (selectionChanged) {scrollEditorIntoView(); restartBlink();} + if (selectionChanged) restartBlink(); - if (focused && !leaveInputAlone && - (updateInput === true || (updateInput !== false && selectionChanged))) + if (focused && (updateInput === true || (updateInput !== false && selectionChanged))) resetInput(userSelChange); if (selectionChanged && options.matchBrackets) @@ -2081,11 +2007,12 @@ var CodeMirror = (function() { dragDrop: true, onChange: null, onCursorActivity: null, + onViewportChange: null, onGutterClick: null, - onHighlightComplete: null, onUpdate: null, onFocus: null, onBlur: null, onScroll: null, matchBrackets: false, + cursorBlinkRate: 530, workTime: 100, workDelay: 200, pollInterval: 100, @@ -2124,7 +2051,13 @@ var CodeMirror = (function() { var spec = CodeMirror.resolveMode(spec); var mfactory = modes[spec.name]; if (!mfactory) return CodeMirror.getMode(options, "text/plain"); - return mfactory(options, spec); + var modeObj = mfactory(options, spec); + if (modeExtensions.hasOwnProperty(spec.name)) { + var exts = modeExtensions[spec.name]; + for (var prop in exts) if (exts.hasOwnProperty(prop)) modeObj[prop] = exts[prop]; + } + modeObj.name = spec.name; + return modeObj; }; CodeMirror.listModes = function() { var list = []; @@ -2144,6 +2077,13 @@ var CodeMirror = (function() { extensions[name] = func; }; + var modeExtensions = CodeMirror.modeExtensions = {}; + CodeMirror.extendMode = function(mode, properties) { + var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); + for (var prop in properties) if (properties.hasOwnProperty(prop)) + exts[prop] = properties[prop]; + }; + var commands = CodeMirror.commands = { selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});}, killLine: function(cm) { @@ -2241,6 +2181,10 @@ var CodeMirror = (function() { function lookup(map) { map = getKeyMap(map); var found = map[name]; + if (found === false) { + if (stop) stop(); + return true; + } if (found != null && handle(found)) return true; if (map.nofallthrough) { if (stop) stop(); @@ -2268,8 +2212,15 @@ var CodeMirror = (function() { options.value = textarea.value; if (!options.tabindex && textarea.tabindex) options.tabindex = textarea.tabindex; - if (options.autofocus == null && textarea.getAttribute("autofocus") != null) - options.autofocus = true; + // Set autofocus to true if this textarea is focused, or if it has + // autofocus and no other element is focused. + if (options.autofocus == null) { + var hasFocus = document.body; + // doc.activeElement occasionally throws on IE + try { hasFocus = document.activeElement; } catch(e) {} + options.autofocus = hasFocus == textarea || + textarea.getAttribute("autofocus") != null && hasFocus == document.body; + } function save() {textarea.value = instance.getValue();} if (textarea.form) { @@ -2277,13 +2228,12 @@ var CodeMirror = (function() { var rmSubmit = connect(textarea.form, "submit", save, true); if (typeof textarea.form.submit == "function") { var realSubmit = textarea.form.submit; - function wrappedSubmit() { + textarea.form.submit = function wrappedSubmit() { save(); textarea.form.submit = realSubmit; textarea.form.submit(); textarea.form.submit = wrappedSubmit; - } - textarea.form.submit = wrappedSubmit; + }; } } @@ -2306,6 +2256,18 @@ var CodeMirror = (function() { return instance; }; + var gecko = /gecko\/\d{7}/i.test(navigator.userAgent); + var ie = /MSIE \d/.test(navigator.userAgent); + var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent); + var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent); + var quirksMode = ie && document.documentMode == 5; + var webkit = /WebKit\//.test(navigator.userAgent); + var chrome = /Chrome\//.test(navigator.userAgent); + var opera = /Opera\//.test(navigator.userAgent); + var safari = /Apple Computer/.test(navigator.vendor); + var khtml = /KHTML\//.test(navigator.userAgent); + var mac_geLion = /Mac OS X 10\D([7-9]|\d\d)\D/.test(navigator.userAgent); + // Utility functions for working with state. Exported because modes // sometimes need to do this. function copyState(mode, state) { @@ -2324,6 +2286,14 @@ var CodeMirror = (function() { return mode.startState ? mode.startState(a1, a2) : true; } CodeMirror.startState = startState; + CodeMirror.innerMode = function(mode, state) { + while (mode.innerMode) { + var info = mode.innerMode(state); + state = info.state; + mode = info.mode; + } + return info || {mode: mode, state: state}; + }; // The character stream used by a mode's parser. function StringStream(string, tabSize) { @@ -2334,7 +2304,7 @@ var CodeMirror = (function() { StringStream.prototype = { eol: function() {return this.pos >= this.string.length;}, sol: function() {return this.pos == 0;}, - peek: function() {return this.string.charAt(this.pos);}, + peek: function() {return this.string.charAt(this.pos) || undefined;}, next: function() { if (this.pos < this.string.length) return this.string.charAt(this.pos++); @@ -2365,13 +2335,14 @@ var CodeMirror = (function() { indentation: function() {return countColumn(this.string, null, this.tabSize);}, match: function(pattern, consume, caseInsensitive) { if (typeof pattern == "string") { - function cased(str) {return caseInsensitive ? str.toLowerCase() : str;} + var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;}; if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) { if (consume !== false) this.pos += pattern.length; return true; } } else { var match = this.string.slice(this.pos).match(pattern); + if (match && match.index > 0) return null; if (match && consume !== false) this.pos += match[0].length; return match; } @@ -2380,210 +2351,162 @@ var CodeMirror = (function() { }; CodeMirror.StringStream = StringStream; - function MarkedText(from, to, className, marker) { - this.from = from; this.to = to; this.style = className; this.marker = marker; + function MarkedSpan(from, to, marker) { + this.from = from; this.to = to; this.marker = marker; } - MarkedText.prototype = { - attach: function(line) { this.marker.set.push(line); }, - detach: function(line) { - var ix = indexOf(this.marker.set, line); - if (ix > -1) this.marker.set.splice(ix, 1); - }, - split: function(pos, lenBefore) { - if (this.to <= pos && this.to != null) return null; - var from = this.from < pos || this.from == null ? null : this.from - pos + lenBefore; - var to = this.to == null ? null : this.to - pos + lenBefore; - return new MarkedText(from, to, this.style, this.marker); - }, - dup: function() { return new MarkedText(null, null, this.style, this.marker); }, - clipTo: function(fromOpen, from, toOpen, to, diff) { - if (fromOpen && to > this.from && (to < this.to || this.to == null)) - this.from = null; - else if (this.from != null && this.from >= from) - this.from = Math.max(to, this.from) + diff; - if (toOpen && (from < this.to || this.to == null) && (from > this.from || this.from == null)) - this.to = null; - else if (this.to != null && this.to > from) - this.to = to < this.to ? this.to + diff : from; - }, - isDead: function() { return this.from != null && this.to != null && this.from >= this.to; }, - sameSet: function(x) { return this.marker == x.marker; } - }; - function Bookmark(pos) { - this.from = pos; this.to = pos; this.line = null; - } - Bookmark.prototype = { - attach: function(line) { this.line = line; }, - detach: function(line) { if (this.line == line) this.line = null; }, - split: function(pos, lenBefore) { - if (pos < this.from) { - this.from = this.to = (this.from - pos) + lenBefore; - return this; - } - }, - isDead: function() { return this.from > this.to; }, - clipTo: function(fromOpen, from, toOpen, to, diff) { - if ((fromOpen || from < this.from) && (toOpen || to > this.to)) { - this.from = 0; this.to = -1; - } else if (this.from > from) { - this.from = this.to = Math.max(to, this.from) + diff; - } - }, - sameSet: function(x) { return false; }, - find: function() { - if (!this.line || !this.line.parent) return null; - return {line: lineNo(this.line), ch: this.from}; - }, - clear: function() { - if (this.line) { - var found = indexOf(this.line.marked, this); - if (found != -1) this.line.marked.splice(found, 1); - this.line = null; + function getMarkedSpanFor(spans, marker, del) { + if (spans) for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.marker == marker) { + if (del) spans.splice(i, 1); + return span; } } - }; + } + + function markedSpansBefore(old, startCh, endCh) { + if (old) for (var i = 0, nw; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh); + if (startsBefore || marker.type == "bookmark" && span.from == startCh && span.from != endCh) { + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh); + (nw || (nw = [])).push({from: span.from, + to: endsAfter ? null : span.to, + marker: marker}); + } + } + return nw; + } + + function markedSpansAfter(old, endCh) { + if (old) for (var i = 0, nw; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh); + if (endsAfter || marker.type == "bookmark" && span.from == endCh) { + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh); + (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh, + to: span.to == null ? null : span.to - endCh, + marker: marker}); + } + } + return nw; + } + + function updateMarkedSpans(oldFirst, oldLast, startCh, endCh, newText) { + if (!oldFirst && !oldLast) return newText; + // Get the spans that 'stick out' on both sides + var first = markedSpansBefore(oldFirst, startCh); + var last = markedSpansAfter(oldLast, endCh); + + // Next, merge those two ends + var sameLine = newText.length == 1, offset = lst(newText).length + (sameLine ? startCh : 0); + if (first) { + // Fix up .to properties of first + for (var i = 0; i < first.length; ++i) { + var span = first[i]; + if (span.to == null) { + var found = getMarkedSpanFor(last, span.marker); + if (!found) span.to = startCh; + else if (sameLine) span.to = found.to == null ? null : found.to + offset; + } + } + } + if (last) { + // Fix up .from in last (or move them into first in case of sameLine) + for (var i = 0; i < last.length; ++i) { + var span = last[i]; + if (span.to != null) span.to += offset; + if (span.from == null) { + var found = getMarkedSpanFor(first, span.marker); + if (!found) { + span.from = offset; + if (sameLine) (first || (first = [])).push(span); + } + } else { + span.from += offset; + if (sameLine) (first || (first = [])).push(span); + } + } + } + + var newMarkers = [newHL(newText[0], first)]; + if (!sameLine) { + // Fill gap with whole-line-spans + var gap = newText.length - 2, gapMarkers; + if (gap > 0 && first) + for (var i = 0; i < first.length; ++i) + if (first[i].to == null) + (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker}); + for (var i = 0; i < gap; ++i) + newMarkers.push(newHL(newText[i+1], gapMarkers)); + newMarkers.push(newHL(lst(newText), last)); + } + return newMarkers; + } + + // hl stands for history-line, a data structure that can be either a + // string (line without markers) or a {text, markedSpans} object. + function hlText(val) { return typeof val == "string" ? val : val.text; } + function hlSpans(val) { return typeof val == "string" ? null : val.markedSpans; } + function newHL(text, spans) { return spans ? {text: text, markedSpans: spans} : text; } + + function detachMarkedSpans(line) { + var spans = line.markedSpans; + if (!spans) return; + for (var i = 0; i < spans.length; ++i) { + var lines = spans[i].marker.lines; + var ix = indexOf(lines, line); + lines.splice(ix, 1); + } + line.markedSpans = null; + } + + function attachMarkedSpans(line, spans) { + if (!spans) return; + for (var i = 0; i < spans.length; ++i) + var marker = spans[i].marker.lines.push(line); + line.markedSpans = spans; + } + + // When measuring the position of the end of a line, different + // browsers require different approaches. If an empty span is added, + // many browsers report bogus offsets. Of those, some (Webkit, + // recent IE) will accept a space without moving the whole span to + // the next line when wrapping it, others work with a zero-width + // space. + var eolSpanContent = " "; + if (gecko || (ie && !ie_lt8)) eolSpanContent = "\u200b"; + else if (opera) eolSpanContent = ""; // Line objects. These hold state related to a line, including // highlighting info (the styles array). - function Line(text, styles) { - this.styles = styles || [text, null]; + function Line(text, markedSpans) { this.text = text; this.height = 1; - this.marked = this.gutterMarker = this.className = this.bgClassName = this.handlers = null; - this.stateAfter = this.parent = this.hidden = null; - } - Line.inheritMarks = function(text, orig) { - var ln = new Line(text), mk = orig && orig.marked; - if (mk) { - for (var i = 0; i < mk.length; ++i) { - if (mk[i].to == null && mk[i].style) { - var newmk = ln.marked || (ln.marked = []), mark = mk[i]; - var nmark = mark.dup(); newmk.push(nmark); nmark.attach(ln); - } - } - } - return ln; + attachMarkedSpans(this, markedSpans); } Line.prototype = { - // Replace a piece of a line, keeping the styles around it intact. - replace: function(from, to_, text) { - var st = [], mk = this.marked, to = to_ == null ? this.text.length : to_; - copyStyles(0, from, this.styles, st); - if (text) st.push(text, null); - copyStyles(to, this.text.length, this.styles, st); - this.styles = st; - this.text = this.text.slice(0, from) + text + this.text.slice(to); - this.stateAfter = null; - if (mk) { - var diff = text.length - (to - from); - for (var i = 0; i < mk.length; ++i) { - var mark = mk[i]; - mark.clipTo(from == null, from || 0, to_ == null, to, diff); - if (mark.isDead()) {mark.detach(this); mk.splice(i--, 1);} - } - } - }, - // Split a part off a line, keeping styles and markers intact. - split: function(pos, textBefore) { - var st = [textBefore, null], mk = this.marked; - copyStyles(pos, this.text.length, this.styles, st); - var taken = new Line(textBefore + this.text.slice(pos), st); - if (mk) { - for (var i = 0; i < mk.length; ++i) { - var mark = mk[i]; - var newmark = mark.split(pos, textBefore.length); - if (newmark) { - if (!taken.marked) taken.marked = []; - taken.marked.push(newmark); newmark.attach(taken); - if (newmark == mark) mk.splice(i--, 1); - } - } - } - return taken; - }, - append: function(line) { - var mylen = this.text.length, mk = line.marked, mymk = this.marked; - this.text += line.text; - copyStyles(0, line.text.length, line.styles, this.styles); - if (mymk) { - for (var i = 0; i < mymk.length; ++i) - if (mymk[i].to == null) mymk[i].to = mylen; - } - if (mk && mk.length) { - if (!mymk) this.marked = mymk = []; - outer: for (var i = 0; i < mk.length; ++i) { - var mark = mk[i]; - if (!mark.from) { - for (var j = 0; j < mymk.length; ++j) { - var mymark = mymk[j]; - if (mymark.to == mylen && mymark.sameSet(mark)) { - mymark.to = mark.to == null ? null : mark.to + mylen; - if (mymark.isDead()) { - mymark.detach(this); - mk.splice(i--, 1); - } - continue outer; - } - } - } - mymk.push(mark); - mark.attach(this); - mark.from += mylen; - if (mark.to != null) mark.to += mylen; - } - } - }, - fixMarkEnds: function(other) { - var mk = this.marked, omk = other.marked; - if (!mk) return; - outer: for (var i = 0; i < mk.length; ++i) { - var mark = mk[i], close = mark.to == null; - if (close && omk) { - for (var j = 0; j < omk.length; ++j) { - var om = omk[j]; - if (!om.sameSet(mark) || om.from != null) continue - if (mark.from == this.text.length && om.to == 0) { - omk.splice(j, 1); - mk.splice(i--, 1); - continue outer; - } else { - close = false; break; - } - } - } - if (close) mark.to = this.text.length; - } - }, - fixMarkStarts: function() { - var mk = this.marked; - if (!mk) return; - for (var i = 0; i < mk.length; ++i) - if (mk[i].from == null) mk[i].from = 0; - }, - addMark: function(mark) { - mark.attach(this); - if (this.marked == null) this.marked = []; - this.marked.push(mark); - this.marked.sort(function(a, b){return (a.from || 0) - (b.from || 0);}); + update: function(text, markedSpans) { + this.text = text; + this.stateAfter = this.styles = null; + detachMarkedSpans(this); + attachMarkedSpans(this, markedSpans); }, // Run the given mode's parser over a line, update the styles // array, which contains alternating fragments of text and CSS // classes. highlight: function(mode, state, tabSize) { - var stream = new StringStream(this.text, tabSize), st = this.styles, pos = 0; - var changed = false, curWord = st[0], prevWord; + var stream = new StringStream(this.text, tabSize), st = this.styles || (this.styles = []); + var pos = st.length = 0; if (this.text == "" && mode.blankLine) mode.blankLine(state); while (!stream.eol()) { - var style = mode.token(stream, state); - var substr = this.text.slice(stream.start, stream.pos); + var style = mode.token(stream, state), substr = stream.current(); stream.start = stream.pos; - if (pos && st[pos-1] == style) + if (pos && st[pos-1] == style) { st[pos-2] += substr; - else if (substr) { - if (!changed && (st[pos+1] != style || (pos && st[pos-2] != prevWord))) changed = true; + } else if (substr) { st[pos++] = substr; st[pos++] = style; - prevWord = curWord; curWord = st[pos]; } // Give up when line is ridiculously long if (stream.pos > 5000) { @@ -2591,17 +2514,19 @@ var CodeMirror = (function() { break; } } - if (st.length != pos) {st.length = pos; changed = true;} - if (pos && st[pos-2] != prevWord) changed = true; - // Short lines with simple highlights return null, and are - // counted as changed by the driver because they are likely to - // highlight the same way in various contexts. - return changed || (st.length < 5 && this.text.length < 10 ? null : false); + }, + process: function(mode, state, tabSize) { + var stream = new StringStream(this.text, tabSize); + if (this.text == "" && mode.blankLine) mode.blankLine(state); + while (!stream.eol() && stream.pos <= 5000) { + mode.token(stream, state); + stream.start = stream.pos; + } }, // Fetch the parser token for a given character. Useful for hacks // that want to inspect the mode state (say, for completion). - getTokenAt: function(mode, state, ch) { - var txt = this.text, stream = new StringStream(txt); + getTokenAt: function(mode, state, tabSize, ch) { + var txt = this.text, stream = new StringStream(txt, tabSize); while (stream.pos < ch && !stream.eol()) { stream.start = stream.pos; var style = mode.token(stream, state); @@ -2615,94 +2540,98 @@ var CodeMirror = (function() { indentation: function(tabSize) {return countColumn(this.text, null, tabSize);}, // Produces an HTML fragment for the line, taking selection, // marking, and highlighting into account. - getHTML: function(makeTab, wrapAt, wrapId, wrapWBR) { - var html = [], first = true, col = 0; - function span_(text, style) { + getContent: function(tabSize, wrapAt, compensateForWrapping) { + var first = true, col = 0, specials = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g; + var pre = elt("pre"); + function span_(html, text, style) { if (!text) return; // Work around a bug where, in some compat modes, IE ignores leading spaces if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1); first = false; - if (text.indexOf("\t") == -1) { + if (!specials.test(text)) { col += text.length; - var escaped = htmlEscape(text); + var content = document.createTextNode(text); } else { - var escaped = ""; - for (var pos = 0;;) { - var idx = text.indexOf("\t", pos); - if (idx == -1) { - escaped += htmlEscape(text.slice(pos)); - col += text.length - pos; - break; + var content = document.createDocumentFragment(), pos = 0; + while (true) { + specials.lastIndex = pos; + var m = specials.exec(text); + var skipped = m ? m.index - pos : text.length - pos; + if (skipped) { + content.appendChild(document.createTextNode(text.slice(pos, pos + skipped))); + col += skipped; + } + if (!m) break; + pos += skipped + 1; + if (m[0] == "\t") { + var tabWidth = tabSize - col % tabSize; + content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); + col += tabWidth; } else { - col += idx - pos; - var tab = makeTab(col); - escaped += htmlEscape(text.slice(pos, idx)) + tab.html; - col += tab.width; - pos = idx + 1; + var token = elt("span", "\u2022", "cm-invalidchar"); + token.title = "\\u" + m[0].charCodeAt(0).toString(16); + content.appendChild(token); + col += 1; } } } - if (style) html.push('', escaped, ""); - else html.push(escaped); + if (style) html.appendChild(elt("span", [content], style)); + else html.appendChild(content); } var span = span_; if (wrapAt != null) { - var outPos = 0, open = ""; - span = function(text, style) { + var outPos = 0, anchor = pre.anchor = elt("span"); + span = function(html, text, style) { var l = text.length; if (wrapAt >= outPos && wrapAt < outPos + l) { if (wrapAt > outPos) { - span_(text.slice(0, wrapAt - outPos), style); + span_(html, text.slice(0, wrapAt - outPos), style); // See comment at the definition of spanAffectsWrapping - if (wrapWBR) html.push(""); + if (compensateForWrapping) html.appendChild(elt("wbr")); } - html.push(open); + html.appendChild(anchor); var cut = wrapAt - outPos; - span_(opera ? text.slice(cut, cut + 1) : text.slice(cut), style); - html.push(""); - if (opera) span_(text.slice(cut + 1), style); + span_(anchor, opera ? text.slice(cut, cut + 1) : text.slice(cut), style); + if (opera) span_(html, text.slice(cut + 1), style); wrapAt--; outPos += l; } else { outPos += l; - span_(text, style); - // Output empty wrapper when at end of line - // (Gecko and IE8+ do strange wrapping when adding a space - // to the end of the line. Other browsers don't react well - // to zero-width spaces. So we do hideous browser sniffing - // to determine which to use.) - if (outPos == wrapAt && outPos == len) - html.push(open + (gecko || (ie && !ie_lt8) ? "​" : " ") + ""); + span_(html, text, style); + if (outPos == wrapAt && outPos == len) { + setTextContent(anchor, eolSpanContent); + html.appendChild(anchor); + } // Stop outputting HTML when gone sufficiently far beyond measure else if (outPos > wrapAt + 10 && /\s/.test(text)) span = function(){}; } - } + }; } - var st = this.styles, allText = this.text, marked = this.marked; + var st = this.styles, allText = this.text, marked = this.markedSpans; var len = allText.length; function styleToClass(style) { if (!style) return null; return "cm-" + style.replace(/ +/g, " cm-"); } - if (!allText && wrapAt == null) { - span(" "); + span(pre, " "); } else if (!marked || !marked.length) { for (var i = 0, ch = 0; ch < len; i+=2) { var str = st[i], style = st[i+1], l = str.length; if (ch + l > len) str = str.slice(0, len - ch); ch += l; - span(str, styleToClass(style)); + span(pre, str, styleToClass(style)); } } else { + marked.sort(function(a, b) { return a.from - b.from; }); var pos = 0, i = 0, text = "", style, sg = 0; var nextChange = marked[0].from || 0, marks = [], markpos = 0; - function advanceMarks() { + var advanceMarks = function() { var m; while (markpos < marked.length && ((m = marked[markpos]).from == pos || m.from == null)) { - if (m.style != null) marks.push(m); + if (m.marker.type == "range") marks.push(m); ++markpos; } nextChange = markpos < marked.length ? marked[markpos].from : Infinity; @@ -2712,7 +2641,7 @@ var CodeMirror = (function() { if (to == pos) marks.splice(i--, 1); else nextChange = Math.min(to, nextChange); } - } + }; var m = 0; while (pos < len) { if (nextChange == pos) advanceMarks(); @@ -2721,9 +2650,13 @@ var CodeMirror = (function() { if (text) { var end = pos + text.length; var appliedStyle = style; - for (var j = 0; j < marks.length; ++j) - appliedStyle = (appliedStyle ? appliedStyle + " " : "") + marks[j].style; - span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle); + for (var j = 0; j < marks.length; ++j) { + var mark = marks[j]; + appliedStyle = (appliedStyle ? appliedStyle + " " : "") + mark.marker.style; + if (mark.marker.endStyle && mark.to === Math.min(end, upto)) appliedStyle += " " + mark.marker.endStyle; + if (mark.marker.startStyle && mark.from === pos) appliedStyle += " " + mark.marker.startStyle; + } + span(pre, end > upto ? text.slice(0, upto - pos) : text, appliedStyle); if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;} pos = end; } @@ -2731,28 +2664,13 @@ var CodeMirror = (function() { } } } - return html.join(""); + return pre; }, cleanUp: function() { this.parent = null; - if (this.marked) - for (var i = 0, e = this.marked.length; i < e; ++i) this.marked[i].detach(this); + detachMarkedSpans(this); } }; - // Utility used by replace and split above - function copyStyles(from, to, source, dest) { - for (var i = 0, pos = 0, state = 0; pos < to; i+=2) { - var part = source[i], end = pos + part.length; - if (state == 0) { - if (end > from) dest.push(part.slice(from - pos, Math.min(part.length, to - pos)), source[i+1]); - if (end >= from) state = 1; - } else if (state == 1) { - if (end > to) dest.push(part.slice(0, to - pos), source[i+1]); - else dest.push(part, source[i+1]); - } - pos = end; - } - } // Data structure that holds the sequence of lines. function LeafChunk(lines) { @@ -2953,7 +2871,7 @@ var CodeMirror = (function() { History.prototype = { addChange: function(start, added, old) { this.undone.length = 0; - var time = +new Date, cur = this.done[this.done.length - 1], last = cur && cur[cur.length - 1]; + var time = +new Date, cur = lst(this.done), last = cur && lst(cur); var dtime = time - this.time; if (this.compound && cur && !this.closed) { @@ -3038,30 +2956,18 @@ var CodeMirror = (function() { var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}}; - var gecko = /gecko\/\d{7}/i.test(navigator.userAgent); - var ie = /MSIE \d/.test(navigator.userAgent); - var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent); - var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent); - var quirksMode = ie && document.documentMode == 5; - var webkit = /WebKit\//.test(navigator.userAgent); - var chrome = /Chrome\//.test(navigator.userAgent); - var opera = /Opera\//.test(navigator.userAgent); - var safari = /Apple Computer/.test(navigator.vendor); - var khtml = /KHTML\//.test(navigator.userAgent); - var mac_geLion = /Mac OS X 10\D([7-9]|\d\d)\D/.test(navigator.userAgent); - // Detect drag-and-drop var dragAndDrop = function() { // There is *some* kind of drag-and-drop support in IE6-8, but I // couldn't get it to work yet. if (ie_lt9) return false; - var div = document.createElement('div'); + var div = elt('div'); return "draggable" in div || "dragDrop" in div; }(); // Feature-detect whether newlines in textareas are converted to \r\n var lineSep = function () { - var te = document.createElement("textarea"); + var te = elt("textarea"); te.value = "foo\nbar"; if (te.value.indexOf("\r") > -1) return "\r\n"; return "\n"; @@ -3093,11 +2999,6 @@ var CodeMirror = (function() { return n; } - function computedStyle(elt) { - if (elt.currentStyle) return elt.currentStyle; - return window.getComputedStyle(elt, null); - } - function eltOffset(node, screen) { // Take the parts of bounding client rect that we are interested in so we are able to edit if need be, // since the returned value cannot be changed externally (they are kept in sync as the element moves within the page) @@ -3116,10 +3017,19 @@ var CodeMirror = (function() { return box; } - // Get a node's text content. function eltText(node) { return node.textContent || node.innerText || node.nodeValue || ""; } + + var spaceStrs = [""]; + function spaceStr(n) { + while (spaceStrs.length <= n) + spaceStrs.push(lst(spaceStrs) + " "); + return spaceStrs[n]; + } + + function lst(arr) { return arr[arr.length-1]; } + function selectInput(node) { if (ios) { // Mobile Safari apparently has a bug where select() is broken. node.selectionStart = 0; @@ -3132,27 +3042,27 @@ var CodeMirror = (function() { function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);} function copyPos(x) {return {line: x.line, ch: x.ch};} - var escapeElement = document.createElement("pre"); - function htmlEscape(str) { - escapeElement.textContent = str; - return escapeElement.innerHTML; + function elt(tag, content, className, style) { + var e = document.createElement(tag); + if (className) e.className = className; + if (style) e.style.cssText = style; + if (typeof content == "string") setTextContent(e, content); + else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]); + return e; } - // Recent (late 2011) Opera betas insert bogus newlines at the start - // of the textContent, so we strip those. - if (htmlEscape("a") == "\na") { - htmlEscape = function(str) { - escapeElement.textContent = str; - return escapeElement.innerHTML.slice(1); - }; - // Some IEs don't preserve tabs through innerHTML - } else if (htmlEscape("\t") != "\t") { - htmlEscape = function(str) { - escapeElement.innerHTML = ""; - escapeElement.appendChild(document.createTextNode(str)); - return escapeElement.innerHTML; - }; + function removeChildren(e) { + e.innerHTML = ""; + return e; + } + function removeChildrenAndAdd(parent, e) { + removeChildren(parent).appendChild(e); + } + function setTextContent(e, str) { + if (ie_lt9) { + e.innerHTML = ""; + e.appendChild(document.createTextNode(str)); + } else e.textContent = str; } - CodeMirror.htmlEscape = htmlEscape; // Used to position the cursor after an undo/redo by finding the // last edited character. @@ -3227,5 +3137,7 @@ var CodeMirror = (function() { for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i; })(); + CodeMirror.version = "2.34"; + return CodeMirror; })(); diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/closetag.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/closetag.js index 656e93c288..5096678473 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/closetag.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/closetag.js @@ -26,6 +26,11 @@ /** Array of tag names where an end tag is forbidden. */ CodeMirror.defaults['closeTagVoid'] = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr']; + function innerState(cm, state) { + return CodeMirror.innerMode(cm.getMode(), state).state; + } + + /** * Call during key processing to close tags. Handles the key event if the tag is closed, otherwise throws CodeMirror.Pass. * - cm: The editor instance. @@ -39,40 +44,34 @@ throw CodeMirror.Pass; } - var mode = cm.getOption('mode'); + /* + * Relevant structure of token: + * + * htmlmixed + * className + * state + * htmlState + * type + * tagName + * context + * tagName + * mode + * + * xml + * className + * state + * tagName + * type + */ - if (mode == 'text/html' || mode == 'xml') { - - /* - * Relevant structure of token: - * - * htmlmixed - * className - * state - * htmlState - * type - * tagName - * context - * tagName - * mode - * - * xml - * className - * state - * tagName - * type - */ - - var pos = cm.getCursor(); - var tok = cm.getTokenAt(pos); - var state = tok.state; - - if (state.mode && state.mode != 'html') { - throw CodeMirror.Pass; // With htmlmixed, we only care about the html sub-mode. - } + var pos = cm.getCursor(); + var tok = cm.getTokenAt(pos); + var state = innerState(cm, tok.state); + + if (state) { if (ch == '>') { - var type = state.htmlState ? state.htmlState.type : state.type; // htmlmixed : xml + var type = state.type; if (tok.className == 'tag' && type == 'closeTag') { throw CodeMirror.Pass; // Don't process the '>' at the end of an end-tag. @@ -83,11 +82,12 @@ cm.setCursor(pos); tok = cm.getTokenAt(cm.getCursor()); - state = tok.state; - type = state.htmlState ? state.htmlState.type : state.type; // htmlmixed : xml + state = innerState(cm, tok.state); + if (!state) throw CodeMirror.Pass; + var type = state.type; if (tok.className == 'tag' && type != 'selfcloseTag') { - var tagName = state.htmlState ? state.htmlState.tagName : state.tagName; // htmlmixed : xml + var tagName = state.tagName; if (tagName.length > 0 && shouldClose(cm, vd, tagName)) { insertEndTag(cm, indent, pos, tagName); } @@ -100,7 +100,7 @@ } else if (ch == '/') { if (tok.className == 'tag' && tok.string == '<') { - var tagName = state.htmlState ? (state.htmlState.context ? state.htmlState.context.tagName : '') : (state.context ? state.context.tagName : ''); // htmlmixed : xml + var ctx = state.context, tagName = ctx ? ctx.tagName : ''; if (tagName.length > 0) { completeEndTag(cm, pos, tagName); return; diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/dialog.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/dialog.js index 36ea10f44d..7aad7eaecf 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/dialog.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/dialog.js @@ -30,7 +30,10 @@ inp.focus(); CodeMirror.connect(inp, "blur", close); } else if (button = dialog.getElementsByTagName("button")[0]) { - CodeMirror.connect(button, "click", close); + CodeMirror.connect(button, "click", function() { + close(); + me.focus(); + }); button.focus(); CodeMirror.connect(button, "blur", close); } diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/formatting.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/formatting.js index ccac9b1aa3..2c502b25c6 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/formatting.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/formatting.js @@ -1,110 +1,22 @@ // ============== Formatting extensions ============================ -// A common storage for all mode-specific formatting features -if (!CodeMirror.modeExtensions) CodeMirror.modeExtensions = {}; - -// Returns the extension of the editor's current mode -CodeMirror.defineExtension("getModeExt", function () { - var mname = CodeMirror.resolveMode(this.getOption("mode")).name; - var ext = CodeMirror.modeExtensions[mname]; - if (!ext) throw new Error("No extensions found for mode " + mname); - return ext; -}); - -// If the current mode is 'htmlmixed', returns the extension of a mode located at -// the specified position (can be htmlmixed, css or javascript). Otherwise, simply -// returns the extension of the editor's current mode. -CodeMirror.defineExtension("getModeExtAtPos", function (pos) { - var token = this.getTokenAt(pos); - if (token && token.state && token.state.mode) - return CodeMirror.modeExtensions[token.state.mode == "html" ? "htmlmixed" : token.state.mode]; - else - return this.getModeExt(); -}); - -// Comment/uncomment the specified range -CodeMirror.defineExtension("commentRange", function (isComment, from, to) { - var curMode = this.getModeExtAtPos(this.getCursor()); - if (isComment) { // Comment range - var commentedText = this.getRange(from, to); - this.replaceRange(curMode.commentStart + this.getRange(from, to) + curMode.commentEnd - , from, to); - if (from.line == to.line && from.ch == to.ch) { // An empty comment inserted - put cursor inside - this.setCursor(from.line, from.ch + curMode.commentStart.length); - } - } - else { // Uncomment range - var selText = this.getRange(from, to); - var startIndex = selText.indexOf(curMode.commentStart); - var endIndex = selText.lastIndexOf(curMode.commentEnd); - if (startIndex > -1 && endIndex > -1 && endIndex > startIndex) { - // Take string till comment start - selText = selText.substr(0, startIndex) - // From comment start till comment end - + selText.substring(startIndex + curMode.commentStart.length, endIndex) - // From comment end till string end - + selText.substr(endIndex + curMode.commentEnd.length); - } - this.replaceRange(selText, from, to); - } -}); - -// Applies automatic mode-aware indentation to the specified range -CodeMirror.defineExtension("autoIndentRange", function (from, to) { - var cmInstance = this; - this.operation(function () { - for (var i = from.line; i <= to.line; i++) { - cmInstance.indentLine(i, "smart"); +(function() { + // Define extensions for a few modes + CodeMirror.extendMode("css", { + commentStart: "/*", + commentEnd: "*/", + wordWrapChars: [";", "\\{", "\\}"], + autoFormatLineBreaks: function (text) { + return text.replace(new RegExp("(;|\\{|\\})([^\r\n])", "g"), "$1\n$2"); } }); -}); -// Applies automatic formatting to the specified range -CodeMirror.defineExtension("autoFormatRange", function (from, to) { - var absStart = this.indexFromPos(from); - var absEnd = this.indexFromPos(to); - // Insert additional line breaks where necessary according to the - // mode's syntax - var res = this.getModeExt().autoFormatLineBreaks(this.getValue(), absStart, absEnd); - var cmInstance = this; - - // Replace and auto-indent the range - this.operation(function () { - cmInstance.replaceRange(res, from, to); - var startLine = cmInstance.posFromIndex(absStart).line; - var endLine = cmInstance.posFromIndex(absStart + res.length).line; - for (var i = startLine; i <= endLine; i++) { - cmInstance.indentLine(i, "smart"); - } - }); -}); - -// Define extensions for a few modes - -CodeMirror.modeExtensions["css"] = { - commentStart: "/*", - commentEnd: "*/", - wordWrapChars: [";", "\\{", "\\}"], - autoFormatLineBreaks: function (text, startPos, endPos) { - text = text.substring(startPos, endPos); - return text.replace(new RegExp("(;|\\{|\\})([^\r\n])", "g"), "$1\n$2"); - } -}; - -CodeMirror.modeExtensions["javascript"] = { - commentStart: "/*", - commentEnd: "*/", - wordWrapChars: [";", "\\{", "\\}"], - - getNonBreakableBlocks: function (text) { - var nonBreakableRegexes = [ - new RegExp("for\\s*?\\(([\\s\\S]*?)\\)"), - new RegExp("\\\\\"([\\s\\S]*?)(\\\\\"|$)"), - new RegExp("\\\\\'([\\s\\S]*?)(\\\\\'|$)"), - new RegExp("'([\\s\\S]*?)('|$)"), - new RegExp("\"([\\s\\S]*?)(\"|$)"), - new RegExp("//.*([\r\n]|$)") - ]; - var nonBreakableBlocks = new Array(); + function jsNonBreakableBlocks(text) { + var nonBreakableRegexes = [/for\s*?\((.*?)\)/, + /\"(.*?)(\"|$)/, + /\'(.*?)(\'|$)/, + /\/\*(.*?)(\*\/|$)/, + /\/\/.*/]; + var nonBreakableBlocks = []; for (var i = 0; i < nonBreakableRegexes.length; i++) { var curPos = 0; while (curPos < text.length) { @@ -126,174 +38,156 @@ CodeMirror.modeExtensions["javascript"] = { }); return nonBreakableBlocks; - }, - - autoFormatLineBreaks: function (text, startPos, endPos) { - text = text.substring(startPos, endPos); - var curPos = 0; - var reLinesSplitter = new RegExp("(;|\\{|\\})([^\r\n])", "g"); - var nonBreakableBlocks = this.getNonBreakableBlocks(text); - if (nonBreakableBlocks != null) { - var res = ""; - for (var i = 0; i < nonBreakableBlocks.length; i++) { - if (nonBreakableBlocks[i].start > curPos) { // Break lines till the block - res += text.substring(curPos, nonBreakableBlocks[i].start).replace(reLinesSplitter, "$1\n$2"); - curPos = nonBreakableBlocks[i].start; - } - if (nonBreakableBlocks[i].start <= curPos - && nonBreakableBlocks[i].end >= curPos) { // Skip non-breakable block - res += text.substring(curPos, nonBreakableBlocks[i].end); - curPos = nonBreakableBlocks[i].end; - } - } - if (curPos < text.length - 1) { - res += text.substr(curPos).replace(reLinesSplitter, "$1\n$2"); - } - return res; - } - else { - return text.replace(reLinesSplitter, "$1\n$2"); - } } -}; -CodeMirror.modeExtensions["xml"] = { - commentStart: "", - wordWrapChars: [">"], + CodeMirror.extendMode("javascript", { + commentStart: "/*", + commentEnd: "*/", + wordWrapChars: [";", "\\{", "\\}"], - autoFormatLineBreaks: function (text, startPos, endPos) { - text = text.substring(startPos, endPos); - var lines = text.split("\n"); - var reProcessedPortion = new RegExp("(^\\s*?<|^[^<]*?)(.+)(>\\s*?$|[^>]*?$)"); - var reOpenBrackets = new RegExp("<", "g"); - var reCloseBrackets = new RegExp("(>)([^\r\n])", "g"); - for (var i = 0; i < lines.length; i++) { - var mToProcess = lines[i].match(reProcessedPortion); - if (mToProcess != null && mToProcess.length > 3) { // The line starts with whitespaces and ends with whitespaces - lines[i] = mToProcess[1] + autoFormatLineBreaks: function (text) { + var curPos = 0; + var reLinesSplitter = /(;|\{|\})([^\r\n;])/g; + var nonBreakableBlocks = jsNonBreakableBlocks(text); + if (nonBreakableBlocks != null) { + var res = ""; + for (var i = 0; i < nonBreakableBlocks.length; i++) { + if (nonBreakableBlocks[i].start > curPos) { // Break lines till the block + res += text.substring(curPos, nonBreakableBlocks[i].start).replace(reLinesSplitter, "$1\n$2"); + curPos = nonBreakableBlocks[i].start; + } + if (nonBreakableBlocks[i].start <= curPos + && nonBreakableBlocks[i].end >= curPos) { // Skip non-breakable block + res += text.substring(curPos, nonBreakableBlocks[i].end); + curPos = nonBreakableBlocks[i].end; + } + } + if (curPos < text.length) + res += text.substr(curPos).replace(reLinesSplitter, "$1\n$2"); + return res; + } else { + return text.replace(reLinesSplitter, "$1\n$2"); + } + } + }); + + CodeMirror.extendMode("xml", { + commentStart: "", + wordWrapChars: [">"], + + autoFormatLineBreaks: function (text) { + var lines = text.split("\n"); + var reProcessedPortion = new RegExp("(^\\s*?<|^[^<]*?)(.+)(>\\s*?$|[^>]*?$)"); + var reOpenBrackets = new RegExp("<", "g"); + var reCloseBrackets = new RegExp("(>)([^\r\n])", "g"); + for (var i = 0; i < lines.length; i++) { + var mToProcess = lines[i].match(reProcessedPortion); + if (mToProcess != null && mToProcess.length > 3) { // The line starts with whitespaces and ends with whitespaces + lines[i] = mToProcess[1] + mToProcess[2].replace(reOpenBrackets, "\n$&").replace(reCloseBrackets, "$1\n$2") + mToProcess[3]; - continue; + continue; + } } + return lines.join("\n"); } + }); - return lines.join("\n"); + function localModeAt(cm, pos) { + return CodeMirror.innerMode(cm.getMode(), cm.getTokenAt(pos).state).mode; } -}; -CodeMirror.modeExtensions["htmlmixed"] = { - commentStart: "", - wordWrapChars: [">", ";", "\\{", "\\}"], - - getModeInfos: function (text, absPos) { - var modeInfos = new Array(); - modeInfos[0] = - { - pos: 0, - modeExt: CodeMirror.modeExtensions["xml"], - modeName: "xml" - }; - - var modeMatchers = new Array(); - modeMatchers[0] = - { - regex: new RegExp("]*>([\\s\\S]*?)(]*>|$)", "i"), - modeExt: CodeMirror.modeExtensions["css"], - modeName: "css" - }; - modeMatchers[1] = - { - regex: new RegExp("]*>([\\s\\S]*?)(]*>|$)", "i"), - modeExt: CodeMirror.modeExtensions["javascript"], - modeName: "javascript" - }; - - var lastCharPos = (typeof (absPos) !== "undefined" ? absPos : text.length - 1); - // Detect modes for the entire text - for (var i = 0; i < modeMatchers.length; i++) { - var curPos = 0; - while (curPos <= lastCharPos) { - var m = text.substr(curPos).match(modeMatchers[i].regex); - if (m != null) { - if (m.length > 1 && m[1].length > 0) { - // Push block begin pos - var blockBegin = curPos + m.index + m[0].indexOf(m[1]); - modeInfos.push( - { - pos: blockBegin, - modeExt: modeMatchers[i].modeExt, - modeName: modeMatchers[i].modeName - }); - // Push block end pos - modeInfos.push( - { - pos: blockBegin + m[1].length, - modeExt: modeInfos[0].modeExt, - modeName: modeInfos[0].modeName - }); - curPos += m.index + m[0].length; - continue; - } - else { - curPos += m.index + Math.max(m[0].length, 1); - } - } - else { // No more matches - break; - } + function enumerateModesBetween(cm, line, start, end) { + var outer = cm.getMode(), text = cm.getLine(line); + if (end == null) end = text.length; + if (!outer.innerMode) return [{from: start, to: end, mode: outer}]; + var state = cm.getTokenAt({line: line, ch: start}).state; + var mode = CodeMirror.innerMode(outer, state).mode; + var found = [], stream = new CodeMirror.StringStream(text); + stream.pos = stream.start = start; + for (;;) { + outer.token(stream, state); + var curMode = CodeMirror.innerMode(outer, state).mode; + if (curMode != mode) { + var cut = stream.start; + // Crappy heuristic to deal with the fact that a change in + // mode can occur both at the end and the start of a token, + // and we don't know which it was. + if (mode.name == "xml" && text.charAt(stream.pos - 1) == ">") cut = stream.pos; + found.push({from: start, to: cut, mode: mode}); + start = cut; + mode = curMode; } + if (stream.pos >= end) break; + stream.start = stream.pos; } - // Sort mode infos - modeInfos.sort(function sortModeInfo(a, b) { - return a.pos - b.pos; + if (start < end) found.push({from: start, to: end, mode: mode}); + return found; + } + + // Comment/uncomment the specified range + CodeMirror.defineExtension("commentRange", function (isComment, from, to) { + var curMode = localModeAt(this, from), cm = this; + this.operation(function() { + if (isComment) { // Comment range + cm.replaceRange(curMode.commentEnd, to); + cm.replaceRange(curMode.commentStart, from); + if (from.line == to.line && from.ch == to.ch) // An empty comment inserted - put cursor inside + cm.setCursor(from.line, from.ch + curMode.commentStart.length); + } else { // Uncomment range + var selText = cm.getRange(from, to); + var startIndex = selText.indexOf(curMode.commentStart); + var endIndex = selText.lastIndexOf(curMode.commentEnd); + if (startIndex > -1 && endIndex > -1 && endIndex > startIndex) { + // Take string till comment start + selText = selText.substr(0, startIndex) + // From comment start till comment end + + selText.substring(startIndex + curMode.commentStart.length, endIndex) + // From comment end till string end + + selText.substr(endIndex + curMode.commentEnd.length); + } + cm.replaceRange(selText, from, to); + } }); + }); - return modeInfos; - }, - - autoFormatLineBreaks: function (text, startPos, endPos) { - var modeInfos = this.getModeInfos(text); - var reBlockStartsWithNewline = new RegExp("^\\s*?\n"); - var reBlockEndsWithNewline = new RegExp("\n\\s*?$"); - var res = ""; - // Use modes info to break lines correspondingly - if (modeInfos.length > 1) { // Deal with multi-mode text - for (var i = 1; i <= modeInfos.length; i++) { - var selStart = modeInfos[i - 1].pos; - var selEnd = (i < modeInfos.length ? modeInfos[i].pos : endPos); - - if (selStart >= endPos) { // The block starts later than the needed fragment - break; - } - if (selStart < startPos) { - if (selEnd <= startPos) { // The block starts earlier than the needed fragment - continue; - } - selStart = startPos; - } - if (selEnd > endPos) { - selEnd = endPos; - } - var textPortion = text.substring(selStart, selEnd); - if (modeInfos[i - 1].modeName != "xml") { // Starting a CSS or JavaScript block - if (!reBlockStartsWithNewline.test(textPortion) - && selStart > 0) { // The block does not start with a line break - textPortion = "\n" + textPortion; - } - if (!reBlockEndsWithNewline.test(textPortion) - && selEnd < text.length - 1) { // The block does not end with a line break - textPortion += "\n"; - } - } - res += modeInfos[i - 1].modeExt.autoFormatLineBreaks(textPortion); + // Applies automatic mode-aware indentation to the specified range + CodeMirror.defineExtension("autoIndentRange", function (from, to) { + var cmInstance = this; + this.operation(function () { + for (var i = from.line; i <= to.line; i++) { + cmInstance.indentLine(i, "smart"); } - } - else { // Single-mode text - res = modeInfos[0].modeExt.autoFormatLineBreaks(text.substring(startPos, endPos)); - } + }); + }); - return res; - } -}; + // Applies automatic formatting to the specified range + CodeMirror.defineExtension("autoFormatRange", function (from, to) { + var cm = this; + cm.operation(function () { + for (var cur = from.line, end = to.line; cur <= end; ++cur) { + var f = {line: cur, ch: cur == from.line ? from.ch : 0}; + var t = {line: cur, ch: cur == end ? to.ch : null}; + var modes = enumerateModesBetween(cm, cur, f.ch, t.ch), mangled = ""; + var text = cm.getRange(f, t); + for (var i = 0; i < modes.length; ++i) { + var part = modes.length > 1 ? text.slice(modes[i].from, modes[i].to) : text; + if (mangled) mangled += "\n"; + if (modes[i].mode.autoFormatLineBreaks) { + mangled += modes[i].mode.autoFormatLineBreaks(part); + } else mangled += text; + } + if (mangled != text) { + for (var count = 0, pos = mangled.indexOf("\n"); pos != -1; pos = mangled.indexOf("\n", pos + 1), ++count) {} + cm.replaceRange(mangled, f, t); + cur += count; + end += count; + } + } + for (var cur = from.line + 1; cur <= end; ++cur) + cm.indentLine(cur, "smart"); + cm.setSelection(from, cm.getCursor(false)); + }); + }); +})(); diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/javascript-hint.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/javascript-hint.js index 2117e5af14..ff15adb3ac 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/javascript-hint.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/javascript-hint.js @@ -38,7 +38,7 @@ case '(': level--; break; default: break; } - } while (level > 0) + } while (level > 0); tprop = getToken(editor, {line: cur.line, ch: tprop.start}); if (tprop.className == 'variable') tprop.className = 'function'; @@ -55,7 +55,7 @@ CodeMirror.javascriptHint = function(editor) { return scriptHint(editor, javascriptKeywords, function (e, cur) {return e.getTokenAt(cur);}); - } + }; function getCoffeeScriptToken(editor, cur) { // This getToken, it is for coffeescript, imitates the behavior of @@ -77,7 +77,7 @@ CodeMirror.coffeescriptHint = function(editor) { return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken); - } + }; var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " + "toUpperCase toLowerCase split concat match replace search").split(" "); diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/loadmode.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/loadmode.js index 48d5a7abf2..60fafbb178 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/loadmode.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/loadmode.js @@ -4,7 +4,7 @@ var loading = {}; function splitCallback(cont, n) { var countDown = n; - return function() { if (--countDown == 0) cont(); } + return function() { if (--countDown == 0) cont(); }; } function ensureDeps(mode, cont) { var deps = CodeMirror.modes[mode].dependencies; diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/multiplex.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/multiplex.js index c8bb1e9785..214730839d 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/multiplex.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/multiplex.js @@ -35,7 +35,7 @@ CodeMirror.multiplexingMode = function(outer /*, others */) { if (found == stream.pos) { stream.match(other.open); state.innerActive = other; - state.inner = CodeMirror.startState(other.mode, outer.indent(state.outer, "")); + state.inner = CodeMirror.startState(other.mode, outer.indent ? outer.indent(state.outer, "") : 0); return other.delimStyle; } else if (found != -1 && found < cutOff) { cutOff = found; @@ -68,14 +68,10 @@ CodeMirror.multiplexingMode = function(outer /*, others */) { return mode.indent(state.innerActive ? state.inner : state.outer, textAfter); }, - compareStates: function(a, b) { - if (a.innerActive != b.innerActive) return false; - var mode = a.innerActive || outer; - if (!mode.compareStates) return CodeMirror.Pass; - return mode.compareStates(a.innerActive ? a.inner : a.outer, - b.innerActive ? b.inner : b.outer); - }, + electricChars: outer.electricChars, - electricChars: outer.electricChars + innerMode: function(state) { + return state.inner ? {state: state.inner, mode: state.innerActive.mode} : {state: state.outer, mode: outer}; + } }; }; diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/overlay.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/overlay.js index 1d5df6c64c..c38d0ca6bb 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/overlay.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/overlay.js @@ -47,6 +47,8 @@ CodeMirror.overlayMode = CodeMirror.overlayParser = function(base, overlay, comb indent: base.indent && function(state, textAfter) { return base.indent(state.base, textAfter); }, - electricChars: base.electricChars + electricChars: base.electricChars, + + innerMode: function(state) { return {state: state.base, mode: base}; } }; }; diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/pig-hint.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/pig-hint.js index aba1c5e282..08e0dbfae0 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/pig-hint.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/pig-hint.js @@ -43,7 +43,7 @@ CodeMirror.pigHint = function(editor) { return scriptHint(editor, pigKeywordsU, function (e, cur) {return e.getTokenAt(cur);}); - } + }; function toTitleCase(str) { return str.replace(/(?:^|\s)\w/g, function(match) { @@ -71,12 +71,10 @@ + "ISEMPTY JSONLOADER JSONMETADATA JSONSTORAGE LAST_INDEX_OF LCFIRST LOG LOG10 LOWER LONGABS " + "LONGAVG LONGMAX LONGMIN LONGSUM MAX MIN MAPSIZE MONITOREDUDF NONDETERMINISTIC OUTPUTSCHEMA " + "PIGSTORAGE PIGSTREAMING RANDOM REGEX_EXTRACT REGEX_EXTRACT_ALL REPLACE ROUND SIN SINH SIZE " - + "SQRT STRSPLIT SUBSTRING SUM STRINGCONCAT STRINGMAX STRINGMIN STRINGSIZE TAN TANH TOBAG " - + "TOKENIZE TOMAP TOP TOTUPLE TRIM TEXTLOADER TUPLESIZE UCFIRST UPPER UTF8STORAGECONVERTER"; - - var pigBuiltinsU = pigBuiltins.split(" ").join("() ").split(" "); - var pigBuiltinsL = pigBuiltins.toLowerCase().split(" ").join("() ").split(" "); - + + "SQRT STRSPLIT SUBSTRING SUM STRINGCONCAT STRINGMAX STRINGMIN STRINGSIZE TAN TANH TOBAG " + + "TOKENIZE TOMAP TOP TOTUPLE TRIM TEXTLOADER TUPLESIZE UCFIRST UPPER UTF8STORAGECONVERTER"; + var pigBuiltinsU = pigBuiltins.split(" ").join("() ").split(" "); + var pigBuiltinsL = pigBuiltins.toLowerCase().split(" ").join("() ").split(" "); var pigBuiltinsC = ("BagSize BinStorage Bloom BuildBloom ConstantSize CubeDimensions DoubleAbs " + "DoubleAvg DoubleBase DoubleMax DoubleMin DoubleRound DoubleSum FloatAbs FloatAvg FloatMax " + "FloatMin FloatRound FloatSum GenericInvoker IntAbs IntAvg IntMax IntMin IntSum " diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/runmode-standalone.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/runmode-standalone.js new file mode 100644 index 0000000000..afdf044d8d --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/runmode-standalone.js @@ -0,0 +1,90 @@ +/* Just enough of CodeMirror to run runMode under node.js */ + +function splitLines(string){ return string.split(/\r?\n|\r/); }; + +function StringStream(string) { + this.pos = this.start = 0; + this.string = string; +} +StringStream.prototype = { + eol: function() {return this.pos >= this.string.length;}, + sol: function() {return this.pos == 0;}, + peek: function() {return this.string.charAt(this.pos) || null;}, + next: function() { + if (this.pos < this.string.length) + return this.string.charAt(this.pos++); + }, + eat: function(match) { + var ch = this.string.charAt(this.pos); + if (typeof match == "string") var ok = ch == match; + else var ok = ch && (match.test ? match.test(ch) : match(ch)); + if (ok) {++this.pos; return ch;} + }, + eatWhile: function(match) { + var start = this.pos; + while (this.eat(match)){} + return this.pos > start; + }, + eatSpace: function() { + var start = this.pos; + while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos; + return this.pos > start; + }, + skipToEnd: function() {this.pos = this.string.length;}, + skipTo: function(ch) { + var found = this.string.indexOf(ch, this.pos); + if (found > -1) {this.pos = found; return true;} + }, + backUp: function(n) {this.pos -= n;}, + column: function() {return this.start;}, + indentation: function() {return 0;}, + match: function(pattern, consume, caseInsensitive) { + if (typeof pattern == "string") { + function cased(str) {return caseInsensitive ? str.toLowerCase() : str;} + if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) { + if (consume !== false) this.pos += pattern.length; + return true; + } + } + else { + var match = this.string.slice(this.pos).match(pattern); + if (match && consume !== false) this.pos += match[0].length; + return match; + } + }, + current: function(){return this.string.slice(this.start, this.pos);} +}; +exports.StringStream = StringStream; + +exports.startState = function(mode, a1, a2) { + return mode.startState ? mode.startState(a1, a2) : true; +}; + +var modes = exports.modes = {}, mimeModes = exports.mimeModes = {}; +exports.defineMode = function(name, mode) { modes[name] = mode; }; +exports.defineMIME = function(mime, spec) { mimeModes[mime] = spec; }; +exports.getMode = function(options, spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) + spec = mimeModes[spec]; + if (typeof spec == "string") + var mname = spec, config = {}; + else if (spec != null) + var mname = spec.name, config = spec; + var mfactory = modes[mname]; + if (!mfactory) throw new Error("Unknown mode: " + spec); + return mfactory(options, config || {}); +}; + +exports.runMode = function(string, modespec, callback) { + var mode = exports.getMode({indentUnit: 2}, modespec); + var lines = splitLines(string), state = exports.startState(mode); + for (var i = 0, e = lines.length; i < e; ++i) { + if (i) callback("\n"); + var stream = new exports.StringStream(lines[i]); + while (!stream.eol()) { + var style = mode.token(stream, state); + callback(stream.current(), style, i, stream.start); + stream.start = stream.pos; + } + } +}; diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/runmode.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/runmode.js index fc58d857d9..6723927f2d 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/runmode.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/runmode.js @@ -1,4 +1,8 @@ CodeMirror.runMode = function(string, modespec, callback, options) { + function esc(str) { + return str.replace(/[<&]/, function(ch) { return ch == "<" ? "<" : "&"; }); + } + var mode = CodeMirror.getMode(CodeMirror.defaults, modespec); var isNode = callback.nodeType == 1; var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize; @@ -15,12 +19,12 @@ CodeMirror.runMode = function(string, modespec, callback, options) { for (var pos = 0;;) { var idx = text.indexOf("\t", pos); if (idx == -1) { - escaped += CodeMirror.htmlEscape(text.slice(pos)); + escaped += esc(text.slice(pos)); col += text.length - pos; break; } else { col += idx - pos; - escaped += CodeMirror.htmlEscape(text.slice(pos, idx)); + escaped += esc(text.slice(pos, idx)); var size = tabSize - col % tabSize; col += size; for (var i = 0; i < size; ++i) escaped += " "; @@ -29,10 +33,10 @@ CodeMirror.runMode = function(string, modespec, callback, options) { } if (style) - accum.push("" + escaped + ""); + accum.push("" + escaped + ""); else accum.push(escaped); - } + }; } var lines = CodeMirror.splitLines(string), state = CodeMirror.startState(mode); for (var i = 0, e = lines.length; i < e; ++i) { diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/search.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/search.js index c5a2bccfaf..356283ab84 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/search.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/search.js @@ -40,7 +40,7 @@ if (!query || state.query) return; state.query = parseQuery(query); if (cm.lineCount() < 2000) { // This is too expensive on big documents. - for (var cursor = getSearchCursor(cm, query); cursor.findNext();) + for (var cursor = getSearchCursor(cm, state.query); cursor.findNext();) state.marked.push(cm.markText(cursor.from(), cursor.to(), "CodeMirror-searching")); } state.posFrom = state.posTo = cm.getCursor(); @@ -57,14 +57,14 @@ } cm.setSelection(cursor.from(), cursor.to()); state.posFrom = cursor.from(); state.posTo = cursor.to(); - })} + });} function clearSearch(cm) {cm.operation(function() { var state = getSearchState(cm); if (!state.query) return; state.query = null; for (var i = 0; i < state.marked.length; ++i) state.marked[i].clear(); state.marked.length = 0; - })} + });} var replaceQueryDialog = 'Replace: (Use /re/ syntax for regexp search)'; @@ -83,7 +83,7 @@ cursor.replace(text.replace(/\$(\d)/, function(w, i) {return match[i];})); } else cursor.replace(text); } - })}); + });}); } else { clearSearch(cm); var cursor = getSearchCursor(cm, query, cm.getCursor()); diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/searchcursor.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/searchcursor.js index ec3f73c3fa..970af899d7 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/searchcursor.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/searchcursor.js @@ -10,30 +10,32 @@ // It takes a position and a direction, and returns an object // describing the next occurrence of the query, or null if no // more matches were found. - if (typeof query != "string") // Regexp match + if (typeof query != "string") { // Regexp match + if (!query.global) query = new RegExp(query.source, query.ignoreCase ? "ig" : "g"); this.matches = function(reverse, pos) { if (reverse) { - var line = cm.getLine(pos.line).slice(0, pos.ch), match = line.match(query), start = 0; + query.lastIndex = 0; + var line = cm.getLine(pos.line).slice(0, pos.ch), match = query.exec(line), start = 0; while (match) { - var ind = line.indexOf(match[0]); - start += ind; - line = line.slice(ind + 1); - var newmatch = line.match(query); + start += match.index; + line = line.slice(match.index); + query.lastIndex = 0; + var newmatch = query.exec(line); if (newmatch) match = newmatch; else break; start++; } - } - else { - var line = cm.getLine(pos.line).slice(pos.ch), match = line.match(query), - start = match && pos.ch + line.indexOf(match[0]); + } else { + query.lastIndex = pos.ch; + var line = cm.getLine(pos.line), match = query.exec(line), + start = match && match.index; } if (match) return {from: {line: pos.line, ch: start}, to: {line: pos.line, ch: start + match[0].length}, match: match}; }; - else { // String query + } else { // String query if (caseFold) query = query.toLowerCase(); var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;}; var target = query.split("\n"); diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/simple-hint.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/simple-hint.js new file mode 100644 index 0000000000..04909cb236 --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/simple-hint.js @@ -0,0 +1,97 @@ +(function() { + CodeMirror.simpleHint = function(editor, getHints, givenOptions) { + // Determine effective options based on given values and defaults. + var options = {}, defaults = CodeMirror.simpleHint.defaults; + for (var opt in defaults) + if (defaults.hasOwnProperty(opt)) + options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt]; + + function collectHints(previousToken) { + // We want a single cursor position. + if (editor.somethingSelected()) return; + + var tempToken = editor.getTokenAt(editor.getCursor()); + + // Don't show completions if token has changed and the option is set. + if (options.closeOnTokenChange && previousToken != null && + (tempToken.start != previousToken.start || tempToken.className != previousToken.className)) { + return; + } + + var result = getHints(editor); + if (!result || !result.list.length) return; + var completions = result.list; + function insert(str) { + editor.replaceRange(str, result.from, result.to); + } + // When there is only one completion, use it directly. + if (completions.length == 1) {insert(completions[0]); return true;} + + // Build the select widget + var complete = document.createElement("div"); + complete.className = "CodeMirror-completions"; + var sel = complete.appendChild(document.createElement("select")); + // Opera doesn't move the selection when pressing up/down in a + // multi-select, but it does properly support the size property on + // single-selects, so no multi-select is necessary. + if (!window.opera) sel.multiple = true; + for (var i = 0; i < completions.length; ++i) { + var opt = sel.appendChild(document.createElement("option")); + opt.appendChild(document.createTextNode(completions[i])); + } + sel.firstChild.selected = true; + sel.size = Math.min(10, completions.length); + var pos = editor.cursorCoords(); + complete.style.left = pos.x + "px"; + complete.style.top = pos.yBot + "px"; + document.body.appendChild(complete); + // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor. + var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth); + if(winW - pos.x < sel.clientWidth) + complete.style.left = (pos.x - sel.clientWidth) + "px"; + // Hack to hide the scrollbar. + if (completions.length <= 10) + complete.style.width = (sel.clientWidth - 1) + "px"; + + var done = false; + function close() { + if (done) return; + done = true; + complete.parentNode.removeChild(complete); + } + function pick() { + insert(completions[sel.selectedIndex]); + close(); + setTimeout(function(){editor.focus();}, 50); + } + CodeMirror.connect(sel, "blur", close); + CodeMirror.connect(sel, "keydown", function(event) { + var code = event.keyCode; + // Enter + if (code == 13) {CodeMirror.e_stop(event); pick();} + // Escape + else if (code == 27) {CodeMirror.e_stop(event); close(); editor.focus();} + else if (code != 38 && code != 40 && code != 33 && code != 34) { + close(); editor.focus(); + // Pass the event to the CodeMirror instance so that it can handle things like backspace properly. + editor.triggerOnKeyDown(event); + // Don't show completions if the code is backspace and the option is set. + if (!options.closeOnBackspace || code != 8) { + setTimeout(function(){collectHints(tempToken);}, 50); + } + } + }); + CodeMirror.connect(sel, "dblclick", pick); + + sel.focus(); + // Opera sometimes ignores focusing a freshly created node + if (window.opera) setTimeout(function(){if (!done) sel.focus();}, 100); + return true; + } + return collectHints(); + }; + CodeMirror.simpleHint.defaults = { + closeOnBackspace: true, + closeOnTokenChange: false + }; +})(); diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/xml-hint.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/xml-hint.js index 5f04976c7b..816e3b4a69 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/xml-hint.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/lib/util/xml-hint.js @@ -1,8 +1,10 @@ - + (function() { CodeMirror.xmlHints = []; + CodeMirror.xmlHint = function(cm, simbol) { + if(simbol.length > 0) { var cursor = cm.getCursor(); cm.replaceSelection(simbol); @@ -13,7 +15,7 @@ // dirty hack for simple-hint to receive getHint event on space var getTokenAt = editor.getTokenAt; - editor.getTokenAt = function() { return 'disabled'; } + editor.getTokenAt = function() { return 'disabled'; }; CodeMirror.simpleHint(cm, getHint); editor.getTokenAt = getTokenAt; @@ -38,7 +40,7 @@ } } - text = text.substr(0, text.length - typed.length); + text = text.slice(0, text.length - typed.length); var path = getActiveElement(cm, text) + simbol; var hints = CodeMirror.xmlHints[path]; @@ -56,10 +58,10 @@ return { list: hints, from: { line: cursor.line, ch: cursor.ch - typed.length }, - to: cursor, + to: cursor }; }; - } + }; var getActiveElement = function(codeMirror, text) { @@ -74,7 +76,7 @@ while ((match = regex.exec(text)) != null) { matches.push({ tag: match[1], - selfclose: (match[0].substr(-1) === '/>') + selfclose: (match[0].slice(match[0].length - 2) === '/>') }); } @@ -112,7 +114,7 @@ if (close < open) { - text = text.substr(open); + text = text.slice(open); if(text != '<') { @@ -125,7 +127,7 @@ if (space < 0) space = text.length; - return text.substr(0, space); + return text.slice(0, space); } } diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/clike/clike.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/clike/clike.js index e0c7d18e55..59555e9432 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/clike/clike.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/clike/clike.js @@ -181,14 +181,18 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { return "string"; } - CodeMirror.defineMIME("text/x-csrc", { + function mimes(ms, mode) { + for (var i = 0; i < ms.length; ++i) CodeMirror.defineMIME(ms[i], mode); + } + + mimes(["text/x-csrc", "text/x-c", "text/x-chdr"], { name: "clike", keywords: words(cKeywords), blockKeywords: words("case do else for if switch while struct"), atoms: words("null"), hooks: {"#": cppHook} }); - CodeMirror.defineMIME("text/x-c++src", { + mimes(["text/x-c++src", "text/x-c++hdr"], { name: "clike", keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try bool explicit new " + "static_cast typeid catch operator template typename class friend private " + diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/clike/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/clike/index.html index 64d02f11e7..90a5fc105b 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/clike/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/clike/index.html @@ -1,6 +1,7 @@ + CodeMirror: C-like mode diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/clike/scala.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/clike/scala.html index 5fdd84ede5..a5aba99cac 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/clike/scala.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/clike/scala.html @@ -1,6 +1,7 @@ + CodeMirror: C-like mode diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/clojure/clojure.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/clojure/clojure.js index c683641554..84f6073fd5 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/clojure/clojure.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/clojure/clojure.js @@ -40,9 +40,9 @@ CodeMirror.defineMode("clojure", function (config, mode) { var tests = { digit: /\d/, digit_or_colon: /[\d:]/, - hex: /[0-9a-fA-F]/, + hex: /[0-9a-f]/i, sign: /[+-]/, - exponent: /[eE]/, + exponent: /e/i, keyword_char: /[^\s\(\[\;\)\]]/, basic: /[\w\$_\-]/, lang_keyword: /[\w*+!\-_?:\/]/ @@ -64,14 +64,13 @@ CodeMirror.defineMode("clojure", function (config, mode) { function isNumber(ch, stream){ // hex - if ( ch === '0' && 'x' == stream.peek().toLowerCase() ) { - stream.eat('x'); + if ( ch === '0' && stream.eat(/x/i) ) { stream.eatWhile(tests.hex); return true; } // leading sign - if ( ch == '+' || ch == '-' ) { + if ( ( ch == '+' || ch == '-' ) && ( tests.digit.test(stream.peek()) ) ) { stream.eat(tests.sign); ch = stream.next(); } @@ -85,8 +84,7 @@ CodeMirror.defineMode("clojure", function (config, mode) { stream.eatWhile(tests.digit); } - if ( 'e' == stream.peek().toLowerCase() ) { - stream.eat(tests.exponent); + if ( stream.eat(tests.exponent) ) { stream.eat(tests.sign); stream.eatWhile(tests.digit); } @@ -145,7 +143,7 @@ CodeMirror.defineMode("clojure", function (config, mode) { } else if (isNumber(ch,stream)){ returnType = NUMBER; } else if (ch == "(" || ch == "[") { - var keyWord = ''; var indentTemp = stream.column(); + var keyWord = '', indentTemp = stream.column(), letter; /** Either (indent-word .. @@ -157,7 +155,8 @@ CodeMirror.defineMode("clojure", function (config, mode) { keyWord += letter; } - if (keyWord.length > 0 && indentKeys.propertyIsEnumerable(keyWord)) { // indent-word + if (keyWord.length > 0 && (indentKeys.propertyIsEnumerable(keyWord) || + /^(?:def|with)/.test(keyWord))) { // indent-word pushStack(state, indentTemp + INDENT_WORD_SKIP, ch); } else { // non-indent word // we continue eating the spaces diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/clojure/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/clojure/index.html index 9762d5892f..bce0473530 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/clojure/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/clojure/index.html @@ -1,6 +1,7 @@ + CodeMirror: Clojure mode diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/coffeescript/coffeescript.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/coffeescript/coffeescript.js index d8bfe36f24..e9b97f14eb 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/coffeescript/coffeescript.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/coffeescript/coffeescript.js @@ -187,7 +187,7 @@ CodeMirror.defineMode('coffeescript', function(conf) { } if (singleline) { if (conf.mode.singleLineStringErrors) { - outclass = ERRORCLASS + outclass = ERRORCLASS; } else { state.tokenize = tokenBase; } @@ -205,7 +205,7 @@ CodeMirror.defineMode('coffeescript', function(conf) { } stream.eatWhile("#"); } - return "comment" + return "comment"; } function indent(stream, state, type) { @@ -244,7 +244,7 @@ CodeMirror.defineMode('coffeescript', function(conf) { while (state.scopes[0].offset !== _indent) { state.scopes.shift(); } - return false + return false; } else { state.scopes.shift(); return false; diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/coffeescript/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/coffeescript/index.html index 98bd4971e3..ee72b8d2f2 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/coffeescript/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/coffeescript/index.html @@ -1,6 +1,7 @@ + CodeMirror: CoffeeScript mode diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/commonlisp/commonlisp.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/commonlisp/commonlisp.js new file mode 100644 index 0000000000..4fb4bdf9bd --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/commonlisp/commonlisp.js @@ -0,0 +1,101 @@ +CodeMirror.defineMode("commonlisp", function (config) { + var assumeBody = /^with|^def|^do|^prog|case$|^cond$|bind$|when$|unless$/; + var numLiteral = /^(?:[+\-]?(?:\d+|\d*\.\d+)(?:[efd][+\-]?\d+)?|[+\-]?\d+(?:\/[+\-]?\d+)?|#b[+\-]?[01]+|#o[+\-]?[0-7]+|#x[+\-]?[\da-f]+)/; + var symbol = /[^\s'`,@()\[\]";]/; + var type; + + function readSym(stream) { + var ch; + while (ch = stream.next()) { + if (ch == "\\") stream.next(); + else if (!symbol.test(ch)) { stream.backUp(1); break; } + } + return stream.current(); + } + + function base(stream, state) { + if (stream.eatSpace()) {type = "ws"; return null;} + if (stream.match(numLiteral)) return "number"; + var ch = stream.next(); + if (ch == "\\") ch = stream.next(); + + if (ch == '"') return (state.tokenize = inString)(stream, state); + else if (ch == "(") { type = "open"; return "bracket"; } + else if (ch == ")" || ch == "]") { type = "close"; return "bracket"; } + else if (ch == ";") { stream.skipToEnd(); type = "ws"; return "comment"; } + else if (/['`,@]/.test(ch)) return null; + else if (ch == "|") { + if (stream.skipTo("|")) { stream.next(); return "symbol"; } + else { stream.skipToEnd(); return "error"; } + } else if (ch == "#") { + var ch = stream.next(); + if (ch == "[") { type = "open"; return "bracket"; } + else if (/[+\-=\.']/.test(ch)) return null; + else if (/\d/.test(ch) && stream.match(/^\d*#/)) return null; + else if (ch == "|") return (state.tokenize = inComment)(stream, state); + else if (ch == ":") { readSym(stream); return "meta"; } + else return "error"; + } else { + var name = readSym(stream); + if (name == ".") return null; + type = "symbol"; + if (name == "nil" || name == "t") return "atom"; + if (name.charAt(0) == ":") return "keyword"; + if (name.charAt(0) == "&") return "variable-2"; + return "variable"; + } + } + + function inString(stream, state) { + var escaped = false, next; + while (next = stream.next()) { + if (next == '"' && !escaped) { state.tokenize = base; break; } + escaped = !escaped && next == "\\"; + } + return "string"; + } + + function inComment(stream, state) { + var next, last; + while (next = stream.next()) { + if (next == "#" && last == "|") { state.tokenize = base; break; } + last = next; + } + type = "ws"; + return "comment"; + } + + return { + startState: function () { + return {ctx: {prev: null, start: 0, indentTo: 0}, tokenize: base}; + }, + + token: function (stream, state) { + if (stream.sol() && typeof state.ctx.indentTo != "number") + state.ctx.indentTo = state.ctx.start + 1; + + type = null; + var style = state.tokenize(stream, state); + if (type != "ws") { + if (state.ctx.indentTo == null) { + if (type == "symbol" && assumeBody.test(stream.current())) + state.ctx.indentTo = state.ctx.start + config.indentUnit; + else + state.ctx.indentTo = "next"; + } else if (state.ctx.indentTo == "next") { + state.ctx.indentTo = stream.column(); + } + } + if (type == "open") state.ctx = {prev: state.ctx, start: stream.column(), indentTo: null}; + else if (type == "close") state.ctx = state.ctx.prev || state.ctx; + return style; + }, + + indent: function (state, textAfter) { + var i = state.ctx.indentTo; + return typeof i == "number" ? i : state.ctx.start + 1; + } + }; +}); + +CodeMirror.defineMIME("text/x-common-lisp", "commonlisp"); diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/commonlisp/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/commonlisp/index.html new file mode 100644 index 0000000000..f9766a844c --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/commonlisp/index.html @@ -0,0 +1,165 @@ + + + + + CodeMirror: Common Lisp mode + + + + + + + +

CodeMirror: Common Lisp mode

+
+ + +

MIME types defined: text/x-common-lisp.

+ + + diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/css/css.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/css/css.js index 050e1124c5..5e3e233edb 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/css/css.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/css/css.js @@ -1,10 +1,196 @@ CodeMirror.defineMode("css", function(config) { var indentUnit = config.indentUnit, type; + + var atMediaTypes = keySet([ + "all", "aural", "braille", "handheld", "print", "projection", "screen", + "tty", "tv", "embossed" + ]); + + var atMediaFeatures = keySet([ + "width", "min-width", "max-width", "height", "min-height", "max-height", + "device-width", "min-device-width", "max-device-width", "device-height", + "min-device-height", "max-device-height", "aspect-ratio", + "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio", + "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color", + "max-color", "color-index", "min-color-index", "max-color-index", + "monochrome", "min-monochrome", "max-monochrome", "resolution", + "min-resolution", "max-resolution", "scan", "grid" + ]); + + var propertyKeywords = keySet([ + "align-content", "align-items", "align-self", "alignment-adjust", + "alignment-baseline", "anchor-point", "animation", "animation-delay", + "animation-direction", "animation-duration", "animation-iteration-count", + "animation-name", "animation-play-state", "animation-timing-function", + "appearance", "azimuth", "backface-visibility", "background", + "background-attachment", "background-clip", "background-color", + "background-image", "background-origin", "background-position", + "background-repeat", "background-size", "baseline-shift", "binding", + "bleed", "bookmark-label", "bookmark-level", "bookmark-state", + "bookmark-target", "border", "border-bottom", "border-bottom-color", + "border-bottom-left-radius", "border-bottom-right-radius", + "border-bottom-style", "border-bottom-width", "border-collapse", + "border-color", "border-image", "border-image-outset", + "border-image-repeat", "border-image-slice", "border-image-source", + "border-image-width", "border-left", "border-left-color", + "border-left-style", "border-left-width", "border-radius", "border-right", + "border-right-color", "border-right-style", "border-right-width", + "border-spacing", "border-style", "border-top", "border-top-color", + "border-top-left-radius", "border-top-right-radius", "border-top-style", + "border-top-width", "border-width", "bottom", "box-decoration-break", + "box-shadow", "box-sizing", "break-after", "break-before", "break-inside", + "caption-side", "clear", "clip", "color", "color-profile", "column-count", + "column-fill", "column-gap", "column-rule", "column-rule-color", + "column-rule-style", "column-rule-width", "column-span", "column-width", + "columns", "content", "counter-increment", "counter-reset", "crop", "cue", + "cue-after", "cue-before", "cursor", "direction", "display", + "dominant-baseline", "drop-initial-after-adjust", + "drop-initial-after-align", "drop-initial-before-adjust", + "drop-initial-before-align", "drop-initial-size", "drop-initial-value", + "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis", + "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap", + "float", "float-offset", "font", "font-feature-settings", "font-family", + "font-kerning", "font-language-override", "font-size", "font-size-adjust", + "font-stretch", "font-style", "font-synthesis", "font-variant", + "font-variant-alternates", "font-variant-caps", "font-variant-east-asian", + "font-variant-ligatures", "font-variant-numeric", "font-variant-position", + "font-weight", "grid-cell", "grid-column", "grid-column-align", + "grid-column-sizing", "grid-column-span", "grid-columns", "grid-flow", + "grid-row", "grid-row-align", "grid-row-sizing", "grid-row-span", + "grid-rows", "grid-template", "hanging-punctuation", "height", "hyphens", + "icon", "image-orientation", "image-rendering", "image-resolution", + "inline-box-align", "justify-content", "left", "letter-spacing", + "line-break", "line-height", "line-stacking", "line-stacking-ruby", + "line-stacking-shift", "line-stacking-strategy", "list-style", + "list-style-image", "list-style-position", "list-style-type", "margin", + "margin-bottom", "margin-left", "margin-right", "margin-top", + "marker-offset", "marks", "marquee-direction", "marquee-loop", + "marquee-play-count", "marquee-speed", "marquee-style", "max-height", + "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index", + "nav-left", "nav-right", "nav-up", "opacity", "order", "orphans", "outline", + "outline-color", "outline-offset", "outline-style", "outline-width", + "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y", + "padding", "padding-bottom", "padding-left", "padding-right", "padding-top", + "page", "page-break-after", "page-break-before", "page-break-inside", + "page-policy", "pause", "pause-after", "pause-before", "perspective", + "perspective-origin", "pitch", "pitch-range", "play-during", "position", + "presentation-level", "punctuation-trim", "quotes", "rendering-intent", + "resize", "rest", "rest-after", "rest-before", "richness", "right", + "rotation", "rotation-point", "ruby-align", "ruby-overhang", + "ruby-position", "ruby-span", "size", "speak", "speak-as", "speak-header", + "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set", + "tab-size", "table-layout", "target", "target-name", "target-new", + "target-position", "text-align", "text-align-last", "text-decoration", + "text-decoration-color", "text-decoration-line", "text-decoration-skip", + "text-decoration-style", "text-emphasis", "text-emphasis-color", + "text-emphasis-position", "text-emphasis-style", "text-height", + "text-indent", "text-justify", "text-outline", "text-shadow", + "text-space-collapse", "text-transform", "text-underline-position", + "text-wrap", "top", "transform", "transform-origin", "transform-style", + "transition", "transition-delay", "transition-duration", + "transition-property", "transition-timing-function", "unicode-bidi", + "vertical-align", "visibility", "voice-balance", "voice-duration", + "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress", + "voice-volume", "volume", "white-space", "widows", "width", "word-break", + "word-spacing", "word-wrap", "z-index" + ]); + + var colorKeywords = keySet([ + "black", "silver", "gray", "white", "maroon", "red", "purple", "fuchsia", + "green", "lime", "olive", "yellow", "navy", "blue", "teal", "aqua" + ]); + + var valueKeywords = keySet([ + "above", "absolute", "activeborder", "activecaption", "afar", + "after-white-space", "ahead", "alias", "all", "all-scroll", "alternate", + "always", "amharic", "amharic-abegede", "antialiased", "appworkspace", + "arabic-indic", "armenian", "asterisks", "auto", "avoid", "background", + "backwards", "baseline", "below", "bidi-override", "binary", "bengali", + "blink", "block", "block-axis", "bold", "bolder", "border", "border-box", + "both", "bottom", "break-all", "break-word", "button", "button-bevel", + "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian", + "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret", + "cell", "center", "checkbox", "circle", "cjk-earthly-branch", + "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote", + "col-resize", "collapse", "compact", "condensed", "contain", "content", + "content-box", "context-menu", "continuous", "copy", "cover", "crop", + "cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal", + "decimal-leading-zero", "default", "default-button", "destination-atop", + "destination-in", "destination-out", "destination-over", "devanagari", + "disc", "discard", "document", "dot-dash", "dot-dot-dash", "dotted", + "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out", + "element", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede", + "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er", + "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er", + "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et", + "ethiopic-halehame-gez", "ethiopic-halehame-om-et", + "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et", + "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", + "ethiopic-halehame-tig", "ew-resize", "expanded", "extra-condensed", + "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes", + "forwards", "from", "geometricPrecision", "georgian", "graytext", "groove", + "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew", + "help", "hidden", "hide", "higher", "highlight", "highlighttext", + "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore", + "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite", + "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis", + "inline-block", "inline-table", "inset", "inside", "intrinsic", "invert", + "italic", "justify", "kannada", "katakana", "katakana-iroha", "khmer", + "landscape", "lao", "large", "larger", "left", "level", "lighter", + "line-through", "linear", "lines", "list-item", "listbox", "listitem", + "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian", + "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian", + "lower-roman", "lowercase", "ltr", "malayalam", "match", + "media-controls-background", "media-current-time-display", + "media-fullscreen-button", "media-mute-button", "media-play-button", + "media-return-to-realtime-button", "media-rewind-button", + "media-seek-back-button", "media-seek-forward-button", "media-slider", + "media-sliderthumb", "media-time-remaining-display", "media-volume-slider", + "media-volume-slider-container", "media-volume-sliderthumb", "medium", + "menu", "menulist", "menulist-button", "menulist-text", + "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic", + "mix", "mongolian", "monospace", "move", "multiple", "myanmar", "n-resize", + "narrower", "navy", "ne-resize", "nesw-resize", "no-close-quote", "no-drop", + "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap", + "ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote", + "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset", + "outside", "overlay", "overline", "padding", "padding-box", "painted", + "paused", "persian", "plus-darker", "plus-lighter", "pointer", "portrait", + "pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button", + "radio", "read-only", "read-write", "read-write-plaintext-only", "relative", + "repeat", "repeat-x", "repeat-y", "reset", "reverse", "rgb", "rgba", + "ridge", "right", "round", "row-resize", "rtl", "run-in", "running", + "s-resize", "sans-serif", "scroll", "scrollbar", "se-resize", "searchfield", + "searchfield-cancel-button", "searchfield-decoration", + "searchfield-results-button", "searchfield-results-decoration", + "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama", + "single", "skip-white-space", "slide", "slider-horizontal", + "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow", + "small", "small-caps", "small-caption", "smaller", "solid", "somali", + "source-atop", "source-in", "source-out", "source-over", "space", "square", + "square-button", "start", "static", "status-bar", "stretch", "stroke", + "sub", "subpixel-antialiased", "super", "sw-resize", "table", + "table-caption", "table-cell", "table-column", "table-column-group", + "table-footer-group", "table-header-group", "table-row", "table-row-group", + "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai", + "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight", + "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er", + "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top", + "transparent", "ultra-condensed", "ultra-expanded", "underline", "up", + "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal", + "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url", + "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted", + "visibleStroke", "visual", "w-resize", "wait", "wave", "white", "wider", + "window", "windowframe", "windowtext", "x-large", "x-small", "xor", + "xx-large", "xx-small", "yellow" + ]); + + function keySet(array) { var keys = {}; for (var i = 0; i < array.length; ++i) keys[array[i]] = true; return keys; } function ret(style, tp) {type = tp; return style;} function tokenBase(stream, state) { var ch = stream.next(); - if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("meta", stream.current());} + if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("def", stream.current());} else if (ch == "/" && stream.eat("*")) { state.tokenize = tokenCComment; return tokenCComment(stream, state); @@ -31,15 +217,29 @@ CodeMirror.defineMode("css", function(config) { stream.eatWhile(/[\w.%]/); return ret("number", "unit"); } - else if (/[,.+>*\/]/.test(ch)) { + else if (ch === "-") { + if (/\d/.test(stream.peek())) { + stream.eatWhile(/[\w.%]/); + return ret("number", "unit"); + } else if (stream.match(/^[^-]+-/)) { + return ret("meta", type); + } + } + else if (/[,+>*\/]/.test(ch)) { return ret(null, "select-op"); } - else if (/[;{}:\[\]]/.test(ch)) { + else if (ch == "." && stream.match(/^\w+/)) { + return ret("qualifier", type); + } + else if (ch == ":") { + return ret("operator", ch); + } + else if (/[;{}\[\]\(\)]/.test(ch)) { return ret(null, ch); } else { stream.eatWhile(/[\w\\\-]/); - return ret("variable", "variable"); + return ret("property", "variable"); } } @@ -88,32 +288,156 @@ CodeMirror.defineMode("css", function(config) { }, token: function(stream, state) { + + // Use these terms when applicable (see http://www.xanthir.com/blog/b4E50) + // + // rule** or **ruleset: + // A selector + braces combo, or an at-rule. + // + // declaration block: + // A sequence of declarations. + // + // declaration: + // A property + colon + value combo. + // + // property value: + // The entire value of a property. + // + // component value: + // A single piece of a property value. Like the 5px in + // text-shadow: 0 0 5px blue;. Can also refer to things that are + // multiple terms, like the 1-4 terms that make up the background-size + // portion of the background shorthand. + // + // term: + // The basic unit of author-facing CSS, like a single number (5), + // dimension (5px), string ("foo"), or function. Officially defined + // by the CSS 2.1 grammar (look for the 'term' production) + // + // + // simple selector: + // A single atomic selector, like a type selector, an attr selector, a + // class selector, etc. + // + // compound selector: + // One or more simple selectors without a combinator. div.example is + // compound, div > .example is not. + // + // complex selector: + // One or more compound selectors chained with combinators. + // + // combinator: + // The parts of selectors that express relationships. There are four + // currently - the space (descendant combinator), the greater-than + // bracket (child combinator), the plus sign (next sibling combinator), + // and the tilda (following sibling combinator). + // + // sequence of selectors: + // One or more of the named type of selector chained with commas. + if (stream.eatSpace()) return null; var style = state.tokenize(stream, state); + // Changing style returned based on context var context = state.stack[state.stack.length-1]; - if (type == "hash" && context != "rule") style = "string-2"; - else if (style == "variable") { - if (context == "rule") style = "number"; - else if (!context || context == "@media{") style = "tag"; + if (style == "property") { + if (context == "propertyValue"){ + if (valueKeywords[stream.current()]) { + style = "string-2"; + } else if (colorKeywords[stream.current()]) { + style = "keyword"; + } else { + style = "variable-2"; + } + } else if (context == "rule") { + if (!propertyKeywords[stream.current()]) { + style += " error"; + } + } else if (!context || context == "@media{") { + style = "tag"; + } else if (context == "@media") { + if (atMediaTypes[stream.current()]) { + style = "attribute"; // Known attribute + } else if (/^(only|not)$/i.test(stream.current())) { + style = "keyword"; + } else if (stream.current().toLowerCase() == "and") { + style = "error"; // "and" is only allowed in @mediaType + } else if (atMediaFeatures[stream.current()]) { + style = "error"; // Known property, should be in @mediaType( + } else { + // Unknown, expecting keyword or attribute, assuming attribute + style = "attribute error"; + } + } else if (context == "@mediaType") { + if (atMediaTypes[stream.current()]) { + style = "attribute"; + } else if (stream.current().toLowerCase() == "and") { + style = "operator"; + } else if (/^(only|not)$/i.test(stream.current())) { + style = "error"; // Only allowed in @media + } else if (atMediaFeatures[stream.current()]) { + style = "error"; // Known property, should be in parentheses + } else { + // Unknown attribute or property, but expecting property (preceded + // by "and"). Should be in parentheses + style = "error"; + } + } else if (context == "@mediaType(") { + if (propertyKeywords[stream.current()]) { + // do nothing, remains "property" + } else if (atMediaTypes[stream.current()]) { + style = "error"; // Known property, should be in parentheses + } else if (stream.current().toLowerCase() == "and") { + style = "operator"; + } else if (/^(only|not)$/i.test(stream.current())) { + style = "error"; // Only allowed in @media + } else { + style += " error"; + } + } else { + style = "error"; + } + } else if (style == "atom") { + if(!context || context == "@media{") { + style = "builtin"; + } else if (context == "propertyValue") { + if (!/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) { + style += " error"; + } + } else { + style = "error"; + } + } else if (context == "@media" && type == "{") { + style = "error"; } - if (context == "rule" && /^[\{\};]$/.test(type)) - state.stack.pop(); + // Push/pop context stack if (type == "{") { - if (context == "@media") state.stack[state.stack.length-1] = "@media{"; - else state.stack.push("{"); + if (context == "@media" || context == "@mediaType") { + state.stack.pop(); + state.stack[state.stack.length-1] = "@media{"; + } + else state.stack.push("rule"); + } + else if (type == "}") { + state.stack.pop(); + if (context == "propertyValue") state.stack.pop(); } - else if (type == "}") state.stack.pop(); else if (type == "@media") state.stack.push("@media"); - else if (context == "{" && type != "comment") state.stack.push("rule"); + else if (context == "@media" && /\b(keyword|attribute)\b/.test(style)) + state.stack.push("@mediaType"); + else if (context == "@mediaType" && stream.current() == ",") state.stack.pop(); + else if (context == "@mediaType" && type == "(") state.stack.push("@mediaType("); + else if (context == "@mediaType(" && type == ")") state.stack.pop(); + else if (context == "rule" && type == ":") state.stack.push("propertyValue"); + else if (context == "propertyValue" && type == ";") state.stack.pop(); return style; }, indent: function(state, textAfter) { var n = state.stack.length; if (/^\}/.test(textAfter)) - n -= state.stack[state.stack.length-1] == "rule" ? 2 : 1; + n -= state.stack[state.stack.length-1] == "propertyValue" ? 2 : 1; return state.baseIndent + n * indentUnit; }, diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/css/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/css/index.html index 49934346db..ae2c3bfcee 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/css/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/css/index.html @@ -1,6 +1,7 @@ + CodeMirror: CSS mode @@ -51,5 +52,7 @@ code {

MIME types defined: text/css.

+

Parsing/Highlighting Tests: normal, verbose.

+ diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/css/test.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/css/test.js new file mode 100644 index 0000000000..4e2d0e8e56 --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/css/test.js @@ -0,0 +1,501 @@ +// Initiate ModeTest and set defaults +var MT = ModeTest; +MT.modeName = 'css'; +MT.modeOptions = {}; + +// Requires at least one media query +MT.testMode( + 'atMediaEmpty', + '@media { }', + [ + 'def', '@media', + null, ' ', + 'error', '{', + null, ' }' + ] +); + +MT.testMode( + 'atMediaMultiple', + '@media not screen and (color), not print and (color) { }', + [ + 'def', '@media', + null, ' ', + 'keyword', 'not', + null, ' ', + 'attribute', 'screen', + null, ' ', + 'operator', 'and', + null, ' (', + 'property', 'color', + null, '), ', + 'keyword', 'not', + null, ' ', + 'attribute', 'print', + null, ' ', + 'operator', 'and', + null, ' (', + 'property', 'color', + null, ') { }' + ] +); + +MT.testMode( + 'atMediaCheckStack', + '@media screen { } foo { }', + [ + 'def', '@media', + null, ' ', + 'attribute', 'screen', + null, ' { } ', + 'tag', 'foo', + null, ' { }' + ] +); + +MT.testMode( + 'atMediaCheckStack', + '@media screen (color) { } foo { }', + [ + 'def', '@media', + null, ' ', + 'attribute', 'screen', + null, ' (', + 'property', 'color', + null, ') { } ', + 'tag', 'foo', + null, ' { }' + ] +); + +MT.testMode( + 'atMediaCheckStackInvalidAttribute', + '@media foobarhello { } foo { }', + [ + 'def', '@media', + null, ' ', + 'attribute error', 'foobarhello', + null, ' { } ', + 'tag', 'foo', + null, ' { }' + ] +); + +// Error, because "and" is only allowed immediately preceding a media expression +MT.testMode( + 'atMediaInvalidAttribute', + '@media foobarhello { }', + [ + 'def', '@media', + null, ' ', + 'attribute error', 'foobarhello', + null, ' { }' + ] +); + +// Error, because "and" is only allowed immediately preceding a media expression +MT.testMode( + 'atMediaInvalidAnd', + '@media and screen { }', + [ + 'def', '@media', + null, ' ', + 'error', 'and', + null, ' ', + 'attribute', 'screen', + null, ' { }' + ] +); + +// Error, because "not" is only allowed as the first item in each media query +MT.testMode( + 'atMediaInvalidNot', + '@media screen not (not) { }', + [ + 'def', '@media', + null, ' ', + 'attribute', 'screen', + null, ' ', + 'error', 'not', + null, ' (', + 'error', 'not', + null, ') { }' + ] +); + +// Error, because "only" is only allowed as the first item in each media query +MT.testMode( + 'atMediaInvalidOnly', + '@media screen only (only) { }', + [ + 'def', '@media', + null, ' ', + 'attribute', 'screen', + null, ' ', + 'error', 'only', + null, ' (', + 'error', 'only', + null, ') { }' + ] +); + +// Error, because "foobarhello" is neither a known type or property, but +// property was expected (after "and"), and it should be in parenthese. +MT.testMode( + 'atMediaUnknownType', + '@media screen and foobarhello { }', + [ + 'def', '@media', + null, ' ', + 'attribute', 'screen', + null, ' ', + 'operator', 'and', + null, ' ', + 'error', 'foobarhello', + null, ' { }' + ] +); + +// Error, because "color" is not a known type, but is a known property, and +// should be in parentheses. +MT.testMode( + 'atMediaInvalidType', + '@media screen and color { }', + [ + 'def', '@media', + null, ' ', + 'attribute', 'screen', + null, ' ', + 'operator', 'and', + null, ' ', + 'error', 'color', + null, ' { }' + ] +); + +// Error, because "print" is not a known property, but is a known type, +// and should not be in parenthese. +MT.testMode( + 'atMediaInvalidProperty', + '@media screen and (print) { }', + [ + 'def', '@media', + null, ' ', + 'attribute', 'screen', + null, ' ', + 'operator', 'and', + null, ' (', + 'error', 'print', + null, ') { }' + ] +); + +// Soft error, because "foobarhello" is not a known property or type. +MT.testMode( + 'atMediaUnknownProperty', + '@media screen and (foobarhello) { }', + [ + 'def', '@media', + null, ' ', + 'attribute', 'screen', + null, ' ', + 'operator', 'and', + null, ' (', + 'property error', 'foobarhello', + null, ') { }' + ] +); + +MT.testMode( + 'tagSelector', + 'foo { }', + [ + 'tag', 'foo', + null, ' { }' + ] +); + +MT.testMode( + 'classSelector', + '.foo { }', + [ + 'qualifier', '.foo', + null, ' { }' + ] +); + +MT.testMode( + 'idSelector', + '#foo { #foo }', + [ + 'builtin', '#foo', + null, ' { ', + 'error', '#foo', + null, ' }' + ] +); + +MT.testMode( + 'tagSelectorUnclosed', + 'foo { margin: 0 } bar { }', + [ + 'tag', 'foo', + null, ' { ', + 'property', 'margin', + 'operator', ':', + null, ' ', + 'number', '0', + null, ' } ', + 'tag', 'bar', + null, ' { }' + ] +); + +MT.testMode( + 'tagStringNoQuotes', + 'foo { font-family: hello world; }', + [ + 'tag', 'foo', + null, ' { ', + 'property', 'font-family', + 'operator', ':', + null, ' ', + 'variable-2', 'hello', + null, ' ', + 'variable-2', 'world', + null, '; }' + ] +); + +MT.testMode( + 'tagStringDouble', + 'foo { font-family: "hello world"; }', + [ + 'tag', 'foo', + null, ' { ', + 'property', 'font-family', + 'operator', ':', + null, ' ', + 'string', '"hello world"', + null, '; }' + ] +); + +MT.testMode( + 'tagStringSingle', + 'foo { font-family: \'hello world\'; }', + [ + 'tag', 'foo', + null, ' { ', + 'property', 'font-family', + 'operator', ':', + null, ' ', + 'string', '\'hello world\'', + null, '; }' + ] +); + +MT.testMode( + 'tagColorKeyword', + 'foo { color: black; }', + [ + 'tag', 'foo', + null, ' { ', + 'property', 'color', + 'operator', ':', + null, ' ', + 'keyword', 'black', + null, '; }' + ] +); + +MT.testMode( + 'tagColorHex3', + 'foo { background: #fff; }', + [ + 'tag', 'foo', + null, ' { ', + 'property', 'background', + 'operator', ':', + null, ' ', + 'atom', '#fff', + null, '; }' + ] +); + +MT.testMode( + 'tagColorHex6', + 'foo { background: #ffffff; }', + [ + 'tag', 'foo', + null, ' { ', + 'property', 'background', + 'operator', ':', + null, ' ', + 'atom', '#ffffff', + null, '; }' + ] +); + +MT.testMode( + 'tagColorHex4', + 'foo { background: #ffff; }', + [ + 'tag', 'foo', + null, ' { ', + 'property', 'background', + 'operator', ':', + null, ' ', + 'atom error', '#ffff', + null, '; }' + ] +); + +MT.testMode( + 'tagColorHexInvalid', + 'foo { background: #ffg; }', + [ + 'tag', 'foo', + null, ' { ', + 'property', 'background', + 'operator', ':', + null, ' ', + 'atom error', '#ffg', + null, '; }' + ] +); + +MT.testMode( + 'tagNegativeNumber', + 'foo { margin: -5px; }', + [ + 'tag', 'foo', + null, ' { ', + 'property', 'margin', + 'operator', ':', + null, ' ', + 'number', '-5px', + null, '; }' + ] +); + +MT.testMode( + 'tagPositiveNumber', + 'foo { padding: 5px; }', + [ + 'tag', 'foo', + null, ' { ', + 'property', 'padding', + 'operator', ':', + null, ' ', + 'number', '5px', + null, '; }' + ] +); + +MT.testMode( + 'tagVendor', + 'foo { -foo-box-sizing: -foo-border-box; }', + [ + 'tag', 'foo', + null, ' { ', + 'meta', '-foo-', + 'property', 'box-sizing', + 'operator', ':', + null, ' ', + 'meta', '-foo-', + 'string-2', 'border-box', + null, '; }' + ] +); + +MT.testMode( + 'tagBogusProperty', + 'foo { barhelloworld: 0; }', + [ + 'tag', 'foo', + null, ' { ', + 'property error', 'barhelloworld', + 'operator', ':', + null, ' ', + 'number', '0', + null, '; }' + ] +); + +MT.testMode( + 'tagTwoProperties', + 'foo { margin: 0; padding: 0; }', + [ + 'tag', 'foo', + null, ' { ', + 'property', 'margin', + 'operator', ':', + null, ' ', + 'number', '0', + null, '; ', + 'property', 'padding', + 'operator', ':', + null, ' ', + 'number', '0', + null, '; }' + ] +); +// +//MT.testMode( +// 'tagClass', +// '@media only screen and (min-width: 500px), print {foo.bar#hello { color: black !important; background: #f00; margin: -5px; padding: 5px; -foo-box-sizing: border-box; } /* world */}', +// [ +// 'def', '@media', +// null, ' ', +// 'keyword', 'only', +// null, ' ', +// 'attribute', 'screen', +// null, ' ', +// 'operator', 'and', +// null, ' ', +// 'bracket', '(', +// 'property', 'min-width', +// 'operator', ':', +// null, ' ', +// 'number', '500px', +// 'bracket', ')', +// null, ', ', +// 'attribute', 'print', +// null, ' {', +// 'tag', 'foo', +// 'qualifier', '.bar', +// 'header', '#hello', +// null, ' { ', +// 'property', 'color', +// 'operator', ':', +// null, ' ', +// 'keyword', 'black', +// null, ' ', +// 'keyword', '!important', +// null, '; ', +// 'property', 'background', +// 'operator', ':', +// null, ' ', +// 'atom', '#f00', +// null, '; ', +// 'property', 'padding', +// 'operator', ':', +// null, ' ', +// 'number', '5px', +// null, '; ', +// 'property', 'margin', +// 'operator', ':', +// null, ' ', +// 'number', '-5px', +// null, '; ', +// 'meta', '-foo-', +// 'property', 'box-sizing', +// 'operator', ':', +// null, ' ', +// 'string-2', 'border-box', +// null, '; } ', +// 'comment', '/* world */', +// null, '}' +// ] +//); \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/diff/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/diff/index.html index b102c090fa..556025204d 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/diff/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/diff/index.html @@ -1,6 +1,7 @@ + CodeMirror: Diff mode diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/ecl/ecl.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/ecl/ecl.js index 25df24ba92..c0e4479270 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/ecl/ecl.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/ecl/ecl.js @@ -49,7 +49,7 @@ CodeMirror.defineMode("ecl", function(config) { } if (/[\[\]{}\(\),;\:\.]/.test(ch)) { curPunc = ch; - return null + return null; } if (/\d/.test(ch)) { stream.eatWhile(/[\w\.]/); diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/erlang/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/erlang/index.html index 80eefef469..c28389aa90 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/erlang/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/erlang/index.html @@ -1,6 +1,7 @@ + CodeMirror: Erlang mode diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/gfm/gfm.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/gfm/gfm.js index 8f457c530b..2bc273ab68 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/gfm/gfm.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/gfm/gfm.js @@ -32,14 +32,14 @@ CodeMirror.defineMode("gfm", function(config, parserConfig) { return function (lang) { return modes[lang] ? CodeMirror.getMode(config, modes[lang]) : null; - } + }; }()); function markdown(stream, state) { // intercept fenced code blocks if (stream.sol() && stream.match(/^```([\w+#]*)/)) { // try switching mode - state.localMode = getMode(RegExp.$1) + state.localMode = getMode(RegExp.$1); if (state.localMode) state.localState = state.localMode.startState(); @@ -96,13 +96,14 @@ CodeMirror.defineMode("gfm", function(config, parserConfig) { }, copyState: function(state) { - return {token: state.token, mode: state.mode, mdState: CodeMirror.copyState(mdMode, state.mdState), + return {token: state.token, mdState: CodeMirror.copyState(mdMode, state.mdState), localMode: state.localMode, localState: state.localMode ? CodeMirror.copyState(state.localMode, state.localState) : null}; }, token: function(stream, state) { /* Parse GFM double bracket links */ + var ch; if ((ch = stream.peek()) != undefined && ch == '[') { stream.next(); // Advance the stream @@ -139,6 +140,11 @@ CodeMirror.defineMode("gfm", function(config, parserConfig) { } return state.token(stream, state); + }, + + innerMode: function(state) { + if (state.token == markdown) return {state: state.mdState, mode: mdMode}; + else return {state: state.localState, mode: state.localMode}; } - } + }; }, "markdown"); diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/gfm/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/gfm/index.html index b27d49abef..d0214c17d6 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/gfm/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/gfm/index.html @@ -1,6 +1,7 @@ + CodeMirror: GFM mode diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/go/go.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/go/go.js index 657c014a91..9863bbf043 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/go/go.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/go/go.js @@ -47,7 +47,7 @@ CodeMirror.defineMode("go", function(config, parserConfig) { } if (/[\[\]{}\(\),;\:\.]/.test(ch)) { curPunc = ch; - return null + return null; } if (ch == "/") { if (stream.eat("*")) { @@ -144,7 +144,7 @@ CodeMirror.defineMode("go", function(config, parserConfig) { if (curPunc == "{") pushContext(state, stream.column(), "}"); else if (curPunc == "[") pushContext(state, stream.column(), "]"); else if (curPunc == "(") pushContext(state, stream.column(), ")"); - else if (curPunc == "case") ctx.type = "case" + else if (curPunc == "case") ctx.type = "case"; else if (curPunc == "}" && ctx.type == "}") ctx = popContext(state); else if (curPunc == ctx.type) popContext(state); state.startOfLine = false; diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/go/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/go/index.html index 9cdad1ada0..24b1cb9db0 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/go/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/go/index.html @@ -1,6 +1,7 @@ + CodeMirror: Go mode diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/groovy/groovy.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/groovy/groovy.js index a7daadb5d2..752bc2f7d3 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/groovy/groovy.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/groovy/groovy.js @@ -21,7 +21,7 @@ CodeMirror.defineMode("groovy", function(config, parserConfig) { } if (/[\[\]{}\(\),;\:\.]/.test(ch)) { curPunc = ch; - return null + return null; } if (/\d/.test(ch)) { stream.eatWhile(/[\w\.]/); diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/groovy/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/groovy/index.html index 226475caa6..0393362a47 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/groovy/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/groovy/index.html @@ -1,6 +1,7 @@ + CodeMirror: Groovy mode diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/haskell/haskell.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/haskell/haskell.js index aac5041089..e15a32cdf3 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/haskell/haskell.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/haskell/haskell.js @@ -128,7 +128,7 @@ CodeMirror.defineMode("haskell", function(cmCfg, modeCfg) { } setState(ncomment(type, currNest)); return type; - } + }; } function stringLiteral(source, setState) { @@ -170,7 +170,7 @@ CodeMirror.defineMode("haskell", function(cmCfg, modeCfg) { return function () { for (var i = 0; i < arguments.length; i++) wkw[arguments[i]] = t; - } + }; } setType("keyword")( diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/haskell/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/haskell/index.html index 15706e710e..963430f3f6 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/haskell/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/haskell/index.html @@ -1,6 +1,7 @@ + CodeMirror: Haskell mode diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/haxe/haxe.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/haxe/haxe.js index ef78974dae..64f4eb3ff8 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/haxe/haxe.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/haxe/haxe.js @@ -6,7 +6,7 @@ CodeMirror.defineMode("haxe", function(config, parserConfig) { var keywords = function(){ function kw(type) {return {type: type, style: "keyword"};} var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"); - var operator = kw("operator"), atom = {type: "atom", style: "atom"}, attribute = {type:"attribute", style: "attribute"} + var operator = kw("operator"), atom = {type: "atom", style: "atom"}, attribute = {type:"attribute", style: "attribute"}; var type = kw("typedef"); return { "if": A, "while": A, "else": B, "do": B, "try": B, @@ -219,7 +219,7 @@ CodeMirror.defineMode("haxe", function(config, parserConfig) { function pushlex(type, info) { var result = function() { var state = cx.state; - state.lexical = new HaxeLexical(state.indented, cx.stream.column(), type, null, state.lexical, info) + state.lexical = new HaxeLexical(state.indented, cx.stream.column(), type, null, state.lexical, info); }; result.lex = true; return result; @@ -243,7 +243,7 @@ CodeMirror.defineMode("haxe", function(config, parserConfig) { } function statement(type) { - if (type == "@") return cont(metadef) + if (type == "@") return cont(metadef); if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex); if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex); if (type == "keyword b") return cont(pushlex("form"), statement, poplex); @@ -297,7 +297,7 @@ CodeMirror.defineMode("haxe", function(config, parserConfig) { function metadef(type, value) { if(type == ":") return cont(metadef); if(type == "variable") return cont(metadef); - if(type == "(") return cont(pushlex(")"), comasep(metaargs, ")"), poplex, statement) + if(type == "(") return cont(pushlex(")"), comasep(metaargs, ")"), poplex, statement); } function metaargs(type, value) { if(typ == "variable") return cont(); @@ -421,9 +421,6 @@ CodeMirror.defineMode("haxe", function(config, parserConfig) { else if (lexical.align) return lexical.column + (closing ? 0 : 1); else return lexical.indented + (closing ? 0 : indentUnit); }, - compareStates: function(state1, state2) { - return (state1.localVars == state2.localVars) && (state1.context == state2.context); - }, electricChars: "{}" }; diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/haxe/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/haxe/index.html index 6e9948a0b4..ee5983ae0d 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/haxe/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/haxe/index.html @@ -1,6 +1,7 @@ + CodeMirror: Haxe mode diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/htmlembedded/htmlembedded.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/htmlembedded/htmlembedded.js index 1773aeb5c1..195d48e328 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/htmlembedded/htmlembedded.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/htmlembedded/htmlembedded.js @@ -36,7 +36,7 @@ CodeMirror.defineMode("htmlembedded", function(config, parserConfig) { token : parserConfig.startOpen ? scriptingDispatch : htmlDispatch, htmlState : htmlMixedMode.startState(), scriptState : scriptingMode.startState() - } + }; }, token: function(stream, state) { @@ -55,12 +55,16 @@ CodeMirror.defineMode("htmlembedded", function(config, parserConfig) { token : state.token, htmlState : CodeMirror.copyState(htmlMixedMode, state.htmlState), scriptState : CodeMirror.copyState(scriptingMode, state.scriptState) - } + }; }, + electricChars: "/{}:", - electricChars: "/{}:" - } + innerMode: function(state) { + if (state.token == scriptingDispatch) return {state: state.scriptState, mode: scriptingMode}; + else return {state: state.htmlState, mode: htmlMixedMode}; + } + }; }, "htmlmixed"); CodeMirror.defineMIME("application/x-ejs", { name: "htmlembedded", scriptingModeSpec:"javascript"}); diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/htmlembedded/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/htmlembedded/index.html index c1374e58c4..a4b5883676 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/htmlembedded/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/htmlembedded/index.html @@ -1,6 +1,7 @@ + CodeMirror: Html Embedded Scripts mode diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/htmlmixed/htmlmixed.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/htmlmixed/htmlmixed.js index a6c62c9d09..4652848296 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/htmlmixed/htmlmixed.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/htmlmixed/htmlmixed.js @@ -1,4 +1,4 @@ -CodeMirror.defineMode("htmlmixed", function(config, parserConfig) { +CodeMirror.defineMode("htmlmixed", function(config) { var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true}); var jsMode = CodeMirror.getMode(config, "javascript"); var cssMode = CodeMirror.getMode(config, "css"); @@ -9,27 +9,28 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) { if (/^script$/i.test(state.htmlState.context.tagName)) { state.token = javascript; state.localState = jsMode.startState(htmlMode.indent(state.htmlState, "")); - state.mode = "javascript"; } else if (/^style$/i.test(state.htmlState.context.tagName)) { state.token = css; state.localState = cssMode.startState(htmlMode.indent(state.htmlState, "")); - state.mode = "css"; } } return style; } function maybeBackup(stream, pat, style) { var cur = stream.current(); - var close = cur.search(pat); + var close = cur.search(pat), m; if (close > -1) stream.backUp(cur.length - close); + else if (m = cur.match(/<\/?$/)) { + stream.backUp(cur[0].length); + if (!stream.match(pat, false)) stream.match(cur[0]); + } return style; } function javascript(stream, state) { if (stream.match(/^<\/\s*script\s*>/i, false)) { state.token = html; state.localState = null; - state.mode = "html"; return html(stream, state); } return maybeBackup(stream, /<\/\s*script\s*>/, @@ -39,7 +40,6 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) { if (stream.match(/^<\/\s*style\s*>/i, false)) { state.token = html; state.localState = null; - state.mode = "html"; return html(stream, state); } return maybeBackup(stream, /<\/\s*style\s*>/, @@ -72,14 +72,13 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) { return cssMode.indent(state.localState, textAfter); }, - compareStates: function(a, b) { - if (a.mode != b.mode) return false; - if (a.localState) return CodeMirror.Pass; - return htmlMode.compareStates(a.htmlState, b.htmlState); - }, + electricChars: "/{}:", - electricChars: "/{}:" - } + innerMode: function(state) { + var mode = state.token == html ? htmlMode : state.token == javascript ? jsMode : cssMode; + return {state: state.localState || state.htmlState, mode: mode}; + } + }; }, "xml", "javascript", "css"); CodeMirror.defineMIME("text/html", "htmlmixed"); diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/htmlmixed/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/htmlmixed/index.html index 63fc41209f..45a9c03983 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/htmlmixed/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/htmlmixed/index.html @@ -1,6 +1,7 @@ + CodeMirror: HTML mixed mode diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/javascript/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/javascript/index.html index c3ab91dc6c..206df3fca1 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/javascript/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/javascript/index.html @@ -1,6 +1,7 @@ + CodeMirror: JavaScript mode diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/javascript/javascript.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/javascript/javascript.js index 65f11c5bff..e754a047f0 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/javascript/javascript.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/javascript/javascript.js @@ -175,8 +175,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { var defaultVars = {name: "this", next: {name: "arguments"}}; function pushcontext() { - if (!cx.state.context) cx.state.localVars = defaultVars; cx.state.context = {prev: cx.state.context, vars: cx.state.localVars}; + cx.state.localVars = defaultVars; } function popcontext() { cx.state.localVars = cx.state.context.vars; @@ -185,7 +185,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function pushlex(type, info) { var result = function() { var state = cx.state; - state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info) + state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info); }; result.lex = true; return result; @@ -243,7 +243,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function maybeoperator(type, value) { if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator); - if (type == "operator" || type == ":") return cont(expression); + if (type == "operator" && value == "?") return cont(expression, expect(":"), expression); if (type == ";") return; if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator); if (type == ".") return cont(property, maybeoperator); diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/jinja2/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/jinja2/index.html index 021a2829a6..7cd1da2335 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/jinja2/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/jinja2/index.html @@ -1,6 +1,7 @@ + CodeMirror: Jinja2 mode diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/less/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/less/index.html index cad8e4b228..69467bbb2d 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/less/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/less/index.html @@ -1,6 +1,7 @@ + CodeMirror: LESS mode @@ -8,11 +9,132 @@ -

CodeMirror: LESS mode

-
+ + +

MIME types defined: application/sieve.

+ + + diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/sieve/sieve.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/sieve/sieve.js new file mode 100644 index 0000000000..db777c1316 --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/sieve/sieve.js @@ -0,0 +1,156 @@ +/* + * See LICENSE in this directory for the license under which this code + * is released. + */ + +CodeMirror.defineMode("sieve", function(config) { + function words(str) { + var obj = {}, words = str.split(" "); + for (var i = 0; i < words.length; ++i) obj[words[i]] = true; + return obj; + } + + var keywords = words("if elsif else stop require"); + var atoms = words("true false not"); + var indentUnit = config.indentUnit; + + function tokenBase(stream, state) { + + var ch = stream.next(); + if (ch == "/" && stream.eat("*")) { + state.tokenize = tokenCComment; + return tokenCComment(stream, state); + } + + if (ch === '#') { + stream.skipToEnd(); + return "comment"; + } + + if (ch == "\"") { + state.tokenize = tokenString(ch); + return state.tokenize(stream, state); + } + + if (ch === "{") + { + state._indent++; + return null; + } + + if (ch === "}") + { + state._indent--; + return null; + } + + if (/[{}\(\),;]/.test(ch)) + return null; + + // 1*DIGIT "K" / "M" / "G" + if (/\d/.test(ch)) { + stream.eatWhile(/[\d]/); + stream.eat(/[KkMmGg]/); + return "number"; + } + + // ":" (ALPHA / "_") *(ALPHA / DIGIT / "_") + if (ch == ":") { + stream.eatWhile(/[a-zA-Z_]/); + stream.eatWhile(/[a-zA-Z0-9_]/); + + return "operator"; + } + + stream.eatWhile(/[\w\$_]/); + var cur = stream.current(); + + // "text:" *(SP / HTAB) (hash-comment / CRLF) + // *(multiline-literal / multiline-dotstart) + // "." CRLF + if ((cur == "text") && stream.eat(":")) + { + state.tokenize = tokenMultiLineString; + return "string"; + } + + if (keywords.propertyIsEnumerable(cur)) + return "keyword"; + + if (atoms.propertyIsEnumerable(cur)) + return "atom"; + } + + function tokenMultiLineString(stream, state) + { + state._multiLineString = true; + // the first line is special it may contain a comment + if (!stream.sol()) { + stream.eatSpace(); + + if (stream.peek() == "#") { + stream.skipToEnd(); + return "comment"; + } + + stream.skipToEnd(); + return "string"; + } + + if ((stream.next() == ".") && (stream.eol())) + { + state._multiLineString = false; + state.tokenize = tokenBase; + } + + return "string"; + } + + function tokenCComment(stream, state) { + var maybeEnd = false, ch; + while ((ch = stream.next()) != null) { + if (maybeEnd && ch == "/") { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch == "*"); + } + return "comment"; + } + + function tokenString(quote) { + return function(stream, state) { + var escaped = false, ch; + while ((ch = stream.next()) != null) { + if (ch == quote && !escaped) + break; + escaped = !escaped && ch == "\\"; + } + if (!escaped) state.tokenize = tokenBase; + return "string"; + }; + } + + return { + startState: function(base) { + return {tokenize: tokenBase, + baseIndent: base || 0, + _indent: 0}; + }, + + token: function(stream, state) { + if (stream.eatSpace()) + return null; + + return (state.tokenize || tokenBase)(stream, state);; + }, + + indent: function(state, textAfter) { + return state.baseIndent + state._indent * indentUnit; + }, + + electricChars: "}" + }; +}); + +CodeMirror.defineMIME("application/sieve", "sieve"); diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/smalltalk/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/smalltalk/index.html index 8a85c39eea..9a48ec19fc 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/smalltalk/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/smalltalk/index.html @@ -1,6 +1,7 @@ + CodeMirror: Smalltalk mode diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/smalltalk/smalltalk.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/smalltalk/smalltalk.js index e58933223c..ba17cbdc98 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/smalltalk/smalltalk.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/smalltalk/smalltalk.js @@ -63,7 +63,7 @@ CodeMirror.defineMode('smalltalk', function(config, modeConfig) { } else if (/\d/.test(aChar)) { stream.eatWhile(/[\w\d]/); - token.name = 'number' + token.name = 'number'; } else if (/[\w_]/.test(aChar)) { stream.eatWhile(/[\w\d_]/); @@ -100,7 +100,7 @@ CodeMirror.defineMode('smalltalk', function(config, modeConfig) { } return token; - } + }; return { startState: function() { diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/smarty/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/smarty/index.html index ad4dccf034..6b7debedc4 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/smarty/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/smarty/index.html @@ -1,6 +1,7 @@ + CodeMirror: Smarty mode diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/smarty/smarty.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/smarty/smarty.js index 9da7da626d..941e7f374e 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/smarty/smarty.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/smarty/smarty.js @@ -5,7 +5,7 @@ CodeMirror.defineMode("smarty", function(config, parserConfig) { operatorChars: /[+\-*&%=<>!?]/, validIdentifier: /[a-zA-Z0-9\_]/, stringChar: /[\'\"]/ - } + }; var leftDelim = (typeof config.mode.leftDelimiter != 'undefined') ? config.mode.leftDelimiter : "{"; var rightDelim = (typeof config.mode.rightDelimiter != 'undefined') ? config.mode.rightDelimiter : "}"; function ret(style, lst) { last = lst; return style; } @@ -142,7 +142,7 @@ CodeMirror.defineMode("smarty", function(config, parserConfig) { return style; }, electricChars: "" - } + }; }); CodeMirror.defineMIME("text/x-smarty", "smarty"); \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/sparql/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/sparql/index.html index d7772f6bc8..b7eafa3ca8 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/sparql/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/sparql/index.html @@ -1,6 +1,7 @@ + CodeMirror: SPARQL mode diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/stex/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/stex/index.html index e49289e9db..2dafe69816 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/stex/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/stex/index.html @@ -1,6 +1,7 @@ + CodeMirror: sTeX mode @@ -91,5 +92,7 @@

MIME types defined: text/x-stex.

+

Parsing/Highlighting Tests: normal, verbose.

+ diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/stex/stex.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/stex/stex.js index 2e2f026a54..100854dab3 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/stex/stex.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/stex/stex.js @@ -54,7 +54,7 @@ CodeMirror.defineMode("stex", function(cmCfg, modeCfg) }; this.closeBracket = function(content) { }; - } + }; } var plugins = new Array(); diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/stex/test.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/stex/test.js new file mode 100644 index 0000000000..c5a34f3d8d --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/stex/test.js @@ -0,0 +1,343 @@ +var MT = ModeTest; +MT.modeName = 'stex'; +MT.modeOptions = {}; + +MT.testMode( + 'word', + 'foo', + [ + null, 'foo' + ] +); + +MT.testMode( + 'twoWords', + 'foo bar', + [ + null, 'foo bar' + ] +); + +MT.testMode( + 'beginEndDocument', + '\\begin{document}\n\\end{document}', + [ + 'tag', '\\begin', + 'bracket', '{', + 'atom', 'document', + 'bracket', '}', + 'tag', '\\end', + 'bracket', '{', + 'atom', 'document', + 'bracket', '}' + ] +); + +MT.testMode( + 'beginEndEquation', + '\\begin{equation}\n E=mc^2\n\\end{equation}', + [ + 'tag', '\\begin', + 'bracket', '{', + 'atom', 'equation', + 'bracket', '}', + null, ' E=mc^2', + 'tag', '\\end', + 'bracket', '{', + 'atom', 'equation', + 'bracket', '}' + ] +); + +MT.testMode( + 'beginModule', + '\\begin{module}[]', + [ + 'tag', '\\begin', + 'bracket', '{', + 'atom', 'module', + 'bracket', '}[]' + ] +); + +MT.testMode( + 'beginModuleId', + '\\begin{module}[id=bbt-size]', + [ + 'tag', '\\begin', + 'bracket', '{', + 'atom', 'module', + 'bracket', '}[', + null, 'id=bbt-size', + 'bracket', ']' + ] +); + +MT.testMode( + 'importModule', + '\\importmodule[b-b-t]{b-b-t}', + [ + 'tag', '\\importmodule', + 'bracket', '[', + 'string', 'b-b-t', + 'bracket', ']{', + 'builtin', 'b-b-t', + 'bracket', '}' + ] +); + +MT.testMode( + 'importModulePath', + '\\importmodule[\\KWARCslides{dmath/en/cardinality}]{card}', + [ + 'tag', '\\importmodule', + 'bracket', '[', + 'tag', '\\KWARCslides', + 'bracket', '{', + 'string', 'dmath/en/cardinality', + 'bracket', '}]{', + 'builtin', 'card', + 'bracket', '}' + ] +); + +MT.testMode( + 'psForPDF', + '\\PSforPDF[1]{#1}', // could treat #1 specially + [ + 'tag', '\\PSforPDF', + 'bracket', '[', + 'atom', '1', + 'bracket', ']{', + null, '#1', + 'bracket', '}' + ] +); + +MT.testMode( + 'comment', + '% foo', + [ + 'comment', '% foo' + ] +); + +MT.testMode( + 'tagComment', + '\\item% bar', + [ + 'tag', '\\item', + 'comment', '% bar' + ] +); + +MT.testMode( + 'commentTag', + ' % \\item', + [ + null, ' ', + 'comment', '% \\item' + ] +); + +MT.testMode( + 'commentLineBreak', + '%\nfoo', + [ + 'comment', '%', + null, 'foo' + ] +); + +MT.testMode( + 'tagErrorCurly', + '\\begin}{', + [ + 'tag', '\\begin', + 'error', '}', + 'bracket', '{' + ] +); + +MT.testMode( + 'tagErrorSquare', + '\\item]{', + [ + 'tag', '\\item', + 'error', ']', + 'bracket', '{' + ] +); + +MT.testMode( + 'commentCurly', + '% }', + [ + 'comment', '% }' + ] +); + +MT.testMode( + 'tagHash', + 'the \\# key', + [ + null, 'the ', + 'tag', '\\#', + null, ' key' + ] +); + +MT.testMode( + 'tagNumber', + 'a \\$5 stetson', + [ + null, 'a ', + 'tag', '\\$', + 'atom', 5, + null, ' stetson' + ] +); + +MT.testMode( + 'tagPercent', + '100\\% beef', + [ + 'atom', '100', + 'tag', '\\%', + null, ' beef' + ] +); + +MT.testMode( + 'tagAmpersand', + 'L \\& N', + [ + null, 'L ', + 'tag', '\\&', + null, ' N' + ] +); + +MT.testMode( + 'tagUnderscore', + 'foo\\_bar', + [ + null, 'foo', + 'tag', '\\_', + null, 'bar' + ] +); + +MT.testMode( + 'tagBracketOpen', + '\\emph{\\{}', + [ + 'tag', '\\emph', + 'bracket', '{', + 'tag', '\\{', + 'bracket', '}' + ] +); + +MT.testMode( + 'tagBracketClose', + '\\emph{\\}}', + [ + 'tag', '\\emph', + 'bracket', '{', + 'tag', '\\}', + 'bracket', '}' + ] +); + +MT.testMode( + 'tagLetterNumber', + 'section \\S1', + [ + null, 'section ', + 'tag', '\\S', + 'atom', '1' + ] +); + +MT.testMode( + 'textTagNumber', + 'para \\P2', + [ + null, 'para ', + 'tag', '\\P', + 'atom', '2' + ] +); + +MT.testMode( + 'thinspace', + 'x\\,y', // thinspace + [ + null, 'x', + 'tag', '\\,', + null, 'y' + ] +); + +MT.testMode( + 'thickspace', + 'x\\;y', // thickspace + [ + null, 'x', + 'tag', '\\;', + null, 'y' + ] +); + +MT.testMode( + 'negativeThinspace', + 'x\\!y', // negative thinspace + [ + null, 'x', + 'tag', '\\!', + null, 'y' + ] +); + +MT.testMode( + 'periodNotSentence', + 'J.\\ L.\\ is', // period not ending a sentence + [ + null, 'J.\\ L.\\ is' + ] +); // maybe could be better + +MT.testMode( + 'periodSentence', + 'X\\@. The', // period ending a sentence + [ + null, 'X', + 'tag', '\\@', + null, '. The' + ] +); + +MT.testMode( + 'italicCorrection', + '{\\em If\\/} I', // italic correction + [ + 'bracket', '{', + 'tag', '\\em', + null, ' If', + 'tag', '\\/', + 'bracket', '}', + null, ' I' + ] +); + +MT.testMode( + 'tagBracket', + '\\newcommand{\\pop}', + [ + 'tag', '\\newcommand', + 'bracket', '{', + 'tag', '\\pop', + 'bracket', '}' + ] +); \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/tiddlywiki/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/tiddlywiki/index.html index 2f7da0e06d..40c2dff5b3 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/tiddlywiki/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/tiddlywiki/index.html @@ -1,6 +1,7 @@ + CodeMirror: TiddlyWiki mode diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/tiki/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/tiki/index.html index bf800407a9..3579cff508 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/tiki/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/tiki/index.html @@ -1,5 +1,6 @@ + CodeMirror: Tiki wiki mode diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/tiki/tiki.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/tiki/tiki.js index 350dd51bfb..af83dc1b5b 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/tiki/tiki.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/tiki/tiki.js @@ -18,7 +18,7 @@ CodeMirror.defineMode('tiki', function(config, parserConfig) { function inLine(style, terminator) { return function(stream, state) { while(!stream.eol()) { - stream.next() + stream.next(); } state.tokenize = inText; return style; @@ -37,9 +37,9 @@ CodeMirror.defineMode('tiki', function(config, parserConfig) { //non start of line switch (ch) { //switch is generally much faster than if, so it is used here case "{": //plugin - type = stream.eat("/") ? "closeTag" : "openTag"; + var type = stream.eat("/") ? "closeTag" : "openTag"; stream.eatSpace(); - tagName = ""; + var tagName = ""; var c; while ((c = stream.eat(/[^\s\u00a0=\"\'\/?(}]/))) tagName += c; state.tokenize = inPlugin; @@ -251,7 +251,7 @@ CodeMirror.defineMode('tiki', function(config, parserConfig) { if (err) setStyle = "error"; if (type == "endPlugin") return cont(); return pass(); - } + }; } function attributes(type) { @@ -301,13 +301,6 @@ CodeMirror.defineMode('tiki', function(config, parserConfig) { if (context) return context.indent + indentUnit; else return 0; }, - compareStates: function(a, b) { - if (a.indented != b.indented || a.pluginName != b.pluginName) return false; - for (var ca = a.context, cb = b.context; ; ca = ca.prev, cb = cb.prev) { - if (!ca || !cb) return ca == cb; - if (ca.pluginName != cb.pluginName) return false; - } - }, electricChars: "/" }; }); diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/vb/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/vb/index.html index 7c42be4939..af313419be 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/vb/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/vb/index.html @@ -1,5 +1,6 @@ + CodeMirror: VB.NET mode diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/vb/vb.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/vb/vb.js index 01f9890389..be01d13ad9 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/vb/vb.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/vb/vb.js @@ -12,8 +12,8 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { var tripleDelimiters = new RegExp("^((//=)|(>>=)|(<<=)|(\\*\\*=))"); var identifiers = new RegExp("^[_A-Za-z][_A-Za-z0-9]*"); - var openingKeywords = ['class','module', 'sub','enum','select','while','if','function', 'get','set','property']; - var middleKeywords = ['else','elseif','case']; + var openingKeywords = ['class','module', 'sub','enum','select','while','if','function', 'get','set','property', 'try']; + var middleKeywords = ['else','elseif','case', 'catch']; var endKeywords = ['next','loop']; var wordOperators = wordRegexp(['and', 'or', 'not', 'xor', 'in']); diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/vbscript/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/vbscript/index.html index dd207cae9a..e7375fb0fc 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/vbscript/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/vbscript/index.html @@ -1,6 +1,7 @@ + CodeMirror: VBScript mode diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/velocity/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/velocity/index.html index 49dba3820c..1e25c651e7 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/velocity/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/velocity/index.html @@ -1,6 +1,7 @@ + CodeMirror: Velocity mode diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/velocity/velocity.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/velocity/velocity.js index 0b80c75814..b7048b152f 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/velocity/velocity.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/velocity/velocity.js @@ -5,7 +5,7 @@ CodeMirror.defineMode("velocity", function(config) { return obj; } - var indentUnit = config.indentUnit + var indentUnit = config.indentUnit; var keywords = parseWords("#end #else #break #stop #[[ #]] " + "#{end} #{else} #{break} #{stop}"); var functions = parseWords("#if #elseif #foreach #set #include #parse #macro #define #evaluate " + diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/verilog/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/verilog/index.html index 775dd5374a..f251a34433 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/verilog/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/verilog/index.html @@ -1,6 +1,7 @@ + CodeMirror: Verilog mode diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/verilog/verilog.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/verilog/verilog.js index 62700ceaf5..3e3eca9b9c 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/verilog/verilog.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/verilog/verilog.js @@ -21,7 +21,7 @@ CodeMirror.defineMode("verilog", function(config, parserConfig) { } if (/[\[\]{}\(\),;\:\.]/.test(ch)) { curPunc = ch; - return null + return null; } if (/[\d']/.test(ch)) { stream.eatWhile(/[\w\.']/); diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xml/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xml/index.html index 3b6d6af1ed..49a627de73 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xml/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xml/index.html @@ -1,11 +1,12 @@ + CodeMirror: XML mode - + diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xml/xml.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xml/xml.js index 060dad181b..860e368f5b 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xml/xml.js +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xml/xml.js @@ -30,7 +30,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { }, doNotIndent: {"pre": true}, allowUnquoted: true, - allowMissing: false + allowMissing: true } : { autoSelfClosers: {}, implicitlyClosed: {}, @@ -229,7 +229,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { if (type == "endTag") { popContext(); return cont(); } setStyle = "error"; return cont(arguments.callee); - } + }; } function maybePopContext(nextTagName) { var parentTagName; @@ -308,14 +308,6 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { else return 0; }, - compareStates: function(a, b) { - if (a.indented != b.indented || a.tokenize != b.tokenize) return false; - for (var ca = a.context, cb = b.context; ; ca = ca.prev, cb = cb.prev) { - if (!ca || !cb) return ca == cb; - if (ca.tagName != cb.tagName || ca.indent != cb.indent) return false; - } - }, - electricChars: "/" }; }); diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/index.html index 82f00d2475..4364fe18a4 100644 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/index.html +++ b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/index.html @@ -25,7 +25,8 @@ THE SOFTWARE. */ --> - CodeMirror 2: JavaScript mode + + CodeMirror: XQuery mode @@ -41,7 +42,7 @@ THE SOFTWARE. -

CodeMirror 2: XQuery mode

+

CodeMirror: XQuery mode