From 8db5f53579e05bd3541578ff87c3817333978729 Mon Sep 17 00:00:00 2001
From: Nick Fitzgerald
Date: Tue, 19 Nov 2013 15:17:40 -0800
Subject: [PATCH 01/27] Bug 922812 - Add an 'eval current top level function'
command to scratchpad; r=past
---
browser/devtools/scratchpad/scratchpad.js | 166 ++++++++++++++++++
browser/devtools/scratchpad/scratchpad.xul | 15 ++
browser/devtools/scratchpad/test/browser.ini | 1 +
.../test/browser_scratchpad_eval_func.js | 86 +++++++++
browser/devtools/sourceeditor/editor.js | 1 +
.../chrome/browser/devtools/scratchpad.dtd | 7 +
browser/themes/shared/devtools/dark-theme.css | 5 +
.../themes/shared/devtools/light-theme.css | 5 +
8 files changed, 286 insertions(+)
create mode 100644 browser/devtools/scratchpad/test/browser_scratchpad_eval_func.js
diff --git a/browser/devtools/scratchpad/scratchpad.js b/browser/devtools/scratchpad/scratchpad.js
index 5e786bcf3b7a..0cc646e32d6a 100644
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -24,6 +24,7 @@ const BUTTON_POSITION_SAVE = 0;
const BUTTON_POSITION_CANCEL = 1;
const BUTTON_POSITION_DONT_SAVE = 2;
const BUTTON_POSITION_REVERT = 0;
+const EVAL_FUNCTION_TIMEOUT = 1000; // milliseconds
const SCRATCHPAD_L10N = "chrome://browser/locale/devtools/scratchpad.properties";
const DEVTOOLS_CHROME_ENABLED = "devtools.chrome.enabled";
@@ -75,6 +76,9 @@ XPCOMUtils.defineLazyGetter(this, "REMOTE_TIMEOUT", () =>
XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils",
"resource://gre/modules/ShortcutUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Reflect",
+ "resource://gre/modules/reflect.jsm");
+
// Because we have no constructor / destructor where we can log metrics we need
// to do so here.
let telemetry = new Telemetry();
@@ -567,6 +571,168 @@ var Scratchpad = {
return deferred.promise;
},
+ /**
+ * Parse the text and return an AST. If we can't parse it, write an error
+ * comment and return false.
+ */
+ _parseText: function SP__parseText(aText) {
+ try {
+ return Reflect.parse(aText);
+ } catch (e) {
+ this.writeAsErrorComment(DevToolsUtils.safeErrorString(e));
+ return false;
+ }
+ },
+
+ /**
+ * Determine if the given AST node location contains the given cursor
+ * position.
+ *
+ * @returns Boolean
+ */
+ _containsCursor: function (aLoc, aCursorPos) {
+ // Our line numbers are 1-based, while CodeMirror's are 0-based.
+ const lineNumber = aCursorPos.line + 1;
+ const columnNumber = aCursorPos.ch;
+
+ if (aLoc.start.line <= lineNumber && aLoc.end.line >= lineNumber) {
+ if (aLoc.start.line === aLoc.end.line) {
+ return aLoc.start.column <= columnNumber
+ && aLoc.end.column >= columnNumber;
+ }
+
+ if (aLoc.start.line == lineNumber) {
+ return columnNumber >= aLoc.start.column;
+ }
+
+ if (aLoc.end.line == lineNumber) {
+ return columnNumber <= aLoc.end.column;
+ }
+
+ return true;
+ }
+
+ return false;
+ },
+
+ /**
+ * Find the top level function AST node that the cursor is within.
+ *
+ * @returns Object|null
+ */
+ _findTopLevelFunction: function SP__findTopLevelFunction(aAst, aCursorPos) {
+ for (let statement of aAst.body) {
+ switch (statement.type) {
+ case "FunctionDeclaration":
+ if (this._containsCursor(statement.loc, aCursorPos)) {
+ return statement;
+ }
+ break;
+
+ case "VariableDeclaration":
+ for (let decl of statement.declarations) {
+ if (!decl.init) {
+ continue;
+ }
+ if ((decl.init.type == "FunctionExpression"
+ || decl.init.type == "ArrowExpression")
+ && this._containsCursor(decl.loc, aCursorPos)) {
+ return decl;
+ }
+ }
+ break;
+ }
+ }
+
+ return null;
+ },
+
+ /**
+ * Get the source text associated with the given function statement.
+ *
+ * @param Object aFunction
+ * @param String aFullText
+ * @returns String
+ */
+ _getFunctionText: function SP__getFunctionText(aFunction, aFullText) {
+ let functionText = "";
+ // Initially set to 0, but incremented first thing in the loop below because
+ // line numbers are 1 based, not 0 based.
+ let lineNumber = 0;
+ const { start, end } = aFunction.loc;
+ const singleLine = start.line === end.line;
+
+ for (let line of aFullText.split(/\n/g)) {
+ lineNumber++;
+
+ if (singleLine && start.line === lineNumber) {
+ functionText = line.slice(start.column, end.column);
+ break;
+ }
+
+ if (start.line === lineNumber) {
+ functionText += line.slice(start.column) + "\n";
+ continue;
+ }
+
+ if (end.line === lineNumber) {
+ functionText += line.slice(0, end.column);
+ break;
+ }
+
+ if (start.line < lineNumber && end.line > lineNumber) {
+ functionText += line + "\n";
+ }
+ }
+
+ return functionText;
+ },
+
+ /**
+ * Evaluate the top level function that the cursor is resting in.
+ *
+ * @returns Promise [text, error, result]
+ */
+ evalTopLevelFunction: function SP_evalTopLevelFunction() {
+ const text = this.getText();
+ const ast = this._parseText(text);
+ if (!ast) {
+ return promise.resolve([text, undefined, undefined]);
+ }
+
+ const cursorPos = this.editor.getCursor();
+ const funcStatement = this._findTopLevelFunction(ast, cursorPos);
+ if (!funcStatement) {
+ return promise.resolve([text, undefined, undefined]);
+ }
+
+ let functionText = this._getFunctionText(funcStatement, text);
+
+ // TODO: This is a work around for bug 940086. It should be removed when
+ // that is fixed.
+ if (funcStatement.type == "FunctionDeclaration"
+ && !functionText.startsWith("function ")) {
+ functionText = "function " + functionText;
+ funcStatement.loc.start.column -= 9;
+ }
+
+ // The decrement by one is because our line numbers are 1-based, while
+ // CodeMirror's are 0-based.
+ const from = {
+ line: funcStatement.loc.start.line - 1,
+ ch: funcStatement.loc.start.column
+ };
+ const to = {
+ line: funcStatement.loc.end.line - 1,
+ ch: funcStatement.loc.end.column
+ };
+
+ const marker = this.editor.markText(from, to, { className: "eval-text" });
+ setTimeout(() => marker.clear(), EVAL_FUNCTION_TIMEOUT);
+
+ return this.evaluate(functionText);
+ },
+
/**
* Writes out a primitive value as a comment. This handles values which are
* to be printed directly (number, string) as well as grips to values
diff --git a/browser/devtools/scratchpad/scratchpad.xul b/browser/devtools/scratchpad/scratchpad.xul
index 5c8f780a9aa7..afffbba93793 100644
--- a/browser/devtools/scratchpad/scratchpad.xul
+++ b/browser/devtools/scratchpad/scratchpad.xul
@@ -63,6 +63,7 @@
+
@@ -108,6 +109,10 @@
key="&reloadAndRun.key;"
command="sp-cmd-reloadAndRun"
modifiers="accel,shift"/>
+
+
@@ -314,6 +324,11 @@
accesskey="&display.accesskey;"
key="sp-key-display"
command="sp-cmd-display"/>
+
diff --git a/browser/devtools/scratchpad/test/browser.ini b/browser/devtools/scratchpad/test/browser.ini
index 82ee710d3223..9c0c98764712 100644
--- a/browser/devtools/scratchpad/test/browser.ini
+++ b/browser/devtools/scratchpad/test/browser.ini
@@ -4,6 +4,7 @@ support-files = head.js
[browser_scratchpad_browser_last_window_closing.js]
[browser_scratchpad_reset_undo.js]
[browser_scratchpad_display_outputs_errors.js]
+[browser_scratchpad_eval_func.js]
[browser_scratchpad_goto_line_ui.js]
[browser_scratchpad_reload_and_run.js]
[browser_scratchpad_display_non_error_exceptions.js]
diff --git a/browser/devtools/scratchpad/test/browser_scratchpad_eval_func.js b/browser/devtools/scratchpad/test/browser_scratchpad_eval_func.js
new file mode 100644
index 000000000000..1dbc5fdfc206
--- /dev/null
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_eval_func.js
@@ -0,0 +1,86 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test()
+{
+ waitForExplicitFinish();
+
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+ gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+ openScratchpad(runTests);
+ }, true);
+
+ content.location = "data:text/html;charset=utf8,test Scratchpad eval function.";
+}
+
+function reportErrorAndQuit(error) {
+ DevToolsUtils.reportException("browser_scratchpad_eval_func.js", error);
+ ok(false);
+ finish();
+}
+
+function runTests(sw)
+{
+ const sp = sw.Scratchpad;
+
+ let foo = "" + function main() { console.log(1); };
+ let bar = "var bar = " + (() => { console.log(2); });
+
+ const fullText =
+ foo + "\n" +
+ "\n" +
+ bar + "\n"
+
+ sp.setText(fullText);
+
+ // On the function declaration.
+ sp.editor.setCursor({ line: 0, ch: 18 });
+ sp.evalTopLevelFunction()
+ .then(([text, error, result]) => {
+ is(text, foo, "Should re-eval foo.");
+ ok(!error, "Should not have got an error.");
+ ok(result, "Should have got a result.");
+ })
+
+ // On the arrow function.
+ .then(() => {
+ sp.editor.setCursor({ line: 2, ch: 18 });
+ return sp.evalTopLevelFunction();
+ })
+ .then(([text, error, result]) => {
+ is(text, bar.replace("var ", ""), "Should re-eval bar.");
+ ok(!error, "Should not have got an error.");
+ ok(result, "Should have got a result.");
+ })
+
+ // On the empty line.
+ .then(() => {
+ sp.editor.setCursor({ line: 1, ch: 0 });
+ return sp.evalTopLevelFunction();
+ })
+ .then(([text, error, result]) => {
+ is(text, fullText,
+ "Should get full text back since we didn't find a specific function.");
+ ok(!error, "Should not have got an error.");
+ ok(!result, "Should not have got a result.");
+ })
+
+ // Syntax error.
+ .then(() => {
+ sp.setText("function {}");
+ sp.editor.setCursor({ line: 0, ch: 9 });
+ return sp.evalTopLevelFunction();
+ })
+ .then(([text, error, result]) => {
+ is(text, "function {}",
+ "Should get the full text back since there was a parse error.");
+ ok(!error, "Should not have got an error");
+ ok(!result, "Should not have got a result");
+ ok(sp.getText().contains("SyntaxError"),
+ "We should have written the syntax error to the scratchpad.");
+ })
+
+ .then(finish, reportErrorAndQuit);
+}
diff --git a/browser/devtools/sourceeditor/editor.js b/browser/devtools/sourceeditor/editor.js
index 25b0706aea79..895eb4cee292 100644
--- a/browser/devtools/sourceeditor/editor.js
+++ b/browser/devtools/sourceeditor/editor.js
@@ -78,6 +78,7 @@ const CM_MAPPING = [
"clearHistory",
"openDialog",
"cursorCoords",
+ "markText",
"refresh"
];
diff --git a/browser/locales/en-US/chrome/browser/devtools/scratchpad.dtd b/browser/locales/en-US/chrome/browser/devtools/scratchpad.dtd
index e1d5d2a6c79e..e1a2c776c43b 100644
--- a/browser/locales/en-US/chrome/browser/devtools/scratchpad.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/scratchpad.dtd
@@ -120,3 +120,10 @@
- the same name in browser.dtd.
-->
+
+
+
+
+
diff --git a/browser/themes/shared/devtools/dark-theme.css b/browser/themes/shared/devtools/dark-theme.css
index 3bf69fa15d59..46c6c7609733 100644
--- a/browser/themes/shared/devtools/dark-theme.css
+++ b/browser/themes/shared/devtools/dark-theme.css
@@ -189,6 +189,11 @@ div.cm-s-mozilla span.CodeMirror-matchingbracket { /* highlight brackets */
color: white;
}
+/* Highlight for evaluating current statement. */
+div.CodeMirror span.eval-text {
+ background-color: #556;
+}
+
.cm-s-mozilla .CodeMirror-linenumber { /* line number text */
color: #5f7387;
}
diff --git a/browser/themes/shared/devtools/light-theme.css b/browser/themes/shared/devtools/light-theme.css
index acbbf6d090ae..3ae8aa38e3d4 100644
--- a/browser/themes/shared/devtools/light-theme.css
+++ b/browser/themes/shared/devtools/light-theme.css
@@ -188,6 +188,11 @@ div.cm-s-mozilla span.CodeMirror-matchingbracket { /* highlight brackets */
color: black;
}
+/* Highlight for evaluating current statement. */
+div.CodeMirror span.eval-text {
+ background-color: #ccd;
+}
+
.cm-s-mozilla .CodeMirror-linenumber { /* line number text */
color: #667380;
}
From 05ae73ac5895ce80cfeb0938bb1765b85b2aa930 Mon Sep 17 00:00:00 2001
From: Tim Taubert
Date: Mon, 28 Oct 2013 10:09:04 +0100
Subject: [PATCH 02/27] Bug 939784 - Provide a notification when the applicable
state of a style sheet changes r=bz
From 0d69149ae5e9809d42bb7fbe9c60b760b931ec7e Mon Sep 17 00:00:00 2001
---
content/base/src/nsDocument.cpp | 20 ++++++++++++++++++++
content/base/src/nsDocument.h | 6 ++++++
2 files changed, 26 insertions(+)
diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp
index 6b5feb2e73ff..89fbdb9c40c5 100644
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -3989,6 +3989,26 @@ nsDocument::SetStyleSheetApplicableState(nsIStyleSheet* aSheet,
"StyleSheetApplicableStateChanged",
aApplicable);
}
+
+ if (!mSSApplicableStateNotificationPending) {
+ nsRefPtr notification = NS_NewRunnableMethod(this,
+ &nsDocument::NotifyStyleSheetApplicableStateChanged);
+ mSSApplicableStateNotificationPending =
+ NS_SUCCEEDED(NS_DispatchToCurrentThread(notification));
+ }
+}
+
+void
+nsDocument::NotifyStyleSheetApplicableStateChanged()
+{
+ mSSApplicableStateNotificationPending = false;
+ nsCOMPtr observerService =
+ mozilla::services::GetObserverService();
+ if (observerService) {
+ observerService->NotifyObservers(static_cast(this),
+ "style-sheet-applicable-state-changed",
+ nullptr);
+ }
}
// These three functions are a lot like the implementation of the
diff --git a/content/base/src/nsDocument.h b/content/base/src/nsDocument.h
index 57f2fcf958c4..730923035630 100644
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -1156,6 +1156,8 @@ protected:
void EnsureOnloadBlocker();
+ void NotifyStyleSheetApplicableStateChanged();
+
nsTArray mCharSetObservers;
PLDHashTable *mSubDocuments;
@@ -1279,6 +1281,10 @@ protected:
bool mAsyncFullscreenPending:1;
+ // Keeps track of whether we have a pending
+ // 'style-sheet-applicable-state-changed' notification.
+ bool mSSApplicableStateNotificationPending:1;
+
uint32_t mCancelledPointerLockRequests;
uint8_t mXMLDeclarationBits;
From a28902649f4d9c467846b87b29d82b27aecc7026 Mon Sep 17 00:00:00 2001
From: Brad Lassey
Date: Wed, 23 Oct 2013 18:37:14 +0200
Subject: [PATCH 03/27] bug 930072 - Provide example app using GeckoView
library in the tree r=mfinkle, nalexander
---
.../geckoview_example/AndroidManifest.xml | 22 ++++++++++++
.../geckoview_example/GeckoViewExample.java | 14 ++++++++
.../android/geckoview_example/Makefile.in | 34 +++++++++++++++++++
embedding/android/geckoview_example/main.xml | 12 +++++++
embedding/android/geckoview_example/moz.build | 0
embedding/moz.build | 4 +++
6 files changed, 86 insertions(+)
create mode 100644 embedding/android/geckoview_example/AndroidManifest.xml
create mode 100644 embedding/android/geckoview_example/GeckoViewExample.java
create mode 100644 embedding/android/geckoview_example/Makefile.in
create mode 100644 embedding/android/geckoview_example/main.xml
create mode 100644 embedding/android/geckoview_example/moz.build
diff --git a/embedding/android/geckoview_example/AndroidManifest.xml b/embedding/android/geckoview_example/AndroidManifest.xml
new file mode 100644
index 000000000000..6ced8a2b56bf
--- /dev/null
+++ b/embedding/android/geckoview_example/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/embedding/android/geckoview_example/GeckoViewExample.java b/embedding/android/geckoview_example/GeckoViewExample.java
new file mode 100644
index 000000000000..dafe70e861cf
--- /dev/null
+++ b/embedding/android/geckoview_example/GeckoViewExample.java
@@ -0,0 +1,14 @@
+package org.mozilla.geckoviewexample;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.AttributeSet;
+
+public class GeckoViewExample extends Activity {
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+ }
+}
diff --git a/embedding/android/geckoview_example/Makefile.in b/embedding/android/geckoview_example/Makefile.in
new file mode 100644
index 000000000000..005c714067ad
--- /dev/null
+++ b/embedding/android/geckoview_example/Makefile.in
@@ -0,0 +1,34 @@
+include $(topsrcdir)/config/rules.mk
+
+GARBAGE = \
+ AndroidManifest.xml \
+ proguard-project.txt \
+ project.properties \
+ ant.properties \
+ build.xml \
+ local.properties \
+ $(NULL)
+
+GARBAGE_DIRS = \
+ assets \
+ geckoview_library \
+ gen \
+ bin \
+ libs \
+ res \
+ src \
+ $(NULL)
+
+ANDROID=$(ANDROID_SDK)/../../tools/android
+
+build.xml:
+ $(ANDROID) create project --name GeckoViewExample --target android-18 --path $(CURDIR) --activity GeckoViewExample --package org.mozilla.geckoviewexample
+ $(ANDROID) update project --target android-18 --path $(CURDIR) --library $(DEPTH)/mobile/android/geckoview_library
+ $(UNZIP) -o $(DIST)/geckoview_library/geckoview_assets.zip
+ $(NSINSTALL) $(srcdir)/main.xml res/layout/
+ $(NSINSTALL) $(srcdir)/AndroidManifest.xml .
+ $(NSINSTALL) $(srcdir)/GeckoViewExample.java src/org/mozilla/geckoviewexample/
+ echo jar.libs.dir=libs >> project.properties
+
+package: build.xml
+ ant debug
diff --git a/embedding/android/geckoview_example/main.xml b/embedding/android/geckoview_example/main.xml
new file mode 100644
index 000000000000..4f1c80e13dff
--- /dev/null
+++ b/embedding/android/geckoview_example/main.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/embedding/android/geckoview_example/moz.build b/embedding/android/geckoview_example/moz.build
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/embedding/moz.build b/embedding/moz.build
index 6b326a85cf7d..35f6bde5e017 100644
--- a/embedding/moz.build
+++ b/embedding/moz.build
@@ -5,6 +5,10 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DIRS += ['base', 'components', 'browser']
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
+ DIRS += ['android/geckoview_example']
+
TEST_DIRS += ['test']
if CONFIG['ENABLE_TESTS']:
From 618ec264b40ec64198e05c3536ad7c8ccb59efc3 Mon Sep 17 00:00:00 2001
From: Anton Kovalyov
Date: Tue, 19 Nov 2013 15:53:13 -0800
Subject: [PATCH 04/27] Bug 929766 - Remove Orion from the SourceEditor
component. r=msucan
---
browser/app/profile/firefox.js | 8 -
browser/devtools/jar.mn | 2 -
browser/devtools/markupview/html-editor.js | 3 +-
browser/devtools/scratchpad/scratchpad.js | 2 +-
.../browser_scratchpad_edit_ui_updates.js | 4 -
.../sourceeditor/codemirror/mozilla.css | 10 +-
browser/devtools/sourceeditor/editor.js | 19 +-
browser/devtools/sourceeditor/moz.build | 5 +-
browser/devtools/sourceeditor/orion/LICENSE | 29 -
.../sourceeditor/orion/Makefile.dryice.js | 56 -
browser/devtools/sourceeditor/orion/README | 43 -
browser/devtools/sourceeditor/orion/UPGRADE | 20 -
browser/devtools/sourceeditor/orion/orion.css | 277 -
browser/devtools/sourceeditor/orion/orion.js | 12303 ----------------
.../sourceeditor/source-editor-orion.jsm | 2131 ---
.../sourceeditor/source-editor-overlay.xul | 204 -
.../sourceeditor/source-editor-ui.jsm | 332 -
.../devtools/sourceeditor/source-editor.jsm | 455 -
.../devtools/sourceeditor/test/browser.ini | 26 +-
.../test/browser_bug650345_find.js | 149 -
.../test/browser_bug684546_reset_undo.js | 72 -
.../test/browser_bug684862_paste_html.js | 119 -
.../test/browser_bug687160_line_api.js | 90 -
.../test/browser_bug687568_pagescroll.js | 89 -
.../test/browser_bug687573_vscroll.js | 133 -
.../test/browser_bug687580_drag_and_drop.js | 162 -
.../browser_bug695035_middle_click_paste.js | 100 -
.../test/browser_bug700893_dirty_state.js | 94 -
.../test/browser_bug703692_focus_blur.js | 71 -
.../browser_bug707987_debugger_breakpoints.js | 169 -
.../browser_bug712982_line_ruler_click.js | 74 -
.../test/browser_bug725388_mouse_events.js | 107 -
...wser_bug725392_mouse_coords_char_offset.js | 160 -
.../browser_bug725430_comment_uncomment.js | 151 -
.../browser_bug725618_moveLines_shortcut.js | 117 -
.../browser_bug729480_line_vertical_align.js | 99 -
.../browser_bug729960_block_bracket_jump.js | 164 -
.../browser_bug731721_debugger_stepping.js | 59 -
...rowser_bug744021_next_prev_bracket_jump.js | 104 -
.../sourceeditor/test/browser_editor_basic.js | 51 +
.../test/browser_editor_cursor.js | 36 +
.../test/browser_editor_history.js | 32 +
.../test/browser_editor_markers.js | 39 +
.../browser_sourceeditor_initialization.js | 499 -
browser/devtools/sourceeditor/test/head.js | 202 +-
.../devtools/styleeditor/StyleSheetEditor.jsm | 7 +-
browser/devtools/styleeditor/styleeditor.xul | 45 +-
.../test/browser_styleeditor_sv_resize.js | 2 +-
...n-breakpoint.png => editor-breakpoint.png} | Bin
...location.png => editor-debug-location.png} | Bin
.../{orion-error.png => editor-error.png} | Bin
.../themes/linux/devtools/orion-container.css | 39 -
browser/themes/linux/devtools/orion-task.png | Bin 413 -> 0 bytes
browser/themes/linux/devtools/orion.css | 199 -
browser/themes/linux/jar.mn | 9 +-
...n-breakpoint.png => editor-breakpoint.png} | Bin
...location.png => editor-debug-location.png} | Bin
.../{orion-error.png => editor-error.png} | Bin
.../themes/osx/devtools/orion-container.css | 39 -
browser/themes/osx/devtools/orion-task.png | Bin 413 -> 0 bytes
browser/themes/osx/devtools/orion.css | 199 -
browser/themes/osx/jar.mn | 9 +-
...n-breakpoint.png => editor-breakpoint.png} | Bin
...location.png => editor-debug-location.png} | Bin
.../{orion-error.png => editor-error.png} | Bin
.../windows/devtools/orion-container.css | 39 -
.../themes/windows/devtools/orion-task.png | Bin 413 -> 0 bytes
browser/themes/windows/devtools/orion.css | 199 -
browser/themes/windows/jar.mn | 18 +-
69 files changed, 268 insertions(+), 19607 deletions(-)
delete mode 100644 browser/devtools/sourceeditor/orion/LICENSE
delete mode 100644 browser/devtools/sourceeditor/orion/Makefile.dryice.js
delete mode 100644 browser/devtools/sourceeditor/orion/README
delete mode 100644 browser/devtools/sourceeditor/orion/UPGRADE
delete mode 100644 browser/devtools/sourceeditor/orion/orion.css
delete mode 100644 browser/devtools/sourceeditor/orion/orion.js
delete mode 100644 browser/devtools/sourceeditor/source-editor-orion.jsm
delete mode 100644 browser/devtools/sourceeditor/source-editor-overlay.xul
delete mode 100644 browser/devtools/sourceeditor/source-editor-ui.jsm
delete mode 100644 browser/devtools/sourceeditor/source-editor.jsm
delete mode 100644 browser/devtools/sourceeditor/test/browser_bug650345_find.js
delete mode 100644 browser/devtools/sourceeditor/test/browser_bug684546_reset_undo.js
delete mode 100644 browser/devtools/sourceeditor/test/browser_bug684862_paste_html.js
delete mode 100644 browser/devtools/sourceeditor/test/browser_bug687160_line_api.js
delete mode 100644 browser/devtools/sourceeditor/test/browser_bug687568_pagescroll.js
delete mode 100644 browser/devtools/sourceeditor/test/browser_bug687573_vscroll.js
delete mode 100644 browser/devtools/sourceeditor/test/browser_bug687580_drag_and_drop.js
delete mode 100644 browser/devtools/sourceeditor/test/browser_bug695035_middle_click_paste.js
delete mode 100644 browser/devtools/sourceeditor/test/browser_bug700893_dirty_state.js
delete mode 100644 browser/devtools/sourceeditor/test/browser_bug703692_focus_blur.js
delete mode 100644 browser/devtools/sourceeditor/test/browser_bug707987_debugger_breakpoints.js
delete mode 100644 browser/devtools/sourceeditor/test/browser_bug712982_line_ruler_click.js
delete mode 100644 browser/devtools/sourceeditor/test/browser_bug725388_mouse_events.js
delete mode 100644 browser/devtools/sourceeditor/test/browser_bug725392_mouse_coords_char_offset.js
delete mode 100644 browser/devtools/sourceeditor/test/browser_bug725430_comment_uncomment.js
delete mode 100644 browser/devtools/sourceeditor/test/browser_bug725618_moveLines_shortcut.js
delete mode 100644 browser/devtools/sourceeditor/test/browser_bug729480_line_vertical_align.js
delete mode 100644 browser/devtools/sourceeditor/test/browser_bug729960_block_bracket_jump.js
delete mode 100644 browser/devtools/sourceeditor/test/browser_bug731721_debugger_stepping.js
delete mode 100644 browser/devtools/sourceeditor/test/browser_bug744021_next_prev_bracket_jump.js
create mode 100644 browser/devtools/sourceeditor/test/browser_editor_basic.js
create mode 100644 browser/devtools/sourceeditor/test/browser_editor_cursor.js
create mode 100644 browser/devtools/sourceeditor/test/browser_editor_history.js
create mode 100644 browser/devtools/sourceeditor/test/browser_editor_markers.js
delete mode 100644 browser/devtools/sourceeditor/test/browser_sourceeditor_initialization.js
rename browser/themes/linux/devtools/{orion-breakpoint.png => editor-breakpoint.png} (100%)
rename browser/themes/linux/devtools/{orion-debug-location.png => editor-debug-location.png} (100%)
rename browser/themes/linux/devtools/{orion-error.png => editor-error.png} (100%)
delete mode 100644 browser/themes/linux/devtools/orion-container.css
delete mode 100644 browser/themes/linux/devtools/orion-task.png
delete mode 100644 browser/themes/linux/devtools/orion.css
rename browser/themes/osx/devtools/{orion-breakpoint.png => editor-breakpoint.png} (100%)
rename browser/themes/osx/devtools/{orion-debug-location.png => editor-debug-location.png} (100%)
rename browser/themes/osx/devtools/{orion-error.png => editor-error.png} (100%)
delete mode 100644 browser/themes/osx/devtools/orion-container.css
delete mode 100644 browser/themes/osx/devtools/orion-task.png
delete mode 100644 browser/themes/osx/devtools/orion.css
rename browser/themes/windows/devtools/{orion-breakpoint.png => editor-breakpoint.png} (100%)
rename browser/themes/windows/devtools/{orion-debug-location.png => editor-debug-location.png} (100%)
rename browser/themes/windows/devtools/{orion-error.png => editor-error.png} (100%)
delete mode 100644 browser/themes/windows/devtools/orion-container.css
delete mode 100644 browser/themes/windows/devtools/orion-task.png
delete mode 100644 browser/themes/windows/devtools/orion.css
diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
index 88dc128fb812..0bebde252b52 100644
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1227,14 +1227,6 @@ pref("devtools.hud.loglimit.console", 200);
pref("devtools.editor.tabsize", 4);
pref("devtools.editor.expandtab", true);
-// Tells which component you want to use for source editing in developer tools.
-//
-// Available components:
-// "orion" - this is the Orion source code editor from the Eclipse project. It
-// provides programmer-specific editor features such as syntax highlighting,
-// indenting and bracket recognition.
-pref("devtools.editor.component", "orion");
-
// Enable the Font Inspector
pref("devtools.fontinspector.enabled", true);
diff --git a/browser/devtools/jar.mn b/browser/devtools/jar.mn
index e4e784b3ec72..8f23afb5420b 100644
--- a/browser/devtools/jar.mn
+++ b/browser/devtools/jar.mn
@@ -28,7 +28,6 @@ browser.jar:
content/browser/devtools/fontinspector/font-inspector.js (fontinspector/font-inspector.js)
content/browser/devtools/fontinspector/font-inspector.xhtml (fontinspector/font-inspector.xhtml)
content/browser/devtools/fontinspector/font-inspector.css (fontinspector/font-inspector.css)
- content/browser/devtools/orion.js (sourceeditor/orion/orion.js)
content/browser/devtools/codemirror/codemirror.js (sourceeditor/codemirror/codemirror.js)
content/browser/devtools/codemirror/codemirror.css (sourceeditor/codemirror/codemirror.css)
content/browser/devtools/codemirror/javascript.js (sourceeditor/codemirror/javascript.js)
@@ -45,7 +44,6 @@ browser.jar:
content/browser/devtools/codemirror/dialog.js (sourceeditor/codemirror/dialog/dialog.js)
content/browser/devtools/codemirror/dialog.css (sourceeditor/codemirror/dialog/dialog.css)
content/browser/devtools/codemirror/mozilla.css (sourceeditor/codemirror/mozilla.css)
-* content/browser/devtools/source-editor-overlay.xul (sourceeditor/source-editor-overlay.xul)
content/browser/devtools/debugger.xul (debugger/debugger.xul)
content/browser/devtools/debugger.css (debugger/debugger.css)
content/browser/devtools/debugger-controller.js (debugger/debugger-controller.js)
diff --git a/browser/devtools/markupview/html-editor.js b/browser/devtools/markupview/html-editor.js
index d11e39a928b0..32517147c987 100644
--- a/browser/devtools/markupview/html-editor.js
+++ b/browser/devtools/markupview/html-editor.js
@@ -67,7 +67,8 @@ function HTMLEditor(htmlDocument)
this.editorInner.addEventListener("click", stopPropagation, false);
this.editor = new Editor(config);
- this.editor.appendTo(this.editorInner).then(() => {
+ let iframe = this.editorInner.ownerDocument.createElement("iframe");
+ this.editor.appendTo(this.editorInner, iframe).then(() => {
this.hide(false);
}).then(null, (err) => console.log(err.message));
}
diff --git a/browser/devtools/scratchpad/scratchpad.js b/browser/devtools/scratchpad/scratchpad.js
index 0cc646e32d6a..b8a8e3b7f8eb 100644
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -1682,7 +1682,7 @@ var Scratchpad = {
* Add an observer for Scratchpad events.
*
* The observer implements IScratchpadObserver := {
- * onReady: Called when the Scratchpad and its SourceEditor are ready.
+ * onReady: Called when the Scratchpad and its Editor are ready.
* Arguments: (Scratchpad aScratchpad)
* }
*
diff --git a/browser/devtools/scratchpad/test/browser_scratchpad_edit_ui_updates.js b/browser/devtools/scratchpad/test/browser_scratchpad_edit_ui_updates.js
index 70e9e2248e5e..f864d5973e85 100644
--- a/browser/devtools/scratchpad/test/browser_scratchpad_edit_ui_updates.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_edit_ui_updates.js
@@ -5,10 +5,6 @@
"use strict";
-let tempScope = {};
-Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm", tempScope);
-let SourceEditor = tempScope.SourceEditor;
-
function test()
{
waitForExplicitFinish();
diff --git a/browser/devtools/sourceeditor/codemirror/mozilla.css b/browser/devtools/sourceeditor/codemirror/mozilla.css
index 1f79b470f392..ccda017122d9 100644
--- a/browser/devtools/sourceeditor/codemirror/mozilla.css
+++ b/browser/devtools/sourceeditor/codemirror/mozilla.css
@@ -18,21 +18,21 @@
}
.error {
- background-image: url("chrome://browser/skin/devtools/orion-error.png");
+ background-image: url("chrome://browser/skin/devtools/editor-error.png");
opacity: 0.75;
}
.breakpoint {
- background-image: url("chrome://browser/skin/devtools/orion-breakpoint.png");
+ background-image: url("chrome://browser/skin/devtools/editor-breakpoint.png");
}
.debugLocation {
- background-image: url("chrome://browser/skin/devtools/orion-debug-location.png");
+ background-image: url("chrome://browser/skin/devtools/editor-debug-location.png");
}
.breakpoint.debugLocation {
- background-image: url("chrome://browser/skin/devtools/orion-debug-location.png"),
- url("chrome://browser/skin/devtools/orion-breakpoint.png");
+ background-image: url("chrome://browser/skin/devtools/editor-debug-location.png"),
+ url("chrome://browser/skin/devtools/editor-breakpoint.png");
}
.error-line {
diff --git a/browser/devtools/sourceeditor/editor.js b/browser/devtools/sourceeditor/editor.js
index 895eb4cee292..7b942d47dd8d 100644
--- a/browser/devtools/sourceeditor/editor.js
+++ b/browser/devtools/sourceeditor/editor.js
@@ -84,7 +84,7 @@ const CM_MAPPING = [
const CM_JUMP_DIALOG = [
L10N.GetStringFromName("gotoLineCmd.promptTitle")
- + " "
+ + " "
];
const { cssProperties, cssValues, cssColors } = getCSSKeywords();
@@ -186,15 +186,19 @@ Editor.prototype = {
/**
* Appends the current Editor instance to the element specified by
- * the only argument 'el'. This method actually creates and loads
- * CodeMirror and all its dependencies.
+ * 'el'. You can also provide your won iframe to host the editor as
+ * an optional second parameter. This method actually creates and
+ * loads CodeMirror and all its dependencies.
*
* This method is asynchronous and returns a promise.
*/
- appendTo: function (el) {
+ appendTo: function (el, env) {
let def = promise.defer();
let cm = editors.get(this);
- let env = el.ownerDocument.createElement("iframe");
+
+ if (!env)
+ env = el.ownerDocument.createElementNS(XUL_NS, "iframe");
+
env.flex = 1;
if (cm)
@@ -503,10 +507,11 @@ Editor.prototype = {
hasLineClass: function (line, className) {
let cm = editors.get(this);
let info = cm.lineInfo(line);
- if (!info)
+
+ if (!info || !info.wrapClass)
return false;
- return info.wrapClass == className;
+ return info.wrapClass.split(" ").indexOf(className) != -1;
},
/**
diff --git a/browser/devtools/sourceeditor/moz.build b/browser/devtools/sourceeditor/moz.build
index f12cb40bb3f5..9a586a0f3510 100644
--- a/browser/devtools/sourceeditor/moz.build
+++ b/browser/devtools/sourceeditor/moz.build
@@ -10,9 +10,6 @@ JS_MODULES_PATH = 'modules/devtools/sourceeditor'
EXTRA_JS_MODULES += [
'debugger.js',
- 'editor.js',
- 'source-editor-orion.jsm',
- 'source-editor-ui.jsm',
- 'source-editor.jsm',
+ 'editor.js'
]
diff --git a/browser/devtools/sourceeditor/orion/LICENSE b/browser/devtools/sourceeditor/orion/LICENSE
deleted file mode 100644
index 2d907d73a37e..000000000000
--- a/browser/devtools/sourceeditor/orion/LICENSE
+++ /dev/null
@@ -1,29 +0,0 @@
-Eclipse Distribution License - v 1.0
-
-Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors.
-
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-* Redistributions in binary form must reproduce the above copyright notice, this
- list of conditions and the following disclaimer in the documentation and/or
- other materials provided with the distribution.
-* Neither the name of the Eclipse Foundation, Inc. nor the names of its
- contributors may be used to endorse or promote products derived from this
- software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
diff --git a/browser/devtools/sourceeditor/orion/Makefile.dryice.js b/browser/devtools/sourceeditor/orion/Makefile.dryice.js
deleted file mode 100644
index 6866ed93771a..000000000000
--- a/browser/devtools/sourceeditor/orion/Makefile.dryice.js
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/usr/bin/env node
-/* vim:set ts=2 sw=2 sts=2 et tw=80:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-var copy = require('dryice').copy;
-
-const ORION_EDITOR = "org.eclipse.orion.client.editor/web";
-
-var js_src = copy.createDataObject();
-
-copy({
- source: [
- ORION_EDITOR + "/orion/textview/global.js",
- ORION_EDITOR + "/orion/textview/eventTarget.js",
- ORION_EDITOR + "/orion/editor/regex.js",
- ORION_EDITOR + "/orion/textview/keyBinding.js",
- ORION_EDITOR + "/orion/textview/annotations.js",
- ORION_EDITOR + "/orion/textview/rulers.js",
- ORION_EDITOR + "/orion/textview/undoStack.js",
- ORION_EDITOR + "/orion/textview/textModel.js",
- ORION_EDITOR + "/orion/textview/projectionTextModel.js",
- ORION_EDITOR + "/orion/textview/tooltip.js",
- ORION_EDITOR + "/orion/textview/textView.js",
- ORION_EDITOR + "/orion/textview/textDND.js",
- ORION_EDITOR + "/orion/editor/htmlGrammar.js",
- ORION_EDITOR + "/orion/editor/textMateStyler.js",
- ORION_EDITOR + "/examples/textview/textStyler.js",
- ],
- dest: js_src,
-});
-
-copy({
- source: js_src,
- dest: "orion.js",
-});
-
-var css_src = copy.createDataObject();
-
-copy({
- source: [
- ORION_EDITOR + "/orion/textview/textview.css",
- ORION_EDITOR + "/orion/textview/rulers.css",
- ORION_EDITOR + "/orion/textview/annotations.css",
- ORION_EDITOR + "/examples/textview/textstyler.css",
- ORION_EDITOR + "/examples/editor/htmlStyles.css",
- ],
- dest: css_src,
-});
-
-copy({
- source: css_src,
- dest: "orion.css",
-});
-
diff --git a/browser/devtools/sourceeditor/orion/README b/browser/devtools/sourceeditor/orion/README
deleted file mode 100644
index c7669099cbfb..000000000000
--- a/browser/devtools/sourceeditor/orion/README
+++ /dev/null
@@ -1,43 +0,0 @@
-# Introduction
-
-This is the Orion editor packaged for Mozilla.
-
-The Orion editor web site: http://www.eclipse.org/orion
-
-# Upgrade
-
-To upgrade Orion to a newer version see the UPGRADE file.
-
-Orion version: git clone from 2012-01-26
- commit hash 1d1150131dacecc9f4d9eb3cdda9103ea1819045
-
- + patch for Eclipse Bug 370584 - [Firefox] Edit menu items in context menus
- http://git.eclipse.org/c/orion/org.eclipse.orion.client.git/commit/?id=137d5a8e9bbc0fa204caae74ebd25a7d9d4729bd
- see https://bugs.eclipse.org/bugs/show_bug.cgi?id=370584
-
- + patches for Eclipse Bug 370606 - Problems with UndoStack and deletions at
- the beginning of the document
- http://git.eclipse.org/c/orion/org.eclipse.orion.client.git/commit/?id=cec71bddaf32251c34d3728df5da13c130d14f33
- http://git.eclipse.org/c/orion/org.eclipse.orion.client.git/commit/?id=3ce24b94f1d8103b16b9cf16f2f50a6302d43b18
- http://git.eclipse.org/c/orion/org.eclipse.orion.client.git/commit/?id=27177e9a3dc70c20b4877e3eab3adfff1d56e342
- see https://bugs.eclipse.org/bugs/show_bug.cgi?id=370606
-
- + patch for Mozilla Bug 730532 - remove CSS2Properties aliases for MozOpacity
- and MozOutline*
- see https://bugzilla.mozilla.org/show_bug.cgi?id=730532#c3
-
-# License
-
-The following files are licensed according to the contents in the LICENSE
-file:
- orion.js
- orion.css
-
-# Theming
-
-The syntax highlighting and the editor UI are themed using a style sheet. The
-default theme file is browser/themes/*/devtools/orion.css - this is based on the
-orion.css found in this folder.
-
-Please note that the orion.css file from this folder is not used. It is kept
-here only as reference.
diff --git a/browser/devtools/sourceeditor/orion/UPGRADE b/browser/devtools/sourceeditor/orion/UPGRADE
deleted file mode 100644
index a2c006efe66e..000000000000
--- a/browser/devtools/sourceeditor/orion/UPGRADE
+++ /dev/null
@@ -1,20 +0,0 @@
-Upgrade notes:
-
-1. Get the Orion client source code from:
-http://www.eclipse.org/orion
-
-2. Install Dryice from:
-https://github.com/mozilla/dryice
-
-You also need nodejs for Dryice to run:
-http://nodejs.org
-
-3. Copy Makefile.dryice.js to:
-org.eclipse.orion.client/bundles/
-
-4. Execute Makefile.dryice.js. You should get orion.js and orion.css.
-
-5. Copy the two files back here.
-
-6. Make a new build of Firefox.
-
diff --git a/browser/devtools/sourceeditor/orion/orion.css b/browser/devtools/sourceeditor/orion/orion.css
deleted file mode 100644
index 1e3b003cafe5..000000000000
--- a/browser/devtools/sourceeditor/orion/orion.css
+++ /dev/null
@@ -1,277 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-.view {
- background-color: white;
-}
-
-.viewContainer {
- background-color: #eeeeee;
- font-family: monospace;
- font-size: 10pt;
-}
-::-webkit-scrollbar-corner {
- background-color: #eeeeee;
-}
-
-.viewContent {
-}/* Styles for rulers */
-.ruler {
- background-color: white;
-}
-.ruler.annotations {
- border-right: 1px solid lightgray;
- width: 16px;
-}
-.ruler.folding {
- border-right: 1px solid lightgray;
- width: 14px;
-}
-.ruler.lines {
- border-right: 1px solid lightgray;
- text-align: right;
-}
-.ruler.overview {
- border-left: 1px solid lightgray;
- width: 14px;
-}
-
-/* Styles for the line number ruler */
-.rulerLines {
-}
-.rulerLines.even
-.rulerLines.odd {
-}/* Styles for the annotation ruler (all lines) */
-.annotation {
-}
-.annotation.error,
-.annotation.warning
-.annotation.task,
-.annotation.bookmark,
-.annotation.breakpoint,
-.annotation.collapsed
-.annotation.expanded {
-}
-
-/* Styles for the annotation ruler (first line) */
-.annotationHTML {
- cursor: pointer;
- width: 16px;
- height: 16px;
- display: inline-block;
- vertical-align: middle;
- background-position: center;
- background-repeat: no-repeat;
-}
-.annotationHTML.error {
- /* images/error.gif */
- background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAPVvcvWHiPVucvRuc+ttcfV6f91KVN5LU99PV/FZY/JhaM4oN84pONE4Rd1ATfJLWutVYPRgbdxpcsgWKMgZKs4lNfE/UvE/U+artcpdSc5uXveimslHPuBhW/eJhfV5efaCgO2CgP+/v+PExP///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACUALAAAAAAQABAAAAZ+wJJwSCwaScgkySgkjTQZTkYzWhadnE5oE+pwqkSshwQqkzxfa4kkQXxEpA9J9EFI1KQGQQBAigYCBA14ExEWF0gXihETeA0QD3AkD5QQg0NsDnAJmwkOd5gYFSQKpXAFDBhqaxgLBwQBBAapq00YEg0UDRKqTGtKSL7Cw8JBADs=");
-}
-.annotationHTML.warning {
- /* images/warning.gif */
- background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAP7bc//egf/ij/7ijv/jl/7kl//mnv7lnv/uwf7CTP7DTf7DT/7IW//Na/7Na//NbP7QdP/dmbltAIJNAF03AMSAJMSCLKqASa2DS6uBSquCSrGHTq6ETbCHT7WKUrKIUcCVXL+UXMOYX8GWXsSZYMiib6+ETbOIUcOXX86uhd3Muf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACsALAAAAAAQABAAAAZowJVwSCwaj0ihikRSJYcoBEL0XKlGkcjImQQhJBREKFnyICoThKeE/AAW6AXgdPyUAgrLJBEo0YsbAQyDhAEdRRwDDw8OaA4NDQImRBgFEJdglxAEGEQZKQcHBqOkKRpFF6mqq1WtrUEAOw==");
-}
-.annotationHTML.task {
- /* images/task.gif */
- background-image: url("data:image/gif;base64,R0lGODlhEAAQAMQAAN7s4uTy6ICvY423c2WdP2ugR3mqWYeza2ejOl6VNVqPM1aJMURsJ2GaOnKlT8PbsbPDqGmmO1OCLk98LEhxKGWfOWKaN0t2KkJoJf///////wAAAAAAAAAAAAAAAAAAACH5BAEAABoALAAAAAAQABAAAAVmoCaOZDk+UaquDxkNcCxHJHLceI6QleD/vkCmQrIYjkiDMGAhJRzQ6NKRICkKgYJ2qVWQFktCmEBYkCSNZSbQaDckpAl5TCZMSBdtAaDXX0gUUYJRFCQMSYgGDCQQGI6PkBAmkyUhADs=");
-}
-.annotationHTML.bookmark {
- /* images/bookmark.gif */
- background-image: url("data:image/gif;base64,R0lGODlhEAAQALMAAP7//+/VNPzZS/vifeumAPrBOOSlHOSuRP///wAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAgALAAAAAAQABAAAARLEMlJq5Xn3EvIrkenfRIhCB5pmKhRdbAJAGhssuc8n6eJoAKdkOaTAIdEQeWoA1oGsiZhYAnIcqiApVPjElyUbkFSgCkn5XElLYkAADs=");
-}
-.annotationHTML.breakpoint {
- /* images/breakpoint.gif */
- background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAFheoFxkoFxnpmt0pmZxpnF7rYyWwmJwpnaFs3aDrWt8rXGBrYycwmZ3mXuNs42cu77F03GIs3aJrYGVu2J5oKCuxeDj6LK/03GLrYieu3aIoIygu6m4zcLN3MTM1m6Rs2aLriRgkSZilXGXtoGcs7LD0QBLhSZikihol3ScubrO2Yaqu5q4xpO0wpm7yabF0ZO9yaXI0r3X3tHj6P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAADQALAAAAAAQABAAAAafQJpwSCwWLYZBIDAwWIw0A+FFpW6aRUPCxe1yE4ahhdCCxWSzmSwGgxGeUceKpUqhUCkVa7UK0wgkJCUjJoUmIyWBBEIEGhoeJ4YmJx6OAUIADQ0QIZIhEJoAQgEUFBUgkiAVpZdRCxIPFx8iIh8XDw4FfhYHDhgZHB0dHBkYEwdwUQoTEc3OEwp+QwYHCBMMDBMIB9JESAJLAk5Q5EVBADs=");
-}
-.annotationHTML.collapsed {
- /* images/collapsed.png */
- width: 14px;
- height: 14px;
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAWBJREFUeNpi/P//PwMlgImBQkCxASzoAp++fo+6de+Z+fXbD/Jev/nAICoiwKCpqrBBTUlqNR835zJ09YzIYfDxy7eo/cevLmXlYGNQUJAEahZieP3mHcODB08Zfv/4w+BoqR3Nz8O1DKcXzt94HPqXmZlBU1+LgZNfkMHazIOBA0hr6uswgMTP33gYijcMLlx/EMAnLs7w7sc/hg9AG0HgPZB+B8S84hJA+UcBeMPg+at3DJIMnAxZzt5wsUhnXzDdsmIVWB6vAcLCfAys3z4wzN64huEfkJ/uH8IwexOQDQymD2/fgeXxekFLRWHD51evGDhZGRi4WSFSnCwgNjB2Xr1m0AbK4zXAQkdhNdPf3wx3r91g+PruLcOqnasYvn54x3Dv2k0G5r+/GMyB8nijEQTefvoadeH6w9Cbtx8GvH//kUFQkJ9BQ1V+g76m/GphPu5lBA0YenmBYgMAAgwA34GIKjmLxOUAAAAASUVORK5CYII=");
-}
-.annotationHTML.expanded {
- /* images/expanded.png */
- width: 14px;
- height: 14px;
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAT5JREFUeNrUksFKw0AURW+mTWw67SSEiG209U90r4jddFO34l+5U0HdZCHiFwiCOz9AlMSmGEpMOqk1TWJSFGyFbATR2dyZd+Dw3mOENE3xkyP8PYHrBT3OX7uW43ZefA6FUaw1dJPSyrmu1k8KBYOh37Od4XFZLEPXFdRrFMGIw3U9TKMYqw1tb0VjcxLy9eEF425CCIxWE5JcxSQGxCyNloG87gXhwWIHc4J767lTZQw8ShFGSZbxRyaQmZJxd3NRUJ6ffwQNEi6PzG/L2tjdmvFCgcKqKL2F2Olu43MzggDka+IjPuOFI7Sbujn2fUglYKkkzFIi+R0I/QDrGS8UqDX5QkhiOHYfE84hkhSTkGNgOyDJFCzjhYLTq+vDtrG8r1LZtB6fcHtzB+uhD5VWzLx+lvF/8JV/XfAuwADsrJbMGG4l4AAAAABJRU5ErkJggg==");
-}
-.annotationHTML.multiple {
- /* images/multiple.gif */
- background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAOdpa+yJiuFYXOFYXeBYXONwded8f+NwdmhwkHB4iPr7/ezx+fP2+2h4kOzy+Wh4iPr8/gCBwTaczjaXyjaYyjaXyTaYyfr8/QCMzQCMzACHxzao2jal2Dak1zag03iAgI/Ckn64fZrHmX+4fZLCianPopPCiarOoqbLlafLlbnXq7nWq6fLlMTcsoCIeJCQcIiIeKCYaJiQcO16ee16evGVlfGWlfahn/ahoPWhn/WhoPe1tP///////wAAAAAAACH5BAEAAD0ALAAAAAAQABAAAAaRwJ5wSCwaj8WYcslcDmObaDTGq1Zjzw4mk+FQIRcFTzaUeTRoj4zHaI+HL0lkLnnxFgsH7zWEWSoTFBMwVlUwQy6JMDCJjYwuQx8tk5MfOzk4OjcfkSssKCkqHzY0MzQ1nEIJJSYkJCcJAQCzAQlDDyIjISMiCQYEAgMGD0MNIMfHDQUHBc3EQgjR0tPSSNY9QQA7");
-}
-.annotationHTML.overlay {
- /* images/plus.png */
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAAXNSR0IArs4c6QAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJEAQvB2JVdrAAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAD1JREFUCNdtjkESADAEAzemf69f66HMqGlOIhYiFRFRtSQBWAY7mzx+EDTL6sSgb1jTk7Q87rxyqe37fXsAa78gLyZnRgEAAAAASUVORK5CYII=");
- background-position: right bottom;
- position: relative;
- top: -16px;
-}
-.annotationHTML.currentBracket {
- /* images/currentBracket.png */
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sLEBULCGQmEKAAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAnklEQVQ4y7VTsRHDIBATJg1HCUzAHEzFBExAzwZsRMkE9gifKhc72ODYibr/+xcnoQdugq0LAujEwmbn0UxQh4OxpjX1XgshwFqLnPM5PQTQGlprWpbl3RhJ/CSQUm7qPYLp7i8cEpRSoJT6ju0lIaVEQgiKMQ4lHHpQayVjzHWCn5jIOcc8z9dMBADvPZxz3SC1tzCI8vgWdvL+VzwB8JSj2GFTyxIAAAAASUVORK5CYII=");
-}
-.annotationHTML.matchingBracket {
- /* images/matchingBracket.png */
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sLEBUMAsuyb3kAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAoklEQVQ4y61TsQ3EIAw80DcI0USKGIApWIsB2IGGKbJPugxBR3VfvfRRCOSTvw7LPuPzGXgI8f0gwAsFu5rXIYMdDiEOIdnKW5YFzjnEGH+bhwA/KKVwmibu0BhRnpEZY1BrHTaVT7fQJZjnGeu63tOAJFNKVEox53yqQZfAWstt27oidgm01ve3UEqBaBjnspG89wgh3LiFgZXHt3Dh23/FGxKViehm0X85AAAAAElFTkSuQmCC");
-}
-.annotationHTML.currentLine {
- /* images/currentLine.gif */
- background-image: url("data:image/gif;base64,R0lGODlhEAAQAMQAALxe0bNWzbdZzrlb0KpPx61RybBTy6VLxadNxZGctIeUroyYsG92hHyMqIKRq2l9nmyAoHGDonaIpStXj6q80k1aXf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABYALAAAAAAQABAAAAVCoCWOZGmeKDql5ppOMGXBk/zOoltSNO6XrlXwxIPNYiMGq8SoLC2MaNPygEQkDYdikUg6LQcEoWAICAaA5HPNLoUAADs=");
-}
-
-/* Styles for the overview ruler */
-.annotationOverview {
- cursor: pointer;
- border-radius: 2px;
- left: 2px;
- width: 8px;
-}
-.annotationOverview.task {
- background-color: lightgreen;
- border: 1px solid green;
-}
-.annotationOverview.breakpoint {
- background-color: lightblue;
- border: 1px solid blue;
-}
-.annotationOverview.bookmark {
- background-color: yellow;
- border: 1px solid orange;
-}
-.annotationOverview.error {
- background-color: lightcoral;
- border: 1px solid darkred;
-}
-.annotationOverview.warning {
- background-color: Gold;
- border: 1px solid black;
-}
-.annotationOverview.currentBracket {
- background-color: lightgray;
- border: 1px solid red;
-}
-.annotationOverview.matchingBracket {
- background-color: lightgray;
- border: 1px solid red;
-}
-.annotationOverview.currentLine {
- background-color: #EAF2FE;
- border: 1px solid black;
-}
-
-/* Styles for text range */
-.annotationRange {
- background-repeat: repeat-x;
- background-position: left bottom;
-}
-.annotationRange.task {
- /* images/squiggly_task.png */
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sLDhEoIrb7JmcAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAGUlEQVQI12NggIH/DGdhDCM45z/DfyiBAADgdQjGhI/4DAAAAABJRU5ErkJggg==");
-}
-.annotationRange.breakpoint {
- /* images/squiggly_breakpoint.png */
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sLDhEqHTKradgAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAIklEQVQI11XJMQ0AMAzAMGMafwrFlD19+sUKIJTFo9k+B/kQ+Qr2bIVKOgAAAABJRU5ErkJggg==");
-}
-.annotationRange.bookmark {
- /* images/squiggly_bookmark.png */
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=");
-}
-.annotationRange.error {
- /* images/squiggly_error.png */
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==");
-}
-.annotationRange.warning {
- /* images/squiggly_warning.png */
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=");
-}
-.annotationRange.currentBracket {
-}
-.annotationRange.matchingBracket {
- outline: 1px solid red;
-}
-
-/* Styles for lines of text */
-.annotationLine {
-}
-.annotationLine.currentLine {
- background-color: #EAF2FE;
-}
-
-.token_singleline_comment {
- color: green;
-}
-
-.token_multiline_comment {
- color: green;
-}
-
-.token_doc_comment {
- color: #00008F;
-}
-
-.token_doc_html_markup {
- color: #7F7F9F;
-}
-
-.token_doc_tag {
- color: #7F9FBF;
-}
-
-.token_task_tag {
- color: #7F9FBF;
-}
-
-.token_string {
- color: blue;
-}
-
-.token_keyword {
- color: darkred;
- font-weight: bold;
-}
-
-.token_space {
- /* images/white_space.png */
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAIAAABv85FHAAAABnRSTlMA/wAAAACkwsAdAAAAIUlEQVR4nGP4z8CAC+GUIEXuABhgkTuABEiRw2cmae4EAH05X7xDolNRAAAAAElFTkSuQmCC");
- background-repeat: no-repeat;
- background-position: center center;
-}
-
-.token_tab {
- /* images/white_tab.png */
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAJCAIAAACJ2loDAAAABnRSTlMA/wD/AP83WBt9AAAAMklEQVR4nGP4TwRgoK6i52c3bz5w6zMSA6tJn28d2Lx589nnCAYu63AaSLxJRLoJPwAAeNk0aG4opfMAAAAASUVORK5CYII=");
- background-repeat: no-repeat;
- background-position: left center;
-}
-
-.line_caret {
- background-color: #EAF2FE;
-}
-
-/* Styling for html syntax highlighting */
-.entity-name-tag {
- color: #3f7f7f;
-}
-
-.entity-other-attribute-name {
- color: #7f007f;
-}
-
-.punctuation-definition-comment {
- color: #3f5fbf;
-}
-
-.comment {
- color: #3f5fbf
-}
-
-.string-quoted {
- color: #2a00ff;
- font-style: italic;
-}
-
-.invalid {
- color: red;
- font-weight: bold;
-}
\ No newline at end of file
diff --git a/browser/devtools/sourceeditor/orion/orion.js b/browser/devtools/sourceeditor/orion/orion.js
deleted file mode 100644
index 06ad42195a09..000000000000
--- a/browser/devtools/sourceeditor/orion/orion.js
+++ /dev/null
@@ -1,12303 +0,0 @@
-/*******************************************************************************
- * @license
- * Copyright (c) 2010, 2011 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License v1.0
- * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
- * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
- *
- * Contributors:
- * Felipe Heidrich (IBM Corporation) - initial API and implementation
- * Silenio Quarti (IBM Corporation) - initial API and implementation
- * Mihai Sucan (Mozilla Foundation) - fix for Bug#364214
- */
-
-/*global window */
-
-/**
- * Evaluates the definition function and mixes in the returned module with
- * the module specified by moduleName.
- *
- * This function is intented to by used when RequireJS is not available.
- *
- *
- * @param {String} name The mixin module name.
- * @param {String[]} deps The array of dependency names.
- * @param {Function} callback The definition function.
- */
-if (!window.define) {
- window.define = function(name, deps, callback) {
- var module = this;
- var split = (name || "").split("/"), i, j;
- for (i = 0; i < split.length - 1; i++) {
- module = module[split[i]] = (module[split[i]] || {});
- }
- var depModules = [], depModule;
- for (j = 0; j < deps.length; j++) {
- depModule = this;
- split = deps[j].split("/");
- for (i = 0; i < split.length - 1; i++) {
- depModule = depModule[split[i]] = (depModule[split[i]] || {});
- }
- depModules.push(depModule);
- }
- var newModule = callback.apply(this, depModules);
- for (var p in newModule) {
- if (newModule.hasOwnProperty(p)) {
- module[p] = newModule[p];
- }
- }
- };
-}
-
-/**
- * Require/get the defined modules.
- *
- * This function is intented to by used when RequireJS is not available.
- *
- *
- * @param {String[]|String} deps The array of dependency names. This can also be
- * a string, a single dependency name.
- * @param {Function} [callback] Optional, the callback function to execute when
- * multiple dependencies are required. The callback arguments will have
- * references to each module in the same order as the deps array.
- * @returns {Object|undefined} If the deps parameter is a string, then this
- * function returns the required module definition, otherwise undefined is
- * returned.
- */
-if (!window.require) {
- window.require = function(deps, callback) {
- var depsArr = typeof deps === "string" ? [deps] : deps;
- var depModules = [], depModule, split, i, j;
- for (j = 0; j < depsArr.length; j++) {
- depModule = this;
- split = depsArr[j].split("/");
- for (i = 0; i < split.length - 1; i++) {
- depModule = depModule[split[i]] = (depModule[split[i]] || {});
- }
- depModules.push(depModule);
- }
- if (callback) {
- callback.apply(this, depModules);
- }
- return typeof deps === "string" ? depModules[0] : undefined;
- };
-}/*******************************************************************************
- * Copyright (c) 2010, 2011 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License v1.0
- * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
- * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
- *
- * Contributors:
- * Felipe Heidrich (IBM Corporation) - initial API and implementation
- * Silenio Quarti (IBM Corporation) - initial API and implementation
- ******************************************************************************/
-
-/*global define */
-define("orion/textview/eventTarget", [], function() {
- /**
- * Constructs a new EventTarget object.
- *
- * @class
- * @name orion.textview.EventTarget
- */
- function EventTarget() {
- }
- /**
- * Adds in the event target interface into the specified object.
- *
- * @param {Object} object The object to add in the event target interface.
- */
- EventTarget.addMixin = function(object) {
- var proto = EventTarget.prototype;
- for (var p in proto) {
- if (proto.hasOwnProperty(p)) {
- object[p] = proto[p];
- }
- }
- };
- EventTarget.prototype = /** @lends orion.textview.EventTarget.prototype */ {
- /**
- * Adds an event listener to this event target.
- *
- * @param {String} type The event type.
- * @param {Function|EventListener} listener The function or the EventListener that will be executed when the event happens.
- * @param {Boolean} [useCapture=false] true if the listener should be trigged in the capture phase.
- *
- * @see #removeEventListener
- */
- addEventListener: function(type, listener, useCapture) {
- if (!this._eventTypes) { this._eventTypes = {}; }
- var state = this._eventTypes[type];
- if (!state) {
- state = this._eventTypes[type] = {level: 0, listeners: []};
- }
- var listeners = state.listeners;
- listeners.push({listener: listener, useCapture: useCapture});
- },
- /**
- * Dispatches the given event to the listeners added to this event target.
- * @param {Event} evt The event to dispatch.
- */
- dispatchEvent: function(evt) {
- if (!this._eventTypes) { return; }
- var type = evt.type;
- var state = this._eventTypes[type];
- if (state) {
- var listeners = state.listeners;
- try {
- state.level++;
- if (listeners) {
- for (var i=0, len=listeners.length; i < len; i++) {
- if (listeners[i]) {
- var l = listeners[i].listener;
- if (typeof l === "function") {
- l.call(this, evt);
- } else if (l.handleEvent && typeof l.handleEvent === "function") {
- l.handleEvent(evt);
- }
- }
- }
- }
- } finally {
- state.level--;
- if (state.compact && state.level === 0) {
- for (var j=listeners.length - 1; j >= 0; j--) {
- if (!listeners[j]) {
- listeners.splice(j, 1);
- }
- }
- if (listeners.length === 0) {
- delete this._eventTypes[type];
- }
- state.compact = false;
- }
- }
- }
- },
- /**
- * Returns whether there is a listener for the specified event type.
- *
- * @param {String} type The event type
- *
- * @see #addEventListener
- * @see #removeEventListener
- */
- isListening: function(type) {
- if (!this._eventTypes) { return false; }
- return this._eventTypes[type] !== undefined;
- },
- /**
- * Removes an event listener from the event target.
- *
- * All the parameters must be the same ones used to add the listener.
- *
- *
- * @param {String} type The event type
- * @param {Function|EventListener} listener The function or the EventListener that will be executed when the event happens.
- * @param {Boolean} [useCapture=false] true if the listener should be trigged in the capture phase.
- *
- * @see #addEventListener
- */
- removeEventListener: function(type, listener, useCapture){
- if (!this._eventTypes) { return; }
- var state = this._eventTypes[type];
- if (state) {
- var listeners = state.listeners;
- for (var i=0, len=listeners.length; i < len; i++) {
- var l = listeners[i];
- if (l && l.listener === listener && l.useCapture === useCapture) {
- if (state.level !== 0) {
- listeners[i] = null;
- state.compact = true;
- } else {
- listeners.splice(i, 1);
- }
- break;
- }
- }
- if (listeners.length === 0) {
- delete this._eventTypes[type];
- }
- }
- }
- };
- return {EventTarget: EventTarget};
-});
-/*******************************************************************************
- * @license
- * Copyright (c) 2011 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License v1.0
- * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
- * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
- *
- * Contributors:
- * IBM Corporation - initial API and implementation
- *******************************************************************************/
-/*global define */
-/*jslint browser:true regexp:false*/
-/**
- * @name orion.editor.regex
- * @class Utilities for dealing with regular expressions.
- * @description Utilities for dealing with regular expressions.
- */
-define("orion/editor/regex", [], function() {
- /**
- * @methodOf orion.editor.regex
- * @static
- * @description Escapes regex special characters in the input string.
- * @param {String} str The string to escape.
- * @returns {String} A copy of str with regex special characters escaped.
- */
- function escape(str) {
- return str.replace(/([\\$\^*\/+?\.\(\)|{}\[\]])/g, "\\$&");
- }
-
- /**
- * @methodOf orion.editor.regex
- * @static
- * @description Parses a pattern and flags out of a regex literal string.
- * @param {String} str The string to parse. Should look something like "/ab+c/" or "/ab+c/i".
- * @returns {Object} If str looks like a regex literal, returns an object with properties
- *
- *
pattern
{String}
- *
flags
{String}
- *
otherwise returns null.
- */
- function parse(str) {
- var regexp = /^\s*\/(.+)\/([gim]{0,3})\s*$/.exec(str);
- if (regexp) {
- return {
- pattern : regexp[1],
- flags : regexp[2]
- };
- }
- return null;
- }
-
- return {
- escape: escape,
- parse: parse
- };
-});
-/*******************************************************************************
- * @license
- * Copyright (c) 2010, 2011 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License v1.0
- * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
- * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
- *
- * Contributors:
- * Felipe Heidrich (IBM Corporation) - initial API and implementation
- * Silenio Quarti (IBM Corporation) - initial API and implementation
- ******************************************************************************/
-
-/*global window define */
-
-define("orion/textview/keyBinding", [], function() {
- var isMac = window.navigator.platform.indexOf("Mac") !== -1;
-
- /**
- * Constructs a new key binding with the given key code and modifiers.
- *
- * @param {String|Number} keyCode the key code.
- * @param {Boolean} mod1 the primary modifier (usually Command on Mac and Control on other platforms).
- * @param {Boolean} mod2 the secondary modifier (usually Shift).
- * @param {Boolean} mod3 the third modifier (usually Alt).
- * @param {Boolean} mod4 the fourth modifier (usually Control on the Mac).
- *
- * @class A KeyBinding represents of a key code and a modifier state that can be triggered by the user using the keyboard.
- * @name orion.textview.KeyBinding
- *
- * @property {String|Number} keyCode The key code.
- * @property {Boolean} mod1 The primary modifier (usually Command on Mac and Control on other platforms).
- * @property {Boolean} mod2 The secondary modifier (usually Shift).
- * @property {Boolean} mod3 The third modifier (usually Alt).
- * @property {Boolean} mod4 The fourth modifier (usually Control on the Mac).
- *
- * @see orion.textview.TextView#setKeyBinding
- */
- function KeyBinding (keyCode, mod1, mod2, mod3, mod4) {
- if (typeof(keyCode) === "string") {
- this.keyCode = keyCode.toUpperCase().charCodeAt(0);
- } else {
- this.keyCode = keyCode;
- }
- this.mod1 = mod1 !== undefined && mod1 !== null ? mod1 : false;
- this.mod2 = mod2 !== undefined && mod2 !== null ? mod2 : false;
- this.mod3 = mod3 !== undefined && mod3 !== null ? mod3 : false;
- this.mod4 = mod4 !== undefined && mod4 !== null ? mod4 : false;
- }
- KeyBinding.prototype = /** @lends orion.textview.KeyBinding.prototype */ {
- /**
- * Returns whether this key binding matches the given key event.
- *
- * @param e the key event.
- * @returns {Boolean} true whether the key binding matches the key event.
- */
- match: function (e) {
- if (this.keyCode === e.keyCode) {
- var mod1 = isMac ? e.metaKey : e.ctrlKey;
- if (this.mod1 !== mod1) { return false; }
- if (this.mod2 !== e.shiftKey) { return false; }
- if (this.mod3 !== e.altKey) { return false; }
- if (isMac && this.mod4 !== e.ctrlKey) { return false; }
- return true;
- }
- return false;
- },
- /**
- * Returns whether this key binding is the same as the given parameter.
- *
- * @param {orion.textview.KeyBinding} kb the key binding to compare with.
- * @returns {Boolean} whether or not the parameter and the receiver describe the same key binding.
- */
- equals: function(kb) {
- if (!kb) { return false; }
- if (this.keyCode !== kb.keyCode) { return false; }
- if (this.mod1 !== kb.mod1) { return false; }
- if (this.mod2 !== kb.mod2) { return false; }
- if (this.mod3 !== kb.mod3) { return false; }
- if (this.mod4 !== kb.mod4) { return false; }
- return true;
- }
- };
- return {KeyBinding: KeyBinding};
-});
-/*******************************************************************************
- * @license
- * Copyright (c) 2010, 2011 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License v1.0
- * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
- * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
- *
- * Contributors:
- * Felipe Heidrich (IBM Corporation) - initial API and implementation
- * Silenio Quarti (IBM Corporation) - initial API and implementation
- ******************************************************************************/
-
-/*global define */
-
-define("orion/textview/annotations", ['orion/textview/eventTarget'], function(mEventTarget) {
- /**
- * @class This object represents a decoration attached to a range of text. Annotations are added to a
- * AnnotationModel which is attached to a TextModel.
- *
- * @name orion.textview.Annotation
- *
- * @property {String} type The annotation type (for example, orion.annotation.error).
- * @property {Number} start The start offset of the annotation in the text model.
- * @property {Number} end The end offset of the annotation in the text model.
- * @property {String} html The HTML displayed for the annotation.
- * @property {String} title The text description for the annotation.
- * @property {orion.textview.Style} style The style information for the annotation used in the annotations ruler and tooltips.
- * @property {orion.textview.Style} overviewStyle The style information for the annotation used in the overview ruler.
- * @property {orion.textview.Style} rangeStyle The style information for the annotation used in the text view to decorate a range of text.
- * @property {orion.textview.Style} lineStyle The style information for the annotation used in the text view to decorate a line of text.
- */
- /**
- * Constructs a new folding annotation.
- *
- * @param {orion.textview.ProjectionTextModel} projectionModel The projection text model.
- * @param {String} type The annotation type.
- * @param {Number} start The start offset of the annotation in the text model.
- * @param {Number} end The end offset of the annotation in the text model.
- * @param {String} expandedHTML The HTML displayed for this annotation when it is expanded.
- * @param {orion.textview.Style} expandedStyle The style information for the annotation when it is expanded.
- * @param {String} collapsedHTML The HTML displayed for this annotation when it is collapsed.
- * @param {orion.textview.Style} collapsedStyle The style information for the annotation when it is collapsed.
- *
- * @class This object represents a folding annotation.
- * @name orion.textview.FoldingAnnotation
- */
- function FoldingAnnotation (projectionModel, type, start, end, expandedHTML, expandedStyle, collapsedHTML, collapsedStyle) {
- this.type = type;
- this.start = start;
- this.end = end;
- this._projectionModel = projectionModel;
- this._expandedHTML = this.html = expandedHTML;
- this._expandedStyle = this.style = expandedStyle;
- this._collapsedHTML = collapsedHTML;
- this._collapsedStyle = collapsedStyle;
- this.expanded = true;
- }
-
- FoldingAnnotation.prototype = /** @lends orion.textview.FoldingAnnotation.prototype */ {
- /**
- * Collapses the annotation.
- */
- collapse: function () {
- if (!this.expanded) { return; }
- this.expanded = false;
- this.html = this._collapsedHTML;
- this.style = this._collapsedStyle;
- var projectionModel = this._projectionModel;
- var baseModel = projectionModel.getBaseModel();
- this._projection = {
- start: baseModel.getLineStart(baseModel.getLineAtOffset(this.start) + 1),
- end: baseModel.getLineEnd(baseModel.getLineAtOffset(this.end), true)
- };
- projectionModel.addProjection(this._projection);
- },
- /**
- * Expands the annotation.
- */
- expand: function () {
- if (this.expanded) { return; }
- this.expanded = true;
- this.html = this._expandedHTML;
- this.style = this._expandedStyle;
- this._projectionModel.removeProjection(this._projection);
- }
- };
-
- /**
- * Constructs a new AnnotationTypeList object.
- *
- * @class
- * @name orion.textview.AnnotationTypeList
- */
- function AnnotationTypeList () {
- }
- /**
- * Adds in the annotation type interface into the specified object.
- *
- * @param {Object} object The object to add in the annotation type interface.
- */
- AnnotationTypeList.addMixin = function(object) {
- var proto = AnnotationTypeList.prototype;
- for (var p in proto) {
- if (proto.hasOwnProperty(p)) {
- object[p] = proto[p];
- }
- }
- };
- AnnotationTypeList.prototype = /** @lends orion.textview.AnnotationTypeList.prototype */ {
- /**
- * Adds an annotation type to the receiver.
- *
- * Only annotations of the specified types will be shown by
- * the receiver.
- *
- *
- * @param {Object} type the annotation type to be shown
- *
- * @see #removeAnnotationType
- * @see #isAnnotationTypeVisible
- */
- addAnnotationType: function(type) {
- if (!this._annotationTypes) { this._annotationTypes = []; }
- this._annotationTypes.push(type);
- },
- /**
- * Gets the annotation type priority. The priority is determined by the
- * order the annotation type is added to the receiver. Annotation types
- * added first have higher priority.
- *
- * Returns 0 if the annotation type is not added.
- *
- *
- * @param {Object} type the annotation type
- *
- * @see #addAnnotationType
- * @see #removeAnnotationType
- * @see #isAnnotationTypeVisible
- */
- getAnnotationTypePriority: function(type) {
- if (this._annotationTypes) {
- for (var i = 0; i < this._annotationTypes.length; i++) {
- if (this._annotationTypes[i] === type) {
- return i + 1;
- }
- }
- }
- return 0;
- },
- /**
- * Returns an array of annotations in the specified annotation model for the given range of text sorted by type.
- *
- * @param {orion.textview.AnnotationModel} annotationModel the annotation model.
- * @param {Number} start the start offset of the range.
- * @param {Number} end the end offset of the range.
- * @return {orion.textview.Annotation[]} an annotation array.
- */
- getAnnotationsByType: function(annotationModel, start, end) {
- var iter = annotationModel.getAnnotations(start, end);
- var annotation, annotations = [];
- while (iter.hasNext()) {
- annotation = iter.next();
- var priority = this.getAnnotationTypePriority(annotation.type);
- if (priority === 0) { continue; }
- annotations.push(annotation);
- }
- var self = this;
- annotations.sort(function(a, b) {
- return self.getAnnotationTypePriority(a.type) - self.getAnnotationTypePriority(b.type);
- });
- return annotations;
- },
- /**
- * Returns whether the receiver shows annotations of the specified type.
- *
- * @param {Object} type the annotation type
- * @returns {Boolean} whether the specified annotation type is shown
- *
- * @see #addAnnotationType
- * @see #removeAnnotationType
- */
- isAnnotationTypeVisible: function(type) {
- return this.getAnnotationTypePriority(type) !== 0;
- },
- /**
- * Removes an annotation type from the receiver.
- *
- * @param {Object} type the annotation type to be removed
- *
- * @see #addAnnotationType
- * @see #isAnnotationTypeVisible
- */
- removeAnnotationType: function(type) {
- if (!this._annotationTypes) { return; }
- for (var i = 0; i < this._annotationTypes.length; i++) {
- if (this._annotationTypes[i] === type) {
- this._annotationTypes.splice(i, 1);
- break;
- }
- }
- }
- };
-
- /**
- * Constructs an annotation model.
- *
- * @param {textModel} textModel The text model.
- *
- * @class This object manages annotations for a TextModel.
- *
- * @name orion.textview.AnnotationIterator
- *
- * @property {Function} hasNext Determines whether there are more annotations in the iterator.
- * @property {Function} next Returns the next annotation in the iterator.
- */
- /**
- * Returns an iterator of annotations for the given range of text.
- *
- * @param {Number} start the start offset of the range.
- * @param {Number} end the end offset of the range.
- * @return {orion.textview.AnnotationIterator} an annotation iterartor.
- */
- getAnnotations: function(start, end) {
- var annotations = this._annotations, current;
- //TODO binary search does not work for range intersection when there are overlaping ranges, need interval search tree for this
- var i = 0;
- var skip = function() {
- while (i < annotations.length) {
- var a = annotations[i++];
- if ((start === a.start) || (start > a.start ? start < a.end : a.start < end)) {
- return a;
- }
- if (a.start >= end) {
- break;
- }
- }
- return null;
- };
- current = skip();
- return {
- next: function() {
- var result = current;
- if (result) { current = skip(); }
- return result;
- },
- hasNext: function() {
- return current !== null;
- }
- };
- },
- /**
- * Notifies the annotation model that the given annotation has been modified.
- *
The annotation model listeners are notified of this change.
- *
- * @param {orion.textview.Annotation} annotation the modified annotation.
- *
- * @see #addAnnotation
- */
- modifyAnnotation: function(annotation) {
- if (!annotation) { return; }
- var index = this._getAnnotationIndex(annotation);
- if (index < 0) { return; }
- var e = {
- type: "Changed",
- added: [],
- removed: [],
- changed: [annotation]
- };
- this.onChanged(e);
- },
- /**
- * Notifies all listeners that the annotation model has changed.
- *
- * @param {orion.textview.Annotation[]} added The list of annotation being added to the model.
- * @param {orion.textview.Annotation[]} changed The list of annotation modified in the model.
- * @param {orion.textview.Annotation[]} removed The list of annotation being removed from the model.
- * @param {ModelChangedEvent} textModelChangedEvent the text model changed event that trigger this change, can be null if the change was trigger by a method call (for example, {@link #addAnnotation}).
- */
- onChanged: function(e) {
- return this.dispatchEvent(e);
- },
- /**
- * Removes all annotations of the given type. All annotations
- * are removed if the type is not specified.
- *
The annotation model listeners are notified of this change. Only one changed event is generated.
- *
- * @param {Object} type the type of annotations to be removed.
- *
- * @see #removeAnnotation
- */
- removeAnnotations: function(type) {
- var annotations = this._annotations;
- var removed, i;
- if (type) {
- removed = [];
- for (i = annotations.length - 1; i >= 0; i--) {
- var annotation = annotations[i];
- if (annotation.type === type) {
- annotations.splice(i, 1);
- }
- removed.splice(0, 0, annotation);
- }
- } else {
- removed = annotations;
- annotations = [];
- }
- var e = {
- type: "Changed",
- removed: removed,
- added: [],
- changed: []
- };
- this.onChanged(e);
- },
- /**
- * Removes an annotation from the annotation model.
- *
The annotation model listeners are notified of this change.
- *
- * @param {orion.textview.Annotation} annotation the annotation to be removed.
- *
- * @see #addAnnotation
- */
- removeAnnotation: function(annotation) {
- if (!annotation) { return; }
- var index = this._getAnnotationIndex(annotation);
- if (index < 0) { return; }
- var e = {
- type: "Changed",
- removed: this._annotations.splice(index, 1),
- added: [],
- changed: []
- };
- this.onChanged(e);
- },
- /**
- * Removes and adds the specifed annotations to the annotation model.
- *
The annotation model listeners are notified of this change. Only one changed event is generated.
- *
- * @param {orion.textview.Annotation} remove the annotations to be removed.
- * @param {orion.textview.Annotation} add the annotations to be added.
- *
- * @see #addAnnotation
- * @see #removeAnnotation
- */
- replaceAnnotations: function(remove, add) {
- var annotations = this._annotations, i, index, annotation, removed = [];
- if (remove) {
- for (i = remove.length - 1; i >= 0; i--) {
- annotation = remove[i];
- index = this._getAnnotationIndex(annotation);
- if (index < 0) { continue; }
- annotations.splice(index, 1);
- removed.splice(0, 0, annotation);
- }
- }
- if (!add) { add = []; }
- for (i = 0; i < add.length; i++) {
- annotation = add[i];
- index = this._binarySearch(annotations, annotation.start);
- annotations.splice(index, 0, annotation);
- }
- var e = {
- type: "Changed",
- removed: removed,
- added: add,
- changed: []
- };
- this.onChanged(e);
- },
- /**
- * Sets the text model of the annotation model. The annotation
- * model listens for changes in the text model to update and remove
- * annotations that are affected by the change.
- *
- * @param {orion.textview.TextModel} textModel the text model.
- *
- * @see #getTextModel
- */
- setTextModel: function(textModel) {
- if (this._model) {
- this._model.removeEventListener("Changed", this._listener.onChanged);
- }
- this._model = textModel;
- if (this._model) {
- this._model.addEventListener("Changed", this._listener.onChanged);
- }
- },
- /** @ignore */
- _binarySearch: function (array, offset) {
- var high = array.length, low = -1, index;
- while (high - low > 1) {
- index = Math.floor((high + low) / 2);
- if (offset <= array[index].start) {
- high = index;
- } else {
- low = index;
- }
- }
- return high;
- },
- /** @ignore */
- _getAnnotationIndex: function(annotation) {
- var annotations = this._annotations;
- var index = this._binarySearch(annotations, annotation.start);
- while (index < annotations.length && annotations[index].start === annotation.start) {
- if (annotations[index] === annotation) {
- return index;
- }
- index++;
- }
- return -1;
- },
- /** @ignore */
- _onChanged: function(modelChangedEvent) {
- var start = modelChangedEvent.start;
- var addedCharCount = modelChangedEvent.addedCharCount;
- var removedCharCount = modelChangedEvent.removedCharCount;
- var annotations = this._annotations, end = start + removedCharCount;
- //TODO binary search does not work for range intersection when there are overlaping ranges, need interval search tree for this
- var startIndex = 0;
- if (!(0 <= startIndex && startIndex < annotations.length)) { return; }
- var e = {
- type: "Changed",
- added: [],
- removed: [],
- changed: [],
- textModelChangedEvent: modelChangedEvent
- };
- var changeCount = addedCharCount - removedCharCount, i;
- for (i = startIndex; i < annotations.length; i++) {
- var annotation = annotations[i];
- if (annotation.start >= end) {
- annotation.start += changeCount;
- annotation.end += changeCount;
- e.changed.push(annotation);
- } else if (annotation.end <= start) {
- //nothing
- } else if (annotation.start < start && end < annotation.end) {
- annotation.end += changeCount;
- e.changed.push(annotation);
- } else {
- annotations.splice(i, 1);
- e.removed.push(annotation);
- i--;
- }
- }
- if (e.added.length > 0 || e.removed.length > 0 || e.changed.length > 0) {
- this.onChanged(e);
- }
- }
- };
- mEventTarget.EventTarget.addMixin(AnnotationModel.prototype);
-
- /**
- * Constructs a new styler for annotations.
- *
- * @param {orion.textview.TextView} view The styler view.
- * @param {orion.textview.AnnotationModel} view The styler annotation model.
- *
- * @class This object represents a styler for annotation attached to a text view.
- * @name orion.textview.AnnotationStyler
- * @borrows orion.textview.AnnotationTypeList#addAnnotationType as #addAnnotationType
- * @borrows orion.textview.AnnotationTypeList#getAnnotationTypePriority as #getAnnotationTypePriority
- * @borrows orion.textview.AnnotationTypeList#getAnnotationsByType as #getAnnotationsByType
- * @borrows orion.textview.AnnotationTypeList#isAnnotationTypeVisible as #isAnnotationTypeVisible
- * @borrows orion.textview.AnnotationTypeList#removeAnnotationType as #removeAnnotationType
- */
- function AnnotationStyler (view, annotationModel) {
- this._view = view;
- this._annotationModel = annotationModel;
- var self = this;
- this._listener = {
- onDestroy: function(e) {
- self._onDestroy(e);
- },
- onLineStyle: function(e) {
- self._onLineStyle(e);
- },
- onChanged: function(e) {
- self._onAnnotationModelChanged(e);
- }
- };
- view.addEventListener("Destroy", this._listener.onDestroy);
- view.addEventListener("LineStyle", this._listener.onLineStyle);
- annotationModel.addEventListener("Changed", this._listener.onChanged);
- }
- AnnotationStyler.prototype = /** @lends orion.textview.AnnotationStyler.prototype */ {
- /**
- * Destroys the styler.
- *
- * Removes all listeners added by this styler.
- *
- */
- destroy: function() {
- var view = this._view;
- if (view) {
- view.removeEventListener("Destroy", this._listener.onDestroy);
- view.removeEventListener("LineStyle", this._listener.onLineStyle);
- this.view = null;
- }
- var annotationModel = this._annotationModel;
- if (annotationModel) {
- annotationModel.removeEventListener("Changed", this._listener.onChanged);
- annotationModel = null;
- }
- },
- _mergeStyle: function(result, style) {
- if (style) {
- if (!result) { result = {}; }
- if (result.styleClass && style.styleClass && result.styleClass !== style.styleClass) {
- result.styleClass += " " + style.styleClass;
- } else {
- result.styleClass = style.styleClass;
- }
- var prop;
- if (style.style) {
- if (!result.style) { result.style = {}; }
- for (prop in style.style) {
- if (!result.style[prop]) {
- result.style[prop] = style.style[prop];
- }
- }
- }
- if (style.attributes) {
- if (!result.attributes) { result.attributes = {}; }
- for (prop in style.attributes) {
- if (!result.attributes[prop]) {
- result.attributes[prop] = style.attributes[prop];
- }
- }
- }
- }
- return result;
- },
- _mergeStyleRanges: function(ranges, styleRange) {
- if (!ranges) { return; }
- for (var i=0; i= range.end) { continue; }
- var mergedStyle = this._mergeStyle({}, range.style);
- mergedStyle = this._mergeStyle(mergedStyle, styleRange.style);
- if (styleRange.start <= range.start && styleRange.end >= range.end) {
- ranges[i] = {start: range.start, end: range.end, style: mergedStyle};
- } else if (styleRange.start > range.start && styleRange.end < range.end) {
- ranges.splice(i, 1,
- {start: range.start, end: styleRange.start, style: range.style},
- {start: styleRange.start, end: styleRange.end, style: mergedStyle},
- {start: styleRange.end, end: range.end, style: range.style});
- i += 2;
- } else if (styleRange.start > range.start) {
- ranges.splice(i, 1,
- {start: range.start, end: styleRange.start, style: range.style},
- {start: styleRange.start, end: range.end, style: mergedStyle});
- i += 1;
- } else if (styleRange.end < range.end) {
- ranges.splice(i, 1,
- {start: range.start, end: styleRange.end, style: mergedStyle},
- {start: styleRange.end, end: range.end, style: range.style});
- i += 1;
- }
- }
- },
- _onAnnotationModelChanged: function(e) {
- if (e.textModelChangedEvent) {
- return;
- }
- var view = this._view;
- if (!view) { return; }
- var self = this;
- var model = view.getModel();
- function redraw(changes) {
- for (var i = 0; i < changes.length; i++) {
- if (!self.isAnnotationTypeVisible(changes[i].type)) { continue; }
- var start = changes[i].start;
- var end = changes[i].end;
- if (model.getBaseModel) {
- start = model.mapOffset(start, true);
- end = model.mapOffset(end, true);
- }
- if (start !== -1 && end !== -1) {
- view.redrawRange(start, end);
- }
- }
- }
- redraw(e.added);
- redraw(e.removed);
- redraw(e.changed);
- },
- _onDestroy: function(e) {
- this.destroy();
- },
- _onLineStyle: function (e) {
- var annotationModel = this._annotationModel;
- var viewModel = this._view.getModel();
- var baseModel = annotationModel.getTextModel();
- var start = e.lineStart;
- var end = e.lineStart + e.lineText.length;
- if (baseModel !== viewModel) {
- start = viewModel.mapOffset(start);
- end = viewModel.mapOffset(end);
- }
- var annotations = annotationModel.getAnnotations(start, end);
- while (annotations.hasNext()) {
- var annotation = annotations.next();
- if (!this.isAnnotationTypeVisible(annotation.type)) { continue; }
- if (annotation.rangeStyle) {
- var annotationStart = annotation.start;
- var annotationEnd = annotation.end;
- if (baseModel !== viewModel) {
- annotationStart = viewModel.mapOffset(annotationStart, true);
- annotationEnd = viewModel.mapOffset(annotationEnd, true);
- }
- this._mergeStyleRanges(e.ranges, {start: annotationStart, end: annotationEnd, style: annotation.rangeStyle});
- }
- if (annotation.lineStyle) {
- e.style = this._mergeStyle({}, e.style);
- e.style = this._mergeStyle(e.style, annotation.lineStyle);
- }
- }
- }
- };
- AnnotationTypeList.addMixin(AnnotationStyler.prototype);
-
- return {
- FoldingAnnotation: FoldingAnnotation,
- AnnotationTypeList: AnnotationTypeList,
- AnnotationModel: AnnotationModel,
- AnnotationStyler: AnnotationStyler
- };
-});
-/*******************************************************************************
- * @license
- * Copyright (c) 2010, 2011 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License v1.0
- * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
- * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
- *
- * Contributors: IBM Corporation - initial API and implementation
- ******************************************************************************/
-
-/*global define setTimeout clearTimeout setInterval clearInterval Node */
-
-define("orion/textview/rulers", ['orion/textview/annotations', 'orion/textview/tooltip'], function(mAnnotations, mTooltip) {
-
- /**
- * Constructs a new ruler.
- *
- * The default implementation does not implement all the methods in the interface
- * and is useful only for objects implementing rulers.
- *
- *
- * @param {orion.textview.AnnotationModel} annotationModel the annotation model for the ruler.
- * @param {String} [rulerLocation="left"] the location for the ruler.
- * @param {String} [rulerOverview="page"] the overview for the ruler.
- * @param {orion.textview.Style} [rulerStyle] the style for the ruler.
- *
- * @class This interface represents a ruler for the text view.
- *
- * A Ruler is a graphical element that is placed either on the left or on the right side of
- * the view. It can be used to provide the view with per line decoration such as line numbering,
- * bookmarks, breakpoints, folding disclosures, etc.
- *
- * There are two types of rulers: page and document. A page ruler only shows the content for the lines that are
- * visible, while a document ruler always shows the whole content.
- *
- * @name orion.textview.Ruler
- * @borrows orion.textview.AnnotationTypeList#addAnnotationType as #addAnnotationType
- * @borrows orion.textview.AnnotationTypeList#getAnnotationTypePriority as #getAnnotationTypePriority
- * @borrows orion.textview.AnnotationTypeList#getAnnotationsByType as #getAnnotationsByType
- * @borrows orion.textview.AnnotationTypeList#isAnnotationTypeVisible as #isAnnotationTypeVisible
- * @borrows orion.textview.AnnotationTypeList#removeAnnotationType as #removeAnnotationType
- */
- function Ruler (annotationModel, rulerLocation, rulerOverview, rulerStyle) {
- this._location = rulerLocation || "left";
- this._overview = rulerOverview || "page";
- this._rulerStyle = rulerStyle;
- this._view = null;
- var self = this;
- this._listener = {
- onTextModelChanged: function(e) {
- self._onTextModelChanged(e);
- },
- onAnnotationModelChanged: function(e) {
- self._onAnnotationModelChanged(e);
- }
- };
- this.setAnnotationModel(annotationModel);
- }
- Ruler.prototype = /** @lends orion.textview.Ruler.prototype */ {
- /**
- * Returns the annotations for a given line range merging multiple
- * annotations when necessary.
- *
- * This method is called by the text view when the ruler is redrawn.
- *
- *
- * @param {Number} startLine the start line index
- * @param {Number} endLine the end line index
- * @return {orion.textview.Annotation[]} the annotations for the line range. The array might be sparse.
- */
- getAnnotations: function(startLine, endLine) {
- var annotationModel = this._annotationModel;
- if (!annotationModel) { return []; }
- var model = this._view.getModel();
- var start = model.getLineStart(startLine);
- var end = model.getLineEnd(endLine - 1);
- var baseModel = model;
- if (model.getBaseModel) {
- baseModel = model.getBaseModel();
- start = model.mapOffset(start);
- end = model.mapOffset(end);
- }
- var result = [];
- var annotations = this.getAnnotationsByType(annotationModel, start, end);
- for (var i = 0; i < annotations.length; i++) {
- var annotation = annotations[i];
- var annotationLineStart = baseModel.getLineAtOffset(annotation.start);
- var annotationLineEnd = baseModel.getLineAtOffset(Math.max(annotation.start, annotation.end - 1));
- for (var lineIndex = annotationLineStart; lineIndex<=annotationLineEnd; lineIndex++) {
- var visualLineIndex = lineIndex;
- if (model !== baseModel) {
- var ls = baseModel.getLineStart(lineIndex);
- ls = model.mapOffset(ls, true);
- if (ls === -1) { continue; }
- visualLineIndex = model.getLineAtOffset(ls);
- }
- if (!(startLine <= visualLineIndex && visualLineIndex < endLine)) { continue; }
- var rulerAnnotation = this._mergeAnnotation(result[visualLineIndex], annotation, lineIndex - annotationLineStart, annotationLineEnd - annotationLineStart + 1);
- if (rulerAnnotation) {
- result[visualLineIndex] = rulerAnnotation;
- }
- }
- }
- if (!this._multiAnnotation && this._multiAnnotationOverlay) {
- for (var k in result) {
- if (result[k]._multiple) {
- result[k].html = result[k].html + this._multiAnnotationOverlay.html;
- }
- }
- }
- return result;
- },
- /**
- * Returns the annotation model.
- *
- * @returns {orion.textview.AnnotationModel} the ruler annotation model.
- *
- * @see #setAnnotationModel
- */
- getAnnotationModel: function() {
- return this._annotationModel;
- },
- /**
- * Returns the ruler location.
- *
- * @returns {String} the ruler location, which is either "left" or "right".
- *
- * @see #getOverview
- */
- getLocation: function() {
- return this._location;
- },
- /**
- * Returns the ruler overview type.
- *
- * @returns {String} the overview type, which is either "page" or "document".
- *
- * @see #getLocation
- */
- getOverview: function() {
- return this._overview;
- },
- /**
- * Returns the style information for the ruler.
- *
- * @returns {orion.textview.Style} the style information.
- */
- getRulerStyle: function() {
- return this._rulerStyle;
- },
- /**
- * Returns the widest annotation which determines the width of the ruler.
- *
- * If the ruler does not have a fixed width it should provide the widest
- * annotation to avoid the ruler from changing size as the view scrolls.
- *
- *
- * This method is called by the text view when the ruler is redrawn.
- *
- *
- * @returns {orion.textview.Annotation} the widest annotation.
- *
- * @see #getAnnotations
- */
- getWidestAnnotation: function() {
- return null;
- },
- /**
- * Sets the annotation model for the ruler.
- *
- * @param {orion.textview.AnnotationModel} annotationModel the annotation model.
- *
- * @see #getAnnotationModel
- */
- setAnnotationModel: function (annotationModel) {
- if (this._annotationModel) {
- this._annotationModel.removEventListener("Changed", this._listener.onAnnotationModelChanged);
- }
- this._annotationModel = annotationModel;
- if (this._annotationModel) {
- this._annotationModel.addEventListener("Changed", this._listener.onAnnotationModelChanged);
- }
- },
- /**
- * Sets the annotation that is displayed when a given line contains multiple
- * annotations. This annotation is used when there are different types of
- * annotations in a given line.
- *
- * @param {orion.textview.Annotation} annotation the annotation for lines with multiple annotations.
- *
- * @see #setMultiAnnotationOverlay
- */
- setMultiAnnotation: function(annotation) {
- this._multiAnnotation = annotation;
- },
- /**
- * Sets the annotation that overlays a line with multiple annotations. This annotation is displayed on
- * top of the computed annotation for a given line when there are multiple annotations of the same type
- * in the line. It is also used when the multiple annotation is not set.
- *
- * @param {orion.textview.Annotation} annotation the annotation overlay for lines with multiple annotations.
- *
- * @see #setMultiAnnotation
- */
- setMultiAnnotationOverlay: function(annotation) {
- this._multiAnnotationOverlay = annotation;
- },
- /**
- * Sets the view for the ruler.
- *
- * This method is called by the text view when the ruler
- * is added to the view.
- *
- *
- * @param {orion.textview.TextView} view the text view.
- */
- setView: function (view) {
- if (this._onTextModelChanged && this._view) {
- this._view.removeEventListener("ModelChanged", this._listener.onTextModelChanged);
- }
- this._view = view;
- if (this._onTextModelChanged && this._view) {
- this._view.addEventListener("ModelChanged", this._listener.onTextModelChanged);
- }
- },
- /**
- * This event is sent when the user clicks a line annotation.
- *
- * @event
- * @param {Number} lineIndex the line index of the annotation under the pointer.
- * @param {DOMEvent} e the click event.
- */
- onClick: function(lineIndex, e) {
- },
- /**
- * This event is sent when the user double clicks a line annotation.
- *
- * @event
- * @param {Number} lineIndex the line index of the annotation under the pointer.
- * @param {DOMEvent} e the double click event.
- */
- onDblClick: function(lineIndex, e) {
- },
- /**
- * This event is sent when the user moves the mouse over a line annotation.
- *
- * @event
- * @param {Number} lineIndex the line index of the annotation under the pointer.
- * @param {DOMEvent} e the mouse move event.
- */
- onMouseMove: function(lineIndex, e) {
- var tooltip = mTooltip.Tooltip.getTooltip(this._view);
- if (!tooltip) { return; }
- if (tooltip.isVisible() && this._tooltipLineIndex === lineIndex) { return; }
- this._tooltipLineIndex = lineIndex;
- var self = this;
- tooltip.setTarget({
- y: e.clientY,
- getTooltipInfo: function() {
- return self._getTooltipInfo(self._tooltipLineIndex, this.y);
- }
- });
- },
- /**
- * This event is sent when the mouse pointer enters a line annotation.
- *
- * @event
- * @param {Number} lineIndex the line index of the annotation under the pointer.
- * @param {DOMEvent} e the mouse over event.
- */
- onMouseOver: function(lineIndex, e) {
- this.onMouseMove(lineIndex, e);
- },
- /**
- * This event is sent when the mouse pointer exits a line annotation.
- *
- * @event
- * @param {Number} lineIndex the line index of the annotation under the pointer.
- * @param {DOMEvent} e the mouse out event.
- */
- onMouseOut: function(lineIndex, e) {
- var tooltip = mTooltip.Tooltip.getTooltip(this._view);
- if (!tooltip) { return; }
- tooltip.setTarget(null);
- },
- /** @ignore */
- _getTooltipInfo: function(lineIndex, y) {
- if (lineIndex === undefined) { return; }
- var view = this._view;
- var model = view.getModel();
- var annotationModel = this._annotationModel;
- var annotations = [];
- if (annotationModel) {
- var start = model.getLineStart(lineIndex);
- var end = model.getLineEnd(lineIndex);
- if (model.getBaseModel) {
- start = model.mapOffset(start);
- end = model.mapOffset(end);
- }
- annotations = this.getAnnotationsByType(annotationModel, start, end);
- }
- var contents = this._getTooltipContents(lineIndex, annotations);
- if (!contents) { return null; }
- var info = {
- contents: contents,
- anchor: this.getLocation()
- };
- var rect = view.getClientArea();
- if (this.getOverview() === "document") {
- rect.y = view.convert({y: y}, "view", "document").y;
- } else {
- rect.y = view.getLocationAtOffset(model.getLineStart(lineIndex)).y;
- }
- view.convert(rect, "document", "page");
- info.x = rect.x;
- info.y = rect.y;
- if (info.anchor === "right") {
- info.x += rect.width;
- }
- info.maxWidth = rect.width;
- info.maxHeight = rect.height - (rect.y - view._parent.getBoundingClientRect().top);
- return info;
- },
- /** @ignore */
- _getTooltipContents: function(lineIndex, annotations) {
- return annotations;
- },
- /** @ignore */
- _onAnnotationModelChanged: function(e) {
- var view = this._view;
- if (!view) { return; }
- var model = view.getModel(), self = this;
- var lineCount = model.getLineCount();
- if (e.textModelChangedEvent) {
- var start = e.textModelChangedEvent.start;
- if (model.getBaseModel) { start = model.mapOffset(start, true); }
- var startLine = model.getLineAtOffset(start);
- view.redrawLines(startLine, lineCount, self);
- return;
- }
- function redraw(changes) {
- for (var i = 0; i < changes.length; i++) {
- if (!self.isAnnotationTypeVisible(changes[i].type)) { continue; }
- var start = changes[i].start;
- var end = changes[i].end;
- if (model.getBaseModel) {
- start = model.mapOffset(start, true);
- end = model.mapOffset(end, true);
- }
- if (start !== -1 && end !== -1) {
- view.redrawLines(model.getLineAtOffset(start), model.getLineAtOffset(Math.max(start, end - 1)) + 1, self);
- }
- }
- }
- redraw(e.added);
- redraw(e.removed);
- redraw(e.changed);
- },
- /** @ignore */
- _mergeAnnotation: function(result, annotation, annotationLineIndex, annotationLineCount) {
- if (!result) { result = {}; }
- if (annotationLineIndex === 0) {
- if (result.html && annotation.html) {
- if (annotation.html !== result.html) {
- if (!result._multiple && this._multiAnnotation) {
- result.html = this._multiAnnotation.html;
- }
- }
- result._multiple = true;
- } else {
- result.html = annotation.html;
- }
- }
- result.style = this._mergeStyle(result.style, annotation.style);
- return result;
- },
- /** @ignore */
- _mergeStyle: function(result, style) {
- if (style) {
- if (!result) { result = {}; }
- if (result.styleClass && style.styleClass && result.styleClass !== style.styleClass) {
- result.styleClass += " " + style.styleClass;
- } else {
- result.styleClass = style.styleClass;
- }
- var prop;
- if (style.style) {
- if (!result.style) { result.style = {}; }
- for (prop in style.style) {
- if (!result.style[prop]) {
- result.style[prop] = style.style[prop];
- }
- }
- }
- if (style.attributes) {
- if (!result.attributes) { result.attributes = {}; }
- for (prop in style.attributes) {
- if (!result.attributes[prop]) {
- result.attributes[prop] = style.attributes[prop];
- }
- }
- }
- }
- return result;
- }
- };
- mAnnotations.AnnotationTypeList.addMixin(Ruler.prototype);
-
- /**
- * Constructs a new line numbering ruler.
- *
- * @param {orion.textview.AnnotationModel} annotationModel the annotation model for the ruler.
- * @param {String} [rulerLocation="left"] the location for the ruler.
- * @param {orion.textview.Style} [rulerStyle=undefined] the style for the ruler.
- * @param {orion.textview.Style} [oddStyle={style: {backgroundColor: "white"}] the style for lines with odd line index.
- * @param {orion.textview.Style} [evenStyle={backgroundColor: "white"}] the style for lines with even line index.
- *
- * @augments orion.textview.Ruler
- * @class This objects implements a line numbering ruler.
- *
- *
See:
- * {@link orion.textview.Ruler}
- *
- * @name orion.textview.LineNumberRuler
- */
- function LineNumberRuler (annotationModel, rulerLocation, rulerStyle, oddStyle, evenStyle) {
- Ruler.call(this, annotationModel, rulerLocation, "page", rulerStyle);
- this._oddStyle = oddStyle || {style: {backgroundColor: "white"}};
- this._evenStyle = evenStyle || {style: {backgroundColor: "white"}};
- this._numOfDigits = 0;
- }
- LineNumberRuler.prototype = new Ruler();
- /** @ignore */
- LineNumberRuler.prototype.getAnnotations = function(startLine, endLine) {
- var result = Ruler.prototype.getAnnotations.call(this, startLine, endLine);
- var model = this._view.getModel();
- for (var lineIndex = startLine; lineIndex < endLine; lineIndex++) {
- var style = lineIndex & 1 ? this._oddStyle : this._evenStyle;
- var mapLine = lineIndex;
- if (model.getBaseModel) {
- var lineStart = model.getLineStart(mapLine);
- mapLine = model.getBaseModel().getLineAtOffset(model.mapOffset(lineStart));
- }
- if (!result[lineIndex]) { result[lineIndex] = {}; }
- result[lineIndex].html = (mapLine + 1) + "";
- if (!result[lineIndex].style) { result[lineIndex].style = style; }
- }
- return result;
- };
- /** @ignore */
- LineNumberRuler.prototype.getWidestAnnotation = function() {
- var lineCount = this._view.getModel().getLineCount();
- return this.getAnnotations(lineCount - 1, lineCount)[lineCount - 1];
- };
- /** @ignore */
- LineNumberRuler.prototype._onTextModelChanged = function(e) {
- var start = e.start;
- var model = this._view.getModel();
- var lineCount = model.getBaseModel ? model.getBaseModel().getLineCount() : model.getLineCount();
- var numOfDigits = (lineCount+"").length;
- if (this._numOfDigits !== numOfDigits) {
- this._numOfDigits = numOfDigits;
- var startLine = model.getLineAtOffset(start);
- this._view.redrawLines(startLine, model.getLineCount(), this);
- }
- };
-
- /**
- * @class This is class represents an annotation for the AnnotationRuler.
- *
- *
- * @name orion.textview.Annotation
- *
- * @property {String} [html=""] The html content for the annotation, typically contains an image.
- * @property {orion.textview.Style} [style] the style for the annotation.
- * @property {orion.textview.Style} [overviewStyle] the style for the annotation in the overview ruler.
- */
- /**
- * Constructs a new annotation ruler.
- *
- * @param {orion.textview.AnnotationModel} annotationModel the annotation model for the ruler.
- * @param {String} [rulerLocation="left"] the location for the ruler.
- * @param {orion.textview.Style} [rulerStyle=undefined] the style for the ruler.
- * @param {orion.textview.Annotation} [defaultAnnotation] the default annotation.
- *
- * @augments orion.textview.Ruler
- * @class This objects implements an annotation ruler.
- *
- *
- * @name orion.textview.AnnotationRuler
- */
- function AnnotationRuler (annotationModel, rulerLocation, rulerStyle) {
- Ruler.call(this, annotationModel, rulerLocation, "page", rulerStyle);
- }
- AnnotationRuler.prototype = new Ruler();
-
- /**
- * Constructs a new overview ruler.
- *
- * The overview ruler is used in conjunction with a AnnotationRuler, for each annotation in the
- * AnnotationRuler this ruler displays a mark in the overview. Clicking on the mark causes the
- * view to scroll to the annotated line.
- *
- *
- * @param {orion.textview.AnnotationModel} annotationModel the annotation model for the ruler.
- * @param {String} [rulerLocation="left"] the location for the ruler.
- * @param {orion.textview.Style} [rulerStyle=undefined] the style for the ruler.
- *
- * @augments orion.textview.Ruler
- * @class This objects implements an overview ruler.
- *
- *
- * @name orion.textview.OverviewRuler
- */
- function FoldingRuler (annotationModel, rulerLocation, rulerStyle) {
- AnnotationRuler.call(this, annotationModel, rulerLocation, rulerStyle);
- }
- FoldingRuler.prototype = new AnnotationRuler();
-
- /** @ignore */
- FoldingRuler.prototype.onClick = function(lineIndex, e) {
- if (lineIndex === undefined) { return; }
- var annotationModel = this._annotationModel;
- if (!annotationModel) { return; }
- var view = this._view;
- var model = view.getModel();
- var start = model.getLineStart(lineIndex);
- var end = model.getLineEnd(lineIndex, true);
- if (model.getBaseModel) {
- start = model.mapOffset(start);
- end = model.mapOffset(end);
- }
- var annotation, iter = annotationModel.getAnnotations(start, end);
- while (!annotation && iter.hasNext()) {
- var a = iter.next();
- if (!this.isAnnotationTypeVisible(a.type)) { continue; }
- annotation = a;
- }
- if (annotation) {
- var tooltip = mTooltip.Tooltip.getTooltip(this._view);
- if (tooltip) {
- tooltip.setTarget(null);
- }
- if (annotation.expanded) {
- annotation.collapse();
- } else {
- annotation.expand();
- }
- this._annotationModel.modifyAnnotation(annotation);
- }
- };
- /** @ignore */
- FoldingRuler.prototype._getTooltipContents = function(lineIndex, annotations) {
- if (annotations.length === 1) {
- if (annotations[0].expanded) {
- return null;
- }
- }
- return AnnotationRuler.prototype._getTooltipContents.call(this, lineIndex, annotations);
- };
- /** @ignore */
- FoldingRuler.prototype._onAnnotationModelChanged = function(e) {
- if (e.textModelChangedEvent) {
- AnnotationRuler.prototype._onAnnotationModelChanged.call(this, e);
- return;
- }
- var view = this._view;
- if (!view) { return; }
- var model = view.getModel(), self = this, i;
- var lineCount = model.getLineCount(), lineIndex = lineCount;
- function redraw(changes) {
- for (i = 0; i < changes.length; i++) {
- if (!self.isAnnotationTypeVisible(changes[i].type)) { continue; }
- var start = changes[i].start;
- if (model.getBaseModel) {
- start = model.mapOffset(start, true);
- }
- if (start !== -1) {
- lineIndex = Math.min(lineIndex, model.getLineAtOffset(start));
- }
- }
- }
- redraw(e.added);
- redraw(e.removed);
- redraw(e.changed);
- var rulers = view.getRulers();
- for (i = 0; i < rulers.length; i++) {
- view.redrawLines(lineIndex, lineCount, rulers[i]);
- }
- };
-
- return {
- Ruler: Ruler,
- AnnotationRuler: AnnotationRuler,
- LineNumberRuler: LineNumberRuler,
- OverviewRuler: OverviewRuler,
- FoldingRuler: FoldingRuler
- };
-});
-/*******************************************************************************
- * @license
- * Copyright (c) 2010, 2011 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License v1.0
- * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
- * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
- *
- * Contributors: IBM Corporation - initial API and implementation
- ******************************************************************************/
-
-/*global define */
-
-define("orion/textview/undoStack", [], function() {
-
- /**
- * Constructs a new Change object.
- *
- * @class
- * @name orion.textview.Change
- * @private
- */
- function Change(offset, text, previousText) {
- this.offset = offset;
- this.text = text;
- this.previousText = previousText;
- }
- Change.prototype = {
- /** @ignore */
- undo: function (view, select) {
- this._doUndoRedo(this.offset, this.previousText, this.text, view, select);
- },
- /** @ignore */
- redo: function (view, select) {
- this._doUndoRedo(this.offset, this.text, this.previousText, view, select);
- },
- _doUndoRedo: function(offset, text, previousText, view, select) {
- var model = view.getModel();
- /*
- * TODO UndoStack should be changing the text in the base model.
- * This is code needs to change when modifications in the base
- * model are supported properly by the projection model.
- */
- if (model.mapOffset && view.annotationModel) {
- var mapOffset = model.mapOffset(offset, true);
- if (mapOffset < 0) {
- var annotationModel = view.annotationModel;
- var iter = annotationModel.getAnnotations(offset, offset + 1);
- while (iter.hasNext()) {
- var annotation = iter.next();
- if (annotation.type === "orion.annotation.folding") {
- annotation.expand();
- mapOffset = model.mapOffset(offset, true);
- break;
- }
- }
- }
- if (mapOffset < 0) { return; }
- offset = mapOffset;
- }
- view.setText(text, offset, offset + previousText.length);
- if (select) {
- view.setSelection(offset, offset + text.length);
- }
- }
- };
-
- /**
- * Constructs a new CompoundChange object.
- *
- * @class
- * @name orion.textview.CompoundChange
- * @private
- */
- function CompoundChange () {
- this.changes = [];
- }
- CompoundChange.prototype = {
- /** @ignore */
- add: function (change) {
- this.changes.push(change);
- },
- /** @ignore */
- end: function (view) {
- this.endSelection = view.getSelection();
- this.endCaret = view.getCaretOffset();
- },
- /** @ignore */
- undo: function (view, select) {
- for (var i=this.changes.length - 1; i >= 0; i--) {
- this.changes[i].undo(view, false);
- }
- if (select) {
- var start = this.startSelection.start;
- var end = this.startSelection.end;
- view.setSelection(this.startCaret ? start : end, this.startCaret ? end : start);
- }
- },
- /** @ignore */
- redo: function (view, select) {
- for (var i = 0; i < this.changes.length; i++) {
- this.changes[i].redo(view, false);
- }
- if (select) {
- var start = this.endSelection.start;
- var end = this.endSelection.end;
- view.setSelection(this.endCaret ? start : end, this.endCaret ? end : start);
- }
- },
- /** @ignore */
- start: function (view) {
- this.startSelection = view.getSelection();
- this.startCaret = view.getCaretOffset();
- }
- };
-
- /**
- * Constructs a new UndoStack on a text view.
- *
- * @param {orion.textview.TextView} view the text view for the undo stack.
- * @param {Number} [size=100] the size for the undo stack.
- *
- * @name orion.textview.UndoStack
- * @class The UndoStack is used to record the history of a text model associated to an view. Every
- * change to the model is added to stack, allowing the application to undo and redo these changes.
- *
- *
- * See:
- * {@link orion.textview.TextView}
- *
- */
- function UndoStack (view, size) {
- this.view = view;
- this.size = size !== undefined ? size : 100;
- this.reset();
- var model = view.getModel();
- if (model.getBaseModel) {
- model = model.getBaseModel();
- }
- this.model = model;
- var self = this;
- this._listener = {
- onChanging: function(e) {
- self._onChanging(e);
- },
- onDestroy: function(e) {
- self._onDestroy(e);
- }
- };
- model.addEventListener("Changing", this._listener.onChanging);
- view.addEventListener("Destroy", this._listener.onDestroy);
- }
- UndoStack.prototype = /** @lends orion.textview.UndoStack.prototype */ {
- /**
- * Adds a change to the stack.
- *
- * @param change the change to add.
- * @param {Number} change.offset the offset of the change
- * @param {String} change.text the new text of the change
- * @param {String} change.previousText the previous text of the change
- */
- add: function (change) {
- if (this.compoundChange) {
- this.compoundChange.add(change);
- } else {
- var length = this.stack.length;
- this.stack.splice(this.index, length-this.index, change);
- this.index++;
- if (this.stack.length > this.size) {
- this.stack.shift();
- this.index--;
- this.cleanIndex--;
- }
- }
- },
- /**
- * Marks the current state of the stack as clean.
- *
- *
- * This function is typically called when the content of view associated with the stack is saved.
- *
- *
- * @see #isClean
- */
- markClean: function() {
- this.endCompoundChange();
- this._commitUndo();
- this.cleanIndex = this.index;
- },
- /**
- * Returns true if current state of stack is the same
- * as the state when markClean() was called.
- *
- *
- * For example, the application calls markClean(), then calls undo() four times and redo() four times.
- * At this point isClean() returns true.
- *
- *
- * This function is typically called to determine if the content of the view associated with the stack
- * has changed since the last time it was saved.
- *
- *
- * @return {Boolean} returns if the state is the same as the state when markClean() was called.
- *
- * @see #markClean
- */
- isClean: function() {
- return this.cleanIndex === this.getSize().undo;
- },
- /**
- * Returns true if there is at least one change to undo.
- *
- * @return {Boolean} returns true if there is at least one change to undo.
- *
- * @see #canRedo
- * @see #undo
- */
- canUndo: function() {
- return this.getSize().undo > 0;
- },
- /**
- * Returns true if there is at least one change to redo.
- *
- * @return {Boolean} returns true if there is at least one change to redo.
- *
- * @see #canUndo
- * @see #redo
- */
- canRedo: function() {
- return this.getSize().redo > 0;
- },
- /**
- * Finishes a compound change.
- *
- * @see #startCompoundChange
- */
- endCompoundChange: function() {
- if (this.compoundChange) {
- this.compoundChange.end(this.view);
- }
- this.compoundChange = undefined;
- },
- /**
- * Returns the sizes of the stack.
- *
- * @return {object} a object where object.undo is the number of changes that can be un-done,
- * and object.redo is the number of changes that can be re-done.
- *
- * @see #canUndo
- * @see #canRedo
- */
- getSize: function() {
- var index = this.index;
- var length = this.stack.length;
- if (this._undoStart !== undefined) {
- index++;
- }
- return {undo: index, redo: (length - index)};
- },
- /**
- * Undo the last change in the stack.
- *
- * @return {Boolean} returns true if a change was un-done.
- *
- * @see #redo
- * @see #canUndo
- */
- undo: function() {
- this._commitUndo();
- if (this.index <= 0) {
- return false;
- }
- var change = this.stack[--this.index];
- this._ignoreUndo = true;
- change.undo(this.view, true);
- this._ignoreUndo = false;
- return true;
- },
- /**
- * Redo the last change in the stack.
- *
- * @return {Boolean} returns true if a change was re-done.
- *
- * @see #undo
- * @see #canRedo
- */
- redo: function() {
- this._commitUndo();
- if (this.index >= this.stack.length) {
- return false;
- }
- var change = this.stack[this.index++];
- this._ignoreUndo = true;
- change.redo(this.view, true);
- this._ignoreUndo = false;
- return true;
- },
- /**
- * Reset the stack to its original state. All changes in the stack are thrown away.
- */
- reset: function() {
- this.index = this.cleanIndex = 0;
- this.stack = [];
- this._undoStart = undefined;
- this._undoText = "";
- this._undoType = 0;
- this._ignoreUndo = false;
- this._compoundChange = undefined;
- },
- /**
- * Starts a compound change.
- *
- * All changes added to stack from the time startCompoundChange() is called
- * to the time that endCompoundChange() is called are compound on one change that can be un-done or re-done
- * with one single call to undo() or redo().
- *
- *
- * @see #endCompoundChange
- */
- startCompoundChange: function() {
- this._commitUndo();
- var change = new CompoundChange();
- this.add(change);
- this.compoundChange = change;
- this.compoundChange.start(this.view);
- },
- _commitUndo: function () {
- if (this._undoStart !== undefined) {
- if (this._undoType === -1) {
- this.add(new Change(this._undoStart, "", this._undoText, ""));
- } else {
- this.add(new Change(this._undoStart, this._undoText, ""));
- }
- this._undoStart = undefined;
- this._undoText = "";
- this._undoType = 0;
- }
- },
- _onDestroy: function(evt) {
- this.model.removeEventListener("Changing", this._listener.onChanging);
- this.view.removeEventListener("Destroy", this._listener.onDestroy);
- },
- _onChanging: function(e) {
- var newText = e.text;
- var start = e.start;
- var removedCharCount = e.removedCharCount;
- var addedCharCount = e.addedCharCount;
- if (this._ignoreUndo) {
- return;
- }
- if (this._undoStart !== undefined &&
- !((addedCharCount === 1 && removedCharCount === 0 && this._undoType === 1 && start === this._undoStart + this._undoText.length) ||
- (addedCharCount === 0 && removedCharCount === 1 && this._undoType === -1 && (((start + 1) === this._undoStart) || (start === this._undoStart)))))
- {
- this._commitUndo();
- }
- if (!this.compoundChange) {
- if (addedCharCount === 1 && removedCharCount === 0) {
- if (this._undoStart === undefined) {
- this._undoStart = start;
- }
- this._undoText = this._undoText + newText;
- this._undoType = 1;
- return;
- } else if (addedCharCount === 0 && removedCharCount === 1) {
- var deleting = this._undoText.length > 0 && this._undoStart === start;
- this._undoStart = start;
- this._undoType = -1;
- if (deleting) {
- this._undoText = this._undoText + this.model.getText(start, start + removedCharCount);
- } else {
- this._undoText = this.model.getText(start, start + removedCharCount) + this._undoText;
- }
- return;
- }
- }
- this.add(new Change(start, newText, this.model.getText(start, start + removedCharCount)));
- }
- };
-
- return {
- UndoStack: UndoStack
- };
-});
-/*******************************************************************************
- * @license
- * Copyright (c) 2010, 2011 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License v1.0
- * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
- * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
- *
- * Contributors:
- * Felipe Heidrich (IBM Corporation) - initial API and implementation
- * Silenio Quarti (IBM Corporation) - initial API and implementation
- ******************************************************************************/
-
-/*global define window*/
-
-define("orion/textview/textModel", ['orion/textview/eventTarget'], function(mEventTarget) {
- var isWindows = window.navigator.platform.indexOf("Win") !== -1;
-
- /**
- * Constructs a new TextModel with the given text and default line delimiter.
- *
- * @param {String} [text=""] the text that the model will store
- * @param {String} [lineDelimiter=platform delimiter] the line delimiter used when inserting new lines to the model.
- *
- * @name orion.textview.TextModel
- * @class The TextModel is an interface that provides text for the view. Applications may
- * implement the TextModel interface to provide a custom store for the view content. The
- * view interacts with its text model in order to access and update the text that is being
- * displayed and edited in the view. This is the default implementation.
- *
- * @borrows orion.textview.EventTarget#addEventListener as #addEventListener
- * @borrows orion.textview.EventTarget#removeEventListener as #removeEventListener
- * @borrows orion.textview.EventTarget#dispatchEvent as #dispatchEvent
- */
- function TextModel(text, lineDelimiter) {
- this._lastLineIndex = -1;
- this._text = [""];
- this._lineOffsets = [0];
- this.setText(text);
- this.setLineDelimiter(lineDelimiter);
- }
-
- TextModel.prototype = /** @lends orion.textview.TextModel.prototype */ {
- /**
- * Returns the number of characters in the model.
- *
- * @returns {Number} the number of characters in the model.
- */
- getCharCount: function() {
- var count = 0;
- for (var i = 0; i
- * The valid indices are 0 to line count exclusive. Returns null
- * if the index is out of range.
- *
- *
- * @param {Number} lineIndex the zero based index of the line.
- * @param {Boolean} [includeDelimiter=false] whether or not to include the line delimiter.
- * @returns {String} the line text or null if out of range.
- *
- * @see #getLineAtOffset
- */
- getLine: function(lineIndex, includeDelimiter) {
- var lineCount = this.getLineCount();
- if (!(0 <= lineIndex && lineIndex < lineCount)) {
- return null;
- }
- var start = this._lineOffsets[lineIndex];
- if (lineIndex + 1 < lineCount) {
- var text = this.getText(start, this._lineOffsets[lineIndex + 1]);
- if (includeDelimiter) {
- return text;
- }
- var end = text.length, c;
- while (((c = text.charCodeAt(end - 1)) === 10) || (c === 13)) {
- end--;
- }
- return text.substring(0, end);
- } else {
- return this.getText(start);
- }
- },
- /**
- * Returns the line index at the given character offset.
- *
- * The valid offsets are 0 to char count inclusive. The line index for
- * char count is line count - 1. Returns -1 if
- * the offset is out of range.
- *
- *
- * @param {Number} offset a character offset.
- * @returns {Number} the zero based line index or -1 if out of range.
- */
- getLineAtOffset: function(offset) {
- var charCount = this.getCharCount();
- if (!(0 <= offset && offset <= charCount)) {
- return -1;
- }
- var lineCount = this.getLineCount();
- if (offset === charCount) {
- return lineCount - 1;
- }
- var lineStart, lineEnd;
- var index = this._lastLineIndex;
- if (0 <= index && index < lineCount) {
- lineStart = this._lineOffsets[index];
- lineEnd = index + 1 < lineCount ? this._lineOffsets[index + 1] : charCount;
- if (lineStart <= offset && offset < lineEnd) {
- return index;
- }
- }
- var high = lineCount;
- var low = -1;
- while (high - low > 1) {
- index = Math.floor((high + low) / 2);
- lineStart = this._lineOffsets[index];
- lineEnd = index + 1 < lineCount ? this._lineOffsets[index + 1] : charCount;
- if (offset <= lineStart) {
- high = index;
- } else if (offset < lineEnd) {
- high = index;
- break;
- } else {
- low = index;
- }
- }
- this._lastLineIndex = high;
- return high;
- },
- /**
- * Returns the number of lines in the model.
- *
- * The model always has at least one line.
- *
- *
- * @returns {Number} the number of lines.
- */
- getLineCount: function() {
- return this._lineOffsets.length;
- },
- /**
- * Returns the line delimiter that is used by the view
- * when inserting new lines. New lines entered using key strokes
- * and paste operations use this line delimiter.
- *
- * @return {String} the line delimiter that is used by the view when inserting new lines.
- */
- getLineDelimiter: function() {
- return this._lineDelimiter;
- },
- /**
- * Returns the end character offset for the given line.
- *
- * The end offset is not inclusive. This means that when the line delimiter is included, the
- * offset is either the start offset of the next line or char count. When the line delimiter is
- * not included, the offset is the offset of the line delimiter.
- *
- *
- * The valid indices are 0 to line count exclusive. Returns -1
- * if the index is out of range.
- *
- *
- * @param {Number} lineIndex the zero based index of the line.
- * @param {Boolean} [includeDelimiter=false] whether or not to include the line delimiter.
- * @return {Number} the line end offset or -1 if out of range.
- *
- * @see #getLineStart
- */
- getLineEnd: function(lineIndex, includeDelimiter) {
- var lineCount = this.getLineCount();
- if (!(0 <= lineIndex && lineIndex < lineCount)) {
- return -1;
- }
- if (lineIndex + 1 < lineCount) {
- var end = this._lineOffsets[lineIndex + 1];
- if (includeDelimiter) {
- return end;
- }
- var text = this.getText(Math.max(this._lineOffsets[lineIndex], end - 2), end);
- var i = text.length, c;
- while (((c = text.charCodeAt(i - 1)) === 10) || (c === 13)) {
- i--;
- }
- return end - (text.length - i);
- } else {
- return this.getCharCount();
- }
- },
- /**
- * Returns the start character offset for the given line.
- *
- * The valid indices are 0 to line count exclusive. Returns -1
- * if the index is out of range.
- *
- *
- * @param {Number} lineIndex the zero based index of the line.
- * @return {Number} the line start offset or -1 if out of range.
- *
- * @see #getLineEnd
- */
- getLineStart: function(lineIndex) {
- if (!(0 <= lineIndex && lineIndex < this.getLineCount())) {
- return -1;
- }
- return this._lineOffsets[lineIndex];
- },
- /**
- * Returns the text for the given range.
- *
- * The end offset is not inclusive. This means that character at the end offset
- * is not included in the returned text.
- *
- *
- * @param {Number} [start=0] the zero based start offset of text range.
- * @param {Number} [end=char count] the zero based end offset of text range.
- *
- * @see #setText
- */
- getText: function(start, end) {
- if (start === undefined) { start = 0; }
- if (end === undefined) { end = this.getCharCount(); }
- if (start === end) { return ""; }
- var offset = 0, chunk = 0, length;
- while (chunk
- * This notification is intended to be used only by the view. Application clients should
- * use {@link orion.textview.TextView#event:onModelChanging}.
- *
- *
- * NOTE: This method is not meant to called directly by application code. It is called internally by the TextModel
- * as part of the implementation of {@link #setText}. This method is included in the public API for documentation
- * purposes and to allow integration with other toolkit frameworks.
- *
- *
- * @param {orion.textview.ModelChangingEvent} modelChangingEvent the changing event
- */
- onChanging: function(modelChangingEvent) {
- return this.dispatchEvent(modelChangingEvent);
- },
- /**
- * Notifies all listeners that the text has changed.
- *
- * This notification is intended to be used only by the view. Application clients should
- * use {@link orion.textview.TextView#event:onModelChanged}.
- *
- *
- * NOTE: This method is not meant to called directly by application code. It is called internally by the TextModel
- * as part of the implementation of {@link #setText}. This method is included in the public API for documentation
- * purposes and to allow integration with other toolkit frameworks.
- *
- *
- * @param {orion.textview.ModelChangedEvent} modelChangedEvent the changed event
- */
- onChanged: function(modelChangedEvent) {
- return this.dispatchEvent(modelChangedEvent);
- },
- /**
- * Sets the line delimiter that is used by the view
- * when new lines are inserted in the model due to key
- * strokes and paste operations.
- *
- * If lineDelimiter is "auto", the delimiter is computed to be
- * the first delimiter found the in the current text. If lineDelimiter
- * is undefined or if there are no delimiters in the current text, the
- * platform delimiter is used.
- *
- *
- * @param {String} lineDelimiter the line delimiter that is used by the view when inserting new lines.
- */
- setLineDelimiter: function(lineDelimiter) {
- if (lineDelimiter === "auto") {
- lineDelimiter = undefined;
- if (this.getLineCount() > 1) {
- lineDelimiter = this.getText(this.getLineEnd(0), this.getLineEnd(0, true));
- }
- }
- this._lineDelimiter = lineDelimiter ? lineDelimiter : (isWindows ? "\r\n" : "\n");
- },
- /**
- * Replaces the text in the given range with the given text.
- *
- * The end offset is not inclusive. This means that the character at the
- * end offset is not replaced.
- *
- *
- * The text model must notify the listeners before and after the
- * the text is changed by calling {@link #onChanging} and {@link #onChanged}
- * respectively.
- *
- *
- * @param {String} [text=""] the new text.
- * @param {Number} [start=0] the zero based start offset of text range.
- * @param {Number} [end=char count] the zero based end offset of text range.
- *
- * @see #getText
- */
- setText: function(text, start, end) {
- if (text === undefined) { text = ""; }
- if (start === undefined) { start = 0; }
- if (end === undefined) { end = this.getCharCount(); }
- if (start === end && text === "") { return; }
- var startLine = this.getLineAtOffset(start);
- var endLine = this.getLineAtOffset(end);
- var eventStart = start;
- var removedCharCount = end - start;
- var removedLineCount = endLine - startLine;
- var addedCharCount = text.length;
- var addedLineCount = 0;
- var lineCount = this.getLineCount();
-
- var cr = 0, lf = 0, index = 0;
- var newLineOffsets = [];
- while (true) {
- if (cr !== -1 && cr <= index) { cr = text.indexOf("\r", index); }
- if (lf !== -1 && lf <= index) { lf = text.indexOf("\n", index); }
- if (lf === -1 && cr === -1) { break; }
- if (cr !== -1 && lf !== -1) {
- if (cr + 1 === lf) {
- index = lf + 1;
- } else {
- index = (cr < lf ? cr : lf) + 1;
- }
- } else if (cr !== -1) {
- index = cr + 1;
- } else {
- index = lf + 1;
- }
- newLineOffsets.push(start + index);
- addedLineCount++;
- }
-
- var modelChangingEvent = {
- type: "Changing",
- text: text,
- start: eventStart,
- removedCharCount: removedCharCount,
- addedCharCount: addedCharCount,
- removedLineCount: removedLineCount,
- addedLineCount: addedLineCount
- };
- this.onChanging(modelChangingEvent);
-
- //TODO this should be done the loops below to avoid getText()
- if (newLineOffsets.length === 0) {
- var startLineOffset = this.getLineStart(startLine), endLineOffset;
- if (endLine + 1 < lineCount) {
- endLineOffset = this.getLineStart(endLine + 1);
- } else {
- endLineOffset = this.getCharCount();
- }
- if (start !== startLineOffset) {
- text = this.getText(startLineOffset, start) + text;
- start = startLineOffset;
- }
- if (end !== endLineOffset) {
- text = text + this.getText(end, endLineOffset);
- end = endLineOffset;
- }
- }
-
- var changeCount = addedCharCount - removedCharCount;
- for (var j = startLine + removedLineCount + 1; j < lineCount; j++) {
- this._lineOffsets[j] += changeCount;
- }
- var args = [startLine + 1, removedLineCount].concat(newLineOffsets);
- Array.prototype.splice.apply(this._lineOffsets, args);
-
- var offset = 0, chunk = 0, length;
- while (chunk
- * See:
- * {@link orion.textview.ProjectionTextModel}
- * {@link orion.textview.ProjectionTextModel#addProjection}
- *
- * @name orion.textview.Projection
- *
- * @property {Number} start The start offset of the projection range.
- * @property {Number} end The end offset of the projection range. This offset is exclusive.
- * @property {String|orion.textview.TextModel} [text=""] The projection text to be inserted
- */
- /**
- * Constructs a new ProjectionTextModel based on the specified TextModel.
- *
- * @param {orion.textview.TextModel} baseModel The base text model.
- *
- * @name orion.textview.ProjectionTextModel
- * @class The ProjectionTextModel represents a projection of its base text
- * model. Projection ranges can be added to the projection text model to hide and/or insert
- * ranges to the base text model.
- *
- * The contents of the projection text model is modified when changes occur in the base model,
- * projection model or by calls to {@link #addProjection} and {@link #removeProjection}.
- *
- * @borrows orion.textview.EventTarget#addEventListener as #addEventListener
- * @borrows orion.textview.EventTarget#removeEventListener as #removeEventListener
- * @borrows orion.textview.EventTarget#dispatchEvent as #dispatchEvent
- */
- function ProjectionTextModel(baseModel) {
- this._model = baseModel; /* Base Model */
- this._projections = [];
- }
-
- ProjectionTextModel.prototype = /** @lends orion.textview.ProjectionTextModel.prototype */ {
- /**
- * Adds a projection range to the model.
- *
- * The model must notify the listeners before and after the the text is
- * changed by calling {@link #onChanging} and {@link #onChanged} respectively.
- *
- * @param {orion.textview.Projection} projection The projection range to be added.
- *
- * @see #removeProjection
- */
- addProjection: function(projection) {
- if (!projection) {return;}
- //start and end can't overlap any exist projection
- var model = this._model, projections = this._projections;
- projection._lineIndex = model.getLineAtOffset(projection.start);
- projection._lineCount = model.getLineAtOffset(projection.end) - projection._lineIndex;
- var text = projection.text;
- if (!text) { text = ""; }
- if (typeof text === "string") {
- projection._model = new mTextModel.TextModel(text, model.getLineDelimiter());
- } else {
- projection._model = text;
- }
- var eventStart = this.mapOffset(projection.start, true);
- var removedCharCount = projection.end - projection.start;
- var removedLineCount = projection._lineCount;
- var addedCharCount = projection._model.getCharCount();
- var addedLineCount = projection._model.getLineCount() - 1;
- var modelChangingEvent = {
- type: "Changing",
- text: projection._model.getText(),
- start: eventStart,
- removedCharCount: removedCharCount,
- addedCharCount: addedCharCount,
- removedLineCount: removedLineCount,
- addedLineCount: addedLineCount
- };
- this.onChanging(modelChangingEvent);
- var index = this._binarySearch(projections, projection.start);
- projections.splice(index, 0, projection);
- var modelChangedEvent = {
- type: "Changed",
- start: eventStart,
- removedCharCount: removedCharCount,
- addedCharCount: addedCharCount,
- removedLineCount: removedLineCount,
- addedLineCount: addedLineCount
- };
- this.onChanged(modelChangedEvent);
- },
- /**
- * Returns all projection ranges of this model.
- *
- * @return {orion.textview.Projection[]} The projection ranges.
- *
- * @see #addProjection
- */
- getProjections: function() {
- return this._projections.slice(0);
- },
- /**
- * Gets the base text model.
- *
- * @return {orion.textview.TextModel} The base text model.
- */
- getBaseModel: function() {
- return this._model;
- },
- /**
- * Maps offsets between the projection model and its base model.
- *
- * @param {Number} offset The offset to be mapped.
- * @param {Boolean} [baseOffset=false] true if offset is in base model and
- * should be mapped to the projection model.
- * @return {Number} The mapped offset
- */
- mapOffset: function(offset, baseOffset) {
- var projections = this._projections, delta = 0, i, projection;
- if (baseOffset) {
- for (i = 0; i < projections.length; i++) {
- projection = projections[i];
- if (projection.start > offset) { break; }
- if (projection.end > offset) { return -1; }
- delta += projection._model.getCharCount() - (projection.end - projection.start);
- }
- return offset + delta;
- }
- for (i = 0; i < projections.length; i++) {
- projection = projections[i];
- if (projection.start > offset - delta) { break; }
- var charCount = projection._model.getCharCount();
- if (projection.start + charCount > offset - delta) {
- return -1;
- }
- delta += charCount - (projection.end - projection.start);
- }
- return offset - delta;
- },
- /**
- * Removes a projection range from the model.
- *
- * The model must notify the listeners before and after the the text is
- * changed by calling {@link #onChanging} and {@link #onChanged} respectively.
- *
- *
- * @param {orion.textview.Projection} projection The projection range to be removed.
- *
- * @see #addProjection
- */
- removeProjection: function(projection) {
- //TODO remove listeners from model
- var i, delta = 0;
- for (i = 0; i < this._projections.length; i++) {
- var p = this._projections[i];
- if (p === projection) {
- projection = p;
- break;
- }
- delta += p._model.getCharCount() - (p.end - p.start);
- }
- if (i < this._projections.length) {
- var model = this._model;
- var eventStart = projection.start + delta;
- var addedCharCount = projection.end - projection.start;
- var addedLineCount = projection._lineCount;
- var removedCharCount = projection._model.getCharCount();
- var removedLineCount = projection._model.getLineCount() - 1;
- var modelChangingEvent = {
- type: "Changing",
- text: model.getText(projection.start, projection.end),
- start: eventStart,
- removedCharCount: removedCharCount,
- addedCharCount: addedCharCount,
- removedLineCount: removedLineCount,
- addedLineCount: addedLineCount
- };
- this.onChanging(modelChangingEvent);
- this._projections.splice(i, 1);
- var modelChangedEvent = {
- type: "Changed",
- start: eventStart,
- removedCharCount: removedCharCount,
- addedCharCount: addedCharCount,
- removedLineCount: removedLineCount,
- addedLineCount: addedLineCount
- };
- this.onChanged(modelChangedEvent);
- }
- },
- /** @ignore */
- _binarySearch: function (array, offset) {
- var high = array.length, low = -1, index;
- while (high - low > 1) {
- index = Math.floor((high + low) / 2);
- if (offset <= array[index].start) {
- high = index;
- } else {
- low = index;
- }
- }
- return high;
- },
- /**
- * @see orion.textview.TextModel#getCharCount
- */
- getCharCount: function() {
- var count = this._model.getCharCount(), projections = this._projections;
- for (var i = 0; i < projections.length; i++) {
- var projection = projections[i];
- count += projection._model.getCharCount() - (projection.end - projection.start);
- }
- return count;
- },
- /**
- * @see orion.textview.TextModel#getLine
- */
- getLine: function(lineIndex, includeDelimiter) {
- if (lineIndex < 0) { return null; }
- var model = this._model, projections = this._projections;
- var delta = 0, result = [], offset = 0, i, lineCount, projection;
- for (i = 0; i < projections.length; i++) {
- projection = projections[i];
- if (projection._lineIndex >= lineIndex - delta) { break; }
- lineCount = projection._model.getLineCount() - 1;
- if (projection._lineIndex + lineCount >= lineIndex - delta) {
- var projectionLineIndex = lineIndex - (projection._lineIndex + delta);
- if (projectionLineIndex < lineCount) {
- return projection._model.getLine(projectionLineIndex, includeDelimiter);
- } else {
- result.push(projection._model.getLine(lineCount));
- }
- }
- offset = projection.end;
- delta += lineCount - projection._lineCount;
- }
- offset = Math.max(offset, model.getLineStart(lineIndex - delta));
- for (; i < projections.length; i++) {
- projection = projections[i];
- if (projection._lineIndex > lineIndex - delta) { break; }
- result.push(model.getText(offset, projection.start));
- lineCount = projection._model.getLineCount() - 1;
- if (projection._lineIndex + lineCount > lineIndex - delta) {
- result.push(projection._model.getLine(0, includeDelimiter));
- return result.join("");
- }
- result.push(projection._model.getText());
- offset = projection.end;
- delta += lineCount - projection._lineCount;
- }
- var end = model.getLineEnd(lineIndex - delta, includeDelimiter);
- if (offset < end) {
- result.push(model.getText(offset, end));
- }
- return result.join("");
- },
- /**
- * @see orion.textview.TextModel#getLineAtOffset
- */
- getLineAtOffset: function(offset) {
- var model = this._model, projections = this._projections;
- var delta = 0, lineDelta = 0;
- for (var i = 0; i < projections.length; i++) {
- var projection = projections[i];
- if (projection.start > offset - delta) { break; }
- var charCount = projection._model.getCharCount();
- if (projection.start + charCount > offset - delta) {
- var projectionOffset = offset - (projection.start + delta);
- lineDelta += projection._model.getLineAtOffset(projectionOffset);
- delta += projectionOffset;
- break;
- }
- lineDelta += projection._model.getLineCount() - 1 - projection._lineCount;
- delta += charCount - (projection.end - projection.start);
- }
- return model.getLineAtOffset(offset - delta) + lineDelta;
- },
- /**
- * @see orion.textview.TextModel#getLineCount
- */
- getLineCount: function() {
- var model = this._model, projections = this._projections;
- var count = model.getLineCount();
- for (var i = 0; i < projections.length; i++) {
- var projection = projections[i];
- count += projection._model.getLineCount() - 1 - projection._lineCount;
- }
- return count;
- },
- /**
- * @see orion.textview.TextModel#getLineDelimiter
- */
- getLineDelimiter: function() {
- return this._model.getLineDelimiter();
- },
- /**
- * @see orion.textview.TextModel#getLineEnd
- */
- getLineEnd: function(lineIndex, includeDelimiter) {
- if (lineIndex < 0) { return -1; }
- var model = this._model, projections = this._projections;
- var delta = 0, offsetDelta = 0;
- for (var i = 0; i < projections.length; i++) {
- var projection = projections[i];
- if (projection._lineIndex > lineIndex - delta) { break; }
- var lineCount = projection._model.getLineCount() - 1;
- if (projection._lineIndex + lineCount > lineIndex - delta) {
- var projectionLineIndex = lineIndex - (projection._lineIndex + delta);
- return projection._model.getLineEnd (projectionLineIndex, includeDelimiter) + projection.start + offsetDelta;
- }
- offsetDelta += projection._model.getCharCount() - (projection.end - projection.start);
- delta += lineCount - projection._lineCount;
- }
- return model.getLineEnd(lineIndex - delta, includeDelimiter) + offsetDelta;
- },
- /**
- * @see orion.textview.TextModel#getLineStart
- */
- getLineStart: function(lineIndex) {
- if (lineIndex < 0) { return -1; }
- var model = this._model, projections = this._projections;
- var delta = 0, offsetDelta = 0;
- for (var i = 0; i < projections.length; i++) {
- var projection = projections[i];
- if (projection._lineIndex >= lineIndex - delta) { break; }
- var lineCount = projection._model.getLineCount() - 1;
- if (projection._lineIndex + lineCount >= lineIndex - delta) {
- var projectionLineIndex = lineIndex - (projection._lineIndex + delta);
- return projection._model.getLineStart (projectionLineIndex) + projection.start + offsetDelta;
- }
- offsetDelta += projection._model.getCharCount() - (projection.end - projection.start);
- delta += lineCount - projection._lineCount;
- }
- return model.getLineStart(lineIndex - delta) + offsetDelta;
- },
- /**
- * @see orion.textview.TextModel#getText
- */
- getText: function(start, end) {
- if (start === undefined) { start = 0; }
- var model = this._model, projections = this._projections;
- var delta = 0, result = [], i, projection, charCount;
- for (i = 0; i < projections.length; i++) {
- projection = projections[i];
- if (projection.start > start - delta) { break; }
- charCount = projection._model.getCharCount();
- if (projection.start + charCount > start - delta) {
- if (end !== undefined && projection.start + charCount > end - delta) {
- return projection._model.getText(start - (projection.start + delta), end - (projection.start + delta));
- } else {
- result.push(projection._model.getText(start - (projection.start + delta)));
- start = projection.end + delta + charCount - (projection.end - projection.start);
- }
- }
- delta += charCount - (projection.end - projection.start);
- }
- var offset = start - delta;
- if (end !== undefined) {
- for (; i < projections.length; i++) {
- projection = projections[i];
- if (projection.start > end - delta) { break; }
- result.push(model.getText(offset, projection.start));
- charCount = projection._model.getCharCount();
- if (projection.start + charCount > end - delta) {
- result.push(projection._model.getText(0, end - (projection.start + delta)));
- return result.join("");
- }
- result.push(projection._model.getText());
- offset = projection.end;
- delta += charCount - (projection.end - projection.start);
- }
- result.push(model.getText(offset, end - delta));
- } else {
- for (; i < projections.length; i++) {
- projection = projections[i];
- result.push(model.getText(offset, projection.start));
- result.push(projection._model.getText());
- offset = projection.end;
- }
- result.push(model.getText(offset));
- }
- return result.join("");
- },
- /** @ignore */
- _onChanging: function(text, start, removedCharCount, addedCharCount, removedLineCount, addedLineCount) {
- var model = this._model, projections = this._projections, i, projection, delta = 0, lineDelta;
- var end = start + removedCharCount;
- for (; i < projections.length; i++) {
- projection = projections[i];
- if (projection.start > start) { break; }
- delta += projection._model.getCharCount() - (projection.end - projection.start);
- }
- /*TODO add stuff saved by setText*/
- var mapStart = start + delta, rangeStart = i;
- for (; i < projections.length; i++) {
- projection = projections[i];
- if (projection.start > end) { break; }
- delta += projection._model.getCharCount() - (projection.end - projection.start);
- lineDelta += projection._model.getLineCount() - 1 - projection._lineCount;
- }
- /*TODO add stuff saved by setText*/
- var mapEnd = end + delta, rangeEnd = i;
- this.onChanging(mapStart, mapEnd - mapStart, addedCharCount/*TODO add stuff saved by setText*/, removedLineCount + lineDelta/*TODO add stuff saved by setText*/, addedLineCount/*TODO add stuff saved by setText*/);
- projections.splice(projections, rangeEnd - rangeStart);
- var count = text.length - (mapEnd - mapStart);
- for (; i < projections.length; i++) {
- projection = projections[i];
- projection.start += count;
- projection.end += count;
- projection._lineIndex = model.getLineAtOffset(projection.start);
- }
- },
- /**
- * @see orion.textview.TextModel#onChanging
- */
- onChanging: function(modelChangingEvent) {
- return this.dispatchEvent(modelChangingEvent);
- },
- /**
- * @see orion.textview.TextModel#onChanged
- */
- onChanged: function(modelChangedEvent) {
- return this.dispatchEvent(modelChangedEvent);
- },
- /**
- * @see orion.textview.TextModel#setLineDelimiter
- */
- setLineDelimiter: function(lineDelimiter) {
- this._model.setLineDelimiter(lineDelimiter);
- },
- /**
- * @see orion.textview.TextModel#setText
- */
- setText: function(text, start, end) {
- if (text === undefined) { text = ""; }
- if (start === undefined) { start = 0; }
- var eventStart = start, eventEnd = end;
- var model = this._model, projections = this._projections;
- var delta = 0, lineDelta = 0, i, projection, charCount, startProjection, endProjection, startLineDelta = 0;
- for (i = 0; i < projections.length; i++) {
- projection = projections[i];
- if (projection.start > start - delta) { break; }
- charCount = projection._model.getCharCount();
- if (projection.start + charCount > start - delta) {
- if (end !== undefined && projection.start + charCount > end - delta) {
- projection._model.setText(text, start - (projection.start + delta), end - (projection.start + delta));
- //TODO events - special case
- return;
- } else {
- startLineDelta = projection._model.getLineCount() - 1 - projection._model.getLineAtOffset(start - (projection.start + delta));
- startProjection = {
- projection: projection,
- start: start - (projection.start + delta)
- };
- start = projection.end + delta + charCount - (projection.end - projection.start);
- }
- }
- lineDelta += projection._model.getLineCount() - 1 - projection._lineCount;
- delta += charCount - (projection.end - projection.start);
- }
- var mapStart = start - delta, rangeStart = i, startLine = model.getLineAtOffset(mapStart) + lineDelta - startLineDelta;
- if (end !== undefined) {
- for (; i < projections.length; i++) {
- projection = projections[i];
- if (projection.start > end - delta) { break; }
- charCount = projection._model.getCharCount();
- if (projection.start + charCount > end - delta) {
- lineDelta += projection._model.getLineAtOffset(end - (projection.start + delta));
- charCount = end - (projection.start + delta);
- end = projection.end + delta;
- endProjection = {
- projection: projection,
- end: charCount
- };
- break;
- }
- lineDelta += projection._model.getLineCount() - 1 - projection._lineCount;
- delta += charCount - (projection.end - projection.start);
- }
- } else {
- for (; i < projections.length; i++) {
- projection = projections[i];
- lineDelta += projection._model.getLineCount() - 1 - projection._lineCount;
- delta += projection._model.getCharCount() - (projection.end - projection.start);
- }
- end = eventEnd = model.getCharCount() + delta;
- }
- var mapEnd = end - delta, rangeEnd = i, endLine = model.getLineAtOffset(mapEnd) + lineDelta;
-
- //events
- var removedCharCount = eventEnd - eventStart;
- var removedLineCount = endLine - startLine;
- var addedCharCount = text.length;
- var addedLineCount = 0;
- var cr = 0, lf = 0, index = 0;
- while (true) {
- if (cr !== -1 && cr <= index) { cr = text.indexOf("\r", index); }
- if (lf !== -1 && lf <= index) { lf = text.indexOf("\n", index); }
- if (lf === -1 && cr === -1) { break; }
- if (cr !== -1 && lf !== -1) {
- if (cr + 1 === lf) {
- index = lf + 1;
- } else {
- index = (cr < lf ? cr : lf) + 1;
- }
- } else if (cr !== -1) {
- index = cr + 1;
- } else {
- index = lf + 1;
- }
- addedLineCount++;
- }
-
- var modelChangingEvent = {
- type: "Changing",
- text: text,
- start: eventStart,
- removedCharCount: removedCharCount,
- addedCharCount: addedCharCount,
- removedLineCount: removedLineCount,
- addedLineCount: addedLineCount
- };
- this.onChanging(modelChangingEvent);
-
-// var changeLineCount = model.getLineAtOffset(mapEnd) - model.getLineAtOffset(mapStart) + addedLineCount;
- model.setText(text, mapStart, mapEnd);
- if (startProjection) {
- projection = startProjection.projection;
- projection._model.setText("", startProjection.start);
- }
- if (endProjection) {
- projection = endProjection.projection;
- projection._model.setText("", 0, endProjection.end);
- projection.start = projection.end;
- projection._lineCount = 0;
- }
- projections.splice(rangeStart, rangeEnd - rangeStart);
- var changeCount = text.length - (mapEnd - mapStart);
- for (i = rangeEnd; i < projections.length; i++) {
- projection = projections[i];
- projection.start += changeCount;
- projection.end += changeCount;
-// if (projection._lineIndex + changeLineCount !== model.getLineAtOffset(projection.start)) {
-// log("here");
-// }
- projection._lineIndex = model.getLineAtOffset(projection.start);
-// projection._lineIndex += changeLineCount;
- }
-
- var modelChangedEvent = {
- type: "Changed",
- start: eventStart,
- removedCharCount: removedCharCount,
- addedCharCount: addedCharCount,
- removedLineCount: removedLineCount,
- addedLineCount: addedLineCount
- };
- this.onChanged(modelChangedEvent);
- }
- };
- mEventTarget.EventTarget.addMixin(ProjectionTextModel.prototype);
-
- return {ProjectionTextModel: ProjectionTextModel};
-});
-/*******************************************************************************
- * @license
- * Copyright (c) 2010, 2011 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License v1.0
- * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
- * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
- *
- * Contributors: IBM Corporation - initial API and implementation
- ******************************************************************************/
-
-/*global define setTimeout clearTimeout setInterval clearInterval Node */
-
-define("orion/textview/tooltip", ['orion/textview/textView', 'orion/textview/textModel', 'orion/textview/projectionTextModel'], function(mTextView, mTextModel, mProjectionTextModel) {
-
- /** @private */
- function Tooltip (view) {
- this._view = view;
- //TODO add API to get the parent of the view
- this._create(view._parent.ownerDocument);
- view.addEventListener("Destroy", this, this.destroy);
- }
- Tooltip.getTooltip = function(view) {
- if (!view._tooltip) {
- view._tooltip = new Tooltip(view);
- }
- return view._tooltip;
- };
- Tooltip.prototype = /** @lends orion.textview.Tooltip.prototype */ {
- _create: function(document) {
- if (this._domNode) { return; }
- this._document = document;
- var domNode = this._domNode = document.createElement("DIV");
- domNode.className = "viewTooltip";
- var viewParent = this._viewParent = document.createElement("DIV");
- domNode.appendChild(viewParent);
- var htmlParent = this._htmlParent = document.createElement("DIV");
- domNode.appendChild(htmlParent);
- document.body.appendChild(domNode);
- this.hide();
- },
- destroy: function() {
- if (!this._domNode) { return; }
- if (this._contentsView) {
- this._contentsView.destroy();
- this._contentsView = null;
- this._emptyModel = null;
- }
- var parent = this._domNode.parentNode;
- if (parent) { parent.removeChild(this._domNode); }
- this._domNode = null;
- },
- hide: function() {
- if (this._contentsView) {
- this._contentsView.setModel(this._emptyModel);
- }
- if (this._viewParent) {
- this._viewParent.style.left = "-10000px";
- this._viewParent.style.position = "fixed";
- this._viewParent.style.visibility = "hidden";
- }
- if (this._htmlParent) {
- this._htmlParent.style.left = "-10000px";
- this._htmlParent.style.position = "fixed";
- this._htmlParent.style.visibility = "hidden";
- this._htmlParent.innerHTML = "";
- }
- if (this._domNode) {
- this._domNode.style.visibility = "hidden";
- }
- if (this._showTimeout) {
- clearTimeout(this._showTimeout);
- this._showTimeout = null;
- }
- if (this._hideTimeout) {
- clearTimeout(this._hideTimeout);
- this._hideTimeout = null;
- }
- if (this._fadeTimeout) {
- clearInterval(this._fadeTimeout);
- this._fadeTimeout = null;
- }
- },
- isVisible: function() {
- return this._domNode && this._domNode.style.visibility === "visible";
- },
- setTarget: function(target) {
- if (this.target === target) { return; }
- this._target = target;
- this.hide();
- if (target) {
- var self = this;
- self._showTimeout = setTimeout(function() {
- self.show(true);
- }, 1000);
- }
- },
- show: function(autoHide) {
- if (!this._target) { return; }
- var info = this._target.getTooltipInfo();
- if (!info) { return; }
- var domNode = this._domNode;
- domNode.style.left = domNode.style.right = domNode.style.width = domNode.style.height = "auto";
- var contents = info.contents, contentsDiv;
- if (contents instanceof Array) {
- contents = this._getAnnotationContents(contents);
- }
- if (typeof contents === "string") {
- (contentsDiv = this._htmlParent).innerHTML = contents;
- } else if (contents instanceof Node) {
- (contentsDiv = this._htmlParent).appendChild(contents);
- } else if (contents instanceof mProjectionTextModel.ProjectionTextModel) {
- if (!this._contentsView) {
- this._emptyModel = new mTextModel.TextModel("");
- //TODO need hook into setup.js (or editor.js) to create a text view (and styler)
- var newView = this._contentsView = new mTextView.TextView({
- model: this._emptyModel,
- parent: this._viewParent,
- tabSize: 4,
- sync: true,
- stylesheet: ["/orion/textview/tooltip.css", "/orion/textview/rulers.css",
- "/examples/textview/textstyler.css", "/css/default-theme.css"]
- });
- //TODO this is need to avoid IE from getting focus
- newView._clientDiv.contentEditable = false;
- //TODO need to find a better way of sharing the styler for multiple views
- var view = this._view;
- var listener = {
- onLineStyle: function(e) {
- view.onLineStyle(e);
- }
- };
- newView.addEventListener("LineStyle", listener.onLineStyle);
- }
- var contentsView = this._contentsView;
- contentsView.setModel(contents);
- var size = contentsView.computeSize();
- contentsDiv = this._viewParent;
- //TODO always make the width larger than the size of the scrollbar to avoid bug in updatePage
- contentsDiv.style.width = (size.width + 20) + "px";
- contentsDiv.style.height = size.height + "px";
- } else {
- return;
- }
- contentsDiv.style.left = "auto";
- contentsDiv.style.position = "static";
- contentsDiv.style.visibility = "visible";
- var left = parseInt(this._getNodeStyle(domNode, "padding-left", "0"), 10);
- left += parseInt(this._getNodeStyle(domNode, "border-left-width", "0"), 10);
- if (info.anchor === "right") {
- var right = parseInt(this._getNodeStyle(domNode, "padding-right", "0"), 10);
- right += parseInt(this._getNodeStyle(domNode, "border-right-width", "0"), 10);
- domNode.style.right = (domNode.ownerDocument.body.getBoundingClientRect().right - info.x + left + right) + "px";
- } else {
- domNode.style.left = (info.x - left) + "px";
- }
- var top = parseInt(this._getNodeStyle(domNode, "padding-top", "0"), 10);
- top += parseInt(this._getNodeStyle(domNode, "border-top-width", "0"), 10);
- domNode.style.top = (info.y - top) + "px";
- domNode.style.maxWidth = info.maxWidth + "px";
- domNode.style.maxHeight = info.maxHeight + "px";
- domNode.style.opacity = "1";
- domNode.style.visibility = "visible";
- if (autoHide) {
- var self = this;
- self._hideTimeout = setTimeout(function() {
- var opacity = parseFloat(self._getNodeStyle(domNode, "opacity", "1"));
- self._fadeTimeout = setInterval(function() {
- if (domNode.style.visibility === "visible" && opacity > 0) {
- opacity -= 0.1;
- domNode.style.opacity = opacity;
- return;
- }
- self.hide();
- }, 50);
- }, 5000);
- }
- },
- _getAnnotationContents: function(annotations) {
- if (annotations.length === 0) {
- return null;
- }
- var model = this._view.getModel(), annotation;
- var baseModel = model.getBaseModel ? model.getBaseModel() : model;
- function getText(start, end) {
- var textStart = baseModel.getLineStart(baseModel.getLineAtOffset(start));
- var textEnd = baseModel.getLineEnd(baseModel.getLineAtOffset(end), true);
- return baseModel.getText(textStart, textEnd);
- }
- var title;
- if (annotations.length === 1) {
- annotation = annotations[0];
- if (annotation.title) {
- title = annotation.title.replace(//g, ">");
- return "
" + annotation.html + " " + title + "
";
- } else {
- var newModel = new mProjectionTextModel.ProjectionTextModel(baseModel);
- var lineStart = baseModel.getLineStart(baseModel.getLineAtOffset(annotation.start));
- newModel.addProjection({start: annotation.end, end: newModel.getCharCount()});
- newModel.addProjection({start: 0, end: lineStart});
- return newModel;
- }
- } else {
- var tooltipHTML = "
Multiple annotations:
";
- for (var i = 0; i < annotations.length; i++) {
- annotation = annotations[i];
- title = annotation.title;
- if (!title) {
- title = getText(annotation.start, annotation.end);
- }
- title = title.replace(//g, ">");
- tooltipHTML += "
" + annotation.html + " " + title + "
";
- }
- return tooltipHTML;
- }
- },
- _getNodeStyle: function(node, prop, defaultValue) {
- var value;
- if (node) {
- value = node.style[prop];
- if (!value) {
- if (node.currentStyle) {
- var index = 0, p = prop;
- while ((index = p.indexOf("-", index)) !== -1) {
- p = p.substring(0, index) + p.substring(index + 1, index + 2).toUpperCase() + p.substring(index + 2);
- }
- value = node.currentStyle[p];
- } else {
- var css = node.ownerDocument.defaultView.getComputedStyle(node, null);
- value = css ? css.getPropertyValue(prop) : null;
- }
- }
- }
- return value || defaultValue;
- }
- };
- return {Tooltip: Tooltip};
-});
-/*******************************************************************************
- * @license
- * Copyright (c) 2010, 2011 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License v1.0
- * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
- * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
- *
- * Contributors:
- * Felipe Heidrich (IBM Corporation) - initial API and implementation
- * Silenio Quarti (IBM Corporation) - initial API and implementation
- * Mihai Sucan (Mozilla Foundation) - fix for Bug#334583 Bug#348471 Bug#349485 Bug#350595 Bug#360726 Bug#361180 Bug#362835 Bug#362428 Bug#362286 Bug#354270 Bug#361474 Bug#363945 Bug#366312 Bug#370584
- ******************************************************************************/
-
-/*global window document navigator setTimeout clearTimeout XMLHttpRequest define DOMException */
-
-define("orion/textview/textView", ['orion/textview/textModel', 'orion/textview/keyBinding', 'orion/textview/eventTarget'], function(mTextModel, mKeyBinding, mEventTarget) {
-
- /** @private */
- function addHandler(node, type, handler, capture) {
- if (typeof node.addEventListener === "function") {
- node.addEventListener(type, handler, capture === true);
- } else {
- node.attachEvent("on" + type, handler);
- }
- }
- /** @private */
- function removeHandler(node, type, handler, capture) {
- if (typeof node.removeEventListener === "function") {
- node.removeEventListener(type, handler, capture === true);
- } else {
- node.detachEvent("on" + type, handler);
- }
- }
- var userAgent = navigator.userAgent;
- var isIE;
- if (document.selection && window.ActiveXObject && /MSIE/.test(userAgent)) {
- isIE = document.documentMode ? document.documentMode : 7;
- }
- var isFirefox = parseFloat(userAgent.split("Firefox/")[1] || userAgent.split("Minefield/")[1]) || undefined;
- var isOpera = userAgent.indexOf("Opera") !== -1;
- var isChrome = userAgent.indexOf("Chrome") !== -1;
- var isSafari = userAgent.indexOf("Safari") !== -1 && !isChrome;
- var isWebkit = userAgent.indexOf("WebKit") !== -1;
- var isPad = userAgent.indexOf("iPad") !== -1;
- var isMac = navigator.platform.indexOf("Mac") !== -1;
- var isWindows = navigator.platform.indexOf("Win") !== -1;
- var isLinux = navigator.platform.indexOf("Linux") !== -1;
- var isW3CEvents = typeof window.document.documentElement.addEventListener === "function";
- var isRangeRects = (!isIE || isIE >= 9) && typeof window.document.createRange().getBoundingClientRect === "function";
- var platformDelimiter = isWindows ? "\r\n" : "\n";
-
- /**
- * Constructs a new Selection object.
- *
- * @class A Selection represents a range of selected text in the view.
- * @name orion.textview.Selection
- */
- function Selection (start, end, caret) {
- /**
- * The selection start offset.
- *
- * @name orion.textview.Selection#start
- */
- this.start = start;
- /**
- * The selection end offset.
- *
- * @name orion.textview.Selection#end
- */
- this.end = end;
- /** @private */
- this.caret = caret; //true if the start, false if the caret is at end
- }
- Selection.prototype = /** @lends orion.textview.Selection.prototype */ {
- /** @private */
- clone: function() {
- return new Selection(this.start, this.end, this.caret);
- },
- /** @private */
- collapse: function() {
- if (this.caret) {
- this.end = this.start;
- } else {
- this.start = this.end;
- }
- },
- /** @private */
- extend: function (offset) {
- if (this.caret) {
- this.start = offset;
- } else {
- this.end = offset;
- }
- if (this.start > this.end) {
- var tmp = this.start;
- this.start = this.end;
- this.end = tmp;
- this.caret = !this.caret;
- }
- },
- /** @private */
- setCaret: function(offset) {
- this.start = offset;
- this.end = offset;
- this.caret = false;
- },
- /** @private */
- getCaret: function() {
- return this.caret ? this.start : this.end;
- },
- /** @private */
- toString: function() {
- return "start=" + this.start + " end=" + this.end + (this.caret ? " caret is at start" : " caret is at end");
- },
- /** @private */
- isEmpty: function() {
- return this.start === this.end;
- },
- /** @private */
- equals: function(object) {
- return this.caret === object.caret && this.start === object.start && this.end === object.end;
- }
- };
- /**
- * @class This object describes the options for the text view.
- *
- * @name orion.textview.TextViewOptions
- *
- * @property {String|DOMElement} parent the parent element for the view, it can be either a DOM element or an ID for a DOM element.
- * @property {orion.textview.TextModel} [model] the text model for the view. If it is not set the view creates an empty {@link orion.textview.TextModel}.
- * @property {Boolean} [readonly=false] whether or not the view is read-only.
- * @property {Boolean} [fullSelection=true] whether or not the view is in full selection mode.
- * @property {Boolean} [sync=false] whether or not the view creation should be synchronous (if possible).
- * @property {Boolean} [expandTab=false] whether or not the tab key inserts white spaces.
- * @property {String|String[]} [stylesheet] one or more stylesheet for the view. Each stylesheet can be either a URI or a string containing the CSS rules.
- * @property {String} [themeClass] the CSS class for the view theming.
- * @property {Number} [tabSize] The number of spaces in a tab.
- */
- /**
- * Constructs a new text view.
- *
- * @param {orion.textview.TextViewOptions} options the view options.
- *
- * @class A TextView is a user interface for editing text.
- * @name orion.textview.TextView
- * @borrows orion.textview.EventTarget#addEventListener as #addEventListener
- * @borrows orion.textview.EventTarget#removeEventListener as #removeEventListener
- * @borrows orion.textview.EventTarget#dispatchEvent as #dispatchEvent
- */
- function TextView (options) {
- this._init(options);
- }
-
- TextView.prototype = /** @lends orion.textview.TextView.prototype */ {
- /**
- * Adds a ruler to the text view.
- *
- * @param {orion.textview.Ruler} ruler the ruler.
- */
- addRuler: function (ruler) {
- this._rulers.push(ruler);
- ruler.setView(this);
- this._createRuler(ruler);
- this._updatePage();
- },
- computeSize: function() {
- var w = 0, h = 0;
- var model = this._model, clientDiv = this._clientDiv;
- if (!clientDiv) { return {width: w, height: h}; }
- var clientWidth = clientDiv.style.width;
- /*
- * Feature in WekKit. Webkit limits the width of the lines
- * computed below to the width of the client div. This causes
- * the lines to be wrapped even though "pre" is set. The fix
- * is to set the width of the client div to a larger number
- * before computing the lines width. Note that this value is
- * reset to the appropriate value further down.
- */
- if (isWebkit) {
- clientDiv.style.width = (0x7FFFF).toString() + "px";
- }
- var lineCount = model.getLineCount();
- var document = this._frameDocument;
- for (var lineIndex=0; lineIndexThe supported coordinate spaces are:
- *
- *
"document" - relative to document, the origin is the top-left corner of first line
- *
"page" - relative to html page that contains the text view
- *
"view" - relative to text view, the origin is the top-left corner of the view container
- *
- *
- *
All methods in the view that take or return a position are in the document coordinate space.
- *
- * @param rect the rectangle to convert.
- * @param rect.x the x of the rectangle.
- * @param rect.y the y of the rectangle.
- * @param rect.width the width of the rectangle.
- * @param rect.height the height of the rectangle.
- * @param {String} from the source coordinate space.
- * @param {String} to the destination coordinate space.
- *
- * @see #getLocationAtOffset
- * @see #getOffsetAtLocation
- * @see #getTopPixel
- * @see #setTopPixel
- */
- convert: function(rect, from, to) {
- if (!this._clientDiv) { return; }
- var scroll = this._getScroll();
- var viewPad = this._getViewPadding();
- var frame = this._frame.getBoundingClientRect();
- var viewRect = this._viewDiv.getBoundingClientRect();
- switch(from) {
- case "document":
- if (rect.x !== undefined) {
- rect.x += - scroll.x + viewRect.left + viewPad.left;
- }
- if (rect.y !== undefined) {
- rect.y += - scroll.y + viewRect.top + viewPad.top;
- }
- break;
- case "page":
- if (rect.x !== undefined) {
- rect.x += - frame.left;
- }
- if (rect.y !== undefined) {
- rect.y += - frame.top;
- }
- break;
- }
- //At this point rect is in the widget coordinate space
- switch (to) {
- case "document":
- if (rect.x !== undefined) {
- rect.x += scroll.x - viewRect.left - viewPad.left;
- }
- if (rect.y !== undefined) {
- rect.y += scroll.y - viewRect.top - viewPad.top;
- }
- break;
- case "page":
- if (rect.x !== undefined) {
- rect.x += frame.left;
- }
- if (rect.y !== undefined) {
- rect.y += frame.top;
- }
- break;
- }
- return rect;
- },
- /**
- * Destroys the text view.
- *
- * Removes the view from the page and frees all resources created by the view.
- * Calling this function causes the "Destroy" event to be fire so that all components
- * attached to view can release their references.
- *
- *
- * @see #onDestroy
- */
- destroy: function() {
- /* Destroy rulers*/
- for (var i=0; i< this._rulers.length; i++) {
- this._rulers[i].setView(null);
- }
- this.rulers = null;
-
- /*
- * Note that when the frame is removed, the unload event is trigged
- * and the view contents and handlers is released properly by
- * destroyView().
- */
- this._destroyFrame();
-
- var e = {type: "Destroy"};
- this.onDestroy(e);
-
- this._parent = null;
- this._parentDocument = null;
- this._model = null;
- this._selection = null;
- this._doubleClickSelection = null;
- this._keyBindings = null;
- this._actions = null;
- },
- /**
- * Gives focus to the text view.
- */
- focus: function() {
- if (!this._clientDiv) { return; }
- /*
- * Feature in Chrome. When focus is called in the clientDiv without
- * setting selection the browser will set the selection to the first dom
- * element, which can be above the client area. When this happen the
- * browser also scrolls the window to show that element.
- * The fix is to call _updateDOMSelection() before calling focus().
- */
- this._updateDOMSelection();
- if (isPad) {
- this._textArea.focus();
- } else {
- if (isOpera) { this._clientDiv.blur(); }
- this._clientDiv.focus();
- }
- /*
- * Feature in Safari. When focus is called the browser selects the clientDiv
- * itself. The fix is to call _updateDOMSelection() after calling focus().
- */
- this._updateDOMSelection();
- },
- /**
- * Check if the text view has focus.
- *
- * @returns {Boolean} true if the text view has focus, otherwise false.
- */
- hasFocus: function() {
- return this._hasFocus;
- },
- /**
- * Returns all action names defined in the text view.
- *
- * There are two types of actions, the predefined actions of the view
- * and the actions added by application code.
- *
- *
- * The predefined actions are:
- *
- *
Navigation actions. These actions move the caret collapsing the selection.
- *
- *
"lineUp" - moves the caret up by one line
- *
"lineDown" - moves the caret down by one line
- *
"lineStart" - moves the caret to beginning of the current line
- *
"lineEnd" - moves the caret to end of the current line
- *
"charPrevious" - moves the caret to the previous character
- *
"charNext" - moves the caret to the next character
- *
"pageUp" - moves the caret up by one page
- *
"pageDown" - moves the caret down by one page
- *
"wordPrevious" - moves the caret to the previous word
- *
"wordNext" - moves the caret to the next word
- *
"textStart" - moves the caret to the beginning of the document
- *
"textEnd" - moves the caret to the end of the document
- *
- *
Selection actions. These actions move the caret extending the selection.
- *
- *
"selectLineUp" - moves the caret up by one line
- *
"selectLineDown" - moves the caret down by one line
- *
"selectLineStart" - moves the caret to beginning of the current line
- *
"selectLineEnd" - moves the caret to end of the current line
- *
"selectCharPrevious" - moves the caret to the previous character
- *
"selectCharNext" - moves the caret to the next character
- *
"selectPageUp" - moves the caret up by one page
- *
"selectPageDown" - moves the caret down by one page
- *
"selectWordPrevious" - moves the caret to the previous word
- *
"selectWordNext" - moves the caret to the next word
- *
"selectTextStart" - moves the caret to the beginning of the document
- *
"selectTextEnd" - moves the caret to the end of the document
- *
"selectAll" - selects the entire document
- *
- *
Edit actions. These actions modify the text view text
- *
- *
"deletePrevious" - deletes the character preceding the caret
- *
"deleteNext" - deletes the charecter following the caret
- *
"deleteWordPrevious" - deletes the word preceding the caret
- *
"deleteWordNext" - deletes the word following the caret
- *
"tab" - inserts a tab character at the caret
- *
"enter" - inserts a line delimiter at the caret
- *
- *
Clipboard actions.
- *
- *
"copy" - copies the selected text to the clipboard
- *
"cut" - copies the selected text to the clipboard and deletes the selection
- *
"paste" - replaces the selected text with the clipboard contents
- *
- *
- *
- *
- * @param {Boolean} [defaultAction=false] whether or not the predefined actions are included.
- * @returns {String[]} an array of action names defined in the text view.
- *
- * @see #invokeAction
- * @see #setAction
- * @see #setKeyBinding
- * @see #getKeyBindings
- */
- getActions: function (defaultAction) {
- var result = [];
- var actions = this._actions;
- for (var i = 0; i < actions.length; i++) {
- if (!defaultAction && actions[i].defaultHandler) { continue; }
- result.push(actions[i].name);
- }
- return result;
- },
- /**
- * Returns the bottom index.
- *
- * The bottom index is the line that is currently at the bottom of the view. This
- * line may be partially visible depending on the vertical scroll of the view. The parameter
- * fullyVisible determines whether to return only fully visible lines.
- *
- *
- * @param {Boolean} [fullyVisible=false] if true, returns the index of the last fully visible line. This
- * parameter is ignored if the view is not big enough to show one line.
- * @returns {Number} the index of the bottom line.
- *
- * @see #getTopIndex
- * @see #setTopIndex
- */
- getBottomIndex: function(fullyVisible) {
- if (!this._clientDiv) { return 0; }
- return this._getBottomIndex(fullyVisible);
- },
- /**
- * Returns the bottom pixel.
- *
- * The bottom pixel is the pixel position that is currently at
- * the bottom edge of the view. This position is relative to the
- * beginning of the document.
- *
- *
- * @returns {Number} the bottom pixel.
- *
- * @see #getTopPixel
- * @see #setTopPixel
- * @see #convert
- */
- getBottomPixel: function() {
- if (!this._clientDiv) { return 0; }
- return this._getScroll().y + this._getClientHeight();
- },
- /**
- * Returns the caret offset relative to the start of the document.
- *
- * @returns the caret offset relative to the start of the document.
- *
- * @see #setCaretOffset
- * @see #setSelection
- * @see #getSelection
- */
- getCaretOffset: function () {
- var s = this._getSelection();
- return s.getCaret();
- },
- /**
- * Returns the client area.
- *
- * The client area is the portion in pixels of the document that is visible. The
- * client area position is relative to the beginning of the document.
- *
- * The horizontal pixel is the pixel position that is currently at
- * the left edge of the view. This position is relative to the
- * beginning of the document.
- *
- *
- * @returns {Number} the horizontal pixel.
- *
- * @see #setHorizontalPixel
- * @see #convert
- */
- getHorizontalPixel: function() {
- if (!this._clientDiv) { return 0; }
- return this._getScroll().x;
- },
- /**
- * Returns all the key bindings associated to the given action name.
- *
- * @param {String} name the action name.
- * @returns {orion.textview.KeyBinding[]} the array of key bindings associated to the given action name.
- *
- * @see #setKeyBinding
- * @see #setAction
- */
- getKeyBindings: function (name) {
- var result = [];
- var keyBindings = this._keyBindings;
- for (var i = 0; i < keyBindings.length; i++) {
- if (keyBindings[i].name === name) {
- result.push(keyBindings[i].keyBinding);
- }
- }
- return result;
- },
- /**
- * Returns the line height for a given line index. Returns the default line
- * height if the line index is not specified.
- *
- * @param {Number} [lineIndex] the line index.
- * @returns {Number} the height of the line in pixels.
- *
- * @see #getLinePixel
- */
- getLineHeight: function(lineIndex) {
- if (!this._clientDiv) { return 0; }
- return this._getLineHeight();
- },
- /**
- * Returns the top pixel position of a given line index relative to the beginning
- * of the document.
- *
- * Clamps out of range indices.
- *
- *
- * @param {Number} lineIndex the line index.
- * @returns {Number} the pixel position of the line.
- *
- * @see #setTopPixel
- * @see #convert
- */
- getLinePixel: function(lineIndex) {
- if (!this._clientDiv) { return 0; }
- lineIndex = Math.min(Math.max(0, lineIndex), this._model.getLineCount());
- var lineHeight = this._getLineHeight();
- return lineHeight * lineIndex;
- },
- /**
- * Returns the {x, y} pixel location of the top-left corner of the character
- * bounding box at the specified offset in the document. The pixel location
- * is relative to the document.
- *
- * Clamps out of range offsets.
- *
- *
- * @param {Number} offset the character offset
- * @returns the {x, y} pixel location of the given offset.
- *
- * @see #getOffsetAtLocation
- * @see #convert
- */
- getLocationAtOffset: function(offset) {
- if (!this._clientDiv) { return {x: 0, y: 0}; }
- var model = this._model;
- offset = Math.min(Math.max(0, offset), model.getCharCount());
- var lineIndex = model.getLineAtOffset(offset);
- var scroll = this._getScroll();
- var viewRect = this._viewDiv.getBoundingClientRect();
- var viewPad = this._getViewPadding();
- var x = this._getOffsetToX(offset) + scroll.x - viewRect.left - viewPad.left;
- var y = this.getLinePixel(lineIndex);
- return {x: x, y: y};
- },
- /**
- * Returns the specified view options.
- *
- * The returned value is either a orion.textview.TextViewOptions or an option value. An option value is returned when only one string paremeter
- * is specified. A orion.textview.TextViewOptions is returned when there are no paremeters, or the parameters are a list of options names or a
- * orion.textview.TextViewOptions. All view options are returned when there no paremeters.
- *
- *
- * @param {String|orion.textview.TextViewOptions} [options] The options to return.
- * @return {Object|orion.textview.TextViewOptions} The requested options or an option value.
- *
- * @see #setOptions
- */
- getOptions: function() {
- var options;
- if (arguments.length === 0) {
- options = this._defaultOptions();
- } else if (arguments.length === 1) {
- var arg = arguments[0];
- if (typeof arg === "string") {
- return this._clone(this["_" + arg]);
- }
- options = arg;
- } else {
- options = {};
- for (var index in arguments) {
- if (arguments.hasOwnProperty(index)) {
- options[arguments[index]] = undefined;
- }
- }
- }
- for (var option in options) {
- if (options.hasOwnProperty(option)) {
- options[option] = this._clone(this["_" + option]);
- }
- }
- return options;
- },
- /**
- * Returns the text model of the text view.
- *
- * @returns {orion.textview.TextModel} the text model of the view.
- */
- getModel: function() {
- return this._model;
- },
- /**
- * Returns the character offset nearest to the given pixel location. The
- * pixel location is relative to the document.
- *
- * @param x the x of the location
- * @param y the y of the location
- * @returns the character offset at the given location.
- *
- * @see #getLocationAtOffset
- */
- getOffsetAtLocation: function(x, y) {
- if (!this._clientDiv) { return 0; }
- var scroll = this._getScroll();
- var viewRect = this._viewDiv.getBoundingClientRect();
- var viewPad = this._getViewPadding();
- var lineIndex = this._getYToLine(y - scroll.y);
- x += -scroll.x + viewRect.left + viewPad.left;
- var offset = this._getXToOffset(lineIndex, x);
- return offset;
- },
- /**
- * Get the view rulers.
- *
- * @returns the view rulers
- *
- * @see #addRuler
- */
- getRulers: function() {
- return this._rulers.slice(0);
- },
- /**
- * Returns the text view selection.
- *
- * The selection is defined by a start and end character offset relative to the
- * document. The character at end offset is not included in the selection.
- *
- *
- * @returns {orion.textview.Selection} the view selection
- *
- * @see #setSelection
- */
- getSelection: function () {
- var s = this._getSelection();
- return {start: s.start, end: s.end};
- },
- /**
- * Returns the text for the given range.
- *
- * The text does not include the character at the end offset.
- *
- *
- * @param {Number} [start=0] the start offset of text range.
- * @param {Number} [end=char count] the end offset of text range.
- *
- * @see #setText
- */
- getText: function(start, end) {
- var model = this._model;
- return model.getText(start, end);
- },
- /**
- * Returns the top index.
- *
- * The top index is the line that is currently at the top of the view. This
- * line may be partially visible depending on the vertical scroll of the view. The parameter
- * fullyVisible determines whether to return only fully visible lines.
- *
- *
- * @param {Boolean} [fullyVisible=false] if true, returns the index of the first fully visible line. This
- * parameter is ignored if the view is not big enough to show one line.
- * @returns {Number} the index of the top line.
- *
- * @see #getBottomIndex
- * @see #setTopIndex
- */
- getTopIndex: function(fullyVisible) {
- if (!this._clientDiv) { return 0; }
- return this._getTopIndex(fullyVisible);
- },
- /**
- * Returns the top pixel.
- *
- * The top pixel is the pixel position that is currently at
- * the top edge of the view. This position is relative to the
- * beginning of the document.
- *
- *
- * @returns {Number} the top pixel.
- *
- * @see #getBottomPixel
- * @see #setTopPixel
- * @see #convert
- */
- getTopPixel: function() {
- if (!this._clientDiv) { return 0; }
- return this._getScroll().y;
- },
- /**
- * Executes the action handler associated with the given name.
- *
- * The application defined action takes precedence over predefined actions unless
- * the defaultAction paramater is true.
- *
- *
- * If the application defined action returns false, the text view predefined
- * action is executed if present.
- *
- *
- * @param {String} name the action name.
- * @param {Boolean} [defaultAction] whether to always execute the predefined action.
- * @returns {Boolean} true if the action was executed.
- *
- * @see #setAction
- * @see #getActions
- */
- invokeAction: function (name, defaultAction) {
- if (!this._clientDiv) { return; }
- var actions = this._actions;
- for (var i = 0; i < actions.length; i++) {
- var a = actions[i];
- if (a.name && a.name === name) {
- if (!defaultAction && a.userHandler) {
- if (a.userHandler()) { return; }
- }
- if (a.defaultHandler) { return a.defaultHandler(); }
- return false;
- }
- }
- return false;
- },
- /**
- * Returns if the view is loaded.
- *
- * @returns {Boolean} true if the view is loaded.
- *
- * @see #onLoad
- */
- isLoaded: function () {
- return !!this._clientDiv;
- },
- /**
- * @class This is the event sent when the user right clicks or otherwise invokes the context menu of the view.
- *
- *
- * @name orion.textview.ContextMenuEvent
- *
- * @property {Number} x The pointer location on the x axis, relative to the document the user is editing.
- * @property {Number} y The pointer location on the y axis, relative to the document the user is editing.
- * @property {Number} screenX The pointer location on the x axis, relative to the screen. This is copied from the DOM contextmenu event.screenX property.
- * @property {Number} screenY The pointer location on the y axis, relative to the screen. This is copied from the DOM contextmenu event.screenY property.
- */
- /**
- * This event is sent when the user invokes the view context menu.
- *
- * @event
- * @param {orion.textview.ContextMenuEvent} contextMenuEvent the event
- */
- onContextMenu: function(contextMenuEvent) {
- return this.dispatchEvent(contextMenuEvent);
- },
- onDragStart: function(dragEvent) {
- return this.dispatchEvent(dragEvent);
- },
- onDrag: function(dragEvent) {
- return this.dispatchEvent(dragEvent);
- },
- onDragEnd: function(dragEvent) {
- return this.dispatchEvent(dragEvent);
- },
- onDragEnter: function(dragEvent) {
- return this.dispatchEvent(dragEvent);
- },
- onDragOver: function(dragEvent) {
- return this.dispatchEvent(dragEvent);
- },
- onDragLeave: function(dragEvent) {
- return this.dispatchEvent(dragEvent);
- },
- onDrop: function(dragEvent) {
- return this.dispatchEvent(dragEvent);
- },
- /**
- * @class This is the event sent when the text view is destroyed.
- *
- * @name orion.textview.DestroyEvent
- */
- /**
- * This event is sent when the text view has been destroyed.
- *
- * @event
- * @param {orion.textview.DestroyEvent} destroyEvent the event
- *
- * @see #destroy
- */
- onDestroy: function(destroyEvent) {
- return this.dispatchEvent(destroyEvent);
- },
- /**
- * @class This object is used to define style information for the text view.
- *
- * @name orion.textview.Style
- *
- * @property {String} styleClass A CSS class name.
- * @property {Object} style An object with CSS properties.
- * @property {String} tagName A DOM tag name.
- * @property {Object} attributes An object with DOM attributes.
- */
- /**
- * @class This object is used to style range.
- *
- * @name orion.textview.StyleRange
- *
- * @property {Number} start The start character offset, relative to the document, where the style should be applied.
- * @property {Number} end The end character offset (exclusive), relative to the document, where the style should be applied.
- * @property {orion.textview.Style} style The style for the range.
- */
- /**
- * @class This is the event sent when the text view needs the style information for a line.
- *
- * @name orion.textview.LineStyleEvent
- *
- * @property {orion.textview.TextView} textView The text view.
- * @property {Number} lineIndex The line index.
- * @property {String} lineText The line text.
- * @property {Number} lineStart The character offset, relative to document, of the first character in the line.
- * @property {orion.textview.Style} style The style for the entire line (output argument).
- * @property {orion.textview.StyleRange[]} ranges An array of style ranges for the line (output argument).
- */
- /**
- * This event is sent when the text view needs the style information for a line.
- *
- * @event
- * @param {orion.textview.LineStyleEvent} lineStyleEvent the event
- */
- onLineStyle: function(lineStyleEvent) {
- return this.dispatchEvent(lineStyleEvent);
- },
- /**
- * @class This is the event sent when the text view has loaded its contents.
- *
- * @name orion.textview.LoadEvent
- */
- /**
- * This event is sent when the text view has loaded its contents.
- *
- * @event
- * @param {orion.textview.LoadEvent} loadEvent the event
- */
- onLoad: function(loadEvent) {
- return this.dispatchEvent(loadEvent);
- },
- /**
- * @class This is the event sent when the text in the model has changed.
- *
- * @name orion.textview.ModelChangedEvent
- *
- * @property {Number} start The character offset in the model where the change has occurred.
- * @property {Number} removedCharCount The number of characters removed from the model.
- * @property {Number} addedCharCount The number of characters added to the model.
- * @property {Number} removedLineCount The number of lines removed from the model.
- * @property {Number} addedLineCount The number of lines added to the model.
- */
- /**
- * This event is sent when the text in the model has changed.
- *
- * @event
- * @param {orion.textview.ModelChangedEvent} modelChangedEvent the event
- */
- onModelChanged: function(modelChangedEvent) {
- return this.dispatchEvent(modelChangedEvent);
- },
- /**
- * @class This is the event sent when the text in the model is about to change.
- *
- * @name orion.textview.ModelChangingEvent
- *
- * @property {String} text The text that is about to be inserted in the model.
- * @property {Number} start The character offset in the model where the change will occur.
- * @property {Number} removedCharCount The number of characters being removed from the model.
- * @property {Number} addedCharCount The number of characters being added to the model.
- * @property {Number} removedLineCount The number of lines being removed from the model.
- * @property {Number} addedLineCount The number of lines being added to the model.
- */
- /**
- * This event is sent when the text in the model is about to change.
- *
- * @event
- * @param {orion.textview.ModelChangingEvent} modelChangingEvent the event
- */
- onModelChanging: function(modelChangingEvent) {
- return this.dispatchEvent(modelChangingEvent);
- },
- /**
- * @class This is the event sent when the text is modified by the text view.
- *
- * @name orion.textview.SelectionEvent
- *
- * @property {orion.textview.Selection} oldValue The old selection.
- * @property {orion.textview.Selection} newValue The new selection.
- */
- /**
- * This event is sent when the text view selection has changed.
- *
- * @event
- * @param {orion.textview.SelectionEvent} selectionEvent the event
- */
- onSelection: function(selectionEvent) {
- return this.dispatchEvent(selectionEvent);
- },
- /**
- * @class This is the event sent when the text view scrolls.
- *
- * @name orion.textview.ScrollEvent
- *
- * @property oldValue The old scroll {x,y}.
- * @property newValue The new scroll {x,y}.
- */
- /**
- * This event is sent when the text view scrolls vertically or horizontally.
- *
- * @event
- * @param {orion.textview.ScrollEvent} scrollEvent the event
- */
- onScroll: function(scrollEvent) {
- return this.dispatchEvent(scrollEvent);
- },
- /**
- * @class This is the event sent when the text is about to be modified by the text view.
- *
- * @name orion.textview.VerifyEvent
- *
- * @property {String} text The text being inserted.
- * @property {Number} start The start offset of the text range to be replaced.
- * @property {Number} end The end offset (exclusive) of the text range to be replaced.
- */
- /**
- * This event is sent when the text view is about to change text in the model.
- *
- * If the text is changed directly through the model API, this event
- * is not sent.
- *
- *
- * Listeners are allowed to change these parameters. Setting text to null
- * or undefined stops the change.
- *
- *
- * @event
- * @param {orion.textview.VerifyEvent} verifyEvent the event
- */
- onVerify: function(verifyEvent) {
- return this.dispatchEvent(verifyEvent);
- },
- /**
- * @class This is the event sent when the text view has unloaded its contents.
- *
- * @name orion.textview.UnloadEvent
- */
- /**
- * This event is sent when the text view has unloaded its contents.
- *
- * @event
- * @param {orion.textview.UnloadEvent} unloadEvent the event
- */
- onUnload: function(unloadEvent) {
- return this.dispatchEvent(unloadEvent);
- },
- /**
- * @class This is the event sent when the text view is focused.
- *
- * @name orion.textview.FocusEvent
- */
- /**
- * This event is sent when the text view is focused.
- *
- * @event
- * @param {orion.textview.FocusEvent} focusEvent the event
- */
- onFocus: function(focusEvent) {
- return this.dispatchEvent(focusEvent);
- },
- /**
- * @class This is the event sent when the text view goes out of focus.
- *
- * @name orion.textview.BlurEvent
- */
- /**
- * This event is sent when the text view goes out of focus.
- *
- * @event
- * @param {orion.textview.BlurEvent} blurEvent the event
- */
- onBlur: function(blurEvent) {
- return this.dispatchEvent(blurEvent);
- },
- /**
- * Redraws the entire view, including rulers.
- *
- * @see #redrawLines
- * @see #redrawRange
- * @see #setRedraw
- */
- redraw: function() {
- if (this._redrawCount > 0) { return; }
- var lineCount = this._model.getLineCount();
- var rulers = this.getRulers();
- for (var i = 0; i < rulers.length; i++) {
- this.redrawLines(0, lineCount, rulers[i]);
- }
- this.redrawLines(0, lineCount);
- },
- /**
- * Redraws the text in the given line range.
- *
- * The line at the end index is not redrawn.
- *
- *
- * @param {Number} [startLine=0] the start line
- * @param {Number} [endLine=line count] the end line
- *
- * @see #redraw
- * @see #redrawRange
- * @see #setRedraw
- */
- redrawLines: function(startLine, endLine, ruler) {
- if (this._redrawCount > 0) { return; }
- if (startLine === undefined) { startLine = 0; }
- if (endLine === undefined) { endLine = this._model.getLineCount(); }
- if (startLine === endLine) { return; }
- var div = this._clientDiv;
- if (!div) { return; }
- if (ruler) {
- var location = ruler.getLocation();//"left" or "right"
- var divRuler = location === "left" ? this._leftDiv : this._rightDiv;
- var cells = divRuler.firstChild.rows[0].cells;
- for (var i = 0; i < cells.length; i++) {
- if (cells[i].firstChild._ruler === ruler) {
- div = cells[i].firstChild;
- break;
- }
- }
- }
- if (ruler) {
- div.rulerChanged = true;
- }
- if (!ruler || ruler.getOverview() === "page") {
- var child = div.firstChild;
- while (child) {
- var lineIndex = child.lineIndex;
- if (startLine <= lineIndex && lineIndex < endLine) {
- child.lineChanged = true;
- }
- child = child.nextSibling;
- }
- }
- if (!ruler) {
- if (startLine <= this._maxLineIndex && this._maxLineIndex < endLine) {
- this._checkMaxLineIndex = this._maxLineIndex;
- this._maxLineIndex = -1;
- this._maxLineWidth = 0;
- }
- }
- this._queueUpdatePage();
- },
- /**
- * Redraws the text in the given range.
- *
- * The character at the end offset is not redrawn.
- *
- *
- * @param {Number} [start=0] the start offset of text range
- * @param {Number} [end=char count] the end offset of text range
- *
- * @see #redraw
- * @see #redrawLines
- * @see #setRedraw
- */
- redrawRange: function(start, end) {
- if (this._redrawCount > 0) { return; }
- var model = this._model;
- if (start === undefined) { start = 0; }
- if (end === undefined) { end = model.getCharCount(); }
- var startLine = model.getLineAtOffset(start);
- var endLine = model.getLineAtOffset(Math.max(start, end - 1)) + 1;
- this.redrawLines(startLine, endLine);
- },
- /**
- * Removes a ruler from the text view.
- *
- * @param {orion.textview.Ruler} ruler the ruler.
- */
- removeRuler: function (ruler) {
- var rulers = this._rulers;
- for (var i=0; i
- * If the action name is a predefined action, the given handler executes before
- * the default action handler. If the given handler returns true, the
- * default action handler is not called.
- *
- *
- * @param {String} name the action name.
- * @param {Function} handler the action handler.
- *
- * @see #getActions
- * @see #invokeAction
- */
- setAction: function(name, handler) {
- if (!name) { return; }
- var actions = this._actions;
- for (var i = 0; i < actions.length; i++) {
- var a = actions[i];
- if (a.name === name) {
- a.userHandler = handler;
- return;
- }
- }
- actions.push({name: name, userHandler: handler});
- },
- /**
- * Associates a key binding with the given action name. Any previous
- * association with the specified key binding is overwriten. If the
- * action name is null, the association is removed.
- *
- * @param {orion.textview.KeyBinding} keyBinding the key binding
- * @param {String} name the action
- */
- setKeyBinding: function(keyBinding, name) {
- var keyBindings = this._keyBindings;
- for (var i = 0; i < keyBindings.length; i++) {
- var kb = keyBindings[i];
- if (kb.keyBinding.equals(keyBinding)) {
- if (name) {
- kb.name = name;
- } else {
- if (kb.predefined) {
- kb.name = null;
- } else {
- var oldName = kb.name;
- keyBindings.splice(i, 1);
- var index = 0;
- while (index < keyBindings.length && oldName !== keyBindings[index].name) {
- index++;
- }
- if (index === keyBindings.length) {
- /*
- * Removing all the key bindings associated to an user action will cause
- * the user action to be removed. TextView predefined actions are never
- * removed (so they can be reinstalled in the future).
- *
- */
- var actions = this._actions;
- for (var j = 0; j < actions.length; j++) {
- if (actions[j].name === oldName) {
- if (!actions[j].defaultHandler) {
- actions.splice(j, 1);
- }
- }
- }
- }
- }
- }
- return;
- }
- }
- if (name) {
- keyBindings.push({keyBinding: keyBinding, name: name});
- }
- },
- /**
- * Sets the caret offset relative to the start of the document.
- *
- * @param {Number} caret the caret offset relative to the start of the document.
- * @param {Boolean} [show=true] if true, the view will scroll if needed to show the caret location.
- *
- * @see #getCaretOffset
- * @see #setSelection
- * @see #getSelection
- */
- setCaretOffset: function(offset, show) {
- var charCount = this._model.getCharCount();
- offset = Math.max(0, Math.min (offset, charCount));
- var selection = new Selection(offset, offset, false);
- this._setSelection (selection, show === undefined || show);
- },
- /**
- * Sets the horizontal pixel.
- *
- * The horizontal pixel is the pixel position that is currently at
- * the left edge of the view. This position is relative to the
- * beginning of the document.
- *
- * This can be used to improve the performance.
- *
- * When the flag is set to true,
- * the entire view is marked as needing to be redrawn.
- * Nested calls to this method are stacked.
- *
- *
- * @param {Boolean} redraw the new redraw state
- *
- * @see #redraw
- */
- setRedraw: function(redraw) {
- if (redraw) {
- if (--this._redrawCount === 0) {
- this.redraw();
- }
- } else {
- this._redrawCount++;
- }
- },
- /**
- * Sets the text model of the text view.
- *
- * @param {orion.textview.TextModel} model the text model of the view.
- */
- setModel: function(model) {
- if (!model) { return; }
- if (model === this._model) { return; }
- this._model.removeEventListener("Changing", this._modelListener.onChanging);
- this._model.removeEventListener("Changed", this._modelListener.onChanged);
- var oldLineCount = this._model.getLineCount();
- var oldCharCount = this._model.getCharCount();
- var newLineCount = model.getLineCount();
- var newCharCount = model.getCharCount();
- var newText = model.getText();
- var e = {
- type: "ModelChanging",
- text: newText,
- start: 0,
- removedCharCount: oldCharCount,
- addedCharCount: newCharCount,
- removedLineCount: oldLineCount,
- addedLineCount: newLineCount
- };
- this.onModelChanging(e);
- this._model = model;
- e = {
- type: "ModelChanged",
- start: 0,
- removedCharCount: oldCharCount,
- addedCharCount: newCharCount,
- removedLineCount: oldLineCount,
- addedLineCount: newLineCount
- };
- this.onModelChanged(e);
- this._model.addEventListener("Changing", this._modelListener.onChanging);
- this._model.addEventListener("Changed", this._modelListener.onChanged);
- this._reset();
- this._updatePage();
- },
- /**
- * Sets the view options for the view.
- *
- * @param {orion.textview.TextViewOptions} options the view options.
- *
- * @see #getOptions
- */
- setOptions: function (options) {
- var defaultOptions = this._defaultOptions();
- var recreate = false, option, created = this._clientDiv;
- if (created) {
- for (option in options) {
- if (options.hasOwnProperty(option)) {
- if (defaultOptions[option].recreate) {
- recreate = true;
- break;
- }
- }
- }
- }
- var changed = false;
- for (option in options) {
- if (options.hasOwnProperty(option)) {
- var newValue = options[option], oldValue = this["_" + option];
- if (this._compare(oldValue, newValue)) { continue; }
- changed = true;
- if (!recreate) {
- var update = defaultOptions[option].update;
- if (created && update) {
- if (update.call(this, newValue)) {
- recreate = true;
- }
- continue;
- }
- }
- this["_" + option] = this._clone(newValue);
- }
- }
- if (changed) {
- if (recreate) {
- var oldParent = this._frame.parentNode;
- oldParent.removeChild(this._frame);
- this._parent.appendChild(this._frame);
- }
- }
- },
- /**
- * Sets the text view selection.
- *
- * The selection is defined by a start and end character offset relative to the
- * document. The character at end offset is not included in the selection.
- *
- *
- * The caret is always placed at the end offset. The start offset can be
- * greater than the end offset to place the caret at the beginning of the
- * selection.
- *
- *
- * Clamps out of range offsets.
- *
- *
- * @param {Number} start the start offset of the selection
- * @param {Number} end the end offset of the selection
- * @param {Boolean} [show=true] if true, the view will scroll if needed to show the caret location.
- *
- * @see #getSelection
- */
- setSelection: function (start, end, show) {
- var caret = start > end;
- if (caret) {
- var tmp = start;
- start = end;
- end = tmp;
- }
- var charCount = this._model.getCharCount();
- start = Math.max(0, Math.min (start, charCount));
- end = Math.max(0, Math.min (end, charCount));
- var selection = new Selection(start, end, caret);
- this._setSelection(selection, show === undefined || show);
- },
- /**
- * Replaces the text in the given range with the given text.
- *
- * The character at the end offset is not replaced.
- *
- *
- * When both start and end parameters
- * are not specified, the text view places the caret at the beginning
- * of the document and scrolls to make it visible.
- *
- *
- * @param {String} text the new text.
- * @param {Number} [start=0] the start offset of text range.
- * @param {Number} [end=char count] the end offset of text range.
- *
- * @see #getText
- */
- setText: function (text, start, end) {
- var reset = start === undefined && end === undefined;
- if (start === undefined) { start = 0; }
- if (end === undefined) { end = this._model.getCharCount(); }
- this._modifyContent({text: text, start: start, end: end, _code: true}, !reset);
- if (reset) {
- this._columnX = -1;
- this._setSelection(new Selection (0, 0, false), true);
-
- /*
- * Bug in Firefox. For some reason, the caret does not show after the
- * view is refreshed. The fix is to toggle the contentEditable state and
- * force the clientDiv to loose and receive focus if it is focused.
- */
- if (isFirefox) {
- this._fixCaret();
- }
- }
- },
- /**
- * Sets the top index.
- *
- * The top index is the line that is currently at the top of the text view. This
- * line may be partially visible depending on the vertical scroll of the view.
- *
- *
- * @param {Number} topIndex the index of the top line.
- *
- * @see #getBottomIndex
- * @see #getTopIndex
- */
- setTopIndex: function(topIndex) {
- if (!this._clientDiv) { return; }
- var model = this._model;
- if (model.getCharCount() === 0) {
- return;
- }
- var lineCount = model.getLineCount();
- var lineHeight = this._getLineHeight();
- var pageSize = Math.max(1, Math.min(lineCount, Math.floor(this._getClientHeight () / lineHeight)));
- if (topIndex < 0) {
- topIndex = 0;
- } else if (topIndex > lineCount - pageSize) {
- topIndex = lineCount - pageSize;
- }
- var pixel = topIndex * lineHeight - this._getScroll().y;
- this._scrollView(0, pixel);
- },
- /**
- * Sets the top pixel.
- *
- * The top pixel is the pixel position that is currently at
- * the top edge of the view. This position is relative to the
- * beginning of the document.
- *
- *
- * @param {Number} pixel the top pixel.
- *
- * @see #getBottomPixel
- * @see #getTopPixel
- * @see #convert
- */
- setTopPixel: function(pixel) {
- if (!this._clientDiv) { return; }
- var lineHeight = this._getLineHeight();
- var clientHeight = this._getClientHeight();
- var lineCount = this._model.getLineCount();
- pixel = Math.min(Math.max(0, pixel), lineHeight * lineCount - clientHeight);
- this._scrollView(0, pixel - this._getScroll().y);
- },
- /**
- * Scrolls the selection into view if needed.
- *
- * @returns true if the view was scrolled.
- *
- * @see #getSelection
- * @see #setSelection
- */
- showSelection: function() {
- return this._showCaret(true);
- },
-
- /**************************************** Event handlers *********************************/
- _handleBodyMouseDown: function (e) {
- if (!e) { e = window.event; }
- if (isFirefox && e.which === 1) {
- this._clientDiv.contentEditable = false;
- (this._overlayDiv || this._clientDiv).draggable = true;
- this._ignoreBlur = true;
- }
-
- /*
- * Prevent clicks outside of the view from taking focus
- * away the view. Note that in Firefox and Opera clicking on the
- * scrollbar also take focus from the view. Other browsers
- * do not have this problem and stopping the click over the
- * scrollbar for them causes mouse capture problems.
- */
- var topNode = isOpera || (isFirefox && !this._overlayDiv) ? this._clientDiv : this._overlayDiv || this._viewDiv;
-
- var temp = e.target ? e.target : e.srcElement;
- while (temp) {
- if (topNode === temp) {
- return;
- }
- temp = temp.parentNode;
- }
- if (e.preventDefault) { e.preventDefault(); }
- if (e.stopPropagation){ e.stopPropagation(); }
- if (!isW3CEvents) {
- /* In IE 8 is not possible to prevent the default handler from running
- * during mouse down event using usual API. The workaround is to use
- * setCapture/releaseCapture.
- */
- topNode.setCapture();
- setTimeout(function() { topNode.releaseCapture(); }, 0);
- }
- },
- _handleBodyMouseUp: function (e) {
- if (!e) { e = window.event; }
- if (isFirefox && e.which === 1) {
- this._clientDiv.contentEditable = true;
- (this._overlayDiv || this._clientDiv).draggable = false;
-
- /*
- * Bug in Firefox. For some reason, Firefox stops showing the caret
- * in some cases. For example when the user cancels a drag operation
- * by pressing ESC. The fix is to detect that the drag operation was
- * cancelled, toggle the contentEditable state and force the clientDiv
- * to loose and receive focus if it is focused.
- */
- this._fixCaret();
- this._ignoreBlur = false;
- }
- },
- _handleBlur: function (e) {
- if (!e) { e = window.event; }
- if (this._ignoreBlur) { return; }
- this._hasFocus = false;
- /*
- * Bug in IE 8 and earlier. For some reason when text is deselected
- * the overflow selection at the end of some lines does not get redrawn.
- * The fix is to create a DOM element in the body to force a redraw.
- */
- if (isIE < 9) {
- if (!this._getSelection().isEmpty()) {
- var document = this._frameDocument;
- var child = document.createElement("DIV");
- var body = document.body;
- body.appendChild(child);
- body.removeChild(child);
- }
- }
- if (isFirefox || isIE) {
- if (this._selDiv1) {
- var color = isIE ? "transparent" : "#AFAFAF";
- this._selDiv1.style.background = color;
- this._selDiv2.style.background = color;
- this._selDiv3.style.background = color;
- }
- }
- if (!this._ignoreFocus) {
- this.onBlur({type: "Blur"});
- }
- },
- _handleContextMenu: function (e) {
- if (!e) { e = window.event; }
- if (isFirefox && this._lastMouseButton === 3) {
- // We need to update the DOM selection, because on
- // right-click the caret moves to the mouse location.
- // See bug 366312.
- var timeDiff = e.timeStamp - this._lastMouseTime;
- if (timeDiff <= this._clickTime) {
- this._updateDOMSelection();
- }
- }
- if (this.isListening("ContextMenu")) {
- var evt = this._createMouseEvent("ContextMenu", e);
- evt.screenX = e.screenX;
- evt.screenY = e.screenY;
- this.onContextMenu(evt);
- }
- if (e.preventDefault) { e.preventDefault(); }
- return false;
- },
- _handleCopy: function (e) {
- if (this._ignoreCopy) { return; }
- if (!e) { e = window.event; }
- if (this._doCopy(e)) {
- if (e.preventDefault) { e.preventDefault(); }
- return false;
- }
- },
- _handleCut: function (e) {
- if (!e) { e = window.event; }
- if (this._doCut(e)) {
- if (e.preventDefault) { e.preventDefault(); }
- return false;
- }
- },
- _handleDOMAttrModified: function (e) {
- if (!e) { e = window.event; }
- var ancestor = false;
- var parent = this._parent;
- while (parent) {
- if (parent === e.target) {
- ancestor = true;
- break;
- }
- parent = parent.parentNode;
- }
- if (!ancestor) { return; }
- var state = this._getVisible();
- if (state === "visible") {
- this._createView();
- } else if (state === "hidden") {
- this._destroyView();
- }
- },
- _handleDataModified: function(e) {
- this._startIME();
- },
- _handleDblclick: function (e) {
- if (!e) { e = window.event; }
- var time = e.timeStamp ? e.timeStamp : new Date().getTime();
- this._lastMouseTime = time;
- if (this._clickCount !== 2) {
- this._clickCount = 2;
- this._handleMouse(e);
- }
- },
- _handleDragStart: function (e) {
- if (!e) { e = window.event; }
- if (isFirefox) {
- var self = this;
- setTimeout(function() {
- self._clientDiv.contentEditable = true;
- self._clientDiv.draggable = false;
- self._ignoreBlur = false;
- }, 0);
- }
- if (this.isListening("DragStart") && this._dragOffset !== -1) {
- this._isMouseDown = false;
- this.onDragStart(this._createMouseEvent("DragStart", e));
- this._dragOffset = -1;
- } else {
- if (e.preventDefault) { e.preventDefault(); }
- return false;
- }
- },
- _handleDrag: function (e) {
- if (!e) { e = window.event; }
- if (this.isListening("Drag")) {
- this.onDrag(this._createMouseEvent("Drag", e));
- }
- },
- _handleDragEnd: function (e) {
- if (!e) { e = window.event; }
- this._dropTarget = false;
- this._dragOffset = -1;
- if (this.isListening("DragEnd")) {
- this.onDragEnd(this._createMouseEvent("DragEnd", e));
- }
- if (isFirefox) {
- this._fixCaret();
- /*
- * Bug in Firefox. For some reason, Firefox stops showing the caret when the
- * selection is dropped onto itself. The fix is to detected the case and
- * call fixCaret() a second time.
- */
- if (e.dataTransfer.dropEffect === "none" && !e.dataTransfer.mozUserCancelled) {
- this._fixCaret();
- }
- }
- },
- _handleDragEnter: function (e) {
- if (!e) { e = window.event; }
- var prevent = true;
- this._dropTarget = true;
- if (this.isListening("DragEnter")) {
- prevent = false;
- this.onDragEnter(this._createMouseEvent("DragEnter", e));
- }
- /*
- * Webkit will not send drop events if this event is not prevented, as spec in HTML5.
- * Firefox and IE do not follow this spec for contentEditable. Note that preventing this
- * event will result is loss of functionality (insertion mark, etc).
- */
- if (isWebkit || prevent) {
- if (e.preventDefault) { e.preventDefault(); }
- return false;
- }
- },
- _handleDragOver: function (e) {
- if (!e) { e = window.event; }
- var prevent = true;
- if (this.isListening("DragOver")) {
- prevent = false;
- this.onDragOver(this._createMouseEvent("DragOver", e));
- }
- /*
- * Webkit will not send drop events if this event is not prevented, as spec in HTML5.
- * Firefox and IE do not follow this spec for contentEditable. Note that preventing this
- * event will result is loss of functionality (insertion mark, etc).
- */
- if (isWebkit || prevent) {
- if (prevent) { e.dataTransfer.dropEffect = "none"; }
- if (e.preventDefault) { e.preventDefault(); }
- return false;
- }
- },
- _handleDragLeave: function (e) {
- if (!e) { e = window.event; }
- this._dropTarget = false;
- if (this.isListening("DragLeave")) {
- this.onDragLeave(this._createMouseEvent("DragLeave", e));
- }
- },
- _handleDrop: function (e) {
- if (!e) { e = window.event; }
- this._dropTarget = false;
- if (this.isListening("Drop")) {
- this.onDrop(this._createMouseEvent("Drop", e));
- }
- /*
- * This event must be prevented otherwise the user agent will modify
- * the DOM. Note that preventing the event on some user agents (i.e. IE)
- * indicates that the operation is cancelled. This causes the dropEffect to
- * be set to none in the dragend event causing the implementor to not execute
- * the code responsible by the move effect.
- */
- if (e.preventDefault) { e.preventDefault(); }
- return false;
- },
- _handleDocFocus: function (e) {
- if (!e) { e = window.event; }
- this._clientDiv.focus();
- },
- _handleFocus: function (e) {
- if (!e) { e = window.event; }
- this._hasFocus = true;
- /*
- * Feature in IE. The selection is not restored when the
- * view gets focus and the caret is always placed at the
- * beginning of the document. The fix is to update the DOM
- * selection during the focus event.
- */
- if (isIE) {
- this._updateDOMSelection();
- }
- if (isFirefox || isIE) {
- if (this._selDiv1) {
- var color = this._hightlightRGB;
- this._selDiv1.style.background = color;
- this._selDiv2.style.background = color;
- this._selDiv3.style.background = color;
- }
- }
- if (!this._ignoreFocus) {
- this.onFocus({type: "Focus"});
- }
- },
- _handleKeyDown: function (e) {
- if (!e) { e = window.event; }
- if (isPad) {
- if (e.keyCode === 8) {
- this._doBackspace({});
- e.preventDefault();
- }
- return;
- }
- switch (e.keyCode) {
- case 16: /* Shift */
- case 17: /* Control */
- case 18: /* Alt */
- case 91: /* Command */
- break;
- default:
- this._setLinksVisible(false);
- }
- if (e.keyCode === 229) {
- if (this._readonly) {
- if (e.preventDefault) { e.preventDefault(); }
- return false;
- }
- var startIME = true;
-
- /*
- * Bug in Safari. Some Control+key combinations send key events
- * with keyCode equals to 229. This is unexpected and causes the
- * view to start an IME composition. The fix is to ignore these
- * events.
- */
- if (isSafari && isMac) {
- if (e.ctrlKey) {
- startIME = false;
- }
- }
- if (startIME) {
- this._startIME();
- }
- } else {
- this._commitIME();
- }
- /*
- * Feature in Firefox. When a key is held down the browser sends
- * right number of keypress events but only one keydown. This is
- * unexpected and causes the view to only execute an action
- * just one time. The fix is to ignore the keydown event and
- * execute the actions from the keypress handler.
- * Note: This only happens on the Mac and Linux (Firefox 3.6).
- *
- * Feature in Opera. Opera sends keypress events even for non-printable
- * keys. The fix is to handle actions in keypress instead of keydown.
- */
- if (((isMac || isLinux) && isFirefox < 4) || isOpera) {
- this._keyDownEvent = e;
- return true;
- }
-
- if (this._doAction(e)) {
- if (e.preventDefault) {
- e.preventDefault();
- } else {
- e.cancelBubble = true;
- e.returnValue = false;
- e.keyCode = 0;
- }
- return false;
- }
- },
- _handleKeyPress: function (e) {
- if (!e) { e = window.event; }
- /*
- * Feature in Embedded WebKit. Embedded WekKit on Mac runs in compatibility mode and
- * generates key press events for these Unicode values (Function keys). This does not
- * happen in Safari or Chrome. The fix is to ignore these key events.
- */
- if (isMac && isWebkit) {
- if ((0xF700 <= e.keyCode && e.keyCode <= 0xF7FF) || e.keyCode === 13 || e.keyCode === 8) {
- if (e.preventDefault) { e.preventDefault(); }
- return false;
- }
- }
- if (((isMac || isLinux) && isFirefox < 4) || isOpera) {
- if (this._doAction(this._keyDownEvent)) {
- if (e.preventDefault) { e.preventDefault(); }
- return false;
- }
- }
- var ctrlKey = isMac ? e.metaKey : e.ctrlKey;
- if (e.charCode !== undefined) {
- if (ctrlKey) {
- switch (e.charCode) {
- /*
- * In Firefox and Safari if ctrl+v, ctrl+c ctrl+x is canceled
- * the clipboard events are not sent. The fix to allow
- * the browser to handles these key events.
- */
- case 99://c
- case 118://v
- case 120://x
- return true;
- }
- }
- }
- var ignore = false;
- if (isMac) {
- if (e.ctrlKey || e.metaKey) { ignore = true; }
- } else {
- if (isFirefox) {
- //Firefox clears the state mask when ALT GR generates input
- if (e.ctrlKey || e.altKey) { ignore = true; }
- } else {
- //IE and Chrome only send ALT GR when input is generated
- if (e.ctrlKey ^ e.altKey) { ignore = true; }
- }
- }
- if (!ignore) {
- var key = isOpera ? e.which : (e.charCode !== undefined ? e.charCode : e.keyCode);
- if (key > 31) {
- this._doContent(String.fromCharCode (key));
- if (e.preventDefault) { e.preventDefault(); }
- return false;
- }
- }
- },
- _handleKeyUp: function (e) {
- if (!e) { e = window.event; }
- var ctrlKey = isMac ? e.metaKey : e.ctrlKey;
- if (!ctrlKey) {
- this._setLinksVisible(false);
- }
- // don't commit for space (it happens during JP composition)
- if (e.keyCode === 13) {
- this._commitIME();
- }
- },
- _handleLinkClick: function (e) {
- if (!e) { e = window.event; }
- var ctrlKey = isMac ? e.metaKey : e.ctrlKey;
- if (!ctrlKey) {
- if (e.preventDefault) { e.preventDefault(); }
- return false;
- }
- },
- _handleLoad: function (e) {
- var state = this._getVisible();
- if (state === "visible" || (state === "hidden" && isWebkit)) {
- this._createView();
- }
- },
- _handleMouse: function (e) {
- var result = true;
- var target = this._frameWindow;
- if (isIE || (isFirefox && !this._overlayDiv)) { target = this._clientDiv; }
- if (this._overlayDiv) {
- if (this._hasFocus) {
- this._ignoreFocus = true;
- }
- var self = this;
- setTimeout(function () {
- self.focus();
- self._ignoreFocus = false;
- }, 0);
- }
- if (this._clickCount === 1) {
- result = this._setSelectionTo(e.clientX, e.clientY, e.shiftKey, !isOpera && this.isListening("DragStart"));
- if (result) { this._setGrab(target); }
- } else {
- /*
- * Feature in IE8 and older, the sequence of events in the IE8 event model
- * for a doule-click is:
- *
- * down
- * up
- * up
- * dblclick
- *
- * Given that the mouse down/up events are not balanced, it is not possible to
- * grab on mouse down and ungrab on mouse up. The fix is to grab on the first
- * mouse down and ungrab on mouse move when the button 1 is not set.
- */
- if (isW3CEvents) { this._setGrab(target); }
-
- this._doubleClickSelection = null;
- this._setSelectionTo(e.clientX, e.clientY, e.shiftKey);
- this._doubleClickSelection = this._getSelection();
- }
- return result;
- },
- _handleMouseDown: function (e) {
- if (!e) { e = window.event; }
- if (this.isListening("MouseDown")) {
- this.onMouseDown(this._createMouseEvent("MouseDown", e));
- }
- if (this._linksVisible) {
- var target = e.target || e.srcElement;
- if (target.tagName !== "A") {
- this._setLinksVisible(false);
- } else {
- return;
- }
- }
- this._commitIME();
-
- var button = e.which; // 1 - left, 2 - middle, 3 - right
- if (!button) {
- // if IE 8 or older
- if (e.button === 4) { button = 2; }
- if (e.button === 2) { button = 3; }
- if (e.button === 1) { button = 1; }
- }
-
- // For middle click we always need getTime(). See _getClipboardText().
- var time = button !== 2 && e.timeStamp ? e.timeStamp : new Date().getTime();
- var timeDiff = time - this._lastMouseTime;
- var deltaX = Math.abs(this._lastMouseX - e.clientX);
- var deltaY = Math.abs(this._lastMouseY - e.clientY);
- var sameButton = this._lastMouseButton === button;
- this._lastMouseX = e.clientX;
- this._lastMouseY = e.clientY;
- this._lastMouseTime = time;
- this._lastMouseButton = button;
-
- if (button === 1) {
- this._isMouseDown = true;
- if (sameButton && timeDiff <= this._clickTime && deltaX <= this._clickDist && deltaY <= this._clickDist) {
- this._clickCount++;
- } else {
- this._clickCount = 1;
- }
- if (this._handleMouse(e) && (isOpera || isChrome || (isFirefox && !this._overlayDiv))) {
- if (!this._hasFocus) {
- this.focus();
- }
- e.preventDefault();
- }
- }
- },
- _handleMouseOver: function (e) {
- if (!e) { e = window.event; }
- if (this.isListening("MouseOver")) {
- this.onMouseOver(this._createMouseEvent("MouseOver", e));
- }
- },
- _handleMouseOut: function (e) {
- if (!e) { e = window.event; }
- if (this.isListening("MouseOut")) {
- this.onMouseOut(this._createMouseEvent("MouseOut", e));
- }
- },
- _handleMouseMove: function (e) {
- if (!e) { e = window.event; }
- if (this.isListening("MouseMove")) {
- var topNode = this._overlayDiv || this._clientDiv;
- var temp = e.target ? e.target : e.srcElement;
- while (temp) {
- if (topNode === temp) {
- this.onMouseMove(this._createMouseEvent("MouseMove", e));
- break;
- }
- temp = temp.parentNode;
- }
- }
- if (this._dropTarget) {
- return;
- }
- /*
- * Bug in IE9. IE sends one mouse event when the user changes the text by
- * pasting or undo. These operations usually happen with the Ctrl key
- * down which causes the view to enter link mode. Link mode does not end
- * because there are no further events. The fix is to only enter link
- * mode when the coordinates of the mouse move event have changed.
- */
- var changed = this._linksVisible || this._lastMouseMoveX !== e.clientX || this._lastMouseMoveY !== e.clientY;
- this._lastMouseMoveX = e.clientX;
- this._lastMouseMoveY = e.clientY;
- this._setLinksVisible(changed && !this._isMouseDown && (isMac ? e.metaKey : e.ctrlKey));
-
- /*
- * Feature in IE8 and older, the sequence of events in the IE8 event model
- * for a doule-click is:
- *
- * down
- * up
- * up
- * dblclick
- *
- * Given that the mouse down/up events are not balanced, it is not possible to
- * grab on mouse down and ungrab on mouse up. The fix is to grab on the first
- * mouse down and ungrab on mouse move when the button 1 is not set.
- *
- * In order to detect double-click and drag gestures, it is necessary to send
- * a mouse down event from mouse move when the button is still down and isMouseDown
- * flag is not set.
- */
- if (!isW3CEvents) {
- if (e.button === 0) {
- this._setGrab(null);
- return true;
- }
- if (!this._isMouseDown && e.button === 1 && (this._clickCount & 1) !== 0) {
- this._clickCount = 2;
- return this._handleMouse(e, this._clickCount);
- }
- }
- if (!this._isMouseDown || this._dragOffset !== -1) {
- return;
- }
-
- var x = e.clientX;
- var y = e.clientY;
- if (isChrome) {
- if (e.currentTarget !== this._frameWindow) {
- var rect = this._frame.getBoundingClientRect();
- x -= rect.left;
- y -= rect.top;
- }
- }
- var viewPad = this._getViewPadding();
- var viewRect = this._viewDiv.getBoundingClientRect();
- var width = this._getClientWidth (), height = this._getClientHeight();
- var leftEdge = viewRect.left + viewPad.left;
- var topEdge = viewRect.top + viewPad.top;
- var rightEdge = viewRect.left + viewPad.left + width;
- var bottomEdge = viewRect.top + viewPad.top + height;
- var model = this._model;
- var caretLine = model.getLineAtOffset(this._getSelection().getCaret());
- if (y < topEdge && caretLine !== 0) {
- this._doAutoScroll("up", x, y - topEdge);
- } else if (y > bottomEdge && caretLine !== model.getLineCount() - 1) {
- this._doAutoScroll("down", x, y - bottomEdge);
- } else if (x < leftEdge) {
- this._doAutoScroll("left", x - leftEdge, y);
- } else if (x > rightEdge) {
- this._doAutoScroll("right", x - rightEdge, y);
- } else {
- this._endAutoScroll();
- this._setSelectionTo(x, y, true);
- /*
- * Feature in IE. IE does redraw the selection background right
- * away after the selection changes because of mouse move events.
- * The fix is to call getBoundingClientRect() on the
- * body element to force the selection to be redraw. Some how
- * calling this method forces a redraw.
- */
- if (isIE) {
- var body = this._frameDocument.body;
- body.getBoundingClientRect();
- }
- }
- },
- _createMouseEvent: function(type, e) {
- var scroll = this._getScroll();
- var viewRect = this._viewDiv.getBoundingClientRect();
- var viewPad = this._getViewPadding();
- var x = e.clientX + scroll.x - viewRect.left - viewPad.left;
- var y = e.clientY + scroll.y - viewRect.top;
- return {
- type: type,
- event: e,
- x: x,
- y: y
- };
- },
- _handleMouseUp: function (e) {
- if (!e) { e = window.event; }
- if (this.isListening("MouseUp")) {
- this.onMouseUp(this._createMouseEvent("MouseUp", e));
- }
- if (this._linksVisible) {
- return;
- }
- var left = e.which ? e.button === 0 : e.button === 1;
- if (left) {
- if (this._dragOffset !== -1) {
- var selection = this._getSelection();
- selection.extend(this._dragOffset);
- selection.collapse();
- this._setSelection(selection, true, true);
- this._dragOffset = -1;
- }
- this._isMouseDown = false;
- this._endAutoScroll();
-
- /*
- * Feature in IE8 and older, the sequence of events in the IE8 event model
- * for a doule-click is:
- *
- * down
- * up
- * up
- * dblclick
- *
- * Given that the mouse down/up events are not balanced, it is not possible to
- * grab on mouse down and ungrab on mouse up. The fix is to grab on the first
- * mouse down and ungrab on mouse move when the button 1 is not set.
- */
- if (isW3CEvents) { this._setGrab(null); }
-
- /*
- * Note that there cases when Firefox sets the DOM selection in mouse up.
- * This happens for example after a cancelled drag operation.
- *
- * Note that on Chrome and IE, the caret stops blicking if mouse up is
- * prevented.
- */
- if (isFirefox) {
- e.preventDefault();
- }
- }
- },
- _handleMouseWheel: function (e) {
- if (!e) { e = window.event; }
- var lineHeight = this._getLineHeight();
- var pixelX = 0, pixelY = 0;
- // Note: On the Mac the correct behaviour is to scroll by pixel.
- if (isFirefox) {
- var pixel;
- if (isMac) {
- pixel = e.detail * 3;
- } else {
- var limit = 256;
- pixel = Math.max(-limit, Math.min(limit, e.detail)) * lineHeight;
- }
- if (e.axis === e.HORIZONTAL_AXIS) {
- pixelX = pixel;
- } else {
- pixelY = pixel;
- }
- } else {
- //Webkit
- if (isMac) {
- /*
- * In Safari, the wheel delta is a multiple of 120. In order to
- * convert delta to pixel values, it is necessary to divide delta
- * by 40.
- *
- * In Chrome and Safari 5, the wheel delta depends on the type of the
- * mouse. In general, it is the pixel value for Mac mice and track pads,
- * but it is a multiple of 120 for other mice. There is no presise
- * way to determine if it is pixel value or a multiple of 120.
- *
- * Note that the current approach does not calculate the correct
- * pixel value for Mac mice when the delta is a multiple of 120.
- */
- var denominatorX = 40, denominatorY = 40;
- if (e.wheelDeltaX % 120 !== 0) { denominatorX = 1; }
- if (e.wheelDeltaY % 120 !== 0) { denominatorY = 1; }
- pixelX = -e.wheelDeltaX / denominatorX;
- if (-1 < pixelX && pixelX < 0) { pixelX = -1; }
- if (0 < pixelX && pixelX < 1) { pixelX = 1; }
- pixelY = -e.wheelDeltaY / denominatorY;
- if (-1 < pixelY && pixelY < 0) { pixelY = -1; }
- if (0 < pixelY && pixelY < 1) { pixelY = 1; }
- } else {
- pixelX = -e.wheelDeltaX;
- var linesToScroll = 8;
- pixelY = (-e.wheelDeltaY / 120 * linesToScroll) * lineHeight;
- }
- }
- /*
- * Feature in Safari. If the event target is removed from the DOM
- * safari stops smooth scrolling. The fix is keep the element target
- * in the DOM and remove it on a later time.
- *
- * Note: Using a timer is not a solution, because the timeout needs to
- * be at least as long as the gesture (which is too long).
- */
- if (isSafari) {
- var lineDiv = e.target;
- while (lineDiv && lineDiv.lineIndex === undefined) {
- lineDiv = lineDiv.parentNode;
- }
- this._mouseWheelLine = lineDiv;
- }
- var oldScroll = this._getScroll();
- this._scrollView(pixelX, pixelY);
- var newScroll = this._getScroll();
- if (isSafari) { this._mouseWheelLine = null; }
- if (oldScroll.x !== newScroll.x || oldScroll.y !== newScroll.y) {
- if (e.preventDefault) { e.preventDefault(); }
- return false;
- }
- },
- _handlePaste: function (e) {
- if (this._ignorePaste) { return; }
- if (!e) { e = window.event; }
- if (this._doPaste(e)) {
- if (isIE) {
- /*
- * Bug in IE,
- */
- var self = this;
- this._ignoreFocus = true;
- setTimeout(function() {
- self._updateDOMSelection();
- this._ignoreFocus = false;
- }, 0);
- }
- if (e.preventDefault) { e.preventDefault(); }
- return false;
- }
- },
- _handleResize: function (e) {
- if (!e) { e = window.event; }
- var element = this._frameDocument.documentElement;
- var newWidth = element.clientWidth;
- var newHeight = element.clientHeight;
- if (this._frameWidth !== newWidth || this._frameHeight !== newHeight) {
- this._frameWidth = newWidth;
- this._frameHeight = newHeight;
- /*
- * Feature in IE7. For some reason, sometimes Internet Explorer 7
- * returns incorrect values for element.getBoundingClientRect() when
- * inside a resize handler. The fix is to queue the work.
- */
- if (isIE < 9) {
- this._queueUpdatePage();
- } else {
- this._updatePage();
- }
- }
- },
- _handleRulerEvent: function (e) {
- if (!e) { e = window.event; }
- var target = e.target ? e.target : e.srcElement;
- var lineIndex = target.lineIndex;
- var element = target;
- while (element && !element._ruler) {
- if (lineIndex === undefined && element.lineIndex !== undefined) {
- lineIndex = element.lineIndex;
- }
- element = element.parentNode;
- }
- var ruler = element ? element._ruler : null;
- if (lineIndex === undefined && ruler && ruler.getOverview() === "document") {
- var buttonHeight = isPad ? 0 : 17;
- var clientHeight = this._getClientHeight ();
- var lineCount = this._model.getLineCount ();
- var viewPad = this._getViewPadding();
- var trackHeight = clientHeight + viewPad.top + viewPad.bottom - 2 * buttonHeight;
- lineIndex = Math.floor((e.clientY - buttonHeight) * lineCount / trackHeight);
- if (!(0 <= lineIndex && lineIndex < lineCount)) {
- lineIndex = undefined;
- }
- }
- if (ruler) {
- switch (e.type) {
- case "click":
- if (ruler.onClick) { ruler.onClick(lineIndex, e); }
- break;
- case "dblclick":
- if (ruler.onDblClick) { ruler.onDblClick(lineIndex, e); }
- break;
- case "mousemove":
- if (ruler.onMouseMove) { ruler.onMouseMove(lineIndex, e); }
- break;
- case "mouseover":
- if (ruler.onMouseOver) { ruler.onMouseOver(lineIndex, e); }
- break;
- case "mouseout":
- if (ruler.onMouseOut) { ruler.onMouseOut(lineIndex, e); }
- break;
- }
- }
- },
- _handleScroll: function () {
- var scroll = this._getScroll();
- var oldX = this._hScroll;
- var oldY = this._vScroll;
- if (oldX !== scroll.x || oldY !== scroll.y) {
- this._hScroll = scroll.x;
- this._vScroll = scroll.y;
- this._commitIME();
- this._updatePage(oldY === scroll.y);
- var e = {
- type: "Scroll",
- oldValue: {x: oldX, y: oldY},
- newValue: scroll
- };
- this.onScroll(e);
- }
- },
- _handleSelectStart: function (e) {
- if (!e) { e = window.event; }
- if (this._ignoreSelect) {
- if (e && e.preventDefault) { e.preventDefault(); }
- return false;
- }
- },
- _handleUnload: function (e) {
- if (!e) { e = window.event; }
- this._destroyView();
- },
- _handleInput: function (e) {
- var textArea = this._textArea;
- this._doContent(textArea.value);
- textArea.selectionStart = textArea.selectionEnd = 0;
- textArea.value = "";
- e.preventDefault();
- },
- _handleTextInput: function (e) {
- this._doContent(e.data);
- e.preventDefault();
- },
- _touchConvert: function (touch) {
- var rect = this._frame.getBoundingClientRect();
- var body = this._parentDocument.body;
- return {left: touch.clientX - rect.left - body.scrollLeft, top: touch.clientY - rect.top - body.scrollTop};
- },
- _handleTextAreaClick: function (e) {
- var pt = this._touchConvert(e);
- this._clickCount = 1;
- this._ignoreDOMSelection = false;
- this._setSelectionTo(pt.left, pt.top, false);
- var textArea = this._textArea;
- textArea.focus();
- },
- _handleTouchStart: function (e) {
- var touches = e.touches, touch, pt, sel;
- this._touchMoved = false;
- this._touchStartScroll = undefined;
- if (touches.length === 1) {
- touch = touches[0];
- var pageX = touch.pageX;
- var pageY = touch.pageY;
- this._touchStartX = pageX;
- this._touchStartY = pageY;
- this._touchStartTime = e.timeStamp;
- this._touchStartScroll = this._getScroll();
- sel = this._getSelection();
- pt = this._touchConvert(touches[0]);
- this._touchGesture = "none";
- if (!sel.isEmpty()) {
- if (this._hitOffset(sel.end, pt.left, pt.top)) {
- this._touchGesture = "extendEnd";
- } else if (this._hitOffset(sel.start, pt.left, pt.top)) {
- this._touchGesture = "extendStart";
- }
- }
- if (this._touchGesture === "none") {
- var textArea = this._textArea;
- textArea.value = "";
- textArea.style.left = "-1000px";
- textArea.style.top = "-1000px";
- textArea.style.width = "3000px";
- textArea.style.height = "3000px";
- }
- } else if (touches.length === 2) {
- this._touchGesture = "select";
- if (this._touchTimeout) {
- clearTimeout(this._touchTimeout);
- this._touchTimeout = null;
- }
- pt = this._touchConvert(touches[0]);
- var offset1 = this._getXToOffset(this._getYToLine(pt.top), pt.left);
- pt = this._touchConvert(touches[1]);
- var offset2 = this._getXToOffset(this._getYToLine(pt.top), pt.left);
- sel = this._getSelection();
- sel.setCaret(offset1);
- sel.extend(offset2);
- this._setSelection(sel, true, true);
- }
- //Cannot prevent to show magnifier
-// e.preventDefault();
- },
- _handleTouchMove: function (e) {
- this._touchMoved = true;
- var touches = e.touches, pt, sel;
- if (touches.length === 1) {
- var touch = touches[0];
- var pageX = touch.pageX;
- var pageY = touch.pageY;
- var deltaX = this._touchStartX - pageX;
- var deltaY = this._touchStartY - pageY;
- pt = this._touchConvert(touch);
- sel = this._getSelection();
- if (this._touchGesture === "none") {
- if ((e.timeStamp - this._touchStartTime) < 200 && (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5)) {
- this._touchGesture = "scroll";
- } else {
- this._touchGesture = "caret";
- }
- }
- if (this._touchGesture === "select") {
- if (this._hitOffset(sel.end, pt.left, pt.top)) {
- this._touchGesture = "extendEnd";
- } else if (this._hitOffset(sel.start, pt.left, pt.top)) {
- this._touchGesture = "extendStart";
- } else {
- this._touchGesture = "caret";
- }
- }
- switch (this._touchGesture) {
- case "scroll":
- this._touchStartX = pageX;
- this._touchStartY = pageY;
- this._scrollView(deltaX, deltaY);
- break;
- case "extendStart":
- case "extendEnd":
- this._clickCount = 1;
- var lineIndex = this._getYToLine(pt.top);
- var offset = this._getXToOffset(lineIndex, pt.left);
- sel.setCaret(this._touchGesture === "extendStart" ? sel.end : sel.start);
- sel.extend(offset);
- if (offset >= sel.end && this._touchGesture === "extendStart") {
- this._touchGesture = "extendEnd";
- }
- if (offset <= sel.start && this._touchGesture === "extendEnd") {
- this._touchGesture = "extendStart";
- }
- this._setSelection(sel, true, true);
- break;
- case "caret":
- this._setSelectionTo(pt.left, pt.top, false);
- break;
- }
- } else if (touches.length === 2) {
- pt = this._touchConvert(touches[0]);
- var offset1 = this._getXToOffset(this._getYToLine(pt.top), pt.left);
- pt = this._touchConvert(touches[1]);
- var offset2 = this._getXToOffset(this._getYToLine(pt.top), pt.left);
- sel = this._getSelection();
- sel.setCaret(offset1);
- sel.extend(offset2);
- this._setSelection(sel, true, true);
- }
- e.preventDefault();
- },
- _handleTouchEnd: function (e) {
- var self = this;
- if (!this._touchMoved) {
- if (e.touches.length === 0 && e.changedTouches.length === 1) {
- var touch = e.changedTouches[0];
- var pt = this._touchConvert(touch);
- var textArea = this._textArea;
- textArea.value = "";
- textArea.style.left = "-1000px";
- textArea.style.top = "-1000px";
- textArea.style.width = "3000px";
- textArea.style.height = "3000px";
- setTimeout(function() {
- self._clickCount = 1;
- self._ignoreDOMSelection = false;
- self._setSelectionTo(pt.left, pt.top, false);
- }, 300);
- }
- }
- if (e.touches.length === 0) {
- setTimeout(function() {
- var selection = self._getSelection();
- var text = self._model.getText(selection.start, selection.end);
- var textArea = self._textArea;
- textArea.value = text;
- textArea.selectionStart = 0;
- textArea.selectionEnd = text.length;
- if (!selection.isEmpty()) {
- var touchRect = self._touchDiv.getBoundingClientRect();
- var bounds = self._getOffsetBounds(selection.start);
- textArea.style.left = (touchRect.width / 2) + "px";
- textArea.style.top = ((bounds.top > 40 ? bounds.top - 30 : bounds.top + 30)) + "px";
- }
- }, 0);
- }
-// e.preventDefault();
- },
-
- /************************************ Actions ******************************************/
- _doAction: function (e) {
- var keyBindings = this._keyBindings;
- for (var i = 0; i < keyBindings.length; i++) {
- var kb = keyBindings[i];
- if (kb.keyBinding.match(e)) {
- if (kb.name) {
- var actions = this._actions;
- for (var j = 0; j < actions.length; j++) {
- var a = actions[j];
- if (a.name === kb.name) {
- if (a.userHandler) {
- if (!a.userHandler()) {
- if (a.defaultHandler) {
- a.defaultHandler();
- } else {
- return false;
- }
- }
- } else if (a.defaultHandler) {
- a.defaultHandler();
- }
- break;
- }
- }
- }
- return true;
- }
- }
- return false;
- },
- _doBackspace: function (args) {
- var selection = this._getSelection();
- if (selection.isEmpty()) {
- var model = this._model;
- var caret = selection.getCaret();
- var lineIndex = model.getLineAtOffset(caret);
- var lineStart = model.getLineStart(lineIndex);
- if (caret === lineStart) {
- if (lineIndex > 0) {
- selection.extend(model.getLineEnd(lineIndex - 1));
- }
- } else {
- var removeTab = false;
- if (this._expandTab && args.unit === "character" && (caret - lineStart) % this._tabSize === 0) {
- var lineText = model.getText(lineStart, caret);
- removeTab = !/[^ ]/.test(lineText); // Only spaces between line start and caret.
- }
- if (removeTab) {
- selection.extend(caret - this._tabSize);
- } else {
- selection.extend(this._getOffset(caret, args.unit, -1));
- }
- }
- }
- this._modifyContent({text: "", start: selection.start, end: selection.end}, true);
- return true;
- },
- _doContent: function (text) {
- var selection = this._getSelection();
- this._modifyContent({text: text, start: selection.start, end: selection.end, _ignoreDOMSelection: true}, true);
- },
- _doCopy: function (e) {
- var selection = this._getSelection();
- if (!selection.isEmpty()) {
- var text = this._getBaseText(selection.start, selection.end);
- return this._setClipboardText(text, e);
- }
- return true;
- },
- _doCursorNext: function (args) {
- if (!args.select) {
- if (this._clearSelection("next")) { return true; }
- }
- var model = this._model;
- var selection = this._getSelection();
- var caret = selection.getCaret();
- var lineIndex = model.getLineAtOffset(caret);
- if (caret === model.getLineEnd(lineIndex)) {
- if (lineIndex + 1 < model.getLineCount()) {
- selection.extend(model.getLineStart(lineIndex + 1));
- }
- } else {
- selection.extend(this._getOffset(caret, args.unit, 1));
- }
- if (!args.select) { selection.collapse(); }
- this._setSelection(selection, true);
- return true;
- },
- _doCursorPrevious: function (args) {
- if (!args.select) {
- if (this._clearSelection("previous")) { return true; }
- }
- var model = this._model;
- var selection = this._getSelection();
- var caret = selection.getCaret();
- var lineIndex = model.getLineAtOffset(caret);
- if (caret === model.getLineStart(lineIndex)) {
- if (lineIndex > 0) {
- selection.extend(model.getLineEnd(lineIndex - 1));
- }
- } else {
- selection.extend(this._getOffset(caret, args.unit, -1));
- }
- if (!args.select) { selection.collapse(); }
- this._setSelection(selection, true);
- return true;
- },
- _doCut: function (e) {
- var selection = this._getSelection();
- if (!selection.isEmpty()) {
- var text = this._getBaseText(selection.start, selection.end);
- this._doContent("");
- return this._setClipboardText(text, e);
- }
- return true;
- },
- _doDelete: function (args) {
- var selection = this._getSelection();
- if (selection.isEmpty()) {
- var model = this._model;
- var caret = selection.getCaret();
- var lineIndex = model.getLineAtOffset(caret);
- if (caret === model.getLineEnd (lineIndex)) {
- if (lineIndex + 1 < model.getLineCount()) {
- selection.extend(model.getLineStart(lineIndex + 1));
- }
- } else {
- selection.extend(this._getOffset(caret, args.unit, 1));
- }
- }
- this._modifyContent({text: "", start: selection.start, end: selection.end}, true);
- return true;
- },
- _doEnd: function (args) {
- var selection = this._getSelection();
- var model = this._model;
- if (args.ctrl) {
- selection.extend(model.getCharCount());
- } else {
- var lineIndex = model.getLineAtOffset(selection.getCaret());
- selection.extend(model.getLineEnd(lineIndex));
- }
- if (!args.select) { selection.collapse(); }
- this._setSelection(selection, true);
- return true;
- },
- _doEnter: function (args) {
- var model = this._model;
- var selection = this._getSelection();
- this._doContent(model.getLineDelimiter());
- if (args && args.noCursor) {
- selection.end = selection.start;
- this._setSelection(selection);
- }
- return true;
- },
- _doHome: function (args) {
- var selection = this._getSelection();
- var model = this._model;
- if (args.ctrl) {
- selection.extend(0);
- } else {
- var lineIndex = model.getLineAtOffset(selection.getCaret());
- selection.extend(model.getLineStart(lineIndex));
- }
- if (!args.select) { selection.collapse(); }
- this._setSelection(selection, true);
- return true;
- },
- _doLineDown: function (args) {
- var model = this._model;
- var selection = this._getSelection();
- var caret = selection.getCaret();
- var lineIndex = model.getLineAtOffset(caret);
- if (lineIndex + 1 < model.getLineCount()) {
- var scrollX = this._getScroll().x;
- var x = this._columnX;
- if (x === -1 || args.wholeLine || (args.select && isIE)) {
- var offset = args.wholeLine ? model.getLineEnd(lineIndex + 1) : caret;
- x = this._getOffsetToX(offset) + scrollX;
- }
- selection.extend(this._getXToOffset(lineIndex + 1, x - scrollX));
- if (!args.select) { selection.collapse(); }
- this._setSelection(selection, true, true);
- this._columnX = x;
- }
- return true;
- },
- _doLineUp: function (args) {
- var model = this._model;
- var selection = this._getSelection();
- var caret = selection.getCaret();
- var lineIndex = model.getLineAtOffset(caret);
- if (lineIndex > 0) {
- var scrollX = this._getScroll().x;
- var x = this._columnX;
- if (x === -1 || args.wholeLine || (args.select && isIE)) {
- var offset = args.wholeLine ? model.getLineStart(lineIndex - 1) : caret;
- x = this._getOffsetToX(offset) + scrollX;
- }
- selection.extend(this._getXToOffset(lineIndex - 1, x - scrollX));
- if (!args.select) { selection.collapse(); }
- this._setSelection(selection, true, true);
- this._columnX = x;
- }
- return true;
- },
- _doPageDown: function (args) {
- var model = this._model;
- var selection = this._getSelection();
- var caret = selection.getCaret();
- var caretLine = model.getLineAtOffset(caret);
- var lineCount = model.getLineCount();
- if (caretLine < lineCount - 1) {
- var scroll = this._getScroll();
- var clientHeight = this._getClientHeight();
- var lineHeight = this._getLineHeight();
- var lines = Math.floor(clientHeight / lineHeight);
- var scrollLines = Math.min(lineCount - caretLine - 1, lines);
- scrollLines = Math.max(1, scrollLines);
- var x = this._columnX;
- if (x === -1 || (args.select && isIE)) {
- x = this._getOffsetToX(caret) + scroll.x;
- }
- selection.extend(this._getXToOffset(caretLine + scrollLines, x - scroll.x));
- if (!args.select) { selection.collapse(); }
- var verticalMaximum = lineCount * lineHeight;
- var scrollOffset = scroll.y + scrollLines * lineHeight;
- if (scrollOffset + clientHeight > verticalMaximum) {
- scrollOffset = verticalMaximum - clientHeight;
- }
- this._setSelection(selection, true, true, scrollOffset - scroll.y);
- this._columnX = x;
- }
- return true;
- },
- _doPageUp: function (args) {
- var model = this._model;
- var selection = this._getSelection();
- var caret = selection.getCaret();
- var caretLine = model.getLineAtOffset(caret);
- if (caretLine > 0) {
- var scroll = this._getScroll();
- var clientHeight = this._getClientHeight();
- var lineHeight = this._getLineHeight();
- var lines = Math.floor(clientHeight / lineHeight);
- var scrollLines = Math.max(1, Math.min(caretLine, lines));
- var x = this._columnX;
- if (x === -1 || (args.select && isIE)) {
- x = this._getOffsetToX(caret) + scroll.x;
- }
- selection.extend(this._getXToOffset(caretLine - scrollLines, x - scroll.x));
- if (!args.select) { selection.collapse(); }
- var scrollOffset = Math.max(0, scroll.y - scrollLines * lineHeight);
- this._setSelection(selection, true, true, scrollOffset - scroll.y);
- this._columnX = x;
- }
- return true;
- },
- _doPaste: function(e) {
- var self = this;
- var result = this._getClipboardText(e, function(text) {
- if (text) {
- if (isLinux && self._lastMouseButton === 2) {
- var timeDiff = new Date().getTime() - self._lastMouseTime;
- if (timeDiff <= self._clickTime) {
- self._setSelectionTo(self._lastMouseX, self._lastMouseY);
- }
- }
- self._doContent(text);
- }
- });
- return result !== null;
- },
- _doScroll: function (args) {
- var type = args.type;
- var model = this._model;
- var lineCount = model.getLineCount();
- var clientHeight = this._getClientHeight();
- var lineHeight = this._getLineHeight();
- var verticalMaximum = lineCount * lineHeight;
- var verticalScrollOffset = this._getScroll().y;
- var pixel;
- switch (type) {
- case "textStart": pixel = 0; break;
- case "textEnd": pixel = verticalMaximum - clientHeight; break;
- case "pageDown": pixel = verticalScrollOffset + clientHeight; break;
- case "pageUp": pixel = verticalScrollOffset - clientHeight; break;
- case "centerLine":
- var selection = this._getSelection();
- var lineStart = model.getLineAtOffset(selection.start);
- var lineEnd = model.getLineAtOffset(selection.end);
- var selectionHeight = (lineEnd - lineStart + 1) * lineHeight;
- pixel = (lineStart * lineHeight) - (clientHeight / 2) + (selectionHeight / 2);
- break;
- }
- if (pixel !== undefined) {
- pixel = Math.min(Math.max(0, pixel), verticalMaximum - clientHeight);
- this._scrollView(0, pixel - verticalScrollOffset);
- }
- },
- _doSelectAll: function (args) {
- var model = this._model;
- var selection = this._getSelection();
- selection.setCaret(0);
- selection.extend(model.getCharCount());
- this._setSelection(selection, false);
- return true;
- },
- _doTab: function (args) {
- var text = "\t";
- if (this._expandTab) {
- var model = this._model;
- var caret = this._getSelection().getCaret();
- var lineIndex = model.getLineAtOffset(caret);
- var lineStart = model.getLineStart(lineIndex);
- var spaces = this._tabSize - ((caret - lineStart) % this._tabSize);
- text = (new Array(spaces + 1)).join(" ");
- }
- this._doContent(text);
- return true;
- },
-
- /************************************ Internals ******************************************/
- _applyStyle: function(style, node, reset) {
- if (reset) {
- var attrs = node.attributes;
- for (var i= attrs.length; i-->0;) {
- if (attrs[i].specified) {
- node.removeAttributeNode(attrs[i]);
- }
- }
- }
- if (!style) {
- return;
- }
- if (style.styleClass) {
- node.className = style.styleClass;
- }
- var properties = style.style;
- if (properties) {
- for (var s in properties) {
- if (properties.hasOwnProperty(s)) {
- node.style[s] = properties[s];
- }
- }
- }
- var attributes = style.attributes;
- if (attributes) {
- for (var a in attributes) {
- if (attributes.hasOwnProperty(a)) {
- node.setAttribute(a, attributes[a]);
- }
- }
- }
- },
- _autoScroll: function () {
- var selection = this._getSelection();
- var line;
- var x = this._autoScrollX;
- if (this._autoScrollDir === "up" || this._autoScrollDir === "down") {
- var scroll = this._autoScrollY / this._getLineHeight();
- scroll = scroll < 0 ? Math.floor(scroll) : Math.ceil(scroll);
- line = this._model.getLineAtOffset(selection.getCaret());
- line = Math.max(0, Math.min(this._model.getLineCount() - 1, line + scroll));
- } else if (this._autoScrollDir === "left" || this._autoScrollDir === "right") {
- line = this._getYToLine(this._autoScrollY);
- x += this._getOffsetToX(selection.getCaret());
- }
- selection.extend(this._getXToOffset(line, x));
- this._setSelection(selection, true);
- },
- _autoScrollTimer: function () {
- this._autoScroll();
- var self = this;
- this._autoScrollTimerID = setTimeout(function () {self._autoScrollTimer();}, this._AUTO_SCROLL_RATE);
- },
- _calculateLineHeight: function() {
- var parent = this._clientDiv;
- var document = this._frameDocument;
- var c = " ";
- var line = document.createElement("DIV");
- line.style.position = "fixed";
- line.style.left = "-1000px";
- var span1 = document.createElement("SPAN");
- span1.appendChild(document.createTextNode(c));
- line.appendChild(span1);
- var span2 = document.createElement("SPAN");
- span2.style.fontStyle = "italic";
- span2.appendChild(document.createTextNode(c));
- line.appendChild(span2);
- var span3 = document.createElement("SPAN");
- span3.style.fontWeight = "bold";
- span3.appendChild(document.createTextNode(c));
- line.appendChild(span3);
- var span4 = document.createElement("SPAN");
- span4.style.fontWeight = "bold";
- span4.style.fontStyle = "italic";
- span4.appendChild(document.createTextNode(c));
- line.appendChild(span4);
- parent.appendChild(line);
- var lineRect = line.getBoundingClientRect();
- var spanRect1 = span1.getBoundingClientRect();
- var spanRect2 = span2.getBoundingClientRect();
- var spanRect3 = span3.getBoundingClientRect();
- var spanRect4 = span4.getBoundingClientRect();
- var h1 = spanRect1.bottom - spanRect1.top;
- var h2 = spanRect2.bottom - spanRect2.top;
- var h3 = spanRect3.bottom - spanRect3.top;
- var h4 = spanRect4.bottom - spanRect4.top;
- var fontStyle = 0;
- var lineHeight = lineRect.bottom - lineRect.top;
- if (h2 > h1) {
- fontStyle = 1;
- }
- if (h3 > h2) {
- fontStyle = 2;
- }
- if (h4 > h3) {
- fontStyle = 3;
- }
- var style;
- if (fontStyle !== 0) {
- style = {style: {}};
- if ((fontStyle & 1) !== 0) {
- style.style.fontStyle = "italic";
- }
- if ((fontStyle & 2) !== 0) {
- style.style.fontWeight = "bold";
- }
- }
- this._largestFontStyle = style;
- parent.removeChild(line);
- return lineHeight;
- },
- _calculatePadding: function() {
- var document = this._frameDocument;
- var parent = this._clientDiv;
- var pad = this._getPadding(this._viewDiv);
- var div1 = document.createElement("DIV");
- div1.style.position = "fixed";
- div1.style.left = "-1000px";
- div1.style.paddingLeft = pad.left + "px";
- div1.style.paddingTop = pad.top + "px";
- div1.style.paddingRight = pad.right + "px";
- div1.style.paddingBottom = pad.bottom + "px";
- div1.style.width = "100px";
- div1.style.height = "100px";
- var div2 = document.createElement("DIV");
- div2.style.width = "100%";
- div2.style.height = "100%";
- div1.appendChild(div2);
- parent.appendChild(div1);
- var rect1 = div1.getBoundingClientRect();
- var rect2 = div2.getBoundingClientRect();
- parent.removeChild(div1);
- pad = {
- left: rect2.left - rect1.left,
- top: rect2.top - rect1.top,
- right: rect1.right - rect2.right,
- bottom: rect1.bottom - rect2.bottom
- };
- return pad;
- },
- _clearSelection: function (direction) {
- var selection = this._getSelection();
- if (selection.isEmpty()) { return false; }
- if (direction === "next") {
- selection.start = selection.end;
- } else {
- selection.end = selection.start;
- }
- this._setSelection(selection, true);
- return true;
- },
- _clone: function (obj) {
- /*Note that this code only works because of the limited types used in TextViewOptions */
- if (obj instanceof Array) {
- return obj.slice(0);
- }
- return obj;
- },
- _compare: function (s1, s2) {
- if (s1 === s2) { return true; }
- if (s1 && !s2 || !s1 && s2) { return false; }
- if ((s1 && s1.constructor === String) || (s2 && s2.constructor === String)) { return false; }
- if (s1 instanceof Array || s2 instanceof Array) {
- if (!(s1 instanceof Array && s2 instanceof Array)) { return false; }
- if (s1.length !== s2.length) { return false; }
- for (var i = 0; i < s1.length; i++) {
- if (!this._compare(s1[i], s2[i])) {
- return false;
- }
- }
- return true;
- }
- if (!(s1 instanceof Object) || !(s2 instanceof Object)) { return false; }
- var p;
- for (p in s1) {
- if (s1.hasOwnProperty(p)) {
- if (!s2.hasOwnProperty(p)) { return false; }
- if (!this._compare(s1[p], s2[p])) {return false; }
- }
- }
- for (p in s2) {
- if (!s1.hasOwnProperty(p)) { return false; }
- }
- return true;
- },
- _commitIME: function () {
- if (this._imeOffset === -1) { return; }
- // make the state of the IME match the state the view expects it be in
- // when the view commits the text and IME also need to be committed
- // this can be accomplished by changing the focus around
- this._scrollDiv.focus();
- this._clientDiv.focus();
-
- var model = this._model;
- var lineIndex = model.getLineAtOffset(this._imeOffset);
- var lineStart = model.getLineStart(lineIndex);
- var newText = this._getDOMText(lineIndex);
- var oldText = model.getLine(lineIndex);
- var start = this._imeOffset - lineStart;
- var end = start + newText.length - oldText.length;
- if (start !== end) {
- var insertText = newText.substring(start, end);
- this._doContent(insertText);
- }
- this._imeOffset = -1;
- },
- _convertDelimiter: function (text, addTextFunc, addDelimiterFunc) {
- var cr = 0, lf = 0, index = 0, length = text.length;
- while (index < length) {
- if (cr !== -1 && cr <= index) { cr = text.indexOf("\r", index); }
- if (lf !== -1 && lf <= index) { lf = text.indexOf("\n", index); }
- var start = index, end;
- if (lf === -1 && cr === -1) {
- addTextFunc(text.substring(index));
- break;
- }
- if (cr !== -1 && lf !== -1) {
- if (cr + 1 === lf) {
- end = cr;
- index = lf + 1;
- } else {
- end = cr < lf ? cr : lf;
- index = (cr < lf ? cr : lf) + 1;
- }
- } else if (cr !== -1) {
- end = cr;
- index = cr + 1;
- } else {
- end = lf;
- index = lf + 1;
- }
- addTextFunc(text.substring(start, end));
- addDelimiterFunc();
- }
- },
- _createActions: function () {
- var KeyBinding = mKeyBinding.KeyBinding;
- //no duplicate keybindings
- var bindings = this._keyBindings = [];
-
- // Cursor Navigation
- bindings.push({name: "lineUp", keyBinding: new KeyBinding(38), predefined: true});
- bindings.push({name: "lineDown", keyBinding: new KeyBinding(40), predefined: true});
- bindings.push({name: "charPrevious", keyBinding: new KeyBinding(37), predefined: true});
- bindings.push({name: "charNext", keyBinding: new KeyBinding(39), predefined: true});
- if (isMac) {
- bindings.push({name: "scrollPageUp", keyBinding: new KeyBinding(33), predefined: true});
- bindings.push({name: "scrollPageDown", keyBinding: new KeyBinding(34), predefined: true});
- bindings.push({name: "pageUp", keyBinding: new KeyBinding(33, null, null, true), predefined: true});
- bindings.push({name: "pageDown", keyBinding: new KeyBinding(34, null, null, true), predefined: true});
- bindings.push({name: "lineStart", keyBinding: new KeyBinding(37, true), predefined: true});
- bindings.push({name: "lineEnd", keyBinding: new KeyBinding(39, true), predefined: true});
- bindings.push({name: "wordPrevious", keyBinding: new KeyBinding(37, null, null, true), predefined: true});
- bindings.push({name: "wordNext", keyBinding: new KeyBinding(39, null, null, true), predefined: true});
- bindings.push({name: "scrollTextStart", keyBinding: new KeyBinding(36), predefined: true});
- bindings.push({name: "scrollTextEnd", keyBinding: new KeyBinding(35), predefined: true});
- bindings.push({name: "textStart", keyBinding: new KeyBinding(38, true), predefined: true});
- bindings.push({name: "textEnd", keyBinding: new KeyBinding(40, true), predefined: true});
- bindings.push({name: "scrollPageUp", keyBinding: new KeyBinding(38, null, null, null, true), predefined: true});
- bindings.push({name: "scrollPageDown", keyBinding: new KeyBinding(40, null, null, null, true), predefined: true});
- bindings.push({name: "lineStart", keyBinding: new KeyBinding(37, null, null, null, true), predefined: true});
- bindings.push({name: "lineEnd", keyBinding: new KeyBinding(39, null, null, null, true), predefined: true});
- //TODO These two actions should be changed to paragraph start and paragraph end when word wrap is implemented
- bindings.push({name: "lineStart", keyBinding: new KeyBinding(38, null, null, true), predefined: true});
- bindings.push({name: "lineEnd", keyBinding: new KeyBinding(40, null, null, true), predefined: true});
- } else {
- bindings.push({name: "pageUp", keyBinding: new KeyBinding(33), predefined: true});
- bindings.push({name: "pageDown", keyBinding: new KeyBinding(34), predefined: true});
- bindings.push({name: "lineStart", keyBinding: new KeyBinding(36), predefined: true});
- bindings.push({name: "lineEnd", keyBinding: new KeyBinding(35), predefined: true});
- bindings.push({name: "wordPrevious", keyBinding: new KeyBinding(37, true), predefined: true});
- bindings.push({name: "wordNext", keyBinding: new KeyBinding(39, true), predefined: true});
- bindings.push({name: "textStart", keyBinding: new KeyBinding(36, true), predefined: true});
- bindings.push({name: "textEnd", keyBinding: new KeyBinding(35, true), predefined: true});
- }
- if (isFirefox && isLinux) {
- bindings.push({name: "lineUp", keyBinding: new KeyBinding(38, true), predefined: true});
- bindings.push({name: "lineDown", keyBinding: new KeyBinding(40, true), predefined: true});
- }
-
- // Select Cursor Navigation
- bindings.push({name: "selectLineUp", keyBinding: new KeyBinding(38, null, true), predefined: true});
- bindings.push({name: "selectLineDown", keyBinding: new KeyBinding(40, null, true), predefined: true});
- bindings.push({name: "selectCharPrevious", keyBinding: new KeyBinding(37, null, true), predefined: true});
- bindings.push({name: "selectCharNext", keyBinding: new KeyBinding(39, null, true), predefined: true});
- bindings.push({name: "selectPageUp", keyBinding: new KeyBinding(33, null, true), predefined: true});
- bindings.push({name: "selectPageDown", keyBinding: new KeyBinding(34, null, true), predefined: true});
- if (isMac) {
- bindings.push({name: "selectLineStart", keyBinding: new KeyBinding(37, true, true), predefined: true});
- bindings.push({name: "selectLineEnd", keyBinding: new KeyBinding(39, true, true), predefined: true});
- bindings.push({name: "selectWordPrevious", keyBinding: new KeyBinding(37, null, true, true), predefined: true});
- bindings.push({name: "selectWordNext", keyBinding: new KeyBinding(39, null, true, true), predefined: true});
- bindings.push({name: "selectTextStart", keyBinding: new KeyBinding(36, null, true), predefined: true});
- bindings.push({name: "selectTextEnd", keyBinding: new KeyBinding(35, null, true), predefined: true});
- bindings.push({name: "selectTextStart", keyBinding: new KeyBinding(38, true, true), predefined: true});
- bindings.push({name: "selectTextEnd", keyBinding: new KeyBinding(40, true, true), predefined: true});
- bindings.push({name: "selectLineStart", keyBinding: new KeyBinding(37, null, true, null, true), predefined: true});
- bindings.push({name: "selectLineEnd", keyBinding: new KeyBinding(39, null, true, null, true), predefined: true});
- //TODO These two actions should be changed to select paragraph start and select paragraph end when word wrap is implemented
- bindings.push({name: "selectLineStart", keyBinding: new KeyBinding(38, null, true, true), predefined: true});
- bindings.push({name: "selectLineEnd", keyBinding: new KeyBinding(40, null, true, true), predefined: true});
- } else {
- if (isLinux) {
- bindings.push({name: "selectWholeLineUp", keyBinding: new KeyBinding(38, true, true), predefined: true});
- bindings.push({name: "selectWholeLineDown", keyBinding: new KeyBinding(40, true, true), predefined: true});
- }
- bindings.push({name: "selectLineStart", keyBinding: new KeyBinding(36, null, true), predefined: true});
- bindings.push({name: "selectLineEnd", keyBinding: new KeyBinding(35, null, true), predefined: true});
- bindings.push({name: "selectWordPrevious", keyBinding: new KeyBinding(37, true, true), predefined: true});
- bindings.push({name: "selectWordNext", keyBinding: new KeyBinding(39, true, true), predefined: true});
- bindings.push({name: "selectTextStart", keyBinding: new KeyBinding(36, true, true), predefined: true});
- bindings.push({name: "selectTextEnd", keyBinding: new KeyBinding(35, true, true), predefined: true});
- }
-
- //Misc
- bindings.push({name: "deletePrevious", keyBinding: new KeyBinding(8), predefined: true});
- bindings.push({name: "deletePrevious", keyBinding: new KeyBinding(8, null, true), predefined: true});
- bindings.push({name: "deleteNext", keyBinding: new KeyBinding(46), predefined: true});
- bindings.push({name: "deleteWordPrevious", keyBinding: new KeyBinding(8, true), predefined: true});
- bindings.push({name: "deleteWordPrevious", keyBinding: new KeyBinding(8, true, true), predefined: true});
- bindings.push({name: "deleteWordNext", keyBinding: new KeyBinding(46, true), predefined: true});
- bindings.push({name: "tab", keyBinding: new KeyBinding(9), predefined: true});
- bindings.push({name: "enter", keyBinding: new KeyBinding(13), predefined: true});
- bindings.push({name: "enter", keyBinding: new KeyBinding(13, null, true), predefined: true});
- bindings.push({name: "selectAll", keyBinding: new KeyBinding('a', true), predefined: true});
- if (isMac) {
- bindings.push({name: "deleteNext", keyBinding: new KeyBinding(46, null, true), predefined: true});
- bindings.push({name: "deleteWordPrevious", keyBinding: new KeyBinding(8, null, null, true), predefined: true});
- bindings.push({name: "deleteWordNext", keyBinding: new KeyBinding(46, null, null, true), predefined: true});
- }
-
- /*
- * Feature in IE/Chrome: prevent ctrl+'u', ctrl+'i', and ctrl+'b' from applying styles to the text.
- *
- * Note that Chrome applies the styles on the Mac with Ctrl instead of Cmd.
- */
- if (!isFirefox) {
- var isMacChrome = isMac && isChrome;
- bindings.push({name: null, keyBinding: new KeyBinding('u', !isMacChrome, false, false, isMacChrome), predefined: true});
- bindings.push({name: null, keyBinding: new KeyBinding('i', !isMacChrome, false, false, isMacChrome), predefined: true});
- bindings.push({name: null, keyBinding: new KeyBinding('b', !isMacChrome, false, false, isMacChrome), predefined: true});
- }
-
- if (isFirefox) {
- bindings.push({name: "copy", keyBinding: new KeyBinding(45, true), predefined: true});
- bindings.push({name: "paste", keyBinding: new KeyBinding(45, null, true), predefined: true});
- bindings.push({name: "cut", keyBinding: new KeyBinding(46, null, true), predefined: true});
- }
-
- // Add the emacs Control+ ... key bindings.
- if (isMac) {
- bindings.push({name: "lineStart", keyBinding: new KeyBinding("a", false, false, false, true), predefined: true});
- bindings.push({name: "lineEnd", keyBinding: new KeyBinding("e", false, false, false, true), predefined: true});
- bindings.push({name: "lineUp", keyBinding: new KeyBinding("p", false, false, false, true), predefined: true});
- bindings.push({name: "lineDown", keyBinding: new KeyBinding("n", false, false, false, true), predefined: true});
- bindings.push({name: "charPrevious", keyBinding: new KeyBinding("b", false, false, false, true), predefined: true});
- bindings.push({name: "charNext", keyBinding: new KeyBinding("f", false, false, false, true), predefined: true});
- bindings.push({name: "deletePrevious", keyBinding: new KeyBinding("h", false, false, false, true), predefined: true});
- bindings.push({name: "deleteNext", keyBinding: new KeyBinding("d", false, false, false, true), predefined: true});
- bindings.push({name: "deleteLineEnd", keyBinding: new KeyBinding("k", false, false, false, true), predefined: true});
- if (isFirefox) {
- bindings.push({name: "scrollPageDown", keyBinding: new KeyBinding("v", false, false, false, true), predefined: true});
- bindings.push({name: "deleteLineStart", keyBinding: new KeyBinding("u", false, false, false, true), predefined: true});
- bindings.push({name: "deleteWordPrevious", keyBinding: new KeyBinding("w", false, false, false, true), predefined: true});
- } else {
- bindings.push({name: "pageDown", keyBinding: new KeyBinding("v", false, false, false, true), predefined: true});
- bindings.push({name: "centerLine", keyBinding: new KeyBinding("l", false, false, false, true), predefined: true});
- bindings.push({name: "enterNoCursor", keyBinding: new KeyBinding("o", false, false, false, true), predefined: true});
- //TODO implement: y (yank), t (transpose)
- }
- }
-
- //1 to 1, no duplicates
- var self = this;
- this._actions = [
- {name: "lineUp", defaultHandler: function() {return self._doLineUp({select: false});}},
- {name: "lineDown", defaultHandler: function() {return self._doLineDown({select: false});}},
- {name: "lineStart", defaultHandler: function() {return self._doHome({select: false, ctrl:false});}},
- {name: "lineEnd", defaultHandler: function() {return self._doEnd({select: false, ctrl:false});}},
- {name: "charPrevious", defaultHandler: function() {return self._doCursorPrevious({select: false, unit:"character"});}},
- {name: "charNext", defaultHandler: function() {return self._doCursorNext({select: false, unit:"character"});}},
- {name: "pageUp", defaultHandler: function() {return self._doPageUp({select: false});}},
- {name: "pageDown", defaultHandler: function() {return self._doPageDown({select: false});}},
- {name: "scrollPageUp", defaultHandler: function() {return self._doScroll({type: "pageUp"});}},
- {name: "scrollPageDown", defaultHandler: function() {return self._doScroll({type: "pageDown"});}},
- {name: "wordPrevious", defaultHandler: function() {return self._doCursorPrevious({select: false, unit:"word"});}},
- {name: "wordNext", defaultHandler: function() {return self._doCursorNext({select: false, unit:"word"});}},
- {name: "textStart", defaultHandler: function() {return self._doHome({select: false, ctrl:true});}},
- {name: "textEnd", defaultHandler: function() {return self._doEnd({select: false, ctrl:true});}},
- {name: "scrollTextStart", defaultHandler: function() {return self._doScroll({type: "textStart"});}},
- {name: "scrollTextEnd", defaultHandler: function() {return self._doScroll({type: "textEnd"});}},
- {name: "centerLine", defaultHandler: function() {return self._doScroll({type: "centerLine"});}},
-
- {name: "selectLineUp", defaultHandler: function() {return self._doLineUp({select: true});}},
- {name: "selectLineDown", defaultHandler: function() {return self._doLineDown({select: true});}},
- {name: "selectWholeLineUp", defaultHandler: function() {return self._doLineUp({select: true, wholeLine: true});}},
- {name: "selectWholeLineDown", defaultHandler: function() {return self._doLineDown({select: true, wholeLine: true});}},
- {name: "selectLineStart", defaultHandler: function() {return self._doHome({select: true, ctrl:false});}},
- {name: "selectLineEnd", defaultHandler: function() {return self._doEnd({select: true, ctrl:false});}},
- {name: "selectCharPrevious", defaultHandler: function() {return self._doCursorPrevious({select: true, unit:"character"});}},
- {name: "selectCharNext", defaultHandler: function() {return self._doCursorNext({select: true, unit:"character"});}},
- {name: "selectPageUp", defaultHandler: function() {return self._doPageUp({select: true});}},
- {name: "selectPageDown", defaultHandler: function() {return self._doPageDown({select: true});}},
- {name: "selectWordPrevious", defaultHandler: function() {return self._doCursorPrevious({select: true, unit:"word"});}},
- {name: "selectWordNext", defaultHandler: function() {return self._doCursorNext({select: true, unit:"word"});}},
- {name: "selectTextStart", defaultHandler: function() {return self._doHome({select: true, ctrl:true});}},
- {name: "selectTextEnd", defaultHandler: function() {return self._doEnd({select: true, ctrl:true});}},
-
- {name: "deletePrevious", defaultHandler: function() {return self._doBackspace({unit:"character"});}},
- {name: "deleteNext", defaultHandler: function() {return self._doDelete({unit:"character"});}},
- {name: "deleteWordPrevious", defaultHandler: function() {return self._doBackspace({unit:"word"});}},
- {name: "deleteWordNext", defaultHandler: function() {return self._doDelete({unit:"word"});}},
- {name: "deleteLineStart", defaultHandler: function() {return self._doBackspace({unit: "line"});}},
- {name: "deleteLineEnd", defaultHandler: function() {return self._doDelete({unit: "line"});}},
- {name: "tab", defaultHandler: function() {return self._doTab();}},
- {name: "enter", defaultHandler: function() {return self._doEnter();}},
- {name: "enterNoCursor", defaultHandler: function() {return self._doEnter({noCursor:true});}},
- {name: "selectAll", defaultHandler: function() {return self._doSelectAll();}},
- {name: "copy", defaultHandler: function() {return self._doCopy();}},
- {name: "cut", defaultHandler: function() {return self._doCut();}},
- {name: "paste", defaultHandler: function() {return self._doPaste();}}
- ];
- },
- _createLine: function(parent, div, document, lineIndex, model) {
- var lineText = model.getLine(lineIndex);
- var lineStart = model.getLineStart(lineIndex);
- var e = {type:"LineStyle", textView: this, lineIndex: lineIndex, lineText: lineText, lineStart: lineStart};
- this.onLineStyle(e);
- var lineDiv = div || document.createElement("DIV");
- if (!div || !this._compare(div.viewStyle, e.style)) {
- this._applyStyle(e.style, lineDiv, div);
- lineDiv.viewStyle = e.style;
- }
- lineDiv.lineIndex = lineIndex;
- var ranges = [];
- var data = {tabOffset: 0, ranges: ranges};
- this._createRanges(e.ranges, lineText, 0, lineText.length, lineStart, data);
-
- /*
- * A trailing span with a whitespace is added for three different reasons:
- * 1. Make sure the height of each line is the largest of the default font
- * in normal, italic, bold, and italic-bold.
- * 2. When full selection is off, Firefox, Opera and IE9 do not extend the
- * selection at the end of the line when the line is fully selected.
- * 3. The height of a div with only an empty span is zero.
- */
- var c = " ";
- if (!this._fullSelection && isIE < 9) {
- /*
- * IE8 already selects extra space at end of a line fully selected,
- * adding another space at the end of the line causes the selection
- * to look too big. The fix is to use a zero-width space (\uFEFF) instead.
- */
- c = "\uFEFF";
- }
- if (isWebkit) {
- /*
- * Feature in WekKit. Adding a regular white space to the line will
- * cause the longest line in the view to wrap even though "pre" is set.
- * The fix is to use the zero-width non-joiner character (\u200C) instead.
- * Note: To not use \uFEFF because in old version of Chrome this character
- * shows a glyph;
- */
- c = "\u200C";
- }
- ranges.push({text: c, style: this._largestFontStyle, ignoreChars: 1});
-
- var range, span, style, oldSpan, oldStyle, text, oldText, end = 0, oldEnd = 0, next;
- var changeCount, changeStart;
- if (div) {
- var modelChangedEvent = div.modelChangedEvent;
- if (modelChangedEvent) {
- if (modelChangedEvent.removedLineCount === 0 && modelChangedEvent.addedLineCount === 0) {
- changeStart = modelChangedEvent.start - lineStart;
- changeCount = modelChangedEvent.addedCharCount - modelChangedEvent.removedCharCount;
- } else {
- changeStart = -1;
- }
- div.modelChangedEvent = undefined;
- }
- oldSpan = div.firstChild;
- }
- for (var i = 0; i < ranges.length; i++) {
- range = ranges[i];
- text = range.text;
- end += text.length;
- style = range.style;
- if (oldSpan) {
- oldText = oldSpan.firstChild.data;
- oldStyle = oldSpan.viewStyle;
- if (oldText === text && this._compare(style, oldStyle)) {
- oldEnd += oldText.length;
- oldSpan._rectsCache = undefined;
- span = oldSpan = oldSpan.nextSibling;
- continue;
- } else {
- while (oldSpan) {
- if (changeStart !== -1) {
- var spanEnd = end;
- if (spanEnd >= changeStart) {
- spanEnd -= changeCount;
- }
- var length = oldSpan.firstChild.data.length;
- if (oldEnd + length > spanEnd) { break; }
- oldEnd += length;
- }
- next = oldSpan.nextSibling;
- lineDiv.removeChild(oldSpan);
- oldSpan = next;
- }
- }
- }
- span = this._createSpan(lineDiv, document, text, style, range.ignoreChars);
- if (oldSpan) {
- lineDiv.insertBefore(span, oldSpan);
- } else {
- lineDiv.appendChild(span);
- }
- if (div) {
- div.lineWidth = undefined;
- }
- }
- if (div) {
- var tmp = span ? span.nextSibling : null;
- while (tmp) {
- next = tmp.nextSibling;
- div.removeChild(tmp);
- tmp = next;
- }
- } else {
- parent.appendChild(lineDiv);
- }
- return lineDiv;
- },
- _createRanges: function(ranges, text, start, end, lineStart, data) {
- if (start >= end) { return; }
- if (ranges) {
- for (var i = 0; i < ranges.length; i++) {
- var range = ranges[i];
- if (range.end <= lineStart + start) { continue; }
- var styleStart = Math.max(lineStart + start, range.start) - lineStart;
- if (styleStart >= end) { break; }
- var styleEnd = Math.min(lineStart + end, range.end) - lineStart;
- if (styleStart < styleEnd) {
- styleStart = Math.max(start, styleStart);
- styleEnd = Math.min(end, styleEnd);
- if (start < styleStart) {
- this._createRange(text, start, styleStart, null, data);
- }
- while (i + 1 < ranges.length && ranges[i + 1].start - lineStart === styleEnd && this._compare(range.style, ranges[i + 1].style)) {
- range = ranges[i + 1];
- styleEnd = Math.min(lineStart + end, range.end) - lineStart;
- i++;
- }
- this._createRange(text, styleStart, styleEnd, range.style, data);
- start = styleEnd;
- }
- }
- }
- if (start < end) {
- this._createRange(text, start, end, null, data);
- }
- },
- _createRange: function(text, start, end, style, data) {
- if (start >= end) { return; }
- var tabSize = this._customTabSize, range;
- if (tabSize && tabSize !== 8) {
- var tabIndex = text.indexOf("\t", start);
- while (tabIndex !== -1 && tabIndex < end) {
- if (start < tabIndex) {
- range = {text: text.substring(start, tabIndex), style: style};
- data.ranges.push(range);
- data.tabOffset += range.text.length;
- }
- var spacesCount = tabSize - (data.tabOffset % tabSize);
- if (spacesCount > 0) {
- //TODO hack to preserve text length in getDOMText()
- var spaces = "\u00A0";
- for (var i = 1; i < spacesCount; i++) {
- spaces += " ";
- }
- range = {text: spaces, style: style, ignoreChars: spacesCount - 1};
- data.ranges.push(range);
- data.tabOffset += range.text.length;
- }
- start = tabIndex + 1;
- tabIndex = text.indexOf("\t", start);
- }
- }
- if (start < end) {
- range = {text: text.substring(start, end), style: style};
- data.ranges.push(range);
- data.tabOffset += range.text.length;
- }
- },
- _createSpan: function(parent, document, text, style, ignoreChars) {
- var isLink = style && style.tagName === "A";
- if (isLink) { parent.hasLink = true; }
- var tagName = isLink && this._linksVisible ? "A" : "SPAN";
- var child = document.createElement(tagName);
- child.appendChild(document.createTextNode(text));
- this._applyStyle(style, child);
- if (tagName === "A") {
- var self = this;
- addHandler(child, "click", function(e) { return self._handleLinkClick(e); }, false);
- }
- child.viewStyle = style;
- if (ignoreChars) {
- child.ignoreChars = ignoreChars;
- }
- return child;
- },
- _createRuler: function(ruler) {
- if (!this._clientDiv) { return; }
- var document = this._frameDocument;
- var body = document.body;
- var side = ruler.getLocation();
- var rulerParent = side === "left" ? this._leftDiv : this._rightDiv;
- if (!rulerParent) {
- rulerParent = document.createElement("DIV");
- rulerParent.style.overflow = "hidden";
- rulerParent.style.MozUserSelect = "none";
- rulerParent.style.WebkitUserSelect = "none";
- if (isIE) {
- rulerParent.attachEvent("onselectstart", function() {return false;});
- }
- rulerParent.style.position = "absolute";
- rulerParent.style.top = "0px";
- rulerParent.style.cursor = "default";
- body.appendChild(rulerParent);
- if (side === "left") {
- this._leftDiv = rulerParent;
- rulerParent.className = "viewLeftRuler";
- } else {
- this._rightDiv = rulerParent;
- rulerParent.className = "viewRightRuler";
- }
- var table = document.createElement("TABLE");
- rulerParent.appendChild(table);
- table.cellPadding = "0px";
- table.cellSpacing = "0px";
- table.border = "0px";
- table.insertRow(0);
- var self = this;
- addHandler(rulerParent, "click", function(e) { self._handleRulerEvent(e); });
- addHandler(rulerParent, "dblclick", function(e) { self._handleRulerEvent(e); });
- addHandler(rulerParent, "mousemove", function(e) { self._handleRulerEvent(e); });
- addHandler(rulerParent, "mouseover", function(e) { self._handleRulerEvent(e); });
- addHandler(rulerParent, "mouseout", function(e) { self._handleRulerEvent(e); });
- }
- var div = document.createElement("DIV");
- div._ruler = ruler;
- div.rulerChanged = true;
- div.style.position = "relative";
- var row = rulerParent.firstChild.rows[0];
- var index = row.cells.length;
- var cell = row.insertCell(index);
- cell.vAlign = "top";
- cell.appendChild(div);
- },
- _createFrame: function() {
- if (this.frame) { return; }
- var parent = this._parent;
- while (parent.hasChildNodes()) { parent.removeChild(parent.lastChild); }
- var parentDocument = parent.ownerDocument;
- this._parentDocument = parentDocument;
- var frame = parentDocument.createElement("IFRAME");
- this._frame = frame;
- frame.frameBorder = "0px";//for IE, needs to be set before the frame is added to the parent
- frame.style.border = "0px";
- frame.style.width = "100%";
- frame.style.height = "100%";
- frame.scrolling = "no";
- var self = this;
- /*
- * Note that it is not possible to create the contents of the frame if the
- * parent is not connected to the document. Only create it when the load
- * event is trigged.
- */
- this._loadHandler = function(e) {
- self._handleLoad(e);
- };
- addHandler(frame, "load", this._loadHandler, !!isFirefox);
- if (!isWebkit) {
- /*
- * Feature in IE and Firefox. It is not possible to get the style of an
- * element if it is not layed out because one of the ancestor has
- * style.display = none. This means that the view cannot be created in this
- * situations, since no measuring can be performed. The fix is to listen
- * for DOMAttrModified and create or destroy the view when the style.display
- * attribute changes.
- */
- addHandler(parentDocument, "DOMAttrModified", this._attrModifiedHandler = function(e) {
- self._handleDOMAttrModified(e);
- });
- }
- parent.appendChild(frame);
- /* create synchronously if possible */
- if (this._sync) {
- this._handleLoad();
- }
- },
- _getFrameHTML: function() {
- var html = [];
- html.push("");
- html.push("");
- html.push("");
- if (isIE < 9) {
- html.push("");
- }
- html.push("");
- if (this._stylesheet) {
- var stylesheet = typeof(this._stylesheet) === "string" ? [this._stylesheet] : this._stylesheet;
- for (var i = 0; i < stylesheet.length; i++) {
- var sheet = stylesheet[i];
- var isLink = this._isLinkURL(sheet);
- if (isLink && this._sync) {
- try {
- var objXml = new XMLHttpRequest();
- if (objXml.overrideMimeType) {
- objXml.overrideMimeType("text/css");
- }
- objXml.open("GET", sheet, false);
- objXml.send(null);
- sheet = objXml.responseText;
- isLink = false;
- } catch (e) {}
- }
- if (isLink) {
- html.push("");
- } else {
- html.push("");
- }
- }
- }
- /*
- * Feature in WebKit. In WebKit, window load will not wait for the style sheets
- * to be loaded unless there is script element after the style sheet link elements.
- */
- html.push("");
- html.push("");
- html.push("");
- html.push("");
- return html.join("");
- },
- _createView: function() {
- if (this._frameDocument) { return; }
- var frameWindow = this._frameWindow = this._frame.contentWindow;
- var frameDocument = this._frameDocument = frameWindow.document;
- var self = this;
- function write() {
- frameDocument.open("text/html", "replace");
- frameDocument.write(self._getFrameHTML());
- frameDocument.close();
- self._windowLoadHandler = function(e) {
- /*
- * Bug in Safari. Safari sends the window load event before the
- * style sheets are loaded. The fix is to defer creation of the
- * contents until the document readyState changes to complete.
- */
- if (self._isDocumentReady()) {
- self._createContent();
- }
- };
- addHandler(frameWindow, "load", self._windowLoadHandler);
- }
- write();
- if (this._sync) {
- this._createContent();
- } else {
- /*
- * Bug in Webkit. Webkit does not send the load event for the iframe window when the main page
- * loads as a result of backward or forward navigation.
- * The fix is to use a timer to create the content only when the document is ready.
- */
- this._createViewTimer = function() {
- if (self._clientDiv) { return; }
- if (self._isDocumentReady()) {
- self._createContent();
- } else {
- setTimeout(self._createViewTimer, 10);
- }
- };
- setTimeout(this._createViewTimer, 10);
- }
- },
- _isDocumentReady: function() {
- var frameDocument = this._frameDocument;
- if (!frameDocument) { return false; }
- if (frameDocument.readyState === "complete") {
- return true;
- } else if (frameDocument.readyState === "interactive" && isFirefox) {
- /*
- * Bug in Firefox. Firefox does not change the document ready state to complete
- * all the time. The fix is to wait for the ready state to be "interactive" and check that
- * all css rules are initialized.
- */
- var styleSheets = frameDocument.styleSheets;
- var styleSheetCount = 1;
- if (this._stylesheet) {
- styleSheetCount += typeof(this._stylesheet) === "string" ? 1 : this._stylesheet.length;
- }
- if (styleSheetCount === styleSheets.length) {
- var index = 0;
- while (index < styleSheets.length) {
- var count = 0;
- try {
- count = styleSheets.item(index).cssRules.length;
- } catch (ex) {
- /*
- * Feature in Firefox. To determine if a stylesheet is loaded the number of css rules is used, if the
- * stylesheet is not loaded this operation will throw an invalid access error. When a stylesheet from
- * a different domain is loaded, accessing the css rules will result in a security exception. In this
- * case count is set to 1 to indicate the stylesheet is loaded.
- */
- if (ex.code !== DOMException.INVALID_ACCESS_ERR) {
- count = 1;
- }
- }
- if (count === 0) { break; }
- index++;
- }
- return index === styleSheets.length;
- }
- }
- return false;
- },
- _createContent: function() {
- if (this._clientDiv) { return; }
- var parent = this._parent;
- var parentDocument = this._parentDocument;
- var frameDocument = this._frameDocument;
- var body = frameDocument.body;
- this._setThemeClass(this._themeClass, true);
- body.style.margin = "0px";
- body.style.borderWidth = "0px";
- body.style.padding = "0px";
-
- var textArea;
- if (isPad) {
- var touchDiv = parentDocument.createElement("DIV");
- this._touchDiv = touchDiv;
- touchDiv.style.position = "absolute";
- touchDiv.style.border = "0px";
- touchDiv.style.padding = "0px";
- touchDiv.style.margin = "0px";
- touchDiv.style.zIndex = "2";
- touchDiv.style.overflow = "hidden";
- touchDiv.style.background="transparent";
- touchDiv.style.WebkitUserSelect = "none";
- parent.appendChild(touchDiv);
-
- textArea = parentDocument.createElement("TEXTAREA");
- this._textArea = textArea;
- textArea.style.position = "absolute";
- textArea.style.whiteSpace = "pre";
- textArea.style.left = "-1000px";
- textArea.tabIndex = 1;
- textArea.autocapitalize = "off";
- textArea.autocorrect = "off";
- textArea.className = "viewContainer";
- textArea.style.background = "transparent";
- textArea.style.color = "transparent";
- textArea.style.border = "0px";
- textArea.style.padding = "0px";
- textArea.style.margin = "0px";
- textArea.style.borderRadius = "0px";
- textArea.style.WebkitAppearance = "none";
- textArea.style.WebkitTapHighlightColor = "transparent";
- touchDiv.appendChild(textArea);
- }
- if (isFirefox) {
- var clipboardDiv = frameDocument.createElement("DIV");
- this._clipboardDiv = clipboardDiv;
- clipboardDiv.style.position = "fixed";
- clipboardDiv.style.whiteSpace = "pre";
- clipboardDiv.style.left = "-1000px";
- body.appendChild(clipboardDiv);
- }
-
- var viewDiv = frameDocument.createElement("DIV");
- viewDiv.className = "view";
- this._viewDiv = viewDiv;
- viewDiv.id = "viewDiv";
- viewDiv.tabIndex = -1;
- viewDiv.style.overflow = "auto";
- viewDiv.style.position = "absolute";
- viewDiv.style.top = "0px";
- viewDiv.style.borderWidth = "0px";
- viewDiv.style.margin = "0px";
- viewDiv.style.outline = "none";
- body.appendChild(viewDiv);
-
- var scrollDiv = frameDocument.createElement("DIV");
- this._scrollDiv = scrollDiv;
- scrollDiv.id = "scrollDiv";
- scrollDiv.style.margin = "0px";
- scrollDiv.style.borderWidth = "0px";
- scrollDiv.style.padding = "0px";
- viewDiv.appendChild(scrollDiv);
-
- if (isFirefox) {
- var clipDiv = frameDocument.createElement("DIV");
- this._clipDiv = clipDiv;
- clipDiv.id = "clipDiv";
- clipDiv.style.position = "fixed";
- clipDiv.style.overflow = "hidden";
- clipDiv.style.margin = "0px";
- clipDiv.style.borderWidth = "0px";
- clipDiv.style.padding = "0px";
- scrollDiv.appendChild(clipDiv);
-
- var clipScrollDiv = frameDocument.createElement("DIV");
- this._clipScrollDiv = clipScrollDiv;
- clipScrollDiv.id = "clipScrollDiv";
- clipScrollDiv.style.position = "absolute";
- clipScrollDiv.style.height = "1px";
- clipScrollDiv.style.top = "-1000px";
- clipDiv.appendChild(clipScrollDiv);
- }
-
- this._setFullSelection(this._fullSelection, true);
-
- var clientDiv = frameDocument.createElement("DIV");
- clientDiv.className = "viewContent";
- this._clientDiv = clientDiv;
- clientDiv.id = "clientDiv";
- clientDiv.style.whiteSpace = "pre";
- clientDiv.style.position = this._clipDiv ? "absolute" : "fixed";
- clientDiv.style.borderWidth = "0px";
- clientDiv.style.margin = "0px";
- clientDiv.style.padding = "0px";
- clientDiv.style.outline = "none";
- clientDiv.style.zIndex = "1";
- if (isPad) {
- clientDiv.style.WebkitTapHighlightColor = "transparent";
- }
- (this._clipDiv || scrollDiv).appendChild(clientDiv);
-
- if (isFirefox && !clientDiv.setCapture) {
- var overlayDiv = frameDocument.createElement("DIV");
- this._overlayDiv = overlayDiv;
- overlayDiv.id = "overlayDiv";
- overlayDiv.style.position = clientDiv.style.position;
- overlayDiv.style.borderWidth = clientDiv.style.borderWidth;
- overlayDiv.style.margin = clientDiv.style.margin;
- overlayDiv.style.padding = clientDiv.style.padding;
- overlayDiv.style.cursor = "text";
- overlayDiv.style.zIndex = "2";
- (this._clipDiv || scrollDiv).appendChild(overlayDiv);
- }
- if (!isPad) {
- clientDiv.contentEditable = "true";
- }
- this._lineHeight = this._calculateLineHeight();
- this._viewPadding = this._calculatePadding();
- if (isIE) {
- body.style.lineHeight = this._lineHeight + "px";
- }
- this._setTabSize(this._tabSize, true);
- this._hookEvents();
- var rulers = this._rulers;
- for (var i=0; i 0 || v > 0) {
- viewDiv.scrollLeft = h;
- viewDiv.scrollTop = v;
- }
- this.onLoad({type: "Load"});
- },
- _defaultOptions: function() {
- return {
- parent: {value: undefined, recreate: true, update: null},
- model: {value: undefined, recreate: false, update: this.setModel},
- readonly: {value: false, recreate: false, update: null},
- fullSelection: {value: true, recreate: false, update: this._setFullSelection},
- tabSize: {value: 8, recreate: false, update: this._setTabSize},
- expandTab: {value: false, recreate: false, update: null},
- stylesheet: {value: [], recreate: false, update: this._setStyleSheet},
- themeClass: {value: undefined, recreate: false, update: this._setThemeClass},
- sync: {value: false, recreate: false, update: null}
- };
- },
- _destroyFrame: function() {
- var frame = this._frame;
- if (!frame) { return; }
- if (this._loadHandler) {
- removeHandler(frame, "load", this._loadHandler, !!isFirefox);
- this._loadHandler = null;
- }
- if (this._attrModifiedHandler) {
- removeHandler(this._parentDocument, "DOMAttrModified", this._attrModifiedHandler);
- this._attrModifiedHandler = null;
- }
- frame.parentNode.removeChild(frame);
- this._frame = null;
- },
- _destroyRuler: function(ruler) {
- var side = ruler.getLocation();
- var rulerParent = side === "left" ? this._leftDiv : this._rightDiv;
- if (rulerParent) {
- var row = rulerParent.firstChild.rows[0];
- var cells = row.cells;
- for (var index = 0; index < cells.length; index++) {
- var cell = cells[index];
- if (cell.firstChild._ruler === ruler) { break; }
- }
- if (index === cells.length) { return; }
- row.cells[index]._ruler = undefined;
- row.deleteCell(index);
- }
- },
- _destroyView: function() {
- var clientDiv = this._clientDiv;
- if (!clientDiv) { return; }
- this._setGrab(null);
- this._unhookEvents();
- if (this._windowLoadHandler) {
- removeHandler(this._frameWindow, "load", this._windowLoadHandler);
- this._windowLoadHandler = null;
- }
-
- /* Destroy timers */
- if (this._autoScrollTimerID) {
- clearTimeout(this._autoScrollTimerID);
- this._autoScrollTimerID = null;
- }
- if (this._updateTimer) {
- clearTimeout(this._updateTimer);
- this._updateTimer = null;
- }
-
- /* Destroy DOM */
- var parent = this._frameDocument.body;
- while (parent.hasChildNodes()) { parent.removeChild(parent.lastChild); }
- if (this._touchDiv) {
- this._parent.removeChild(this._touchDiv);
- this._touchDiv = null;
- }
- this._selDiv1 = null;
- this._selDiv2 = null;
- this._selDiv3 = null;
- this._insertedSelRule = false;
- this._textArea = null;
- this._clipboardDiv = null;
- this._scrollDiv = null;
- this._viewDiv = null;
- this._clipDiv = null;
- this._clipScrollDiv = null;
- this._clientDiv = null;
- this._overlayDiv = null;
- this._leftDiv = null;
- this._rightDiv = null;
- this._frameDocument = null;
- this._frameWindow = null;
- this.onUnload({type: "Unload"});
- },
- _doAutoScroll: function (direction, x, y) {
- this._autoScrollDir = direction;
- this._autoScrollX = x;
- this._autoScrollY = y;
- if (!this._autoScrollTimerID) {
- this._autoScrollTimer();
- }
- },
- _endAutoScroll: function () {
- if (this._autoScrollTimerID) { clearTimeout(this._autoScrollTimerID); }
- this._autoScrollDir = undefined;
- this._autoScrollTimerID = undefined;
- },
- _fixCaret: function() {
- var clientDiv = this._clientDiv;
- if (clientDiv) {
- var hasFocus = this._hasFocus;
- this._ignoreFocus = true;
- if (hasFocus) { clientDiv.blur(); }
- clientDiv.contentEditable = false;
- clientDiv.contentEditable = true;
- if (hasFocus) { clientDiv.focus(); }
- this._ignoreFocus = false;
- }
- },
- _getBaseText: function(start, end) {
- var model = this._model;
- /* This is the only case the view access the base model, alternatively the view could use a event to application to customize the text */
- if (model.getBaseModel) {
- start = model.mapOffset(start);
- end = model.mapOffset(end);
- model = model.getBaseModel();
- }
- return model.getText(start, end);
- },
- _getBoundsAtOffset: function (offset) {
- var model = this._model;
- var document = this._frameDocument;
- var clientDiv = this._clientDiv;
- var lineIndex = model.getLineAtOffset(offset);
- var dummy;
- var child = this._getLineNode(lineIndex);
- if (!child) {
- child = dummy = this._createLine(clientDiv, null, document, lineIndex, model);
- }
- var result = null;
- if (offset < model.getLineEnd(lineIndex)) {
- var lineOffset = model.getLineStart(lineIndex);
- var lineChild = child.firstChild;
- while (lineChild) {
- var textNode = lineChild.firstChild;
- var nodeLength = textNode.length;
- if (lineChild.ignoreChars) {
- nodeLength -= lineChild.ignoreChars;
- }
- if (lineOffset + nodeLength > offset) {
- var index = offset - lineOffset;
- var range;
- if (isRangeRects) {
- range = document.createRange();
- range.setStart(textNode, index);
- range.setEnd(textNode, index + 1);
- result = range.getBoundingClientRect();
- } else if (isIE) {
- range = document.body.createTextRange();
- range.moveToElementText(lineChild);
- range.collapse();
- range.moveEnd("character", index + 1);
- range.moveStart("character", index);
- result = range.getBoundingClientRect();
- } else {
- var text = textNode.data;
- lineChild.removeChild(textNode);
- lineChild.appendChild(document.createTextNode(text.substring(0, index)));
- var span = document.createElement("SPAN");
- span.appendChild(document.createTextNode(text.substring(index, index + 1)));
- lineChild.appendChild(span);
- lineChild.appendChild(document.createTextNode(text.substring(index + 1)));
- result = span.getBoundingClientRect();
- lineChild.innerHTML = "";
- lineChild.appendChild(textNode);
- if (!dummy) {
- /*
- * Removing the element node that holds the selection start or end
- * causes the selection to be lost. The fix is to detect this case
- * and restore the selection.
- */
- var s = this._getSelection();
- if ((lineOffset <= s.start && s.start < lineOffset + nodeLength) || (lineOffset <= s.end && s.end < lineOffset + nodeLength)) {
- this._updateDOMSelection();
- }
- }
- }
- if (isIE) {
- var logicalXDPI = window.screen.logicalXDPI;
- var deviceXDPI = window.screen.deviceXDPI;
- result.left = result.left * logicalXDPI / deviceXDPI;
- result.right = result.right * logicalXDPI / deviceXDPI;
- }
- break;
- }
- lineOffset += nodeLength;
- lineChild = lineChild.nextSibling;
- }
- }
- if (!result) {
- var rect = this._getLineBoundingClientRect(child);
- result = {left: rect.right, right: rect.right};
- }
- if (dummy) { clientDiv.removeChild(dummy); }
- return result;
- },
- _getBottomIndex: function (fullyVisible) {
- var child = this._bottomChild;
- if (fullyVisible && this._getClientHeight() > this._getLineHeight()) {
- var rect = child.getBoundingClientRect();
- var clientRect = this._clientDiv.getBoundingClientRect();
- if (rect.bottom > clientRect.bottom) {
- child = this._getLinePrevious(child) || child;
- }
- }
- return child.lineIndex;
- },
- _getFrameHeight: function() {
- return this._frameDocument.documentElement.clientHeight;
- },
- _getFrameWidth: function() {
- return this._frameDocument.documentElement.clientWidth;
- },
- _getClientHeight: function() {
- var viewPad = this._getViewPadding();
- return Math.max(0, this._viewDiv.clientHeight - viewPad.top - viewPad.bottom);
- },
- _getClientWidth: function() {
- var viewPad = this._getViewPadding();
- return Math.max(0, this._viewDiv.clientWidth - viewPad.left - viewPad.right);
- },
- _getClipboardText: function (event, handler) {
- var delimiter = this._model.getLineDelimiter();
- var clipboadText, text;
- if (this._frameWindow.clipboardData) {
- //IE
- clipboadText = [];
- text = this._frameWindow.clipboardData.getData("Text");
- this._convertDelimiter(text, function(t) {clipboadText.push(t);}, function() {clipboadText.push(delimiter);});
- text = clipboadText.join("");
- if (handler) { handler(text); }
- return text;
- }
- if (isFirefox) {
- this._ignoreFocus = true;
- var document = this._frameDocument;
- var clipboardDiv = this._clipboardDiv;
- clipboardDiv.innerHTML = "";
- clipboardDiv.firstChild.focus();
- var self = this;
- var _getText = function() {
- var noteText = self._getTextFromElement(clipboardDiv);
- clipboardDiv.innerHTML = "";
- clipboadText = [];
- self._convertDelimiter(noteText, function(t) {clipboadText.push(t);}, function() {clipboadText.push(delimiter);});
- return clipboadText.join("");
- };
-
- /* Try execCommand first. Works on firefox with clipboard permission. */
- var result = false;
- this._ignorePaste = true;
-
- /* Do not try execCommand if middle-click is used, because if we do, we get the clipboard text, not the primary selection text. */
- if (!isLinux || this._lastMouseButton !== 2) {
- try {
- result = document.execCommand("paste", false, null);
- } catch (ex) {
- /* Firefox can throw even when execCommand() works, see bug 362835. */
- result = clipboardDiv.childNodes.length > 1 || clipboardDiv.firstChild && clipboardDiv.firstChild.childNodes.length > 0;
- }
- }
- this._ignorePaste = false;
- if (!result) {
- /* Try native paste in DOM, works for firefox during the paste event. */
- if (event) {
- setTimeout(function() {
- self.focus();
- text = _getText();
- if (text && handler) {
- handler(text);
- }
- self._ignoreFocus = false;
- }, 0);
- return null;
- } else {
- /* no event and no clipboard permission, paste can't be performed */
- this.focus();
- this._ignoreFocus = false;
- return "";
- }
- }
- this.focus();
- this._ignoreFocus = false;
- text = _getText();
- if (text && handler) {
- handler(text);
- }
- return text;
- }
- //webkit
- if (event && event.clipboardData) {
- /*
- * Webkit (Chrome/Safari) allows getData during the paste event
- * Note: setData is not allowed, not even during copy/cut event
- */
- clipboadText = [];
- text = event.clipboardData.getData("text/plain");
- this._convertDelimiter(text, function(t) {clipboadText.push(t);}, function() {clipboadText.push(delimiter);});
- text = clipboadText.join("");
- if (text && handler) {
- handler(text);
- }
- return text;
- } else {
- //TODO try paste using extension (Chrome only)
- }
- return "";
- },
- _getDOMText: function(lineIndex) {
- var child = this._getLineNode(lineIndex);
- var lineChild = child.firstChild;
- var text = "";
- while (lineChild) {
- var textNode = lineChild.firstChild;
- while (textNode) {
- if (lineChild.ignoreChars) {
- for (var i = 0; i < textNode.length; i++) {
- var ch = textNode.data.substring(i, i + 1);
- if (ch !== " ") {
- text += ch;
- }
- }
- } else {
- text += textNode.data;
- }
- textNode = textNode.nextSibling;
- }
- lineChild = lineChild.nextSibling;
- }
- return text;
- },
- _getTextFromElement: function(element) {
- var document = element.ownerDocument;
- var window = document.defaultView;
- if (!window.getSelection) {
- return element.innerText || element.textContent;
- }
-
- var newRange = document.createRange();
- newRange.selectNode(element);
-
- var selection = window.getSelection();
- var oldRanges = [], i;
- for (i = 0; i < selection.rangeCount; i++) {
- oldRanges.push(selection.getRangeAt(i));
- }
-
- this._ignoreSelect = true;
- selection.removeAllRanges();
- selection.addRange(newRange);
-
- var text = selection.toString();
-
- selection.removeAllRanges();
- for (i = 0; i < oldRanges.length; i++) {
- selection.addRange(oldRanges[i]);
- }
-
- this._ignoreSelect = false;
- return text;
- },
- _getViewPadding: function() {
- return this._viewPadding;
- },
- _getLineBoundingClientRect: function (child) {
- var rect = child.getBoundingClientRect();
- var lastChild = child.lastChild;
- //Remove any artificial trailing whitespace in the line
- while (lastChild && lastChild.ignoreChars === lastChild.firstChild.length) {
- lastChild = lastChild.previousSibling;
- }
- if (!lastChild) {
- return {left: rect.left, top: rect.top, right: rect.left, bottom: rect.bottom};
- }
- var lastRect = lastChild.getBoundingClientRect();
- return {left: rect.left, top: rect.top, right: lastRect.right, bottom: rect.bottom};
- },
- _getLineHeight: function() {
- return this._lineHeight;
- },
- _getLineNode: function (lineIndex) {
- var clientDiv = this._clientDiv;
- var child = clientDiv.firstChild;
- while (child) {
- if (lineIndex === child.lineIndex) {
- return child;
- }
- child = child.nextSibling;
- }
- return undefined;
- },
- _getLineNext: function (lineNode) {
- var node = lineNode ? lineNode.nextSibling : this._clientDiv.firstChild;
- while (node && node.lineIndex === -1) {
- node = node.nextSibling;
- }
- return node;
- },
- _getLinePrevious: function (lineNode) {
- var node = lineNode ? lineNode.previousSibling : this._clientDiv.lastChild;
- while (node && node.lineIndex === -1) {
- node = node.previousSibling;
- }
- return node;
- },
- _getOffset: function (offset, unit, direction) {
- if (unit === "line") {
- var model = this._model;
- var lineIndex = model.getLineAtOffset(offset);
- if (direction > 0) {
- return model.getLineEnd(lineIndex);
- }
- return model.getLineStart(lineIndex);
- }
- if (unit === "wordend") {
- return this._getOffset_W3C(offset, unit, direction);
- }
- return isIE ? this._getOffset_IE(offset, unit, direction) : this._getOffset_W3C(offset, unit, direction);
- },
- _getOffset_W3C: function (offset, unit, direction) {
- function _isPunctuation(c) {
- return (33 <= c && c <= 47) || (58 <= c && c <= 64) || (91 <= c && c <= 94) || c === 96 || (123 <= c && c <= 126);
- }
- function _isWhitespace(c) {
- return c === 32 || c === 9;
- }
- if (unit === "word" || unit === "wordend") {
- var model = this._model;
- var lineIndex = model.getLineAtOffset(offset);
- var lineText = model.getLine(lineIndex);
- var lineStart = model.getLineStart(lineIndex);
- var lineEnd = model.getLineEnd(lineIndex);
- var lineLength = lineText.length;
- var offsetInLine = offset - lineStart;
-
-
- var c, previousPunctuation, previousLetterOrDigit, punctuation, letterOrDigit;
- if (direction > 0) {
- if (offsetInLine === lineLength) { return lineEnd; }
- c = lineText.charCodeAt(offsetInLine);
- previousPunctuation = _isPunctuation(c);
- previousLetterOrDigit = !previousPunctuation && !_isWhitespace(c);
- offsetInLine++;
- while (offsetInLine < lineLength) {
- c = lineText.charCodeAt(offsetInLine);
- punctuation = _isPunctuation(c);
- if (unit === "wordend") {
- if (!punctuation && previousPunctuation) { break; }
- } else {
- if (punctuation && !previousPunctuation) { break; }
- }
- letterOrDigit = !punctuation && !_isWhitespace(c);
- if (unit === "wordend") {
- if (!letterOrDigit && previousLetterOrDigit) { break; }
- } else {
- if (letterOrDigit && !previousLetterOrDigit) { break; }
- }
- previousLetterOrDigit = letterOrDigit;
- previousPunctuation = punctuation;
- offsetInLine++;
- }
- } else {
- if (offsetInLine === 0) { return lineStart; }
- offsetInLine--;
- c = lineText.charCodeAt(offsetInLine);
- previousPunctuation = _isPunctuation(c);
- previousLetterOrDigit = !previousPunctuation && !_isWhitespace(c);
- while (0 < offsetInLine) {
- c = lineText.charCodeAt(offsetInLine - 1);
- punctuation = _isPunctuation(c);
- if (unit === "wordend") {
- if (punctuation && !previousPunctuation) { break; }
- } else {
- if (!punctuation && previousPunctuation) { break; }
- }
- letterOrDigit = !punctuation && !_isWhitespace(c);
- if (unit === "wordend") {
- if (letterOrDigit && !previousLetterOrDigit) { break; }
- } else {
- if (!letterOrDigit && previousLetterOrDigit) { break; }
- }
- previousLetterOrDigit = letterOrDigit;
- previousPunctuation = punctuation;
- offsetInLine--;
- }
- }
- return lineStart + offsetInLine;
- }
- return offset + direction;
- },
- _getOffset_IE: function (offset, unit, direction) {
- var document = this._frameDocument;
- var model = this._model;
- var lineIndex = model.getLineAtOffset(offset);
- var clientDiv = this._clientDiv;
- var dummy;
- var child = this._getLineNode(lineIndex);
- if (!child) {
- child = dummy = this._createLine(clientDiv, null, document, lineIndex, model);
- }
- var result = 0, range, length;
- var lineOffset = model.getLineStart(lineIndex);
- if (offset === model.getLineEnd(lineIndex)) {
- range = document.body.createTextRange();
- range.moveToElementText(child.lastChild);
- length = range.text.length;
- range.moveEnd(unit, direction);
- result = offset + range.text.length - length;
- } else if (offset === lineOffset && direction < 0) {
- result = lineOffset;
- } else {
- var lineChild = child.firstChild;
- while (lineChild) {
- var textNode = lineChild.firstChild;
- var nodeLength = textNode.length;
- if (lineChild.ignoreChars) {
- nodeLength -= lineChild.ignoreChars;
- }
- if (lineOffset + nodeLength > offset) {
- range = document.body.createTextRange();
- if (offset === lineOffset && direction < 0) {
- range.moveToElementText(lineChild.previousSibling);
- } else {
- range.moveToElementText(lineChild);
- range.collapse();
- range.moveEnd("character", offset - lineOffset);
- }
- length = range.text.length;
- range.moveEnd(unit, direction);
- result = offset + range.text.length - length;
- break;
- }
- lineOffset = nodeLength + lineOffset;
- lineChild = lineChild.nextSibling;
- }
- }
- if (dummy) { clientDiv.removeChild(dummy); }
- return result;
- },
- _getOffsetToX: function (offset) {
- return this._getBoundsAtOffset(offset).left;
- },
- _getPadding: function (node) {
- var left,top,right,bottom;
- if (node.currentStyle) {
- left = node.currentStyle.paddingLeft;
- top = node.currentStyle.paddingTop;
- right = node.currentStyle.paddingRight;
- bottom = node.currentStyle.paddingBottom;
- } else if (this._frameWindow.getComputedStyle) {
- var style = this._frameWindow.getComputedStyle(node, null);
- left = style.getPropertyValue("padding-left");
- top = style.getPropertyValue("padding-top");
- right = style.getPropertyValue("padding-right");
- bottom = style.getPropertyValue("padding-bottom");
- }
- return {
- left: parseInt(left, 10),
- top: parseInt(top, 10),
- right: parseInt(right, 10),
- bottom: parseInt(bottom, 10)
- };
- },
- _getScroll: function() {
- var viewDiv = this._viewDiv;
- return {x: viewDiv.scrollLeft, y: viewDiv.scrollTop};
- },
- _getSelection: function () {
- return this._selection.clone();
- },
- _getTopIndex: function (fullyVisible) {
- var child = this._topChild;
- if (fullyVisible && this._getClientHeight() > this._getLineHeight()) {
- var rect = child.getBoundingClientRect();
- var viewPad = this._getViewPadding();
- var viewRect = this._viewDiv.getBoundingClientRect();
- if (rect.top < viewRect.top + viewPad.top) {
- child = this._getLineNext(child) || child;
- }
- }
- return child.lineIndex;
- },
- _getXToOffset: function (lineIndex, x) {
- var model = this._model;
- var lineStart = model.getLineStart(lineIndex);
- var lineEnd = model.getLineEnd(lineIndex);
- if (lineStart === lineEnd) {
- return lineStart;
- }
- var document = this._frameDocument;
- var clientDiv = this._clientDiv;
- var dummy;
- var child = this._getLineNode(lineIndex);
- if (!child) {
- child = dummy = this._createLine(clientDiv, null, document, lineIndex, model);
- }
- var lineRect = this._getLineBoundingClientRect(child);
- if (x < lineRect.left) { x = lineRect.left; }
- if (x > lineRect.right) { x = lineRect.right; }
- /*
- * Bug in IE 8 and earlier. The coordinates of getClientRects() are relative to
- * the browser window. The fix is to convert to the frame window before using it.
- */
- var deltaX = 0, rects;
- if (isIE < 9) {
- rects = child.getClientRects();
- var minLeft = rects[0].left;
- for (var i=1; i 1) {
- var mid = Math.floor((high + low) / 2);
- start = low + 1;
- end = mid === nodeLength - 1 && lineChild.ignoreChars ? textNode.length : mid + 1;
- if (isRangeRects) {
- range.setStart(textNode, start);
- range.setEnd(textNode, end);
- } else {
- range.moveToElementText(lineChild);
- range.move("character", start);
- range.moveEnd("character", end - start);
- }
- rects = range.getClientRects();
- var found = false;
- for (var k = 0; k < rects.length; k++) {
- rect = rects[k];
- var rangeLeft = rect.left * logicalXDPI / deviceXDPI - deltaX;
- var rangeRight = rect.right * logicalXDPI / deviceXDPI - deltaX;
- if (rangeLeft <= x && x < rangeRight) {
- found = true;
- break;
- }
- }
- if (found) {
- high = mid;
- } else {
- low = mid;
- }
- }
- offset += high;
- start = high;
- end = high === nodeLength - 1 && lineChild.ignoreChars ? textNode.length : Math.min(high + 1, textNode.length);
- if (isRangeRects) {
- range.setStart(textNode, start);
- range.setEnd(textNode, end);
- } else {
- range.moveToElementText(lineChild);
- range.move("character", start);
- range.moveEnd("character", end - start);
- }
- rect = range.getClientRects()[0];
- //TODO test for character trailing (wrong for bidi)
- if (x > ((rect.left * logicalXDPI / deviceXDPI - deltaX) + ((rect.right - rect.left) * logicalXDPI / deviceXDPI / 2))) {
- offset++;
- }
- } else {
- var newText = [];
- for (var q = 0; q < nodeLength; q++) {
- newText.push("");
- if (q === nodeLength - 1) {
- newText.push(textNode.data.substring(q));
- } else {
- newText.push(textNode.data.substring(q, q + 1));
- }
- newText.push("");
- }
- lineChild.innerHTML = newText.join("");
- var rangeChild = lineChild.firstChild;
- while (rangeChild) {
- rect = rangeChild.getBoundingClientRect();
- if (rect.left <= x && x < rect.right) {
- //TODO test for character trailing (wrong for bidi)
- if (x > rect.left + (rect.right - rect.left) / 2) {
- offset++;
- }
- break;
- }
- offset++;
- rangeChild = rangeChild.nextSibling;
- }
- if (!dummy) {
- lineChild.innerHTML = "";
- lineChild.appendChild(textNode);
- /*
- * Removing the element node that holds the selection start or end
- * causes the selection to be lost. The fix is to detect this case
- * and restore the selection.
- */
- var s = this._getSelection();
- if ((offset <= s.start && s.start < offset + nodeLength) || (offset <= s.end && s.end < offset + nodeLength)) {
- this._updateDOMSelection();
- }
- }
- }
- break done;
- }
- }
- offset += nodeLength;
- lineChild = lineChild.nextSibling;
- }
- if (dummy) { clientDiv.removeChild(dummy); }
- return Math.min(lineEnd, Math.max(lineStart, offset));
- },
- _getYToLine: function (y) {
- var viewPad = this._getViewPadding();
- var viewRect = this._viewDiv.getBoundingClientRect();
- y -= viewRect.top + viewPad.top;
- var lineHeight = this._getLineHeight();
- var lineIndex = Math.floor((y + this._getScroll().y) / lineHeight);
- var lineCount = this._model.getLineCount();
- return Math.max(0, Math.min(lineCount - 1, lineIndex));
- },
- _getOffsetBounds: function(offset) {
- var model = this._model;
- var lineIndex = model.getLineAtOffset(offset);
- var lineHeight = this._getLineHeight();
- var scroll = this._getScroll();
- var viewPad = this._getViewPadding();
- var viewRect = this._viewDiv.getBoundingClientRect();
- var bounds = this._getBoundsAtOffset(offset);
- var left = bounds.left;
- var right = bounds.right;
- var top = (lineIndex * lineHeight) - scroll.y + viewRect.top + viewPad.top;
- var bottom = top + lineHeight;
- return {left: left, top: top, right: right, bottom: bottom};
- },
- _getVisible: function() {
- var temp = this._parent;
- var parentDocument = temp.ownerDocument;
- while (temp !== parentDocument) {
- var hidden;
- if (isIE < 9) {
- hidden = temp.currentStyle && temp.currentStyle.display === "none";
- } else {
- var tempStyle = parentDocument.defaultView.getComputedStyle(temp, null);
- hidden = tempStyle && tempStyle.getPropertyValue("display") === "none";
- }
- if (hidden) { return "hidden"; }
- temp = temp.parentNode;
- if (!temp) { return "disconnected"; }
- }
- return "visible";
- },
- _hitOffset: function (offset, x, y) {
- var bounds = this._getOffsetBounds(offset);
- var left = bounds.left;
- var right = bounds.right;
- var top = bounds.top;
- var bottom = bounds.bottom;
- var area = 20;
- left -= area;
- top -= area;
- right += area;
- bottom += area;
- return (left <= x && x <= right && top <= y && y <= bottom);
- },
- _hookEvents: function() {
- var self = this;
- this._modelListener = {
- /** @private */
- onChanging: function(modelChangingEvent) {
- self._onModelChanging(modelChangingEvent);
- },
- /** @private */
- onChanged: function(modelChangedEvent) {
- self._onModelChanged(modelChangedEvent);
- }
- };
- this._model.addEventListener("Changing", this._modelListener.onChanging);
- this._model.addEventListener("Changed", this._modelListener.onChanged);
-
- var clientDiv = this._clientDiv;
- var viewDiv = this._viewDiv;
- var body = this._frameDocument.body;
- var handlers = this._handlers = [];
- var resizeNode = isIE < 9 ? this._frame : this._frameWindow;
- var focusNode = isPad ? this._textArea : (isIE || isFirefox ? this._clientDiv: this._frameWindow);
- handlers.push({target: this._frameWindow, type: "unload", handler: function(e) { return self._handleUnload(e);}});
- handlers.push({target: resizeNode, type: "resize", handler: function(e) { return self._handleResize(e);}});
- handlers.push({target: focusNode, type: "blur", handler: function(e) { return self._handleBlur(e);}});
- handlers.push({target: focusNode, type: "focus", handler: function(e) { return self._handleFocus(e);}});
- handlers.push({target: viewDiv, type: "scroll", handler: function(e) { return self._handleScroll(e);}});
- if (isPad) {
- var touchDiv = this._touchDiv;
- var textArea = this._textArea;
- handlers.push({target: textArea, type: "keydown", handler: function(e) { return self._handleKeyDown(e);}});
- handlers.push({target: textArea, type: "input", handler: function(e) { return self._handleInput(e); }});
- handlers.push({target: textArea, type: "textInput", handler: function(e) { return self._handleTextInput(e); }});
- handlers.push({target: textArea, type: "click", handler: function(e) { return self._handleTextAreaClick(e); }});
- handlers.push({target: touchDiv, type: "touchstart", handler: function(e) { return self._handleTouchStart(e); }});
- handlers.push({target: touchDiv, type: "touchmove", handler: function(e) { return self._handleTouchMove(e); }});
- handlers.push({target: touchDiv, type: "touchend", handler: function(e) { return self._handleTouchEnd(e); }});
- } else {
- var topNode = this._overlayDiv || this._clientDiv;
- var grabNode = isIE ? clientDiv : this._frameWindow;
- handlers.push({target: clientDiv, type: "keydown", handler: function(e) { return self._handleKeyDown(e);}});
- handlers.push({target: clientDiv, type: "keypress", handler: function(e) { return self._handleKeyPress(e);}});
- handlers.push({target: clientDiv, type: "keyup", handler: function(e) { return self._handleKeyUp(e);}});
- handlers.push({target: clientDiv, type: "selectstart", handler: function(e) { return self._handleSelectStart(e);}});
- handlers.push({target: clientDiv, type: "contextmenu", handler: function(e) { return self._handleContextMenu(e);}});
- handlers.push({target: clientDiv, type: "copy", handler: function(e) { return self._handleCopy(e);}});
- handlers.push({target: clientDiv, type: "cut", handler: function(e) { return self._handleCut(e);}});
- handlers.push({target: clientDiv, type: "paste", handler: function(e) { return self._handlePaste(e);}});
- handlers.push({target: clientDiv, type: "mousedown", handler: function(e) { return self._handleMouseDown(e);}});
- handlers.push({target: clientDiv, type: "mouseover", handler: function(e) { return self._handleMouseOver(e);}});
- handlers.push({target: clientDiv, type: "mouseout", handler: function(e) { return self._handleMouseOut(e);}});
- handlers.push({target: grabNode, type: "mouseup", handler: function(e) { return self._handleMouseUp(e);}});
- handlers.push({target: grabNode, type: "mousemove", handler: function(e) { return self._handleMouseMove(e);}});
- handlers.push({target: body, type: "mousedown", handler: function(e) { return self._handleBodyMouseDown(e);}});
- handlers.push({target: body, type: "mouseup", handler: function(e) { return self._handleBodyMouseUp(e);}});
- handlers.push({target: topNode, type: "dragstart", handler: function(e) { return self._handleDragStart(e);}});
- handlers.push({target: topNode, type: "drag", handler: function(e) { return self._handleDrag(e);}});
- handlers.push({target: topNode, type: "dragend", handler: function(e) { return self._handleDragEnd(e);}});
- handlers.push({target: topNode, type: "dragenter", handler: function(e) { return self._handleDragEnter(e);}});
- handlers.push({target: topNode, type: "dragover", handler: function(e) { return self._handleDragOver(e);}});
- handlers.push({target: topNode, type: "dragleave", handler: function(e) { return self._handleDragLeave(e);}});
- handlers.push({target: topNode, type: "drop", handler: function(e) { return self._handleDrop(e);}});
- if (isChrome) {
- handlers.push({target: this._parentDocument, type: "mousemove", handler: function(e) { return self._handleMouseMove(e);}});
- handlers.push({target: this._parentDocument, type: "mouseup", handler: function(e) { return self._handleMouseUp(e);}});
- }
- if (isIE) {
- handlers.push({target: this._frameDocument, type: "activate", handler: function(e) { return self._handleDocFocus(e); }});
- }
- if (isFirefox) {
- handlers.push({target: this._frameDocument, type: "focus", handler: function(e) { return self._handleDocFocus(e); }});
- }
- if (!isIE && !isOpera) {
- var wheelEvent = isFirefox ? "DOMMouseScroll" : "mousewheel";
- handlers.push({target: this._viewDiv, type: wheelEvent, handler: function(e) { return self._handleMouseWheel(e); }});
- }
- if (isFirefox && !isWindows) {
- handlers.push({target: this._clientDiv, type: "DOMCharacterDataModified", handler: function (e) { return self._handleDataModified(e); }});
- }
- if (this._overlayDiv) {
- handlers.push({target: this._overlayDiv, type: "mousedown", handler: function(e) { return self._handleMouseDown(e);}});
- handlers.push({target: this._overlayDiv, type: "mouseover", handler: function(e) { return self._handleMouseOver(e);}});
- handlers.push({target: this._overlayDiv, type: "mouseout", handler: function(e) { return self._handleMouseOut(e);}});
- handlers.push({target: this._overlayDiv, type: "contextmenu", handler: function(e) { return self._handleContextMenu(e); }});
- }
- if (!isW3CEvents) {
- handlers.push({target: this._clientDiv, type: "dblclick", handler: function(e) { return self._handleDblclick(e); }});
- }
- }
- for (var i=0; i start) {
- if (selection.end > start && selection.start < start + removedCharCount) {
- // selection intersects replaced text. set caret behind text change
- selection.setCaret(start + addedCharCount);
- } else {
- // move selection to keep same text selected
- selection.start += addedCharCount - removedCharCount;
- selection.end += addedCharCount - removedCharCount;
- }
- this._setSelection(selection, false, false);
- }
-
- var model = this._model;
- var startLine = model.getLineAtOffset(start);
- var child = this._getLineNext();
- while (child) {
- var lineIndex = child.lineIndex;
- if (startLine <= lineIndex && lineIndex <= startLine + removedLineCount) {
- if (startLine === lineIndex && !child.modelChangedEvent && !child.lineRemoved) {
- child.modelChangedEvent = modelChangedEvent;
- child.lineChanged = true;
- } else {
- child.lineRemoved = true;
- child.lineChanged = false;
- child.modelChangedEvent = null;
- }
- }
- if (lineIndex > startLine + removedLineCount) {
- child.lineIndex = lineIndex + addedLineCount - removedLineCount;
- }
- child = this._getLineNext(child);
- }
- if (startLine <= this._maxLineIndex && this._maxLineIndex <= startLine + removedLineCount) {
- this._checkMaxLineIndex = this._maxLineIndex;
- this._maxLineIndex = -1;
- this._maxLineWidth = 0;
- }
- this._updatePage();
- },
- _onModelChanging: function(modelChangingEvent) {
- modelChangingEvent.type = "ModelChanging";
- this.onModelChanging(modelChangingEvent);
- modelChangingEvent.type = "Changing";
- },
- _queueUpdatePage: function() {
- if (this._updateTimer) { return; }
- var self = this;
- this._updateTimer = setTimeout(function() {
- self._updateTimer = null;
- self._updatePage();
- }, 0);
- },
- _reset: function() {
- this._maxLineIndex = -1;
- this._maxLineWidth = 0;
- this._columnX = -1;
- this._topChild = null;
- this._bottomChild = null;
- this._partialY = 0;
- this._setSelection(new Selection (0, 0, false), false, false);
- if (this._viewDiv) {
- this._viewDiv.scrollLeft = 0;
- this._viewDiv.scrollTop = 0;
- }
- var clientDiv = this._clientDiv;
- if (clientDiv) {
- var child = clientDiv.firstChild;
- while (child) {
- child.lineRemoved = true;
- child = child.nextSibling;
- }
- /*
- * Bug in Firefox. For some reason, the caret does not show after the
- * view is refreshed. The fix is to toggle the contentEditable state and
- * force the clientDiv to loose and receive focus if it is focused.
- */
- if (isFirefox) {
- this._ignoreFocus = false;
- var hasFocus = this._hasFocus;
- if (hasFocus) { clientDiv.blur(); }
- clientDiv.contentEditable = false;
- clientDiv.contentEditable = true;
- if (hasFocus) { clientDiv.focus(); }
- this._ignoreFocus = false;
- }
- }
- },
- _resizeTouchDiv: function() {
- var viewRect = this._viewDiv.getBoundingClientRect();
- var parentRect = this._frame.getBoundingClientRect();
- var temp = this._frame;
- while (temp) {
- if (temp.style && temp.style.top) { break; }
- temp = temp.parentNode;
- }
- var parentTop = parentRect.top;
- if (temp) {
- parentTop -= temp.getBoundingClientRect().top;
- } else {
- parentTop += this._parentDocument.body.scrollTop;
- }
- temp = this._frame;
- while (temp) {
- if (temp.style && temp.style.left) { break; }
- temp = temp.parentNode;
- }
- var parentLeft = parentRect.left;
- if (temp) {
- parentLeft -= temp.getBoundingClientRect().left;
- } else {
- parentLeft += this._parentDocument.body.scrollLeft;
- }
- var touchDiv = this._touchDiv;
- touchDiv.style.left = (parentLeft + viewRect.left) + "px";
- touchDiv.style.top = (parentTop + viewRect.top) + "px";
- touchDiv.style.width = viewRect.width + "px";
- touchDiv.style.height = viewRect.height + "px";
- },
- _scrollView: function (pixelX, pixelY) {
- /*
- * Always set _ensureCaretVisible to false so that the view does not scroll
- * to show the caret when scrollView is not called from showCaret().
- */
- this._ensureCaretVisible = false;
-
- /*
- * Scrolling is done only by setting the scrollLeft and scrollTop fields in the
- * view div. This causes an updatePage from the scroll event. In some browsers
- * this event is asynchronous and forcing update page to run synchronously
- * leads to redraw problems.
- * On Chrome 11, the view redrawing at times when holding PageDown/PageUp key.
- * On Firefox 4 for Linux, the view redraws the first page when holding
- * PageDown/PageUp key, but it will not redraw again until the key is released.
- */
- var viewDiv = this._viewDiv;
- if (pixelX) { viewDiv.scrollLeft += pixelX; }
- if (pixelY) { viewDiv.scrollTop += pixelY; }
- },
- _setClipboardText: function (text, event) {
- var clipboardText;
- if (this._frameWindow.clipboardData) {
- //IE
- clipboardText = [];
- this._convertDelimiter(text, function(t) {clipboardText.push(t);}, function() {clipboardText.push(platformDelimiter);});
- return this._frameWindow.clipboardData.setData("Text", clipboardText.join(""));
- }
- /* Feature in Chrome, clipboardData.setData is no-op on Chrome even though it returns true */
- if (isChrome || isFirefox || !event) {
- var window = this._frameWindow;
- var document = this._frameDocument;
- var child = document.createElement("PRE");
- child.style.position = "fixed";
- child.style.left = "-1000px";
- this._convertDelimiter(text,
- function(t) {
- child.appendChild(document.createTextNode(t));
- },
- function() {
- child.appendChild(document.createElement("BR"));
- }
- );
- child.appendChild(document.createTextNode(" "));
- this._clientDiv.appendChild(child);
- var range = document.createRange();
- range.setStart(child.firstChild, 0);
- range.setEndBefore(child.lastChild);
- var sel = window.getSelection();
- if (sel.rangeCount > 0) { sel.removeAllRanges(); }
- sel.addRange(range);
- var self = this;
- /** @ignore */
- var cleanup = function() {
- if (child && child.parentNode === self._clientDiv) {
- self._clientDiv.removeChild(child);
- }
- self._updateDOMSelection();
- };
- var result = false;
- /*
- * Try execCommand first, it works on firefox with clipboard permission,
- * chrome 5, safari 4.
- */
- this._ignoreCopy = true;
- try {
- result = document.execCommand("copy", false, null);
- } catch (e) {}
- this._ignoreCopy = false;
- if (!result) {
- if (event) {
- setTimeout(cleanup, 0);
- return false;
- }
- }
- /* no event and no permission, copy can not be done */
- cleanup();
- return true;
- }
- if (event && event.clipboardData) {
- //webkit
- clipboardText = [];
- this._convertDelimiter(text, function(t) {clipboardText.push(t);}, function() {clipboardText.push(platformDelimiter);});
- return event.clipboardData.setData("text/plain", clipboardText.join(""));
- }
- },
- _setDOMSelection: function (startNode, startOffset, endNode, endOffset) {
- var window = this._frameWindow;
- var document = this._frameDocument;
- var startLineNode, startLineOffset, endLineNode, endLineOffset;
- var offset = 0;
- var lineChild = startNode.firstChild;
- var node, nodeLength, model = this._model;
- var startLineEnd = model.getLine(startNode.lineIndex).length;
- while (lineChild) {
- node = lineChild.firstChild;
- nodeLength = node.length;
- if (lineChild.ignoreChars) {
- nodeLength -= lineChild.ignoreChars;
- }
- if (offset + nodeLength > startOffset || offset + nodeLength >= startLineEnd) {
- startLineNode = node;
- startLineOffset = startOffset - offset;
- if (lineChild.ignoreChars && nodeLength > 0 && startLineOffset === nodeLength) {
- startLineOffset += lineChild.ignoreChars;
- }
- break;
- }
- offset += nodeLength;
- lineChild = lineChild.nextSibling;
- }
- offset = 0;
- lineChild = endNode.firstChild;
- var endLineEnd = this._model.getLine(endNode.lineIndex).length;
- while (lineChild) {
- node = lineChild.firstChild;
- nodeLength = node.length;
- if (lineChild.ignoreChars) {
- nodeLength -= lineChild.ignoreChars;
- }
- if (nodeLength + offset > endOffset || offset + nodeLength >= endLineEnd) {
- endLineNode = node;
- endLineOffset = endOffset - offset;
- if (lineChild.ignoreChars && nodeLength > 0 && endLineOffset === nodeLength) {
- endLineOffset += lineChild.ignoreChars;
- }
- break;
- }
- offset += nodeLength;
- lineChild = lineChild.nextSibling;
- }
-
- this._setDOMFullSelection(startNode, startOffset, startLineEnd, endNode, endOffset, endLineEnd);
- if (isPad) { return; }
-
- var range;
- if (window.getSelection) {
- //W3C
- range = document.createRange();
- range.setStart(startLineNode, startLineOffset);
- range.setEnd(endLineNode, endLineOffset);
- var sel = window.getSelection();
- this._ignoreSelect = false;
- if (sel.rangeCount > 0) { sel.removeAllRanges(); }
- sel.addRange(range);
- this._ignoreSelect = true;
- } else if (document.selection) {
- //IE < 9
- var body = document.body;
-
- /*
- * Bug in IE. For some reason when text is deselected the overflow
- * selection at the end of some lines does not get redrawn. The
- * fix is to create a DOM element in the body to force a redraw.
- */
- var child = document.createElement("DIV");
- body.appendChild(child);
- body.removeChild(child);
-
- range = body.createTextRange();
- range.moveToElementText(startLineNode.parentNode);
- range.moveStart("character", startLineOffset);
- var endRange = body.createTextRange();
- endRange.moveToElementText(endLineNode.parentNode);
- endRange.moveStart("character", endLineOffset);
- range.setEndPoint("EndToStart", endRange);
- this._ignoreSelect = false;
- range.select();
- this._ignoreSelect = true;
- }
- },
- _setDOMFullSelection: function(startNode, startOffset, startLineEnd, endNode, endOffset, endLineEnd) {
- var model = this._model;
- if (this._selDiv1) {
- var startLineBounds, l;
- startLineBounds = this._getLineBoundingClientRect(startNode);
- if (startOffset === 0) {
- l = startLineBounds.left;
- } else {
- if (startOffset >= startLineEnd) {
- l = startLineBounds.right;
- } else {
- this._ignoreDOMSelection = true;
- l = this._getBoundsAtOffset(model.getLineStart(startNode.lineIndex) + startOffset).left;
- this._ignoreDOMSelection = false;
- }
- }
- var textArea = this._textArea;
- if (textArea && isPad) {
- textArea.selectionStart = textArea.selectionEnd = 0;
- var rect = this._frame.getBoundingClientRect();
- var touchRect = this._touchDiv.getBoundingClientRect();
- var viewBounds = this._viewDiv.getBoundingClientRect();
- if (!(viewBounds.left <= l && l <= viewBounds.left + viewBounds.width &&
- viewBounds.top <= startLineBounds.top && startLineBounds.top <= viewBounds.top + viewBounds.height) ||
- !(startNode === endNode && startOffset === endOffset))
- {
- textArea.style.left = "-1000px";
- } else {
- textArea.style.left = (l - 4 + rect.left - touchRect.left) + "px";
- }
- textArea.style.top = (startLineBounds.top + rect.top - touchRect.top) + "px";
- textArea.style.width = "6px";
- textArea.style.height = (startLineBounds.bottom - startLineBounds.top) + "px";
- }
-
- var selDiv = this._selDiv1;
- selDiv.style.width = "0px";
- selDiv.style.height = "0px";
- selDiv = this._selDiv2;
- selDiv.style.width = "0px";
- selDiv.style.height = "0px";
- selDiv = this._selDiv3;
- selDiv.style.width = "0px";
- selDiv.style.height = "0px";
- if (!(startNode === endNode && startOffset === endOffset)) {
- var handleWidth = isPad ? 2 : 0;
- var handleBorder = handleWidth + "px blue solid";
- var viewPad = this._getViewPadding();
- var clientRect = this._clientDiv.getBoundingClientRect();
- var viewRect = this._viewDiv.getBoundingClientRect();
- var left = viewRect.left + viewPad.left;
- var right = clientRect.right;
- var top = viewRect.top + viewPad.top;
- var bottom = clientRect.bottom;
- var hd = 0, vd = 0;
- if (this._clipDiv) {
- var clipRect = this._clipDiv.getBoundingClientRect();
- hd = clipRect.left - this._clipDiv.scrollLeft;
- vd = clipRect.top;
- }
- var r;
- var endLineBounds = this._getLineBoundingClientRect(endNode);
- if (endOffset === 0) {
- r = endLineBounds.left;
- } else {
- if (endOffset >= endLineEnd) {
- r = endLineBounds.right;
- } else {
- this._ignoreDOMSelection = true;
- r = this._getBoundsAtOffset(model.getLineStart(endNode.lineIndex) + endOffset).left;
- this._ignoreDOMSelection = false;
- }
- }
- var sel1Div = this._selDiv1;
- var sel1Left = Math.min(right, Math.max(left, l));
- var sel1Top = Math.min(bottom, Math.max(top, startLineBounds.top));
- var sel1Right = right;
- var sel1Bottom = Math.min(bottom, Math.max(top, startLineBounds.bottom));
- sel1Div.style.left = (sel1Left - hd) + "px";
- sel1Div.style.top = (sel1Top - vd) + "px";
- sel1Div.style.width = Math.max(0, sel1Right - sel1Left) + "px";
- sel1Div.style.height = Math.max(0, sel1Bottom - sel1Top) + (isPad ? 1 : 0) + "px";
- if (isPad) {
- sel1Div.style.borderLeft = handleBorder;
- sel1Div.style.borderRight = "0px";
- }
- if (startNode === endNode) {
- sel1Right = Math.min(r, right);
- sel1Div.style.width = Math.max(0, sel1Right - sel1Left - handleWidth * 2) + "px";
- if (isPad) {
- sel1Div.style.borderRight = handleBorder;
- }
- } else {
- var sel3Left = left;
- var sel3Top = Math.min(bottom, Math.max(top, endLineBounds.top));
- var sel3Right = Math.min(right, Math.max(left, r));
- var sel3Bottom = Math.min(bottom, Math.max(top, endLineBounds.bottom));
- var sel3Div = this._selDiv3;
- sel3Div.style.left = (sel3Left - hd) + "px";
- sel3Div.style.top = (sel3Top - vd) + "px";
- sel3Div.style.width = Math.max(0, sel3Right - sel3Left - handleWidth) + "px";
- sel3Div.style.height = Math.max(0, sel3Bottom - sel3Top) + "px";
- if (isPad) {
- sel3Div.style.borderRight = handleBorder;
- }
- if (sel3Top - sel1Bottom > 0) {
- var sel2Div = this._selDiv2;
- sel2Div.style.left = (left - hd) + "px";
- sel2Div.style.top = (sel1Bottom - vd) + "px";
- sel2Div.style.width = Math.max(0, right - left) + "px";
- sel2Div.style.height = Math.max(0, sel3Top - sel1Bottom) + (isPad ? 1 : 0) + "px";
- }
- }
- }
- }
- },
- _setGrab: function (target) {
- if (target === this._grabControl) { return; }
- if (target) {
- if (target.setCapture) { target.setCapture(); }
- this._grabControl = target;
- } else {
- if (this._grabControl.releaseCapture) { this._grabControl.releaseCapture(); }
- this._grabControl = null;
- }
- },
- _setLinksVisible: function(visible) {
- if (this._linksVisible === visible) { return; }
- this._linksVisible = visible;
- /*
- * Feature in IE. The client div looses focus and does not regain it back
- * when the content editable flag is reset. The fix is to remember that it
- * had focus when the flag is cleared and give focus back to the div when
- * the flag is set.
- */
- if (isIE && visible) {
- this._hadFocus = this._hasFocus;
- }
- var clientDiv = this._clientDiv;
- clientDiv.contentEditable = !visible;
- if (this._hadFocus && !visible) {
- clientDiv.focus();
- }
- if (this._overlayDiv) {
- this._overlayDiv.style.zIndex = visible ? "-1" : "1";
- }
- var document = this._frameDocument;
- var line = this._getLineNext();
- while (line) {
- if (line.hasLink) {
- var lineChild = line.firstChild;
- while (lineChild) {
- var next = lineChild.nextSibling;
- var style = lineChild.viewStyle;
- if (style && style.tagName === "A") {
- line.replaceChild(this._createSpan(line, document, lineChild.firstChild.data, style), lineChild);
- }
- lineChild = next;
- }
- }
- line = this._getLineNext(line);
- }
- },
- _setSelection: function (selection, scroll, update, pageScroll) {
- if (selection) {
- this._columnX = -1;
- if (update === undefined) { update = true; }
- var oldSelection = this._selection;
- if (!oldSelection.equals(selection)) {
- this._selection = selection;
- var e = {
- type: "Selection",
- oldValue: {start:oldSelection.start, end:oldSelection.end},
- newValue: {start:selection.start, end:selection.end}
- };
- this.onSelection(e);
- }
- /*
- * Always showCaret(), even when the selection is not changing, to ensure the
- * caret is visible. Note that some views do not scroll to show the caret during
- * keyboard navigation when the selection does not chanage. For example, line down
- * when the caret is already at the last line.
- */
- if (scroll) { update = !this._showCaret(false, pageScroll); }
-
- /*
- * Sometimes the browser changes the selection
- * as result of method calls or "leaked" events.
- * The fix is to set the visual selection even
- * when the logical selection is not changed.
- */
- if (update) { this._updateDOMSelection(); }
- }
- },
- _setSelectionTo: function (x, y, extent, drag) {
- var model = this._model, offset;
- var selection = this._getSelection();
- var lineIndex = this._getYToLine(y);
- if (this._clickCount === 1) {
- offset = this._getXToOffset(lineIndex, x);
- if (drag && !extent) {
- if (selection.start <= offset && offset < selection.end) {
- this._dragOffset = offset;
- return false;
- }
- }
- selection.extend(offset);
- if (!extent) { selection.collapse(); }
- } else {
- var word = (this._clickCount & 1) === 0;
- var start, end;
- if (word) {
- offset = this._getXToOffset(lineIndex, x);
- if (this._doubleClickSelection) {
- if (offset >= this._doubleClickSelection.start) {
- start = this._doubleClickSelection.start;
- end = this._getOffset(offset, "wordend", +1);
- } else {
- start = this._getOffset(offset, "word", -1);
- end = this._doubleClickSelection.end;
- }
- } else {
- start = this._getOffset(offset, "word", -1);
- end = this._getOffset(start, "wordend", +1);
- }
- } else {
- if (this._doubleClickSelection) {
- var doubleClickLine = model.getLineAtOffset(this._doubleClickSelection.start);
- if (lineIndex >= doubleClickLine) {
- start = model.getLineStart(doubleClickLine);
- end = model.getLineEnd(lineIndex);
- } else {
- start = model.getLineStart(lineIndex);
- end = model.getLineEnd(doubleClickLine);
- }
- } else {
- start = model.getLineStart(lineIndex);
- end = model.getLineEnd(lineIndex);
- }
- }
- selection.setCaret(start);
- selection.extend(end);
- }
- this._setSelection(selection, true, true);
- return true;
- },
- _setStyleSheet: function(stylesheet) {
- var oldstylesheet = this._stylesheet;
- if (!(oldstylesheet instanceof Array)) {
- oldstylesheet = [oldstylesheet];
- }
- this._stylesheet = stylesheet;
- if (!(stylesheet instanceof Array)) {
- stylesheet = [stylesheet];
- }
- var document = this._frameDocument;
- var documentStylesheet = document.styleSheets;
- var head = document.getElementsByTagName("head")[0];
- var changed = false;
- var i = 0, sheet, oldsheet, documentSheet, ownerNode, styleNode, textNode;
- while (i < stylesheet.length) {
- if (i >= oldstylesheet.length) { break; }
- sheet = stylesheet[i];
- oldsheet = oldstylesheet[i];
- if (sheet !== oldsheet) {
- if (this._isLinkURL(sheet)) {
- return true;
- } else {
- documentSheet = documentStylesheet[i+1];
- ownerNode = documentSheet.ownerNode;
- styleNode = document.createElement('STYLE');
- textNode = document.createTextNode(sheet);
- styleNode.appendChild(textNode);
- head.replaceChild(styleNode, ownerNode);
- changed = true;
- }
- }
- i++;
- }
- if (i < oldstylesheet.length) {
- while (i < oldstylesheet.length) {
- sheet = oldstylesheet[i];
- if (this._isLinkURL(sheet)) {
- return true;
- } else {
- documentSheet = documentStylesheet[i+1];
- ownerNode = documentSheet.ownerNode;
- head.removeChild(ownerNode);
- changed = true;
- }
- i++;
- }
- } else {
- while (i < stylesheet.length) {
- sheet = stylesheet[i];
- if (this._isLinkURL(sheet)) {
- return true;
- } else {
- styleNode = document.createElement('STYLE');
- textNode = document.createTextNode(sheet);
- styleNode.appendChild(textNode);
- head.appendChild(styleNode);
- changed = true;
- }
- i++;
- }
- }
- if (changed) {
- this._updateStyle();
- }
- return false;
- },
- _setFullSelection: function(fullSelection, init) {
- this._fullSelection = fullSelection;
-
- /*
- * Bug in IE 8. For some reason, during scrolling IE does not reflow the elements
- * that are used to compute the location for the selection divs. This causes the
- * divs to be placed at the wrong location. The fix is to disabled full selection for IE8.
- */
- if (isIE < 9) {
- this._fullSelection = false;
- }
- if (isWebkit) {
- this._fullSelection = true;
- }
- var parent = this._clipDiv || this._scrollDiv;
- if (!parent) {
- return;
- }
- if (!isPad && !this._fullSelection) {
- if (this._selDiv1) {
- parent.removeChild(this._selDiv1);
- this._selDiv1 = null;
- }
- if (this._selDiv2) {
- parent.removeChild(this._selDiv2);
- this._selDiv2 = null;
- }
- if (this._selDiv3) {
- parent.removeChild(this._selDiv3);
- this._selDiv3 = null;
- }
- return;
- }
-
- if (!this._selDiv1 && (isPad || (this._fullSelection && !isWebkit))) {
- var frameDocument = this._frameDocument;
- this._hightlightRGB = "Highlight";
- var selDiv1 = frameDocument.createElement("DIV");
- this._selDiv1 = selDiv1;
- selDiv1.id = "selDiv1";
- selDiv1.style.position = this._clipDiv ? "absolute" : "fixed";
- selDiv1.style.borderWidth = "0px";
- selDiv1.style.margin = "0px";
- selDiv1.style.padding = "0px";
- selDiv1.style.outline = "none";
- selDiv1.style.background = this._hightlightRGB;
- selDiv1.style.width = "0px";
- selDiv1.style.height = "0px";
- selDiv1.style.zIndex = "0";
- parent.appendChild(selDiv1);
- var selDiv2 = frameDocument.createElement("DIV");
- this._selDiv2 = selDiv2;
- selDiv2.id = "selDiv2";
- selDiv2.style.position = this._clipDiv ? "absolute" : "fixed";
- selDiv2.style.borderWidth = "0px";
- selDiv2.style.margin = "0px";
- selDiv2.style.padding = "0px";
- selDiv2.style.outline = "none";
- selDiv2.style.background = this._hightlightRGB;
- selDiv2.style.width = "0px";
- selDiv2.style.height = "0px";
- selDiv2.style.zIndex = "0";
- parent.appendChild(selDiv2);
- var selDiv3 = frameDocument.createElement("DIV");
- this._selDiv3 = selDiv3;
- selDiv3.id = "selDiv3";
- selDiv3.style.position = this._clipDiv ? "absolute" : "fixed";
- selDiv3.style.borderWidth = "0px";
- selDiv3.style.margin = "0px";
- selDiv3.style.padding = "0px";
- selDiv3.style.outline = "none";
- selDiv3.style.background = this._hightlightRGB;
- selDiv3.style.width = "0px";
- selDiv3.style.height = "0px";
- selDiv3.style.zIndex = "0";
- parent.appendChild(selDiv3);
-
- /*
- * Bug in Firefox. The Highlight color is mapped to list selection
- * background instead of the text selection background. The fix
- * is to map known colors using a table or fallback to light blue.
- */
- if (isFirefox && isMac) {
- var style = this._frameWindow.getComputedStyle(selDiv3, null);
- var rgb = style.getPropertyValue("background-color");
- switch (rgb) {
- case "rgb(119, 141, 168)": rgb = "rgb(199, 208, 218)"; break;
- case "rgb(127, 127, 127)": rgb = "rgb(198, 198, 198)"; break;
- case "rgb(255, 193, 31)": rgb = "rgb(250, 236, 115)"; break;
- case "rgb(243, 70, 72)": rgb = "rgb(255, 176, 139)"; break;
- case "rgb(255, 138, 34)": rgb = "rgb(255, 209, 129)"; break;
- case "rgb(102, 197, 71)": rgb = "rgb(194, 249, 144)"; break;
- case "rgb(140, 78, 184)": rgb = "rgb(232, 184, 255)"; break;
- default: rgb = "rgb(180, 213, 255)"; break;
- }
- this._hightlightRGB = rgb;
- selDiv1.style.background = rgb;
- selDiv2.style.background = rgb;
- selDiv3.style.background = rgb;
- if (!this._insertedSelRule) {
- var styleSheet = frameDocument.styleSheets[0];
- styleSheet.insertRule("::-moz-selection {background: " + rgb + "; }", 0);
- this._insertedSelRule = true;
- }
- }
- if (!init) {
- this._updateDOMSelection();
- }
- }
- },
- _setTabSize: function (tabSize, init) {
- this._tabSize = tabSize;
- this._customTabSize = undefined;
- var clientDiv = this._clientDiv;
- if (isOpera) {
- if (clientDiv) { clientDiv.style.OTabSize = this._tabSize+""; }
- } else if (isFirefox >= 4) {
- if (clientDiv) { clientDiv.style.MozTabSize = this._tabSize+""; }
- } else if (this._tabSize !== 8) {
- this._customTabSize = this._tabSize;
- if (!init) {
- this.redrawLines();
- }
- }
- },
- _setThemeClass: function (themeClass, init) {
- this._themeClass = themeClass;
- var document = this._frameDocument;
- if (document) {
- var viewContainerClass = "viewContainer";
- if (this._themeClass) { viewContainerClass += " " + this._themeClass; }
- document.body.className = viewContainerClass;
- if (!init) {
- this._updateStyle();
- }
- }
- },
- _showCaret: function (allSelection, pageScroll) {
- if (!this._clientDiv) { return; }
- var model = this._model;
- var selection = this._getSelection();
- var scroll = this._getScroll();
- var caret = selection.getCaret();
- var start = selection.start;
- var end = selection.end;
- var startLine = model.getLineAtOffset(start);
- var endLine = model.getLineAtOffset(end);
- var endInclusive = Math.max(Math.max(start, model.getLineStart(endLine)), end - 1);
- var viewPad = this._getViewPadding();
-
- var clientWidth = this._getClientWidth();
- var leftEdge = viewPad.left;
- var rightEdge = viewPad.left + clientWidth;
- var bounds = this._getBoundsAtOffset(caret === start ? start : endInclusive);
- var left = bounds.left;
- var right = bounds.right;
- var minScroll = clientWidth / 4;
- if (allSelection && !selection.isEmpty() && startLine === endLine) {
- bounds = this._getBoundsAtOffset(caret === end ? start : endInclusive);
- var selectionWidth = caret === start ? bounds.right - left : right - bounds.left;
- if ((clientWidth - minScroll) > selectionWidth) {
- if (left > bounds.left) { left = bounds.left; }
- if (right < bounds.right) { right = bounds.right; }
- }
- }
- var viewRect = this._viewDiv.getBoundingClientRect();
- left -= viewRect.left;
- right -= viewRect.left;
- var pixelX = 0;
- if (left < leftEdge) {
- pixelX = Math.min(left - leftEdge, -minScroll);
- }
- if (right > rightEdge) {
- var maxScroll = this._scrollDiv.scrollWidth - scroll.x - clientWidth;
- pixelX = Math.min(maxScroll, Math.max(right - rightEdge, minScroll));
- }
-
- var pixelY = 0;
- var topIndex = this._getTopIndex(true);
- var bottomIndex = this._getBottomIndex(true);
- var caretLine = model.getLineAtOffset(caret);
- var clientHeight = this._getClientHeight();
- if (!(topIndex <= caretLine && caretLine <= bottomIndex)) {
- var lineHeight = this._getLineHeight();
- var selectionHeight = allSelection ? (endLine - startLine) * lineHeight : 0;
- pixelY = caretLine * lineHeight;
- pixelY -= scroll.y;
- if (pixelY + lineHeight > clientHeight) {
- pixelY -= clientHeight - lineHeight;
- if (caret === start && start !== end) {
- pixelY += Math.min(clientHeight - lineHeight, selectionHeight);
- }
- } else {
- if (caret === end) {
- pixelY -= Math.min (clientHeight - lineHeight, selectionHeight);
- }
- }
- if (pageScroll) {
- if (pageScroll > 0) {
- if (pixelY > 0) {
- pixelY = Math.max(pixelY, pageScroll);
- }
- } else {
- if (pixelY < 0) {
- pixelY = Math.min(pixelY, pageScroll);
- }
- }
- }
- }
-
- if (pixelX !== 0 || pixelY !== 0) {
- this._scrollView (pixelX, pixelY);
- /*
- * When the view scrolls it is possible that one of the scrollbars can show over the caret.
- * Depending on the browser scrolling can be synchronous (Safari), in which case the change
- * can be detected before showCaret() returns. When scrolling is asynchronous (most browsers),
- * the detection is done during the next update page.
- */
- if (clientHeight !== this._getClientHeight() || clientWidth !== this._getClientWidth()) {
- this._showCaret();
- } else {
- this._ensureCaretVisible = true;
- }
- return true;
- }
- return false;
- },
- _startIME: function () {
- if (this._imeOffset !== -1) { return; }
- var selection = this._getSelection();
- if (!selection.isEmpty()) {
- this._modifyContent({text: "", start: selection.start, end: selection.end}, true);
- }
- this._imeOffset = selection.start;
- },
- _unhookEvents: function() {
- this._model.removeEventListener("Changing", this._modelListener.onChanging);
- this._model.removeEventListener("Changed", this._modelListener.onChanged);
- this._modelListener = null;
- for (var i=0; i lastNode.lineIndex) {
- topNode = lastNode;
- topOffset = 0;
- } else {
- topNode = this._getLineNode(startLine);
- topOffset = selection.start - model.getLineStart(startLine);
- }
-
- if (endLine < firstNode.lineIndex) {
- bottomNode = firstNode;
- bottomOffset = 0;
- } else if (endLine > lastNode.lineIndex) {
- bottomNode = lastNode;
- bottomOffset = 0;
- } else {
- bottomNode = this._getLineNode(endLine);
- bottomOffset = selection.end - model.getLineStart(endLine);
- }
- this._setDOMSelection(topNode, topOffset, bottomNode, bottomOffset);
- },
- _updatePage: function(hScrollOnly) {
- if (this._redrawCount > 0) { return; }
- if (this._updateTimer) {
- clearTimeout(this._updateTimer);
- this._updateTimer = null;
- hScrollOnly = false;
- }
- var clientDiv = this._clientDiv;
- if (!clientDiv) { return; }
- var model = this._model;
- var scroll = this._getScroll();
- var viewPad = this._getViewPadding();
- var lineCount = model.getLineCount();
- var lineHeight = this._getLineHeight();
- var firstLine = Math.max(0, scroll.y) / lineHeight;
- var topIndex = Math.floor(firstLine);
- var lineStart = Math.max(0, topIndex - 1);
- var top = Math.round((firstLine - lineStart) * lineHeight);
- var partialY = this._partialY = Math.round((firstLine - topIndex) * lineHeight);
- var scrollWidth, scrollHeight = lineCount * lineHeight;
- var leftWidth, clientWidth, clientHeight;
- if (hScrollOnly) {
- clientWidth = this._getClientWidth();
- clientHeight = this._getClientHeight();
- leftWidth = this._leftDiv ? this._leftDiv.scrollWidth : 0;
- scrollWidth = Math.max(this._maxLineWidth, clientWidth);
- } else {
- var document = this._frameDocument;
- var frameWidth = this._getFrameWidth();
- var frameHeight = this._getFrameHeight();
- document.body.style.width = frameWidth + "px";
- document.body.style.height = frameHeight + "px";
-
- /* Update view height in order to have client height computed */
- var viewDiv = this._viewDiv;
- viewDiv.style.height = Math.max(0, (frameHeight - viewPad.top - viewPad.bottom)) + "px";
- clientHeight = this._getClientHeight();
- var linesPerPage = Math.floor((clientHeight + partialY) / lineHeight);
- var bottomIndex = Math.min(topIndex + linesPerPage, lineCount - 1);
- var lineEnd = Math.min(bottomIndex + 1, lineCount - 1);
-
- var lineIndex, lineWidth;
- var child = clientDiv.firstChild;
- while (child) {
- lineIndex = child.lineIndex;
- var nextChild = child.nextSibling;
- if (!(lineStart <= lineIndex && lineIndex <= lineEnd) || child.lineRemoved || child.lineIndex === -1) {
- if (this._mouseWheelLine === child) {
- child.style.display = "none";
- child.lineIndex = -1;
- } else {
- clientDiv.removeChild(child);
- }
- }
- child = nextChild;
- }
-
- child = this._getLineNext();
- var frag = document.createDocumentFragment();
- for (lineIndex=lineStart; lineIndex<=lineEnd; lineIndex++) {
- if (!child || child.lineIndex > lineIndex) {
- this._createLine(frag, null, document, lineIndex, model);
- } else {
- if (frag.firstChild) {
- clientDiv.insertBefore(frag, child);
- frag = document.createDocumentFragment();
- }
- if (child && child.lineChanged) {
- child = this._createLine(frag, child, document, lineIndex, model);
- child.lineChanged = false;
- }
- child = this._getLineNext(child);
- }
- }
- if (frag.firstChild) { clientDiv.insertBefore(frag, child); }
-
- /*
- * Feature in WekKit. Webkit limits the width of the lines
- * computed below to the width of the client div. This causes
- * the lines to be wrapped even though "pre" is set. The fix
- * is to set the width of the client div to a larger number
- * before computing the lines width. Note that this value is
- * reset to the appropriate value further down.
- */
- if (isWebkit) {
- clientDiv.style.width = (0x7FFFF).toString() + "px";
- }
-
- var rect;
- child = this._getLineNext();
- while (child) {
- lineWidth = child.lineWidth;
- if (lineWidth === undefined) {
- rect = this._getLineBoundingClientRect(child);
- lineWidth = child.lineWidth = rect.right - rect.left;
- }
- if (lineWidth >= this._maxLineWidth) {
- this._maxLineWidth = lineWidth;
- this._maxLineIndex = child.lineIndex;
- }
- if (child.lineIndex === topIndex) { this._topChild = child; }
- if (child.lineIndex === bottomIndex) { this._bottomChild = child; }
- if (this._checkMaxLineIndex === child.lineIndex) { this._checkMaxLineIndex = -1; }
- child = this._getLineNext(child);
- }
- if (this._checkMaxLineIndex !== -1) {
- lineIndex = this._checkMaxLineIndex;
- this._checkMaxLineIndex = -1;
- if (0 <= lineIndex && lineIndex < lineCount) {
- var dummy = this._createLine(clientDiv, null, document, lineIndex, model);
- rect = this._getLineBoundingClientRect(dummy);
- lineWidth = rect.right - rect.left;
- if (lineWidth >= this._maxLineWidth) {
- this._maxLineWidth = lineWidth;
- this._maxLineIndex = lineIndex;
- }
- clientDiv.removeChild(dummy);
- }
- }
-
- // Update rulers
- this._updateRuler(this._leftDiv, topIndex, bottomIndex);
- this._updateRuler(this._rightDiv, topIndex, bottomIndex);
-
- leftWidth = this._leftDiv ? this._leftDiv.scrollWidth : 0;
- var rightWidth = this._rightDiv ? this._rightDiv.scrollWidth : 0;
- viewDiv.style.left = leftWidth + "px";
- viewDiv.style.width = Math.max(0, frameWidth - leftWidth - rightWidth - viewPad.left - viewPad.right) + "px";
- if (this._rightDiv) {
- this._rightDiv.style.left = (frameWidth - rightWidth) + "px";
- }
- /* Need to set the height first in order for the width to consider the vertical scrollbar */
- var scrollDiv = this._scrollDiv;
- scrollDiv.style.height = scrollHeight + "px";
- /*
- * TODO if frameHeightWithoutHScrollbar < scrollHeight < frameHeightWithHScrollbar and the horizontal bar is visible,
- * then the clientWidth is wrong because the vertical scrollbar is showing. To correct code should hide both scrollbars
- * at this point.
- */
- clientWidth = this._getClientWidth();
- var width = Math.max(this._maxLineWidth, clientWidth);
- /*
- * Except by IE 8 and earlier, all other browsers are not allocating enough space for the right padding
- * in the scrollbar. It is possible this a bug since all other paddings are considered.
- */
- scrollWidth = width;
- if (!isIE || isIE >= 9) { width += viewPad.right; }
- scrollDiv.style.width = width + "px";
- if (this._clipScrollDiv) {
- this._clipScrollDiv.style.width = width + "px";
- }
- /* Get the left scroll after setting the width of the scrollDiv as this can change the horizontal scroll offset. */
- scroll = this._getScroll();
- var rulerHeight = clientHeight + viewPad.top + viewPad.bottom;
- this._updateRulerSize(this._leftDiv, rulerHeight);
- this._updateRulerSize(this._rightDiv, rulerHeight);
- }
- var left = scroll.x;
- var clipDiv = this._clipDiv;
- var overlayDiv = this._overlayDiv;
- var clipLeft, clipTop;
- if (clipDiv) {
- clipDiv.scrollLeft = left;
- clipLeft = leftWidth + viewPad.left;
- clipTop = viewPad.top;
- var clipWidth = clientWidth;
- var clipHeight = clientHeight;
- var clientLeft = 0, clientTop = -top;
- if (scroll.x === 0) {
- clipLeft -= viewPad.left;
- clipWidth += viewPad.left;
- clientLeft = viewPad.left;
- }
- if (scroll.x + clientWidth === scrollWidth) {
- clipWidth += viewPad.right;
- }
- if (scroll.y === 0) {
- clipTop -= viewPad.top;
- clipHeight += viewPad.top;
- clientTop += viewPad.top;
- }
- if (scroll.y + clientHeight === scrollHeight) {
- clipHeight += viewPad.bottom;
- }
- clipDiv.style.left = clipLeft + "px";
- clipDiv.style.top = clipTop + "px";
- clipDiv.style.width = clipWidth + "px";
- clipDiv.style.height = clipHeight + "px";
- clientDiv.style.left = clientLeft + "px";
- clientDiv.style.top = clientTop + "px";
- clientDiv.style.width = scrollWidth + "px";
- clientDiv.style.height = (clientHeight + top) + "px";
- if (overlayDiv) {
- overlayDiv.style.left = clientDiv.style.left;
- overlayDiv.style.top = clientDiv.style.top;
- overlayDiv.style.width = clientDiv.style.width;
- overlayDiv.style.height = clientDiv.style.height;
- }
- } else {
- clipLeft = left;
- clipTop = top;
- var clipRight = left + clientWidth;
- var clipBottom = top + clientHeight;
- if (clipLeft === 0) { clipLeft -= viewPad.left; }
- if (clipTop === 0) { clipTop -= viewPad.top; }
- if (clipRight === scrollWidth) { clipRight += viewPad.right; }
- if (scroll.y + clientHeight === scrollHeight) { clipBottom += viewPad.bottom; }
- clientDiv.style.clip = "rect(" + clipTop + "px," + clipRight + "px," + clipBottom + "px," + clipLeft + "px)";
- clientDiv.style.left = (-left + leftWidth + viewPad.left) + "px";
- clientDiv.style.width = (isWebkit ? scrollWidth : clientWidth + left) + "px";
- if (!hScrollOnly) {
- clientDiv.style.top = (-top + viewPad.top) + "px";
- clientDiv.style.height = (clientHeight + top) + "px";
- }
- if (overlayDiv) {
- overlayDiv.style.clip = clientDiv.style.clip;
- overlayDiv.style.left = clientDiv.style.left;
- overlayDiv.style.width = clientDiv.style.width;
- if (!hScrollOnly) {
- overlayDiv.style.top = clientDiv.style.top;
- overlayDiv.style.height = clientDiv.style.height;
- }
- }
- }
- this._updateDOMSelection();
-
- /*
- * If the client height changed during the update page it means that scrollbar has either been shown or hidden.
- * When this happens update page has to run again to ensure that the top and bottom lines div are correct.
- *
- * Note: On IE, updateDOMSelection() has to be called before getting the new client height because it
- * forces the client area to be recomputed.
- */
- var ensureCaretVisible = this._ensureCaretVisible;
- this._ensureCaretVisible = false;
- if (clientHeight !== this._getClientHeight()) {
- this._updatePage();
- if (ensureCaretVisible) {
- this._showCaret();
- }
- }
- if (isPad) {
- var self = this;
- setTimeout(function() {self._resizeTouchDiv();}, 0);
- }
- },
- _updateRulerSize: function (divRuler, rulerHeight) {
- if (!divRuler) { return; }
- var partialY = this._partialY;
- var lineHeight = this._getLineHeight();
- var cells = divRuler.firstChild.rows[0].cells;
- for (var i = 0; i < cells.length; i++) {
- var div = cells[i].firstChild;
- var offset = lineHeight;
- if (div._ruler.getOverview() === "page") { offset += partialY; }
- div.style.top = -offset + "px";
- div.style.height = (rulerHeight + offset) + "px";
- div = div.nextSibling;
- }
- divRuler.style.height = rulerHeight + "px";
- },
- _updateRuler: function (divRuler, topIndex, bottomIndex) {
- if (!divRuler) { return; }
- var cells = divRuler.firstChild.rows[0].cells;
- var lineHeight = this._getLineHeight();
- var parentDocument = this._frameDocument;
- var viewPad = this._getViewPadding();
- for (var i = 0; i < cells.length; i++) {
- var div = cells[i].firstChild;
- var ruler = div._ruler;
- if (div.rulerChanged) {
- this._applyStyle(ruler.getRulerStyle(), div);
- }
-
- var widthDiv;
- var child = div.firstChild;
- if (child) {
- widthDiv = child;
- child = child.nextSibling;
- } else {
- widthDiv = parentDocument.createElement("DIV");
- widthDiv.style.visibility = "hidden";
- div.appendChild(widthDiv);
- }
- var lineIndex, annotation;
- if (div.rulerChanged) {
- if (widthDiv) {
- lineIndex = -1;
- annotation = ruler.getWidestAnnotation();
- if (annotation) {
- this._applyStyle(annotation.style, widthDiv);
- if (annotation.html) {
- widthDiv.innerHTML = annotation.html;
- }
- }
- widthDiv.lineIndex = lineIndex;
- widthDiv.style.height = (lineHeight + viewPad.top) + "px";
- }
- }
-
- var overview = ruler.getOverview(), lineDiv, frag, annotations;
- if (overview === "page") {
- annotations = ruler.getAnnotations(topIndex, bottomIndex + 1);
- while (child) {
- lineIndex = child.lineIndex;
- var nextChild = child.nextSibling;
- if (!(topIndex <= lineIndex && lineIndex <= bottomIndex) || child.lineChanged) {
- div.removeChild(child);
- }
- child = nextChild;
- }
- child = div.firstChild.nextSibling;
- frag = parentDocument.createDocumentFragment();
- for (lineIndex=topIndex; lineIndex<=bottomIndex; lineIndex++) {
- if (!child || child.lineIndex > lineIndex) {
- lineDiv = parentDocument.createElement("DIV");
- annotation = annotations[lineIndex];
- if (annotation) {
- this._applyStyle(annotation.style, lineDiv);
- if (annotation.html) {
- lineDiv.innerHTML = annotation.html;
- }
- lineDiv.annotation = annotation;
- }
- lineDiv.lineIndex = lineIndex;
- lineDiv.style.height = lineHeight + "px";
- frag.appendChild(lineDiv);
- } else {
- if (frag.firstChild) {
- div.insertBefore(frag, child);
- frag = parentDocument.createDocumentFragment();
- }
- if (child) {
- child = child.nextSibling;
- }
- }
- }
- if (frag.firstChild) { div.insertBefore(frag, child); }
- } else {
- var buttonHeight = isPad ? 0 : 17;
- var clientHeight = this._getClientHeight ();
- var lineCount = this._model.getLineCount ();
- var contentHeight = lineHeight * lineCount;
- var trackHeight = clientHeight + viewPad.top + viewPad.bottom - 2 * buttonHeight;
- var divHeight;
- if (contentHeight < trackHeight) {
- divHeight = lineHeight;
- } else {
- divHeight = trackHeight / lineCount;
- }
- if (div.rulerChanged) {
- var count = div.childNodes.length;
- while (count > 1) {
- div.removeChild(div.lastChild);
- count--;
- }
- annotations = ruler.getAnnotations(0, lineCount);
- frag = parentDocument.createDocumentFragment();
- for (var prop in annotations) {
- lineIndex = prop >>> 0;
- if (lineIndex < 0) { continue; }
- lineDiv = parentDocument.createElement("DIV");
- annotation = annotations[prop];
- this._applyStyle(annotation.style, lineDiv);
- lineDiv.style.position = "absolute";
- lineDiv.style.top = buttonHeight + lineHeight + Math.floor(lineIndex * divHeight) + "px";
- if (annotation.html) {
- lineDiv.innerHTML = annotation.html;
- }
- lineDiv.annotation = annotation;
- lineDiv.lineIndex = lineIndex;
- frag.appendChild(lineDiv);
- }
- div.appendChild(frag);
- } else if (div._oldTrackHeight !== trackHeight) {
- lineDiv = div.firstChild ? div.firstChild.nextSibling : null;
- while (lineDiv) {
- lineDiv.style.top = buttonHeight + lineHeight + Math.floor(lineDiv.lineIndex * divHeight) + "px";
- lineDiv = lineDiv.nextSibling;
- }
- }
- div._oldTrackHeight = trackHeight;
- }
- div.rulerChanged = false;
- div = div.nextSibling;
- }
- },
- _updateStyle: function () {
- var document = this._frameDocument;
- if (isIE) {
- document.body.style.lineHeight = "normal";
- }
- this._lineHeight = this._calculateLineHeight();
- this._viewPadding = this._calculatePadding();
- if (isIE) {
- document.body.style.lineHeight = this._lineHeight + "px";
- }
- this.redraw();
- }
- };//end prototype
- mEventTarget.EventTarget.addMixin(TextView.prototype);
-
- return {TextView: TextView};
-});
-
-/*******************************************************************************
- * @license
- * Copyright (c) 2010, 2011 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License v1.0
- * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
- * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
- *
- * Contributors:
- * Felipe Heidrich (IBM Corporation) - initial API and implementation
- * Silenio Quarti (IBM Corporation) - initial API and implementation
- ******************************************************************************/
-
-/*global define */
-
-define("orion/textview/textDND", [], function() {
-
- function TextDND(view, undoStack) {
- this._view = view;
- this._undoStack = undoStack;
- this._dragSelection = null;
- this._dropOffset = -1;
- this._dropText = null;
- var self = this;
- this._listener = {
- onDragStart: function (evt) {
- self._onDragStart(evt);
- },
- onDragEnd: function (evt) {
- self._onDragEnd(evt);
- },
- onDragEnter: function (evt) {
- self._onDragEnter(evt);
- },
- onDragOver: function (evt) {
- self._onDragOver(evt);
- },
- onDrop: function (evt) {
- self._onDrop(evt);
- },
- onDestroy: function (evt) {
- self._onDestroy(evt);
- }
- };
- view.addEventListener("DragStart", this._listener.onDragStart);
- view.addEventListener("DragEnd", this._listener.onDragEnd);
- view.addEventListener("DragEnter", this._listener.onDragEnter);
- view.addEventListener("DragOver", this._listener.onDragOver);
- view.addEventListener("Drop", this._listener.onDrop);
- view.addEventListener("Destroy", this._listener.onDestroy);
- }
- TextDND.prototype = {
- destroy: function() {
- var view = this._view;
- if (!view) { return; }
- view.removeEventListener("DragStart", this._listener.onDragStart);
- view.removeEventListener("DragEnd", this._listener.onDragEnd);
- view.removeEventListener("DragEnter", this._listener.onDragEnter);
- view.removeEventListener("DragOver", this._listener.onDragOver);
- view.removeEventListener("Drop", this._listener.onDrop);
- view.removeEventListener("Destroy", this._listener.onDestroy);
- this._view = null;
- },
- _onDestroy: function(e) {
- this.destroy();
- },
- _onDragStart: function(e) {
- var view = this._view;
- var selection = view.getSelection();
- var model = view.getModel();
- if (model.getBaseModel) {
- selection.start = model.mapOffset(selection.start);
- selection.end = model.mapOffset(selection.end);
- model = model.getBaseModel();
- }
- var text = model.getText(selection.start, selection.end);
- if (text) {
- this._dragSelection = selection;
- e.event.dataTransfer.effectAllowed = "copyMove";
- e.event.dataTransfer.setData("Text", text);
- }
- },
- _onDragEnd: function(e) {
- var view = this._view;
- if (this._dragSelection) {
- if (this._undoStack) { this._undoStack.startCompoundChange(); }
- var move = e.event.dataTransfer.dropEffect === "move";
- if (move) {
- view.setText("", this._dragSelection.start, this._dragSelection.end);
- }
- if (this._dropText) {
- var text = this._dropText;
- var offset = this._dropOffset;
- if (move) {
- if (offset >= this._dragSelection.end) {
- offset -= this._dragSelection.end - this._dragSelection.start;
- } else if (offset >= this._dragSelection.start) {
- offset = this._dragSelection.start;
- }
- }
- view.setText(text, offset, offset);
- view.setSelection(offset, offset + text.length);
- this._dropText = null;
- this._dropOffset = -1;
- }
- if (this._undoStack) { this._undoStack.endCompoundChange(); }
- }
- this._dragSelection = null;
- },
- _onDragEnter: function(e) {
- this._onDragOver(e);
- },
- _onDragOver: function(e) {
- var types = e.event.dataTransfer.types;
- if (types) {
- var allowed = types.contains ? types.contains("text/plain") : types.indexOf("text/plain") !== -1;
- if (!allowed) {
- e.event.dataTransfer.dropEffect = "none";
- }
- }
- },
- _onDrop: function(e) {
- var view = this._view;
- var text = e.event.dataTransfer.getData("Text");
- if (text) {
- var offset = view.getOffsetAtLocation(e.x, e.y);
- if (this._dragSelection) {
- this._dropOffset = offset;
- this._dropText = text;
- } else {
- view.setText(text, offset, offset);
- view.setSelection(offset, offset + text.length);
- }
- }
- }
- };
-
- return {TextDND: TextDND};
-});/*******************************************************************************
- * @license
- * Copyright (c) 2011 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License v1.0
- * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
- * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
- *
- * Contributors: IBM Corporation - initial API and implementation
- ******************************************************************************/
-
-/*jslint */
-/*global define */
-
-define("orion/editor/htmlGrammar", [], function() {
-
- /**
- * Provides a grammar that can do some very rough syntax highlighting for HTML.
- * @class orion.syntax.HtmlGrammar
- */
- function HtmlGrammar() {
- /**
- * Object containing the grammar rules.
- * @public
- * @type Object
- */
- return {
- "name": "HTML",
- "scopeName": "source.html",
- "uuid": "3B5C76FB-EBB5-D930-F40C-047D082CE99B",
- "patterns": [
- // TODO unicode?
- {
- "match": "]+>",
- "name": "entity.name.tag.doctype.html"
- },
- {
- "begin": "",
- "beginCaptures": {
- "0": { "name": "punctuation.definition.comment.html" }
- },
- "endCaptures": {
- "0": { "name": "punctuation.definition.comment.html" }
- },
- "patterns": [
- {
- "match": "--",
- "name": "invalid.illegal.badcomment.html"
- }
- ],
- "contentName": "comment.block.html"
- },
- { // startDelimiter + tagName
- "match": "<[A-Za-z0-9_\\-:]+(?= ?)",
- "name": "entity.name.tag.html"
- },
- { "include": "#attrName" },
- { "include": "#qString" },
- { "include": "#qqString" },
- // TODO attrName, qString, qqString should be applied first while inside a tag
- { // startDelimiter + slash + tagName + endDelimiter
- "match": "[A-Za-z0-9_\\-:]+>",
- "name": "entity.name.tag.html"
- },
- { // end delimiter of open tag
- "match": ">",
- "name": "entity.name.tag.html"
- } ],
- "repository": {
- "attrName": { // attribute name
- "match": "[A-Za-z\\-:]+(?=\\s*=\\s*['\"])",
- "name": "entity.other.attribute.name.html"
- },
- "qqString": { // double quoted string
- "match": "(\")[^\"]+(\")",
- "name": "string.quoted.double.html"
- },
- "qString": { // single quoted string
- "match": "(')[^']+(\')",
- "name": "string.quoted.single.html"
- }
- }
- };
- }
-
- return {HtmlGrammar: HtmlGrammar};
-});
-/*******************************************************************************
- * @license
- * Copyright (c) 2011 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License v1.0
- * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
- * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
- *
- * Contributors: IBM Corporation - initial API and implementation
- ******************************************************************************/
-
-/*jslint regexp:false laxbreak:true*/
-/*global define */
-
-define("orion/editor/textMateStyler", ['orion/editor/regex'], function(mRegex) {
-
-var RegexUtil = {
- // Rules to detect some unsupported Oniguruma features
- unsupported: [
- {regex: /\(\?[ims\-]:/, func: function(match) { return "option on/off for subexp"; }},
- {regex: /\(\?<([=!])/, func: function(match) { return (match[1] === "=") ? "lookbehind" : "negative lookbehind"; }},
- {regex: /\(\?>/, func: function(match) { return "atomic group"; }}
- ],
-
- /**
- * @param {String} str String giving a regular expression pattern from a TextMate grammar.
- * @param {String} [flags] [ismg]+
- * @returns {RegExp}
- */
- toRegExp: function(str) {
- function fail(feature, match) {
- throw new Error("Unsupported regex feature \"" + feature + "\": \"" + match[0] + "\" at index: "
- + match.index + " in " + match.input);
- }
- // Turns an extended regex pattern into a normal one
- function normalize(/**String*/ str) {
- var result = "";
- var insideCharacterClass = false;
- var len = str.length;
- for (var i=0; i < len; ) {
- var chr = str[i];
- if (!insideCharacterClass && chr === "#") {
- // skip to eol
- while (i < len && chr !== "\r" && chr !== "\n") {
- chr = str[++i];
- }
- } else if (!insideCharacterClass && /\s/.test(chr)) {
- // skip whitespace
- while (i < len && /\s/.test(chr)) {
- chr = str[++i];
- }
- } else if (chr === "\\") {
- result += chr;
- if (!/\s/.test(str[i+1])) {
- result += str[i+1];
- i += 1;
- }
- i += 1;
- } else if (chr === "[") {
- insideCharacterClass = true;
- result += chr;
- i += 1;
- } else if (chr === "]") {
- insideCharacterClass = false;
- result += chr;
- i += 1;
- } else {
- result += chr;
- i += 1;
- }
- }
- return result;
- }
-
- var flags = "";
- var i;
-
- // Handle global "x" flag (whitespace/comments)
- str = RegexUtil.processGlobalFlag("x", str, function(subexp) {
- return normalize(subexp);
- });
-
- // Handle global "i" flag (case-insensitive)
- str = RegexUtil.processGlobalFlag("i", str, function(subexp) {
- flags += "i";
- return subexp;
- });
-
- // Check for remaining unsupported syntax
- for (i=0; i < this.unsupported.length; i++) {
- var match;
- if ((match = this.unsupported[i].regex.exec(str))) {
- fail(this.unsupported[i].func(match), match);
- }
- }
-
- return new RegExp(str, flags);
- },
-
- /**
- * Checks if flag applies to entire pattern. If so, obtains replacement string by calling processor
- * on the unwrapped pattern. Handles 2 possible syntaxes: (?f)pat and (?f:pat)
- */
- processGlobalFlag: function(/**String*/ flag, /**String*/ str, /**Function*/ processor) {
- function getMatchingCloseParen(/*String*/pat, /*Number*/start) {
- var depth = 0,
- len = pat.length,
- flagStop = -1;
- for (var i=start; i < len && flagStop === -1; i++) {
- switch (pat[i]) {
- case "\\":
- i++; // escape: skip next char
- break;
- case "(":
- depth++;
- break;
- case ")":
- depth--;
- if (depth === 0) {
- flagStop = i;
- }
- break;
- }
- }
- return flagStop;
- }
- var flag1 = "(?" + flag + ")",
- flag2 = "(?" + flag + ":";
- if (str.substring(0, flag1.length) === flag1) {
- return processor(str.substring(flag1.length));
- } else if (str.substring(0, flag2.length) === flag2) {
- var flagStop = getMatchingCloseParen(str, 0);
- if (flagStop < str.length-1) {
- throw new Error("Only a " + flag2 + ") group that encloses the entire regex is supported in: " + str);
- }
- return processor(str.substring(flag2.length, flagStop));
- }
- return str;
- },
-
- hasBackReference: function(/**RegExp*/ regex) {
- return (/\\\d+/).test(regex.source);
- },
-
- /** @returns {RegExp} A regex made by substituting any backreferences in regex for the value of the property
- * in sub with the same name as the backreferenced group number. */
- getSubstitutedRegex: function(/**RegExp*/ regex, /**Object*/ sub, /**Boolean*/ escape) {
- escape = (typeof escape === "undefined") ? true : false;
- var exploded = regex.source.split(/(\\\d+)/g);
- var array = [];
- for (var i=0; i < exploded.length; i++) {
- var term = exploded[i];
- var backrefMatch = /\\(\d+)/.exec(term);
- if (backrefMatch) {
- var text = sub[backrefMatch[1]] || "";
- array.push(escape ? mRegex.escape(text) : text);
- } else {
- array.push(term);
- }
- }
- return new RegExp(array.join(""));
- },
-
- /**
- * Builds a version of regex with every non-capturing term converted into a capturing group. This is a workaround
- * for JavaScript's lack of API to get the index at which a matched group begins in the input string.
- * Using the "groupified" regex, we can sum the lengths of matches from consuming groups 1..n-1 to obtain the
- * starting index of group n. (A consuming group is a capturing group that is not inside a lookahead assertion).
- * Example: groupify(/(a+)x+(b+)/) === /(a+)(x+)(b+)/
- * Example: groupify(/(?:x+(a+))b+/) === /(?:(x+)(a+))(b+)/
- * @param {RegExp} regex The regex to groupify.
- * @param {Object} [backRefOld2NewMap] Optional. If provided, the backreference numbers in regex will be updated using the
- * properties of this object rather than the new group numbers of regex itself.
- *
[0] {RegExp} The groupified version of the input regex.
- *
[1] {Object} A map containing old-group to new-group info. Each property is a capturing group number of regex
- * and its value is the corresponding capturing group number of [0].
- *
[2] {Object} A map indicating which capturing groups of [0] are also consuming groups. If a group number is found
- * as a property in this object, then it's a consuming group.
- */
- groupify: function(regex, backRefOld2NewMap) {
- var NON_CAPTURING = 1,
- CAPTURING = 2,
- LOOKAHEAD = 3,
- NEW_CAPTURING = 4;
- var src = regex.source,
- len = src.length;
- var groups = [],
- lookaheadDepth = 0,
- newGroups = [],
- oldGroupNumber = 1,
- newGroupNumber = 1;
- var result = [],
- old2New = {},
- consuming = {};
- for (var i=0; i < len; i++) {
- var curGroup = groups[groups.length-1];
- var chr = src[i];
- switch (chr) {
- case "(":
- // If we're in new capturing group, close it since ( signals end-of-term
- if (curGroup === NEW_CAPTURING) {
- groups.pop();
- result.push(")");
- newGroups[newGroups.length-1].end = i;
- }
- var peek2 = (i + 2 < len) ? (src[i+1] + "" + src[i+2]) : null;
- if (peek2 === "?:" || peek2 === "?=" || peek2 === "?!") {
- // Found non-capturing group or lookahead assertion. Note that we preserve non-capturing groups
- // as such, but any term inside them will become a new capturing group (unless it happens to
- // also be inside a lookahead).
- var groupType;
- if (peek2 === "?:") {
- groupType = NON_CAPTURING;
- } else {
- groupType = LOOKAHEAD;
- lookaheadDepth++;
- }
- groups.push(groupType);
- newGroups.push({ start: i, end: -1, type: groupType /*non capturing*/ });
- result.push(chr);
- result.push(peek2);
- i += peek2.length;
- } else {
- groups.push(CAPTURING);
- newGroups.push({ start: i, end: -1, type: CAPTURING, oldNum: oldGroupNumber, num: newGroupNumber });
- result.push(chr);
- if (lookaheadDepth === 0) {
- consuming[newGroupNumber] = null;
- }
- old2New[oldGroupNumber] = newGroupNumber;
- oldGroupNumber++;
- newGroupNumber++;
- }
- break;
- case ")":
- var group = groups.pop();
- if (group === LOOKAHEAD) { lookaheadDepth--; }
- newGroups[newGroups.length-1].end = i;
- result.push(chr);
- break;
- case "*":
- case "+":
- case "?":
- case "}":
- // Unary operator. If it's being applied to a capturing group, we need to add a new capturing group
- // enclosing the pair
- var op = chr;
- var prev = src[i-1],
- prevIndex = i-1;
- if (chr === "}") {
- for (var j=i-1; src[j] !== "{" && j >= 0; j--) {}
- prev = src[j-1];
- prevIndex = j-1;
- op = src.substring(j, i+1);
- }
- var lastGroup = newGroups[newGroups.length-1];
- if (prev === ")" && (lastGroup.type === CAPTURING || lastGroup.type === NEW_CAPTURING)) {
- // Shove in the new group's (, increment num/start in from [lastGroup.start .. end]
- result.splice(lastGroup.start, 0, "(");
- result.push(op);
- result.push(")");
- var newGroup = { start: lastGroup.start, end: result.length-1, type: NEW_CAPTURING, num: lastGroup.num };
- for (var k=0; k < newGroups.length; k++) {
- group = newGroups[k];
- if (group.type === CAPTURING || group.type === NEW_CAPTURING) {
- if (group.start >= lastGroup.start && group.end <= prevIndex) {
- group.start += 1;
- group.end += 1;
- group.num = group.num + 1;
- if (group.type === CAPTURING) {
- old2New[group.oldNum] = group.num;
- }
- }
- }
- }
- newGroups.push(newGroup);
- newGroupNumber++;
- break;
- } else {
- // Fallthrough to default
- }
- default:
- if (chr !== "|" && curGroup !== CAPTURING && curGroup !== NEW_CAPTURING) {
- // Not in a capturing group, so make a new one to hold this term.
- // Perf improvement: don't create the new group if we're inside a lookahead, since we don't
- // care about them (nothing inside a lookahead actually consumes input so we don't need it)
- if (lookaheadDepth === 0) {
- groups.push(NEW_CAPTURING);
- newGroups.push({ start: i, end: -1, type: NEW_CAPTURING, num: newGroupNumber });
- result.push("(");
- consuming[newGroupNumber] = null;
- newGroupNumber++;
- }
- }
- result.push(chr);
- if (chr === "\\") {
- var peek = src[i+1];
- // Eat next so following iteration doesn't think it's a real special character
- result.push(peek);
- i += 1;
- }
- break;
- }
- }
- while (groups.length) {
- // Close any remaining new capturing groups
- groups.pop();
- result.push(")");
- }
- var newRegex = new RegExp(result.join(""));
-
- // Update backreferences so they refer to the new group numbers. Use backRefOld2NewMap if provided
- var subst = {};
- backRefOld2NewMap = backRefOld2NewMap || old2New;
- for (var prop in backRefOld2NewMap) {
- if (backRefOld2NewMap.hasOwnProperty(prop)) {
- subst[prop] = "\\" + backRefOld2NewMap[prop];
- }
- }
- newRegex = this.getSubstitutedRegex(newRegex, subst, false);
-
- return [newRegex, old2New, consuming];
- },
-
- /** @returns {Boolean} True if the captures object assigns scope to a matching group other than "0". */
- complexCaptures: function(capturesObj) {
- if (!capturesObj) { return false; }
- for (var prop in capturesObj) {
- if (capturesObj.hasOwnProperty(prop)) {
- if (prop !== "0") {
- return true;
- }
- }
- }
- return false;
- }
-};
-
- /**
- * @name orion.editor.TextMateStyler
- * @class A styler that knows how to apply a subset of the TextMate grammar format to style a line.
- *
- *
Styling from a grammar:
- *
Each scope name given in the grammar is converted to an array of CSS class names. For example
- * a region of text with scope keyword.control.php will be assigned the CSS classes
- * keyword, keyword-control, keyword-control-php
- *
- *
A CSS file can give rules matching any of these class names to provide generic or more specific styling.
- * For example,
- *
.keyword { font-color: blue; }
- *
colors all keywords blue, while
- *
.keyword-control-php { font-weight: bold; }
- *
bolds only PHP control keywords.
- *
- *
This is useful when using grammars that adhere to TextMate's
- * scope name conventions,
- * as a single CSS rule can provide consistent styling to similar constructs across different languages.
- *
- *
Top-level grammar constructs:
- *
patterns, repository (with limitations, see "Other Features") are supported.
- *
scopeName, firstLineMatch, foldingStartMarker, foldingStopMarker are not supported.
- *
fileTypes is not supported. When using the Orion service registry, the "orion.edit.highlighter"
- * service serves a similar purpose.
- *
- *
- *
Regular expression constructs:
- *
- *
match patterns are supported.
- *
begin .. end patterns are supported.
- *
The "extended" regex forms (?x) and (?x:...) are supported, but only when they
- * apply to the entire regex pattern.
- *
Matching is done using native JavaScript RegExps. As a result, many features of the Oniguruma regex
- * engine used by TextMate are not supported.
- * Unsupported features include:
- *
Named captures
- *
Setting flags inside subgroups (eg. (?i:a)b)
- *
Lookbehind and negative lookbehind
- *
Subexpression call
- *
etc.
- *
- *
- *
- *
- *
Scope-assignment constructs:
- *
- *
captures, beginCaptures, endCaptures are supported.
- *
name and contentName are supported.
- *
- *
- *
Other features:
- *
- *
applyEndPatternLast is supported.
- *
include is supported, but only when it references a rule in the current grammar's repository.
- * Including $self, $base, or rule.from.another.grammar is not supported.
- *
- *
- * @description Creates a new TextMateStyler.
- * @extends orion.editor.AbstractStyler
- * @param {orion.textview.TextView} textView The TextView to provide styling for.
- * @param {Object} grammar The TextMate grammar to use for styling the TextView, as a JavaScript object. You can
- * produce this object by running a PList-to-JavaScript conversion tool on a TextMate .tmLanguage file.
- * @param {Object[]} [externalGrammars] Additional grammar objects that will be used to resolve named rule references.
- */
- function TextMateStyler(textView, grammar, externalGrammars) {
- this.initialize(textView);
- // Copy grammar object(s) since we will mutate them
- this.grammar = this.copy(grammar);
- this.externalGrammars = externalGrammars ? this.copy(externalGrammars) : [];
-
- this._styles = {}; /* key: {String} scopeName, value: {String[]} cssClassNames */
- this._tree = null;
- this._allGrammars = {}; /* key: {String} scopeName of grammar, value: {Object} grammar */
- this.preprocess(this.grammar);
- }
- TextMateStyler.prototype = /** @lends orion.editor.TextMateStyler.prototype */ {
- initialize: function(textView) {
- this.textView = textView;
- var self = this;
- this._listener = {
- onModelChanged: function(e) {
- self.onModelChanged(e);
- },
- onDestroy: function(e) {
- self.onDestroy(e);
- },
- onLineStyle: function(e) {
- self.onLineStyle(e);
- }
- };
- textView.addEventListener("ModelChanged", this._listener.onModelChanged);
- textView.addEventListener("Destroy", this._listener.onDestroy);
- textView.addEventListener("LineStyle", this._listener.onLineStyle);
- textView.redrawLines();
- },
- onDestroy: function(/**eclipse.DestroyEvent*/ e) {
- this.destroy();
- },
- destroy: function() {
- if (this.textView) {
- this.textView.removeEventListener("ModelChanged", this._listener.onModelChanged);
- this.textView.removeEventListener("Destroy", this._listener.onDestroy);
- this.textView.removeEventListener("LineStyle", this._listener.onLineStyle);
- this.textView = null;
- }
- this.grammar = null;
- this._styles = null;
- this._tree = null;
- this._listener = null;
- },
- /** @private */
- copy: function(obj) {
- return JSON.parse(JSON.stringify(obj));
- },
- /** @private */
- preprocess: function(grammar) {
- var stack = [grammar];
- for (; stack.length !== 0; ) {
- var rule = stack.pop();
- if (rule._resolvedRule && rule._typedRule) {
- continue;
- }
-// console.debug("Process " + (rule.include || rule.name));
-
- // Look up include'd rule, create typed *Rule instance
- rule._resolvedRule = this._resolve(rule);
- rule._typedRule = this._createTypedRule(rule);
-
- // Convert the scope names to styles and cache them for later
- this.addStyles(rule.name);
- this.addStyles(rule.contentName);
- this.addStylesForCaptures(rule.captures);
- this.addStylesForCaptures(rule.beginCaptures);
- this.addStylesForCaptures(rule.endCaptures);
-
- if (rule._resolvedRule !== rule) {
- // Add include target
- stack.push(rule._resolvedRule);
- }
- if (rule.patterns) {
- // Add subrules
- for (var i=0; i < rule.patterns.length; i++) {
- stack.push(rule.patterns[i]);
- }
- }
- }
- },
-
- /**
- * @private
- * Adds eclipse.Style objects for scope to our _styles cache.
- * @param {String} scope A scope name, like "constant.character.php".
- */
- addStyles: function(scope) {
- if (scope && !this._styles[scope]) {
- this._styles[scope] = [];
- var scopeArray = scope.split(".");
- for (var i = 0; i < scopeArray.length; i++) {
- this._styles[scope].push(scopeArray.slice(0, i + 1).join("-"));
- }
- }
- },
- /** @private */
- addStylesForCaptures: function(/**Object*/ captures) {
- for (var prop in captures) {
- if (captures.hasOwnProperty(prop)) {
- var scope = captures[prop].name;
- this.addStyles(scope);
- }
- }
- },
- /**
- * A rule that contains subrules ("patterns" in TextMate parlance) but has no "begin" or "end".
- * Also handles top level of grammar.
- * @private
- */
- ContainerRule: (function() {
- function ContainerRule(/**Object*/ rule) {
- this.rule = rule;
- this.subrules = rule.patterns;
- }
- ContainerRule.prototype.valueOf = function() { return "aa"; };
- return ContainerRule;
- }()),
- /**
- * A rule that is delimited by "begin" and "end" matches, which may be separated by any number of
- * lines. This type of rule may contain subrules, which apply only inside the begin .. end region.
- * @private
- */
- BeginEndRule: (function() {
- function BeginEndRule(/**Object*/ rule) {
- this.rule = rule;
- // TODO: the TextMate blog claims that "end" is optional.
- this.beginRegex = RegexUtil.toRegExp(rule.begin);
- this.endRegex = RegexUtil.toRegExp(rule.end);
- this.subrules = rule.patterns || [];
-
- this.endRegexHasBackRef = RegexUtil.hasBackReference(this.endRegex);
-
- // Deal with non-0 captures
- var complexCaptures = RegexUtil.complexCaptures(rule.captures);
- var complexBeginEnd = RegexUtil.complexCaptures(rule.beginCaptures) || RegexUtil.complexCaptures(rule.endCaptures);
- this.isComplex = complexCaptures || complexBeginEnd;
- if (this.isComplex) {
- var bg = RegexUtil.groupify(this.beginRegex);
- this.beginRegex = bg[0];
- this.beginOld2New = bg[1];
- this.beginConsuming = bg[2];
-
- var eg = RegexUtil.groupify(this.endRegex, this.beginOld2New /*Update end's backrefs to begin's new group #s*/);
- this.endRegex = eg[0];
- this.endOld2New = eg[1];
- this.endConsuming = eg[2];
- }
- }
- BeginEndRule.prototype.valueOf = function() { return this.beginRegex; };
- return BeginEndRule;
- }()),
- /**
- * A rule with a "match" pattern.
- * @private
- */
- MatchRule: (function() {
- function MatchRule(/**Object*/ rule) {
- this.rule = rule;
- this.matchRegex = RegexUtil.toRegExp(rule.match);
- this.isComplex = RegexUtil.complexCaptures(rule.captures);
- if (this.isComplex) {
- var mg = RegexUtil.groupify(this.matchRegex);
- this.matchRegex = mg[0];
- this.matchOld2New = mg[1];
- this.matchConsuming = mg[2];
- }
- }
- MatchRule.prototype.valueOf = function() { return this.matchRegex; };
- return MatchRule;
- }()),
- /**
- * @param {Object} rule A rule from the grammar.
- * @returns {MatchRule|BeginEndRule|ContainerRule}
- * @private
- */
- _createTypedRule: function(rule) {
- if (rule.match) {
- return new this.MatchRule(rule);
- } else if (rule.begin) {
- return new this.BeginEndRule(rule);
- } else {
- return new this.ContainerRule(rule);
- }
- },
- /**
- * Resolves a rule from the grammar (which may be an include) into the real rule that it points to.
- * @private
- */
- _resolve: function(rule) {
- var resolved = rule;
- if (rule.include) {
- if (rule.begin || rule.end || rule.match) {
- throw new Error("Unexpected regex pattern in \"include\" rule " + rule.include);
- }
- var name = rule.include;
- if (name[0] === "#") {
- resolved = this.grammar.repository && this.grammar.repository[name.substring(1)];
- if (!resolved) { throw new Error("Couldn't find included rule " + name + " in grammar repository"); }
- } else if (name === "$self") {
- resolved = this.grammar;
- } else if (name === "$base") {
- // $base is only relevant when including rules from foreign grammars
- throw new Error("Include \"$base\" is not supported");
- } else {
- resolved = this._allGrammars[name];
- if (!resolved) {
- for (var i=0; i < this.externalGrammars.length; i++) {
- var grammar = this.externalGrammars[i];
- if (grammar.scopeName === name) {
- this.preprocess(grammar);
- this._allGrammars[name] = grammar;
- resolved = grammar;
- break;
- }
- }
- }
- }
- }
- return resolved;
- },
- /** @private */
- ContainerNode: (function() {
- function ContainerNode(parent, rule) {
- this.parent = parent;
- this.rule = rule;
- this.children = [];
-
- this.start = null;
- this.end = null;
- }
- ContainerNode.prototype.addChild = function(child) {
- this.children.push(child);
- };
- ContainerNode.prototype.valueOf = function() {
- var r = this.rule;
- return "ContainerNode { " + (r.include || "") + " " + (r.name || "") + (r.comment || "") + "}";
- };
- return ContainerNode;
- }()),
- /** @private */
- BeginEndNode: (function() {
- function BeginEndNode(parent, rule, beginMatch) {
- this.parent = parent;
- this.rule = rule;
- this.children = [];
-
- this.setStart(beginMatch);
- this.end = null; // will be set eventually during parsing (may be EOF)
- this.endMatch = null; // may remain null if we never match our "end" pattern
-
- // Build a new regex if the "end" regex has backrefs since they refer to matched groups of beginMatch
- if (rule.endRegexHasBackRef) {
- this.endRegexSubstituted = RegexUtil.getSubstitutedRegex(rule.endRegex, beginMatch);
- } else {
- this.endRegexSubstituted = null;
- }
- }
- BeginEndNode.prototype.addChild = function(child) {
- this.children.push(child);
- };
- /** @return {Number} This node's index in its parent's "children" list */
- BeginEndNode.prototype.getIndexInParent = function(node) {
- return this.parent ? this.parent.children.indexOf(this) : -1;
- };
- /** @param {RegExp.match} beginMatch */
- BeginEndNode.prototype.setStart = function(beginMatch) {
- this.start = beginMatch.index;
- this.beginMatch = beginMatch;
- };
- /** @param {RegExp.match|Number} endMatchOrLastChar */
- BeginEndNode.prototype.setEnd = function(endMatchOrLastChar) {
- if (endMatchOrLastChar && typeof(endMatchOrLastChar) === "object") {
- var endMatch = endMatchOrLastChar;
- this.endMatch = endMatch;
- this.end = endMatch.index + endMatch[0].length;
- } else {
- var lastChar = endMatchOrLastChar;
- this.endMatch = null;
- this.end = lastChar;
- }
- };
- BeginEndNode.prototype.shiftStart = function(amount) {
- this.start += amount;
- this.beginMatch.index += amount;
- };
- BeginEndNode.prototype.shiftEnd = function(amount) {
- this.end += amount;
- if (this.endMatch) { this.endMatch.index += amount; }
- };
- BeginEndNode.prototype.valueOf = function() {
- return "{" + this.rule.beginRegex + " range=" + this.start + ".." + this.end + "}";
- };
- return BeginEndNode;
- }()),
- /** Pushes rules onto stack such that rules[startFrom] is on top
- * @private
- */
- push: function(/**Array*/ stack, /**Array*/ rules) {
- if (!rules) { return; }
- for (var i = rules.length; i > 0; ) {
- stack.push(rules[--i]);
- }
- },
- /** Executes regex on text, and returns the match object with its index
- * offset by the given amount.
- * @returns {RegExp.match}
- * @private
- */
- exec: function(/**RegExp*/ regex, /**String*/ text, /**Number*/ offset) {
- var match = regex.exec(text);
- if (match) { match.index += offset; }
- regex.lastIndex = 0; // Just in case
- return match;
- },
- /** @returns {Number} The position immediately following the match.
- * @private
- */
- afterMatch: function(/**RegExp.match*/ match) {
- return match.index + match[0].length;
- },
- /**
- * @returns {RegExp.match} If node is a BeginEndNode and its rule's "end" pattern matches the text.
- * @private
- */
- getEndMatch: function(/**Node*/ node, /**String*/ text, /**Number*/ offset) {
- if (node instanceof this.BeginEndNode) {
- var rule = node.rule;
- var endRegex = node.endRegexSubstituted || rule.endRegex;
- if (!endRegex) { return null; }
- return this.exec(endRegex, text, offset);
- }
- return null;
- },
- /** Called once when file is first loaded to build the parse tree. Tree is updated incrementally thereafter
- * as buffer is modified.
- * @private
- */
- initialParse: function() {
- var last = this.textView.getModel().getCharCount();
- // First time; make parse tree for whole buffer
- var root = new this.ContainerNode(null, this.grammar._typedRule);
- this._tree = root;
- this.parse(this._tree, false, 0);
- },
- onModelChanged: function(/**eclipse.ModelChangedEvent*/ e) {
- var addedCharCount = e.addedCharCount,
- addedLineCount = e.addedLineCount,
- removedCharCount = e.removedCharCount,
- removedLineCount = e.removedLineCount,
- start = e.start;
- if (!this._tree) {
- this.initialParse();
- } else {
- var model = this.textView.getModel();
- var charCount = model.getCharCount();
-
- // For rs, we must rewind to the line preceding the line 'start' is on. We can't rely on start's
- // line since it may've been changed in a way that would cause a new beginMatch at its lineStart.
- var rs = model.getLineEnd(model.getLineAtOffset(start) - 1); // may be < 0
- var fd = this.getFirstDamaged(rs, rs);
- rs = rs === -1 ? 0 : rs;
- var stoppedAt;
- if (fd) {
- // [rs, re] is the region we need to verify. If we find the structure of the tree
- // has changed in that area, then we may need to reparse the rest of the file.
- stoppedAt = this.parse(fd, true, rs, start, addedCharCount, removedCharCount);
- } else {
- // FIXME: fd == null ?
- stoppedAt = charCount;
- }
- this.textView.redrawRange(rs, stoppedAt);
- }
- },
- /** @returns {BeginEndNode|ContainerNode} The result of taking the first (smallest "start" value)
- * node overlapping [start,end] and drilling down to get its deepest damaged descendant (if any).
- * @private
- */
- getFirstDamaged: function(start, end) {
- // If start === 0 we actually have to start from the root because there is no position
- // we can rely on. (First index is damaged)
- if (start < 0) {
- return this._tree;
- }
-
- var nodes = [this._tree];
- var result = null;
- while (nodes.length) {
- var n = nodes.pop();
- if (!n.parent /*n is root*/ || this.isDamaged(n, start, end)) {
- // n is damaged by the edit, so go into its children
- // Note: If a node is damaged, then some of its descendents MAY be damaged
- // If a node is undamaged, then ALL of its descendents are undamaged
- if (n instanceof this.BeginEndNode) {
- result = n;
- }
- // Examine children[0] last
- for (var i=0; i < n.children.length; i++) {
- nodes.push(n.children[i]);
- }
- }
- }
- return result || this._tree;
- },
- /** @returns true If n overlaps the interval [start,end].
- * @private
- */
- isDamaged: function(/**BeginEndNode*/ n, start, end) {
- // Note strict > since [2,5] doesn't overlap [5,7]
- return (n.start <= end && n.end > start);
- },
- /**
- * Builds tree from some of the buffer content
- *
- * TODO cleanup params
- * @param {BeginEndNode|ContainerNode} origNode The deepest node that overlaps [rs,rs], or the root.
- * @param {Boolean} repairing
- * @param {Number} rs See _onModelChanged()
- * @param {Number} [editStart] Only used for repairing === true
- * @param {Number} [addedCharCount] Only used for repairing === true
- * @param {Number} [removedCharCount] Only used for repairing === true
- * @returns {Number} The end position that redrawRange should be called for.
- * @private
- */
- parse: function(origNode, repairing, rs, editStart, addedCharCount, removedCharCount) {
- var model = this.textView.getModel();
- var lastLineStart = model.getLineStart(model.getLineCount() - 1);
- var eof = model.getCharCount();
- var initialExpected = this.getInitialExpected(origNode, rs);
-
- // re is best-case stopping point; if we detect change to tree, we must continue past it
- var re = -1;
- if (repairing) {
- origNode.repaired = true;
- origNode.endNeedsUpdate = true;
- var lastChild = origNode.children[origNode.children.length-1];
- var delta = addedCharCount - removedCharCount;
- var lastChildLineEnd = lastChild ? model.getLineEnd(model.getLineAtOffset(lastChild.end + delta)) : -1;
- var editLineEnd = model.getLineEnd(model.getLineAtOffset(editStart + removedCharCount));
- re = Math.max(lastChildLineEnd, editLineEnd);
- }
- re = (re === -1) ? eof : re;
-
- var expected = initialExpected;
- var node = origNode;
- var matchedChildOrEnd = false;
- var pos = rs;
- var redrawEnd = -1;
- while (node && (!repairing || (pos < re))) {
- var matchInfo = this.getNextMatch(model, node, pos);
- if (!matchInfo) {
- // Go to next line, if any
- pos = (pos >= lastLineStart) ? eof : model.getLineStart(model.getLineAtOffset(pos) + 1);
- }
- var match = matchInfo && matchInfo.match,
- rule = matchInfo && matchInfo.rule,
- isSub = matchInfo && matchInfo.isSub,
- isEnd = matchInfo && matchInfo.isEnd;
- if (isSub) {
- pos = this.afterMatch(match);
- if (rule instanceof this.BeginEndRule) {
- matchedChildOrEnd = true;
- // Matched a child. Did we expect that?
- if (repairing && rule === expected.rule && node === expected.parent) {
- // Yes: matched expected child
- var foundChild = expected;
- foundChild.setStart(match);
- // Note: the 'end' position for this node will either be matched, or fixed up by us post-loop
- foundChild.repaired = true;
- foundChild.endNeedsUpdate = true;
- node = foundChild; // descend
- expected = this.getNextExpected(expected, "begin");
- } else {
- if (repairing) {
- // No: matched unexpected child.
- this.prune(node, expected);
- repairing = false;
- }
-
- // Add the new child (will replace 'expected' in node's children list)
- var subNode = new this.BeginEndNode(node, rule, match);
- node.addChild(subNode);
- node = subNode; // descend
- }
- } else {
- // Matched a MatchRule; no changes to tree required
- }
- } else if (isEnd || pos === eof) {
- if (node instanceof this.BeginEndNode) {
- if (match) {
- matchedChildOrEnd = true;
- redrawEnd = Math.max(redrawEnd, node.end); // if end moved up, must still redraw to its old value
- node.setEnd(match);
- pos = this.afterMatch(match);
- // Matched node's end. Did we expect that?
- if (repairing && node === expected && node.parent === expected.parent) {
- // Yes: found the expected end of node
- node.repaired = true;
- delete node.endNeedsUpdate;
- expected = this.getNextExpected(expected, "end");
- } else {
- if (repairing) {
- // No: found an unexpected end
- this.prune(node, expected);
- repairing = false;
- }
- }
- } else {
- // Force-ending a BeginEndNode that runs until eof
- node.setEnd(eof);
- delete node.endNeedsUpdate;
- }
- }
- node = node.parent; // ascend
- }
-
- if (repairing && pos >= re && !matchedChildOrEnd) {
- // Reached re without matching any begin/end => initialExpected itself was removed => repair fail
- this.prune(origNode, initialExpected);
- repairing = false;
- }
- } // end loop
- // TODO: do this for every node we end?
- this.removeUnrepairedChildren(origNode, repairing, rs);
-
- //console.debug("parsed " + (pos - rs) + " of " + model.getCharCount + "buf");
- this.cleanup(repairing, origNode, rs, re, eof, addedCharCount, removedCharCount);
- if (repairing) {
- return Math.max(redrawEnd, pos);
- } else {
- return pos; // where we stopped reparsing
- }
- },
- /** Helper for parse() in the repair case. To be called when ending a node, as any children that
- * lie in [rs,node.end] and were not repaired must've been deleted.
- * @private
- */
- removeUnrepairedChildren: function(node, repairing, start) {
- if (repairing) {
- var children = node.children;
- var removeFrom = -1;
- for (var i=0; i < children.length; i++) {
- var child = children[i];
- if (!child.repaired && this.isDamaged(child, start, Number.MAX_VALUE /*end doesn't matter*/)) {
- removeFrom = i;
- break;
- }
- }
- if (removeFrom !== -1) {
- node.children.length = removeFrom;
- }
- }
- },
- /** Helper for parse() in the repair case
- * @private
- */
- cleanup: function(repairing, origNode, rs, re, eof, addedCharCount, removedCharCount) {
- var i, node, maybeRepairedNodes;
- if (repairing) {
- // The repair succeeded, so update stale begin/end indices by simple translation.
- var delta = addedCharCount - removedCharCount;
- // A repaired node's end can't exceed re, but it may exceed re-delta+1.
- // TODO: find a way to guarantee disjoint intervals for repaired vs unrepaired, then stop using flag
- var maybeUnrepairedNodes = this.getIntersecting(re-delta+1, eof);
- maybeRepairedNodes = this.getIntersecting(rs, re);
- // Handle unrepaired nodes. They are those intersecting [re-delta+1, eof] that don't have the flag
- for (i=0; i < maybeUnrepairedNodes.length; i++) {
- node = maybeUnrepairedNodes[i];
- if (!node.repaired && node instanceof this.BeginEndNode) {
- node.shiftEnd(delta);
- node.shiftStart(delta);
- }
- }
- // Translate 'end' index of repaired node whose 'end' was not matched in loop (>= re)
- for (i=0; i < maybeRepairedNodes.length; i++) {
- node = maybeRepairedNodes[i];
- if (node.repaired && node.endNeedsUpdate) {
- node.shiftEnd(delta);
- }
- delete node.endNeedsUpdate;
- delete node.repaired;
- }
- } else {
- // Clean up after ourself
- maybeRepairedNodes = this.getIntersecting(rs, re);
- for (i=0; i < maybeRepairedNodes.length; i++) {
- delete maybeRepairedNodes[i].repaired;
- }
- }
- },
- /**
- * @param model {orion.textview.TextModel}
- * @param node {Node}
- * @param pos {Number}
- * @param [matchRulesOnly] {Boolean} Optional, if true only "match" subrules will be considered.
- * @returns {Object} A match info object with properties:
- * {Boolean} isEnd
- * {Boolean} isSub
- * {RegExp.match} match
- * {(Match|BeginEnd)Rule} rule
- * @private
- */
- getNextMatch: function(model, node, pos, matchRulesOnly) {
- var lineIndex = model.getLineAtOffset(pos);
- var lineEnd = model.getLineEnd(lineIndex);
- var line = model.getText(pos, lineEnd);
-
- var stack = [],
- expandedContainers = [],
- subMatches = [],
- subrules = [];
- this.push(stack, node.rule.subrules);
- while (stack.length) {
- var next = stack.length ? stack.pop() : null;
- var subrule = next && next._resolvedRule._typedRule;
- if (subrule instanceof this.ContainerRule && expandedContainers.indexOf(subrule) === -1) {
- // Expand ContainerRule by pushing its subrules on
- expandedContainers.push(subrule);
- this.push(stack, subrule.subrules);
- continue;
- }
- if (subrule && matchRulesOnly && !(subrule.matchRegex)) {
- continue;
- }
- var subMatch = subrule && this.exec(subrule.matchRegex || subrule.beginRegex, line, pos);
- if (subMatch) {
- subMatches.push(subMatch);
- subrules.push(subrule);
- }
- }
-
- var bestSub = Number.MAX_VALUE,
- bestSubIndex = -1;
- for (var i=0; i < subMatches.length; i++) {
- var match = subMatches[i];
- if (match.index < bestSub) {
- bestSub = match.index;
- bestSubIndex = i;
- }
- }
-
- if (!matchRulesOnly) {
- // See if the "end" pattern of the active begin/end node matches.
- // TODO: The active begin/end node may not be the same as the node that holds the subrules
- var activeBENode = node;
- var endMatch = this.getEndMatch(node, line, pos);
- if (endMatch) {
- var doEndLast = activeBENode.rule.applyEndPatternLast;
- var endWins = bestSubIndex === -1 || (endMatch.index < bestSub) || (!doEndLast && endMatch.index === bestSub);
- if (endWins) {
- return {isEnd: true, rule: activeBENode.rule, match: endMatch};
- }
- }
- }
- return bestSubIndex === -1 ? null : {isSub: true, rule: subrules[bestSubIndex], match: subMatches[bestSubIndex]};
- },
- /**
- * Gets the node corresponding to the first match we expect to see in the repair.
- * @param {BeginEndNode|ContainerNode} node The node returned via getFirstDamaged(rs,rs) -- may be the root.
- * @param {Number} rs See _onModelChanged()
- * Note that because rs is a line end (or 0, a line start), it will intersect a beginMatch or
- * endMatch either at their 0th character, or not at all. (begin/endMatches can't cross lines).
- * This is the only time we rely on the start/end values from the pre-change tree. After this
- * we only look at node ordering, never use the old indices.
- * @returns {Node}
- * @private
- */
- getInitialExpected: function(node, rs) {
- // TODO: Kind of weird.. maybe ContainerNodes should have start & end set, like BeginEndNodes
- var i, child;
- if (node === this._tree) {
- // get whichever of our children comes after rs
- for (i=0; i < node.children.length; i++) {
- child = node.children[i]; // BeginEndNode
- if (child.start >= rs) {
- return child;
- }
- }
- } else if (node instanceof this.BeginEndNode) {
- if (node.endMatch) {
- // Which comes next after rs: our nodeEnd or one of our children?
- var nodeEnd = node.endMatch.index;
- for (i=0; i < node.children.length; i++) {
- child = node.children[i]; // BeginEndNode
- if (child.start >= rs) {
- break;
- }
- }
- if (child && child.start < nodeEnd) {
- return child; // Expect child as the next match
- }
- } else {
- // No endMatch => node goes until eof => it end should be the next match
- }
- }
- return node; // We expect node to end, so it should be the next match
- },
- /**
- * Helper for repair() to tell us what kind of event we expect next.
- * @param {Node} expected Last value returned by this method.
- * @param {String} event "begin" if the last value of expected was matched as "begin",
- * or "end" if it was matched as an end.
- * @returns {Node} The next expected node to match, or null.
- * @private
- */
- getNextExpected: function(/**Node*/ expected, event) {
- var node = expected;
- if (event === "begin") {
- var child = node.children[0];
- if (child) {
- return child;
- } else {
- return node;
- }
- } else if (event === "end") {
- var parent = node.parent;
- if (parent) {
- var nextSibling = parent.children[parent.children.indexOf(node) + 1];
- if (nextSibling) {
- return nextSibling;
- } else {
- return parent;
- }
- }
- }
- return null;
- },
- /** Helper for parse() when repairing. Prunes out the unmatched nodes from the tree so we can continue parsing.
- * @private
- */
- prune: function(/**BeginEndNode|ContainerNode*/ node, /**Node*/ expected) {
- var expectedAChild = expected.parent === node;
- if (expectedAChild) {
- // Expected child wasn't matched; prune it and all siblings after it
- node.children.length = expected.getIndexInParent();
- } else if (node instanceof this.BeginEndNode) {
- // Expected node to end but it didn't; set its end unknown and we'll match it eventually
- node.endMatch = null;
- node.end = null;
- }
- // Reparsing from node, so prune the successors outside of node's subtree
- if (node.parent) {
- node.parent.children.length = node.getIndexInParent() + 1;
- }
- },
- onLineStyle: function(/**eclipse.LineStyleEvent*/ e) {
- function byStart(r1, r2) {
- return r1.start - r2.start;
- }
-
- if (!this._tree) {
- // In some cases it seems onLineStyle is called before onModelChanged, so we need to parse here
- this.initialParse();
- }
- var lineStart = e.lineStart,
- model = this.textView.getModel(),
- lineEnd = model.getLineEnd(e.lineIndex);
-
- var rs = model.getLineEnd(model.getLineAtOffset(lineStart) - 1); // may be < 0
- var node = this.getFirstDamaged(rs, rs);
-
- var scopes = this.getLineScope(model, node, lineStart, lineEnd);
- e.ranges = this.toStyleRanges(scopes);
- // Editor requires StyleRanges must be in ascending order by 'start', or else some will be ignored
- e.ranges.sort(byStart);
- },
- /** Runs parse algorithm on [start, end] in the context of node, assigning scope as we find matches.
- * @private
- */
- getLineScope: function(model, node, start, end) {
- var pos = start;
- var expected = this.getInitialExpected(node, start);
- var scopes = [],
- gaps = [];
- while (node && (pos < end)) {
- var matchInfo = this.getNextMatch(model, node, pos);
- if (!matchInfo) {
- break; // line is over
- }
- var match = matchInfo && matchInfo.match,
- rule = matchInfo && matchInfo.rule,
- isSub = matchInfo && matchInfo.isSub,
- isEnd = matchInfo && matchInfo.isEnd;
- if (match.index !== pos) {
- // gap [pos..match.index]
- gaps.push({ start: pos, end: match.index, node: node});
- }
- if (isSub) {
- pos = this.afterMatch(match);
- if (rule instanceof this.BeginEndRule) {
- // Matched a "begin", assign its scope and descend into it
- this.addBeginScope(scopes, match, rule);
- node = expected; // descend
- expected = this.getNextExpected(expected, "begin");
- } else {
- // Matched a child MatchRule;
- this.addMatchScope(scopes, match, rule);
- }
- } else if (isEnd) {
- pos = this.afterMatch(match);
- // Matched and "end", assign its end scope and go up
- this.addEndScope(scopes, match, rule);
- expected = this.getNextExpected(expected, "end");
- node = node.parent; // ascend
- }
- }
- if (pos < end) {
- gaps.push({ start: pos, end: end, node: node });
- }
- var inherited = this.getInheritedLineScope(gaps, start, end);
- return scopes.concat(inherited);
- },
- /** @private */
- getInheritedLineScope: function(gaps, start, end) {
- var scopes = [];
- for (var i=0; i < gaps.length; i++) {
- var gap = gaps[i];
- var node = gap.node;
- while (node) {
- // if node defines a contentName or name, apply it
- var rule = node.rule.rule;
- var name = rule.name,
- contentName = rule.contentName;
- // TODO: if both are given, we don't resolve the conflict. contentName always wins
- var scope = contentName || name;
- if (scope) {
- this.addScopeRange(scopes, gap.start, gap.end, scope);
- break;
- }
- node = node.parent;
- }
- }
- return scopes;
- },
- /** @private */
- addBeginScope: function(scopes, match, typedRule) {
- var rule = typedRule.rule;
- this.addCapturesScope(scopes, match, (rule.beginCaptures || rule.captures), typedRule.isComplex, typedRule.beginOld2New, typedRule.beginConsuming);
- },
- /** @private */
- addEndScope: function(scopes, match, typedRule) {
- var rule = typedRule.rule;
- this.addCapturesScope(scopes, match, (rule.endCaptures || rule.captures), typedRule.isComplex, typedRule.endOld2New, typedRule.endConsuming);
- },
- /** @private */
- addMatchScope: function(scopes, match, typedRule) {
- var rule = typedRule.rule,
- name = rule.name,
- captures = rule.captures;
- if (captures) {
- // captures takes priority over name
- this.addCapturesScope(scopes, match, captures, typedRule.isComplex, typedRule.matchOld2New, typedRule.matchConsuming);
- } else {
- this.addScope(scopes, match, name);
- }
- },
- /** @private */
- addScope: function(scopes, match, name) {
- if (!name) { return; }
- scopes.push({start: match.index, end: this.afterMatch(match), scope: name });
- },
- /** @private */
- addScopeRange: function(scopes, start, end, name) {
- if (!name) { return; }
- scopes.push({start: start, end: end, scope: name });
- },
- /** @private */
- addCapturesScope: function(/**Array*/scopes, /*RegExp.match*/ match, /**Object*/captures, /**Boolean*/isComplex, /**Object*/old2New, /**Object*/consuming) {
- if (!captures) { return; }
- if (!isComplex) {
- this.addScope(scopes, match, captures[0] && captures[0].name);
- } else {
- // apply scopes captures[1..n] to matching groups [1]..[n] of match
-
- // Sum up the lengths of preceding consuming groups to get the start offset for each matched group.
- var newGroupStarts = {1: 0};
- var sum = 0;
- for (var num = 1; match[num] !== undefined; num++) {
- if (consuming[num] !== undefined) {
- sum += match[num].length;
- }
- if (match[num+1] !== undefined) {
- newGroupStarts[num + 1] = sum;
- }
- }
- // Map the group numbers referred to in captures object to the new group numbers, and get the actual matched range.
- var start = match.index;
- for (var oldGroupNum = 1; captures[oldGroupNum]; oldGroupNum++) {
- var scope = captures[oldGroupNum].name;
- var newGroupNum = old2New[oldGroupNum];
- var groupStart = start + newGroupStarts[newGroupNum];
- // Not every capturing group defined in regex need match every time the regex is run.
- // eg. (a)|b matches "b" but group 1 is undefined
- if (typeof match[newGroupNum] !== "undefined") {
- var groupEnd = groupStart + match[newGroupNum].length;
- this.addScopeRange(scopes, groupStart, groupEnd, scope);
- }
- }
- }
- },
- /** @returns {Node[]} In depth-first order
- * @private
- */
- getIntersecting: function(start, end) {
- var result = [];
- var nodes = this._tree ? [this._tree] : [];
- while (nodes.length) {
- var n = nodes.pop();
- var visitChildren = false;
- if (n instanceof this.ContainerNode) {
- visitChildren = true;
- } else if (this.isDamaged(n, start, end)) {
- visitChildren = true;
- result.push(n);
- }
- if (visitChildren) {
- var len = n.children.length;
-// for (var i=len-1; i >= 0; i--) {
-// nodes.push(n.children[i]);
-// }
- for (var i=0; i < len; i++) {
- nodes.push(n.children[i]);
- }
- }
- }
- return result.reverse();
- },
- /**
- * Applies the grammar to obtain the {@link eclipse.StyleRange[]} for the given line.
- * @returns eclipse.StyleRange[]
- * @private
- */
- toStyleRanges: function(/**ScopeRange[]*/ scopeRanges) {
- var styleRanges = [];
- for (var i=0; i < scopeRanges.length; i++) {
- var scopeRange = scopeRanges[i];
- var classNames = this._styles[scopeRange.scope];
- if (!classNames) { throw new Error("styles not found for " + scopeRange.scope); }
- var classNamesString = classNames.join(" ");
- styleRanges.push({start: scopeRange.start, end: scopeRange.end, style: {styleClass: classNamesString}});
-// console.debug("{start " + styleRanges[i].start + ", end " + styleRanges[i].end + ", style: " + styleRanges[i].style.styleClass + "}");
- }
- return styleRanges;
- }
- };
-
- return {
- RegexUtil: RegexUtil,
- TextMateStyler: TextMateStyler
- };
-});
-/*******************************************************************************
- * @license
- * Copyright (c) 2010, 2011 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License v1.0
- * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
- * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
- *
- * Contributors: IBM Corporation - initial API and implementation
- * Alex Lakatos - fix for bug#369781
- ******************************************************************************/
-
-/*global document window navigator define */
-
-define("examples/textview/textStyler", ['orion/textview/annotations'], function(mAnnotations) {
-
- var JS_KEYWORDS =
- ["break",
- "case", "class", "catch", "continue", "const",
- "debugger", "default", "delete", "do",
- "else", "enum", "export", "extends",
- "false", "finally", "for", "function",
- "if", "implements", "import", "in", "instanceof", "interface",
- "let",
- "new", "null",
- "package", "private", "protected", "public",
- "return",
- "static", "super", "switch",
- "this", "throw", "true", "try", "typeof",
- "undefined",
- "var", "void",
- "while", "with",
- "yield"];
-
- var JAVA_KEYWORDS =
- ["abstract",
- "boolean", "break", "byte",
- "case", "catch", "char", "class", "continue",
- "default", "do", "double",
- "else", "extends",
- "false", "final", "finally", "float", "for",
- "if", "implements", "import", "instanceof", "int", "interface",
- "long",
- "native", "new", "null",
- "package", "private", "protected", "public",
- "return",
- "short", "static", "super", "switch", "synchronized",
- "this", "throw", "throws", "transient", "true", "try",
- "void", "volatile",
- "while"];
-
- var CSS_KEYWORDS =
- ["alignment-adjust", "alignment-baseline", "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-align", "box-decoration-break", "box-direction", "box-flex", "box-flex-group", "box-lines",
- "box-ordinal-group", "box-orient", "box-pack", "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-align", "flex-flow", "flex-inline-pack", "flex-order",
- "flex-pack", "float", "float-offset", "font", "font-family", "font-size", "font-size-adjust", "font-stretch", "font-style",
- "font-variant", "font-weight", "grid-columns", "grid-rows", "hanging-punctuation", "height", "hyphenate-after",
- "hyphenate-before", "hyphenate-character", "hyphenate-lines", "hyphenate-resource", "hyphens", "icon", "image-orientation",
- "image-rendering", "image-resolution", "inline-box-align", "left", "letter-spacing", "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", "mark", "mark-after", "mark-before",
- "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", "orphans",
- "outline", "outline-color", "outline-offset", "outline-style", "outline-width", "overflow", "overflow-style", "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", "phonemes", "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-header", "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set", "table-layout",
- "target", "target-name", "target-new", "target-position", "text-align", "text-align-last", "text-decoration", "text-emphasis",
- "text-height", "text-indent", "text-justify", "text-outline", "text-shadow", "text-transform", "text-wrap", "top", "transform",
- "transform-origin", "transform-style", "transition", "transition-delay", "transition-duration", "transition-property",
- "transition-timing-function", "unicode-bidi", "vector-effect", "vertical-align", "visibility", "voice-balance", "voice-duration", "voice-family",
- "voice-pitch", "voice-pitch-range", "voice-rate", "voice-stress", "voice-volume", "volume", "white-space", "white-space-collapse",
- "widows", "width", "word-break", "word-spacing", "word-wrap", "z-index"
- ];
-
- // Scanner constants
- var UNKOWN = 1;
- var KEYWORD = 2;
- var STRING = 3;
- var SINGLELINE_COMMENT = 4;
- var MULTILINE_COMMENT = 5;
- var DOC_COMMENT = 6;
- var WHITE = 7;
- var WHITE_TAB = 8;
- var WHITE_SPACE = 9;
- var HTML_MARKUP = 10;
- var DOC_TAG = 11;
- var TASK_TAG = 12;
-
- // Styles
- var singleCommentStyle = {styleClass: "token_singleline_comment"};
- var multiCommentStyle = {styleClass: "token_multiline_comment"};
- var docCommentStyle = {styleClass: "token_doc_comment"};
- var htmlMarkupStyle = {styleClass: "token_doc_html_markup"};
- var tasktagStyle = {styleClass: "token_task_tag"};
- var doctagStyle = {styleClass: "token_doc_tag"};
- var stringStyle = {styleClass: "token_string"};
- var keywordStyle = {styleClass: "token_keyword"};
- var spaceStyle = {styleClass: "token_space"};
- var tabStyle = {styleClass: "token_tab"};
- var caretLineStyle = {styleClass: "line_caret"};
-
- function Scanner (keywords, whitespacesVisible) {
- this.keywords = keywords;
- this.whitespacesVisible = whitespacesVisible;
- this.setText("");
- }
-
- Scanner.prototype = {
- getOffset: function() {
- return this.offset;
- },
- getStartOffset: function() {
- return this.startOffset;
- },
- getData: function() {
- return this.text.substring(this.startOffset, this.offset);
- },
- getDataLength: function() {
- return this.offset - this.startOffset;
- },
- _default: function(c) {
- var keywords = this.keywords;
- switch (c) {
- case 32: // SPACE
- case 9: // TAB
- if (this.whitespacesVisible) {
- return c === 32 ? WHITE_SPACE : WHITE_TAB;
- }
- do {
- c = this._read();
- } while(c === 32 || c === 9);
- this._unread(c);
- return WHITE;
- case 123: // {
- case 125: // }
- case 40: // (
- case 41: // )
- case 91: // [
- case 93: // ]
- case 60: // <
- case 62: // >
- // BRACKETS
- return c;
- default:
- var isCSS = this.isCSS;
- if ((97 <= c && c <= 122) || (65 <= c && c <= 90) || c === 95 || (48 <= c && c <= 57) || (0x2d === c && isCSS)) { //LETTER OR UNDERSCORE OR NUMBER
- var off = this.offset - 1;
- do {
- c = this._read();
- } while((97 <= c && c <= 122) || (65 <= c && c <= 90) || c === 95 || (48 <= c && c <= 57) || (0x2d === c && isCSS)); //LETTER OR UNDERSCORE OR NUMBER
- this._unread(c);
- if (keywords.length > 0) {
- var word = this.text.substring(off, this.offset);
- //TODO slow
- for (var i=0; i comment
- c = this._read();
- if (!this.isCSS) {
- if (c === 47) { // SLASH -> single line
- while (true) {
- c = this._read();
- if ((c === -1) || (c === 10) || (c === 13)) {
- this._unread(c);
- return SINGLELINE_COMMENT;
- }
- }
- }
- }
- if (c === 42) { // STAR -> multi line
- c = this._read();
- var token = MULTILINE_COMMENT;
- if (c === 42) {
- token = DOC_COMMENT;
- }
- while (true) {
- while (c === 42) {
- c = this._read();
- if (c === 47) {
- return token;
- }
- }
- if (c === -1) {
- this._unread(c);
- return token;
- }
- c = this._read();
- }
- }
- this._unread(c);
- return UNKOWN;
- case 39: // SINGLE QUOTE -> char const
- while(true) {
- c = this._read();
- switch (c) {
- case 39:
- return STRING;
- case 13:
- case 10:
- case -1:
- this._unread(c);
- return STRING;
- case 92: // BACKSLASH
- c = this._read();
- break;
- }
- }
- break;
- case 34: // DOUBLE QUOTE -> string
- while(true) {
- c = this._read();
- switch (c) {
- case 34: // DOUBLE QUOTE
- return STRING;
- case 13:
- case 10:
- case -1:
- this._unread(c);
- return STRING;
- case 92: // BACKSLASH
- c = this._read();
- break;
- }
- }
- break;
- default:
- return this._default(c);
- }
- }
- },
- setText: function(text) {
- this.text = text;
- this.offset = 0;
- this.startOffset = 0;
- }
- };
-
- function WhitespaceScanner () {
- Scanner.call(this, null, true);
- }
- WhitespaceScanner.prototype = new Scanner(null);
- WhitespaceScanner.prototype.nextToken = function() {
- this.startOffset = this.offset;
- while (true) {
- var c = this._read();
- switch (c) {
- case -1: return null;
- case 32: // SPACE
- return WHITE_SPACE;
- case 9: // TAB
- return WHITE_TAB;
- default:
- do {
- c = this._read();
- } while(!(c === 32 || c === 9 || c === -1));
- this._unread(c);
- return UNKOWN;
- }
- }
- };
-
- function CommentScanner (whitespacesVisible) {
- Scanner.call(this, null, whitespacesVisible);
- }
- CommentScanner.prototype = new Scanner(null);
- CommentScanner.prototype.setType = function(type) {
- this._type = type;
- };
- CommentScanner.prototype.nextToken = function() {
- this.startOffset = this.offset;
- while (true) {
- var c = this._read();
- switch (c) {
- case -1: return null;
- case 32: // SPACE
- case 9: // TAB
- if (this.whitespacesVisible) {
- return c === 32 ? WHITE_SPACE : WHITE_TAB;
- }
- do {
- c = this._read();
- } while(c === 32 || c === 9);
- this._unread(c);
- return WHITE;
- case 60: // <
- if (this._type === DOC_COMMENT) {
- do {
- c = this._read();
- } while(!(c === 62 || c === -1)); // >
- if (c === 62) {
- return HTML_MARKUP;
- }
- }
- return UNKOWN;
- case 64: // @
- if (this._type === DOC_COMMENT) {
- do {
- c = this._read();
- } while((97 <= c && c <= 122) || (65 <= c && c <= 90) || c === 95 || (48 <= c && c <= 57)); //LETTER OR UNDERSCORE OR NUMBER
- this._unread(c);
- return DOC_TAG;
- }
- return UNKOWN;
- case 84: // T
- if ((c = this._read()) === 79) { // O
- if ((c = this._read()) === 68) { // D
- if ((c = this._read()) === 79) { // O
- c = this._read();
- if (!((97 <= c && c <= 122) || (65 <= c && c <= 90) || c === 95 || (48 <= c && c <= 57))) {
- this._unread(c);
- return TASK_TAG;
- }
- this._unread(c);
- } else {
- this._unread(c);
- }
- } else {
- this._unread(c);
- }
- } else {
- this._unread(c);
- }
- //FALL THROUGH
- default:
- do {
- c = this._read();
- } while(!(c === 32 || c === 9 || c === -1 || c === 60 || c === 64 || c === 84));
- this._unread(c);
- return UNKOWN;
- }
- }
- };
-
- function FirstScanner () {
- Scanner.call(this, null, false);
- }
- FirstScanner.prototype = new Scanner(null);
- FirstScanner.prototype._default = function(c) {
- while(true) {
- c = this._read();
- switch (c) {
- case 47: // SLASH
- case 34: // DOUBLE QUOTE
- case 39: // SINGLE QUOTE
- case -1:
- this._unread(c);
- return UNKOWN;
- }
- }
- };
-
- function TextStyler (view, lang, annotationModel) {
- this.commentStart = "/*";
- this.commentEnd = "*/";
- var keywords = [];
- switch (lang) {
- case "java": keywords = JAVA_KEYWORDS; break;
- case "js": keywords = JS_KEYWORDS; break;
- case "css": keywords = CSS_KEYWORDS; break;
- }
- this.whitespacesVisible = false;
- this.detectHyperlinks = true;
- this.highlightCaretLine = false;
- this.foldingEnabled = true;
- this.detectTasks = true;
- this._scanner = new Scanner(keywords, this.whitespacesVisible);
- this._firstScanner = new FirstScanner();
- this._commentScanner = new CommentScanner(this.whitespacesVisible);
- this._whitespaceScanner = new WhitespaceScanner();
- //TODO these scanners are not the best/correct way to parse CSS
- if (lang === "css") {
- this._scanner.isCSS = true;
- this._firstScanner.isCSS = true;
- }
- this.view = view;
- this.annotationModel = annotationModel;
- this._bracketAnnotations = undefined;
-
- var self = this;
- this._listener = {
- onChanged: function(e) {
- self._onModelChanged(e);
- },
- onDestroy: function(e) {
- self._onDestroy(e);
- },
- onLineStyle: function(e) {
- self._onLineStyle(e);
- },
- onSelection: function(e) {
- self._onSelection(e);
- }
- };
- var model = view.getModel();
- if (model.getBaseModel) {
- model.getBaseModel().addEventListener("Changed", this._listener.onChanged);
- } else {
- //TODO still needed to keep the event order correct (styler before view)
- view.addEventListener("ModelChanged", this._listener.onChanged);
- }
- view.addEventListener("Selection", this._listener.onSelection);
- view.addEventListener("Destroy", this._listener.onDestroy);
- view.addEventListener("LineStyle", this._listener.onLineStyle);
- this._computeComments ();
- this._computeFolding();
- view.redrawLines();
- }
-
- TextStyler.prototype = {
- getClassNameForToken: function(token) {
- switch (token) {
- case "singleLineComment": return singleCommentStyle.styleClass;
- case "multiLineComment": return multiCommentStyle.styleClass;
- case "docComment": return docCommentStyle.styleClass;
- case "docHtmlComment": return htmlMarkupStyle.styleClass;
- case "tasktag": return tasktagStyle.styleClass;
- case "doctag": return doctagStyle.styleClass;
- case "string": return stringStyle.styleClass;
- case "keyword": return keywordStyle.styleClass;
- case "space": return spaceStyle.styleClass;
- case "tab": return tabStyle.styleClass;
- case "caretLine": return caretLineStyle.styleClass;
- }
- return null;
- },
- destroy: function() {
- var view = this.view;
- if (view) {
- var model = view.getModel();
- if (model.getBaseModel) {
- model.getBaseModel().removeEventListener("Changed", this._listener.onChanged);
- } else {
- view.removeEventListener("ModelChanged", this._listener.onChanged);
- }
- view.removeEventListener("Selection", this._listener.onSelection);
- view.removeEventListener("Destroy", this._listener.onDestroy);
- view.removeEventListener("LineStyle", this._listener.onLineStyle);
- this.view = null;
- }
- },
- setHighlightCaretLine: function(highlight) {
- this.highlightCaretLine = highlight;
- },
- setWhitespacesVisible: function(visible) {
- this.whitespacesVisible = visible;
- this._scanner.whitespacesVisible = visible;
- this._commentScanner.whitespacesVisible = visible;
- },
- setDetectHyperlinks: function(enabled) {
- this.detectHyperlinks = enabled;
- },
- setFoldingEnabled: function(enabled) {
- this.foldingEnabled = enabled;
- },
- setDetectTasks: function(enabled) {
- this.detectTasks = enabled;
- },
- _binarySearch: function (array, offset, inclusive, low, high) {
- var index;
- if (low === undefined) { low = -1; }
- if (high === undefined) { high = array.length; }
- while (high - low > 1) {
- index = Math.floor((high + low) / 2);
- if (offset <= array[index].start) {
- high = index;
- } else if (inclusive && offset < array[index].end) {
- high = index;
- break;
- } else {
- low = index;
- }
- }
- return high;
- },
- _computeComments: function() {
- var model = this.view.getModel();
- if (model.getBaseModel) { model = model.getBaseModel(); }
- this.comments = this._findComments(model.getText());
- },
- _computeFolding: function() {
- if (!this.foldingEnabled) { return; }
- var view = this.view;
- var viewModel = view.getModel();
- if (!viewModel.getBaseModel) { return; }
- var annotationModel = this.annotationModel;
- if (!annotationModel) { return; }
- annotationModel.removeAnnotations("orion.annotation.folding");
- var add = [];
- var baseModel = viewModel.getBaseModel();
- var comments = this.comments;
- for (var i=0; i
", {styleClass: "annotation expanded"},
- "", {styleClass: "annotation collapsed"});
- },
- _computeTasks: function(type, commentStart, commentEnd) {
- if (!this.detectTasks) { return; }
- var annotationModel = this.annotationModel;
- if (!annotationModel) { return; }
- var view = this.view;
- var viewModel = view.getModel(), baseModel = viewModel;
- if (viewModel.getBaseModel) { baseModel = viewModel.getBaseModel(); }
- var annotations = annotationModel.getAnnotations(commentStart, commentEnd);
- var remove = [];
- var annotationType = "orion.annotation.task";
- while (annotations.hasNext()) {
- var annotation = annotations.next();
- if (annotation.type === annotationType) {
- remove.push(annotation);
- }
- }
- var add = [];
- var scanner = this._commentScanner;
- scanner.setText(baseModel.getText(commentStart, commentEnd));
- var token;
- while ((token = scanner.nextToken())) {
- var tokenStart = scanner.getStartOffset() + commentStart;
- if (token === TASK_TAG) {
- var end = baseModel.getLineEnd(baseModel.getLineAtOffset(tokenStart));
- if (type !== SINGLELINE_COMMENT) {
- end = Math.min(end, commentEnd - this.commentEnd.length);
- }
- add.push({
- start: tokenStart,
- end: end,
- type: annotationType,
- title: baseModel.getText(tokenStart, end),
- style: {styleClass: "annotation task"},
- html: "",
- overviewStyle: {styleClass: "annotationOverview task"},
- rangeStyle: {styleClass: "annotationRange task"}
- });
- }
- }
- annotationModel.replaceAnnotations(remove, add);
- },
- _getLineStyle: function(lineIndex) {
- if (this.highlightCaretLine) {
- var view = this.view;
- var model = view.getModel();
- var selection = view.getSelection();
- if (selection.start === selection.end && model.getLineAtOffset(selection.start) === lineIndex) {
- return caretLineStyle;
- }
- }
- return null;
- },
- _getStyles: function(model, text, start) {
- if (model.getBaseModel) {
- start = model.mapOffset(start);
- }
- var end = start + text.length;
-
- var styles = [];
-
- // for any sub range that is not a comment, parse code generating tokens (keywords, numbers, brackets, line comments, etc)
- var offset = start, comments = this.comments;
- var startIndex = this._binarySearch(comments, start, true);
- for (var i = startIndex; i < comments.length; i++) {
- if (comments[i].start >= end) { break; }
- var commentStart = comments[i].start;
- var commentEnd = comments[i].end;
- if (offset < commentStart) {
- this._parse(text.substring(offset - start, commentStart - start), offset, styles);
- }
- var style = comments[i].type === DOC_COMMENT ? docCommentStyle : multiCommentStyle;
- if (this.whitespacesVisible || this.detectHyperlinks) {
- var s = Math.max(offset, commentStart);
- var e = Math.min(end, commentEnd);
- this._parseComment(text.substring(s - start, e - start), s, styles, style, comments[i].type);
- } else {
- styles.push({start: commentStart, end: commentEnd, style: style});
- }
- offset = commentEnd;
- }
- if (offset < end) {
- this._parse(text.substring(offset - start, end - start), offset, styles);
- }
- if (model.getBaseModel) {
- for (var j = 0; j < styles.length; j++) {
- var length = styles[j].end - styles[j].start;
- styles[j].start = model.mapOffset(styles[j].start, true);
- styles[j].end = styles[j].start + length;
- }
- }
- return styles;
- },
- _parse: function(text, offset, styles) {
- var scanner = this._scanner;
- scanner.setText(text);
- var token;
- while ((token = scanner.nextToken())) {
- var tokenStart = scanner.getStartOffset() + offset;
- var style = null;
- switch (token) {
- case KEYWORD: style = keywordStyle; break;
- case STRING:
- if (this.whitespacesVisible) {
- this._parseString(scanner.getData(), tokenStart, styles, stringStyle);
- continue;
- } else {
- style = stringStyle;
- }
- break;
- case DOC_COMMENT:
- this._parseComment(scanner.getData(), tokenStart, styles, docCommentStyle, token);
- continue;
- case SINGLELINE_COMMENT:
- this._parseComment(scanner.getData(), tokenStart, styles, singleCommentStyle, token);
- continue;
- case MULTILINE_COMMENT:
- this._parseComment(scanner.getData(), tokenStart, styles, multiCommentStyle, token);
- continue;
- case WHITE_TAB:
- if (this.whitespacesVisible) {
- style = tabStyle;
- }
- break;
- case WHITE_SPACE:
- if (this.whitespacesVisible) {
- style = spaceStyle;
- }
- break;
- }
- styles.push({start: tokenStart, end: scanner.getOffset() + offset, style: style});
- }
- },
- _parseComment: function(text, offset, styles, s, type) {
- var scanner = this._commentScanner;
- scanner.setText(text);
- scanner.setType(type);
- var token;
- while ((token = scanner.nextToken())) {
- var tokenStart = scanner.getStartOffset() + offset;
- var style = s;
- switch (token) {
- case WHITE_TAB:
- if (this.whitespacesVisible) {
- style = tabStyle;
- }
- break;
- case WHITE_SPACE:
- if (this.whitespacesVisible) {
- style = spaceStyle;
- }
- break;
- case HTML_MARKUP:
- style = htmlMarkupStyle;
- break;
- case DOC_TAG:
- style = doctagStyle;
- break;
- case TASK_TAG:
- style = tasktagStyle;
- break;
- default:
- if (this.detectHyperlinks) {
- style = this._detectHyperlinks(scanner.getData(), tokenStart, styles, style);
- }
- }
- if (style) {
- styles.push({start: tokenStart, end: scanner.getOffset() + offset, style: style});
- }
- }
- },
- _parseString: function(text, offset, styles, s) {
- var scanner = this._whitespaceScanner;
- scanner.setText(text);
- var token;
- while ((token = scanner.nextToken())) {
- var tokenStart = scanner.getStartOffset() + offset;
- var style = s;
- switch (token) {
- case WHITE_TAB:
- if (this.whitespacesVisible) {
- style = tabStyle;
- }
- break;
- case WHITE_SPACE:
- if (this.whitespacesVisible) {
- style = spaceStyle;
- }
- break;
- }
- if (style) {
- styles.push({start: tokenStart, end: scanner.getOffset() + offset, style: style});
- }
- }
- },
- _detectHyperlinks: function(text, offset, styles, s) {
- var href = null, index, linkStyle;
- if ((index = text.indexOf("://")) > 0) {
- href = text;
- var start = index;
- while (start > 0) {
- var c = href.charCodeAt(start - 1);
- if (!((97 <= c && c <= 122) || (65 <= c && c <= 90) || 0x2d === c || (48 <= c && c <= 57))) { //LETTER OR DASH OR NUMBER
- break;
- }
- start--;
- }
- if (start > 0) {
- var brackets = "\"\"''(){}[]<>";
- index = brackets.indexOf(href.substring(start - 1, start));
- if (index !== -1 && (index & 1) === 0 && (index = href.lastIndexOf(brackets.substring(index + 1, index + 2))) !== -1) {
- var end = index;
- linkStyle = this._clone(s);
- linkStyle.tagName = "A";
- linkStyle.attributes = {href: href.substring(start, end)};
- styles.push({start: offset, end: offset + start, style: s});
- styles.push({start: offset + start, end: offset + end, style: linkStyle});
- styles.push({start: offset + end, end: offset + text.length, style: s});
- return null;
- }
- }
- } else if (text.toLowerCase().indexOf("bug#") === 0) {
- href = "https://bugs.eclipse.org/bugs/show_bug.cgi?id=" + parseInt(text.substring(4), 10);
- }
- if (href) {
- linkStyle = this._clone(s);
- linkStyle.tagName = "A";
- linkStyle.attributes = {href: href};
- return linkStyle;
- }
- return s;
- },
- _clone: function(obj) {
- if (!obj) { return obj; }
- var newObj = {};
- for (var p in obj) {
- if (obj.hasOwnProperty(p)) {
- var value = obj[p];
- newObj[p] = value;
- }
- }
- return newObj;
- },
- _findComments: function(text, offset) {
- offset = offset || 0;
- var scanner = this._firstScanner, token;
- scanner.setText(text);
- var result = [];
- while ((token = scanner.nextToken())) {
- if (token === MULTILINE_COMMENT || token === DOC_COMMENT) {
- var comment = {
- start: scanner.getStartOffset() + offset,
- end: scanner.getOffset() + offset,
- type: token
- };
- result.push(comment);
- //TODO can we avoid this work if edition does not overlap comment?
- this._computeTasks(token, scanner.getStartOffset() + offset, scanner.getOffset() + offset);
- }
- if (token === SINGLELINE_COMMENT) {
- //TODO can we avoid this work if edition does not overlap comment?
- this._computeTasks(token, scanner.getStartOffset() + offset, scanner.getOffset() + offset);
- }
- }
- return result;
- },
- _findMatchingBracket: function(model, offset) {
- var brackets = "{}()[]<>";
- var bracket = model.getText(offset, offset + 1);
- var bracketIndex = brackets.indexOf(bracket, 0);
- if (bracketIndex === -1) { return -1; }
- var closingBracket;
- if (bracketIndex & 1) {
- closingBracket = brackets.substring(bracketIndex - 1, bracketIndex);
- } else {
- closingBracket = brackets.substring(bracketIndex + 1, bracketIndex + 2);
- }
- var lineIndex = model.getLineAtOffset(offset);
- var lineText = model.getLine(lineIndex);
- var lineStart = model.getLineStart(lineIndex);
- var lineEnd = model.getLineEnd(lineIndex);
- brackets = this._findBrackets(bracket, closingBracket, lineText, lineStart, lineStart, lineEnd);
- for (var i=0; i= 0 ? 1 : -1;
- if (brackets[i] * sign === offset) {
- var level = 1;
- if (bracketIndex & 1) {
- i--;
- for (; i>=0; i--) {
- sign = brackets[i] >= 0 ? 1 : -1;
- level += sign;
- if (level === 0) {
- return brackets[i] * sign;
- }
- }
- lineIndex -= 1;
- while (lineIndex >= 0) {
- lineText = model.getLine(lineIndex);
- lineStart = model.getLineStart(lineIndex);
- lineEnd = model.getLineEnd(lineIndex);
- brackets = this._findBrackets(bracket, closingBracket, lineText, lineStart, lineStart, lineEnd);
- for (var j=brackets.length - 1; j>=0; j--) {
- sign = brackets[j] >= 0 ? 1 : -1;
- level += sign;
- if (level === 0) {
- return brackets[j] * sign;
- }
- }
- lineIndex--;
- }
- } else {
- i++;
- for (; i= 0 ? 1 : -1;
- level += sign;
- if (level === 0) {
- return brackets[i] * sign;
- }
- }
- lineIndex += 1;
- var lineCount = model.getLineCount ();
- while (lineIndex < lineCount) {
- lineText = model.getLine(lineIndex);
- lineStart = model.getLineStart(lineIndex);
- lineEnd = model.getLineEnd(lineIndex);
- brackets = this._findBrackets(bracket, closingBracket, lineText, lineStart, lineStart, lineEnd);
- for (var k=0; k= 0 ? 1 : -1;
- level += sign;
- if (level === 0) {
- return brackets[k] * sign;
- }
- }
- lineIndex++;
- }
- }
- break;
- }
- }
- return -1;
- },
- _findBrackets: function(bracket, closingBracket, text, textOffset, start, end) {
- var result = [];
- var bracketToken = bracket.charCodeAt(0);
- var closingBracketToken = closingBracket.charCodeAt(0);
- // for any sub range that is not a comment, parse code generating tokens (keywords, numbers, brackets, line comments, etc)
- var offset = start, scanner = this._scanner, token, comments = this.comments;
- var startIndex = this._binarySearch(comments, start, true);
- for (var i = startIndex; i < comments.length; i++) {
- if (comments[i].start >= end) { break; }
- var commentStart = comments[i].start;
- var commentEnd = comments[i].end;
- if (offset < commentStart) {
- scanner.setText(text.substring(offset - start, commentStart - start));
- while ((token = scanner.nextToken())) {
- if (token === bracketToken) {
- result.push(scanner.getStartOffset() + offset - start + textOffset);
- } else if (token === closingBracketToken) {
- result.push(-(scanner.getStartOffset() + offset - start + textOffset));
- }
- }
- }
- offset = commentEnd;
- }
- if (offset < end) {
- scanner.setText(text.substring(offset - start, end - start));
- while ((token = scanner.nextToken())) {
- if (token === bracketToken) {
- result.push(scanner.getStartOffset() + offset - start + textOffset);
- } else if (token === closingBracketToken) {
- result.push(-(scanner.getStartOffset() + offset - start + textOffset));
- }
- }
- }
- return result;
- },
- _onDestroy: function(e) {
- this.destroy();
- },
- _onLineStyle: function (e) {
- if (e.textView === this.view) {
- e.style = this._getLineStyle(e.lineIndex);
- }
- e.ranges = this._getStyles(e.textView.getModel(), e.lineText, e.lineStart);
- },
- _onSelection: function(e) {
- var oldSelection = e.oldValue;
- var newSelection = e.newValue;
- var view = this.view;
- var model = view.getModel();
- var lineIndex;
- if (this.highlightCaretLine) {
- var oldLineIndex = model.getLineAtOffset(oldSelection.start);
- lineIndex = model.getLineAtOffset(newSelection.start);
- var newEmpty = newSelection.start === newSelection.end;
- var oldEmpty = oldSelection.start === oldSelection.end;
- if (!(oldLineIndex === lineIndex && oldEmpty && newEmpty)) {
- if (oldEmpty) {
- view.redrawLines(oldLineIndex, oldLineIndex + 1);
- }
- if ((oldLineIndex !== lineIndex || !oldEmpty) && newEmpty) {
- view.redrawLines(lineIndex, lineIndex + 1);
- }
- }
- }
- if (!this.annotationModel) { return; }
- var remove = this._bracketAnnotations, add, caret;
- if (newSelection.start === newSelection.end && (caret = view.getCaretOffset()) > 0) {
- var mapCaret = caret - 1;
- if (model.getBaseModel) {
- mapCaret = model.mapOffset(mapCaret);
- model = model.getBaseModel();
- }
- var bracket = this._findMatchingBracket(model, mapCaret);
- if (bracket !== -1) {
- add = [{
- start: bracket,
- end: bracket + 1,
- type: "orion.annotation.matchingBracket",
- title: "Matching Bracket",
- html: "",
- overviewStyle: {styleClass: "annotationOverview matchingBracket"},
- rangeStyle: {styleClass: "annotationRange matchingBracket"}
- },
- {
- start: mapCaret,
- end: mapCaret + 1,
- type: "orion.annotation.currentBracket",
- title: "Current Bracket",
- html: "",
- overviewStyle: {styleClass: "annotationOverview currentBracket"},
- rangeStyle: {styleClass: "annotationRange currentBracket"}
- }];
- }
- }
- this._bracketAnnotations = add;
- this.annotationModel.replaceAnnotations(remove, add);
- },
- _onModelChanged: function(e) {
- var start = e.start;
- var removedCharCount = e.removedCharCount;
- var addedCharCount = e.addedCharCount;
- var changeCount = addedCharCount - removedCharCount;
- var view = this.view;
- var viewModel = view.getModel();
- var baseModel = viewModel.getBaseModel ? viewModel.getBaseModel() : viewModel;
- var end = start + removedCharCount;
- var charCount = baseModel.getCharCount();
- var commentCount = this.comments.length;
- var lineStart = baseModel.getLineStart(baseModel.getLineAtOffset(start));
- var commentStart = this._binarySearch(this.comments, lineStart, true);
- var commentEnd = this._binarySearch(this.comments, end, false, commentStart - 1, commentCount);
-
- var ts;
- if (commentStart < commentCount && this.comments[commentStart].start <= lineStart && lineStart < this.comments[commentStart].end) {
- ts = this.comments[commentStart].start;
- if (ts > start) { ts += changeCount; }
- } else {
- if (commentStart === commentCount && commentCount > 0 && charCount - changeCount === this.comments[commentCount - 1].end) {
- ts = this.comments[commentCount - 1].start;
- } else {
- ts = lineStart;
- }
- }
- var te;
- if (commentEnd < commentCount) {
- te = this.comments[commentEnd].end;
- if (te > start) { te += changeCount; }
- commentEnd += 1;
- } else {
- commentEnd = commentCount;
- te = charCount;//TODO could it be smaller?
- }
- var text = baseModel.getText(ts, te), comment;
- var newComments = this._findComments(text, ts), i;
- for (i = commentStart; i < this.comments.length; i++) {
- comment = this.comments[i];
- if (comment.start > start) { comment.start += changeCount; }
- if (comment.start > start) { comment.end += changeCount; }
- }
- var redraw = (commentEnd - commentStart) !== newComments.length;
- if (!redraw) {
- for (i=0; i start) {
- annotationStart -= changeCount;
- }
- if (annotationEnd > start) {
- annotationEnd -= changeCount;
- }
- if (annotationStart <= start && start < annotationEnd && annotationStart <= end && end < annotationEnd) {
- var startLine = baseModel.getLineAtOffset(annotation.start);
- var endLine = baseModel.getLineAtOffset(annotation.end);
- if (startLine !== endLine) {
- if (!annotation.expanded) {
- annotation.expand();
- annotationModel.modifyAnnotation(annotation);
- }
- } else {
- annotationModel.removeAnnotation(annotation);
- }
- }
- }
- }
- }
- var add = [];
- for (i = 0; i < newComments.length; i++) {
- comment = newComments[i];
- for (var j = 0; j < all.length; j++) {
- if (all[j].start === comment.start && all[j].end === comment.end) {
- break;
- }
- }
- if (j === all.length) {
- annotation = this._createFoldingAnnotation(viewModel, baseModel, comment.start, comment.end);
- if (annotation) {
- add.push(annotation);
- }
- }
- }
- annotationModel.replaceAnnotations(remove, add);
- }
- }
- };
-
- return {TextStyler: TextStyler};
-});
diff --git a/browser/devtools/sourceeditor/source-editor-orion.jsm b/browser/devtools/sourceeditor/source-editor-orion.jsm
deleted file mode 100644
index aa326478332a..000000000000
--- a/browser/devtools/sourceeditor/source-editor-orion.jsm
+++ /dev/null
@@ -1,2131 +0,0 @@
-/* vim:set ts=2 sw=2 sts=2 et tw=80:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const Cu = Components.utils;
-const Ci = Components.interfaces;
-
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource:///modules/devtools/sourceeditor/source-editor-ui.jsm");
-
-XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
- "@mozilla.org/widget/clipboardhelper;1",
- "nsIClipboardHelper");
-
-const ORION_SCRIPT = "chrome://browser/content/devtools/orion.js";
-const ORION_IFRAME = "data:text/html;charset=utf8," +
- "" +
- "" +
- "" +
- "" +
- "";
-
-const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
-/**
- * Maximum allowed vertical offset for the line index when you call
- * SourceEditor.setCaretPosition().
- *
- * @type number
- */
-const VERTICAL_OFFSET = 3;
-
-/**
- * The primary selection update delay. On Linux, the X11 primary selection is
- * updated to hold the currently selected text.
- *
- * @type number
- */
-const PRIMARY_SELECTION_DELAY = 100;
-
-/**
- * Predefined themes for syntax highlighting. This objects maps
- * SourceEditor.THEMES to Orion CSS files.
- */
-const ORION_THEMES = {
- mozilla: ["chrome://browser/skin/devtools/orion.css"],
-};
-
-/**
- * Known Orion editor events you can listen for. This object maps several of the
- * SourceEditor.EVENTS to Orion events.
- */
-const ORION_EVENTS = {
- ContextMenu: "ContextMenu",
- TextChanged: "ModelChanged",
- Selection: "Selection",
- Focus: "Focus",
- Blur: "Blur",
- MouseOver: "MouseOver",
- MouseOut: "MouseOut",
- MouseMove: "MouseMove",
-};
-
-/**
- * Known Orion annotation types.
- */
-const ORION_ANNOTATION_TYPES = {
- currentBracket: "orion.annotation.currentBracket",
- matchingBracket: "orion.annotation.matchingBracket",
- breakpoint: "orion.annotation.breakpoint",
- task: "orion.annotation.task",
- currentLine: "orion.annotation.currentLine",
- debugLocation: "mozilla.annotation.debugLocation",
-};
-
-/**
- * Default key bindings in the Orion editor.
- */
-const DEFAULT_KEYBINDINGS = [
- {
- action: "enter",
- code: Ci.nsIDOMKeyEvent.DOM_VK_ENTER,
- },
- {
- action: "undo",
- code: Ci.nsIDOMKeyEvent.DOM_VK_Z,
- accel: true,
- },
- {
- action: "redo",
- code: Ci.nsIDOMKeyEvent.DOM_VK_Z,
- accel: true,
- shift: true,
- },
- {
- action: "Unindent Lines",
- code: Ci.nsIDOMKeyEvent.DOM_VK_TAB,
- shift: true,
- },
- {
- action: "Move Lines Up",
- code: Ci.nsIDOMKeyEvent.DOM_VK_UP,
- ctrl: Services.appinfo.OS == "Darwin",
- alt: true,
- },
- {
- action: "Move Lines Down",
- code: Ci.nsIDOMKeyEvent.DOM_VK_DOWN,
- ctrl: Services.appinfo.OS == "Darwin",
- alt: true,
- },
- {
- action: "Comment/Uncomment",
- code: Ci.nsIDOMKeyEvent.DOM_VK_SLASH,
- accel: true,
- },
- {
- action: "Move to Bracket Opening",
- code: Ci.nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET,
- accel: true,
- alt: true,
- },
- {
- action: "Move to Bracket Closing",
- code: Ci.nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET,
- accel: true,
- alt: true,
- },
-];
-
-if (Services.appinfo.OS == "WINNT" ||
- Services.appinfo.OS == "Linux") {
- DEFAULT_KEYBINDINGS.push({
- action: "redo",
- code: Ci.nsIDOMKeyEvent.DOM_VK_Y,
- accel: true,
- });
-}
-
-this.EXPORTED_SYMBOLS = ["SourceEditor"];
-
-/**
- * The SourceEditor object constructor. The SourceEditor component allows you to
- * provide users with an editor tailored to the specific needs of editing source
- * code, aimed primarily at web developers.
- *
- * The editor used here is Eclipse Orion (see http://www.eclipse.org/orion).
- *
- * @constructor
- */
-this.SourceEditor = function SourceEditor() {
- // Update the SourceEditor defaults from user preferences.
-
- SourceEditor.DEFAULTS.tabSize =
- Services.prefs.getIntPref(SourceEditor.PREFS.TAB_SIZE);
- SourceEditor.DEFAULTS.expandTab =
- Services.prefs.getBoolPref(SourceEditor.PREFS.EXPAND_TAB);
-
- this._onOrionSelection = this._onOrionSelection.bind(this);
- this._onTextChanged = this._onTextChanged.bind(this);
- this._onOrionContextMenu = this._onOrionContextMenu.bind(this);
-
- this._eventTarget = {};
- this._eventListenersQueue = [];
- this.ui = new SourceEditorUI(this);
-}
-
-SourceEditor.prototype = {
- _view: null,
- _iframe: null,
- _model: null,
- _undoStack: null,
- _linesRuler: null,
- _annotationRuler: null,
- _overviewRuler: null,
- _styler: null,
- _annotationStyler: null,
- _annotationModel: null,
- _dragAndDrop: null,
- _currentLineAnnotation: null,
- _primarySelectionTimeout: null,
- _mode: null,
- _expandTab: null,
- _tabSize: null,
- _iframeWindow: null,
- _eventTarget: null,
- _eventListenersQueue: null,
- _contextMenu: null,
- _dirty: false,
-
- /**
- * The Source Editor user interface manager.
- * @type object
- * An instance of the SourceEditorUI.
- */
- ui: null,
-
- /**
- * The editor container element.
- * @type nsIDOMElement
- */
- parentElement: null,
-
- /**
- * Initialize the editor.
- *
- * @param nsIDOMElement aElement
- * The DOM element where you want the editor to show.
- * @param object aConfig
- * Editor configuration object. See SourceEditor.DEFAULTS for the
- * available configuration options.
- * @param function [aCallback]
- * Function you want to execute once the editor is loaded and
- * initialized.
- * @see SourceEditor.DEFAULTS
- */
- init: function SE_init(aElement, aConfig, aCallback)
- {
- if (this._iframe) {
- throw new Error("SourceEditor is already initialized!");
- }
-
- let doc = aElement.ownerDocument;
-
- this._iframe = doc.createElementNS(XUL_NS, "iframe");
- this._iframe.flex = 1;
-
- let onIframeLoad = (function() {
- this._iframe.removeEventListener("load", onIframeLoad, true);
- this._onIframeLoad();
- }).bind(this);
-
- this._iframe.addEventListener("load", onIframeLoad, true);
-
- this._iframe.setAttribute("src", ORION_IFRAME);
-
- aElement.appendChild(this._iframe);
- this.parentElement = aElement;
-
- this._config = {};
- for (let key in SourceEditor.DEFAULTS) {
- this._config[key] = key in aConfig ?
- aConfig[key] :
- SourceEditor.DEFAULTS[key];
- }
-
- // TODO: Bug 725677 - Remove the deprecated placeholderText option from the
- // Source Editor initialization.
- if (aConfig.placeholderText) {
- this._config.initialText = aConfig.placeholderText;
- Services.console.logStringMessage("SourceEditor.init() was called with the placeholderText option which is deprecated, please use initialText.");
- }
-
- this._onReadyCallback = aCallback;
- this.ui.init();
- },
-
- /**
- * The editor iframe load event handler.
- * @private
- */
- _onIframeLoad: function SE__onIframeLoad()
- {
- this._iframeWindow = this._iframe.contentWindow.wrappedJSObject;
- let window = this._iframeWindow;
- let config = this._config;
-
- Services.scriptloader.loadSubScript(ORION_SCRIPT, window, "utf8");
-
- let TextModel = window.require("orion/textview/textModel").TextModel;
- let TextView = window.require("orion/textview/textView").TextView;
-
- this._expandTab = config.expandTab;
- this._tabSize = config.tabSize;
-
- let theme = config.theme;
- let stylesheet = theme in ORION_THEMES ? ORION_THEMES[theme] : theme;
-
- this._model = new TextModel(config.initialText);
- this._view = new TextView({
- model: this._model,
- parent: "editor",
- stylesheet: stylesheet,
- tabSize: this._tabSize,
- expandTab: this._expandTab,
- readonly: config.readOnly,
- themeClass: "mozilla" + (config.readOnly ? " readonly" : ""),
- });
-
- let onOrionLoad = function() {
- this._view.removeEventListener("Load", onOrionLoad);
- this._onOrionLoad();
- }.bind(this);
-
- this._view.addEventListener("Load", onOrionLoad);
- if (config.highlightCurrentLine || Services.appinfo.OS == "Linux") {
- this.addEventListener(SourceEditor.EVENTS.SELECTION,
- this._onOrionSelection);
- }
- this.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
- this._onTextChanged);
-
- if (typeof config.contextMenu == "string") {
- let chromeDocument = this.parentElement.ownerDocument;
- this._contextMenu = chromeDocument.getElementById(config.contextMenu);
- } else if (typeof config.contextMenu == "object" ) {
- this._contextMenu = config._contextMenu;
- }
- if (this._contextMenu) {
- this.addEventListener(SourceEditor.EVENTS.CONTEXT_MENU,
- this._onOrionContextMenu);
- }
-
- let KeyBinding = window.require("orion/textview/keyBinding").KeyBinding;
- let TextDND = window.require("orion/textview/textDND").TextDND;
- let Rulers = window.require("orion/textview/rulers");
- let LineNumberRuler = Rulers.LineNumberRuler;
- let AnnotationRuler = Rulers.AnnotationRuler;
- let OverviewRuler = Rulers.OverviewRuler;
- let UndoStack = window.require("orion/textview/undoStack").UndoStack;
- let AnnotationModel = window.require("orion/textview/annotations").AnnotationModel;
-
- this._annotationModel = new AnnotationModel(this._model);
-
- if (config.showAnnotationRuler) {
- this._annotationRuler = new AnnotationRuler(this._annotationModel, "left",
- {styleClass: "ruler annotations"});
- this._annotationRuler.onClick = this._annotationRulerClick.bind(this);
- this._annotationRuler.addAnnotationType(ORION_ANNOTATION_TYPES.breakpoint);
- this._annotationRuler.addAnnotationType(ORION_ANNOTATION_TYPES.debugLocation);
- this._view.addRuler(this._annotationRuler);
- }
-
- if (config.showLineNumbers) {
- let rulerClass = this._annotationRuler ?
- "ruler lines linesWithAnnotations" :
- "ruler lines";
-
- this._linesRuler = new LineNumberRuler(this._annotationModel, "left",
- {styleClass: rulerClass}, {styleClass: "rulerLines odd"},
- {styleClass: "rulerLines even"});
-
- this._linesRuler.onClick = this._linesRulerClick.bind(this);
- this._linesRuler.onDblClick = this._linesRulerDblClick.bind(this);
- this._view.addRuler(this._linesRuler);
- }
-
- if (config.showOverviewRuler) {
- this._overviewRuler = new OverviewRuler(this._annotationModel, "right",
- {styleClass: "ruler overview"});
- this._overviewRuler.onClick = this._overviewRulerClick.bind(this);
-
- this._overviewRuler.addAnnotationType(ORION_ANNOTATION_TYPES.matchingBracket);
- this._overviewRuler.addAnnotationType(ORION_ANNOTATION_TYPES.currentBracket);
- this._overviewRuler.addAnnotationType(ORION_ANNOTATION_TYPES.breakpoint);
- this._overviewRuler.addAnnotationType(ORION_ANNOTATION_TYPES.debugLocation);
- this._overviewRuler.addAnnotationType(ORION_ANNOTATION_TYPES.task);
- this._view.addRuler(this._overviewRuler);
- }
-
- this.setMode(config.mode);
-
- this._undoStack = new UndoStack(this._view, config.undoLimit);
-
- this._dragAndDrop = new TextDND(this._view, this._undoStack);
-
- let actions = {
- "undo": [this.undo, this],
- "redo": [this.redo, this],
- "tab": [this._doTab, this],
- "Unindent Lines": [this._doUnindentLines, this],
- "enter": [this._doEnter, this],
- "Find...": [this.ui.find, this.ui],
- "Find Next Occurrence": [this.ui.findNext, this.ui],
- "Find Previous Occurrence": [this.ui.findPrevious, this.ui],
- "Goto Line...": [this.ui.gotoLine, this.ui],
- "Move Lines Down": [this._moveLines, this],
- "Comment/Uncomment": [this._doCommentUncomment, this],
- "Move to Bracket Opening": [this._moveToBracketOpening, this],
- "Move to Bracket Closing": [this._moveToBracketClosing, this],
- };
-
- for (let name in actions) {
- let action = actions[name];
- this._view.setAction(name, action[0].bind(action[1]));
- }
-
- this._view.setAction("Move Lines Up", this._moveLines.bind(this, true));
-
- let keys = (config.keys || []).concat(DEFAULT_KEYBINDINGS);
- keys.forEach(function(aKey) {
- // In Orion mod1 refers to Cmd on Macs and Ctrl on Windows and Linux.
- // So, if ctrl is in aKey we use it on Windows and Linux, otherwise
- // we use aKey.accel for mod1.
- let mod1 = Services.appinfo.OS != "Darwin" &&
- "ctrl" in aKey ? aKey.ctrl : aKey.accel;
- let binding = new KeyBinding(aKey.code, mod1, aKey.shift, aKey.alt,
- aKey.ctrl);
- this._view.setKeyBinding(binding, aKey.action);
-
- if (aKey.callback) {
- this._view.setAction(aKey.action, aKey.callback);
- }
- }, this);
-
- this._initEventTarget();
- },
-
- /**
- * Initialize the private Orion EventTarget object. This is used for tracking
- * our own event listeners for events outside of Orion's scope.
- * @private
- */
- _initEventTarget: function SE__initEventTarget()
- {
- let EventTarget =
- this._iframeWindow.require("orion/textview/eventTarget").EventTarget;
- EventTarget.addMixin(this._eventTarget);
-
- this._eventListenersQueue.forEach(function(aRequest) {
- if (aRequest[0] == "add") {
- this.addEventListener(aRequest[1], aRequest[2]);
- } else {
- this.removeEventListener(aRequest[1], aRequest[2]);
- }
- }, this);
-
- this._eventListenersQueue = [];
- },
-
- /**
- * Dispatch an event to the SourceEditor event listeners. This covers only the
- * SourceEditor-specific events.
- *
- * @private
- * @param object aEvent
- * The event object to dispatch to all listeners.
- */
- _dispatchEvent: function SE__dispatchEvent(aEvent)
- {
- this._eventTarget.dispatchEvent(aEvent);
- },
-
- /**
- * The Orion "Load" event handler. This is called when the Orion editor
- * completes the initialization.
- * @private
- */
- _onOrionLoad: function SE__onOrionLoad()
- {
- this.ui.onReady();
- if (this._onReadyCallback) {
- this._onReadyCallback(this);
- this._onReadyCallback = null;
- }
- },
-
- /**
- * The "tab" editor action implementation. This adds support for expanded tabs
- * to spaces, and support for the indentation of multiple lines at once.
- * @private
- */
- _doTab: function SE__doTab()
- {
- if (this.readOnly) {
- return false;
- }
-
- let indent = "\t";
- let selection = this.getSelection();
- let model = this._model;
- let firstLine = model.getLineAtOffset(selection.start);
- let firstLineStart = this.getLineStart(firstLine);
- let lastLineOffset = selection.end > selection.start ?
- selection.end - 1 : selection.end;
- let lastLine = model.getLineAtOffset(lastLineOffset);
-
- if (this._expandTab) {
- let offsetFromLineStart = firstLine == lastLine ?
- selection.start - firstLineStart : 0;
- let spaces = this._tabSize - (offsetFromLineStart % this._tabSize);
- indent = (new Array(spaces + 1)).join(" ");
- }
-
- // Do selection indentation.
- if (firstLine != lastLine) {
- let lines = [""];
- let lastLineEnd = this.getLineEnd(lastLine, true);
- let selectedLines = lastLine - firstLine + 1;
-
- for (let i = firstLine; i <= lastLine; i++) {
- lines.push(model.getLine(i, true));
- }
-
- this.startCompoundChange();
-
- this.setText(lines.join(indent), firstLineStart, lastLineEnd);
-
- let newSelectionStart = firstLineStart == selection.start ?
- selection.start : selection.start + indent.length;
- let newSelectionEnd = selection.end + (selectedLines * indent.length);
-
- this._view.setSelection(newSelectionStart, newSelectionEnd);
-
- this.endCompoundChange();
- return true;
- }
-
- return false;
- },
-
- /**
- * The "Unindent lines" editor action implementation. This method is invoked
- * when the user presses Shift-Tab.
- * @private
- */
- _doUnindentLines: function SE__doUnindentLines()
- {
- if (this.readOnly) {
- return true;
- }
-
- let indent = "\t";
-
- let selection = this.getSelection();
- let model = this._model;
- let firstLine = model.getLineAtOffset(selection.start);
- let lastLineOffset = selection.end > selection.start ?
- selection.end - 1 : selection.end;
- let lastLine = model.getLineAtOffset(lastLineOffset);
-
- if (this._expandTab) {
- indent = (new Array(this._tabSize + 1)).join(" ");
- }
-
- let lines = [];
- for (let line, i = firstLine; i <= lastLine; i++) {
- line = model.getLine(i, true);
- if (line.indexOf(indent) != 0) {
- return true;
- }
- lines.push(line.substring(indent.length));
- }
-
- let firstLineStart = this.getLineStart(firstLine);
- let lastLineStart = this.getLineStart(lastLine);
- let lastLineEnd = this.getLineEnd(lastLine, true);
-
- this.startCompoundChange();
-
- this.setText(lines.join(""), firstLineStart, lastLineEnd);
-
- let selectedLines = lastLine - firstLine + 1;
- let newSelectionStart = firstLineStart == selection.start ?
- selection.start :
- Math.max(firstLineStart,
- selection.start - indent.length);
- let newSelectionEnd = selection.end - (selectedLines * indent.length) +
- (selection.end == lastLineStart + 1 ? 1 : 0);
- if (firstLine == lastLine) {
- newSelectionEnd = Math.max(lastLineStart, newSelectionEnd);
- }
- this._view.setSelection(newSelectionStart, newSelectionEnd);
-
- this.endCompoundChange();
-
- return true;
- },
-
- /**
- * The editor Enter action implementation, which adds simple automatic
- * indentation based on the previous line when the user presses the Enter key.
- * @private
- */
- _doEnter: function SE__doEnter()
- {
- if (this.readOnly) {
- return false;
- }
-
- let selection = this.getSelection();
- if (selection.start != selection.end) {
- return false;
- }
-
- let model = this._model;
- let lineIndex = model.getLineAtOffset(selection.start);
- let lineText = model.getLine(lineIndex, true);
- let lineStart = this.getLineStart(lineIndex);
- let index = 0;
- let lineOffset = selection.start - lineStart;
- while (index < lineOffset && /[ \t]/.test(lineText.charAt(index))) {
- index++;
- }
-
- if (!index) {
- return false;
- }
-
- let prefix = lineText.substring(0, index);
- index = lineOffset;
- while (index < lineText.length &&
- /[ \t]/.test(lineText.charAt(index++))) {
- selection.end++;
- }
-
- this.setText(this.getLineDelimiter() + prefix, selection.start,
- selection.end);
- return true;
- },
-
- /**
- * Move lines upwards or downwards, relative to the current caret location.
- *
- * @private
- * @param boolean aLineAbove
- * True if moving lines up, false to move lines down.
- */
- _moveLines: function SE__moveLines(aLineAbove)
- {
- if (this.readOnly) {
- return false;
- }
-
- let model = this._model;
- let selection = this.getSelection();
- let firstLine = model.getLineAtOffset(selection.start);
- if (firstLine == 0 && aLineAbove) {
- return true;
- }
-
- let lastLine = model.getLineAtOffset(selection.end);
- let firstLineStart = this.getLineStart(firstLine);
- let lastLineStart = this.getLineStart(lastLine);
- if (selection.start != selection.end && lastLineStart == selection.end) {
- lastLine--;
- }
- if (!aLineAbove && (lastLine + 1) == this.getLineCount()) {
- return true;
- }
-
- let lastLineEnd = this.getLineEnd(lastLine, true);
- let text = this.getText(firstLineStart, lastLineEnd);
-
- if (aLineAbove) {
- let aboveLine = firstLine - 1;
- let aboveLineStart = this.getLineStart(aboveLine);
-
- this.startCompoundChange();
- if (lastLine == (this.getLineCount() - 1)) {
- let delimiterStart = this.getLineEnd(aboveLine);
- let delimiterEnd = this.getLineEnd(aboveLine, true);
- let lineDelimiter = this.getText(delimiterStart, delimiterEnd);
- text += lineDelimiter;
- this.setText("", firstLineStart - lineDelimiter.length, lastLineEnd);
- } else {
- this.setText("", firstLineStart, lastLineEnd);
- }
- this.setText(text, aboveLineStart, aboveLineStart);
- this.endCompoundChange();
- this.setSelection(aboveLineStart, aboveLineStart + text.length);
- } else {
- let belowLine = lastLine + 1;
- let belowLineEnd = this.getLineEnd(belowLine, true);
-
- let insertAt = belowLineEnd - lastLineEnd + firstLineStart;
- let lineDelimiter = "";
- if (belowLine == this.getLineCount() - 1) {
- let delimiterStart = this.getLineEnd(lastLine);
- lineDelimiter = this.getText(delimiterStart, lastLineEnd);
- text = lineDelimiter + text.substr(0, text.length -
- lineDelimiter.length);
- }
- this.startCompoundChange();
- this.setText("", firstLineStart, lastLineEnd);
- this.setText(text, insertAt, insertAt);
- this.endCompoundChange();
- this.setSelection(insertAt + lineDelimiter.length,
- insertAt + text.length);
- }
- return true;
- },
-
- /**
- * The Orion Selection event handler. The current caret line is
- * highlighted and for Linux users the selected text is copied into the X11
- * PRIMARY buffer.
- *
- * @private
- * @param object aEvent
- * The Orion Selection event object.
- */
- _onOrionSelection: function SE__onOrionSelection(aEvent)
- {
- if (this._config.highlightCurrentLine) {
- this._highlightCurrentLine(aEvent);
- }
-
- if (Services.appinfo.OS == "Linux") {
- let window = this.parentElement.ownerDocument.defaultView;
-
- if (this._primarySelectionTimeout) {
- window.clearTimeout(this._primarySelectionTimeout);
- }
- this._primarySelectionTimeout =
- window.setTimeout(this._updatePrimarySelection.bind(this),
- PRIMARY_SELECTION_DELAY);
- }
- },
-
- /**
- * The TextChanged event handler which tracks the dirty state of the editor.
- *
- * @see SourceEditor.EVENTS.TEXT_CHANGED
- * @see SourceEditor.EVENTS.DIRTY_CHANGED
- * @see SourceEditor.dirty
- * @private
- */
- _onTextChanged: function SE__onTextChanged()
- {
- this._updateDirty();
- },
-
- /**
- * The Orion contextmenu event handler. This method opens the default or
- * the custom context menu popup at the pointer location.
- *
- * @param object aEvent
- * The contextmenu event object coming from Orion. This object should
- * hold the screenX and screenY properties.
- */
- _onOrionContextMenu: function SE__onOrionContextMenu(aEvent)
- {
- if (this._contextMenu.state == "closed") {
- this._contextMenu.openPopupAtScreen(aEvent.screenX || 0,
- aEvent.screenY || 0, true);
- }
- },
-
- /**
- * Update the dirty state of the editor based on the undo stack.
- * @private
- */
- _updateDirty: function SE__updateDirty()
- {
- this.dirty = !this._undoStack.isClean();
- },
-
- /**
- * Update the X11 PRIMARY buffer to hold the current selection.
- * @private
- */
- _updatePrimarySelection: function SE__updatePrimarySelection()
- {
- this._primarySelectionTimeout = null;
-
- let text = this.getSelectedText();
- if (!text) {
- return;
- }
-
- clipboardHelper.copyStringToClipboard(text,
- Ci.nsIClipboard.kSelectionClipboard,
- this.parentElement.ownerDocument);
- },
-
- /**
- * Highlight the current line using the Orion annotation model.
- *
- * @private
- * @param object aEvent
- * The Selection event object.
- */
- _highlightCurrentLine: function SE__highlightCurrentLine(aEvent)
- {
- let annotationModel = this._annotationModel;
- let model = this._model;
- let oldAnnotation = this._currentLineAnnotation;
- let newSelection = aEvent.newValue;
-
- let collapsed = newSelection.start == newSelection.end;
- if (!collapsed) {
- if (oldAnnotation) {
- annotationModel.removeAnnotation(oldAnnotation);
- this._currentLineAnnotation = null;
- }
- return;
- }
-
- let line = model.getLineAtOffset(newSelection.start);
- let lineStart = this.getLineStart(line);
- let lineEnd = this.getLineEnd(line);
-
- let title = oldAnnotation ? oldAnnotation.title :
- SourceEditorUI.strings.GetStringFromName("annotation.currentLine");
-
- this._currentLineAnnotation = {
- start: lineStart,
- end: lineEnd,
- type: ORION_ANNOTATION_TYPES.currentLine,
- title: title,
- html: "",
- overviewStyle: {styleClass: "annotationOverview currentLine"},
- lineStyle: {styleClass: "annotationLine currentLine"},
- };
-
- annotationModel.replaceAnnotations(oldAnnotation ? [oldAnnotation] : null,
- [this._currentLineAnnotation]);
- },
-
- /**
- * The click event handler for the lines gutter. This function allows the user
- * to jump to a line or to perform line selection while holding the Shift key
- * down.
- *
- * @private
- * @param number aLineIndex
- * The line index where the click event occurred.
- * @param object aEvent
- * The DOM click event object.
- */
- _linesRulerClick: function SE__linesRulerClick(aLineIndex, aEvent)
- {
- if (aLineIndex === undefined || aLineIndex == -1) {
- return;
- }
-
- if (aEvent.shiftKey) {
- let model = this._model;
- let selection = this.getSelection();
- let selectionLineStart = model.getLineAtOffset(selection.start);
- let selectionLineEnd = model.getLineAtOffset(selection.end);
- let newStart = aLineIndex <= selectionLineStart ?
- this.getLineStart(aLineIndex) : selection.start;
- let newEnd = aLineIndex <= selectionLineStart ?
- selection.end : this.getLineEnd(aLineIndex);
- this.setSelection(newStart, newEnd);
- } else {
- if (this._annotationRuler) {
- this._annotationRulerClick(aLineIndex, aEvent);
- } else {
- this.setCaretPosition(aLineIndex);
- }
- }
- },
-
- /**
- * The dblclick event handler for the lines gutter. This function selects the
- * whole line where the event occurred.
- *
- * @private
- * @param number aLineIndex
- * The line index where the double click event occurred.
- * @param object aEvent
- * The DOM dblclick event object.
- */
- _linesRulerDblClick: function SE__linesRulerDblClick(aLineIndex)
- {
- if (aLineIndex === undefined) {
- return;
- }
-
- let newStart = this.getLineStart(aLineIndex);
- let newEnd = this.getLineEnd(aLineIndex);
- this.setSelection(newStart, newEnd);
- },
-
- /**
- * Highlight the Orion annotations. This updates the annotation styler as
- * needed.
- * @private
- */
- _highlightAnnotations: function SE__highlightAnnotations()
- {
- if (this._annotationStyler) {
- this._annotationStyler.destroy();
- this._annotationStyler = null;
- }
-
- let AnnotationStyler =
- this._iframeWindow.require("orion/textview/annotations").AnnotationStyler;
-
- let styler = new AnnotationStyler(this._view, this._annotationModel);
- this._annotationStyler = styler;
-
- styler.addAnnotationType(ORION_ANNOTATION_TYPES.matchingBracket);
- styler.addAnnotationType(ORION_ANNOTATION_TYPES.currentBracket);
- styler.addAnnotationType(ORION_ANNOTATION_TYPES.task);
- styler.addAnnotationType(ORION_ANNOTATION_TYPES.debugLocation);
-
- if (this._config.highlightCurrentLine) {
- styler.addAnnotationType(ORION_ANNOTATION_TYPES.currentLine);
- }
- },
-
- /**
- * Retrieve the list of Orion Annotations filtered by type for the given text range.
- *
- * @private
- * @param string aType
- * The annotation type to filter annotations for. Use one of the keys
- * in ORION_ANNOTATION_TYPES.
- * @param number aStart
- * Offset from where to start finding the annotations.
- * @param number aEnd
- * End offset for retrieving the annotations.
- * @return array
- * The array of annotations, filtered by type, within the given text
- * range.
- */
- _getAnnotationsByType: function SE__getAnnotationsByType(aType, aStart, aEnd)
- {
- let annotations = this._annotationModel.getAnnotations(aStart, aEnd);
- let annotation, result = [];
- while (annotation = annotations.next()) {
- if (annotation.type == ORION_ANNOTATION_TYPES[aType]) {
- result.push(annotation);
- }
- }
-
- return result;
- },
-
- /**
- * The click event handler for the annotation ruler.
- *
- * @private
- * @param number aLineIndex
- * The line index where the click event occurred.
- * @param object aEvent
- * The DOM click event object.
- */
- _annotationRulerClick: function SE__annotationRulerClick(aLineIndex, aEvent)
- {
- if (aLineIndex === undefined || aLineIndex == -1) {
- return;
- }
-
- let lineStart = this.getLineStart(aLineIndex);
- let lineEnd = this.getLineEnd(aLineIndex);
- let annotations = this._getAnnotationsByType("breakpoint", lineStart, lineEnd);
- if (annotations.length > 0) {
- this.removeBreakpoint(aLineIndex);
- } else {
- this.addBreakpoint(aLineIndex);
- }
- },
-
- /**
- * The click event handler for the overview ruler. When the user clicks on an
- * annotation the editor jumps to the associated line.
- *
- * @private
- * @param number aLineIndex
- * The line index where the click event occurred.
- * @param object aEvent
- * The DOM click event object.
- */
- _overviewRulerClick: function SE__overviewRulerClick(aLineIndex, aEvent)
- {
- if (aLineIndex === undefined || aLineIndex == -1) {
- return;
- }
-
- let model = this._model;
- let lineStart = this.getLineStart(aLineIndex);
- let lineEnd = this.getLineEnd(aLineIndex);
- let annotations = this._annotationModel.getAnnotations(lineStart, lineEnd);
- let annotation = annotations.next();
-
- // Jump to the line where annotation is. If the annotation is specific to
- // a substring part of the line, then select the substring.
- if (!annotation || lineStart == annotation.start && lineEnd == annotation.end) {
- this.setSelection(lineStart, lineStart);
- } else {
- this.setSelection(annotation.start, annotation.end);
- }
- },
-
- /**
- * Get the editor element.
- *
- * @return nsIDOMElement
- * In this implementation a xul:iframe holds the editor.
- */
- get editorElement() {
- return this._iframe;
- },
-
- /**
- * Helper function to retrieve the strings used for comments in the current
- * editor mode.
- *
- * @private
- * @return object
- * An object that holds the following properties:
- * - line: the comment string used for the start of a single line
- * comment.
- * - blockStart: the comment string used for the start of a comment
- * block.
- * - blockEnd: the comment string used for the end of a block comment.
- * Null is returned for unsupported editor modes.
- */
- _getCommentStrings: function SE__getCommentStrings()
- {
- let line = "";
- let blockCommentStart = "";
- let blockCommentEnd = "";
-
- switch (this.getMode()) {
- case SourceEditor.MODES.JAVASCRIPT:
- line = "//";
- blockCommentStart = "/*";
- blockCommentEnd = "*/";
- break;
- case SourceEditor.MODES.CSS:
- blockCommentStart = "/*";
- blockCommentEnd = "*/";
- break;
- case SourceEditor.MODES.HTML:
- case SourceEditor.MODES.XML:
- blockCommentStart = "";
- break;
- default:
- return null;
- }
- return {line: line, blockStart: blockCommentStart, blockEnd: blockCommentEnd};
- },
-
- /**
- * Decide whether to comment the selection/current line or to uncomment it.
- *
- * @private
- */
- _doCommentUncomment: function SE__doCommentUncomment()
- {
- if (this.readOnly) {
- return false;
- }
-
- let commentObject = this._getCommentStrings();
- if (!commentObject) {
- return false;
- }
-
- let selection = this.getSelection();
- let model = this._model;
- let firstLine = model.getLineAtOffset(selection.start);
- let lastLine = model.getLineAtOffset(selection.end);
-
- // Checks for block comment.
- let firstLineText = model.getLine(firstLine);
- let lastLineText = model.getLine(lastLine);
- let openIndex = firstLineText.indexOf(commentObject.blockStart);
- let closeIndex = lastLineText.lastIndexOf(commentObject.blockEnd);
- if (openIndex != -1 && closeIndex != -1 &&
- (firstLine != lastLine ||
- (closeIndex - openIndex) >= commentObject.blockStart.length)) {
- return this._doUncomment();
- }
-
- if (!commentObject.line) {
- return this._doComment();
- }
-
- // If the selection is not a block comment, check for the first and the last
- // lines to be line commented.
- let firstLastCommented = [firstLineText,
- lastLineText].every(function(aLineText) {
- let openIndex = aLineText.indexOf(commentObject.line);
- if (openIndex != -1) {
- let textUntilComment = aLineText.slice(0, openIndex);
- if (!textUntilComment || /^\s+$/.test(textUntilComment)) {
- return true;
- }
- }
- return false;
- });
- if (firstLastCommented) {
- return this._doUncomment();
- }
-
- // If we reach here, then we have to comment the selection/line.
- return this._doComment();
- },
-
- /**
- * Wrap the selected text in comments. If nothing is selected the current
- * caret line is commented out. Single line and block comments depend on the
- * current editor mode.
- *
- * @private
- */
- _doComment: function SE__doComment()
- {
- if (this.readOnly) {
- return false;
- }
-
- let commentObject = this._getCommentStrings();
- if (!commentObject) {
- return false;
- }
-
- let selection = this.getSelection();
-
- if (selection.start == selection.end) {
- let selectionLine = this._model.getLineAtOffset(selection.start);
- let lineStartOffset = this.getLineStart(selectionLine);
- if (commentObject.line) {
- this.setText(commentObject.line, lineStartOffset, lineStartOffset);
- } else {
- let lineEndOffset = this.getLineEnd(selectionLine);
- this.startCompoundChange();
- this.setText(commentObject.blockStart, lineStartOffset, lineStartOffset);
- this.setText(commentObject.blockEnd,
- lineEndOffset + commentObject.blockStart.length,
- lineEndOffset + commentObject.blockStart.length);
- this.endCompoundChange();
- }
- } else {
- this.startCompoundChange();
- this.setText(commentObject.blockStart, selection.start, selection.start);
- this.setText(commentObject.blockEnd,
- selection.end + commentObject.blockStart.length,
- selection.end + commentObject.blockStart.length);
- this.endCompoundChange();
- }
-
- return true;
- },
-
- /**
- * Uncomment the selected text. If nothing is selected the current caret line
- * is umcommented. Single line and block comments depend on the current editor
- * mode.
- *
- * @private
- */
- _doUncomment: function SE__doUncomment()
- {
- if (this.readOnly) {
- return false;
- }
-
- let commentObject = this._getCommentStrings();
- if (!commentObject) {
- return false;
- }
-
- let selection = this.getSelection();
- let firstLine = this._model.getLineAtOffset(selection.start);
- let lastLine = this._model.getLineAtOffset(selection.end);
-
- // Uncomment a block of text.
- let firstLineText = this._model.getLine(firstLine);
- let lastLineText = this._model.getLine(lastLine);
- let openIndex = firstLineText.indexOf(commentObject.blockStart);
- let closeIndex = lastLineText.lastIndexOf(commentObject.blockEnd);
- if (openIndex != -1 && closeIndex != -1 &&
- (firstLine != lastLine ||
- (closeIndex - openIndex) >= commentObject.blockStart.length)) {
- let firstLineStartOffset = this.getLineStart(firstLine);
- let lastLineStartOffset = this.getLineStart(lastLine);
- let openOffset = firstLineStartOffset + openIndex;
- let closeOffset = lastLineStartOffset + closeIndex;
-
- this.startCompoundChange();
- this.setText("", closeOffset, closeOffset + commentObject.blockEnd.length);
- this.setText("", openOffset, openOffset + commentObject.blockStart.length);
- this.endCompoundChange();
-
- return true;
- }
-
- if (!commentObject.line) {
- return true;
- }
-
- // If the selected text is not a block of comment, then uncomment each line.
- this.startCompoundChange();
- let lineCaret = firstLine;
- while (lineCaret <= lastLine) {
- let currentLine = this._model.getLine(lineCaret);
- let lineStart = this.getLineStart(lineCaret);
- let openIndex = currentLine.indexOf(commentObject.line);
- let openOffset = lineStart + openIndex;
- let textUntilComment = this.getText(lineStart, openOffset);
- if (openIndex != -1 &&
- (!textUntilComment || /^\s+$/.test(textUntilComment))) {
- this.setText("", openOffset, openOffset + commentObject.line.length);
- }
- lineCaret++;
- }
- this.endCompoundChange();
-
- return true;
- },
-
- /**
- * Helper function for _moveToBracket{Opening/Closing} to find the offset of
- * matching bracket.
- *
- * @param number aOffset
- * The offset of the bracket for which you want to find the bracket.
- * @private
- */
- _getMatchingBracketIndex: function SE__getMatchingBracketIndex(aOffset)
- {
- return this._styler._findMatchingBracket(this._model, aOffset);
- },
-
- /**
- * Move the cursor to the matching opening bracket if at corresponding closing
- * bracket, otherwise move to the opening bracket for the current block of code.
- *
- * @private
- */
- _moveToBracketOpening: function SE__moveToBracketOpening()
- {
- let mode = this.getMode();
- // Returning early if not in JavaScipt or CSS mode.
- if (mode != SourceEditor.MODES.JAVASCRIPT &&
- mode != SourceEditor.MODES.CSS) {
- return false;
- }
-
- let caretOffset = this.getCaretOffset() - 1;
- let matchingIndex = this._getMatchingBracketIndex(caretOffset);
-
- // If the caret is not at the closing bracket "}", find the index of the
- // opening bracket "{" for the current code block.
- if (matchingIndex == -1 || matchingIndex > caretOffset) {
- matchingIndex = -1;
- let text = this.getText();
- let closingOffset = text.indexOf("}", caretOffset);
- while (closingOffset > -1) {
- let closingMatchingIndex = this._getMatchingBracketIndex(closingOffset);
- if (closingMatchingIndex < caretOffset && closingMatchingIndex != -1) {
- matchingIndex = closingMatchingIndex;
- break;
- }
- closingOffset = text.indexOf("}", closingOffset + 1);
- }
- // Moving to the previous code block starting bracket if caret not inside
- // any code block.
- if (matchingIndex == -1) {
- let lastClosingOffset = text.lastIndexOf("}", caretOffset);
- while (lastClosingOffset > -1) {
- let closingMatchingIndex =
- this._getMatchingBracketIndex(lastClosingOffset);
- if (closingMatchingIndex < caretOffset &&
- closingMatchingIndex != -1) {
- matchingIndex = closingMatchingIndex;
- break;
- }
- lastClosingOffset = text.lastIndexOf("}", lastClosingOffset - 1);
- }
- }
- }
-
- if (matchingIndex > -1) {
- this.setCaretOffset(matchingIndex + 1);
- }
-
- return true;
- },
-
- /**
- * Moves the cursor to the matching closing bracket if at corresponding
- * opening bracket, otherwise move to the closing bracket for the current
- * block of code.
- *
- * @private
- */
- _moveToBracketClosing: function SE__moveToBracketClosing()
- {
- let mode = this.getMode();
- // Returning early if not in JavaScipt or CSS mode.
- if (mode != SourceEditor.MODES.JAVASCRIPT &&
- mode != SourceEditor.MODES.CSS) {
- return false;
- }
-
- let caretOffset = this.getCaretOffset();
- let matchingIndex = this._getMatchingBracketIndex(caretOffset - 1);
-
- // If the caret is not at the opening bracket "{", find the index of the
- // closing bracket "}" for the current code block.
- if (matchingIndex == -1 || matchingIndex < caretOffset) {
- matchingIndex = -1;
- let text = this.getText();
- let openingOffset = text.lastIndexOf("{", caretOffset);
- while (openingOffset > -1) {
- let openingMatchingIndex = this._getMatchingBracketIndex(openingOffset);
- if (openingMatchingIndex > caretOffset) {
- matchingIndex = openingMatchingIndex;
- break;
- }
- openingOffset = text.lastIndexOf("{", openingOffset - 1);
- }
- // Moving to the next code block ending bracket if caret not inside
- // any code block.
- if (matchingIndex == -1) {
- let nextOpeningIndex = text.indexOf("{", caretOffset + 1);
- while (nextOpeningIndex > -1) {
- let openingMatchingIndex =
- this._getMatchingBracketIndex(nextOpeningIndex);
- if (openingMatchingIndex > caretOffset) {
- matchingIndex = openingMatchingIndex;
- break;
- }
- nextOpeningIndex = text.indexOf("{", nextOpeningIndex + 1);
- }
- }
- }
-
- if (matchingIndex > -1) {
- this.setCaretOffset(matchingIndex);
- }
-
- return true;
- },
-
- /**
- * Add an event listener to the editor. You can use one of the known events.
- *
- * @see SourceEditor.EVENTS
- *
- * @param string aEventType
- * The event type you want to listen for.
- * @param function aCallback
- * The function you want executed when the event is triggered.
- */
- addEventListener: function SE_addEventListener(aEventType, aCallback)
- {
- if (this._view && aEventType in ORION_EVENTS) {
- this._view.addEventListener(ORION_EVENTS[aEventType], aCallback);
- } else if (this._eventTarget.addEventListener) {
- this._eventTarget.addEventListener(aEventType, aCallback);
- } else {
- this._eventListenersQueue.push(["add", aEventType, aCallback]);
- }
- },
-
- /**
- * Remove an event listener from the editor. You can use one of the known
- * events.
- *
- * @see SourceEditor.EVENTS
- *
- * @param string aEventType
- * The event type you have a listener for.
- * @param function aCallback
- * The function you have as the event handler.
- */
- removeEventListener: function SE_removeEventListener(aEventType, aCallback)
- {
- if (this._view && aEventType in ORION_EVENTS) {
- this._view.removeEventListener(ORION_EVENTS[aEventType], aCallback);
- } else if (this._eventTarget.removeEventListener) {
- this._eventTarget.removeEventListener(aEventType, aCallback);
- } else {
- this._eventListenersQueue.push(["remove", aEventType, aCallback]);
- }
- },
-
- /**
- * Undo a change in the editor.
- *
- * @return boolean
- * True if there was a change undone, false otherwise.
- */
- undo: function SE_undo()
- {
- let result = this._undoStack.undo();
- this.ui._onUndoRedo();
- return result;
- },
-
- /**
- * Redo a change in the editor.
- *
- * @return boolean
- * True if there was a change redone, false otherwise.
- */
- redo: function SE_redo()
- {
- let result = this._undoStack.redo();
- this.ui._onUndoRedo();
- return result;
- },
-
- /**
- * Check if there are changes that can be undone.
- *
- * @return boolean
- * True if there are changes that can be undone, false otherwise.
- */
- canUndo: function SE_canUndo()
- {
- return this._undoStack.canUndo();
- },
-
- /**
- * Check if there are changes that can be repeated.
- *
- * @return boolean
- * True if there are changes that can be repeated, false otherwise.
- */
- canRedo: function SE_canRedo()
- {
- return this._undoStack.canRedo();
- },
-
- /**
- * Reset the Undo stack.
- */
- resetUndo: function SE_resetUndo()
- {
- this._undoStack.reset();
- this._updateDirty();
- this.ui._onUndoRedo();
- },
-
- /**
- * Set the "dirty" state of the editor. Set this to false when you save the
- * text being edited. The dirty state will become true once the user makes
- * changes to the text.
- *
- * @param boolean aNewValue
- * The new dirty state: true if the text is not saved, false if you
- * just saved the text.
- */
- set dirty(aNewValue)
- {
- if (aNewValue == this._dirty) {
- return;
- }
-
- let event = {
- type: SourceEditor.EVENTS.DIRTY_CHANGED,
- oldValue: this._dirty,
- newValue: aNewValue,
- };
-
- this._dirty = aNewValue;
- if (!this._dirty && !this._undoStack.isClean()) {
- this._undoStack.markClean();
- }
- this._dispatchEvent(event);
- },
-
- /**
- * Get the editor "dirty" state. This tells if the text is considered saved or
- * not.
- *
- * @see SourceEditor.EVENTS.DIRTY_CHANGED
- * @return boolean
- * True if there are changes which are not saved, false otherwise.
- */
- get dirty()
- {
- return this._dirty;
- },
-
- /**
- * Start a compound change in the editor. Compound changes are grouped into
- * only one change that you can undo later, after you invoke
- * endCompoundChange().
- */
- startCompoundChange: function SE_startCompoundChange()
- {
- this._undoStack.startCompoundChange();
- },
-
- /**
- * End a compound change in the editor.
- */
- endCompoundChange: function SE_endCompoundChange()
- {
- this._undoStack.endCompoundChange();
- },
-
- /**
- * Focus the editor.
- */
- focus: function SE_focus()
- {
- this._view.focus();
- },
-
- /**
- * Get the first visible line number.
- *
- * @return number
- * The line number, counting from 0.
- */
- getTopIndex: function SE_getTopIndex()
- {
- return this._view.getTopIndex();
- },
-
- /**
- * Set the first visible line number.
- *
- * @param number aTopIndex
- * The line number, counting from 0.
- */
- setTopIndex: function SE_setTopIndex(aTopIndex)
- {
- this._view.setTopIndex(aTopIndex);
- },
-
- /**
- * Check if the editor has focus.
- *
- * @return boolean
- * True if the editor is focused, false otherwise.
- */
- hasFocus: function SE_hasFocus()
- {
- return this._view.hasFocus();
- },
-
- /**
- * Get the editor content, in the given range. If no range is given you get
- * the entire editor content.
- *
- * @param number [aStart=0]
- * Optional, start from the given offset.
- * @param number [aEnd=content char count]
- * Optional, end offset for the text you want. If this parameter is not
- * given, then the text returned goes until the end of the editor
- * content.
- * @return string
- * The text in the given range.
- */
- getText: function SE_getText(aStart, aEnd)
- {
- return this._view.getText(aStart, aEnd);
- },
-
- /**
- * Get the start character offset of the line with index aLineIndex.
- *
- * @param number aLineIndex
- * Zero based index of the line.
- * @return number
- * Line start offset or -1 if out of range.
- */
- getLineStart: function SE_getLineStart(aLineIndex)
- {
- return this._model.getLineStart(aLineIndex);
- },
-
- /**
- * Get the end character offset of the line with index aLineIndex,
- * excluding the end offset. When the line delimiter is present,
- * the offset is the start offset of the next line or the char count.
- * Otherwise, it is the offset of the line delimiter.
- *
- * @param number aLineIndex
- * Zero based index of the line.
- * @param boolean [aIncludeDelimiter = false]
- * Optional, whether or not to include the line delimiter.
- * @return number
- * Line end offset or -1 if out of range.
- */
- getLineEnd: function SE_getLineEnd(aLineIndex, aIncludeDelimiter)
- {
- return this._model.getLineEnd(aLineIndex, aIncludeDelimiter);
- },
-
- /**
- * Get the number of characters in the editor content.
- *
- * @return number
- * The number of editor content characters.
- */
- getCharCount: function SE_getCharCount()
- {
- return this._model.getCharCount();
- },
-
- /**
- * Get the selected text.
- *
- * @return string
- * The currently selected text.
- */
- getSelectedText: function SE_getSelectedText()
- {
- let selection = this.getSelection();
- return this.getText(selection.start, selection.end);
- },
-
- /**
- * Replace text in the source editor with the given text, in the given range.
- *
- * @param string aText
- * The text you want to put into the editor.
- * @param number [aStart=0]
- * Optional, the start offset, zero based, from where you want to start
- * replacing text in the editor.
- * @param number [aEnd=char count]
- * Optional, the end offset, zero based, where you want to stop
- * replacing text in the editor.
- */
- setText: function SE_setText(aText, aStart, aEnd)
- {
- this._view.setText(aText, aStart, aEnd);
- },
-
- /**
- * Drop the current selection / deselect.
- */
- dropSelection: function SE_dropSelection()
- {
- this.setCaretOffset(this.getCaretOffset());
- },
-
- /**
- * Select a specific range in the editor.
- *
- * @param number aStart
- * Selection range start.
- * @param number aEnd
- * Selection range end.
- */
- setSelection: function SE_setSelection(aStart, aEnd)
- {
- this._view.setSelection(aStart, aEnd, true);
- },
-
- /**
- * Get the current selection range.
- *
- * @return object
- * An object with two properties, start and end, that give the
- * selection range (zero based offsets).
- */
- getSelection: function SE_getSelection()
- {
- return this._view.getSelection();
- },
-
- /**
- * Get the current caret offset.
- *
- * @return number
- * The current caret offset.
- */
- getCaretOffset: function SE_getCaretOffset()
- {
- return this._view.getCaretOffset();
- },
-
- /**
- * Set the caret offset.
- *
- * @param number aOffset
- * The new caret offset you want to set.
- */
- setCaretOffset: function SE_setCaretOffset(aOffset)
- {
- this._view.setCaretOffset(aOffset, true);
- },
-
- /**
- * Get the caret position.
- *
- * @return object
- * An object that holds two properties:
- * - line: the line number, counting from 0.
- * - col: the column number, counting from 0.
- */
- getCaretPosition: function SE_getCaretPosition()
- {
- let offset = this.getCaretOffset();
- let line = this._model.getLineAtOffset(offset);
- let lineStart = this.getLineStart(line);
- let column = offset - lineStart;
- return {line: line, col: column};
- },
-
- /**
- * Set the caret position: line and column.
- *
- * @param number aLine
- * The new caret line location. Line numbers start from 0.
- * @param number [aColumn=0]
- * Optional. The new caret column location. Columns start from 0.
- * @param number [aAlign=0]
- * Optional. Position of the line with respect to viewport.
- * Allowed values are:
- * SourceEditor.VERTICAL_ALIGN.TOP target line at top of view.
- * SourceEditor.VERTICAL_ALIGN.CENTER target line at center of view.
- * SourceEditor.VERTICAL_ALIGN.BOTTOM target line at bottom of view.
- */
- setCaretPosition: function SE_setCaretPosition(aLine, aColumn, aAlign)
- {
- let editorHeight = this._view.getClientArea().height;
- let lineHeight = this._view.getLineHeight();
- let linesVisible = Math.floor(editorHeight/lineHeight);
- let halfVisible = Math.round(linesVisible/2);
- let firstVisible = this.getTopIndex();
- let lastVisible = this._view.getBottomIndex();
- let caretOffset = this.getLineStart(aLine) + (aColumn || 0);
-
- this._view.setSelection(caretOffset, caretOffset, false);
-
- // If the target line is in view, skip the vertical alignment part.
- if (aLine <= lastVisible && aLine >= firstVisible) {
- this._view.showSelection();
- return;
- }
-
- // Setting the offset so that the line always falls in the upper half
- // of visible lines (lower half for BOTTOM aligned).
- // VERTICAL_OFFSET is the maximum allowed value.
- let offset = Math.min(halfVisible, VERTICAL_OFFSET);
-
- let topIndex;
- switch (aAlign) {
- case this.VERTICAL_ALIGN.CENTER:
- topIndex = Math.max(aLine - halfVisible, 0);
- break;
-
- case this.VERTICAL_ALIGN.BOTTOM:
- topIndex = Math.max(aLine - linesVisible + offset, 0);
- break;
-
- default: // this.VERTICAL_ALIGN.TOP.
- topIndex = Math.max(aLine - offset, 0);
- break;
- }
- // Bringing down the topIndex to total lines in the editor if exceeding.
- topIndex = Math.min(topIndex, this.getLineCount());
- this.setTopIndex(topIndex);
-
- let location = this._view.getLocationAtOffset(caretOffset);
- this._view.setHorizontalPixel(location.x);
- },
-
- /**
- * Get the line count.
- *
- * @return number
- * The number of lines in the document being edited.
- */
- getLineCount: function SE_getLineCount()
- {
- return this._model.getLineCount();
- },
-
- /**
- * Get the line delimiter used in the document being edited.
- *
- * @return string
- * The line delimiter.
- */
- getLineDelimiter: function SE_getLineDelimiter()
- {
- return this._model.getLineDelimiter();
- },
-
- /**
- * Get the indentation string used in the document being edited.
- *
- * @return string
- * The indentation string.
- */
- getIndentationString: function SE_getIndentationString()
- {
- if (this._expandTab) {
- return (new Array(this._tabSize + 1)).join(" ");
- }
- return "\t";
- },
-
- /**
- * Set the source editor mode to the file type you are editing.
- *
- * @param string aMode
- * One of the predefined SourceEditor.MODES.
- */
- setMode: function SE_setMode(aMode)
- {
- if (this._styler) {
- this._styler.destroy();
- this._styler = null;
- }
-
- let window = this._iframeWindow;
-
- switch (aMode) {
- case SourceEditor.MODES.JAVASCRIPT:
- case SourceEditor.MODES.CSS:
- let TextStyler =
- window.require("examples/textview/textStyler").TextStyler;
-
- this._styler = new TextStyler(this._view, aMode, this._annotationModel);
- this._styler.setFoldingEnabled(false);
- break;
-
- case SourceEditor.MODES.HTML:
- case SourceEditor.MODES.XML:
- let TextMateStyler =
- window.require("orion/editor/textMateStyler").TextMateStyler;
- let HtmlGrammar =
- window.require("orion/editor/htmlGrammar").HtmlGrammar;
- this._styler = new TextMateStyler(this._view, new HtmlGrammar());
- break;
- }
-
- this._highlightAnnotations();
- this._mode = aMode;
- },
-
- /**
- * Get the current source editor mode.
- *
- * @return string
- * Returns one of the predefined SourceEditor.MODES.
- */
- getMode: function SE_getMode()
- {
- return this._mode;
- },
-
- /**
- * Setter for the read-only state of the editor.
- * @param boolean aValue
- * Tells if you want the editor to read-only or not.
- */
- set readOnly(aValue)
- {
- this._view.setOptions({
- readonly: aValue,
- themeClass: "mozilla" + (aValue ? " readonly" : ""),
- });
- },
-
- /**
- * Getter for the read-only state of the editor.
- * @type boolean
- */
- get readOnly()
- {
- return this._view.getOptions("readonly");
- },
-
- /**
- * Set the current debugger location at the given line index. This is useful in
- * a debugger or in any other context where the user needs to track the
- * current state, where a debugger-like environment is at.
- *
- * @param number aLineIndex
- * Line index of the current debugger location, starting from 0.
- * Use any negative number to clear the current location.
- */
- setDebugLocation: function SE_setDebugLocation(aLineIndex)
- {
- let annotations = this._getAnnotationsByType("debugLocation", 0,
- this.getCharCount());
- if (annotations.length > 0) {
- annotations.forEach(this._annotationModel.removeAnnotation,
- this._annotationModel);
- }
- if (aLineIndex < 0) {
- return;
- }
-
- let lineStart = this._model.getLineStart(aLineIndex);
- let lineEnd = this._model.getLineEnd(aLineIndex);
- let lineText = this._model.getLine(aLineIndex);
- let title = SourceEditorUI.strings.
- formatStringFromName("annotation.debugLocation.title",
- [lineText], 1);
-
- let annotation = {
- type: ORION_ANNOTATION_TYPES.debugLocation,
- start: lineStart,
- end: lineEnd,
- title: title,
- style: {styleClass: "annotation debugLocation"},
- html: "",
- overviewStyle: {styleClass: "annotationOverview debugLocation"},
- rangeStyle: {styleClass: "annotationRange debugLocation"},
- lineStyle: {styleClass: "annotationLine debugLocation"},
- };
- this._annotationModel.addAnnotation(annotation);
- },
-
- /**
- * Retrieve the current debugger line index configured for this editor.
- *
- * @return number
- * The line index starting from 0 where the current debugger is
- * paused. If no debugger location has been set -1 is returned.
- */
- getDebugLocation: function SE_getDebugLocation()
- {
- let annotations = this._getAnnotationsByType("debugLocation", 0,
- this.getCharCount());
- if (annotations.length > 0) {
- return this._model.getLineAtOffset(annotations[0].start);
- }
- return -1;
- },
-
- /**
- * Add a breakpoint at the given line index.
- *
- * @param number aLineIndex
- * Line index where to add the breakpoint (starts from 0).
- * @param string [aCondition]
- * Optional breakpoint condition.
- */
- addBreakpoint: function SE_addBreakpoint(aLineIndex, aCondition)
- {
- let lineStart = this.getLineStart(aLineIndex);
- let lineEnd = this.getLineEnd(aLineIndex);
-
- let annotations = this._getAnnotationsByType("breakpoint", lineStart, lineEnd);
- if (annotations.length > 0) {
- return;
- }
-
- let lineText = this._model.getLine(aLineIndex);
- let title = SourceEditorUI.strings.
- formatStringFromName("annotation.breakpoint.title",
- [lineText], 1);
-
- let annotation = {
- type: ORION_ANNOTATION_TYPES.breakpoint,
- start: lineStart,
- end: lineEnd,
- breakpointCondition: aCondition,
- title: title,
- style: {styleClass: "annotation breakpoint"},
- html: "",
- overviewStyle: {styleClass: "annotationOverview breakpoint"},
- rangeStyle: {styleClass: "annotationRange breakpoint"}
- };
- this._annotationModel.addAnnotation(annotation);
-
- let event = {
- type: SourceEditor.EVENTS.BREAKPOINT_CHANGE,
- added: [{line: aLineIndex, condition: aCondition}],
- removed: [],
- };
-
- this._dispatchEvent(event);
- },
-
- /**
- * Remove the current breakpoint from the given line index.
- *
- * @param number aLineIndex
- * Line index from where to remove the breakpoint (starts from 0).
- * @return boolean
- * True if a breakpoint was removed, false otherwise.
- */
- removeBreakpoint: function SE_removeBreakpoint(aLineIndex)
- {
- let lineStart = this.getLineStart(aLineIndex);
- let lineEnd = this.getLineEnd(aLineIndex);
-
- let event = {
- type: SourceEditor.EVENTS.BREAKPOINT_CHANGE,
- added: [],
- removed: [],
- };
-
- let annotations = this._getAnnotationsByType("breakpoint", lineStart, lineEnd);
-
- annotations.forEach(function(annotation) {
- this._annotationModel.removeAnnotation(annotation);
- event.removed.push({line: aLineIndex,
- condition: annotation.breakpointCondition});
- }, this);
-
- if (event.removed.length > 0) {
- this._dispatchEvent(event);
- }
-
- return event.removed.length > 0;
- },
-
- /**
- * Get the list of breakpoints in the Source Editor instance.
- *
- * @return array
- * The array of breakpoints. Each item is an object with two
- * properties: line and condition.
- */
- getBreakpoints: function SE_getBreakpoints()
- {
- let annotations = this._getAnnotationsByType("breakpoint", 0,
- this.getCharCount());
- let breakpoints = [];
-
- annotations.forEach(function(annotation) {
- breakpoints.push({line: this._model.getLineAtOffset(annotation.start),
- condition: annotation.breakpointCondition});
- }, this);
-
- return breakpoints;
- },
-
- /**
- * Convert the given rectangle from one coordinate reference to another.
- *
- * Known coordinate references:
- * - "document" - gives the coordinates relative to the entire document.
- * - "view" - gives the coordinates relative to the editor viewport.
- *
- * @param object aRect
- * The rectangle to convert. Object properties: x, y, width and height.
- * @param string aFrom
- * The source coordinate reference.
- * @param string aTo
- * The destination coordinate reference.
- * @return object aRect
- * Returns the rectangle with changed coordinates.
- */
- convertCoordinates: function SE_convertCoordinates(aRect, aFrom, aTo)
- {
- return this._view.convert(aRect, aFrom, aTo);
- },
-
- /**
- * Get the character offset nearest to the given pixel location.
- *
- * @param number aX
- * @param number aY
- * @return number
- * Returns the character offset at the given location.
- */
- getOffsetAtLocation: function SE_getOffsetAtLocation(aX, aY)
- {
- return this._view.getOffsetAtLocation(aX, aY);
- },
-
- /**
- * Get the pixel location, relative to the document, at the given character
- * offset.
- *
- * @param number aOffset
- * @return object
- * The pixel location relative to the document being edited. Two
- * properties are included: x and y.
- */
- getLocationAtOffset: function SE_getLocationAtOffset(aOffset)
- {
- return this._view.getLocationAtOffset(aOffset);
- },
-
- /**
- * Get the line location for a given character offset.
- *
- * @param number aOffset
- * @return number
- * The line location relative to the give character offset.
- */
- getLineAtOffset: function SE_getLineAtOffset(aOffset)
- {
- return this._model.getLineAtOffset(aOffset);
- },
-
- /**
- * Destroy/uninitialize the editor.
- */
- destroy: function SE_destroy()
- {
- if (this._config.highlightCurrentLine || Services.appinfo.OS == "Linux") {
- this.removeEventListener(SourceEditor.EVENTS.SELECTION,
- this._onOrionSelection);
- }
- this._onOrionSelection = null;
-
- this.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
- this._onTextChanged);
- this._onTextChanged = null;
-
- if (this._contextMenu) {
- this.removeEventListener(SourceEditor.EVENTS.CONTEXT_MENU,
- this._onOrionContextMenu);
- this._contextMenu = null;
- }
- this._onOrionContextMenu = null;
-
- if (this._primarySelectionTimeout) {
- let window = this.parentElement.ownerDocument.defaultView;
- window.clearTimeout(this._primarySelectionTimeout);
- this._primarySelectionTimeout = null;
- }
-
- this._view.destroy();
- this.ui.destroy();
- this.ui = null;
-
- this.parentElement.removeChild(this._iframe);
- this.parentElement = null;
- this._iframeWindow = null;
- this._iframe = null;
- this._undoStack = null;
- this._styler = null;
- this._linesRuler = null;
- this._annotationRuler = null;
- this._overviewRuler = null;
- this._dragAndDrop = null;
- this._annotationModel = null;
- this._annotationStyler = null;
- this._currentLineAnnotation = null;
- this._eventTarget = null;
- this._eventListenersQueue = null;
- this._view = null;
- this._model = null;
- this._config = null;
- this._lastFind = null;
- },
-};
diff --git a/browser/devtools/sourceeditor/source-editor-overlay.xul b/browser/devtools/sourceeditor/source-editor-overlay.xul
deleted file mode 100644
index e4fb7c91f7f9..000000000000
--- a/browser/devtools/sourceeditor/source-editor-overlay.xul
+++ /dev/null
@@ -1,204 +0,0 @@
-
-
-
- %editMenuStrings;
-
- %sourceEditorStrings;
-]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-#ifdef XP_UNIX
-
-#else
-
-#endif
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/browser/devtools/sourceeditor/source-editor-ui.jsm b/browser/devtools/sourceeditor/source-editor-ui.jsm
deleted file mode 100644
index 8b74d16238f0..000000000000
--- a/browser/devtools/sourceeditor/source-editor-ui.jsm
+++ /dev/null
@@ -1,332 +0,0 @@
-/* vim:set ts=2 sw=2 sts=2 et tw=80:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const Cu = Components.utils;
-
-Cu.import("resource://gre/modules/Services.jsm");
-
-this.EXPORTED_SYMBOLS = ["SourceEditorUI"];
-
-/**
- * The Source Editor component user interface.
- */
-this.SourceEditorUI = function SourceEditorUI(aEditor)
-{
- this.editor = aEditor;
- this._onDirtyChanged = this._onDirtyChanged.bind(this);
-}
-
-SourceEditorUI.prototype = {
- /**
- * Initialize the user interface. This is called by the SourceEditor.init()
- * method.
- */
- init: function SEU_init()
- {
- this._ownerWindow = this.editor.parentElement.ownerDocument.defaultView;
- },
-
- /**
- * The UI onReady function is executed once the Source Editor completes
- * initialization and it is ready for usage. Currently this code sets up the
- * nsIController.
- */
- onReady: function SEU_onReady()
- {
- if (this._ownerWindow.controllers) {
- this._controller = new SourceEditorController(this.editor);
- this._ownerWindow.controllers.insertControllerAt(0, this._controller);
- this.editor.addEventListener(this.editor.EVENTS.DIRTY_CHANGED,
- this._onDirtyChanged);
- }
- },
-
- /**
- * The "go to line" command UI. This displays a prompt that allows the user to
- * input the line number to jump to.
- */
- gotoLine: function SEU_gotoLine()
- {
- let oldLine = this.editor.getCaretPosition ?
- this.editor.getCaretPosition().line : null;
- let newLine = {value: oldLine !== null ? oldLine + 1 : ""};
-
- let result = Services.prompt.prompt(this._ownerWindow,
- SourceEditorUI.strings.GetStringFromName("gotoLineCmd.promptTitle"),
- SourceEditorUI.strings.GetStringFromName("gotoLineCmd.promptMessage"),
- newLine, null, {});
-
- newLine.value = parseInt(newLine.value);
- if (result && !isNaN(newLine.value) && --newLine.value != oldLine) {
- if (this.editor.getLineCount) {
- let lines = this.editor.getLineCount() - 1;
- this.editor.setCaretPosition(Math.max(0, Math.min(lines, newLine.value)));
- } else {
- this.editor.setCaretPosition(Math.max(0, newLine.value));
- }
- }
-
- return true;
- },
-
- /**
- * The "find" command UI. This displays a prompt that allows the user to input
- * the string to search for in the code. By default the current selection is
- * used as a search string, or the last search string.
- */
- find: function SEU_find()
- {
- let str = {value: this.editor.getSelectedText()};
- if (!str.value && this.editor.lastFind) {
- str.value = this.editor.lastFind.str;
- }
-
- let result = Services.prompt.prompt(this._ownerWindow,
- SourceEditorUI.strings.GetStringFromName("findCmd.promptTitle"),
- SourceEditorUI.strings.GetStringFromName("findCmd.promptMessage"),
- str, null, {});
-
- if (result && str.value) {
- let start = this.editor.getSelection().end;
- let pos = this.editor.find(str.value, {ignoreCase: true, start: start});
- if (pos == -1) {
- this.editor.find(str.value, {ignoreCase: true});
- }
- this._onFind();
- }
-
- return true;
- },
-
- /**
- * Find the next occurrence of the last search string.
- */
- findNext: function SEU_findNext()
- {
- let lastFind = this.editor.lastFind;
- if (lastFind) {
- this.editor.findNext(true);
- this._onFind();
- }
-
- return true;
- },
-
- /**
- * Find the previous occurrence of the last search string.
- */
- findPrevious: function SEU_findPrevious()
- {
- let lastFind = this.editor.lastFind;
- if (lastFind) {
- this.editor.findPrevious(true);
- this._onFind();
- }
-
- return true;
- },
-
- /**
- * This executed after each find/findNext/findPrevious operation.
- * @private
- */
- _onFind: function SEU__onFind()
- {
- let lastFind = this.editor.lastFind;
- if (lastFind && lastFind.index > -1) {
- this.editor.setSelection(lastFind.index, lastFind.index + lastFind.str.length);
- }
-
- if (this._ownerWindow.goUpdateCommand) {
- this._ownerWindow.goUpdateCommand("cmd_findAgain");
- this._ownerWindow.goUpdateCommand("cmd_findPrevious");
- }
- },
-
- /**
- * This is executed after each undo/redo operation.
- * @private
- */
- _onUndoRedo: function SEU__onUndoRedo()
- {
- if (this._ownerWindow.goUpdateCommand) {
- this._ownerWindow.goUpdateCommand("se-cmd-undo");
- this._ownerWindow.goUpdateCommand("se-cmd-redo");
- }
- },
-
- /**
- * The DirtyChanged event handler for the editor. This tracks the editor state
- * changes to make sure the Source Editor overlay Undo/Redo commands are kept
- * up to date.
- * @private
- */
- _onDirtyChanged: function SEU__onDirtyChanged()
- {
- this._onUndoRedo();
- },
-
- /**
- * Destroy the SourceEditorUI instance. This is called by the
- * SourceEditor.destroy() method.
- */
- destroy: function SEU_destroy()
- {
- if (this._ownerWindow.controllers) {
- this.editor.removeEventListener(this.editor.EVENTS.DIRTY_CHANGED,
- this._onDirtyChanged);
- }
-
- this._ownerWindow = null;
- this.editor = null;
- this._controller = null;
- },
-};
-
-/**
- * The Source Editor nsIController implements features that need to be available
- * from XUL commands.
- *
- * @constructor
- * @param object aEditor
- * SourceEditor object instance for which the controller is instanced.
- */
-function SourceEditorController(aEditor)
-{
- this._editor = aEditor;
-}
-
-SourceEditorController.prototype = {
- /**
- * Check if a command is supported by the controller.
- *
- * @param string aCommand
- * The command name you want to check support for.
- * @return boolean
- * True if the command is supported, false otherwise.
- */
- supportsCommand: function SEC_supportsCommand(aCommand)
- {
- let result;
-
- switch (aCommand) {
- case "cmd_find":
- case "cmd_findAgain":
- case "cmd_findPrevious":
- case "cmd_gotoLine":
- case "se-cmd-undo":
- case "se-cmd-redo":
- case "se-cmd-cut":
- case "se-cmd-paste":
- case "se-cmd-delete":
- case "se-cmd-selectAll":
- result = true;
- break;
- default:
- result = false;
- break;
- }
-
- return result;
- },
-
- /**
- * Check if a command is enabled or not.
- *
- * @param string aCommand
- * The command name you want to check if it is enabled or not.
- * @return boolean
- * True if the command is enabled, false otherwise.
- */
- isCommandEnabled: function SEC_isCommandEnabled(aCommand)
- {
- let result;
-
- switch (aCommand) {
- case "cmd_find":
- case "cmd_gotoLine":
- case "se-cmd-selectAll":
- result = true;
- break;
- case "cmd_findAgain":
- case "cmd_findPrevious":
- result = this._editor.lastFind && this._editor.lastFind.lastFound != -1;
- break;
- case "se-cmd-undo":
- result = this._editor.canUndo();
- break;
- case "se-cmd-redo":
- result = this._editor.canRedo();
- break;
- case "se-cmd-cut":
- case "se-cmd-delete": {
- let selection = this._editor.getSelection();
- result = selection.start != selection.end && !this._editor.readOnly;
- break;
- }
- case "se-cmd-paste": {
- let window = this._editor._view._frameWindow;
- let controller = window.controllers.getControllerForCommand("cmd_paste");
- result = !this._editor.readOnly &&
- controller.isCommandEnabled("cmd_paste");
- break;
- }
- default:
- result = false;
- break;
- }
-
- return result;
- },
-
- /**
- * Perform a command.
- *
- * @param string aCommand
- * The command name you want to execute.
- * @return void
- */
- doCommand: function SEC_doCommand(aCommand)
- {
- switch (aCommand) {
- case "cmd_find":
- this._editor.ui.find();
- break;
- case "cmd_findAgain":
- this._editor.ui.findNext();
- break;
- case "cmd_findPrevious":
- this._editor.ui.findPrevious();
- break;
- case "cmd_gotoLine":
- this._editor.ui.gotoLine();
- break;
- case "se-cmd-selectAll":
- this._editor._view.invokeAction("selectAll");
- break;
- case "se-cmd-undo":
- this._editor.undo();
- break;
- case "se-cmd-redo":
- this._editor.redo();
- break;
- case "se-cmd-cut":
- this._editor.ui._ownerWindow.goDoCommand("cmd_cut");
- break;
- case "se-cmd-paste":
- this._editor.ui._ownerWindow.goDoCommand("cmd_paste");
- break;
- case "se-cmd-delete": {
- let selection = this._editor.getSelection();
- this._editor.setText("", selection.start, selection.end);
- break;
- }
- }
- },
-
- onEvent: function() { }
-};
diff --git a/browser/devtools/sourceeditor/source-editor.jsm b/browser/devtools/sourceeditor/source-editor.jsm
deleted file mode 100644
index deb0f4455df6..000000000000
--- a/browser/devtools/sourceeditor/source-editor.jsm
+++ /dev/null
@@ -1,455 +0,0 @@
-/* vim:set ts=2 sw=2 sts=2 et tw=80:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const Cu = Components.utils;
-
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource:///modules/devtools/sourceeditor/source-editor-ui.jsm");
-
-const PREF_EDITOR_COMPONENT = "devtools.editor.component";
-const SOURCEEDITOR_L10N = "chrome://browser/locale/devtools/sourceeditor.properties";
-
-var component = Services.prefs.getCharPref(PREF_EDITOR_COMPONENT);
-var obj = {};
-try {
- if (component == "ui") {
- throw new Error("The ui editor component is not available.");
- }
- Cu.import("resource:///modules/devtools/sourceeditor/source-editor-" + component + ".jsm", obj);
-} catch (ex) {
- Cu.reportError(ex);
- Cu.reportError("SourceEditor component failed to load: " + component);
-
- // If the component does not exist, clear the user pref back to the default.
- Services.prefs.clearUserPref(PREF_EDITOR_COMPONENT);
-
- // Load the default editor component.
- component = Services.prefs.getCharPref(PREF_EDITOR_COMPONENT);
- Cu.import("resource:///modules/devtools/sourceeditor/source-editor-" + component + ".jsm", obj);
-}
-
-// Export the SourceEditor.
-this.SourceEditor = obj.SourceEditor;
-this.EXPORTED_SYMBOLS = ["SourceEditor"];
-
-// Add the constants used by all SourceEditors.
-
-XPCOMUtils.defineLazyGetter(SourceEditorUI, "strings", function() {
- return Services.strings.createBundle(SOURCEEDITOR_L10N);
-});
-
-/**
- * Known SourceEditor preferences.
- */
-SourceEditor.PREFS = {
- TAB_SIZE: "devtools.editor.tabsize",
- EXPAND_TAB: "devtools.editor.expandtab",
- COMPONENT: PREF_EDITOR_COMPONENT,
-};
-
-/**
- * Predefined source editor modes for JavaScript, CSS and other languages.
- */
-SourceEditor.MODES = {
- JAVASCRIPT: "js",
- CSS: "css",
- TEXT: "text",
- HTML: "html",
- XML: "xml",
-};
-
-/**
- * Predefined themes for syntax highlighting.
- */
-SourceEditor.THEMES = {
- MOZILLA: "mozilla",
-};
-
-/**
- * Source editor configuration defaults.
- * @see SourceEditor.init
- */
-SourceEditor.DEFAULTS = {
- /**
- * The text you want shown when the editor opens up.
- * @type string
- */
- initialText: "",
-
- /**
- * The editor mode, based on the file type you want to edit. You can use one of
- * the predefined modes.
- *
- * @see SourceEditor.MODES
- * @type string
- */
- mode: SourceEditor.MODES.TEXT,
-
- /**
- * The syntax highlighting theme you want. You can use one of the predefined
- * themes, or you can point to your CSS file.
- *
- * @see SourceEditor.THEMES.
- * @type string
- */
- theme: SourceEditor.THEMES.MOZILLA,
-
- /**
- * How many steps should the undo stack hold.
- * @type number
- */
- undoLimit: 200,
-
- /**
- * Define how many spaces to use for a tab character. This value is overridden
- * by a user preference, see SourceEditor.PREFS.TAB_SIZE.
- *
- * @type number
- */
- tabSize: 4,
-
- /**
- * Tells if you want tab characters to be expanded to spaces. This value is
- * overridden by a user preference, see SourceEditor.PREFS.EXPAND_TAB.
- * @type boolean
- */
- expandTab: true,
-
- /**
- * Tells if you want the editor to be read only or not.
- * @type boolean
- */
- readOnly: false,
-
- /**
- * Display the line numbers gutter.
- * @type boolean
- */
- showLineNumbers: false,
-
- /**
- * Display the annotations gutter/ruler. This gutter currently supports
- * annotations of breakpoint type.
- * @type boolean
- */
- showAnnotationRuler: false,
-
- /**
- * Display the overview gutter/ruler. This gutter presents an overview of the
- * current annotations in the editor, for example the breakpoints.
- * @type boolean
- */
- showOverviewRuler: false,
-
- /**
- * Highlight the current line.
- * @type boolean
- */
- highlightCurrentLine: true,
-
- /**
- * An array of objects that allows you to define custom editor keyboard
- * bindings. Each object can have:
- * - action - name of the editor action to invoke.
- * - code - keyCode for the shortcut.
- * - accel - boolean for the Accel key (Cmd on Macs, Ctrl on Linux/Windows).
- * - ctrl - boolean for the Control key
- * - shift - boolean for the Shift key.
- * - alt - boolean for the Alt key.
- * - callback - optional function to invoke, if the action is not predefined
- * in the editor.
- * @type array
- */
- keys: null,
-
- /**
- * The editor context menu you want to display when the user right-clicks
- * within the editor. This property can be:
- * - a string that tells the ID of the xul:menupopup you want. This needs to
- * be available within the editor parentElement.ownerDocument.
- * - an nsIDOMElement object reference pointing to the xul:menupopup you
- * want to open when the contextmenu event is fired.
- *
- * Set this property to a falsey value to disable the default context menu.
- *
- * @see SourceEditor.EVENTS.CONTEXT_MENU for more control over the contextmenu
- * event.
- * @type string|nsIDOMElement
- */
- contextMenu: "sourceEditorContextMenu",
-};
-
-/**
- * Known editor events you can listen for.
- */
-SourceEditor.EVENTS = {
- /**
- * The contextmenu event is fired when the editor context menu is invoked. The
- * event object properties:
- * - x - the pointer location on the x axis, relative to the document the
- * user is editing.
- * - y - the pointer location on the y axis, relative to the document the
- * user is editing.
- * - screenX - the pointer location on the x axis, relative to the screen.
- * This value comes from the DOM contextmenu event.screenX property.
- * - screenY - the pointer location on the y axis, relative to the screen.
- * This value comes from the DOM contextmenu event.screenY property.
- *
- * @see SourceEditor.DEFAULTS.contextMenu
- */
- CONTEXT_MENU: "ContextMenu",
-
- /**
- * The TextChanged event is fired when the editor content changes. The event
- * object properties:
- * - start - the character offset in the document where the change has
- * occured.
- * - removedCharCount - the number of characters removed from the document.
- * - addedCharCount - the number of characters added to the document.
- */
- TEXT_CHANGED: "TextChanged",
-
- /**
- * The Selection event is fired when the editor selection changes. The event
- * object properties:
- * - oldValue - the old selection range.
- * - newValue - the new selection range.
- * Both ranges are objects which hold two properties: start and end.
- */
- SELECTION: "Selection",
-
- /**
- * The focus event is fired when the editor is focused.
- */
- FOCUS: "Focus",
-
- /**
- * The blur event is fired when the editor goes out of focus.
- */
- BLUR: "Blur",
-
- /**
- * The MouseMove event is sent when the user moves the mouse over a line.
- * The event object properties:
- * - event - the DOM mousemove event object.
- * - x and y - the mouse coordinates relative to the document being edited.
- */
- MOUSE_MOVE: "MouseMove",
-
- /**
- * The MouseOver event is sent when the mouse pointer enters a line.
- * The event object properties:
- * - event - the DOM mouseover event object.
- * - x and y - the mouse coordinates relative to the document being edited.
- */
- MOUSE_OVER: "MouseOver",
-
- /**
- * This MouseOut event is sent when the mouse pointer exits a line.
- * The event object properties:
- * - event - the DOM mouseout event object.
- * - x and y - the mouse coordinates relative to the document being edited.
- */
- MOUSE_OUT: "MouseOut",
-
- /**
- * The BreakpointChange event is fired when a new breakpoint is added or when
- * a breakpoint is removed - either through API use or through the editor UI.
- * Event object properties:
- * - added - array that holds the new breakpoints.
- * - removed - array that holds the breakpoints that have been removed.
- * Each object in the added/removed arrays holds two properties: line and
- * condition.
- */
- BREAKPOINT_CHANGE: "BreakpointChange",
-
- /**
- * The DirtyChanged event is fired when the dirty state of the editor is
- * changed. The dirty state of the editor tells if the are text changes that
- * have not been saved yet. Event object properties: oldValue and newValue.
- * Both are booleans telling the old dirty state and the new state,
- * respectively.
- */
- DIRTY_CHANGED: "DirtyChanged",
-};
-
-/**
- * Allowed vertical alignment options for the line index
- * when you call SourceEditor.setCaretPosition().
- */
-SourceEditor.VERTICAL_ALIGN = {
- TOP: 0,
- CENTER: 1,
- BOTTOM: 2,
-};
-
-/**
- * Extend a destination object with properties from a source object.
- *
- * @param object aDestination
- * @param object aSource
- */
-function extend(aDestination, aSource)
-{
- for (let name in aSource) {
- if (!aDestination.hasOwnProperty(name)) {
- aDestination[name] = aSource[name];
- }
- }
-}
-
-/**
- * Add methods common to all components.
- */
-extend(SourceEditor.prototype, {
- // Expose the static constants on the SourceEditor instances.
- EVENTS: SourceEditor.EVENTS,
- MODES: SourceEditor.MODES,
- THEMES: SourceEditor.THEMES,
- DEFAULTS: SourceEditor.DEFAULTS,
- VERTICAL_ALIGN: SourceEditor.VERTICAL_ALIGN,
-
- _lastFind: null,
-
- /**
- * Find a string in the editor.
- *
- * @param string aString
- * The string you want to search for. If |aString| is not given the
- * currently selected text is used.
- * @param object [aOptions]
- * Optional find options:
- * - start: (integer) offset to start searching from. Default: 0 if
- * backwards is false. If backwards is true then start = text.length.
- * - ignoreCase: (boolean) tells if you want the search to be case
- * insensitive or not. Default: false.
- * - backwards: (boolean) tells if you want the search to go backwards
- * from the given |start| offset. Default: false.
- * @return integer
- * The offset where the string was found.
- */
- find: function SE_find(aString, aOptions)
- {
- if (typeof(aString) != "string") {
- return -1;
- }
-
- aOptions = aOptions || {};
-
- let str = aOptions.ignoreCase ? aString.toLowerCase() : aString;
-
- let text = this.getText();
- if (aOptions.ignoreCase) {
- text = text.toLowerCase();
- }
-
- let index = aOptions.backwards ?
- text.lastIndexOf(str, aOptions.start) :
- text.indexOf(str, aOptions.start);
-
- let lastFoundIndex = index;
- if (index == -1 && this.lastFind && this.lastFind.index > -1 &&
- this.lastFind.str === aString &&
- this.lastFind.ignoreCase === !!aOptions.ignoreCase) {
- lastFoundIndex = this.lastFind.index;
- }
-
- this._lastFind = {
- str: aString,
- index: index,
- lastFound: lastFoundIndex,
- ignoreCase: !!aOptions.ignoreCase,
- };
-
- return index;
- },
-
- /**
- * Find the next occurrence of the last search operation.
- *
- * @param boolean aWrap
- * Tells if you want to restart the search from the beginning of the
- * document if the string is not found.
- * @return integer
- * The offset where the string was found.
- */
- findNext: function SE_findNext(aWrap)
- {
- if (!this.lastFind && this.lastFind.lastFound == -1) {
- return -1;
- }
-
- let options = {
- start: this.lastFind.lastFound + this.lastFind.str.length,
- ignoreCase: this.lastFind.ignoreCase,
- };
-
- let index = this.find(this.lastFind.str, options);
- if (index == -1 && aWrap) {
- options.start = 0;
- index = this.find(this.lastFind.str, options);
- }
-
- return index;
- },
-
- /**
- * Find the previous occurrence of the last search operation.
- *
- * @param boolean aWrap
- * Tells if you want to restart the search from the end of the
- * document if the string is not found.
- * @return integer
- * The offset where the string was found.
- */
- findPrevious: function SE_findPrevious(aWrap)
- {
- if (!this.lastFind && this.lastFind.lastFound == -1) {
- return -1;
- }
-
- let options = {
- start: this.lastFind.lastFound - this.lastFind.str.length,
- ignoreCase: this.lastFind.ignoreCase,
- backwards: true,
- };
-
- let index;
- if (options.start > 0) {
- index = this.find(this.lastFind.str, options);
- } else {
- index = this._lastFind.index = -1;
- }
-
- if (index == -1 && aWrap) {
- options.start = this.getCharCount() - 1;
- index = this.find(this.lastFind.str, options);
- }
-
- return index;
- },
-});
-
-/**
- * Retrieve the last find operation result. This object holds the following
- * properties:
- * - str: the last search string.
- * - index: stores the result of the most recent find operation. This is the
- * index in the text where |str| was found or -1 otherwise.
- * - lastFound: tracks the index where |str| was last found, throughout
- * multiple find operations. This can be -1 if |str| was never found in the
- * document.
- * - ignoreCase: tells if the search was case insensitive or not.
- * @type object
- */
-Object.defineProperty(SourceEditor.prototype, "lastFind", {
- get: function() { return this._lastFind; },
- enumerable: true,
- configurable: true,
-});
-
diff --git a/browser/devtools/sourceeditor/test/browser.ini b/browser/devtools/sourceeditor/test/browser.ini
index ace13b6ea830..a4f871edd507 100644
--- a/browser/devtools/sourceeditor/test/browser.ini
+++ b/browser/devtools/sourceeditor/test/browser.ini
@@ -9,25 +9,9 @@ support-files =
codemirror.html
head.js
-[browser_bug650345_find.js]
-[browser_bug684546_reset_undo.js]
-[browser_bug684862_paste_html.js]
-[browser_bug687160_line_api.js]
-[browser_bug687568_pagescroll.js]
-[browser_bug687573_vscroll.js]
-[browser_bug687580_drag_and_drop.js]
-[browser_bug695035_middle_click_paste.js]
-[browser_bug700893_dirty_state.js]
-[browser_bug703692_focus_blur.js]
-[browser_bug707987_debugger_breakpoints.js]
-[browser_bug712982_line_ruler_click.js]
-[browser_bug725388_mouse_events.js]
-[browser_bug725392_mouse_coords_char_offset.js]
-[browser_bug725430_comment_uncomment.js]
-[browser_bug725618_moveLines_shortcut.js]
-[browser_bug729480_line_vertical_align.js]
-[browser_bug729960_block_bracket_jump.js]
-[browser_bug731721_debugger_stepping.js]
-[browser_bug744021_next_prev_bracket_jump.js]
+[browser_editor_basic.js]
+[browser_editor_cursor.js]
+[browser_editor_history.js]
+[browser_editor_markers.js]
[browser_codemirror.js]
-[browser_sourceeditor_initialization.js]
\ No newline at end of file
+[browser_sourceeditor_initialization.js]
diff --git a/browser/devtools/sourceeditor/test/browser_bug650345_find.js b/browser/devtools/sourceeditor/test/browser_bug650345_find.js
deleted file mode 100644
index 23a3d2eddf0d..000000000000
--- a/browser/devtools/sourceeditor/test/browser_bug650345_find.js
+++ /dev/null
@@ -1,149 +0,0 @@
-/* vim: set ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-let tempScope = {};
-Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm", tempScope);
-let SourceEditor = tempScope.SourceEditor;
-
-let testWin;
-let editor;
-
-function test()
-{
- waitForExplicitFinish();
-
- const windowUrl = "data:text/xml," +
- "";
- const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
-
- testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
- testWin.addEventListener("load", function onWindowLoad() {
- testWin.removeEventListener("load", onWindowLoad, false);
- waitForFocus(initEditor, testWin);
- }, false);
-}
-
-function initEditor()
-{
- let hbox = testWin.document.querySelector("hbox");
- editor = new SourceEditor();
- editor.init(hbox, {}, editorLoaded);
-}
-
-function editorLoaded()
-{
- editor.focus();
-
- let text = "foobar bug650345\nBug650345 bazbaz\nfoobar omg\ntest";
- editor.setText(text);
-
- let needle = "foobar";
- is(editor.find(), -1, "find() works");
- ok(!editor.lastFind, "no editor.lastFind yet");
-
- is(editor.find(needle), 0, "find('" + needle + "') works");
- is(editor.lastFind.str, needle, "lastFind.str is correct");
- is(editor.lastFind.index, 0, "lastFind.index is correct");
- is(editor.lastFind.lastFound, 0, "lastFind.lastFound is correct");
- is(editor.lastFind.ignoreCase, false, "lastFind.ignoreCase is correct");
-
- let newIndex = text.indexOf(needle, needle.length);
- is(editor.findNext(), newIndex, "findNext() works");
- is(editor.lastFind.str, needle, "lastFind.str is correct");
- is(editor.lastFind.index, newIndex, "lastFind.index is correct");
- is(editor.lastFind.lastFound, newIndex, "lastFind.lastFound is correct");
- is(editor.lastFind.ignoreCase, false, "lastFind.ignoreCase is correct");
-
- is(editor.findNext(), -1, "findNext() works again");
- is(editor.lastFind.index, -1, "lastFind.index is correct");
- is(editor.lastFind.lastFound, newIndex, "lastFind.lastFound is correct");
-
- is(editor.findPrevious(), 0, "findPrevious() works");
- is(editor.lastFind.index, 0, "lastFind.index is correct");
- is(editor.lastFind.lastFound, 0, "lastFind.lastFound is correct");
-
- is(editor.findPrevious(), -1, "findPrevious() works again");
- is(editor.lastFind.index, -1, "lastFind.index is correct");
- is(editor.lastFind.lastFound, 0, "lastFind.lastFound is correct");
-
- is(editor.findNext(), newIndex, "findNext() works");
- is(editor.lastFind.index, newIndex, "lastFind.index is correct");
- is(editor.lastFind.lastFound, newIndex, "lastFind.lastFound is correct");
-
- is(editor.findNext(true), 0, "findNext(true) works");
- is(editor.lastFind.index, 0, "lastFind.index is correct");
- is(editor.lastFind.lastFound, 0, "lastFind.lastFound is correct");
-
- is(editor.findNext(true), newIndex, "findNext(true) works again");
- is(editor.lastFind.index, newIndex, "lastFind.index is correct");
- is(editor.lastFind.lastFound, newIndex, "lastFind.lastFound is correct");
-
- is(editor.findPrevious(true), 0, "findPrevious(true) works");
- is(editor.lastFind.index, 0, "lastFind.index is correct");
- is(editor.lastFind.lastFound, 0, "lastFind.lastFound is correct");
-
- is(editor.findPrevious(true), newIndex, "findPrevious(true) works again");
- is(editor.lastFind.index, newIndex, "lastFind.index is correct");
- is(editor.lastFind.lastFound, newIndex, "lastFind.lastFound is correct");
-
- needle = "error";
- is(editor.find(needle), -1, "find('" + needle + "') works");
- is(editor.lastFind.str, needle, "lastFind.str is correct");
- is(editor.lastFind.index, -1, "lastFind.index is correct");
- is(editor.lastFind.lastFound, -1, "lastFind.lastFound is correct");
- is(editor.lastFind.ignoreCase, false, "lastFind.ignoreCase is correct");
-
- is(editor.findNext(), -1, "findNext() works");
- is(editor.lastFind.str, needle, "lastFind.str is correct");
- is(editor.lastFind.index, -1, "lastFind.index is correct");
- is(editor.lastFind.lastFound, -1, "lastFind.lastFound is correct");
- is(editor.findNext(true), -1, "findNext(true) works");
-
- is(editor.findPrevious(), -1, "findPrevious() works");
- is(editor.findPrevious(true), -1, "findPrevious(true) works");
-
- needle = "bug650345";
- newIndex = text.indexOf(needle);
-
- is(editor.find(needle), newIndex, "find('" + needle + "') works");
- is(editor.findNext(), -1, "findNext() works");
- is(editor.findNext(true), newIndex, "findNext(true) works");
- is(editor.findPrevious(), -1, "findPrevious() works");
- is(editor.findPrevious(true), newIndex, "findPrevious(true) works");
- is(editor.lastFind.index, newIndex, "lastFind.index is correct");
- is(editor.lastFind.lastFound, newIndex, "lastFind.lastFound is correct");
-
- is(editor.find(needle, {ignoreCase: 1}), newIndex,
- "find('" + needle + "', {ignoreCase: 1}) works");
- is(editor.lastFind.ignoreCase, true, "lastFind.ignoreCase is correct");
-
- let newIndex2 = text.toLowerCase().indexOf(needle, newIndex + needle.length);
- is(editor.findNext(), newIndex2, "findNext() works");
- is(editor.findNext(), -1, "findNext() works");
- is(editor.lastFind.index, -1, "lastFind.index is correct");
- is(editor.lastFind.lastFound, newIndex2, "lastFind.lastFound is correct");
-
- is(editor.findNext(true), newIndex, "findNext(true) works");
-
- is(editor.findPrevious(), -1, "findPrevious() works");
- is(editor.findPrevious(true), newIndex2, "findPrevious(true) works");
- is(editor.findPrevious(), newIndex, "findPrevious() works again");
-
- needle = "foobar";
- newIndex = text.indexOf(needle, 2);
- is(editor.find(needle, {start: 2}), newIndex,
- "find('" + needle + "', {start:2}) works");
- is(editor.findNext(), -1, "findNext() works");
- is(editor.findNext(true), 0, "findNext(true) works");
-
- editor.destroy();
-
- testWin.close();
- testWin = editor = null;
-
- waitForFocus(finish, window);
-}
diff --git a/browser/devtools/sourceeditor/test/browser_bug684546_reset_undo.js b/browser/devtools/sourceeditor/test/browser_bug684546_reset_undo.js
deleted file mode 100644
index d54e54ab367d..000000000000
--- a/browser/devtools/sourceeditor/test/browser_bug684546_reset_undo.js
+++ /dev/null
@@ -1,72 +0,0 @@
-/* vim: set ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-let tempScope = {};
-Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm", tempScope);
-let SourceEditor = tempScope.SourceEditor;
-
-let testWin;
-let editor;
-
-function test()
-{
- waitForExplicitFinish();
-
- const windowUrl = "data:application/vnd.mozilla.xul+xml," +
- "" +
- "";
- const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
-
- testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
- testWin.addEventListener("load", function onWindowLoad() {
- testWin.removeEventListener("load", onWindowLoad, false);
- waitForFocus(initEditor, testWin);
- }, false);
-}
-
-function initEditor()
-{
- let box = testWin.document.querySelector("box");
-
- editor = new SourceEditor();
- editor.init(box, {}, editorLoaded);
-}
-
-function editorLoaded()
-{
- editor.setText("First");
- editor.setText("Second", 5);
- is(editor.getText(), "FirstSecond", "text set correctly.");
- editor.undo();
- is(editor.getText(), "First", "undo works.");
- editor.redo();
- is(editor.getText(), "FirstSecond", "redo works.");
- editor.resetUndo();
- ok(!editor.canUndo(), "canUndo() is correct");
- ok(!editor.canRedo(), "canRedo() is correct");
- editor.undo();
- is(editor.getText(), "FirstSecond", "reset undo works correctly");
- editor.setText("Third", 11);
- is(editor.getText(), "FirstSecondThird", "text set correctly");
- editor.undo();
- is(editor.getText(), "FirstSecond", "undo works after reset");
- editor.redo();
- is(editor.getText(), "FirstSecondThird", "redo works after reset");
- editor.resetUndo();
- ok(!editor.canUndo(), "canUndo() is correct (again)");
- ok(!editor.canRedo(), "canRedo() is correct (again)");
- editor.undo();
- is(editor.getText(), "FirstSecondThird", "reset undo still works correctly");
-
- finish();
-}
-
-registerCleanupFunction(function() {
- editor.destroy();
- testWin.close();
- testWin = editor = null;
-});
diff --git a/browser/devtools/sourceeditor/test/browser_bug684862_paste_html.js b/browser/devtools/sourceeditor/test/browser_bug684862_paste_html.js
deleted file mode 100644
index 3ac37764e323..000000000000
--- a/browser/devtools/sourceeditor/test/browser_bug684862_paste_html.js
+++ /dev/null
@@ -1,119 +0,0 @@
-/* vim: set ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-let tempScope = {};
-Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm", tempScope);
-let SourceEditor = tempScope.SourceEditor;
-
-let testWin;
-let editor;
-
-function test()
-{
- waitForExplicitFinish();
-
- const pageUrl = "data:text/html,
test
foobarBug684862";
-
- gBrowser.selectedTab = gBrowser.addTab();
- gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
- gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
- waitForFocus(pageLoaded, content);
- }, true);
-
- content.location = pageUrl;
-}
-
-function pageLoaded()
-{
- const windowUrl = "data:application/vnd.mozilla.xul+xml," +
- "" +
- "" +
- "";
- const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
-
- let doCopy = function() {
- gBrowser.selectedBrowser.focus();
- EventUtils.synthesizeKey("a", {accelKey: true}, content);
- EventUtils.synthesizeKey("c", {accelKey: true}, content);
- };
-
- let clipboardValidator = function(aData) aData.indexOf("foobarBug684862") > -1;
-
- let onCopy = function() {
- testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
- testWin.addEventListener("load", function onWindowLoad() {
- testWin.removeEventListener("load", onWindowLoad, false);
- waitForFocus(initEditor, testWin);
- }, false);
- };
-
- waitForClipboard(clipboardValidator, doCopy, onCopy, testEnd);
-}
-
-function initEditor()
-{
- let box = testWin.document.querySelector("box");
-
- editor = new SourceEditor();
- editor.init(box, {}, editorLoaded);
-}
-
-function editorLoaded()
-{
- editor.focus();
-
- ok(!editor.getText(), "editor has no content");
- is(editor.getCaretOffset(), 0, "caret location");
-
- let onPaste = function() {
- editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED, onPaste);
-
- let text = editor.getText();
- ok(text, "editor has content after paste");
-
- let pos = text.indexOf("foobarBug684862");
- isnot(pos, -1, "editor content is correct");
- // Test for bug 699541 - Pasted HTML shows twice in Orion.
- is(text.lastIndexOf("foobarBug684862"), pos, "editor content is correct (no duplicate)");
-
- executeSoon(function() {
- editor.setCaretOffset(4);
- EventUtils.synthesizeKey("a", {}, testWin);
- EventUtils.synthesizeKey("VK_RIGHT", {}, testWin);
-
- text = editor.getText();
-
- is(text.indexOf("foobarBug684862"), pos + 1,
- "editor content is correct after navigation");
- is(editor.getCaretOffset(), 6, "caret location");
-
- executeSoon(testEnd);
- });
- };
-
- editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED, onPaste);
-
- // Do paste
- executeSoon(function() {
- testWin.goDoCommand("cmd_paste");
- });
-}
-
-function testEnd()
-{
- if (editor) {
- editor.destroy();
- }
- if (testWin) {
- testWin.close();
- }
- testWin = editor = null;
- gBrowser.removeCurrentTab();
-
- waitForFocus(finish, window);
-}
-
diff --git a/browser/devtools/sourceeditor/test/browser_bug687160_line_api.js b/browser/devtools/sourceeditor/test/browser_bug687160_line_api.js
deleted file mode 100644
index 864b0d70eca7..000000000000
--- a/browser/devtools/sourceeditor/test/browser_bug687160_line_api.js
+++ /dev/null
@@ -1,90 +0,0 @@
-/* vim: set ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-let tempScope = {};
-Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm", tempScope);
-let SourceEditor = tempScope.SourceEditor;
-
-let testWin;
-let editor;
-
-function test()
-{
- waitForExplicitFinish();
-
- const windowUrl = "data:text/xml," +
- "";
- const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
-
- testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
- testWin.addEventListener("load", function onWindowLoad() {
- testWin.removeEventListener("load", onWindowLoad, false);
- waitForFocus(initEditor, testWin);
- }, false);
-}
-
-function initEditor()
-{
- let hbox = testWin.document.querySelector("hbox");
- editor = new SourceEditor();
- editor.init(hbox, {}, editorLoaded);
-}
-
-function editorLoaded()
-{
- let component = Services.prefs.getCharPref(SourceEditor.PREFS.COMPONENT);
-
- editor.focus();
-
- editor.setText("line1\nline2\nline3");
-
- if (component != "textarea") {
- is(editor.getLineCount(), 3, "getLineCount() works");
- }
-
- editor.setCaretPosition(1);
- is(editor.getCaretOffset(), 6, "setCaretPosition(line) works");
-
- let pos;
- if (component != "textarea") {
- pos = editor.getCaretPosition();
- ok(pos.line == 1 && pos.col == 0, "getCaretPosition() works");
- }
-
- editor.setCaretPosition(1, 3);
- is(editor.getCaretOffset(), 9, "setCaretPosition(line, column) works");
-
- if (component != "textarea") {
- pos = editor.getCaretPosition();
- ok(pos.line == 1 && pos.col == 3, "getCaretPosition() works");
- }
-
- editor.setCaretPosition(2);
- is(editor.getCaretOffset(), 12, "setCaretLine() works, confirmed");
-
- if (component != "textarea") {
- pos = editor.getCaretPosition();
- ok(pos.line == 2 && pos.col == 0, "setCaretPosition(line) works, again");
- }
-
- let offsetLine = editor.getLineAtOffset(0);
- is(offsetLine, 0, "getLineAtOffset() is correct for offset 0");
-
- let offsetLine = editor.getLineAtOffset(6);
- is(offsetLine, 1, "getLineAtOffset() is correct for offset 6");
-
- let offsetLine = editor.getLineAtOffset(12);
- is(offsetLine, 2, "getLineAtOffset() is correct for offset 12");
-
- editor.destroy();
-
- testWin.close();
- testWin = editor = null;
-
- waitForFocus(finish, window);
-}
-
diff --git a/browser/devtools/sourceeditor/test/browser_bug687568_pagescroll.js b/browser/devtools/sourceeditor/test/browser_bug687568_pagescroll.js
deleted file mode 100644
index 675fa829c10a..000000000000
--- a/browser/devtools/sourceeditor/test/browser_bug687568_pagescroll.js
+++ /dev/null
@@ -1,89 +0,0 @@
-/* vim: set ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-let tempScope = {};
-Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm", tempScope);
-let SourceEditor = tempScope.SourceEditor;
-
-let testWin;
-let editor;
-
-function test()
-{
- let component = Services.prefs.getCharPref(SourceEditor.PREFS.COMPONENT);
- if (component != "orion") {
- ok(true, "skip test for bug 687568: only applicable for Orion");
- return; // Testing for the fix requires direct Orion API access.
- }
-
- waitForExplicitFinish();
-
- const windowUrl = "data:application/vnd.mozilla.xul+xml," +
- "" +
- "";
- const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
-
- testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
- testWin.addEventListener("load", function onWindowLoad() {
- testWin.removeEventListener("load", onWindowLoad, false);
- waitForFocus(initEditor, testWin);
- }, false);
-}
-
-function initEditor()
-{
- let box = testWin.document.querySelector("box");
-
- editor = new SourceEditor();
- editor.init(box, { showLineNumbers: true }, editorLoaded);
-}
-
-function editorLoaded()
-{
- editor.focus();
-
- let view = editor._view;
- let model = editor._model;
-
- let lineHeight = view.getLineHeight();
- let editorHeight = view.getClientArea().height;
- let linesPerPage = Math.floor(editorHeight / lineHeight);
- let totalLines = 3 * linesPerPage;
-
- let text = "";
- for (let i = 0; i < totalLines; i++) {
- text += "l" + i + "\n";
- }
-
- editor.setText(text);
- editor.setCaretOffset(0);
-
- EventUtils.synthesizeKey("VK_DOWN", {shiftKey: true}, testWin);
- EventUtils.synthesizeKey("VK_DOWN", {shiftKey: true}, testWin);
- EventUtils.synthesizeKey("VK_DOWN", {shiftKey: true}, testWin);
-
- let bottomLine = view.getBottomIndex(true);
- view.setTopIndex(bottomLine + 1);
-
- executeSoon(function() {
- EventUtils.synthesizeKey("VK_PAGE_DOWN", {shiftKey: true}, testWin);
-
- executeSoon(function() {
- let topLine = view.getTopIndex(true);
- let topLineOffset = model.getLineStart(topLine);
- let selection = editor.getSelection();
- ok(selection.start < topLineOffset && topLineOffset < selection.end,
- "top visible line is selected");
-
- editor.destroy();
- testWin.close();
- testWin = editor = null;
-
- waitForFocus(finish, window);
- });
- });
-}
diff --git a/browser/devtools/sourceeditor/test/browser_bug687573_vscroll.js b/browser/devtools/sourceeditor/test/browser_bug687573_vscroll.js
deleted file mode 100644
index b255e7ec9310..000000000000
--- a/browser/devtools/sourceeditor/test/browser_bug687573_vscroll.js
+++ /dev/null
@@ -1,133 +0,0 @@
-/* vim: set ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-let tempScope = {};
-Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm", tempScope);
-let SourceEditor = tempScope.SourceEditor;
-
-let testWin;
-let editor;
-
-function test()
-{
- let component = Services.prefs.getCharPref(SourceEditor.PREFS.COMPONENT);
- if (component == "textarea") {
- ok(true, "skip test for bug 687573: not applicable for TEXTAREAs");
- return; // TEXTAREAs have different behavior
- }
-
- waitForExplicitFinish();
-
- const windowUrl = "data:application/vnd.mozilla.xul+xml," +
- "" +
- "";
- const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
-
- testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
- testWin.addEventListener("load", function onWindowLoad() {
- testWin.removeEventListener("load", onWindowLoad, false);
- waitForFocus(initEditor, testWin);
- }, false);
-}
-
-function initEditor()
-{
- let box = testWin.document.querySelector("box");
-
- let text = "abba\n" +
- "\n" +
- "abbaabbaabbaabbaabbaabbaabbaabbaabbaabba\n" +
- "abbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabba\n" +
- "abbaabbaabbaabbaabbaabbaabbaabbaabbaabba\n" +
- "\n" +
- "abba\n";
-
- let config = {
- showLineNumbers: true,
- initialText: text,
- };
-
- editor = new SourceEditor();
- editor.init(box, config, editorLoaded);
-}
-
-function editorLoaded()
-{
- let VK_LINE_END = "VK_END";
- let VK_LINE_END_OPT = {};
- let OS = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
- if (OS == "Darwin") {
- VK_LINE_END = "VK_RIGHT";
- VK_LINE_END_OPT = {accelKey: true};
- }
-
- editor.focus();
-
- editor.setCaretOffset(0);
- is(editor.getCaretOffset(), 0, "caret location at start");
-
- EventUtils.synthesizeKey("VK_DOWN", {}, testWin);
- EventUtils.synthesizeKey("VK_DOWN", {}, testWin);
-
- // line 3
- is(editor.getCaretOffset(), 6, "caret location, keypress Down two times, line 3");
-
- // line 3 end
- EventUtils.synthesizeKey(VK_LINE_END, VK_LINE_END_OPT, testWin);
- is(editor.getCaretOffset(), 46, "caret location, keypress End, line 3 end");
-
- // line 4
- EventUtils.synthesizeKey("VK_DOWN", {}, testWin);
- is(editor.getCaretOffset(), 87, "caret location, keypress Down, line 4");
-
- // line 4 end
- EventUtils.synthesizeKey(VK_LINE_END, VK_LINE_END_OPT, testWin);
- is(editor.getCaretOffset(), 135, "caret location, keypress End, line 4 end");
-
- // line 5 end
- EventUtils.synthesizeKey("VK_DOWN", {}, testWin);
- is(editor.getCaretOffset(), 176, "caret location, keypress Down, line 5 end");
-
- // line 6 end
- EventUtils.synthesizeKey("VK_DOWN", {}, testWin);
- is(editor.getCaretOffset(), 177, "caret location, keypress Down, line 6 end");
-
- // The executeSoon() calls are needed to allow reflows...
- EventUtils.synthesizeKey("VK_UP", {}, testWin);
- executeSoon(function() {
- // line 5 end
- is(editor.getCaretOffset(), 176, "caret location, keypress Up, line 5 end");
-
- EventUtils.synthesizeKey("VK_UP", {}, testWin);
- executeSoon(function() {
- // line 4 end
- is(editor.getCaretOffset(), 135, "caret location, keypress Up, line 4 end");
-
- // line 3 end
- EventUtils.synthesizeKey("VK_UP", {}, testWin);
- is(editor.getCaretOffset(), 46, "caret location, keypress Up, line 3 end");
-
- // line 2 end
- EventUtils.synthesizeKey("VK_UP", {}, testWin);
- is(editor.getCaretOffset(), 5, "caret location, keypress Up, line 2 end");
-
- // line 3 end
- EventUtils.synthesizeKey("VK_DOWN", {}, testWin);
- is(editor.getCaretOffset(), 46, "caret location, keypress Down, line 3 end");
-
- // line 4 end
- EventUtils.synthesizeKey("VK_DOWN", {}, testWin);
- is(editor.getCaretOffset(), 135, "caret location, keypress Down, line 4 end");
-
- editor.destroy();
- testWin.close();
- testWin = editor = null;
-
- waitForFocus(finish, window);
- });
- });
-}
diff --git a/browser/devtools/sourceeditor/test/browser_bug687580_drag_and_drop.js b/browser/devtools/sourceeditor/test/browser_bug687580_drag_and_drop.js
deleted file mode 100644
index d4d1b97b9e99..000000000000
--- a/browser/devtools/sourceeditor/test/browser_bug687580_drag_and_drop.js
+++ /dev/null
@@ -1,162 +0,0 @@
-/* vim: set ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-let tempScope = {};
-Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm", tempScope);
-let SourceEditor = tempScope.SourceEditor;
-
-let testWin;
-let editor;
-
-function test()
-{
- let component = Services.prefs.getCharPref(SourceEditor.PREFS.COMPONENT);
- if (component != "orion") {
- ok(true, "skip test for bug 687580: only applicable for Orion");
- return; // Testing for the fix requires direct Orion API access.
- }
- waitForExplicitFinish();
-
- const windowUrl = "data:application/vnd.mozilla.xul+xml," +
- "" +
- "";
- const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
-
- testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
- testWin.addEventListener("load", function onWindowLoad() {
- testWin.removeEventListener("load", onWindowLoad, false);
- waitForFocus(initEditor, testWin);
- }, false);
-}
-
-function initEditor()
-{
- let box = testWin.document.querySelector("box");
-
- editor = new SourceEditor();
- editor.init(box, {}, editorLoaded);
-}
-
-function editorLoaded()
-{
- editor.focus();
-
- let view = editor._view;
- let model = editor._model;
-
- let lineHeight = view.getLineHeight();
- let editorHeight = view.getClientArea().height;
- let linesPerPage = Math.floor(editorHeight / lineHeight);
- let totalLines = 2 * linesPerPage;
-
- let text = "foobarBug687580-";
- for (let i = 0; i < totalLines; i++) {
- text += "l" + i + "\n";
- }
-
- editor.setText(text);
- editor.setCaretOffset(0);
-
- let bottomPixel = view.getBottomPixel();
-
- EventUtils.synthesizeKey("VK_DOWN", {shiftKey: true}, testWin);
- EventUtils.synthesizeKey("VK_DOWN", {shiftKey: true}, testWin);
- EventUtils.synthesizeKey("VK_DOWN", {shiftKey: true}, testWin);
-
- let initialSelection = editor.getSelection();
-
- let ds = Cc["@mozilla.org/widget/dragservice;1"].
- getService(Ci.nsIDragService);
-
- let target = view._clientDiv;
- let targetWin = target.ownerDocument.defaultView;
-
- let dataTransfer = null;
-
- let onDragStart = function(aEvent) {
- target.removeEventListener("dragstart", onDragStart, false);
-
- dataTransfer = aEvent.dataTransfer;
- ok(dataTransfer, "dragstart event fired");
- ok(dataTransfer.types.contains("text/plain"),
- "dataTransfer text/plain available");
- let text = dataTransfer.getData("text/plain");
- isnot(text.indexOf("foobarBug687580"), -1, "text/plain data is correct");
-
- dataTransfer.dropEffect = "move";
- };
-
- let onDrop = executeSoon.bind(null, function() {
- target.removeEventListener("drop", onDrop, false);
-
- let selection = editor.getSelection();
- is(selection.end - selection.start,
- initialSelection.end - initialSelection.start,
- "selection is correct");
- is(editor.getText(0, 2), "l3", "drag and drop worked");
-
- let offset = editor.getCaretOffset();
- ok(offset > initialSelection.end, "new caret location");
-
- let initialLength = initialSelection.end - initialSelection.start;
- let dropText = editor.getText(offset - initialLength, offset);
- isnot(dropText.indexOf("foobarBug687580"), -1, "drop text is correct");
-
- editor.destroy();
- testWin.close();
- testWin = editor = null;
-
- waitForFocus(finish, window);
- });
-
- executeSoon(function() {
- ds.startDragSession();
-
- target.addEventListener("dragstart", onDragStart, false);
- target.addEventListener("drop", onDrop, false);
-
- EventUtils.synthesizeMouse(target, 10, 10, {type: "mousedown"}, targetWin);
-
- EventUtils.synthesizeMouse(target, 11, bottomPixel - 25, {type: "mousemove"},
- targetWin);
-
- EventUtils.synthesizeMouse(target, 12, bottomPixel - 15, {type: "mousemove"},
- targetWin);
-
- let clientX = 5;
- let clientY = bottomPixel - 10;
-
- let event = targetWin.document.createEvent("DragEvents");
- event.initDragEvent("dragenter", true, true, targetWin, 0, 0, 0, clientX,
- clientY, false, false, false, false, 0, null,
- dataTransfer);
- target.dispatchEvent(event);
-
- event = targetWin.document.createEvent("DragEvents");
- event.initDragEvent("dragover", true, true, targetWin, 0, 0, 0, clientX + 1,
- clientY + 2, false, false, false, false, 0, null,
- dataTransfer);
- target.dispatchEvent(event);
-
- EventUtils.synthesizeMouse(target, clientX + 2, clientY + 1,
- {type: "mouseup"}, targetWin);
-
- event = targetWin.document.createEvent("DragEvents");
- event.initDragEvent("drop", true, true, targetWin, 0, 0, 0, clientX + 2,
- clientY + 3, false, false, false, false, 0, null,
- dataTransfer);
- target.dispatchEvent(event);
-
- event = targetWin.document.createEvent("DragEvents");
- event.initDragEvent("dragend", true, true, targetWin, 0, 0, 0, clientX + 3,
- clientY + 2, false, false, false, false, 0, null,
- dataTransfer);
- target.dispatchEvent(event);
-
- ds.endDragSession(true);
- });
-}
diff --git a/browser/devtools/sourceeditor/test/browser_bug695035_middle_click_paste.js b/browser/devtools/sourceeditor/test/browser_bug695035_middle_click_paste.js
deleted file mode 100644
index 79b2f1d5fc6a..000000000000
--- a/browser/devtools/sourceeditor/test/browser_bug695035_middle_click_paste.js
+++ /dev/null
@@ -1,100 +0,0 @@
-/* vim: set ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-let tempScope = {};
-Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm", tempScope);
-let SourceEditor = tempScope.SourceEditor;
-
-let testWin;
-let editor;
-
-function test()
-{
- if (Services.appinfo.OS != "Linux") {
- ok(true, "this test only applies to Linux, skipping.")
- return;
- }
-
- waitForExplicitFinish();
-
- const windowUrl = "data:text/xml," +
- "";
- const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
-
- testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
- testWin.addEventListener("load", function onWindowLoad() {
- testWin.removeEventListener("load", onWindowLoad, false);
- waitForFocus(initEditor, testWin);
- }, false);
-}
-
-function initEditor()
-{
- let hbox = testWin.document.querySelector("hbox");
-
- editor = new SourceEditor();
- editor.init(hbox, {}, editorLoaded);
-}
-
-function editorLoaded()
-{
- editor.focus();
-
- let initialText = "initial text!";
-
- editor.setText(initialText);
-
- let expectedString = "foobarBug695035-" + Date.now();
-
- let doCopy = function() {
- let clipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"].
- getService(Ci.nsIClipboardHelper);
- clipboardHelper.copyStringToClipboard(expectedString,
- Ci.nsIClipboard.kSelectionClipboard,
- testWin.document);
- };
-
- let onCopy = function() {
- editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED, onPaste);
-
- EventUtils.synthesizeMouse(editor.editorElement, 10, 10, {}, testWin);
- EventUtils.synthesizeMouse(editor.editorElement, 11, 11, {button: 1}, testWin);
- };
-
- let onPaste = function() {
- editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED, onPaste);
-
- let text = editor.getText();
- isnot(text.indexOf(expectedString), -1, "middle-click paste works");
- isnot(text, initialText, "middle-click paste works (confirmed)");
-
- executeSoon(doTestBug695032);
- };
-
- let doTestBug695032 = function() {
- info("test for bug 695032 - editor selection should be placed in the X11 primary selection buffer");
-
- let text = "foobarBug695032 test me, test me!";
- editor.setText(text);
-
- waitForSelection(text, function() {
- EventUtils.synthesizeKey("a", {accelKey: true}, testWin);
- }, testEnd, testEnd);
- };
-
- waitForSelection(expectedString, doCopy, onCopy, testEnd);
-}
-
-function testEnd()
-{
- editor.destroy();
- testWin.close();
-
- testWin = editor = null;
-
- waitForFocus(finish, window);
-}
diff --git a/browser/devtools/sourceeditor/test/browser_bug700893_dirty_state.js b/browser/devtools/sourceeditor/test/browser_bug700893_dirty_state.js
deleted file mode 100644
index 5b40d8029167..000000000000
--- a/browser/devtools/sourceeditor/test/browser_bug700893_dirty_state.js
+++ /dev/null
@@ -1,94 +0,0 @@
-/* vim: set ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-function test() {
-
- let temp = {};
- Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm", temp);
- let SourceEditor = temp.SourceEditor;
-
- let component = Services.prefs.getCharPref(SourceEditor.PREFS.COMPONENT);
- if (component == "textarea") {
- ok(true, "skip test for bug 700893: only applicable for non-textarea components");
- return;
- }
-
- waitForExplicitFinish();
-
- let editor;
-
- const windowUrl = "data:text/xml," +
- "";
- const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
-
- let testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
- testWin.addEventListener("load", function onWindowLoad() {
- testWin.removeEventListener("load", onWindowLoad, false);
- waitForFocus(initEditor, testWin);
- }, false);
-
- function initEditor()
- {
- let hbox = testWin.document.querySelector("hbox");
- editor = new SourceEditor();
- editor.init(hbox, {initialText: "foobar"}, editorLoaded);
- }
-
- function editorLoaded()
- {
- editor.focus();
-
- is(editor.dirty, false, "editory is not dirty");
-
- let event = null;
- let eventHandler = function(aEvent) {
- event = aEvent;
- };
- editor.addEventListener(SourceEditor.EVENTS.DIRTY_CHANGED, eventHandler);
-
- editor.setText("omg");
-
- is(editor.dirty, true, "editor is dirty");
- ok(event, "DirtyChanged event fired")
- is(event.oldValue, false, "event.oldValue is correct");
- is(event.newValue, true, "event.newValue is correct");
-
- event = null;
- editor.setText("foo 2");
- ok(!event, "no DirtyChanged event fired");
-
- editor.dirty = false;
-
- is(editor.dirty, false, "editor marked as clean");
- ok(event, "DirtyChanged event fired")
- is(event.oldValue, true, "event.oldValue is correct");
- is(event.newValue, false, "event.newValue is correct");
-
- event = null;
- editor.setText("foo 3");
-
- is(editor.dirty, true, "editor is dirty after changes");
- ok(event, "DirtyChanged event fired")
- is(event.oldValue, false, "event.oldValue is correct");
- is(event.newValue, true, "event.newValue is correct");
-
- editor.undo();
- is(editor.dirty, false, "editor is not dirty after undo");
- ok(event, "DirtyChanged event fired")
- is(event.oldValue, true, "event.oldValue is correct");
- is(event.newValue, false, "event.newValue is correct");
-
- editor.removeEventListener(SourceEditor.EVENTS.DIRTY_CHANGED, eventHandler);
-
- editor.destroy();
-
- testWin.close();
- testWin = editor = null;
-
- waitForFocus(finish, window);
- }
-}
diff --git a/browser/devtools/sourceeditor/test/browser_bug703692_focus_blur.js b/browser/devtools/sourceeditor/test/browser_bug703692_focus_blur.js
deleted file mode 100644
index 9d2453f8c085..000000000000
--- a/browser/devtools/sourceeditor/test/browser_bug703692_focus_blur.js
+++ /dev/null
@@ -1,71 +0,0 @@
-/* vim: set ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-let tempScope = {};
-Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm", tempScope);
-let SourceEditor = tempScope.SourceEditor;
-
-let testWin;
-let editor;
-
-function test()
-{
- waitForExplicitFinish();
-
- const windowUrl = "data:text/xml," +
- "";
- const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
-
- testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
- testWin.addEventListener("load", function onWindowLoad() {
- testWin.removeEventListener("load", onWindowLoad, false);
- waitForFocus(initEditor, testWin);
- }, false);
-}
-
-function initEditor()
-{
- let hbox = testWin.document.querySelector("hbox");
-
- editor = new SourceEditor();
- editor.init(hbox, {}, editorLoaded);
-}
-
-function editorLoaded()
-{
- let focusHandler = function(aEvent) {
- editor.removeEventListener(SourceEditor.EVENTS.FOCUS, focusHandler);
- editor.addEventListener(SourceEditor.EVENTS.BLUR, blurHandler);
-
- ok(aEvent, "Focus event fired");
- window.focus();
- };
-
- let blurHandler = function(aEvent) {
- editor.removeEventListener(SourceEditor.EVENTS.BLUR, blurHandler);
-
- ok(aEvent, "Blur event fired");
- executeSoon(testEnd);
- }
-
- editor.addEventListener(SourceEditor.EVENTS.FOCUS, focusHandler);
-
- editor.focus();
-}
-
-function testEnd()
-{
- if (editor) {
- editor.destroy();
- }
- if (testWin) {
- testWin.close();
- }
- testWin = editor = null;
-
- waitForFocus(finish, window);
-}
diff --git a/browser/devtools/sourceeditor/test/browser_bug707987_debugger_breakpoints.js b/browser/devtools/sourceeditor/test/browser_bug707987_debugger_breakpoints.js
deleted file mode 100644
index ad0484063057..000000000000
--- a/browser/devtools/sourceeditor/test/browser_bug707987_debugger_breakpoints.js
+++ /dev/null
@@ -1,169 +0,0 @@
-/* vim: set ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-function test() {
-
- let temp = {};
- Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm", temp);
- let SourceEditor = temp.SourceEditor;
-
- let component = Services.prefs.getCharPref(SourceEditor.PREFS.COMPONENT);
- if (component == "textarea") {
- ok(true, "skip test for bug 707987: only applicable for non-textarea components");
- return;
- }
-
- waitForExplicitFinish();
-
- let editor;
-
- const windowUrl = "data:text/xml," +
- "";
- const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
-
- let testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
- testWin.addEventListener("load", function onWindowLoad() {
- testWin.removeEventListener("load", onWindowLoad, false);
- waitForFocus(initEditor, testWin);
- }, false);
-
- function initEditor()
- {
- let hbox = testWin.document.querySelector("hbox");
- editor = new SourceEditor();
- editor.init(hbox, {showAnnotationRuler: true}, editorLoaded);
- }
-
- function editorLoaded()
- {
- editor.focus();
-
- editor.setText("line1\nline2\nline3\nline4");
-
- is(editor.getBreakpoints().length, 0, "no breakpoints");
-
- let event = null;
- let eventHandler = function(aEvent) {
- event = aEvent;
- };
- editor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, eventHandler);
-
- // Add breakpoint at line 0
-
- editor.addBreakpoint(0);
-
- let breakpoints = editor.getBreakpoints();
- is(breakpoints.length, 1, "one breakpoint added");
- is(breakpoints[0].line, 0, "breakpoint[0].line is correct");
- ok(!breakpoints[0].condition, "breakpoint[0].condition is correct");
-
- ok(event, "breakpoint event fired");
- is(event.added.length, 1, "one breakpoint added (confirmed)");
- is(event.removed.length, 0, "no breakpoint removed");
- is(event.added[0].line, 0, "event added[0].line is correct");
- ok(!event.added[0].condition, "event added[0].condition is correct");
-
- // Add breakpoint at line 3
-
- event = null;
- editor.addBreakpoint(3, "foo == 'bar'");
-
- breakpoints = editor.getBreakpoints();
- is(breakpoints.length, 2, "another breakpoint added");
- is(breakpoints[0].line, 0, "breakpoint[0].line is correct");
- ok(!breakpoints[0].condition, "breakpoint[0].condition is correct");
- is(breakpoints[1].line, 3, "breakpoint[1].line is correct");
- is(breakpoints[1].condition, "foo == 'bar'",
- "breakpoint[1].condition is correct");
-
- ok(event, "breakpoint event fired");
- is(event.added.length, 1, "another breakpoint added (confirmed)");
- is(event.removed.length, 0, "no breakpoint removed");
- is(event.added[0].line, 3, "event added[0].line is correct");
- is(event.added[0].condition, "foo == 'bar'",
- "event added[0].condition is correct");
-
- // Try to add another breakpoint at line 0
-
- event = null;
- editor.addBreakpoint(0);
-
- is(editor.getBreakpoints().length, 2, "no breakpoint added");
- is(event, null, "no breakpoint event fired");
-
- // Try to remove a breakpoint from line 1
-
- is(editor.removeBreakpoint(1), false, "removeBreakpoint(1) returns false");
- is(editor.getBreakpoints().length, 2, "no breakpoint removed");
- is(event, null, "no breakpoint event fired");
-
- // Remove the breakpoint from line 0
-
- is(editor.removeBreakpoint(0), true, "removeBreakpoint(0) returns true");
-
- breakpoints = editor.getBreakpoints();
- is(breakpoints[0].line, 3, "breakpoint[0].line is correct");
- is(breakpoints[0].condition, "foo == 'bar'",
- "breakpoint[0].condition is correct");
-
- ok(event, "breakpoint event fired");
- is(event.added.length, 0, "no breakpoint added");
- is(event.removed.length, 1, "one breakpoint removed");
- is(event.removed[0].line, 0, "event removed[0].line is correct");
- ok(!event.removed[0].condition, "event removed[0].condition is correct");
-
- // Remove the breakpoint from line 3
-
- event = null;
- is(editor.removeBreakpoint(3), true, "removeBreakpoint(3) returns true");
-
- is(editor.getBreakpoints().length, 0, "no breakpoints");
- ok(event, "breakpoint event fired");
- is(event.added.length, 0, "no breakpoint added");
- is(event.removed.length, 1, "one breakpoint removed");
- is(event.removed[0].line, 3, "event removed[0].line is correct");
- is(event.removed[0].condition, "foo == 'bar'",
- "event removed[0].condition is correct");
-
- // Add a breakpoint with the mouse
-
- event = null;
- EventUtils.synthesizeMouse(editor.editorElement, 10, 10, {}, testWin);
-
- breakpoints = editor.getBreakpoints();
- is(breakpoints.length, 1, "one breakpoint added");
- is(breakpoints[0].line, 0, "breakpoint[0].line is correct");
- ok(!breakpoints[0].condition, "breakpoint[0].condition is correct");
-
- ok(event, "breakpoint event fired");
- is(event.added.length, 1, "one breakpoint added (confirmed)");
- is(event.removed.length, 0, "no breakpoint removed");
- is(event.added[0].line, 0, "event added[0].line is correct");
- ok(!event.added[0].condition, "event added[0].condition is correct");
-
- // Remove a breakpoint with the mouse
-
- event = null;
- EventUtils.synthesizeMouse(editor.editorElement, 10, 10, {}, testWin);
-
- breakpoints = editor.getBreakpoints();
- is(breakpoints.length, 0, "one breakpoint removed");
-
- ok(event, "breakpoint event fired");
- is(event.added.length, 0, "no breakpoint added");
- is(event.removed.length, 1, "one breakpoint removed (confirmed)");
- is(event.removed[0].line, 0, "event removed[0].line is correct");
- ok(!event.removed[0].condition, "event removed[0].condition is correct");
-
- editor.destroy();
-
- testWin.close();
- testWin = editor = null;
-
- waitForFocus(finish, window);
- }
-}
diff --git a/browser/devtools/sourceeditor/test/browser_bug712982_line_ruler_click.js b/browser/devtools/sourceeditor/test/browser_bug712982_line_ruler_click.js
deleted file mode 100644
index a69c7dc0a96d..000000000000
--- a/browser/devtools/sourceeditor/test/browser_bug712982_line_ruler_click.js
+++ /dev/null
@@ -1,74 +0,0 @@
-/* vim: set ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-function test() {
-
- let temp = {};
- Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm", temp);
- let SourceEditor = temp.SourceEditor;
-
- let component = Services.prefs.getCharPref(SourceEditor.PREFS.COMPONENT);
- if (component == "textarea") {
- ok(true, "skip test for bug 712982: only applicable for non-textarea components");
- return;
- }
-
- waitForExplicitFinish();
-
- let editor;
-
- const windowUrl = "data:text/xml," +
- "";
- const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
-
- let testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
- testWin.addEventListener("load", function onWindowLoad() {
- testWin.removeEventListener("load", onWindowLoad, false);
- waitForFocus(initEditor, testWin);
- }, false);
-
- function initEditor()
- {
- let hbox = testWin.document.querySelector("hbox");
- editor = new SourceEditor();
- editor.init(hbox, {showLineNumbers: true}, editorLoaded);
- }
-
- function editorLoaded()
- {
- editor.focus();
-
- editor.setText("line1\nline2\nline3\nline4");
-
- editor.setCaretPosition(3);
- let pos = editor.getCaretPosition();
- ok(pos.line == 3 && pos.col == 0, "initial caret location is correct");
-
- EventUtils.synthesizeMouse(editor.editorElement, 10, 10, {}, testWin);
-
- is(editor.getCaretOffset(), 0, "click on line 0 worked");
-
- editor.setCaretPosition(2);
- EventUtils.synthesizeMouse(editor.editorElement, 11, 11,
- {shiftKey: true}, testWin);
- is(editor.getSelectedText().trim(), "line1\nline2", "shift+click works");
-
- editor.setCaretOffset(0);
-
- EventUtils.synthesizeMouse(editor.editorElement, 10, 10,
- {clickCount: 2}, testWin);
-
- is(editor.getSelectedText().trim(), "line1", "double click works");
-
- editor.destroy();
-
- testWin.close();
- testWin = editor = null;
-
- waitForFocus(finish, window);
- }
-}
diff --git a/browser/devtools/sourceeditor/test/browser_bug725388_mouse_events.js b/browser/devtools/sourceeditor/test/browser_bug725388_mouse_events.js
deleted file mode 100644
index 97ac2d926552..000000000000
--- a/browser/devtools/sourceeditor/test/browser_bug725388_mouse_events.js
+++ /dev/null
@@ -1,107 +0,0 @@
-/* vim: set ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-let tempScope = {};
-Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm", tempScope);
-let SourceEditor = tempScope.SourceEditor;
-
-let testWin;
-let editor;
-
-function test()
-{
- waitForExplicitFinish();
-
- const windowUrl = "data:text/xml," +
- "";
- const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
-
- testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
- testWin.addEventListener("load", function onWindowLoad() {
- testWin.removeEventListener("load", onWindowLoad, false);
- waitForFocus(initEditor, testWin);
- }, false);
-}
-
-function initEditor()
-{
- let hbox = testWin.document.querySelector("hbox");
-
- editor = new SourceEditor();
- editor.init(hbox, {}, editorLoaded);
-}
-
-function editorLoaded()
-{
- editor.focus();
- testWin.resizeBy(1, 2);
-
- let text = "BrowserBug - 725388";
- editor.setText(text);
-
- let target = editor.editorElement;
- let targetWin = target.ownerDocument.defaultView;
-
- let eventsFired = 0;
-
- let done = function() {
- eventsFired++;
- if (eventsFired == 3) {
- executeSoon(testEnd);
- }
- };
-
- let mMoveHandler = function(aEvent) {
- editor.removeEventListener(SourceEditor.EVENTS.MOUSE_MOVE, mMoveHandler);
-
- is(aEvent.event.type, "mousemove", "MouseMove event fired.");
-
- executeSoon(done);
- };
-
- let mOverHandler = function(aEvent) {
- editor.removeEventListener(SourceEditor.EVENTS.MOUSE_OVER, mOverHandler);
-
- is(aEvent.event.type, "mouseover", "MouseOver event fired.");
-
- executeSoon(done);
- };
-
- let mOutHandler = function(aEvent) {
- editor.removeEventListener(SourceEditor.EVENTS.MOUSE_OUT, mOutHandler);
-
- is(aEvent.event.type, "mouseout", "MouseOut event fired.");
-
- executeSoon(done);
- };
-
- editor.addEventListener(SourceEditor.EVENTS.MOUSE_OVER, mOverHandler);
- editor.addEventListener(SourceEditor.EVENTS.MOUSE_MOVE, mMoveHandler);
- editor.addEventListener(SourceEditor.EVENTS.MOUSE_OUT, mOutHandler);
-
- waitForFocus(function() {
- EventUtils.synthesizeMouse(target, 10, 10, {type: "mouseover"},
- targetWin);
- EventUtils.synthesizeMouse(target, 15, 17, {type: "mousemove"},
- targetWin);
- EventUtils.synthesizeMouse(target, -10, -10, {type: "mouseout"},
- targetWin);
- }, targetWin);
-}
-
-function testEnd()
-{
- if (editor) {
- editor.destroy();
- }
- if (testWin) {
- testWin.close();
- }
- testWin = editor = null;
-
- waitForFocus(finish, window);
-}
diff --git a/browser/devtools/sourceeditor/test/browser_bug725392_mouse_coords_char_offset.js b/browser/devtools/sourceeditor/test/browser_bug725392_mouse_coords_char_offset.js
deleted file mode 100644
index 5d5dcff5fdb2..000000000000
--- a/browser/devtools/sourceeditor/test/browser_bug725392_mouse_coords_char_offset.js
+++ /dev/null
@@ -1,160 +0,0 @@
-/* vim: set ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-function test()
-{
- let testWin;
- let editor;
- let mousePos = { x: 36, y: 4 };
- let expectedOffset = 5;
- let maxDiff = 10;
-
- waitForExplicitFinish();
-
- function editorLoaded(aEditor, aWindow)
- {
- editor = aEditor;
- testWin = aWindow;
-
- let text = fillEditor(editor, 3);
- editor.setText(text);
- editor.setCaretOffset(0);
-
- doMouseMove(testPage1);
- }
-
- function doMouseMove(aCallback)
- {
- function mouseEventHandler(aEvent)
- {
- editor.removeEventListener(editor.EVENTS.MOUSE_OUT, mouseEventHandler);
- editor.removeEventListener(editor.EVENTS.MOUSE_OVER, mouseEventHandler);
- editor.removeEventListener(editor.EVENTS.MOUSE_MOVE, mouseEventHandler);
-
- executeSoon(aCallback.bind(null, aEvent));
- }
-
- editor.addEventListener(editor.EVENTS.MOUSE_MOVE, mouseEventHandler);
- editor.addEventListener(editor.EVENTS.MOUSE_OUT, mouseEventHandler);
- editor.addEventListener(editor.EVENTS.MOUSE_OVER, mouseEventHandler);
-
- let target = editor.editorElement;
- let targetWin = testWin;
-
- EventUtils.synthesizeMouse(target, mousePos.x, mousePos.y,
- {type: "mousemove"}, targetWin);
- EventUtils.synthesizeMouse(target, mousePos.x, mousePos.y,
- {type: "mouseout"}, targetWin);
- EventUtils.synthesizeMouse(target, mousePos.x, mousePos.y,
- {type: "mouseover"}, targetWin);
- }
-
- function checkValue(aValue, aExpectedValue)
- {
- let result = Math.abs(aValue - aExpectedValue) <= maxDiff;
- if (!result) {
- info("checkValue() given " + aValue + " expected " + aExpectedValue);
- }
- return result;
- }
-
- function testPage1(aEvent)
- {
- let {event: { clientX: clientX, clientY: clientY }, x: x, y: y} = aEvent;
-
- info("testPage1 " + aEvent.type +
- " clientX " + clientX + " clientY " + clientY +
- " x " + x + " y " + y);
-
- // x and y are in document coordinates.
- // clientX and clientY are in view coordinates.
- // since we are scrolled at the top, both are expected to be approximately
- // the same.
- ok(checkValue(x, mousePos.x), "x is in range");
- ok(checkValue(y, mousePos.y), "y is in range");
-
- ok(checkValue(clientX, mousePos.x), "clientX is in range");
- ok(checkValue(clientY, mousePos.y), "clientY is in range");
-
- // we give document-relative coordinates here.
- let offset = editor.getOffsetAtLocation(x, y);
- ok(checkValue(offset, expectedOffset), "character offset is correct");
-
- let rect = {x: x, y: y};
- let viewCoords = editor.convertCoordinates(rect, "document", "view");
- ok(checkValue(viewCoords.x, clientX), "viewCoords.x is in range");
- ok(checkValue(viewCoords.y, clientY), "viewCoords.y is in range");
-
- rect = {x: clientX, y: clientY};
- let docCoords = editor.convertCoordinates(rect, "view", "document");
- ok(checkValue(docCoords.x, x), "docCoords.x is in range");
- ok(checkValue(docCoords.y, y), "docCoords.y is in range");
-
- // we are given document-relative coordinates.
- let offsetPos = editor.getLocationAtOffset(expectedOffset);
- ok(checkValue(offsetPos.x, x), "offsetPos.x is in range");
- ok(checkValue(offsetPos.y, y), "offsetPos.y is in range");
-
- // Scroll the view and test again.
- let topIndex = Math.round(editor.getLineCount() / 2);
- editor.setTopIndex(topIndex);
- expectedOffset += editor.getLineStart(topIndex);
-
- executeSoon(doMouseMove.bind(null, testPage2));
- }
-
- function testPage2(aEvent)
- {
- let {event: { clientX: clientX, clientY: clientY }, x: x, y: y} = aEvent;
-
- info("testPage2 " + aEvent.type +
- " clientX " + clientX + " clientY " + clientY +
- " x " + x + " y " + y);
-
- // after page scroll document coordinates need to be different from view
- // coordinates.
- ok(checkValue(x, mousePos.x), "x is not different from clientX");
- ok(!checkValue(y, mousePos.y), "y is different from clientY");
-
- ok(checkValue(clientX, mousePos.x), "clientX is in range");
- ok(checkValue(clientY, mousePos.y), "clientY is in range");
-
- // we give document-relative coordinates here.
- let offset = editor.getOffsetAtLocation(x, y);
- ok(checkValue(offset, expectedOffset), "character offset is correct");
-
- let rect = {x: x, y: y};
- let viewCoords = editor.convertCoordinates(rect, "document", "view");
- ok(checkValue(viewCoords.x, clientX), "viewCoords.x is in range");
- ok(checkValue(viewCoords.y, clientY), "viewCoords.y is in range");
-
- rect = {x: clientX, y: clientY};
- let docCoords = editor.convertCoordinates(rect, "view", "document");
- ok(checkValue(docCoords.x, x), "docCoords.x is in range");
- ok(checkValue(docCoords.y, y), "docCoords.y is in range");
-
- // we are given document-relative coordinates.
- let offsetPos = editor.getLocationAtOffset(expectedOffset);
- ok(checkValue(offsetPos.x, x), "offsetPos.x is in range");
- ok(checkValue(offsetPos.y, y), "offsetPos.y is in range");
-
- executeSoon(testEnd);
- }
-
- function testEnd()
- {
- if (editor) {
- editor.destroy();
- }
- if (testWin) {
- testWin.close();
- }
-
- waitForFocus(finish, window);
- }
-
- openSourceEditorWindow(editorLoaded);
-}
diff --git a/browser/devtools/sourceeditor/test/browser_bug725430_comment_uncomment.js b/browser/devtools/sourceeditor/test/browser_bug725430_comment_uncomment.js
deleted file mode 100644
index e43f211a5762..000000000000
--- a/browser/devtools/sourceeditor/test/browser_bug725430_comment_uncomment.js
+++ /dev/null
@@ -1,151 +0,0 @@
-/* vim: set ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-function test() {
-
- let temp = {};
- Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm", temp);
- let SourceEditor = temp.SourceEditor;
-
- waitForExplicitFinish();
-
- let editor;
-
- const windowUrl = "data:text/xml," +
- "";
- const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
-
- let testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
- testWin.addEventListener("load", function onWindowLoad() {
- testWin.removeEventListener("load", onWindowLoad, false);
- waitForFocus(initEditor, testWin);
- }, false);
-
- function initEditor()
- {
- let hbox = testWin.document.querySelector("hbox");
- editor = new SourceEditor();
- editor.init(hbox, {showLineNumbers: true}, editorLoaded);
- }
-
- function editorLoaded()
- {
- editor.focus();
- let text = "firstline\nsecondline\nthirdline\nfourthline";
-
- editor.setMode(SourceEditor.MODES.JAVASCRIPT);
- editor.setText(text)
-
- editor.setCaretPosition(0);
- EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
- is(editor.getText(), "//" + text, "JS Single line Commenting Works");
- editor.undo();
- is(editor.getText(), text, "Undo Single Line Commenting action works");
- editor.redo();
- is(editor.getText(), "//" + text, "Redo works");
- editor.setCaretPosition(0);
- EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
- is(editor.getText(), text, "JS Single Line Uncommenting works");
-
- editor.setText(text);
-
- EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin);
- EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
- is(editor.getText(), "/*" + text + "*/", "JS Block Commenting works");
- editor.undo();
- is(editor.getText(), text, "Undo Block Commenting action works");
- editor.redo();
- is(editor.getText(), "/*" + text + "*/", "Redo works");
- EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin);
- EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
- is(editor.getText(), text, "JS Block Uncommenting works");
- editor.undo();
- is(editor.getText(), "/*" + text + "*/", "Undo Block Uncommenting works");
- editor.redo();
- is(editor.getText(), text, "Redo works");
-
- let regText = "//firstline\n // secondline\nthird//line\n//fourthline";
- let expText = "firstline\n secondline\nthird//line\nfourthline";
- editor.setText(regText);
- EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin);
- EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
- is(editor.getText(), expText, "JS Multiple Line Uncommenting works");
- editor.undo();
- is(editor.getText(), regText, "Undo Multiple Line Uncommenting works");
- editor.redo();
- is(editor.getText(), expText, "Redo works");
-
- editor.setMode(SourceEditor.MODES.CSS);
- editor.setText(text);
-
- expText = "/*firstline*/\nsecondline\nthirdline\nfourthline";
- editor.setCaretPosition(0);
- EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
- is(editor.getText(), expText, "CSS Commenting without selection works");
- editor.setCaretPosition(0);
- EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
- is(editor.getText(), text, "CSS Uncommenting without selection works");
-
- editor.setText(text);
-
- EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin);
- EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
- is(editor.getText(), "/*" + text + "*/", "CSS Multiple Line Commenting works");
- EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin);
- EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
- is(editor.getText(), text, "CSS Multiple Line Uncommenting works");
-
- editor.setMode(SourceEditor.MODES.HTML);
- editor.setText(text);
-
- expText = "\nsecondline\nthirdline\nfourthline";
- editor.setCaretPosition(0);
- EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
- is(editor.getText(), expText, "HTML Commenting without selection works");
- editor.setCaretPosition(0);
- EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
- is(editor.getText(), text, "HTML Uncommenting without selection works");
-
- editor.setText(text);
-
- EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin);
- EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
- is(editor.getText(), "", "HTML Multiple Line Commenting works");
- EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin);
- EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
- is(editor.getText(), text, "HTML Multiple Line Uncommenting works");
-
- editor.setMode(SourceEditor.MODES.TEXT);
- editor.setText(text);
-
- editor.setCaretPosition(0);
- EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
- is(editor.getText(), text, "Commenting disabled in Text mode");
- editor.setText(regText);
- EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin);
- EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
- is(editor.getText(), regText, "Uncommenting disabled in Text mode");
-
- editor.setText(text);
- editor.readOnly = true;
-
- editor.setCaretPosition(0);
- EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
- is(editor.getText(), text, "Commenting disabled in ReadOnly mode");
- editor.setText(regText);
- EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin);
- EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
- is(editor.getText(), regText, "Uncommenting disabled in ReadOnly mode");
-
- editor.destroy();
-
- testWin.close();
- testWin = editor = null;
-
- waitForFocus(finish, window);
- }
-}
diff --git a/browser/devtools/sourceeditor/test/browser_bug725618_moveLines_shortcut.js b/browser/devtools/sourceeditor/test/browser_bug725618_moveLines_shortcut.js
deleted file mode 100644
index b3e5cc203747..000000000000
--- a/browser/devtools/sourceeditor/test/browser_bug725618_moveLines_shortcut.js
+++ /dev/null
@@ -1,117 +0,0 @@
-/* vim: set ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-let tempScope = {};
-Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm", tempScope);
-let SourceEditor = tempScope.SourceEditor;
-
-let editor;
-let testWin;
-
-function test()
-{
- waitForExplicitFinish();
-
- const windowUrl = "data:application/vnd.mozilla.xul+xml," +
- "" +
- "";
- const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
-
- testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
- testWin.addEventListener("load", function onWindowLoad() {
- testWin.removeEventListener("load", onWindowLoad, false);
- waitForFocus(initEditor, testWin);
- }, false);
-}
-
-function initEditor()
-{
- let box = testWin.document.querySelector("box");
-
- let text = "target\nfoo\nbar"
- let config = {
- initialText: text,
- };
-
- editor = new SourceEditor();
- editor.init(box, config, editorLoaded);
-}
-
-function editorLoaded()
-{
- editor.focus();
-
- editor.setCaretOffset(0);
-
- let modifiers = {altKey: true, ctrlKey: Services.appinfo.OS == "Darwin"};
-
- EventUtils.synthesizeKey("VK_DOWN", modifiers, testWin);
- is(editor.getText(), "foo\ntarget\nbar", "Move lines down works");
- is(editor.getSelectedText(), "target\n", "selection is correct");
-
- EventUtils.synthesizeKey("VK_DOWN", modifiers, testWin);
- is(editor.getText(), "foo\nbar\ntarget", "Move lines down works");
- is(editor.getSelectedText(), "target", "selection is correct");
-
- EventUtils.synthesizeKey("VK_DOWN", modifiers, testWin);
- is(editor.getText(), "foo\nbar\ntarget", "Check for bottom of editor works");
- is(editor.getSelectedText(), "target", "selection is correct");
-
- EventUtils.synthesizeKey("VK_UP", modifiers, testWin);
- is(editor.getText(), "foo\ntarget\nbar", "Move lines up works");
- is(editor.getSelectedText(), "target\n", "selection is correct");
-
- EventUtils.synthesizeKey("VK_UP", modifiers, testWin);
- is(editor.getText(), "target\nfoo\nbar", "Move lines up works");
- is(editor.getSelectedText(), "target\n", "selection is correct");
-
- EventUtils.synthesizeKey("VK_UP", modifiers, testWin);
- is(editor.getText(), "target\nfoo\nbar", "Check for top of editor works");
- is(editor.getSelectedText(), "target\n", "selection is correct");
-
- editor.setSelection(0, 10);
- info("text within selection =" + editor.getSelectedText());
-
- EventUtils.synthesizeKey("VK_DOWN", modifiers, testWin);
- is(editor.getText(), "bar\ntarget\nfoo", "Multiple line move down works");
- is(editor.getSelectedText(), "target\nfoo", "selection is correct");
-
- EventUtils.synthesizeKey("VK_DOWN", modifiers, testWin);
- is(editor.getText(), "bar\ntarget\nfoo",
- "Check for bottom of editor works with multiple line selection");
- is(editor.getSelectedText(), "target\nfoo", "selection is correct");
-
- EventUtils.synthesizeKey("VK_UP", modifiers, testWin);
- is(editor.getText(), "target\nfoo\nbar", "Multiple line move up works");
- is(editor.getSelectedText(), "target\nfoo\n", "selection is correct");
-
- EventUtils.synthesizeKey("VK_UP", modifiers, testWin);
- is(editor.getText(), "target\nfoo\nbar",
- "Check for top of editor works with multiple line selection");
- is(editor.getSelectedText(), "target\nfoo\n", "selection is correct");
-
- editor.readOnly = true;
-
- editor.setCaretOffset(0);
-
- EventUtils.synthesizeKey("VK_UP", modifiers, testWin);
- is(editor.getText(), "target\nfoo\nbar",
- "Check for readOnly mode works with move lines up");
-
- EventUtils.synthesizeKey("VK_DOWN", modifiers, testWin);
- is(editor.getText(), "target\nfoo\nbar",
- "Check for readOnly mode works with move lines down");
-
- finish();
-}
-
-registerCleanupFunction(function()
-{
- editor.destroy();
- testWin.close();
- testWin = editor = null;
-});
diff --git a/browser/devtools/sourceeditor/test/browser_bug729480_line_vertical_align.js b/browser/devtools/sourceeditor/test/browser_bug729480_line_vertical_align.js
deleted file mode 100644
index 577e7430169c..000000000000
--- a/browser/devtools/sourceeditor/test/browser_bug729480_line_vertical_align.js
+++ /dev/null
@@ -1,99 +0,0 @@
-/* vim: set ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-let tempScope = {};
-Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm", tempScope);
-let SourceEditor = tempScope.SourceEditor;
-
-let testWin;
-let editor;
-const VERTICAL_OFFSET = 3;
-
-function test()
-{
- waitForExplicitFinish();
-
- const windowUrl = "data:application/vnd.mozilla.xul+xml," +
- "";
- const windowFeatures = "chrome,titlebar,toolbar,centerscreen,dialog=no";
-
- testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
- testWin.addEventListener("load", function onWindowLoad() {
- testWin.removeEventListener("load", onWindowLoad, false);
- waitForFocus(initEditor, testWin);
- }, false);
-}
-
-function initEditor()
-{
- let box = testWin.document.querySelector("box");
-
- editor = new SourceEditor();
- editor.init(box, {showLineNumbers: true}, editorLoaded);
-}
-
-function editorLoaded()
-{
- editor.focus();
-
- // setting 3 pages of lines containing the line number.
- let view = editor._view;
-
- let lineHeight = view.getLineHeight();
- let editorHeight = view.getClientArea().height;
- let linesPerPage = Math.floor(editorHeight / lineHeight);
- let totalLines = 3 * linesPerPage;
-
- let text = "";
- for (let i = 0; i < totalLines; i++) {
- text += "Line " + i + "\n";
- }
-
- editor.setText(text);
- editor.setCaretOffset(0);
-
- let offset = Math.min(Math.round(linesPerPage/2), VERTICAL_OFFSET);
- // Building the iterator array.
- // [line, alignment, topIndex_check]
- let iterateOn = [
- [0, "TOP", 0],
- [25, "TOP", 25 - offset],
- // Case when the target line is already in view.
- [27, "TOP", 25 - offset],
- [0, "BOTTOM", 0],
- [5, "BOTTOM", 0],
- [38, "BOTTOM", 38 - linesPerPage + offset],
- [0, "CENTER", 0],
- [4, "CENTER", 0],
- [34, "CENTER", 34 - Math.round(linesPerPage/2)]
- ];
-
- function testEnd() {
- editor.destroy();
- testWin.close();
- testWin = editor = null;
- waitForFocus(finish, window);
- }
-
- function testPosition(pos) {
- is(editor.getTopIndex(), iterateOn[pos][2], "scroll is correct for test #" + pos);
- iterator(++pos);
- }
-
- function iterator(i) {
- if (i == iterateOn.length) {
- testEnd();
- } else {
- editor.setCaretPosition(iterateOn[i][0], 0,
- editor.VERTICAL_ALIGN[iterateOn[i][1]]);
- executeSoon(testPosition.bind(this, i));
- }
- }
- iterator(0);
-}
diff --git a/browser/devtools/sourceeditor/test/browser_bug729960_block_bracket_jump.js b/browser/devtools/sourceeditor/test/browser_bug729960_block_bracket_jump.js
deleted file mode 100644
index e77f4917bc75..000000000000
--- a/browser/devtools/sourceeditor/test/browser_bug729960_block_bracket_jump.js
+++ /dev/null
@@ -1,164 +0,0 @@
-/* vim: set ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-function test() {
-
- let temp = {};
- Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm", temp);
- let SourceEditor = temp.SourceEditor;
-
- waitForExplicitFinish();
-
- let editor;
-
- const windowUrl = "data:text/xml," +
- "";
- const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
-
- let testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
- testWin.addEventListener("load", function onWindowLoad() {
- testWin.removeEventListener("load", onWindowLoad, false);
- waitForFocus(initEditor, testWin);
- }, false);
-
- function initEditor()
- {
- let hbox = testWin.document.querySelector("hbox");
- editor = new SourceEditor();
- editor.init(hbox, {showLineNumbers: true}, editorLoaded);
- }
-
- function editorLoaded()
- {
- editor.focus();
- let JSText = "function foo(aVar) {\n" +
- " // Block Level 1\n\n" +
- " function level2() {\n" +
- " let baz = aVar;\n" +
- " // Block Level 2\n" +
- " function level3() {\n" +
- " // Block Level 3\n" +
- " }\n" +
- " }\n" +
- " // Block Level 1" +
- " function bar() { /* Block Level 2 */ }\n" +
- "}";
-
- editor.setMode(SourceEditor.MODES.JAVASCRIPT);
- editor.setText(JSText);
-
- // Setting caret at Line 1 bracket start.
- editor.setCaretOffset(19);
- EventUtils.synthesizeKey("]", {accelKey: true, altKey: true}, testWin);
- is(editor.getCaretOffset(), 220,
- "JS : Jump to closing bracket of the code block when caret at block start");
-
- EventUtils.synthesizeKey("[", {accelKey: true, altKey: true}, testWin);
- is(editor.getCaretOffset(), 20,
- "JS : Jump to opening bracket of the code block when caret at block end");
-
- // Setting caret at Line 10 start.
- editor.setCaretOffset(161);
- EventUtils.synthesizeKey("[", {accelKey: true, altKey: true}, testWin);
- is(editor.getCaretOffset(), 20,
- "JS : Jump to opening bracket of code block when inside the function");
-
- editor.setCaretOffset(161);
- EventUtils.synthesizeKey("]", {accelKey: true, altKey: true}, testWin);
- is(editor.getCaretOffset(), 220,
- "JS : Jump to closing bracket of code block when inside the function");
-
- // Setting caret at Line 6 start.
- editor.setCaretOffset(67);
- EventUtils.synthesizeKey("]", {accelKey: true, altKey: true}, testWin);
- is(editor.getCaretOffset(), 159,
- "JS : Jump to closing bracket in a nested function with caret inside");
-
- editor.setCaretOffset(67);
- EventUtils.synthesizeKey("[", {accelKey: true, altKey: true}, testWin);
- is(editor.getCaretOffset(), 62,
- "JS : Jump to opening bracket in a nested function with caret inside");
-
- let CSSText = "#object {\n" +
- " property: value;\n" +
- " /* comment */\n" +
- "}";
-
- editor.setMode(SourceEditor.MODES.CSS);
- editor.setText(CSSText);
-
- // Setting caret at Line 1 bracket start.
- editor.setCaretOffset(8);
- EventUtils.synthesizeKey("]", {accelKey: true, altKey: true}, testWin);
- is(editor.getCaretOffset(), 45,
- "CSS : Jump to closing bracket of the code block when caret at block start");
-
- EventUtils.synthesizeKey("[", {accelKey: true, altKey: true}, testWin);
- is(editor.getCaretOffset(), 9,
- "CSS : Jump to opening bracket of the code block when caret at block end");
-
- // Setting caret at Line 3 start.
- editor.setCaretOffset(28);
- EventUtils.synthesizeKey("[", {accelKey: true, altKey: true}, testWin);
- is(editor.getCaretOffset(), 9,
- "CSS : Jump to opening bracket of code block when inside the function");
-
- editor.setCaretOffset(28);
- EventUtils.synthesizeKey("]", {accelKey: true, altKey: true}, testWin);
- is(editor.getCaretOffset(), 45,
- "CSS : Jump to closing bracket of code block when inside the function");
-
- let HTMLText = "\n" +
- " \n" +
- " Testing Block Jump\n" +
- " \n" +
- " \n" +
- "";
-
- editor.setMode(SourceEditor.MODES.HTML);
- editor.setText(HTMLText);
-
- // Setting caret at Line 1 end.
- editor.setCaretOffset(6);
- EventUtils.synthesizeKey("]", {accelKey: true, altKey: true}, testWin);
- is(editor.getCaretOffset(), 6,
- "HTML : Jump to block end : Nothing happens in html mode");
-
- // Setting caret at Line 4 end.
- editor.setCaretOffset(64);
- EventUtils.synthesizeKey("[", {accelKey: true, altKey: true}, testWin);
- is(editor.getCaretOffset(), 64,
- "HTML : Jump to block start : Nothing happens in html mode");
-
- let text = "line 1\n" +
- "line 2\n" +
- "line 3\n" +
- "line 4\n";
-
- editor.setMode(SourceEditor.MODES.TEXT);
- editor.setText(text);
-
- // Setting caret at Line 1 start.
- editor.setCaretOffset(0);
- EventUtils.synthesizeKey("]", {accelKey: true, altKey: true}, testWin);
- is(editor.getCaretOffset(), 0,
- "Text : Jump to block end : Nothing happens in text mode");
-
- // Setting caret at Line 4 end.
- editor.setCaretOffset(28);
- EventUtils.synthesizeKey("[", {accelKey: true, altKey: true}, testWin);
- is(editor.getCaretOffset(), 28,
- "Text : Jump to block start : Nothing happens in text mode");
-
- editor.destroy();
-
- testWin.close();
- testWin = editor = null;
-
- waitForFocus(finish, window);
- }
-}
diff --git a/browser/devtools/sourceeditor/test/browser_bug731721_debugger_stepping.js b/browser/devtools/sourceeditor/test/browser_bug731721_debugger_stepping.js
deleted file mode 100644
index 272995511de3..000000000000
--- a/browser/devtools/sourceeditor/test/browser_bug731721_debugger_stepping.js
+++ /dev/null
@@ -1,59 +0,0 @@
-/* vim: set ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-function test() {
-
- let temp = {};
- Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm", temp);
- let SourceEditor = temp.SourceEditor;
-
- waitForExplicitFinish();
-
- let editor;
-
- const windowUrl = "data:text/xml," +
- "";
- const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
-
- let testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
- testWin.addEventListener("load", function onWindowLoad() {
- testWin.removeEventListener("load", onWindowLoad, false);
- waitForFocus(initEditor, testWin);
- }, false);
-
- function initEditor()
- {
- let hbox = testWin.document.querySelector("hbox");
- editor = new SourceEditor();
- editor.init(hbox, {showAnnotationRuler: true}, editorLoaded);
- }
-
- function editorLoaded()
- {
- editor.focus();
-
- editor.setText("line1\nline2\nline3\nline4");
-
- is(editor.getDebugLocation(), -1, "no debugger location");
-
- editor.setDebugLocation(1);
- is(editor.getDebugLocation(), 1, "set debugger location works");
-
- editor.setDebugLocation(3);
- is(editor.getDebugLocation(), 3, "change debugger location works");
-
- editor.setDebugLocation(-1);
- is(editor.getDebugLocation(), -1, "clear debugger location works");
-
- editor.destroy();
-
- testWin.close();
- testWin = editor = null;
-
- waitForFocus(finish, window);
- }
-}
diff --git a/browser/devtools/sourceeditor/test/browser_bug744021_next_prev_bracket_jump.js b/browser/devtools/sourceeditor/test/browser_bug744021_next_prev_bracket_jump.js
deleted file mode 100644
index e845513979b8..000000000000
--- a/browser/devtools/sourceeditor/test/browser_bug744021_next_prev_bracket_jump.js
+++ /dev/null
@@ -1,104 +0,0 @@
-/* vim: set ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-function test() {
-
- let temp = {};
- Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm", temp);
- let SourceEditor = temp.SourceEditor;
-
- waitForExplicitFinish();
-
- let editor;
-
- const windowUrl = "data:text/xml;charset=utf8," +
- "";
- const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable," +
- "dialog=no";
-
- let testWin = Services.ww.openWindow(null, windowUrl, "_blank",
- windowFeatures, null);
- testWin.addEventListener("load", function onWindowLoad() {
- testWin.removeEventListener("load", onWindowLoad, false);
- waitForFocus(initEditor, testWin);
- }, false);
-
- function initEditor()
- {
- let hbox = testWin.document.querySelector("hbox");
- editor = new SourceEditor();
- editor.init(hbox, {showLineNumbers: true}, editorLoaded);
- }
-
- function editorLoaded()
- {
- editor.focus();
- let JSText = "function foo() {\n" +
- " \n" +
- " function level2() {\n" +
- " \n" +
- " function level3() {\n" +
- " \n" +
- " }\n" +
- " }\n" +
- " function bar() { /* Block Level 2 */ }\n" +
- "}\n" +
- "function baz() {\n" +
- " \n" +
- "}";
-
- editor.setMode(SourceEditor.MODES.JAVASCRIPT);
- editor.setText(JSText);
-
- // Setting caret at end of line 11 (function baz() {).
- editor.setCaretOffset(147);
- EventUtils.synthesizeKey("[", {accelKey: true, altKey: true}, testWin);
- is(editor.getCaretOffset(), 16,
- "JS : Jump to opening bracket of previous sibling block when no parent");
-
- EventUtils.synthesizeKey("]", {accelKey: true, altKey: true}, testWin);
- is(editor.getCaretOffset(), 129,
- "JS : Jump to closing bracket of same code block");
-
- EventUtils.synthesizeKey("]", {accelKey: true, altKey: true}, testWin);
- is(editor.getCaretOffset(), 151,
- "JS : Jump to closing bracket of next sibling code block");
-
- let CSSText = "#object1 {\n" +
- " property: value;\n" +
- " /* comment */\n" +
- "}\n" +
- ".class1 {\n" +
- " property: value;\n" +
- "}";
-
- editor.setMode(SourceEditor.MODES.CSS);
- editor.setText(CSSText);
-
- // Setting caret at Line 5 end (.class1 {).
- editor.setCaretOffset(57);
- EventUtils.synthesizeKey("[", {accelKey: true, altKey: true}, testWin);
- is(editor.getCaretOffset(), 10,
- "CSS : Jump to opening bracket of previous sibling code block");
-
- EventUtils.synthesizeKey("]", {accelKey: true, altKey: true}, testWin);
- is(editor.getCaretOffset(), 46,
- "CSS : Jump to closing bracket of same code block");
-
- EventUtils.synthesizeKey("]", {accelKey: true, altKey: true}, testWin);
- is(editor.getCaretOffset(), 77,
- "CSS : Jump to closing bracket of next sibling code block");
-
- editor.destroy();
-
- testWin.close();
- testWin = editor = null;
-
- waitForFocus(finish, window);
- }
-}
diff --git a/browser/devtools/sourceeditor/test/browser_editor_basic.js b/browser/devtools/sourceeditor/test/browser_editor_basic.js
new file mode 100644
index 000000000000..83c8c8e7fc05
--- /dev/null
+++ b/browser/devtools/sourceeditor/test/browser_editor_basic.js
@@ -0,0 +1,51 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function test() {
+ waitForExplicitFinish();
+ setup((ed, win) => {
+ // appendTo
+ let src = win.document.querySelector("iframe").getAttribute("src");
+ ok(~src.indexOf(".CodeMirror"), "correct iframe is there");
+
+ // Language modes
+ is(ed.getMode(), Editor.modes.text, "getMode");
+ ed.setMode(Editor.modes.js);
+ is(ed.getMode(), Editor.modes.js, "setMode");
+
+ // Content
+ is(ed.getText(), "Hello.", "getText");
+ ed.setText("Hi.\nHow are you?");
+ is(ed.getText(), "Hi.\nHow are you?", "setText");
+ is(ed.getText(1), "How are you?", "getText(num)");
+ is(ed.getText(5), "", "getText(num) when num is out of scope");
+
+ ed.replaceText("YOU", { line: 1, ch: 8 }, { line: 1, ch: 11 });
+ is(ed.getText(1), "How are YOU?", "replaceText(str, from, to)");
+ ed.replaceText("you?", { line: 1, ch: 8 });
+ is(ed.getText(1), "How are you?", "replaceText(str, from)");
+ ed.replaceText("Hello.");
+ is(ed.getText(), "Hello.", "replaceText(str)");
+
+ ed.insertText(", sir/madam", { line: 0, ch: 5});
+ is(ed.getText(), "Hello, sir/madam.", "insertText");
+
+ // Add-ons
+ ed.extend({ whoami: () => "Anton", whereami: () => "Mozilla" });
+ is(ed.whoami(), "Anton", "extend/1");
+ is(ed.whereami(), "Mozilla", "extend/2");
+
+ // Line classes
+ ed.setText("Hello!\nHow are you?");
+ ok(!ed.hasLineClass(0, "test"), "no test line class");
+ ed.addLineClass(0, "test");
+ ok(ed.hasLineClass(0, "test"), "test line class is there");
+ ed.removeLineClass(0, "test");
+ ok(!ed.hasLineClass(0, "test"), "test line class is gone");
+
+ teardown(ed, win);
+ });
+}
\ No newline at end of file
diff --git a/browser/devtools/sourceeditor/test/browser_editor_cursor.js b/browser/devtools/sourceeditor/test/browser_editor_cursor.js
new file mode 100644
index 000000000000..9c2f9863e7e5
--- /dev/null
+++ b/browser/devtools/sourceeditor/test/browser_editor_cursor.js
@@ -0,0 +1,36 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function test() {
+ waitForExplicitFinish();
+ setup((ed, win) => {
+ ch(ed.getCursor(), { line: 0, ch: 0 }, "default cursor position is ok");
+ ed.setText("Hello.\nHow are you?");
+
+ ed.setCursor({ line: 1, ch: 5 });
+ ch(ed.getCursor(), { line: 1, ch: 5 }, "setCursor({ line, ch })");
+
+ ch(ed.getPosition(7), { line: 1, ch: 0}, "getPosition(num)");
+ ch(ed.getPosition(7, 1)[0], { line: 1, ch: 0}, "getPosition(num, num)[0]");
+ ch(ed.getPosition(7, 1)[1], { line: 0, ch: 1}, "getPosition(num, num)[1]");
+
+ ch(ed.getOffset({ line: 1, ch: 0 }), 7, "getOffset(num)");
+ ch(ed.getOffset({ line: 1, ch: 0 }, { line: 0, ch: 1 })[0], 7, "getOffset(num, num)[0]");
+ ch(ed.getOffset({ line: 1, ch: 0 }, { line: 0, ch: 1 })[0], 2, "getOffset(num, num)[1]");
+
+ is(ed.getSelection(), "", "nothing is selected");
+ ed.setSelection({ line: 0, ch: 0 }, { line: 0, ch: 5 });
+ is(ed.getSelection(), "Hello", "setSelection");
+
+ ed.extendSelection({ start: 0, length: 5 });
+ is(ed.getSelection(), ".\nHow", "extendSelection");
+
+ ed.dropSelection();
+ is(ed.getSelection(), "", "dropSelection");
+
+ teardown(ed, win);
+ });
+}
\ No newline at end of file
diff --git a/browser/devtools/sourceeditor/test/browser_editor_history.js b/browser/devtools/sourceeditor/test/browser_editor_history.js
new file mode 100644
index 000000000000..032393ab0efa
--- /dev/null
+++ b/browser/devtools/sourceeditor/test/browser_editor_history.js
@@ -0,0 +1,32 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function test() {
+ waitForExplicitFinish();
+ setup((ed, win) => {
+ ok(ed.isClean(), "default isClean");
+ ok(!ed.canUndo(), "default canUndo");
+ ok(!ed.canRedo(), "default canRedo");
+
+ ed.setText("Hello, World!");
+ ok(!ed.isClean(), "isClean");
+ ok(ed.canUndo(), "canUndo");
+ ok(!ed.canRedo(), "canRedo");
+
+ ed.undo();
+ ok(ed.isClean(), "isClean after undo");
+ ok(!ed.canUndo(), "canUndo after undo");
+ ok(ed.canRedo(), "canRedo after undo");
+
+ ed.setText("What's up?");
+ ed.setClean();
+ ok(ed.isClean(), "isClean after setClean");
+ ok(ed.canUndo(), "canUndo after setClean");
+ ok(!ed.canRedo(), "canRedo after setClean");
+
+ teardown(ed, win);
+ });
+}
\ No newline at end of file
diff --git a/browser/devtools/sourceeditor/test/browser_editor_markers.js b/browser/devtools/sourceeditor/test/browser_editor_markers.js
new file mode 100644
index 000000000000..bc6baebfeb19
--- /dev/null
+++ b/browser/devtools/sourceeditor/test/browser_editor_markers.js
@@ -0,0 +1,39 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function test() {
+ waitForExplicitFinish();
+ setup((ed, win) => {
+ ok(!ed.hasMarker(0, "breakpoints", "test"), "default is ok");
+ ed.addMarker(0, "breakpoints", "test");
+ ed.addMarker(0, "breakpoints", "test2");
+ ok(ed.hasMarker(0, "breakpoints", "test"), "addMarker/1");
+ ok(ed.hasMarker(0, "breakpoints", "test2"), "addMarker/2");
+
+ ed.removeMarker(0, "breakpoints", "test");
+ ok(!ed.hasMarker(0, "breakpoints", "test"), "removeMarker/1");
+ ok(ed.hasMarker(0, "breakpoints", "test2"), "removeMarker/2");
+
+ ed.removeAllMarkers("breakpoints");
+ ok(!ed.hasMarker(0, "breakpoints", "test"), "removeAllMarkers/1");
+ ok(!ed.hasMarker(0, "breakpoints", "test2"), "removeAllMarkers/2");
+
+ ed.addMarker(0, "breakpoints", "breakpoint");
+ ed.setMarkerListeners(0, "breakpoints", "breakpoint", {
+ "click": (line, marker, param) => {
+ is(line, 0, "line is ok");
+ is(marker.className, "breakpoint", "marker is ok");
+ ok(param, "click is ok");
+
+ teardown(ed, win);
+ }
+ }, [ true ]);
+
+ const env = win.document.querySelector("iframe").contentWindow;
+ const div = env.document.querySelector("div.breakpoint");
+ div.click();
+ });
+}
\ No newline at end of file
diff --git a/browser/devtools/sourceeditor/test/browser_sourceeditor_initialization.js b/browser/devtools/sourceeditor/test/browser_sourceeditor_initialization.js
deleted file mode 100644
index 33897fe7e4c5..000000000000
--- a/browser/devtools/sourceeditor/test/browser_sourceeditor_initialization.js
+++ /dev/null
@@ -1,499 +0,0 @@
-/* vim: set ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-let tempScope = {};
-Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm", tempScope);
-let SourceEditor = tempScope.SourceEditor;
-
-let testWin;
-let testDoc;
-let editor;
-
-function test()
-{
- waitForExplicitFinish();
-
- const windowUrl = "data:text/xml," +
- "";
- const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
-
- testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
- testWin.addEventListener("load", function onWindowLoad() {
- testWin.removeEventListener("load", onWindowLoad, false);
- waitForFocus(initEditor, testWin);
- }, false);
-}
-
-function initEditor()
-{
- testDoc = testWin.document;
-
- let hbox = testDoc.querySelector("hbox");
-
- editor = new SourceEditor();
- let config = {
- showLineNumbers: true,
- initialText: "foobarbaz",
- tabSize: 7,
- expandTab: true,
- };
-
- editor.init(hbox, config, editorLoaded);
-}
-
-function editorLoaded()
-{
- ok(editor.editorElement, "editor loaded");
-
- is(editor.parentElement, testDoc.querySelector("hbox"),
- "parentElement is correct");
-
- editor.focus();
-
- is(editor.getMode(), SourceEditor.DEFAULTS.mode, "default editor mode");
-
- // Test general editing methods.
-
- ok(!editor.canUndo(), "canUndo() works (nothing to undo), just loaded");
-
- ok(!editor.readOnly, "editor is not read-only");
-
- is(editor.getText(), "foobarbaz", "placeholderText works");
-
- is(editor.getText().length, editor.getCharCount(),
- "getCharCount() is correct");
-
- is(editor.getText(3, 5), "ba", "getText() range works");
-
- editor.setText("source-editor");
-
- is(editor.getText(), "source-editor", "setText() works");
-
- editor.setText("code", 0, 6);
-
- is(editor.getText(), "code-editor", "setText() range works");
-
- ok(editor.canUndo(), "canUndo() works (things to undo)");
- ok(!editor.canRedo(), "canRedo() works (nothing to redo yet)");
-
- editor.undo();
-
- is(editor.getText(), "source-editor", "undo() works");
-
- ok(editor.canRedo(), "canRedo() works (things to redo)");
-
- editor.redo();
-
- is(editor.getText(), "code-editor", "redo() works");
-
- EventUtils.synthesizeKey("VK_Z", {accelKey: true}, testWin);
-
- is(editor.getText(), "source-editor", "Ctrl-Z (undo) works");
-
- EventUtils.synthesizeKey("VK_Z", {accelKey: true, shiftKey: true}, testWin);
-
- is(editor.getText(), "code-editor", "Ctrl-Shift-Z (redo) works");
-
- editor.undo();
-
- EventUtils.synthesizeKey("VK_Y", {accelKey: true}, testWin);
- if (Services.appinfo.OS == "WINNT" ||
- Services.appinfo.OS == "Linux") {
- is(editor.getText(), "code-editor",
- "CTRL+Y does redo on Linux and Windows");
- } else {
- is(editor.getText(), "source-editor",
- "CTRL+Y doesn't redo on machines other than Linux and Windows");
- editor.setText("code-editor");
- }
-
- // Test selection methods.
-
- editor.setSelection(0, 4);
-
- is(editor.getSelectedText(), "code", "getSelectedText() works");
-
- let selection = editor.getSelection();
- ok(selection.start == 0 && selection.end == 4, "getSelection() works");
-
- editor.dropSelection();
-
- selection = editor.getSelection();
- ok(selection.start == 4 && selection.end == 4, "dropSelection() works");
-
- editor.setCaretOffset(7);
- is(editor.getCaretOffset(), 7, "setCaretOffset() works");
-
- // Test grouped changes.
-
- editor.setText("foobar");
-
- editor.startCompoundChange();
-
- editor.setText("foo1");
- editor.setText("foo2");
- editor.setText("foo3");
-
- editor.endCompoundChange();
-
- is(editor.getText(), "foo3", "editor content is correct");
-
- editor.undo();
- is(editor.getText(), "foobar", "compound change undo() works");
-
- editor.redo();
- is(editor.getText(), "foo3", "compound change redo() works");
-
- // Minimal keyboard usage tests.
-
- ok(editor.hasFocus(), "editor has focus");
-
- editor.setText("code-editor");
- editor.setCaretOffset(7);
- EventUtils.synthesizeKey(".", {}, testWin);
-
- is(editor.getText(), "code-ed.itor", "focus() and typing works");
-
- EventUtils.synthesizeKey("a", {}, testWin);
-
- is(editor.getText(), "code-ed.aitor", "typing works");
-
- is(editor.getCaretOffset(), 9, "caret moved");
-
- EventUtils.synthesizeKey("VK_LEFT", {}, testWin);
-
- is(editor.getCaretOffset(), 8, "caret moved to the left");
-
- EventUtils.synthesizeKey(".", {}, testWin);
- EventUtils.synthesizeKey("VK_TAB", {}, testWin);
-
- is(editor.getText(), "code-ed.. aitor", "Tab works");
-
- is(editor.getCaretOffset(), 14, "caret location is correct");
-
- // Test the Tab key.
-
- editor.setText("a\n b\n c");
- editor.setCaretOffset(0);
-
- EventUtils.synthesizeKey("VK_TAB", {}, testWin);
- is(editor.getText(), " a\n b\n c", "Tab works");
-
- // Code editor specific tests. These are not applicable when the textarea
- // fallback is used.
- let component = Services.prefs.getCharPref(SourceEditor.PREFS.COMPONENT);
- if (component != "textarea") {
- editor.setMode(SourceEditor.MODES.JAVASCRIPT);
- is(editor.getMode(), SourceEditor.MODES.JAVASCRIPT, "setMode() works");
-
- editor.setSelection(0, editor.getCharCount() - 1);
- EventUtils.synthesizeKey("VK_TAB", {}, testWin);
- is(editor.getText(), " a\n b\n c", "lines indented");
-
- EventUtils.synthesizeKey("VK_TAB", {shiftKey: true}, testWin);
- is(editor.getText(), " a\n b\n c", "lines outdented (shift-tab)");
-
- testEclipseBug362107();
- testBug687577();
- testBackspaceKey();
- testReturnKey();
- }
-
- // Test the read-only mode.
-
- editor.setText("foofoo");
-
- editor.readOnly = true;
- EventUtils.synthesizeKey("b", {}, testWin);
- is(editor.getText(), "foofoo", "editor is now read-only (keyboard)");
-
- editor.setText("foobar");
- is(editor.getText(), "foobar", "editor allows programmatic changes (setText)");
-
- EventUtils.synthesizeKey("VK_RETURN", {}, testWin);
- is(editor.getText(), "foobar", "Enter key does nothing");
-
- EventUtils.synthesizeKey("VK_TAB", {}, testWin);
- is(editor.getText(), "foobar", "Tab does nothing");
-
- editor.setText(" foobar");
- EventUtils.synthesizeKey("VK_TAB", {shiftKey: true}, testWin);
- is(editor.getText(), " foobar", "Shift+Tab does nothing");
-
- editor.readOnly = false;
-
- editor.setCaretOffset(editor.getCharCount());
- EventUtils.synthesizeKey("-", {}, testWin);
- is(editor.getText(), " foobar-", "editor is now editable again");
-
- // Test the Selection event.
-
- editor.setText("foobarbaz");
-
- editor.setSelection(1, 4);
-
- let event = null;
-
- let eventHandler = function(aEvent) {
- event = aEvent;
- };
- editor.addEventListener(SourceEditor.EVENTS.SELECTION, eventHandler);
-
- editor.setSelection(0, 3);
-
- ok(event, "selection event fired");
- ok(event.oldValue.start == 1 && event.oldValue.end == 4,
- "event.oldValue is correct");
- ok(event.newValue.start == 0 && event.newValue.end == 3,
- "event.newValue is correct");
-
- event = null;
- editor.dropSelection();
-
- ok(event, "selection dropped");
- ok(event.oldValue.start == 0 && event.oldValue.end == 3,
- "event.oldValue is correct");
- ok(event.newValue.start == 3 && event.newValue.end == 3,
- "event.newValue is correct");
-
- event = null;
- EventUtils.synthesizeKey("a", {accelKey: true}, testWin);
-
- ok(event, "select all worked");
- ok(event.oldValue.start == 3 && event.oldValue.end == 3,
- "event.oldValue is correct");
- ok(event.newValue.start == 0 && event.newValue.end == 9,
- "event.newValue is correct");
-
- event = null;
- editor.removeEventListener(SourceEditor.EVENTS.SELECTION, eventHandler);
- editor.dropSelection();
-
- ok(!event, "selection event listener removed");
-
- // Test the TextChanged event.
-
- editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED, eventHandler);
-
- EventUtils.synthesizeKey(".", {}, testWin);
-
- ok(event, "the TextChanged event fired after keypress");
- is(event.start, 9, "event.start is correct");
- is(event.removedCharCount, 0, "event.removedCharCount is correct");
- is(event.addedCharCount, 1, "event.addedCharCount is correct");
-
- editor.setText("line1\nline2\nline3");
- let chars = editor.getText().length;
- event = null;
-
- editor.setText("a\nline4\nline5", chars);
-
- ok(event, "the TextChanged event fired after setText()");
- is(event.start, chars, "event.start is correct");
- is(event.removedCharCount, 0, "event.removedCharCount is correct");
- is(event.addedCharCount, 13, "event.addedCharCount is correct");
-
- event = null;
- editor.setText("line3b\nline4b\nfoo", 12, 24);
-
- ok(event, "the TextChanged event fired after setText() again");
- is(event.start, 12, "event.start is correct");
- is(event.removedCharCount, 12, "event.removedCharCount is correct");
- is(event.addedCharCount, 17, "event.addedCharCount is correct");
-
- editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED, eventHandler);
-
- testClipboardEvents();
-}
-
-function testEnd()
-{
- editor.destroy();
- ok(!editor.parentElement && !editor.editorElement, "destroy() works");
-
- testWin.close();
-
- testWin = testDoc = editor = null;
-
- waitForFocus(finish, window);
-}
-
-function testBackspaceKey()
-{
- editor.setText(" a\n b\n c");
- editor.setCaretOffset(7);
- EventUtils.synthesizeKey("VK_BACK_SPACE", {}, testWin);
- is(editor.getText(), "a\n b\n c", "line outdented (Backspace)");
-
- editor.undo();
-
- editor.setCaretOffset(6);
- EventUtils.synthesizeKey("VK_BACK_SPACE", {}, testWin);
- is(editor.getText(), " a\n b\n c", "backspace one char works");
-}
-
-function testReturnKey()
-{
- editor.setText(" a\n b\n c");
-
- editor.setCaretOffset(8);
- EventUtils.synthesizeKey("VK_RETURN", {}, testWin);
- EventUtils.synthesizeKey("x", {}, testWin);
-
- let lineDelimiter = editor.getLineDelimiter();
- ok(lineDelimiter, "we have the line delimiter");
-
- let indentationString = editor.getIndentationString();
- is(" ", indentationString, "we have an indentation string of 7 spaces");
-
- is(editor.getText(), " a" + lineDelimiter + " x\n b\n c",
- "return maintains indentation");
-
- editor.setCaretOffset(12 + lineDelimiter.length);
- EventUtils.synthesizeKey("z", {}, testWin);
- EventUtils.synthesizeKey("VK_RETURN", {}, testWin);
- EventUtils.synthesizeKey("y", {}, testWin);
- is(editor.getText(), " a" + lineDelimiter +
- " z" + lineDelimiter + " yx\n b\n c",
- "return maintains indentation (again)");
-}
-
-function testClipboardEvents()
-{
- editor.setText("foobar");
-
- let doCut = function() {
- EventUtils.synthesizeKey("a", {accelKey: true}, testWin);
-
- is(editor.getSelectedText(), "foobar", "select all worked");
-
- EventUtils.synthesizeKey("x", {accelKey: true}, testWin);
- };
-
- let onCut = function() {
- ok(!editor.getText(), "cut works");
- editor.setText("test--");
- editor.setCaretOffset(5);
-
- editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED, onPaste1);
- EventUtils.synthesizeKey("v", {accelKey: true}, testWin);
- };
-
- let onPaste1 = function() {
- editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED, onPaste1);
-
- is(editor.getText(), "test-foobar-", "paste works");
-
- executeSoon(waitForClipboard.bind(this, "test", doCopy, onCopy, testEnd));
- };
-
- let doCopy = function() {
- editor.setSelection(0, 4);
- EventUtils.synthesizeKey("c", {accelKey: true}, testWin);
- };
-
- let onCopy = function() {
- editor.setSelection(5, 11);
- editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED, onPaste2);
- EventUtils.synthesizeKey("v", {accelKey: true}, testWin);
- };
-
- let pasteTextChanges = 0;
- let removedCharCount = 0;
- let addedCharCount = 0;
- let onPaste2 = function(aEvent) {
- pasteTextChanges++;
- ok(aEvent && (pasteTextChanges == 1 || pasteTextChanges == 2),
- "event TEXT_CHANGED fired " + pasteTextChanges + " time(s)");
-
- is(aEvent.start, 5, "event.start is correct");
- if (aEvent.removedCharCount) {
- removedCharCount = aEvent.removedCharCount;
- }
- if (aEvent.addedCharCount) {
- addedCharCount = aEvent.addedCharCount;
- }
-
- if (pasteTextChanges == 2 || addedCharCount && removedCharCount) {
- editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED, onPaste2);
- executeSoon(checkPaste2Result);
- }
- };
-
- let checkPaste2Result = function() {
- is(removedCharCount, 6, "event.removedCharCount is correct");
- is(addedCharCount, 4, "event.addedCharCount is correct");
-
- is(editor.getText(), "test-test-", "paste works after copy");
- testEnd();
- };
-
- waitForClipboard("foobar", doCut, onCut, testEnd);
-}
-
-function testEclipseBug362107()
-{
- // Test for Eclipse Bug 362107:
- // https://bugs.eclipse.org/bugs/show_bug.cgi?id=362107
- let OS = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
- if (OS != "Linux") {
- return;
- }
-
- editor.setText("line 1\nline 2\nline 3");
- editor.setCaretOffset(16);
-
- EventUtils.synthesizeKey("VK_UP", {ctrlKey: true}, testWin);
- is(editor.getCaretOffset(), 9, "Ctrl-Up works");
-
- EventUtils.synthesizeKey("VK_UP", {ctrlKey: true}, testWin);
- is(editor.getCaretOffset(), 2, "Ctrl-Up works twice");
-
- EventUtils.synthesizeKey("VK_DOWN", {ctrlKey: true}, testWin);
- is(editor.getCaretOffset(), 9, "Ctrl-Down works");
-
- EventUtils.synthesizeKey("VK_DOWN", {ctrlKey: true}, testWin);
- is(editor.getCaretOffset(), 16, "Ctrl-Down works twice");
-}
-
-function testBug687577()
-{
- // Test for Mozilla Bug 687577:
- // https://bugzilla.mozilla.org/show_bug.cgi?id=687577
- let OS = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
- if (OS != "Linux") {
- return;
- }
-
- editor.setText("line foobar 1\nline foobar 2\nline foobar 3");
- editor.setCaretOffset(39);
-
- EventUtils.synthesizeKey("VK_LEFT", {ctrlKey: true, shiftKey: true}, testWin);
- let selection = editor.getSelection();
- is(selection.start, 33, "select.start after Ctrl-Shift-Left");
- is(selection.end, 39, "select.end after Ctrl-Shift-Left");
-
- EventUtils.synthesizeKey("VK_UP", {ctrlKey: true, shiftKey: true}, testWin);
- selection = editor.getSelection();
- is(selection.start, 14, "select.start after Ctrl-Shift-Up");
- is(selection.end, 39, "select.end after Ctrl-Shift-Up");
-
- EventUtils.synthesizeKey("VK_UP", {ctrlKey: true, shiftKey: true}, testWin);
- selection = editor.getSelection();
- is(selection.start, 0, "select.start after Ctrl-Shift-Up (again)");
- is(selection.end, 39, "select.end after Ctrl-Shift-Up (again)");
-
- EventUtils.synthesizeKey("VK_DOWN", {ctrlKey: true, shiftKey: true}, testWin);
- selection = editor.getSelection();
- is(selection.start, 27, "select.start after Ctrl-Shift-Down");
- is(selection.end, 39, "select.end after Ctrl-Shift-Down");
-
- EventUtils.synthesizeKey("VK_DOWN", {ctrlKey: true, shiftKey: true}, testWin);
- selection = editor.getSelection();
- is(selection.start, 39, "select.start after Ctrl-Shift-Down (again)");
- is(selection.end, 41, "select.end after Ctrl-Shift-Down (again)");
-}
diff --git a/browser/devtools/sourceeditor/test/head.js b/browser/devtools/sourceeditor/test/head.js
index 5e1de2c5fb8d..b0cacc3badb9 100644
--- a/browser/devtools/sourceeditor/test/head.js
+++ b/browser/devtools/sourceeditor/test/head.js
@@ -4,179 +4,43 @@
"use strict";
-function getLoadContext() {
- return window.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebNavigation)
- .QueryInterface(Ci.nsILoadContext);
-}
+const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
+const Editor = require("devtools/sourceeditor/editor");
-/*
- * Polls the X11 primary selection buffer waiting for the expected value. A known
- * value different than the expected value is put on the clipboard first (and
- * also polled for) so we can be sure the value we get isn't just the expected
- * value because it was already in the buffer.
- *
- * @param aExpectedStringOrValidatorFn
- * The string value that is expected to be in the X11 primary selection buffer
- * or a validator function getting clipboard data and returning a bool.
- * @param aSetupFn
- * A function responsible for setting the primary selection buffer to the
- * expected value, called after the known value setting succeeds.
- * @param aSuccessFn
- * A function called when the expected value is found in the primary
- * selection buffer.
- * @param aFailureFn
- * A function called if the expected value isn't found in the primary
- * selection buffer within 5s. It can also be called if the known value
- * can't be found.
- * @param aFlavor [optional] The flavor to look for. Defaults to "text/unicode".
- */
-function waitForSelection(aExpectedStringOrValidatorFn, aSetupFn,
- aSuccessFn, aFailureFn, aFlavor) {
- let requestedFlavor = aFlavor || "text/unicode";
-
- // Build a default validator function for common string input.
- var inputValidatorFn = typeof(aExpectedStringOrValidatorFn) == "string"
- ? function(aData) aData == aExpectedStringOrValidatorFn
- : aExpectedStringOrValidatorFn;
-
- let clipboard = Cc["@mozilla.org/widget/clipboard;1"].
- getService(Ci.nsIClipboard);
-
- // reset for the next use
- function reset() {
- waitForSelection._polls = 0;
- }
-
- function wait(validatorFn, successFn, failureFn, flavor) {
- if (++waitForSelection._polls > 50) {
- // Log the failure.
- ok(false, "Timed out while polling the X11 primary selection buffer.");
- reset();
- failureFn();
- return;
- }
-
- let transferable = Cc["@mozilla.org/widget/transferable;1"].
- createInstance(Ci.nsITransferable);
- transferable.init(getLoadContext());
- transferable.addDataFlavor(requestedFlavor);
-
- clipboard.getData(transferable, clipboard.kSelectionClipboard);
-
- let str = {};
- let strLength = {};
-
- transferable.getTransferData(requestedFlavor, str, strLength);
-
- let data = null;
- if (str.value) {
- let strValue = str.value.QueryInterface(Ci.nsISupportsString);
- data = strValue.data.substring(0, strLength.value / 2);
- }
-
- if (validatorFn(data)) {
- // Don't show the success message when waiting for preExpectedVal
- if (preExpectedVal) {
- preExpectedVal = null;
- } else {
- ok(true, "The X11 primary selection buffer has the correct value");
- }
- reset();
- successFn();
- } else {
- setTimeout(function() wait(validatorFn, successFn, failureFn, flavor), 100);
- }
- }
-
- // First we wait for a known value different from the expected one.
- var preExpectedVal = waitForSelection._monotonicCounter +
- "-waitForSelection-known-value";
-
- let clipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"].
- getService(Ci.nsIClipboardHelper);
- clipboardHelper.copyStringToClipboard(preExpectedVal,
- Ci.nsIClipboard.kSelectionClipboard,
- document);
-
- wait(function(aData) aData == preExpectedVal,
- function() {
- // Call the original setup fn
- aSetupFn();
- wait(inputValidatorFn, aSuccessFn, aFailureFn, requestedFlavor);
- }, aFailureFn, "text/unicode");
-}
-
-waitForSelection._polls = 0;
-waitForSelection.__monotonicCounter = 0;
-waitForSelection.__defineGetter__("_monotonicCounter", function () {
- return waitForSelection.__monotonicCounter++;
-});
-
-/**
- * Open a new window with a source editor inside.
- *
- * @param function aCallback
- * The function you want invoked once the editor is loaded. The function
- * is given two arguments: editor instance and the window object.
- * @param object [aOptions]
- * The options object to pass to the SourceEditor.init() method.
- */
-function openSourceEditorWindow(aCallback, aOptions) {
- const windowUrl = "data:text/xml;charset=UTF-8," +
+function setup(cb) {
+ const opt = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
+ const url = "data:text/xml;charset=UTF-8," +
+ "" +
"";
- const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
+ " title='Editor' width='600' height='500'>";
- let editor = null;
- let testWin = Services.ww.openWindow(null, windowUrl, "_blank",
- windowFeatures, null);
- testWin.addEventListener("load", function onWindowLoad() {
- testWin.removeEventListener("load", onWindowLoad, false);
- waitForFocus(initEditor, testWin);
+ let win = Services.ww.openWindow(null, url, "_blank", opt, null);
+
+ win.addEventListener("load", function onLoad() {
+ win.removeEventListener("load", onLoad, false);
+
+ waitForFocus(function () {
+ let box = win.document.querySelector("box");
+ let editor = new Editor({
+ value: "Hello.",
+ lineNumbers: true,
+ gutters: [ "breakpoints" ]
+ });
+
+ editor.appendTo(box)
+ .then(() => cb(editor, win))
+ .then(null, (err) => ok(false, err.message));
+ }, win);
}, false);
-
- function initEditor()
- {
- let tempScope = {};
- Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm", tempScope);
-
- let box = testWin.document.querySelector("box");
- editor = new tempScope.SourceEditor();
- editor.init(box, aOptions || {}, editorLoaded);
- }
-
- function editorLoaded()
- {
- editor.focus();
- waitForFocus(aCallback.bind(null, editor, testWin), testWin);
- }
}
-/**
- * Get text needed to fill the editor view.
- *
- * @param object aEditor
- * The SourceEditor instance you work with.
- * @param number aPages
- * The number of pages you want filled with lines.
- * @return string
- * The string you can insert into the editor so you fill the desired
- * number of pages.
- */
-function fillEditor(aEditor, aPages) {
- let view = aEditor._view;
- let model = aEditor._model;
-
- let lineHeight = view.getLineHeight();
- let editorHeight = view.getClientArea().height;
- let linesPerPage = Math.floor(editorHeight / lineHeight);
- let totalLines = aPages * linesPerPage;
-
- let text = "";
- for (let i = 0; i < totalLines; i++) {
- text += "l" + i + " lorem ipsum dolor sit amet. lipsum foobaris bazbaz,\n";
- }
-
- return text;
+function ch(exp, act, label) {
+ is(exp.line, act.line, label + " (line)");
+ is(exp.ch, act.ch, label + " (ch)");
}
+
+function teardown(ed, win) {
+ ed.destroy();
+ win.close();
+ finish();
+}
\ No newline at end of file
diff --git a/browser/devtools/styleeditor/StyleSheetEditor.jsm b/browser/devtools/styleeditor/StyleSheetEditor.jsm
index fda93ee28261..8288e8d8e7d0 100644
--- a/browser/devtools/styleeditor/StyleSheetEditor.jsm
+++ b/browser/devtools/styleeditor/StyleSheetEditor.jsm
@@ -210,7 +210,8 @@ StyleSheetEditor.prototype = {
mode: Editor.modes.css,
readOnly: this._state.readOnly,
autoCloseBrackets: "{}()[]",
- extraKeys: this._getKeyBindings()
+ extraKeys: this._getKeyBindings(),
+ contextMenu: "sourceEditorContextMenu"
};
let sourceEditor = new Editor(config);
@@ -380,8 +381,8 @@ StyleSheetEditor.prototype = {
},
/**
- * Retrieve custom key bindings objects as expected by SourceEditor.
- * SourceEditor action names are not displayed to the user.
+ * Retrieve custom key bindings objects as expected by Editor.
+ * Editor action names are not displayed to the user.
*
* @return {array} key binding objects for the source editor
*/
diff --git a/browser/devtools/styleeditor/styleeditor.xul b/browser/devtools/styleeditor/styleeditor.xul
index fb6abf45400c..6b9d7620aba9 100644
--- a/browser/devtools/styleeditor/styleeditor.xul
+++ b/browser/devtools/styleeditor/styleeditor.xul
@@ -5,7 +5,12 @@
%styleEditorDTD;
+
+ %editMenuStrings;
+
+ %sourceEditorStrings;
]>
+
@@ -13,7 +18,7 @@
-
+
@@ -21,28 +26,46 @@
+
+ function goUpdateSourceEditorMenuItems() {
+ goUpdateGlobalEditMenuItems();
+
+ ['cmd_undo', 'cmd_redo', 'cmd_cut', 'cmd_paste',
+ 'cmd_delete', 'cmd_find', 'cmd_findAgain'].forEach(goUpdateCommand);
+ }
+
-
+
-
-
-
-
+
+
+
+
-
+
-
-
+
+
-
+
-
+
+
+
+
+
+
diff --git a/browser/devtools/styleeditor/test/browser_styleeditor_sv_resize.js b/browser/devtools/styleeditor/test/browser_styleeditor_sv_resize.js
index f04753223ded..f05a9a60d5a2 100644
--- a/browser/devtools/styleeditor/test/browser_styleeditor_sv_resize.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_sv_resize.js
@@ -44,7 +44,7 @@ function runTests(aUI)
executeSoon(function () {
is(aEditor.sourceEditor, originalSourceEditor,
- "the editor still references the same SourceEditor instance");
+ "the editor still references the same Editor instance");
let editor = aEditor.sourceEditor;
is(editor.getOffset(editor.getCursor()), 4,
"the caret position has been preserved");
diff --git a/browser/themes/linux/devtools/orion-breakpoint.png b/browser/themes/linux/devtools/editor-breakpoint.png
similarity index 100%
rename from browser/themes/linux/devtools/orion-breakpoint.png
rename to browser/themes/linux/devtools/editor-breakpoint.png
diff --git a/browser/themes/linux/devtools/orion-debug-location.png b/browser/themes/linux/devtools/editor-debug-location.png
similarity index 100%
rename from browser/themes/linux/devtools/orion-debug-location.png
rename to browser/themes/linux/devtools/editor-debug-location.png
diff --git a/browser/themes/linux/devtools/orion-error.png b/browser/themes/linux/devtools/editor-error.png
similarity index 100%
rename from browser/themes/linux/devtools/orion-error.png
rename to browser/themes/linux/devtools/editor-error.png
diff --git a/browser/themes/linux/devtools/orion-container.css b/browser/themes/linux/devtools/orion-container.css
deleted file mode 100644
index cf3568a94223..000000000000
--- a/browser/themes/linux/devtools/orion-container.css
+++ /dev/null
@@ -1,39 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-.viewTooltip {
- display: none; /* TODO: add tooltips support, see bug 721752 */
- font-family: monospace;
- font-size: 13px;
- background-color: InfoBackground;
- color: InfoText;
- padding: 2px;
- border-radius: 4px;
- border: 1px solid black;
- z-index: 100;
- position: fixed;
- overflow: hidden;
- white-space: pre;
-}
-
-.viewTooltip em {
- font-style: normal;
- font-weight: bold;
-}
-
-.annotationHTML {
- cursor: pointer;
- width: 16px;
- height: 16px;
- display: inline-block;
- vertical-align: middle;
- background-position: center;
- background-repeat: no-repeat;
-}
-.annotationHTML.task {
- background-image: url("chrome://browser/skin/devtools/orion-task.png");
-}
-.annotationHTML.breakpoint {
- background-image: url("chrome://browser/skin/devtools/orion-breakpoint.png");
-}
diff --git a/browser/themes/linux/devtools/orion-task.png b/browser/themes/linux/devtools/orion-task.png
deleted file mode 100644
index 42dbc00b655fffeae488cc2241d97407977f6dec..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 413
zcmV;O0b>4%P)0mz
zY8!<5QgO4_uYph>EXV*f><%o$M8obN;J(a9lQEh-2LbmH@C*Pa9B$XfXflRnm^j;f
zlxDYUgK;Xw2~V#;Q*|_Zj)X)vYpR}5W(x(igo0Xv*RSFI^8;SLCimis0}^8_>Lqz_
z{c@DZ5&fug>cYWN+Dro6;sr-{~`n+tHVb1v7)yx;i;N{ha1Q$r5E00000NkvXX
Hu0mjf2D_^v
diff --git a/browser/themes/linux/devtools/orion.css b/browser/themes/linux/devtools/orion.css
deleted file mode 100644
index f51ecceebd8d..000000000000
--- a/browser/themes/linux/devtools/orion.css
+++ /dev/null
@@ -1,199 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-.viewContainer {
- background: hsl(0,0%,89%); /* This will be seen as the continuation of the ruler */
- font-family: monospace;
- font-size: inherit; /* inherit browser's default monospace font size */
-}
-
-.view {
- color: black; /* Default text color */
- background: white; /* Background of the editor */
- padding-left: 4px;
-}
-
-.readonly > .view {
- background: #fdfefd; /* super light green */
-}
-
-.ruler {
- background: hsl(0,0%,89%);
- color: hsl(0,0%,55%);
-}
-.ruler.annotations {
- width: 16px;
- padding-left: 4px;
-}
-.ruler.lines {
- border-right: 1px solid #b4c4d3;
- min-width: 1.4em;
- padding-left: 4px;
- padding-right: 4px;
- text-align: end;
-}
-
-.ruler.linesWithAnnotations {
- min-width: 0;
- padding-left: 0;
-}
-
-.ruler.overview {
- border-left: 1px solid #b4c4d3;
- width: 14px;
- text-align: start;
-}
-
-/* Styles for the annotation ruler (first line) */
-.annotationHTML {
- cursor: pointer;
- width: 16px;
- height: 16px;
- display: inline-block;
- vertical-align: middle;
- background-position: center;
- background-repeat: no-repeat;
-}
-.annotation.task .annotationHTML {
- background-image: url("chrome://browser/skin/devtools/orion-task.png");
-}
-.annotation.breakpoint .annotationHTML {
- background-image: url("chrome://browser/skin/devtools/orion-breakpoint.png");
-}
-.annotation.debugLocation .annotationHTML {
- background-image: url("chrome://browser/skin/devtools/orion-debug-location.png");
-}
-.annotation.breakpoint.debugLocation .annotationHTML,
-.annotation.task.debugLocation .annotationHTML {
- background-position: center, center;
- background-repeat: no-repeat, no-repeat;
- background-size: 75%, 100%;
-}
-.annotation.breakpoint.debugLocation .annotationHTML {
- background-image: url("chrome://browser/skin/devtools/orion-debug-location.png"),
- url("chrome://browser/skin/devtools/orion-breakpoint.png");
-}
-
-.annotation.task.debugLocation .annotationHTML {
- background-image: url("chrome://browser/skin/devtools/orion-debug-location.png"),
- url("chrome://browser/skin/devtools/orion-task.png");
-}
-
-/* Styles for the overview ruler */
-.annotationOverview {
- cursor: pointer;
- border-radius: 2px;
- left: 2px;
- width: 8px;
-}
-.annotationOverview.task {
- background-color: lightgreen;
- border: 1px solid green;
-}
-.annotationOverview.breakpoint {
- background-color: lightblue;
- border: 1px solid blue;
-}
-.annotationOverview.debugLocation {
- background-color: white;
- border: 1px solid green;
-}
-.annotationOverview.currentBracket {
- background-color: lightgray;
- border: 1px solid red;
-}
-.annotationOverview.matchingBracket {
- background-color: lightgray;
- border: 1px solid red;
-}
-
-/* Styles for text range */
-.annotationRange {
- background-repeat: repeat-x;
- background-position: left bottom;
-}
-.annotationRange.task {
- outline: 1px dashed rgba(0, 255, 0, 0.5);
-}
-.annotationRange.matchingBracket {
- outline: 1px solid grey;
-}
-
-.token_singleline_comment,
-.token_multiline_comment,
-.token_doc_comment {
- color: hsl(90,2%,50%); /* grey */
-}
-
-.token_doc_html_markup {
- color: #dd0058; /* purple */
-}
-
-.token_doc_tag {
- color: #dd0058; /* purple */
-}
-
-.token_task_tag { /* "TODO" */
- color: black;
- background: yellow;
-}
-
-.token_string {
- color: hsl(72,100%,27%); /* green */
- font-style: italic;
-}
-
-.token_keyword {
- color: hsl(276,44%,45%); /* purple */
-}
-
-.token_space {
- /* images/white_space.png */
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAIAAABv85FHAAAABnRSTlMA/wAAAACkwsAdAAAAIUlEQVR4nGP4z8CAC+GUIEXuABhgkTuABEiRw2cmae4EAH05X7xDolNRAAAAAElFTkSuQmCC");
- background-repeat: no-repeat;
- background-position: center center;
-}
-
-.token_tab {
- /* images/white_tab.png */
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAJCAIAAACJ2loDAAAABnRSTlMA/wD/AP83WBt9AAAAMklEQVR4nGP4TwRgoK6i52c3bz5w6zMSA6tJn28d2Lx589nnCAYu63AaSLxJRLoJPwAAeNk0aG4opfMAAAAASUVORK5CYII=");
- background-repeat: no-repeat;
- background-position: left center;
-}
-
-.line_caret,
-.annotationLine.currentLine { /* Current line */
- background: hsl(208, 93%, 94%);
-}
-
-.readonly .line_caret,
-.readonly .annotationLine.currentLine {
- background: hsl(208, 80%, 90%);
-}
-
-/* Styling for html syntax highlighting */
-.entity-name-tag {
- color: hsl(208,48%,40%); /* blue */
-}
-
-.entity-other-attribute-name {
- color: hsl(208,48%,40%); /* blue */
-}
-
-.punctuation-definition-comment {
- color: hsl(90,2%,50%); /* grey */
-}
-
-.comment {
- color: hsl(90,2%,50%); /* grey */
-}
-
-.string-quoted {
- color: hsl(24,85%,39%); /* orange */
-}
-
-.invalid {
- color: red;
- font-weight: bold;
-}
diff --git a/browser/themes/linux/jar.mn b/browser/themes/linux/jar.mn
index 461e6a88af32..53e98c990d92 100644
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -163,12 +163,9 @@ browser.jar:
skin/classic/browser/devtools/webconsole.png (devtools/webconsole.png)
skin/classic/browser/devtools/commandline.css (devtools/commandline.css)
skin/classic/browser/devtools/markup-view.css (../shared/devtools/markup-view.css)
- skin/classic/browser/devtools/orion.css (devtools/orion.css)
- skin/classic/browser/devtools/orion-container.css (devtools/orion-container.css)
- skin/classic/browser/devtools/orion-task.png (devtools/orion-task.png)
- skin/classic/browser/devtools/orion-error.png (devtools/orion-error.png)
- skin/classic/browser/devtools/orion-breakpoint.png (devtools/orion-breakpoint.png)
- skin/classic/browser/devtools/orion-debug-location.png (devtools/orion-debug-location.png)
+ skin/classic/browser/devtools/editor-error.png (devtools/editor-error.png)
+ skin/classic/browser/devtools/editor-breakpoint.png (devtools/editor-breakpoint.png)
+ skin/classic/browser/devtools/editor-debug-location.png (devtools/editor-debug-location.png)
skin/classic/browser/devtools/breadcrumbs-scrollbutton.png (devtools/breadcrumbs-scrollbutton.png)
skin/classic/browser/devtools/breadcrumbs/ltr-end-pressed.png (devtools/breadcrumbs/ltr-end-pressed.png)
skin/classic/browser/devtools/breadcrumbs/ltr-end-selected-pressed.png (devtools/breadcrumbs/ltr-end-selected-pressed.png)
diff --git a/browser/themes/osx/devtools/orion-breakpoint.png b/browser/themes/osx/devtools/editor-breakpoint.png
similarity index 100%
rename from browser/themes/osx/devtools/orion-breakpoint.png
rename to browser/themes/osx/devtools/editor-breakpoint.png
diff --git a/browser/themes/osx/devtools/orion-debug-location.png b/browser/themes/osx/devtools/editor-debug-location.png
similarity index 100%
rename from browser/themes/osx/devtools/orion-debug-location.png
rename to browser/themes/osx/devtools/editor-debug-location.png
diff --git a/browser/themes/osx/devtools/orion-error.png b/browser/themes/osx/devtools/editor-error.png
similarity index 100%
rename from browser/themes/osx/devtools/orion-error.png
rename to browser/themes/osx/devtools/editor-error.png
diff --git a/browser/themes/osx/devtools/orion-container.css b/browser/themes/osx/devtools/orion-container.css
deleted file mode 100644
index cf3568a94223..000000000000
--- a/browser/themes/osx/devtools/orion-container.css
+++ /dev/null
@@ -1,39 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-.viewTooltip {
- display: none; /* TODO: add tooltips support, see bug 721752 */
- font-family: monospace;
- font-size: 13px;
- background-color: InfoBackground;
- color: InfoText;
- padding: 2px;
- border-radius: 4px;
- border: 1px solid black;
- z-index: 100;
- position: fixed;
- overflow: hidden;
- white-space: pre;
-}
-
-.viewTooltip em {
- font-style: normal;
- font-weight: bold;
-}
-
-.annotationHTML {
- cursor: pointer;
- width: 16px;
- height: 16px;
- display: inline-block;
- vertical-align: middle;
- background-position: center;
- background-repeat: no-repeat;
-}
-.annotationHTML.task {
- background-image: url("chrome://browser/skin/devtools/orion-task.png");
-}
-.annotationHTML.breakpoint {
- background-image: url("chrome://browser/skin/devtools/orion-breakpoint.png");
-}
diff --git a/browser/themes/osx/devtools/orion-task.png b/browser/themes/osx/devtools/orion-task.png
deleted file mode 100644
index 42dbc00b655fffeae488cc2241d97407977f6dec..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 413
zcmV;O0b>4%P)0mz
zY8!<5QgO4_uYph>EXV*f><%o$M8obN;J(a9lQEh-2LbmH@C*Pa9B$XfXflRnm^j;f
zlxDYUgK;Xw2~V#;Q*|_Zj)X)vYpR}5W(x(igo0Xv*RSFI^8;SLCimis0}^8_>Lqz_
z{c@DZ5&fug>cYWN+Dro6;sr-{~`n+tHVb1v7)yx;i;N{ha1Q$r5E00000NkvXX
Hu0mjf2D_^v
diff --git a/browser/themes/osx/devtools/orion.css b/browser/themes/osx/devtools/orion.css
deleted file mode 100644
index f51ecceebd8d..000000000000
--- a/browser/themes/osx/devtools/orion.css
+++ /dev/null
@@ -1,199 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-.viewContainer {
- background: hsl(0,0%,89%); /* This will be seen as the continuation of the ruler */
- font-family: monospace;
- font-size: inherit; /* inherit browser's default monospace font size */
-}
-
-.view {
- color: black; /* Default text color */
- background: white; /* Background of the editor */
- padding-left: 4px;
-}
-
-.readonly > .view {
- background: #fdfefd; /* super light green */
-}
-
-.ruler {
- background: hsl(0,0%,89%);
- color: hsl(0,0%,55%);
-}
-.ruler.annotations {
- width: 16px;
- padding-left: 4px;
-}
-.ruler.lines {
- border-right: 1px solid #b4c4d3;
- min-width: 1.4em;
- padding-left: 4px;
- padding-right: 4px;
- text-align: end;
-}
-
-.ruler.linesWithAnnotations {
- min-width: 0;
- padding-left: 0;
-}
-
-.ruler.overview {
- border-left: 1px solid #b4c4d3;
- width: 14px;
- text-align: start;
-}
-
-/* Styles for the annotation ruler (first line) */
-.annotationHTML {
- cursor: pointer;
- width: 16px;
- height: 16px;
- display: inline-block;
- vertical-align: middle;
- background-position: center;
- background-repeat: no-repeat;
-}
-.annotation.task .annotationHTML {
- background-image: url("chrome://browser/skin/devtools/orion-task.png");
-}
-.annotation.breakpoint .annotationHTML {
- background-image: url("chrome://browser/skin/devtools/orion-breakpoint.png");
-}
-.annotation.debugLocation .annotationHTML {
- background-image: url("chrome://browser/skin/devtools/orion-debug-location.png");
-}
-.annotation.breakpoint.debugLocation .annotationHTML,
-.annotation.task.debugLocation .annotationHTML {
- background-position: center, center;
- background-repeat: no-repeat, no-repeat;
- background-size: 75%, 100%;
-}
-.annotation.breakpoint.debugLocation .annotationHTML {
- background-image: url("chrome://browser/skin/devtools/orion-debug-location.png"),
- url("chrome://browser/skin/devtools/orion-breakpoint.png");
-}
-
-.annotation.task.debugLocation .annotationHTML {
- background-image: url("chrome://browser/skin/devtools/orion-debug-location.png"),
- url("chrome://browser/skin/devtools/orion-task.png");
-}
-
-/* Styles for the overview ruler */
-.annotationOverview {
- cursor: pointer;
- border-radius: 2px;
- left: 2px;
- width: 8px;
-}
-.annotationOverview.task {
- background-color: lightgreen;
- border: 1px solid green;
-}
-.annotationOverview.breakpoint {
- background-color: lightblue;
- border: 1px solid blue;
-}
-.annotationOverview.debugLocation {
- background-color: white;
- border: 1px solid green;
-}
-.annotationOverview.currentBracket {
- background-color: lightgray;
- border: 1px solid red;
-}
-.annotationOverview.matchingBracket {
- background-color: lightgray;
- border: 1px solid red;
-}
-
-/* Styles for text range */
-.annotationRange {
- background-repeat: repeat-x;
- background-position: left bottom;
-}
-.annotationRange.task {
- outline: 1px dashed rgba(0, 255, 0, 0.5);
-}
-.annotationRange.matchingBracket {
- outline: 1px solid grey;
-}
-
-.token_singleline_comment,
-.token_multiline_comment,
-.token_doc_comment {
- color: hsl(90,2%,50%); /* grey */
-}
-
-.token_doc_html_markup {
- color: #dd0058; /* purple */
-}
-
-.token_doc_tag {
- color: #dd0058; /* purple */
-}
-
-.token_task_tag { /* "TODO" */
- color: black;
- background: yellow;
-}
-
-.token_string {
- color: hsl(72,100%,27%); /* green */
- font-style: italic;
-}
-
-.token_keyword {
- color: hsl(276,44%,45%); /* purple */
-}
-
-.token_space {
- /* images/white_space.png */
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAIAAABv85FHAAAABnRSTlMA/wAAAACkwsAdAAAAIUlEQVR4nGP4z8CAC+GUIEXuABhgkTuABEiRw2cmae4EAH05X7xDolNRAAAAAElFTkSuQmCC");
- background-repeat: no-repeat;
- background-position: center center;
-}
-
-.token_tab {
- /* images/white_tab.png */
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAJCAIAAACJ2loDAAAABnRSTlMA/wD/AP83WBt9AAAAMklEQVR4nGP4TwRgoK6i52c3bz5w6zMSA6tJn28d2Lx589nnCAYu63AaSLxJRLoJPwAAeNk0aG4opfMAAAAASUVORK5CYII=");
- background-repeat: no-repeat;
- background-position: left center;
-}
-
-.line_caret,
-.annotationLine.currentLine { /* Current line */
- background: hsl(208, 93%, 94%);
-}
-
-.readonly .line_caret,
-.readonly .annotationLine.currentLine {
- background: hsl(208, 80%, 90%);
-}
-
-/* Styling for html syntax highlighting */
-.entity-name-tag {
- color: hsl(208,48%,40%); /* blue */
-}
-
-.entity-other-attribute-name {
- color: hsl(208,48%,40%); /* blue */
-}
-
-.punctuation-definition-comment {
- color: hsl(90,2%,50%); /* grey */
-}
-
-.comment {
- color: hsl(90,2%,50%); /* grey */
-}
-
-.string-quoted {
- color: hsl(24,85%,39%); /* orange */
-}
-
-.invalid {
- color: red;
- font-weight: bold;
-}
diff --git a/browser/themes/osx/jar.mn b/browser/themes/osx/jar.mn
index 140a20480361..1c93987cb5e6 100644
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -262,12 +262,9 @@ browser.jar:
skin/classic/browser/devtools/ruleview.css (devtools/ruleview.css)
skin/classic/browser/devtools/commandline.css (devtools/commandline.css)
skin/classic/browser/devtools/markup-view.css (../shared/devtools/markup-view.css)
- skin/classic/browser/devtools/orion.css (devtools/orion.css)
- skin/classic/browser/devtools/orion-container.css (devtools/orion-container.css)
- skin/classic/browser/devtools/orion-task.png (devtools/orion-task.png)
- skin/classic/browser/devtools/orion-error.png (devtools/orion-error.png)
- skin/classic/browser/devtools/orion-breakpoint.png (devtools/orion-breakpoint.png)
- skin/classic/browser/devtools/orion-debug-location.png (devtools/orion-debug-location.png)
+ skin/classic/browser/devtools/editor-error.png (devtools/editor-error.png)
+ skin/classic/browser/devtools/editor-breakpoint.png (devtools/editor-breakpoint.png)
+ skin/classic/browser/devtools/editor-debug-location.png (devtools/editor-debug-location.png)
* skin/classic/browser/devtools/webconsole.css (devtools/webconsole.css)
skin/classic/browser/devtools/webconsole_networkpanel.css (devtools/webconsole_networkpanel.css)
skin/classic/browser/devtools/webconsole.png (devtools/webconsole.png)
diff --git a/browser/themes/windows/devtools/orion-breakpoint.png b/browser/themes/windows/devtools/editor-breakpoint.png
similarity index 100%
rename from browser/themes/windows/devtools/orion-breakpoint.png
rename to browser/themes/windows/devtools/editor-breakpoint.png
diff --git a/browser/themes/windows/devtools/orion-debug-location.png b/browser/themes/windows/devtools/editor-debug-location.png
similarity index 100%
rename from browser/themes/windows/devtools/orion-debug-location.png
rename to browser/themes/windows/devtools/editor-debug-location.png
diff --git a/browser/themes/windows/devtools/orion-error.png b/browser/themes/windows/devtools/editor-error.png
similarity index 100%
rename from browser/themes/windows/devtools/orion-error.png
rename to browser/themes/windows/devtools/editor-error.png
diff --git a/browser/themes/windows/devtools/orion-container.css b/browser/themes/windows/devtools/orion-container.css
deleted file mode 100644
index cf3568a94223..000000000000
--- a/browser/themes/windows/devtools/orion-container.css
+++ /dev/null
@@ -1,39 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-.viewTooltip {
- display: none; /* TODO: add tooltips support, see bug 721752 */
- font-family: monospace;
- font-size: 13px;
- background-color: InfoBackground;
- color: InfoText;
- padding: 2px;
- border-radius: 4px;
- border: 1px solid black;
- z-index: 100;
- position: fixed;
- overflow: hidden;
- white-space: pre;
-}
-
-.viewTooltip em {
- font-style: normal;
- font-weight: bold;
-}
-
-.annotationHTML {
- cursor: pointer;
- width: 16px;
- height: 16px;
- display: inline-block;
- vertical-align: middle;
- background-position: center;
- background-repeat: no-repeat;
-}
-.annotationHTML.task {
- background-image: url("chrome://browser/skin/devtools/orion-task.png");
-}
-.annotationHTML.breakpoint {
- background-image: url("chrome://browser/skin/devtools/orion-breakpoint.png");
-}
diff --git a/browser/themes/windows/devtools/orion-task.png b/browser/themes/windows/devtools/orion-task.png
deleted file mode 100644
index 42dbc00b655fffeae488cc2241d97407977f6dec..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 413
zcmV;O0b>4%P)0mz
zY8!<5QgO4_uYph>EXV*f><%o$M8obN;J(a9lQEh-2LbmH@C*Pa9B$XfXflRnm^j;f
zlxDYUgK;Xw2~V#;Q*|_Zj)X)vYpR}5W(x(igo0Xv*RSFI^8;SLCimis0}^8_>Lqz_
z{c@DZ5&fug>cYWN+Dro6;sr-{~`n+tHVb1v7)yx;i;N{ha1Q$r5E00000NkvXX
Hu0mjf2D_^v
diff --git a/browser/themes/windows/devtools/orion.css b/browser/themes/windows/devtools/orion.css
deleted file mode 100644
index f51ecceebd8d..000000000000
--- a/browser/themes/windows/devtools/orion.css
+++ /dev/null
@@ -1,199 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-.viewContainer {
- background: hsl(0,0%,89%); /* This will be seen as the continuation of the ruler */
- font-family: monospace;
- font-size: inherit; /* inherit browser's default monospace font size */
-}
-
-.view {
- color: black; /* Default text color */
- background: white; /* Background of the editor */
- padding-left: 4px;
-}
-
-.readonly > .view {
- background: #fdfefd; /* super light green */
-}
-
-.ruler {
- background: hsl(0,0%,89%);
- color: hsl(0,0%,55%);
-}
-.ruler.annotations {
- width: 16px;
- padding-left: 4px;
-}
-.ruler.lines {
- border-right: 1px solid #b4c4d3;
- min-width: 1.4em;
- padding-left: 4px;
- padding-right: 4px;
- text-align: end;
-}
-
-.ruler.linesWithAnnotations {
- min-width: 0;
- padding-left: 0;
-}
-
-.ruler.overview {
- border-left: 1px solid #b4c4d3;
- width: 14px;
- text-align: start;
-}
-
-/* Styles for the annotation ruler (first line) */
-.annotationHTML {
- cursor: pointer;
- width: 16px;
- height: 16px;
- display: inline-block;
- vertical-align: middle;
- background-position: center;
- background-repeat: no-repeat;
-}
-.annotation.task .annotationHTML {
- background-image: url("chrome://browser/skin/devtools/orion-task.png");
-}
-.annotation.breakpoint .annotationHTML {
- background-image: url("chrome://browser/skin/devtools/orion-breakpoint.png");
-}
-.annotation.debugLocation .annotationHTML {
- background-image: url("chrome://browser/skin/devtools/orion-debug-location.png");
-}
-.annotation.breakpoint.debugLocation .annotationHTML,
-.annotation.task.debugLocation .annotationHTML {
- background-position: center, center;
- background-repeat: no-repeat, no-repeat;
- background-size: 75%, 100%;
-}
-.annotation.breakpoint.debugLocation .annotationHTML {
- background-image: url("chrome://browser/skin/devtools/orion-debug-location.png"),
- url("chrome://browser/skin/devtools/orion-breakpoint.png");
-}
-
-.annotation.task.debugLocation .annotationHTML {
- background-image: url("chrome://browser/skin/devtools/orion-debug-location.png"),
- url("chrome://browser/skin/devtools/orion-task.png");
-}
-
-/* Styles for the overview ruler */
-.annotationOverview {
- cursor: pointer;
- border-radius: 2px;
- left: 2px;
- width: 8px;
-}
-.annotationOverview.task {
- background-color: lightgreen;
- border: 1px solid green;
-}
-.annotationOverview.breakpoint {
- background-color: lightblue;
- border: 1px solid blue;
-}
-.annotationOverview.debugLocation {
- background-color: white;
- border: 1px solid green;
-}
-.annotationOverview.currentBracket {
- background-color: lightgray;
- border: 1px solid red;
-}
-.annotationOverview.matchingBracket {
- background-color: lightgray;
- border: 1px solid red;
-}
-
-/* Styles for text range */
-.annotationRange {
- background-repeat: repeat-x;
- background-position: left bottom;
-}
-.annotationRange.task {
- outline: 1px dashed rgba(0, 255, 0, 0.5);
-}
-.annotationRange.matchingBracket {
- outline: 1px solid grey;
-}
-
-.token_singleline_comment,
-.token_multiline_comment,
-.token_doc_comment {
- color: hsl(90,2%,50%); /* grey */
-}
-
-.token_doc_html_markup {
- color: #dd0058; /* purple */
-}
-
-.token_doc_tag {
- color: #dd0058; /* purple */
-}
-
-.token_task_tag { /* "TODO" */
- color: black;
- background: yellow;
-}
-
-.token_string {
- color: hsl(72,100%,27%); /* green */
- font-style: italic;
-}
-
-.token_keyword {
- color: hsl(276,44%,45%); /* purple */
-}
-
-.token_space {
- /* images/white_space.png */
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAIAAABv85FHAAAABnRSTlMA/wAAAACkwsAdAAAAIUlEQVR4nGP4z8CAC+GUIEXuABhgkTuABEiRw2cmae4EAH05X7xDolNRAAAAAElFTkSuQmCC");
- background-repeat: no-repeat;
- background-position: center center;
-}
-
-.token_tab {
- /* images/white_tab.png */
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAJCAIAAACJ2loDAAAABnRSTlMA/wD/AP83WBt9AAAAMklEQVR4nGP4TwRgoK6i52c3bz5w6zMSA6tJn28d2Lx589nnCAYu63AaSLxJRLoJPwAAeNk0aG4opfMAAAAASUVORK5CYII=");
- background-repeat: no-repeat;
- background-position: left center;
-}
-
-.line_caret,
-.annotationLine.currentLine { /* Current line */
- background: hsl(208, 93%, 94%);
-}
-
-.readonly .line_caret,
-.readonly .annotationLine.currentLine {
- background: hsl(208, 80%, 90%);
-}
-
-/* Styling for html syntax highlighting */
-.entity-name-tag {
- color: hsl(208,48%,40%); /* blue */
-}
-
-.entity-other-attribute-name {
- color: hsl(208,48%,40%); /* blue */
-}
-
-.punctuation-definition-comment {
- color: hsl(90,2%,50%); /* grey */
-}
-
-.comment {
- color: hsl(90,2%,50%); /* grey */
-}
-
-.string-quoted {
- color: hsl(24,85%,39%); /* orange */
-}
-
-.invalid {
- color: red;
- font-weight: bold;
-}
diff --git a/browser/themes/windows/jar.mn b/browser/themes/windows/jar.mn
index 1df89b3b3331..1c5453224de1 100644
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -184,12 +184,9 @@ browser.jar:
skin/classic/browser/devtools/command-scratchpad.png (devtools/command-scratchpad.png)
skin/classic/browser/devtools/command-tilt.png (devtools/command-tilt.png)
skin/classic/browser/devtools/markup-view.css (../shared/devtools/markup-view.css)
- skin/classic/browser/devtools/orion.css (devtools/orion.css)
- skin/classic/browser/devtools/orion-container.css (devtools/orion-container.css)
- skin/classic/browser/devtools/orion-task.png (devtools/orion-task.png)
- skin/classic/browser/devtools/orion-error.png (devtools/orion-error.png)
- skin/classic/browser/devtools/orion-breakpoint.png (devtools/orion-breakpoint.png)
- skin/classic/browser/devtools/orion-debug-location.png (devtools/orion-debug-location.png)
+ skin/classic/browser/devtools/editor-error.png (devtools/editor-error.png)
+ skin/classic/browser/devtools/editor-breakpoint.png (devtools/editor-breakpoint.png)
+ skin/classic/browser/devtools/editor-debug-location.png (devtools/editor-debug-location.png)
* skin/classic/browser/devtools/webconsole.css (devtools/webconsole.css)
skin/classic/browser/devtools/webconsole_networkpanel.css (devtools/webconsole_networkpanel.css)
skin/classic/browser/devtools/webconsole.png (devtools/webconsole.png)
@@ -480,12 +477,9 @@ browser.jar:
skin/classic/aero/browser/devtools/ruleview.css (devtools/ruleview.css)
skin/classic/aero/browser/devtools/commandline.css (devtools/commandline.css)
skin/classic/aero/browser/devtools/markup-view.css (../shared/devtools/markup-view.css)
- skin/classic/aero/browser/devtools/orion.css (devtools/orion.css)
- skin/classic/aero/browser/devtools/orion-container.css (devtools/orion-container.css)
- skin/classic/aero/browser/devtools/orion-task.png (devtools/orion-task.png)
- skin/classic/aero/browser/devtools/orion-error.png (devtools/orion-error.png)
- skin/classic/aero/browser/devtools/orion-breakpoint.png (devtools/orion-breakpoint.png)
- skin/classic/aero/browser/devtools/orion-debug-location.png (devtools/orion-debug-location.png)
+ skin/classic/aero/browser/devtools/editor-error.png (devtools/editor-error.png)
+ skin/classic/aero/browser/devtools/editor-breakpoint.png (devtools/editor-breakpoint.png)
+ skin/classic/aero/browser/devtools/editor-debug-location.png (devtools/editor-debug-location.png)
* skin/classic/aero/browser/devtools/webconsole.css (devtools/webconsole.css)
skin/classic/aero/browser/devtools/webconsole_networkpanel.css (devtools/webconsole_networkpanel.css)
skin/classic/aero/browser/devtools/webconsole.png (devtools/webconsole.png)
From 13e55c6fb15eaef5e722b1abffeb06513eb074e7 Mon Sep 17 00:00:00 2001
From: Brad Lassey
Date: Wed, 23 Oct 2013 18:12:28 +0200
Subject: [PATCH 05/27] bug 930059 - Overscroll.java doesn't need to use
compatibility libraries r=wesj
--HG--
rename : mobile/android/base/gfx/Overscroll.java => mobile/android/base/gfx/OverscrollEdgeEffect.java
---
mobile/android/base/gfx/LayerView.java | 20 ++-
mobile/android/base/gfx/Overscroll.java | 116 +----------------
.../base/gfx/OverscrollEdgeEffect.java | 120 ++++++++++++++++++
mobile/android/base/moz.build | 1 +
4 files changed, 141 insertions(+), 116 deletions(-)
create mode 100644 mobile/android/base/gfx/OverscrollEdgeEffect.java
diff --git a/mobile/android/base/gfx/LayerView.java b/mobile/android/base/gfx/LayerView.java
index 99685ccbd34f..cc4c7c3b7452 100644
--- a/mobile/android/base/gfx/LayerView.java
+++ b/mobile/android/base/gfx/LayerView.java
@@ -110,13 +110,19 @@ public class LayerView extends FrameLayout implements Tabs.OnTabsChangedListener
mBackgroundColor = Color.WHITE;
mTouchInterceptors = new ArrayList();
- mOverscroll = new Overscroll(this);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ mOverscroll = new OverscrollEdgeEffect(this);
+ } else {
+ mOverscroll = null;
+ }
Tabs.registerOnTabsChangedListener(this);
}
public void initializeView(EventDispatcher eventDispatcher) {
mLayerClient = new GeckoLayerClient(getContext(), this, eventDispatcher);
- mLayerClient.setOverscrollHandler(mOverscroll);
+ if (mOverscroll != null) {
+ mLayerClient.setOverscrollHandler(mOverscroll);
+ }
mPanZoomController = mLayerClient.getPanZoomController();
mMarginsAnimator = mLayerClient.getLayerMarginsAnimator();
@@ -248,7 +254,7 @@ public class LayerView extends FrameLayout implements Tabs.OnTabsChangedListener
super.dispatchDraw(canvas);
// We must have a layer client to get valid viewport metrics
- if (mLayerClient != null) {
+ if (mLayerClient != null && mOverscroll != null) {
mOverscroll.draw(canvas, getViewportMetrics());
}
}
@@ -523,7 +529,9 @@ public class LayerView extends FrameLayout implements Tabs.OnTabsChangedListener
mListener.sizeChanged(width, height);
}
- mOverscroll.setSize(width, height);
+ if (mOverscroll != null) {
+ mOverscroll.setSize(width, height);
+ }
}
private void surfaceChanged(int width, int height) {
@@ -533,7 +541,9 @@ public class LayerView extends FrameLayout implements Tabs.OnTabsChangedListener
mListener.surfaceChanged(width, height);
}
- mOverscroll.setSize(width, height);
+ if (mOverscroll != null) {
+ mOverscroll.setSize(width, height);
+ }
}
private void onDestroyed() {
diff --git a/mobile/android/base/gfx/Overscroll.java b/mobile/android/base/gfx/Overscroll.java
index 242fb7c29ce9..e442444d5a7b 100644
--- a/mobile/android/base/gfx/Overscroll.java
+++ b/mobile/android/base/gfx/Overscroll.java
@@ -5,123 +5,17 @@
package org.mozilla.gecko.gfx;
-import android.content.Context;
import android.graphics.Canvas;
-import android.support.v4.widget.EdgeEffectCompat;
-import android.support.v4.view.ViewCompat;
-import android.view.View;
-
-public class Overscroll {
- // Used to index particular edges in the edges array
- private static final int TOP = 0;
- private static final int BOTTOM = 1;
- private static final int LEFT = 2;
- private static final int RIGHT = 3;
-
- // All four edges of the screen
- private final EdgeEffectCompat[] mEdges = new EdgeEffectCompat[4];
-
- // The view we're showing this overscroll on.
- private final View mView;
+public interface Overscroll {
// The axis to show overscroll on.
public enum Axis {
X,
Y,
};
- public Overscroll(final View v) {
- mView = v;
- Context context = v.getContext();
- for (int i = 0; i < 4; i++) {
- mEdges[i] = new EdgeEffectCompat(context);
- }
- }
-
- public void setSize(final int width, final int height) {
- mEdges[LEFT].setSize(height, width);
- mEdges[RIGHT].setSize(height, width);
- mEdges[TOP].setSize(width, height);
- mEdges[BOTTOM].setSize(width, height);
- }
-
- private EdgeEffectCompat getEdgeForAxisAndSide(final Axis axis, final float side) {
- if (axis == Axis.Y) {
- if (side < 0) {
- return mEdges[TOP];
- } else {
- return mEdges[BOTTOM];
- }
- } else {
- if (side < 0) {
- return mEdges[LEFT];
- } else {
- return mEdges[RIGHT];
- }
- }
- }
-
- public void setVelocity(final float velocity, final Axis axis) {
- final EdgeEffectCompat edge = getEdgeForAxisAndSide(axis, velocity);
-
- // If we're showing overscroll already, start fading it out.
- if (!edge.isFinished()) {
- edge.onRelease();
- } else {
- // Otherwise, show an absorb effect
- edge.onAbsorb((int)velocity);
- }
-
- ViewCompat.postInvalidateOnAnimation(mView);
- }
-
- public void setDistance(final float distance, final Axis axis) {
- // The first overscroll event often has zero distance. Throw it out
- if (distance == 0.0f) {
- return;
- }
-
- final EdgeEffectCompat edge = getEdgeForAxisAndSide(axis, (int)distance);
- edge.onPull(distance / (axis == Axis.X ? mView.getWidth() : mView.getHeight()));
- ViewCompat.postInvalidateOnAnimation(mView);
- }
-
- public void draw(final Canvas canvas, final ImmutableViewportMetrics metrics) {
- if (metrics == null) {
- return;
- }
-
- // If we're pulling an edge, or fading it out, draw!
- boolean invalidate = false;
- if (!mEdges[TOP].isFinished()) {
- invalidate |= draw(mEdges[TOP], canvas, metrics.marginLeft, metrics.marginTop, 0);
- }
-
- if (!mEdges[BOTTOM].isFinished()) {
- invalidate |= draw(mEdges[BOTTOM], canvas, mView.getWidth(), mView.getHeight(), 180);
- }
-
- if (!mEdges[LEFT].isFinished()) {
- invalidate |= draw(mEdges[LEFT], canvas, metrics.marginLeft, mView.getHeight(), 270);
- }
-
- if (!mEdges[RIGHT].isFinished()) {
- invalidate |= draw(mEdges[RIGHT], canvas, mView.getWidth(), metrics.marginTop, 90);
- }
-
- // If the edge effect is animating off screen, invalidate.
- if (invalidate) {
- ViewCompat.postInvalidateOnAnimation(mView);
- }
- }
-
- public boolean draw(final EdgeEffectCompat edge, final Canvas canvas, final float translateX, final float translateY, final float rotation) {
- final int state = canvas.save();
- canvas.translate(translateX, translateY);
- canvas.rotate(rotation);
- boolean invalidate = edge.draw(canvas);
- canvas.restoreToCount(state);
-
- return invalidate;
- }
+ public void draw(final Canvas canvas, final ImmutableViewportMetrics metrics);
+ public void setSize(final int width, final int height);
+ public void setVelocity(final float velocity, final Axis axis);
+ public void setDistance(final float distance, final Axis axis);
}
diff --git a/mobile/android/base/gfx/OverscrollEdgeEffect.java b/mobile/android/base/gfx/OverscrollEdgeEffect.java
new file mode 100644
index 000000000000..eb7775ea8ad6
--- /dev/null
+++ b/mobile/android/base/gfx/OverscrollEdgeEffect.java
@@ -0,0 +1,120 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.gfx;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.widget.EdgeEffect;
+import android.view.View;
+
+public class OverscrollEdgeEffect implements Overscroll {
+ // Used to index particular edges in the edges array
+ private static final int TOP = 0;
+ private static final int BOTTOM = 1;
+ private static final int LEFT = 2;
+ private static final int RIGHT = 3;
+
+ // All four edges of the screen
+ private final EdgeEffect[] mEdges = new EdgeEffect[4];
+
+ // The view we're showing this overscroll on.
+ private final View mView;
+
+ public OverscrollEdgeEffect(final View v) {
+ mView = v;
+ Context context = v.getContext();
+ for (int i = 0; i < 4; i++) {
+ mEdges[i] = new EdgeEffect(context);
+ }
+ }
+
+ public void setSize(final int width, final int height) {
+ mEdges[LEFT].setSize(height, width);
+ mEdges[RIGHT].setSize(height, width);
+ mEdges[TOP].setSize(width, height);
+ mEdges[BOTTOM].setSize(width, height);
+ }
+
+ private EdgeEffect getEdgeForAxisAndSide(final Axis axis, final float side) {
+ if (axis == Axis.Y) {
+ if (side < 0) {
+ return mEdges[TOP];
+ } else {
+ return mEdges[BOTTOM];
+ }
+ } else {
+ if (side < 0) {
+ return mEdges[LEFT];
+ } else {
+ return mEdges[RIGHT];
+ }
+ }
+ }
+
+ public void setVelocity(final float velocity, final Axis axis) {
+ final EdgeEffect edge = getEdgeForAxisAndSide(axis, velocity);
+
+ // If we're showing overscroll already, start fading it out.
+ if (!edge.isFinished()) {
+ edge.onRelease();
+ } else {
+ // Otherwise, show an absorb effect
+ edge.onAbsorb((int)velocity);
+ }
+
+ mView.postInvalidateOnAnimation();
+ }
+
+ public void setDistance(final float distance, final Axis axis) {
+ // The first overscroll event often has zero distance. Throw it out
+ if (distance == 0.0f) {
+ return;
+ }
+
+ final EdgeEffect edge = getEdgeForAxisAndSide(axis, (int)distance);
+ edge.onPull(distance / (axis == Axis.X ? mView.getWidth() : mView.getHeight()));
+ mView.postInvalidateOnAnimation();
+ }
+
+ public void draw(final Canvas canvas, final ImmutableViewportMetrics metrics) {
+ if (metrics == null) {
+ return;
+ }
+
+ // If we're pulling an edge, or fading it out, draw!
+ boolean invalidate = false;
+ if (!mEdges[TOP].isFinished()) {
+ invalidate |= draw(mEdges[TOP], canvas, metrics.marginLeft, metrics.marginTop, 0);
+ }
+
+ if (!mEdges[BOTTOM].isFinished()) {
+ invalidate |= draw(mEdges[BOTTOM], canvas, mView.getWidth(), mView.getHeight(), 180);
+ }
+
+ if (!mEdges[LEFT].isFinished()) {
+ invalidate |= draw(mEdges[LEFT], canvas, metrics.marginLeft, mView.getHeight(), 270);
+ }
+
+ if (!mEdges[RIGHT].isFinished()) {
+ invalidate |= draw(mEdges[RIGHT], canvas, mView.getWidth(), metrics.marginTop, 90);
+ }
+
+ // If the edge effect is animating off screen, invalidate.
+ if (invalidate) {
+ mView.postInvalidateOnAnimation();
+ }
+ }
+
+ public boolean draw(final EdgeEffect edge, final Canvas canvas, final float translateX, final float translateY, final float rotation) {
+ final int state = canvas.save();
+ canvas.translate(translateX, translateY);
+ canvas.rotate(rotation);
+ boolean invalidate = edge.draw(canvas);
+ canvas.restoreToCount(state);
+
+ return invalidate;
+ }
+}
diff --git a/mobile/android/base/moz.build b/mobile/android/base/moz.build
index 16d0f461d1e3..8df9e4c0948d 100644
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -168,6 +168,7 @@ gbjar.sources += [
'gfx/NativePanZoomController.java',
'gfx/NinePatchTileLayer.java',
'gfx/Overscroll.java',
+ 'gfx/OverscrollEdgeEffect.java',
'gfx/PanningPerfAPI.java',
'gfx/PanZoomController.java',
'gfx/PanZoomTarget.java',
From 3d9ce0ce93c611bd909946293d636f58206784ee Mon Sep 17 00:00:00 2001
From: Ryan VanderMeulen
Date: Tue, 19 Nov 2013 22:27:07 -0500
Subject: [PATCH 06/27] Backed out changeset 39c711e4570a (bug 930059) for test
bustage.
---
mobile/android/base/gfx/LayerView.java | 20 +--
mobile/android/base/gfx/Overscroll.java | 116 ++++++++++++++++-
.../base/gfx/OverscrollEdgeEffect.java | 120 ------------------
mobile/android/base/moz.build | 1 -
4 files changed, 116 insertions(+), 141 deletions(-)
delete mode 100644 mobile/android/base/gfx/OverscrollEdgeEffect.java
diff --git a/mobile/android/base/gfx/LayerView.java b/mobile/android/base/gfx/LayerView.java
index cc4c7c3b7452..99685ccbd34f 100644
--- a/mobile/android/base/gfx/LayerView.java
+++ b/mobile/android/base/gfx/LayerView.java
@@ -110,19 +110,13 @@ public class LayerView extends FrameLayout implements Tabs.OnTabsChangedListener
mBackgroundColor = Color.WHITE;
mTouchInterceptors = new ArrayList();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
- mOverscroll = new OverscrollEdgeEffect(this);
- } else {
- mOverscroll = null;
- }
+ mOverscroll = new Overscroll(this);
Tabs.registerOnTabsChangedListener(this);
}
public void initializeView(EventDispatcher eventDispatcher) {
mLayerClient = new GeckoLayerClient(getContext(), this, eventDispatcher);
- if (mOverscroll != null) {
- mLayerClient.setOverscrollHandler(mOverscroll);
- }
+ mLayerClient.setOverscrollHandler(mOverscroll);
mPanZoomController = mLayerClient.getPanZoomController();
mMarginsAnimator = mLayerClient.getLayerMarginsAnimator();
@@ -254,7 +248,7 @@ public class LayerView extends FrameLayout implements Tabs.OnTabsChangedListener
super.dispatchDraw(canvas);
// We must have a layer client to get valid viewport metrics
- if (mLayerClient != null && mOverscroll != null) {
+ if (mLayerClient != null) {
mOverscroll.draw(canvas, getViewportMetrics());
}
}
@@ -529,9 +523,7 @@ public class LayerView extends FrameLayout implements Tabs.OnTabsChangedListener
mListener.sizeChanged(width, height);
}
- if (mOverscroll != null) {
- mOverscroll.setSize(width, height);
- }
+ mOverscroll.setSize(width, height);
}
private void surfaceChanged(int width, int height) {
@@ -541,9 +533,7 @@ public class LayerView extends FrameLayout implements Tabs.OnTabsChangedListener
mListener.surfaceChanged(width, height);
}
- if (mOverscroll != null) {
- mOverscroll.setSize(width, height);
- }
+ mOverscroll.setSize(width, height);
}
private void onDestroyed() {
diff --git a/mobile/android/base/gfx/Overscroll.java b/mobile/android/base/gfx/Overscroll.java
index e442444d5a7b..242fb7c29ce9 100644
--- a/mobile/android/base/gfx/Overscroll.java
+++ b/mobile/android/base/gfx/Overscroll.java
@@ -5,17 +5,123 @@
package org.mozilla.gecko.gfx;
+import android.content.Context;
import android.graphics.Canvas;
+import android.support.v4.widget.EdgeEffectCompat;
+import android.support.v4.view.ViewCompat;
+import android.view.View;
+
+public class Overscroll {
+ // Used to index particular edges in the edges array
+ private static final int TOP = 0;
+ private static final int BOTTOM = 1;
+ private static final int LEFT = 2;
+ private static final int RIGHT = 3;
+
+ // All four edges of the screen
+ private final EdgeEffectCompat[] mEdges = new EdgeEffectCompat[4];
+
+ // The view we're showing this overscroll on.
+ private final View mView;
-public interface Overscroll {
// The axis to show overscroll on.
public enum Axis {
X,
Y,
};
- public void draw(final Canvas canvas, final ImmutableViewportMetrics metrics);
- public void setSize(final int width, final int height);
- public void setVelocity(final float velocity, final Axis axis);
- public void setDistance(final float distance, final Axis axis);
+ public Overscroll(final View v) {
+ mView = v;
+ Context context = v.getContext();
+ for (int i = 0; i < 4; i++) {
+ mEdges[i] = new EdgeEffectCompat(context);
+ }
+ }
+
+ public void setSize(final int width, final int height) {
+ mEdges[LEFT].setSize(height, width);
+ mEdges[RIGHT].setSize(height, width);
+ mEdges[TOP].setSize(width, height);
+ mEdges[BOTTOM].setSize(width, height);
+ }
+
+ private EdgeEffectCompat getEdgeForAxisAndSide(final Axis axis, final float side) {
+ if (axis == Axis.Y) {
+ if (side < 0) {
+ return mEdges[TOP];
+ } else {
+ return mEdges[BOTTOM];
+ }
+ } else {
+ if (side < 0) {
+ return mEdges[LEFT];
+ } else {
+ return mEdges[RIGHT];
+ }
+ }
+ }
+
+ public void setVelocity(final float velocity, final Axis axis) {
+ final EdgeEffectCompat edge = getEdgeForAxisAndSide(axis, velocity);
+
+ // If we're showing overscroll already, start fading it out.
+ if (!edge.isFinished()) {
+ edge.onRelease();
+ } else {
+ // Otherwise, show an absorb effect
+ edge.onAbsorb((int)velocity);
+ }
+
+ ViewCompat.postInvalidateOnAnimation(mView);
+ }
+
+ public void setDistance(final float distance, final Axis axis) {
+ // The first overscroll event often has zero distance. Throw it out
+ if (distance == 0.0f) {
+ return;
+ }
+
+ final EdgeEffectCompat edge = getEdgeForAxisAndSide(axis, (int)distance);
+ edge.onPull(distance / (axis == Axis.X ? mView.getWidth() : mView.getHeight()));
+ ViewCompat.postInvalidateOnAnimation(mView);
+ }
+
+ public void draw(final Canvas canvas, final ImmutableViewportMetrics metrics) {
+ if (metrics == null) {
+ return;
+ }
+
+ // If we're pulling an edge, or fading it out, draw!
+ boolean invalidate = false;
+ if (!mEdges[TOP].isFinished()) {
+ invalidate |= draw(mEdges[TOP], canvas, metrics.marginLeft, metrics.marginTop, 0);
+ }
+
+ if (!mEdges[BOTTOM].isFinished()) {
+ invalidate |= draw(mEdges[BOTTOM], canvas, mView.getWidth(), mView.getHeight(), 180);
+ }
+
+ if (!mEdges[LEFT].isFinished()) {
+ invalidate |= draw(mEdges[LEFT], canvas, metrics.marginLeft, mView.getHeight(), 270);
+ }
+
+ if (!mEdges[RIGHT].isFinished()) {
+ invalidate |= draw(mEdges[RIGHT], canvas, mView.getWidth(), metrics.marginTop, 90);
+ }
+
+ // If the edge effect is animating off screen, invalidate.
+ if (invalidate) {
+ ViewCompat.postInvalidateOnAnimation(mView);
+ }
+ }
+
+ public boolean draw(final EdgeEffectCompat edge, final Canvas canvas, final float translateX, final float translateY, final float rotation) {
+ final int state = canvas.save();
+ canvas.translate(translateX, translateY);
+ canvas.rotate(rotation);
+ boolean invalidate = edge.draw(canvas);
+ canvas.restoreToCount(state);
+
+ return invalidate;
+ }
}
diff --git a/mobile/android/base/gfx/OverscrollEdgeEffect.java b/mobile/android/base/gfx/OverscrollEdgeEffect.java
deleted file mode 100644
index eb7775ea8ad6..000000000000
--- a/mobile/android/base/gfx/OverscrollEdgeEffect.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.gfx;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.widget.EdgeEffect;
-import android.view.View;
-
-public class OverscrollEdgeEffect implements Overscroll {
- // Used to index particular edges in the edges array
- private static final int TOP = 0;
- private static final int BOTTOM = 1;
- private static final int LEFT = 2;
- private static final int RIGHT = 3;
-
- // All four edges of the screen
- private final EdgeEffect[] mEdges = new EdgeEffect[4];
-
- // The view we're showing this overscroll on.
- private final View mView;
-
- public OverscrollEdgeEffect(final View v) {
- mView = v;
- Context context = v.getContext();
- for (int i = 0; i < 4; i++) {
- mEdges[i] = new EdgeEffect(context);
- }
- }
-
- public void setSize(final int width, final int height) {
- mEdges[LEFT].setSize(height, width);
- mEdges[RIGHT].setSize(height, width);
- mEdges[TOP].setSize(width, height);
- mEdges[BOTTOM].setSize(width, height);
- }
-
- private EdgeEffect getEdgeForAxisAndSide(final Axis axis, final float side) {
- if (axis == Axis.Y) {
- if (side < 0) {
- return mEdges[TOP];
- } else {
- return mEdges[BOTTOM];
- }
- } else {
- if (side < 0) {
- return mEdges[LEFT];
- } else {
- return mEdges[RIGHT];
- }
- }
- }
-
- public void setVelocity(final float velocity, final Axis axis) {
- final EdgeEffect edge = getEdgeForAxisAndSide(axis, velocity);
-
- // If we're showing overscroll already, start fading it out.
- if (!edge.isFinished()) {
- edge.onRelease();
- } else {
- // Otherwise, show an absorb effect
- edge.onAbsorb((int)velocity);
- }
-
- mView.postInvalidateOnAnimation();
- }
-
- public void setDistance(final float distance, final Axis axis) {
- // The first overscroll event often has zero distance. Throw it out
- if (distance == 0.0f) {
- return;
- }
-
- final EdgeEffect edge = getEdgeForAxisAndSide(axis, (int)distance);
- edge.onPull(distance / (axis == Axis.X ? mView.getWidth() : mView.getHeight()));
- mView.postInvalidateOnAnimation();
- }
-
- public void draw(final Canvas canvas, final ImmutableViewportMetrics metrics) {
- if (metrics == null) {
- return;
- }
-
- // If we're pulling an edge, or fading it out, draw!
- boolean invalidate = false;
- if (!mEdges[TOP].isFinished()) {
- invalidate |= draw(mEdges[TOP], canvas, metrics.marginLeft, metrics.marginTop, 0);
- }
-
- if (!mEdges[BOTTOM].isFinished()) {
- invalidate |= draw(mEdges[BOTTOM], canvas, mView.getWidth(), mView.getHeight(), 180);
- }
-
- if (!mEdges[LEFT].isFinished()) {
- invalidate |= draw(mEdges[LEFT], canvas, metrics.marginLeft, mView.getHeight(), 270);
- }
-
- if (!mEdges[RIGHT].isFinished()) {
- invalidate |= draw(mEdges[RIGHT], canvas, mView.getWidth(), metrics.marginTop, 90);
- }
-
- // If the edge effect is animating off screen, invalidate.
- if (invalidate) {
- mView.postInvalidateOnAnimation();
- }
- }
-
- public boolean draw(final EdgeEffect edge, final Canvas canvas, final float translateX, final float translateY, final float rotation) {
- final int state = canvas.save();
- canvas.translate(translateX, translateY);
- canvas.rotate(rotation);
- boolean invalidate = edge.draw(canvas);
- canvas.restoreToCount(state);
-
- return invalidate;
- }
-}
diff --git a/mobile/android/base/moz.build b/mobile/android/base/moz.build
index 8df9e4c0948d..16d0f461d1e3 100644
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -168,7 +168,6 @@ gbjar.sources += [
'gfx/NativePanZoomController.java',
'gfx/NinePatchTileLayer.java',
'gfx/Overscroll.java',
- 'gfx/OverscrollEdgeEffect.java',
'gfx/PanningPerfAPI.java',
'gfx/PanZoomController.java',
'gfx/PanZoomTarget.java',
From c8b9db84979e5382893fb9ef6e26da68dcca7470 Mon Sep 17 00:00:00 2001
From: Tim Taubert
Date: Fri, 1 Nov 2013 11:25:17 +0100
Subject: [PATCH 07/27] Bug 932898 - Bring back the shutdown leak detector
r=ted
---
build/automation.py.in | 3 +-
build/automationutils.py | 120 ++++++++++++++++++++++++++++++
testing/mochitest/browser-test.js | 33 +++++++-
testing/mochitest/runtests.py | 26 ++++++-
4 files changed, 176 insertions(+), 6 deletions(-)
diff --git a/build/automation.py.in b/build/automation.py.in
index 5898e211a8f0..e0254293c047 100644
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -840,7 +840,8 @@ class Automation(object):
runSSLTunnel = False, utilityPath = None,
xrePath = None, certPath = None,
debuggerInfo = None, symbolsPath = None,
- timeout = -1, maxTime = None, onLaunch = None):
+ timeout = -1, maxTime = None, onLaunch = None,
+ webapprtChrome = False):
"""
Run the app, log the duration it took to execute, return the status code.
Kills the app if it runs for longer than |maxTime| seconds, or outputs nothing for |timeout| seconds.
diff --git a/build/automationutils.py b/build/automationutils.py
index 367419a56b5c..053246683c95 100644
--- a/build/automationutils.py
+++ b/build/automationutils.py
@@ -8,6 +8,7 @@ import glob, logging, os, platform, shutil, subprocess, sys, tempfile, urllib2,
import base64
import re
from urlparse import urlparse
+from operator import itemgetter
try:
import mozinfo
@@ -46,6 +47,7 @@ __all__ = [
'systemMemory',
'environment',
'dumpScreen',
+ "ShutdownLeaks"
]
# Map of debugging programs to information about them, like default arguments
@@ -527,3 +529,121 @@ def dumpScreen(utilityPath):
uri = "data:image/png;base64,%s" % encoded
log.info("SCREENSHOT: %s", uri)
return uri
+
+class ShutdownLeaks(object):
+ """
+ Parses the mochitest run log when running a debug build, assigns all leaked
+ DOM windows (that are still around after test suite shutdown, despite running
+ the GC) to the tests that created them and prints leak statistics.
+ """
+
+ def __init__(self, logger):
+ self.logger = logger
+ self.tests = []
+ self.leakedWindows = {}
+ self.leakedDocShells = set()
+ self.currentTest = None
+ self.seenShutdown = False
+
+ def log(self, line):
+ if line[2:11] == "DOMWINDOW":
+ self._logWindow(line)
+ elif line[2:10] == "DOCSHELL":
+ self._logDocShell(line)
+ elif line.startswith("TEST-START"):
+ fileName = line.split(" ")[-1].strip().replace("chrome://mochitests/content/browser/", "")
+ self.currentTest = {"fileName": fileName, "windows": set(), "docShells": set()}
+ elif line.startswith("INFO TEST-END"):
+ # don't track a test if no windows or docShells leaked
+ if self.currentTest and (self.currentTest["windows"] or self.currentTest["docShells"]):
+ self.tests.append(self.currentTest)
+ self.currentTest = None
+ elif line.startswith("INFO TEST-START | Shutdown"):
+ self.seenShutdown = True
+
+ def process(self):
+ leakingTests = self._parseLeakingTests()
+
+ if leakingTests:
+ totalWindows = sum(len(test["leakedWindows"]) for test in leakingTests)
+ totalDocShells = sum(len(test["leakedDocShells"]) for test in leakingTests)
+ self.logger("TEST-UNEXPECTED-FAIL | ShutdownLeaks | leaked %d DOMWindow(s) and %d DocShell(s) until shutdown", totalWindows, totalDocShells)
+
+ for test in leakingTests:
+ for url, count in self._zipLeakedWindows(test["leakedWindows"]):
+ self.logger("TEST-UNEXPECTED-FAIL | %s | leaked %d window(s) until shutdown [url = %s]", test["fileName"], count, url)
+
+ if test["leakedDocShells"]:
+ self.logger("TEST-UNEXPECTED-FAIL | %s | leaked %d docShell(s) until shutdown", test["fileName"], len(test["leakedDocShells"]))
+
+ def _logWindow(self, line):
+ created = line[:2] == "++"
+ pid = self._parseValue(line, "pid")
+ serial = self._parseValue(line, "serial")
+
+ # log line has invalid format
+ if not pid or not serial:
+ self.logger("TEST-UNEXPECTED-FAIL | ShutdownLeaks | failed to parse line <%s>", line)
+ return
+
+ key = pid + "." + serial
+
+ if self.currentTest:
+ windows = self.currentTest["windows"]
+ if created:
+ windows.add(key)
+ else:
+ windows.discard(key)
+ elif self.seenShutdown and not created:
+ self.leakedWindows[key] = self._parseValue(line, "url")
+
+ def _logDocShell(self, line):
+ created = line[:2] == "++"
+ pid = self._parseValue(line, "pid")
+ id = self._parseValue(line, "id")
+
+ # log line has invalid format
+ if not pid or not id:
+ self.logger("TEST-UNEXPECTED-FAIL | ShutdownLeaks | failed to parse line <%s>", line)
+ return
+
+ key = pid + "." + id
+
+ if self.currentTest:
+ docShells = self.currentTest["docShells"]
+ if created:
+ docShells.add(key)
+ else:
+ docShells.discard(key)
+ elif self.seenShutdown and not created:
+ self.leakedDocShells.add(key)
+
+ def _parseValue(self, line, name):
+ match = re.search("\[%s = (.+?)\]" % name, line)
+ if match:
+ return match.group(1)
+ return None
+
+ def _parseLeakingTests(self):
+ leakingTests = []
+
+ for test in self.tests:
+ test["leakedWindows"] = [self.leakedWindows[id] for id in test["windows"] if id in self.leakedWindows]
+ test["leakedDocShells"] = [id for id in test["docShells"] if id in self.leakedDocShells]
+ test["leakCount"] = len(test["leakedWindows"]) + len(test["leakedDocShells"])
+
+ if test["leakCount"]:
+ leakingTests.append(test)
+
+ return sorted(leakingTests, key=itemgetter("leakCount"), reverse=True)
+
+ def _zipLeakedWindows(self, leakedWindows):
+ counts = []
+ counted = set()
+
+ for url in leakedWindows:
+ if not url in counted:
+ counts.append((url, leakedWindows.count(url)))
+ counted.add(url)
+
+ return sorted(counts, key=itemgetter(1), reverse=True)
diff --git a/testing/mochitest/browser-test.js b/testing/mochitest/browser-test.js
index 0ba9c0830f56..8f67ad114004 100644
--- a/testing/mochitest/browser-test.js
+++ b/testing/mochitest/browser-test.js
@@ -13,6 +13,9 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "BrowserNewTabPreloader",
+ "resource:///modules/BrowserNewTabPreloader.jsm", "BrowserNewTabPreloader");
+
window.addEventListener("load", testOnLoad, false);
function testOnLoad() {
@@ -194,7 +197,7 @@ Tester.prototype = {
Services.console.unregisterListener(this);
Services.obs.removeObserver(this, "chrome-document-global-created");
Services.obs.removeObserver(this, "content-document-global-created");
-
+
this.dumper.dump("\nINFO TEST-START | Shutdown\n");
if (this.tests.length) {
this.dumper.dump("Browser Chrome Test Summary\n");
@@ -380,6 +383,34 @@ Tester.prototype = {
gBrowser.removeCurrentTab();
}
+ // Replace the document currently loaded in the browser's sidebar.
+ // This will prevent false positives for tests that were the last
+ // to touch the sidebar. They will thus not be blamed for leaking
+ // a document.
+ let sidebar = document.getElementById("sidebar");
+ sidebar.setAttribute("src", "data:text/html;charset=utf-8,");
+ sidebar.docShell.createAboutBlankContentViewer(null);
+ sidebar.setAttribute("src", "about:blank");
+
+ // Do the same for the social sidebar.
+ let socialSidebar = document.getElementById("social-sidebar-browser");
+ socialSidebar.setAttribute("src", "data:text/html;charset=utf-8,");
+ socialSidebar.docShell.createAboutBlankContentViewer(null);
+ socialSidebar.setAttribute("src", "about:blank");
+
+ // Destroy BackgroundPageThumbs resources.
+ let {BackgroundPageThumbs} =
+ Cu.import("resource://gre/modules/BackgroundPageThumbs.jsm", {});
+ BackgroundPageThumbs._destroy();
+
+ // Uninitialize a few things explicitly so that they can clean up
+ // frames and browser intentionally kept alive until shutdown to
+ // eliminate false positives.
+ BrowserNewTabPreloader.uninit();
+ SocialFlyout.unload();
+ SocialShare.uninit();
+ TabView.uninit();
+
// Schedule GC and CC runs before finishing in order to detect
// DOM windows leaked by our tests or the tested code.
diff --git a/testing/mochitest/runtests.py b/testing/mochitest/runtests.py
index 2f89ab536990..2d31bd8dfd14 100644
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -28,7 +28,7 @@ import time
import traceback
import urllib2
-from automationutils import environment, getDebuggerInfo, isURL, KeyValueParseError, parseKeyValue, processLeakLog, systemMemory, dumpScreen
+from automationutils import environment, getDebuggerInfo, isURL, KeyValueParseError, parseKeyValue, processLeakLog, systemMemory, dumpScreen, ShutdownLeaks
from datetime import datetime
from manifestparser import TestManifest
from mochitest_options import MochitestOptions
@@ -750,7 +750,8 @@ class Mochitest(MochitestUtilsMixin):
debuggerInfo=None,
symbolsPath=None,
timeout=-1,
- onLaunch=None):
+ onLaunch=None,
+ webapprtChrome=False):
"""
Run the app, log the duration it took to execute, return the status code.
Kills the app if it runs for longer than |maxTime| seconds, or outputs nothing for |timeout| seconds.
@@ -826,11 +827,17 @@ class Mochitest(MochitestUtilsMixin):
if testUrl:
args.append(testUrl)
+ if mozinfo.info["debug"] and not webapprtChrome:
+ shutdownLeaks = ShutdownLeaks(log.info)
+ else:
+ shutdownLeaks = None
+
# create an instance to process the output
outputHandler = self.OutputHandler(harness=self,
utilityPath=utilityPath,
symbolsPath=symbolsPath,
dump_screen_on_timeout=not debuggerInfo,
+ shutdownLeaks=shutdownLeaks,
)
def timeoutHandler():
@@ -1006,7 +1013,8 @@ class Mochitest(MochitestUtilsMixin):
debuggerInfo=debuggerInfo,
symbolsPath=options.symbolsPath,
timeout=timeout,
- onLaunch=onLaunch
+ onLaunch=onLaunch,
+ webapprtChrome=options.webapprtChrome
)
except KeyboardInterrupt:
log.info("runtests.py | Received keyboard interrupt.\n");
@@ -1040,7 +1048,7 @@ class Mochitest(MochitestUtilsMixin):
class OutputHandler(object):
"""line output handler for mozrunner"""
- def __init__(self, harness, utilityPath, symbolsPath=None, dump_screen_on_timeout=True):
+ def __init__(self, harness, utilityPath, symbolsPath=None, dump_screen_on_timeout=True, shutdownLeaks=None):
"""
harness -- harness instance
dump_screen_on_timeout -- whether to dump the screen on timeout
@@ -1049,6 +1057,7 @@ class Mochitest(MochitestUtilsMixin):
self.utilityPath = utilityPath
self.symbolsPath = symbolsPath
self.dump_screen_on_timeout = dump_screen_on_timeout
+ self.shutdownLeaks = shutdownLeaks
# perl binary to use
self.perl = which('perl')
@@ -1078,6 +1087,7 @@ class Mochitest(MochitestUtilsMixin):
self.record_last_test,
self.dumpScreenOnTimeout,
self.metro_subprocess_id,
+ self.trackShutdownLeaks,
self.log,
]
@@ -1126,6 +1136,9 @@ class Mochitest(MochitestUtilsMixin):
if status and not didTimeout:
log.info("TEST-UNEXPECTED-FAIL | runtests.py | Stack fixer process exited with code %d during test run", status)
+ if self.shutdownLeaks:
+ self.shutdownLeaks.process()
+
# output line handlers:
# these take a line and return a line
@@ -1159,6 +1172,11 @@ class Mochitest(MochitestUtilsMixin):
log.info("INFO | runtests.py | metro browser sub process id detected: %s", self.browserProcessId)
return line
+ def trackShutdownLeaks(self, line):
+ if self.shutdownLeaks:
+ self.shutdownLeaks.log(line)
+ return line
+
def log(self, line):
log.info(line)
return line
From 9379e40d2ec01ea01e91f67f42ea1e1291b30d55 Mon Sep 17 00:00:00 2001
From: Nick Fitzgerald
Date: Tue, 5 Nov 2013 16:40:00 -0800
Subject: [PATCH 08/27] Bug 917583 - part 1: make function's definition site
its own request in the RDP; r=past
---
toolkit/devtools/client/dbg-client.jsm | 11 ++++
toolkit/devtools/server/actors/script.js | 49 ++++++++++++--
.../server/tests/unit/test_objectgrips-13.js | 66 +++++++++++++++++++
.../devtools/server/tests/unit/xpcshell.ini | 1 +
4 files changed, 121 insertions(+), 6 deletions(-)
create mode 100644 toolkit/devtools/server/tests/unit/test_objectgrips-13.js
diff --git a/toolkit/devtools/client/dbg-client.jsm b/toolkit/devtools/client/dbg-client.jsm
index 2cd71f8ef656..9ac9a8bfcaec 100644
--- a/toolkit/devtools/client/dbg-client.jsm
+++ b/toolkit/devtools/client/dbg-client.jsm
@@ -1828,6 +1828,17 @@ ObjectClient.prototype = {
get isSealed() this._grip.sealed,
get isExtensible() this._grip.extensible,
+ getDefinitionSite: DebuggerClient.requester({
+ type: "definitionSite"
+ }, {
+ before: function (aPacket) {
+ if (this._grip.class != "Function") {
+ throw new Error("getDefinitionSite is only valid for function grips.");
+ }
+ return aPacket;
+ }
+ }),
+
/**
* Request the names of a function's formal parameters.
*
diff --git a/toolkit/devtools/server/actors/script.js b/toolkit/devtools/server/actors/script.js
index 2d45c257fcdb..ae70da2f4d30 100644
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -2883,12 +2883,6 @@ ObjectActor.prototype = {
// with "permission denied" errors for some functions.
dumpn(e);
}
-
- // Add source location information.
- if (this.obj.script) {
- g.url = this.obj.script.url;
- g.line = this.obj.script.startLine;
- }
}
return g;
@@ -2925,6 +2919,48 @@ ObjectActor.prototype = {
this._forcedMagicProps = true;
},
+ /**
+ * Handle a protocol request to provide the definition site of this function
+ * object.
+ *
+ * @param aRequest object
+ * The protocol request object.
+ */
+ onDefinitionSite: function OA_onDefinitionSite(aRequest) {
+ if (this.obj.class != "Function") {
+ return {
+ from: this.actorID,
+ error: "objectNotFunction",
+ message: this.actorID + " is not a function."
+ };
+ }
+
+ if (!this.obj.script) {
+ return {
+ from: this.actorID,
+ error: "noScript",
+ message: this.actorID + " has no Debugger.Script"
+ };
+ }
+
+ const generatedLocation = {
+ url: this.obj.script.url,
+ line: this.obj.script.startLine,
+ // TODO bug 901138: use Debugger.Script.prototype.startColumn.
+ column: 0
+ };
+
+ return this.threadActor.sources.getOriginalLocation(generatedLocation)
+ .then(({ url, line, column }) => {
+ return {
+ from: this.actorID,
+ url: url,
+ line: line,
+ column: column
+ };
+ });
+ },
+
/**
* Handle a protocol request to provide the names of the properties defined on
* the object and not its prototype.
@@ -3230,6 +3266,7 @@ ObjectActor.prototype = {
};
ObjectActor.prototype.requestTypes = {
+ "definitionSite": ObjectActor.prototype.onDefinitionSite,
"parameterNames": ObjectActor.prototype.onParameterNames,
"prototypeAndProperties": ObjectActor.prototype.onPrototypeAndProperties,
"prototype": ObjectActor.prototype.onPrototype,
diff --git a/toolkit/devtools/server/tests/unit/test_objectgrips-13.js b/toolkit/devtools/server/tests/unit/test_objectgrips-13.js
new file mode 100644
index 000000000000..4f4a9aa20d16
--- /dev/null
+++ b/toolkit/devtools/server/tests/unit/test_objectgrips-13.js
@@ -0,0 +1,66 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that ObjectClient.prototype.getDefinitionSite and the "definitionSite"
+// request work properly.
+
+var gDebuggee;
+var gClient;
+var gThreadClient;
+
+function run_test()
+{
+ initTestDebuggerServer();
+ gDebuggee = addTestGlobal("test-grips");
+ gDebuggee.eval(function stopMe() {
+ debugger;
+ }.toString());
+
+ gClient = new DebuggerClient(DebuggerServer.connectPipe());
+ gClient.connect(function() {
+ attachTestTabAndResume(gClient, "test-grips", function(aResponse, aTabClient, aThreadClient) {
+ gThreadClient = aThreadClient;
+ add_pause_listener();
+ });
+ });
+ do_test_pending();
+}
+
+function add_pause_listener()
+{
+ gThreadClient.addOneTimeListener("paused", function(aEvent, aPacket) {
+ const [funcGrip, objGrip] = aPacket.frame.arguments;
+ const func = gThreadClient.pauseGrip(funcGrip);
+ const obj = gThreadClient.pauseGrip(objGrip);
+ test_definition_site(func, obj);
+ });
+
+ eval_code();
+}
+
+function eval_code() {
+ gDebuggee.eval([
+ "this.line0 = Error().lineNumber;",
+ "function f() {}",
+ "stopMe(f, {});"
+ ].join("\n"));
+}
+
+function test_definition_site(func, obj) {
+ func.getDefinitionSite(({ error, url, line, column }) => {
+ do_check_true(!error);
+ do_check_eq(url, getFilePath("test_objectgrips-13.js"));
+ do_check_eq(line, gDebuggee.line0 + 1);
+ do_check_eq(column, 0);
+
+ test_bad_definition_site(obj);
+ });
+}
+
+function test_bad_definition_site(obj) {
+ try {
+ obj.getDefinitionSite(() => do_check_true(false));
+ } catch (e) {
+ gThreadClient.resume(() => finishClient(gClient));
+ }
+}
diff --git a/toolkit/devtools/server/tests/unit/xpcshell.ini b/toolkit/devtools/server/tests/unit/xpcshell.ini
index 4ec229176ca7..8834a3e255ba 100644
--- a/toolkit/devtools/server/tests/unit/xpcshell.ini
+++ b/toolkit/devtools/server/tests/unit/xpcshell.ini
@@ -148,6 +148,7 @@ reason = bug 820380
[test_objectgrips-10.js]
[test_objectgrips-11.js]
[test_objectgrips-12.js]
+[test_objectgrips-13.js]
[test_interrupt.js]
[test_stepping-01.js]
[test_stepping-02.js]
From b02656d66fac9f0e2c2039b96947bb5840af0d1f Mon Sep 17 00:00:00 2001
From: Nick Fitzgerald
Date: Tue, 5 Nov 2013 17:28:00 -0800
Subject: [PATCH 09/27] Bug 917583 - part 2: request definitionSite before
adding listeners to the view; r=vporof
---
.../devtools/debugger/debugger-controller.js | 47 +++++++++++++------
1 file changed, 32 insertions(+), 15 deletions(-)
diff --git a/browser/devtools/debugger/debugger-controller.js b/browser/devtools/debugger/debugger-controller.js
index f6ca29d0d5f4..0b70fd9a6770 100644
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -1398,12 +1398,36 @@ EventListeners.prototype = {
*/
scheduleEventListenersFetch: function() {
let getListeners = aCallback => gThreadClient.eventListeners(aResponse => {
- this._onEventListeners(aResponse);
+ if (aResponse.error) {
+ let msg = "Error getting event listeners: " + aResponse.message;
+ DevToolsUtils.reportException("scheduleEventListenersFetch", msg);
+ return;
+ }
- // Notify that event listeners were fetched and shown in the view,
- // and callback to resume the active thread if necessary.
- window.emit(EVENTS.EVENT_LISTENERS_FETCHED);
- aCallback && aCallback();
+ promise.all(aResponse.listeners.map(listener => {
+ const deferred = promise.defer();
+
+ gThreadClient.pauseGrip(listener.function).getDefinitionSite(aResponse => {
+ if (aResponse.error) {
+ const msg = "Error getting function definition site: " + aResponse.message;
+ DevToolsUtils.reportException("scheduleEventListenersFetch", msg);
+ deferred.reject(msg);
+ return;
+ }
+
+ listener.function.url = aResponse.url;
+ deferred.resolve(listener);
+ });
+
+ return deferred.promise;
+ })).then(listeners => {
+ this._onEventListeners(listeners);
+
+ // Notify that event listeners were fetched and shown in the view,
+ // and callback to resume the active thread if necessary.
+ window.emit(EVENTS.EVENT_LISTENERS_FETCHED);
+ aCallback && aCallback();
+ });
});
// Make sure we're not sending a batch of closely repeated requests.
@@ -1418,18 +1442,11 @@ EventListeners.prototype = {
},
/**
- * Callback for the debugger's active thread eventListeners() method.
+ * Callback for a debugger's successful active thread eventListeners() call.
*/
- _onEventListeners: function(aResponse) {
- if (aResponse.error) {
- let msg = "Error getting event listeners: " + aResponse.message;
- Cu.reportError(msg);
- dumpn(msg);
- return;
- }
-
+ _onEventListeners: function(aListeners) {
// Add all the listeners in the debugger view event linsteners container.
- for (let listener of aResponse.listeners) {
+ for (let listener of aListeners) {
DebuggerView.EventListeners.addListener(listener, { staged: true });
}
From b97619a8a4ddfc4c65fd72000a2859367c1b16a5 Mon Sep 17 00:00:00 2001
From: Panos Astithas
Date: Wed, 20 Nov 2013 08:49:55 -0800
Subject: [PATCH 10/27] Bug 917583 - part 3: fix the event-listeners test;
r=fitzgen
---
.../test/browser_dbg_event-listeners.js | 98 ++++++++++++-------
1 file changed, 60 insertions(+), 38 deletions(-)
diff --git a/browser/devtools/debugger/test/browser_dbg_event-listeners.js b/browser/devtools/debugger/test/browser_dbg_event-listeners.js
index f4807c635645..83c2f2fd3176 100644
--- a/browser/devtools/debugger/test/browser_dbg_event-listeners.js
+++ b/browser/devtools/debugger/test/browser_dbg_event-listeners.js
@@ -60,55 +60,77 @@ function testEventListeners(aThreadClient) {
let deferred = promise.defer();
aThreadClient.eventListeners(aPacket => {
+ if (aPacket.error) {
+ let msg = "Error getting event listeners: " + aPacket.message;
+ ok(false, msg);
+ deferred.reject(msg);
+ return;
+ }
+
is(aPacket.listeners.length, 3,
"Found all event listeners.");
- let types = [];
+ promise.all(aPacket.listeners.map(listener => {
+ const lDeferred = promise.defer();
+ aThreadClient.pauseGrip(listener.function).getDefinitionSite(aResponse => {
+ if (aResponse.error) {
+ const msg = "Error getting function definition site: " + aResponse.message;
+ ok(false, msg);
+ lDeferred.reject(msg);
+ return;
+ }
+ listener.function.url = aResponse.url;
+ lDeferred.resolve(listener);
+ });
+ return lDeferred.promise;
+ })).then(listeners => {
+ let types = [];
- for (let l of aPacket.listeners) {
- let node = l.node;
- ok(node, "There is a node property.");
- ok(node.object, "There is a node object property.");
- ok(node.selector == "window" ||
- content.document.querySelectorAll(node.selector).length == 1,
- "The node property is a unique CSS selector.");
+ for (let l of listeners) {
+ let node = l.node;
+ ok(node, "There is a node property.");
+ ok(node.object, "There is a node object property.");
+ ok(node.selector == "window" ||
+ content.document.querySelectorAll(node.selector).length == 1,
+ "The node property is a unique CSS selector.");
- let func = l.function;
- ok(func, "There is a function property.");
- is(func.type, "object", "The function form is of type 'object'.");
- is(func.class, "Function", "The function form is of class 'Function'.");
- is(func.url, TAB_URL, "The function url is correct.");
+ let func = l.function;
+ ok(func, "There is a function property.");
+ is(func.type, "object", "The function form is of type 'object'.");
+ is(func.class, "Function", "The function form is of class 'Function'.");
+ is(func.url, TAB_URL, "The function url is correct.");
- is(l.allowsUntrusted, true,
- "'allowsUntrusted' property has the right value.");
- is(l.inSystemEventGroup, false,
- "'inSystemEventGroup' property has the right value.");
+ is(l.allowsUntrusted, true,
+ "'allowsUntrusted' property has the right value.");
+ is(l.inSystemEventGroup, false,
+ "'inSystemEventGroup' property has the right value.");
- types.push(l.type);
+ types.push(l.type);
- if (l.type == "keyup") {
- is(l.capturing, true,
- "Capturing property has the right value.");
- is(l.isEventHandler, false,
- "'isEventHandler' property has the right value.");
- } else if (l.type == "load") {
- is(l.capturing, false,
- "Capturing property has the right value.");
- is(l.isEventHandler, false,
- "'isEventHandler' property has the right value.");
- } else {
- is(l.capturing, false,
- "Capturing property has the right value.");
- is(l.isEventHandler, true,
- "'isEventHandler' property has the right value.");
+ if (l.type == "keyup") {
+ is(l.capturing, true,
+ "Capturing property has the right value.");
+ is(l.isEventHandler, false,
+ "'isEventHandler' property has the right value.");
+ } else if (l.type == "load") {
+ is(l.capturing, false,
+ "Capturing property has the right value.");
+ is(l.isEventHandler, false,
+ "'isEventHandler' property has the right value.");
+ } else {
+ is(l.capturing, false,
+ "Capturing property has the right value.");
+ is(l.isEventHandler, true,
+ "'isEventHandler' property has the right value.");
+ }
}
- }
- ok(types.indexOf("click") != -1, "Found the click handler.");
- ok(types.indexOf("change") != -1, "Found the change handler.");
- ok(types.indexOf("keyup") != -1, "Found the keyup handler.");
+ ok(types.indexOf("click") != -1, "Found the click handler.");
+ ok(types.indexOf("change") != -1, "Found the change handler.");
+ ok(types.indexOf("keyup") != -1, "Found the keyup handler.");
- aThreadClient.resume(deferred.resolve);
+ aThreadClient.resume(deferred.resolve);
+ });
});
return deferred.promise;
From b6ba9103a84a48974bfce82b91ab4139b6c983e7 Mon Sep 17 00:00:00 2001
From: Mike Conley
Date: Wed, 20 Nov 2013 12:12:29 -0500
Subject: [PATCH 11/27] Bug 939965 - [Australis] Use ensureReady to force panel
menu construction instead of opening it. r=Gijs.
---
.../test/browser_876926_customize_mode_wrapping.js | 9 +--------
1 file changed, 1 insertion(+), 8 deletions(-)
diff --git a/browser/components/customizableui/test/browser_876926_customize_mode_wrapping.js b/browser/components/customizableui/test/browser_876926_customize_mode_wrapping.js
index 9d4d4289e90f..75875e417b0d 100644
--- a/browser/components/customizableui/test/browser_876926_customize_mode_wrapping.js
+++ b/browser/components/customizableui/test/browser_876926_customize_mode_wrapping.js
@@ -114,14 +114,7 @@ let gTests = [
setup: startCustomizing,
run: function() {
otherWin = yield openAndLoadWindow(null, true);
- // Open and close the panel to force its construction:
- let shownPromise = promisePanelShown(otherWin);
- otherWin.PanelUI.toggle({type: "command"});
- yield shownPromise;
- let hiddenPromise = promisePanelHidden(otherWin);
- otherWin.PanelUI.toggle({type: "command"});
- yield hiddenPromise;
-
+ yield otherWin.PanelUI.ensureReady();
ok(CustomizableUI.inDefaultState, "Should start in default state");
for (let widgetId of [kXULWidgetId, kAPIWidgetId]) {
From 26ee723822539b976f76729b02dc52f21e501e60 Mon Sep 17 00:00:00 2001
From: Gijs Kruitbosch
Date: Wed, 20 Nov 2013 19:30:06 +0100
Subject: [PATCH 12/27] Bug 941083 - invalidate widget wrappers on createWidget
calls, r=mconley
--HG--
extra : rebase_source : 60a2c6eb754d6c9c87fb5987fe5fbd39be2581a5
---
.../customizableui/src/CustomizableUI.jsm | 10 +++++
.../customizableui/test/browser.ini | 1 +
...3_invalidate_wrapper_cache_createWidget.js | 38 +++++++++++++++++++
3 files changed, 49 insertions(+)
create mode 100644 browser/components/customizableui/test/browser_941083_invalidate_wrapper_cache_createWidget.js
diff --git a/browser/components/customizableui/src/CustomizableUI.jsm b/browser/components/customizableui/src/CustomizableUI.jsm
index 995597607ebd..a7e07d1da760 100644
--- a/browser/components/customizableui/src/CustomizableUI.jsm
+++ b/browser/components/customizableui/src/CustomizableUI.jsm
@@ -1506,6 +1506,16 @@ let CustomizableUIInternal = {
}
gPalette.set(widget.id, widget);
+
+ // Clear our caches:
+ gGroupWrapperCache.delete(widget.id);
+ for (let [win, ] of gBuildWindows) {
+ let cache = gSingleWrapperCache.get(win);
+ if (cache) {
+ cache.delete(widget.id);
+ }
+ }
+
this.notifyListeners("onWidgetCreated", widget.id);
if (widget.defaultArea) {
diff --git a/browser/components/customizableui/test/browser.ini b/browser/components/customizableui/test/browser.ini
index 37ffad7f2f55..e89ad23a32cb 100644
--- a/browser/components/customizableui/test/browser.ini
+++ b/browser/components/customizableui/test/browser.ini
@@ -36,4 +36,5 @@ skip-if = true
skip-if = os == "mac"
[browser_938980_navbar_collapsed.js]
+[browser_941083_invalidate_wrapper_cache_createWidget.js]
[browser_panel_toggle.js]
diff --git a/browser/components/customizableui/test/browser_941083_invalidate_wrapper_cache_createWidget.js b/browser/components/customizableui/test/browser_941083_invalidate_wrapper_cache_createWidget.js
new file mode 100644
index 000000000000..c322b0a47db1
--- /dev/null
+++ b/browser/components/customizableui/test/browser_941083_invalidate_wrapper_cache_createWidget.js
@@ -0,0 +1,38 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=941083
+
+const kWidgetId = "test-invalidate-wrapper-cache";
+
+let gTests = [
+ {
+ desc: "Check createWidget invalidates the widget cache",
+ run: function() {
+ let groupWrapper = CustomizableUI.getWidget(kWidgetId);
+ ok(groupWrapper, "Should get group wrapper.");
+ let singleWrapper = groupWrapper.forWindow(window);
+ ok(singleWrapper, "Should get single wrapper.");
+
+ CustomizableUI.createWidget({id: kWidgetId, label: "Test invalidating widgets caching"});
+
+ let newGroupWrapper = CustomizableUI.getWidget(kWidgetId);
+ ok(newGroupWrapper, "Should get a group wrapper again.");
+ isnot(newGroupWrapper, groupWrapper, "Wrappers shouldn't be the same.");
+ isnot(newGroupWrapper.provider, groupWrapper.provider, "Wrapper providers shouldn't be the same.");
+
+ let newSingleWrapper = newGroupWrapper.forWindow(window);
+ isnot(newSingleWrapper, singleWrapper, "Single wrappers shouldn't be the same.");
+ isnot(newSingleWrapper.provider, singleWrapper.provider, "Single wrapper providers shouldn't be the same.");
+
+ CustomizableUI.destroyWidget(kWidgetId);
+ ok(!CustomizableUI.getWidget(kWidgetId), "Shouldn't get a wrapper after destroying the widget.");
+ },
+ },
+];
+
+function test() {
+ waitForExplicitFinish();
+ runTests(gTests);
+}
From e918aedee1aa10f6c18257457d3c9a9408e6f73b Mon Sep 17 00:00:00 2001
From: Jim Mathies
Date: Wed, 20 Nov 2013 12:45:43 -0600
Subject: [PATCH 13/27] Bug 940632 - Fixup findbar scroll positioning code and
remove some text zoom cruft. r=mbrubeck
---
browser/metro/base/content/AnimatedZoom.js | 124 ------------------
.../metro/base/content/ContentAreaObserver.js | 10 +-
.../metro/base/content/bindings/browser.xml | 47 -------
browser/metro/base/content/browser-scripts.js | 7 -
browser/metro/base/content/browser.js | 35 -----
.../content/contenthandlers/FindHandler.js | 28 ++--
.../base/content/helperui/FindHelperUI.js | 57 ++++----
.../content/helperui/SelectionHelperUI.js | 3 -
browser/metro/base/jar.mn | 1 -
browser/metro/profile/metro.js | 3 -
browser/metro/theme/browser.css | 5 -
11 files changed, 50 insertions(+), 270 deletions(-)
delete mode 100644 browser/metro/base/content/AnimatedZoom.js
diff --git a/browser/metro/base/content/AnimatedZoom.js b/browser/metro/base/content/AnimatedZoom.js
deleted file mode 100644
index 44108fe3b41d..000000000000
--- a/browser/metro/base/content/AnimatedZoom.js
+++ /dev/null
@@ -1,124 +0,0 @@
-// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/**
- * Responsible for zooming in to a given view rectangle
- */
-const AnimatedZoom = {
- startScale: null,
-
- /** Starts an animated zoom to zoomRect. */
- animateTo: function(aZoomRect) {
- if (!aZoomRect)
- return;
-
- this.zoomTo = aZoomRect.clone();
-
- if (this.animationDuration === undefined)
- this.animationDuration = Services.prefs.getIntPref("browser.ui.zoom.animationDuration");
-
- Browser.forceChromeReflow();
-
- this.start();
-
- // Check if zooming animations were occuring before.
- if (!this.zoomRect) {
- this.updateTo(this.zoomFrom);
-
- mozRequestAnimationFrame(this);
-
- let event = document.createEvent("Events");
- event.initEvent("AnimatedZoomBegin", true, true);
- window.dispatchEvent(event);
- }
- },
-
- start: function start() {
- this.tab = Browser.selectedTab;
- this.browser = this.tab.browser;
- this.bcr = this.browser.getBoundingClientRect();
- this.zoomFrom = this.zoomRect || this.getStartRect();
- this.startScale = this.browser.scale;
- this.beginTime = mozAnimationStartTime;
- },
-
- /** Get the visible rect, in device pixels relative to the content origin. */
- getStartRect: function getStartRect() {
- let browser = this.browser;
- let scroll = browser.getRootView().getPosition();
- return new Rect(scroll.x, scroll.y, this.bcr.width, this.bcr.height);
- },
-
- /** Update the visible rect, in device pixels relative to the content origin. */
- updateTo: function(nextRect) {
- // Stop animating if the browser has been destroyed
- if (typeof this.browser.fuzzyZoom !== "function") {
- this.reset();
- return false;
- }
-
- let zoomRatio = this.bcr.width / nextRect.width;
- let scale = this.startScale * zoomRatio;
- let scrollX = nextRect.left * zoomRatio;
- let scrollY = nextRect.top * zoomRatio;
-
- this.browser.fuzzyZoom(scale, scrollX, scrollY);
-
- this.zoomRect = nextRect;
- return true;
- },
-
- /** Stop animation, zoom to point, and clean up. */
- finish: function() {
- if (!this.updateTo(this.zoomTo || this.zoomRect))
- return;
-
- // Check whether the zoom limits have changed since the animation started.
- let browser = this.browser;
- let finalScale = this.tab.clampZoomLevel(browser.scale);
- if (browser.scale != finalScale)
- browser.scale = finalScale; // scale= calls finishFuzzyZoom.
- else
- browser.finishFuzzyZoom();
-
- this.reset();
- browser._updateCSSViewport();
- },
-
- reset: function reset() {
- this.beginTime = null;
- this.zoomTo = null;
- this.zoomFrom = null;
- this.zoomRect = null;
- this.startScale = null;
-
- let event = document.createEvent("Events");
- event.initEvent("AnimatedZoomEnd", true, true);
- window.dispatchEvent(event);
- },
-
- isZooming: function isZooming() {
- return this.beginTime != null;
- },
-
- sample: function(aTimeStamp) {
- try {
- let tdiff = aTimeStamp - this.beginTime;
- let counter = tdiff / this.animationDuration;
- if (counter < 1) {
- // update browser to interpolated rectangle
- let rect = this.zoomFrom.blend(this.zoomTo, counter);
- if (this.updateTo(rect))
- mozRequestAnimationFrame(this);
- } else {
- // last cycle already rendered final scaled image, now clean up
- this.finish();
- }
- } catch(e) {
- this.finish();
- throw e;
- }
- }
-};
diff --git a/browser/metro/base/content/ContentAreaObserver.js b/browser/metro/base/content/ContentAreaObserver.js
index 6d4de6fbd0be..9c6fe8922bbd 100644
--- a/browser/metro/base/content/ContentAreaObserver.js
+++ b/browser/metro/base/content/ContentAreaObserver.js
@@ -99,7 +99,7 @@ var ContentAreaObserver = {
Services.obs.addObserver(this, "metro_softkeyboard_hidden", false);
// setup initial values for browser form repositioning
- this._shiftBrowserDeck(0);
+ this.shiftBrowserDeck(0);
// initialize our custom width and height styles
this._initStyles();
@@ -226,7 +226,7 @@ var ContentAreaObserver = {
this.updateViewableArea();
if (!aNewState) {
- this._shiftBrowserDeck(0);
+ this.shiftBrowserDeck(0);
return;
}
@@ -241,10 +241,10 @@ var ContentAreaObserver = {
_onRepositionResponse: function _onRepositionResponse(aJsonMsg) {
if (!aJsonMsg.reposition || !this.isKeyboardOpened) {
- this._shiftBrowserDeck(0);
+ this.shiftBrowserDeck(0);
return;
}
- this._shiftBrowserDeck(aJsonMsg.raiseContent);
+ this.shiftBrowserDeck(aJsonMsg.raiseContent);
},
observe: function cao_observe(aSubject, aTopic, aData) {
@@ -300,7 +300,7 @@ var ContentAreaObserver = {
}
},
- _shiftBrowserDeck: function _shiftBrowserDeck(aAmount) {
+ shiftBrowserDeck: function (aAmount) {
if (aAmount == 0) {
this._deckTransitioning = false;
this._dispatchWindowEvent("KeyboardChanged", this.isKeyboardOpened);
diff --git a/browser/metro/base/content/bindings/browser.xml b/browser/metro/base/content/bindings/browser.xml
index 9c5d6841d18e..95b2c95e453e 100644
--- a/browser/metro/base/content/bindings/browser.xml
+++ b/browser/metro/base/content/bindings/browser.xml
@@ -760,24 +760,6 @@
this.docShellIsActive = this._active;
]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1150,34 +1132,6 @@
]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/browser/metro/base/content/browser-scripts.js b/browser/metro/base/content/browser-scripts.js
index b3bc91365f1c..bd4d7f3fe84c 100644
--- a/browser/metro/base/content/browser-scripts.js
+++ b/browser/metro/base/content/browser-scripts.js
@@ -102,7 +102,6 @@ let ScriptContexts = {};
["SelectionHelperUI", "chrome://browser/content/helperui/SelectionHelperUI.js"],
["SelectionPrototype", "chrome://browser/content/library/SelectionPrototype.js"],
["ChromeSelectionHandler", "chrome://browser/content/helperui/ChromeSelectionHandler.js"],
- ["AnimatedZoom", "chrome://browser/content/AnimatedZoom.js"],
["CommandUpdater", "chrome://browser/content/commandUtil.js"],
["ContextCommands", "chrome://browser/content/ContextCommands.js"],
["Bookmarks", "chrome://browser/content/bookmarks.js"],
@@ -152,9 +151,3 @@ XPCOMUtils.defineLazyGetter(this, "ContentAreaUtils", function() {
Services.scriptloader.loadSubScript("chrome://global/content/contentAreaUtils.js", ContentAreaUtils);
return ContentAreaUtils;
});
-
-XPCOMUtils.defineLazyGetter(this, "ZoomManager", function() {
- let sandbox = {};
- Services.scriptloader.loadSubScript("chrome://global/content/viewZoomOverlay.js", sandbox);
- return sandbox.ZoomManager;
-});
diff --git a/browser/metro/base/content/browser.js b/browser/metro/base/content/browser.js
index 3e4b0e7982b4..a60f73dbc907 100644
--- a/browser/metro/base/content/browser.js
+++ b/browser/metro/base/content/browser.js
@@ -781,33 +781,6 @@ var Browser = {
Bookmarks.isURIBookmarked(uri, callback);
},
- /** Rect should be in browser coordinates. */
- _getZoomLevelForRect: function _getZoomLevelForRect(rect) {
- const margin = 15;
- return this.selectedTab.clampZoomLevel(ContentAreaObserver.width / (rect.width + margin * 2));
- },
-
- /**
- * Find a good zoom rectangle for point that is specified in browser coordinates.
- * @return Rect in viewport coordinates
- */
- _getZoomRectForPoint: function _getZoomRectForPoint(x, y, zoomLevel) {
- let browser = getBrowser();
- x = x * browser.scale;
- y = y * browser.scale;
-
- zoomLevel = Math.min(ZoomManager.MAX, zoomLevel);
- let oldScale = browser.scale;
- let zoomRatio = zoomLevel / oldScale;
- let browserRect = browser.getBoundingClientRect();
- let newVisW = browserRect.width / zoomRatio, newVisH = browserRect.height / zoomRatio;
- let result = new Rect(x - newVisW / 2, y - newVisH / 2, newVisW, newVisH);
-
- // Make sure rectangle doesn't poke out of viewport
- return result.translateInside(new Rect(0, 0, browser.contentDocumentWidth * oldScale,
- browser.contentDocumentHeight * oldScale));
- },
-
/**
* Convenience function for getting the scrollbox position off of a
* scrollBoxObject interface. Returns the actual values instead of the
@@ -1496,14 +1469,6 @@ Tab.prototype = {
}
},
- /**
- * Takes a scale and restricts it based on this tab's zoom limits.
- * @param aScale The original scale.
- */
- clampZoomLevel: function clampZoomLevel(aScale) {
- return Util.clamp(aScale, ZoomManager.MIN, ZoomManager.MAX);
- },
-
updateThumbnail: function updateThumbnail() {
PageThumbs.captureToCanvas(this.browser.contentWindow, this._chromeTab.thumbnailCanvas);
},
diff --git a/browser/metro/base/content/contenthandlers/FindHandler.js b/browser/metro/base/content/contenthandlers/FindHandler.js
index 50da28931b82..0c0ea8442ebe 100644
--- a/browser/metro/base/content/contenthandlers/FindHandler.js
+++ b/browser/metro/base/content/contenthandlers/FindHandler.js
@@ -36,7 +36,7 @@ var FindHandler = {
}
if (findResult == Ci.nsITypeAheadFind.FIND_NOTFOUND) {
- sendAsyncMessage("FindAssist:Show", { rect: null , result: findResult });
+ sendAsyncMessage("FindAssist:Show", { rect: null, result: findResult });
return;
}
@@ -57,30 +57,28 @@ var FindHandler = {
}
}
- let scroll = ContentScroll.getScrollOffset(content);
+ // Return the bounding selection rect in content coordinates
+ // including the scroll offset.
+
+ let offset = ContentScroll.getScrollOffset(content);
for (let frame = this._fastFind.currentWindow; frame != content; frame = frame.parent) {
let rect = frame.frameElement.getBoundingClientRect();
let left = frame.getComputedStyle(frame.frameElement, "").borderLeftWidth;
let top = frame.getComputedStyle(frame.frameElement, "").borderTopWidth;
- scroll.add(rect.left + parseInt(left), rect.top + parseInt(top));
+ offset.add(rect.left + parseInt(left), rect.top + parseInt(top));
}
let rangeRect = selection.getRangeAt(0).getBoundingClientRect();
- let rect = new Rect(scroll.x + rangeRect.left, scroll.y + rangeRect.top, rangeRect.width, rangeRect.height);
-
- let aNewViewHeight = content.innerHeight - Services.metro.keyboardHeight;
-
- let position = Util.centerElementInView(aNewViewHeight, rangeRect);
- if (position !== undefined) {
- sendAsyncMessage("Content:RepositionInfoResponse", {
- reposition: true,
- raiseContent: position,
- });
- }
+ let rect = new Rect(offset.x + rangeRect.left, offset.y + rangeRect.top,
+ rangeRect.width, rangeRect.height);
// Ensure the potential "scroll" event fired during a search as already fired
let timer = new Util.Timeout(function() {
- sendAsyncMessage("FindAssist:Show", { rect: rect.isEmpty() ? null: rect , result: findResult });
+ sendAsyncMessage("FindAssist:Show", {
+ rect: rect.isEmpty() ? null: rect,
+ contentHeight: content.document.documentElement.scrollHeight,
+ result: findResult
+ });
});
timer.once(0);
}
diff --git a/browser/metro/base/content/helperui/FindHelperUI.js b/browser/metro/base/content/helperui/FindHelperUI.js
index a78fd9df7d3d..1ef15d18adf4 100644
--- a/browser/metro/base/content/helperui/FindHelperUI.js
+++ b/browser/metro/base/content/helperui/FindHelperUI.js
@@ -66,8 +66,10 @@ var FindHelperUI = {
case "FindAssist:Show":
ContextUI.dismiss();
this.status = json.result;
- if (json.rect)
- this._zoom(Rect.fromRect(json.rect));
+ // null rect implies nothing found
+ if (json.rect) {
+ this._zoom(Rect.fromRect(json.rect), json.contentHeight);
+ }
break;
case "FindAssist:Hide":
@@ -118,9 +120,7 @@ var FindHelperUI = {
let findbar = this._container;
setTimeout(() => {
- Elements.browsers.setAttribute("findbar", true);
findbar.show();
-
this.search(this._textbox.value);
this._textbox.select();
this._textbox.focus();
@@ -136,6 +136,8 @@ var FindHelperUI = {
if (!this._open)
return;
+ ContentAreaObserver.shiftBrowserDeck(0);
+
let onTransitionEnd = () => {
this._container.removeEventListener("transitionend", onTransitionEnd, true);
this._textbox.value = "";
@@ -149,7 +151,6 @@ var FindHelperUI = {
this._textbox.blur();
this._container.addEventListener("transitionend", onTransitionEnd, true);
this._container.dismiss();
- Elements.browsers.removeAttribute("findbar");
},
goToPrevious: function findHelperGoToPrevious() {
@@ -172,27 +173,33 @@ var FindHelperUI = {
this._cmdNext.setAttribute("disabled", disabled);
},
- _zoom: function _findHelperZoom(aElementRect) {
- let autozoomEnabled = Services.prefs.getBoolPref("findhelper.autozoom");
- if (!aElementRect || !autozoomEnabled)
- return;
+ _zoom: function _findHelperZoom(aElementRect, aContentHeight) {
+ // The rect we get here is the content rect including scroll offset
+ // in the page.
- if (Browser.selectedTab.allowZoom) {
- let zoomLevel = Browser._getZoomLevelForRect(aElementRect);
-
- // Clamp the zoom level relatively to the default zoom level of the page
- let defaultZoomLevel = Browser.selectedTab.getDefaultZoomLevel();
- zoomLevel = Util.clamp(zoomLevel, (defaultZoomLevel * kBrowserFindZoomLevelMin),
- (defaultZoomLevel * kBrowserFindZoomLevelMax));
- zoomLevel = Browser.selectedTab.clampZoomLevel(zoomLevel);
-
- let zoomRect = Browser._getZoomRectForPoint(aElementRect.center().x, aElementRect.y, zoomLevel);
- AnimatedZoom.animateTo(zoomRect);
- } else {
- // Even if zooming is disabled we could need to reposition the view in
- // order to keep the element on-screen
- let zoomRect = Browser._getZoomRectForPoint(aElementRect.center().x, aElementRect.y, getBrowser().scale);
- AnimatedZoom.animateTo(zoomRect);
+ // If the text falls below the find bar and keyboard shift content up.
+ let browserShift = 0;
+ // aElementRect.y is the top left origin of the selection rect.
+ if ((aElementRect.y + aElementRect.height) >
+ (aContentHeight - this._container.boxObject.height)) {
+ browserShift += this._container.boxObject.height;
}
+ browserShift += Services.metro.keyboardHeight;
+ ContentAreaObserver.shiftBrowserDeck(browserShift);
+
+ // Adjust for keyboad display and position the text selection rect in
+ // the middle of the viewable area.
+ let xPos = aElementRect.x;
+ let yPos = aElementRect.y;
+ let scrollAdjust = ((ContentAreaObserver.height - Services.metro.keyboardHeight) * .5) +
+ Services.metro.keyboardHeight;
+ yPos -= scrollAdjust;
+ if (yPos < 0) {
+ yPos = 0;
+ }
+
+ // TODO zoom via apzc, right now all we support is scroll
+ // positioning.
+ Browser.selectedBrowser.contentWindow.scrollTo(xPos, yPos);
}
};
diff --git a/browser/metro/base/content/helperui/SelectionHelperUI.js b/browser/metro/base/content/helperui/SelectionHelperUI.js
index a0073f101377..a7031110b951 100644
--- a/browser/metro/base/content/helperui/SelectionHelperUI.js
+++ b/browser/metro/base/content/helperui/SelectionHelperUI.js
@@ -587,7 +587,6 @@ var SelectionHelperUI = {
Elements.browsers.addEventListener("URLChanged", this, true);
Elements.browsers.addEventListener("SizeChanged", this, true);
- Elements.browsers.addEventListener("ZoomChanged", this, true);
Elements.navbar.addEventListener("transitionend", this, true);
Elements.navbar.addEventListener("MozAppbarDismissing", this, true);
@@ -615,7 +614,6 @@ var SelectionHelperUI = {
Elements.browsers.removeEventListener("URLChanged", this, true);
Elements.browsers.removeEventListener("SizeChanged", this, true);
- Elements.browsers.removeEventListener("ZoomChanged", this, true);
Elements.navbar.removeEventListener("transitionend", this, true);
Elements.navbar.removeEventListener("MozAppbarDismissing", this, true);
@@ -1080,7 +1078,6 @@ var SelectionHelperUI = {
this._shutdown();
break;
- case "ZoomChanged":
case "MozPrecisePointer":
this.closeEditSession(true);
break;
diff --git a/browser/metro/base/jar.mn b/browser/metro/base/jar.mn
index 27395dba3b16..1d014484e297 100644
--- a/browser/metro/base/jar.mn
+++ b/browser/metro/base/jar.mn
@@ -80,7 +80,6 @@ chrome.jar:
content/Site.js (content/Site.js)
content/TopSites.js (content/TopSites.js)
content/console.js (content/console.js)
- content/AnimatedZoom.js (content/AnimatedZoom.js)
content/dbg-metro-actors.js (content/dbg-metro-actors.js)
#ifdef MOZ_SERVICES_SYNC
content/flyoutpanels/SyncFlyoutPanel.js (content/flyoutpanels/SyncFlyoutPanel.js)
diff --git a/browser/metro/profile/metro.js b/browser/metro/profile/metro.js
index ce8c76fca912..aa6e58babc6c 100644
--- a/browser/metro/profile/metro.js
+++ b/browser/metro/profile/metro.js
@@ -193,9 +193,6 @@ pref("browser.helperApps.deleteTempFileOnExit", false);
/* password manager */
pref("signon.rememberSignons", true);
-/* find helper */
-pref("findhelper.autozoom", true);
-
// this will automatically enable inline spellchecking (if it is available) for
// editable elements in HTML
// 0 = spellcheck nothing
diff --git a/browser/metro/theme/browser.css b/browser/metro/theme/browser.css
index 02f73b49bb15..5ddf49e367a4 100644
--- a/browser/metro/theme/browser.css
+++ b/browser/metro/theme/browser.css
@@ -252,11 +252,6 @@ documenttab[selected] .documenttab-selection {
transition-delay: 0s;
transition-property: padding-bottom;
}
-#browsers[findbar] browser {
- /* delay setting padding-bottom until the findbar has transitioned in */
- transition-delay: @metro_animation_duration@;
- padding-bottom: @findbar_height@;
-}
/* Selection overlay and monocles */
#page,
.selection-overlay {
From 6114e8345c8004161aba85e08e3c8c7c09df1507 Mon Sep 17 00:00:00 2001
From: Jim Mathies
Date: Wed, 20 Nov 2013 12:45:43 -0600
Subject: [PATCH 14/27] Bug 936567 - Add proper support for apzc scale in
browser binding utils, fixing up various ui element positioning issues with
zoom. r=mbrubeck
---
.../metro/base/content/bindings/browser.xml | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/browser/metro/base/content/bindings/browser.xml b/browser/metro/base/content/bindings/browser.xml
index 95b2c95e453e..91b124166402 100644
--- a/browser/metro/base/content/bindings/browser.xml
+++ b/browser/metro/base/content/bindings/browser.xml
@@ -292,6 +292,19 @@