diff --git a/b2g/Makefile.in b/b2g/Makefile.in
index f28113192bb..58affc5b02c 100644
--- a/b2g/Makefile.in
+++ b/b2g/Makefile.in
@@ -42,7 +42,7 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
-DIRS = chrome locales app
+DIRS = chrome components locales app
include $(topsrcdir)/config/rules.mk
include $(topsrcdir)/testing/testsuite-targets.mk
diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js
index 230de4f418d..c9b7c7ab224 100644
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -65,11 +65,7 @@ pref("browser.cache.memory.capacity", 1024); // kilobytes
pref("image.cache.size", 1048576); // bytes
/* offline cache prefs */
-pref("browser.offline-apps.notify", true);
-pref("browser.cache.offline.enable", true);
-pref("browser.cache.offline.capacity", 5120); // kilobytes
-pref("offline-apps.quota.max", 2048); // kilobytes
-pref("offline-apps.quota.warn", 1024); // kilobytes
+pref("browser.offline-apps.notify", false);
/* protocol warning prefs */
pref("network.protocol-handler.warn-external.tel", false);
diff --git a/b2g/chrome/content/shell.js b/b2g/chrome/content/shell.js
index 0e7f83c6c77..01996bb115e 100644
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -68,6 +68,7 @@ function startupHttpd(baseDir, port) {
Services.scriptloader.loadSubScript(httpdURL, httpd);
let server = new httpd.nsHttpServer();
server.registerDirectory('/', new LocalFile(baseDir));
+ server.registerContentType('appcache', 'text/cache-manifest');
server.start(port);
}
@@ -114,6 +115,7 @@ var shell = {
window.controllers.appendController(this);
window.addEventListener('keypress', this);
+ window.addEventListener('MozApplicationManifest', this);
this.home.addEventListener('load', this, true);
try {
@@ -147,6 +149,7 @@ var shell = {
stop: function shell_stop() {
window.controllers.removeController(this);
window.removeEventListener('keypress', this);
+ window.removeEventListener('MozApplicationManifest', this);
},
supportsCommand: function shell_supportsCommand(cmd) {
@@ -195,6 +198,29 @@ var shell = {
this.home.removeEventListener('load', this, true);
this.sendEvent(window, 'ContentStart');
break;
+ case 'MozApplicationManifest':
+ try {
+ let contentWindow = evt.originalTarget.defaultView;
+ let documentElement = contentWindow.document.documentElement;
+ if (!documentElement)
+ return;
+
+ let manifest = documentElement.getAttribute("manifest");
+ if (!manifest)
+ return;
+
+ let documentURI = contentWindow.document.documentURIObject;
+ let manifestURI = Services.io.newURI(manifest, null, documentURI);
+ Services.perms.add(documentURI, 'offline-app',
+ Ci.nsIPermissionManager.ALLOW_ACTION);
+
+ let updateService = Cc['@mozilla.org/offlinecacheupdate-service;1']
+ .getService(Ci.nsIOfflineCacheUpdateService);
+ updateService.scheduleUpdate(manifestURI, documentURI, window);
+ } catch (e) {
+ dump('Error while creating offline cache: ' + e + '\n');
+ }
+ break;
}
},
sendEvent: function shell_sendEvent(content, type, details) {
diff --git a/b2g/components/B2GComponents.manifest b/b2g/components/B2GComponents.manifest
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/intl/uconv/util/external/Makefile.in b/b2g/components/Makefile.in
similarity index 72%
rename from intl/uconv/util/external/Makefile.in
rename to b2g/components/Makefile.in
index b73caddd4f5..a837f027699 100644
--- a/intl/uconv/util/external/Makefile.in
+++ b/b2g/components/Makefile.in
@@ -1,4 +1,3 @@
-#
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
@@ -12,18 +11,18 @@
# for the specific language governing rights and limitations under the
# License.
#
-# The Original Code is mozilla.org code.
+# The Original Code is Mozilla.
#
# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
+# the Mozilla Foundation .
+# Portions created by the Initial Developer are Copyright (C) 2011
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
-# either of the GNU General Public License Version 2 or later (the "GPL"),
-# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
@@ -35,23 +34,18 @@
#
# ***** END LICENSE BLOCK *****
-DEPTH = ../../../..
-topsrcdir = @top_srcdir@
-srcdir = @srcdir@
-VPATH = @srcdir@
+DEPTH = ../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
-MODULE = uconv
-LIBRARY_NAME = ucvxutil_s
-EXPORT_LIBRARY = 1
-FORCE_STATIC_LIB = 1
+MODULE = B2GComponents
+XPIDL_MODULE = B2GComponents
-MODULE_NAME = nsExternalUCUtil
-
-VPATH += $(srcdir)/..
-
-include $(srcdir)/../objs.mk
+EXTRA_PP_COMPONENTS = \
+ B2GComponents.manifest \
+ $(NULL)
include $(topsrcdir)/config/rules.mk
-
diff --git a/b2g/makefiles.sh b/b2g/makefiles.sh
index b7d723519e6..68b28946222 100644
--- a/b2g/makefiles.sh
+++ b/b2g/makefiles.sh
@@ -42,6 +42,7 @@ security/manager/locales/Makefile
b2g/app/Makefile
$MOZ_BRANDING_DIRECTORY/Makefile
b2g/chrome/Makefile
+b2g/components/Makefile
b2g/installer/Makefile
b2g/locales/Makefile
b2g/Makefile"
diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
index 445fe9b023a..07e5548e2cc 100644
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -945,6 +945,7 @@ pref("services.sync.prefs.sync.browser.safebrowsing.enabled", true);
pref("services.sync.prefs.sync.browser.safebrowsing.malware.enabled", true);
pref("services.sync.prefs.sync.browser.search.selectedEngine", true);
pref("services.sync.prefs.sync.browser.search.update", true);
+pref("services.sync.prefs.sync.browser.sessionstore.restore_on_demand", true);
pref("services.sync.prefs.sync.browser.startup.homepage", true);
pref("services.sync.prefs.sync.browser.startup.page", true);
pref("services.sync.prefs.sync.browser.tabs.autoHide", true);
diff --git a/browser/components/places/content/editBookmarkOverlay.js b/browser/components/places/content/editBookmarkOverlay.js
index 44c3ded0020..51096d14f6b 100644
--- a/browser/components/places/content/editBookmarkOverlay.js
+++ b/browser/components/places/content/editBookmarkOverlay.js
@@ -221,8 +221,9 @@ var gEditItemOverlay = {
this._allTags = this._getCommonTags();
this._initTextField("tagsField", this._allTags.join(", "), false);
this._element("itemsCountText").value =
- PlacesUIUtils.getFormattedString("detailsPane.multipleItems",
- [this._itemIds.length]);
+ PlacesUIUtils.getPluralString("detailsPane.itemsCountLabel",
+ this._itemIds.length,
+ [this._itemIds.length]);
}
// tags selector
diff --git a/browser/components/places/content/places.js b/browser/components/places/content/places.js
index 9cf3db93510..1e24d9e659f 100644
--- a/browser/components/places/content/places.js
+++ b/browser/components/places/content/places.js
@@ -713,8 +713,8 @@ var PlacesOrganizer = {
var itemsCountLabel = document.getElementById("itemsCountText");
selectItemDesc.hidden = false;
itemsCountLabel.value =
- PlacesUIUtils.getFormattedString("detailsPane.multipleItems",
- [aNodeList.length]);
+ PlacesUIUtils.getPluralString("detailsPane.itemsCountLabel",
+ aNodeList.length, [aNodeList.length]);
infoBox.hidden = true;
return;
}
@@ -743,13 +743,9 @@ var PlacesOrganizer = {
}
else {
selectItemDesc.hidden = false;
- if (rowCount == 1)
- itemsCountLabel.value = PlacesUIUtils.getString("detailsPane.oneItem");
- else {
- itemsCountLabel.value =
- PlacesUIUtils.getFormattedString("detailsPane.multipleItems",
- [rowCount]);
- }
+ itemsCountLabel.value =
+ PlacesUIUtils.getPluralString("detailsPane.itemsCountLabel",
+ rowCount, [rowCount]);
}
}
},
diff --git a/browser/components/places/src/PlacesUIUtils.jsm b/browser/components/places/src/PlacesUIUtils.jsm
index ab9679b8257..0b63574b53c 100644
--- a/browser/components/places/src/PlacesUIUtils.jsm
+++ b/browser/components/places/src/PlacesUIUtils.jsm
@@ -50,6 +50,9 @@ var Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
+ "resource://gre/modules/PluralForm.jsm");
+
XPCOMUtils.defineLazyGetter(this, "PlacesUtils", function() {
Cu.import("resource://gre/modules/PlacesUtils.jsm");
return PlacesUtils;
@@ -79,6 +82,31 @@ var PlacesUIUtils = {
return bundle.formatStringFromName(key, params, params.length);
},
+ /**
+ * Get a localized plural string for the specified key name and numeric value
+ * substituting parameters.
+ *
+ * @param aKey
+ * String, key for looking up the localized string in the bundle
+ * @param aNumber
+ * Number based on which the final localized form is looked up
+ * @param aParams
+ * Array whose items will substitute #1, #2,... #n parameters
+ * in the string.
+ *
+ * @see https://developer.mozilla.org/en/Localization_and_Plurals
+ * @return The localized plural string.
+ */
+ getPluralString: function PUIU_getPluralString(aKey, aNumber, aParams) {
+ let str = PluralForm.get(aNumber, bundle.GetStringFromName(aKey));
+
+ // Replace #1 with aParams[0], #2 with aParams[1], and so on.
+ return str.replace(/\#(\d+)/g, function (matchedId, matchedNumber) {
+ let param = aParams[parseInt(matchedNumber, 10) - 1];
+ return param !== undefined ? param : matchedId;
+ });
+ },
+
getString: function PUIU_getString(key) {
return bundle.GetStringFromName(key);
},
diff --git a/browser/components/preferences/main.js b/browser/components/preferences/main.js
index 33962566503..ac611d846c7 100644
--- a/browser/components/preferences/main.js
+++ b/browser/components/preferences/main.js
@@ -50,7 +50,7 @@ var gMainPane = {
// set up the "use current page" label-changing listener
this._updateUseCurrentButton();
- window.addEventListener("focus", this._updateUseCurrentButton, false);
+ window.addEventListener("focus", this._updateUseCurrentButton.bind(this), false);
this.updateBrowserStartupLastSession();
this.startupPagePrefChanged();
@@ -128,23 +128,13 @@ var gMainPane = {
*/
setHomePageToCurrent: function ()
{
- var win;
- if (document.documentElement.instantApply) {
- // If we're in instant-apply mode, use the most recent browser window
- var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
- .getService(Components.interfaces.nsIWindowMediator);
- win = wm.getMostRecentWindow("navigator:browser");
- }
- else
- win = window.opener;
+ let homePage = document.getElementById("browser.startup.homepage");
+ let tabs = this._getTabsForHomePage();
+ function getTabURI(t) t.linkedBrowser.currentURI.spec;
- if (win) {
- var homePage = document.getElementById("browser.startup.homepage");
- var tabs = win.gBrowser.visibleTabs;
- function getTabURI(t) t.linkedBrowser.currentURI.spec;
- // FIXME Bug 244192: using dangerous "|" joiner!
+ // FIXME Bug 244192: using dangerous "|" joiner!
+ if (tabs.length)
homePage.value = tabs.map(getTabURI).join("|");
- }
},
/**
@@ -170,10 +160,26 @@ var gMainPane = {
* forms.
*/
_updateUseCurrentButton: function () {
- var useCurrent = document.getElementById("useCurrent");
+ let useCurrent = document.getElementById("useCurrent");
- var windowIsPresent;
+ let tabs = this._getTabsForHomePage();
+ if (tabs.length > 1)
+ useCurrent.label = useCurrent.getAttribute("label2");
+ else
+ useCurrent.label = useCurrent.getAttribute("label1");
+
+ // In this case, the button's disabled state is set by preferences.xml.
+ if (document.getElementById
+ ("pref.browser.homepage.disable_button.current_page").locked)
+ return;
+
+ useCurrent.disabled = !tabs.length
+ },
+
+ _getTabsForHomePage: function ()
+ {
var win;
+ var tabs = [];
if (document.documentElement.instantApply) {
const Cc = Components.classes, Ci = Components.interfaces;
// If we're in instant-apply mode, use the most recent browser window
@@ -181,30 +187,17 @@ var gMainPane = {
.getService(Ci.nsIWindowMediator);
win = wm.getMostRecentWindow("navigator:browser");
}
- else
+ else {
win = window.opener;
+ }
if (win && win.document.documentElement
.getAttribute("windowtype") == "navigator:browser") {
- windowIsPresent = true;
-
- var tabbrowser = win.document.getElementById("content");
- if (tabbrowser.browsers.length > 1)
- useCurrent.label = useCurrent.getAttribute("label2");
- else
- useCurrent.label = useCurrent.getAttribute("label1");
- }
- else {
- windowIsPresent = false;
- useCurrent.label = useCurrent.getAttribute("label1");
+ // We should only include visible & non-pinned tabs
+ tabs = win.gBrowser.visibleTabs.slice(win.gBrowser._numPinnedTabs);
}
- // In this case, the button's disabled state is set by preferences.xml.
- if (document.getElementById
- ("pref.browser.homepage.disable_button.current_page").locked)
- return;
-
- useCurrent.disabled = !windowIsPresent;
+ return tabs;
},
/**
diff --git a/browser/components/sessionstore/test/browser/browser_644409-scratchpads.js b/browser/components/sessionstore/test/browser/browser_644409-scratchpads.js
index a81746e9357..0058c44aa7b 100644
--- a/browser/components/sessionstore/test/browser/browser_644409-scratchpads.js
+++ b/browser/components/sessionstore/test/browser/browser_644409-scratchpads.js
@@ -38,11 +38,19 @@ function test() {
function windowObserver(aSubject, aTopic, aData) {
if (aTopic == "domwindowopened") {
let win = aSubject.QueryInterface(Ci.nsIDOMWindow);
- win.addEventListener("load", function() {
+ win.addEventListener("load", function onLoad() {
+ win.removeEventListener("load", onLoad, false);
+
if (win.Scratchpad) {
- let state = win.Scratchpad.getState();
- win.close();
- addState(state);
+ win.Scratchpad.addObserver({
+ onReady: function() {
+ win.Scratchpad.removeObserver(this);
+
+ let state = win.Scratchpad.getState();
+ win.close();
+ addState(state);
+ },
+ });
}
}, false);
}
@@ -56,4 +64,4 @@ function statesMatch(restored, states) {
state.executionContext == restoredState.executionContext;
})
});
-}
\ No newline at end of file
+}
diff --git a/browser/devtools/jar.mn b/browser/devtools/jar.mn
index 9a35144980b..5c367906873 100644
--- a/browser/devtools/jar.mn
+++ b/browser/devtools/jar.mn
@@ -2,7 +2,7 @@ browser.jar:
* content/browser/inspector.html (highlighter/inspector.html)
content/browser/NetworkPanel.xhtml (webconsole/NetworkPanel.xhtml)
* content/browser/scratchpad.xul (scratchpad/scratchpad.xul)
-* content/browser/scratchpad.js (scratchpad/scratchpad.js)
+ content/browser/scratchpad.js (scratchpad/scratchpad.js)
* content/browser/styleeditor.xul (styleeditor/styleeditor.xul)
content/browser/splitview.css (styleeditor/splitview.css)
content/browser/styleeditor.css (styleeditor/styleeditor.css)
diff --git a/browser/devtools/scratchpad/scratchpad-manager.jsm b/browser/devtools/scratchpad/scratchpad-manager.jsm
index 42a88d86363..ac4446d4cf2 100644
--- a/browser/devtools/scratchpad/scratchpad-manager.jsm
+++ b/browser/devtools/scratchpad/scratchpad-manager.jsm
@@ -104,7 +104,7 @@ var ScratchpadManager = {
let enumerator = Services.wm.getEnumerator("devtools:scratchpad");
while (enumerator.hasMoreElements()) {
let win = enumerator.getNext();
- if (!win.closed) {
+ if (!win.closed && win.Scratchpad.initialized) {
this._scratchpads.push(win.Scratchpad.getState());
}
}
diff --git a/browser/devtools/scratchpad/scratchpad.js b/browser/devtools/scratchpad/scratchpad.js
index 7ed3b496ad9..19525596ebe 100644
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -86,6 +86,13 @@ var Scratchpad = {
*/
executionContext: SCRATCHPAD_CONTEXT_CONTENT,
+ /**
+ * Tells if this Scratchpad is initialized and ready for use.
+ * @boolean
+ * @see addObserver
+ */
+ initialized: false,
+
/**
* Retrieve the xul:notificationbox DOM element. It notifies the user when
* the current code execution context is SCRATCHPAD_CONTEXT_BROWSER.
@@ -733,7 +740,7 @@ var Scratchpad = {
},
/**
- * The Scratchpad window DOMContentLoaded event handler. This method
+ * The Scratchpad window load event handler. This method
* initializes the Scratchpad window and source editor.
*
* @param nsIDOMEvent aEvent
@@ -784,7 +791,9 @@ var Scratchpad = {
this.onContextMenu);
this.editor.focus();
this.editor.setCaretOffset(this.editor.getCharCount());
-
+
+ this.initialized = true;
+
if (this.filename && !this.saved) {
this.onTextChanged();
}
@@ -866,7 +875,7 @@ var Scratchpad = {
if (aStatus && !Components.isSuccessCode(aStatus)) {
return;
}
- if (!document) {
+ if (!document || !this.initialized) {
return; // file saved to disk after window has closed
}
document.title = document.title.replace(/^\*/, "");
@@ -904,6 +913,7 @@ var Scratchpad = {
this.onContextMenu);
this.editor.destroy();
this.editor = null;
+ this.initialized = false;
},
/**
@@ -1033,6 +1043,6 @@ XPCOMUtils.defineLazyGetter(Scratchpad, "strings", function () {
return Services.strings.createBundle(SCRATCHPAD_L10N);
});
-addEventListener("DOMContentLoaded", Scratchpad.onLoad.bind(Scratchpad), false);
+addEventListener("load", Scratchpad.onLoad.bind(Scratchpad), false);
addEventListener("unload", Scratchpad.onUnload.bind(Scratchpad), false);
addEventListener("close", Scratchpad.onClose.bind(Scratchpad), false);
diff --git a/browser/devtools/scratchpad/test/browser_scratchpad_bug_646070_chrome_context_pref.js b/browser/devtools/scratchpad/test/browser_scratchpad_bug_646070_chrome_context_pref.js
index b2c1bdb1a04..28f6b08fec3 100644
--- a/browser/devtools/scratchpad/test/browser_scratchpad_bug_646070_chrome_context_pref.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_bug_646070_chrome_context_pref.js
@@ -2,24 +2,21 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
-let gOldPref;
let DEVTOOLS_CHROME_ENABLED = "devtools.chrome.enabled";
function test()
{
waitForExplicitFinish();
- gOldPref = Services.prefs.getBoolPref(DEVTOOLS_CHROME_ENABLED);
Services.prefs.setBoolPref(DEVTOOLS_CHROME_ENABLED, true);
gBrowser.selectedTab = gBrowser.addTab();
- gBrowser.selectedBrowser.addEventListener("load", function() {
- gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+ gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+ gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
- ok(Scratchpad, "Scratchpad variable exists");
+ ok(window.Scratchpad, "Scratchpad variable exists");
- gScratchpadWindow = Scratchpad.openScratchpad();
- gScratchpadWindow.addEventListener("load", runTests, false);
+ openScratchpad(runTests);
}, true);
content.location = "data:text/html,Scratchpad test for bug 646070 - chrome context preference";
@@ -27,8 +24,6 @@ function test()
function runTests()
{
- gScratchpadWindow.removeEventListener("load", arguments.callee, false);
-
let sp = gScratchpadWindow.Scratchpad;
ok(sp, "Scratchpad object exists in new window");
@@ -50,7 +45,7 @@ function runTests()
ok(!chromeContextCommand.hasAttribute("disabled"),
"Chrome context command is disabled");
- Services.prefs.setBoolPref(DEVTOOLS_CHROME_ENABLED, gOldPref);
+ Services.prefs.clearUserPref(DEVTOOLS_CHROME_ENABLED);
finish();
}
diff --git a/browser/devtools/scratchpad/test/browser_scratchpad_bug_653427_confirm_close.js b/browser/devtools/scratchpad/test/browser_scratchpad_bug_653427_confirm_close.js
index 9704d1b98cb..c85ec8e5f68 100644
--- a/browser/devtools/scratchpad/test/browser_scratchpad_bug_653427_confirm_close.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_bug_653427_confirm_close.js
@@ -17,10 +17,10 @@ function done()
}
}
-var ScratchpadManager = Scratchpad.ScratchpadManager;
var gFile;
var oldPrompt = Services.prompt;
+var promptButton = -1;
function test()
{
@@ -29,6 +29,12 @@ function test()
gFile = createTempFile("fileForBug653427.tmp");
writeFile(gFile, "text", testUnsaved.call(this));
+ Services.prompt = {
+ confirmEx: function() {
+ return promptButton;
+ }
+ };
+
testNew();
testSavedFile();
@@ -37,28 +43,23 @@ function test()
function testNew()
{
- let win = ScratchpadManager.openScratchpad();
-
- win.addEventListener("load", function() {
+ openScratchpad(function(win) {
win.Scratchpad.close();
-
ok(win.closed, "new scratchpad window should close without prompting")
done();
- });
+ }, {noFocus: true});
}
function testSavedFile()
{
- let win = ScratchpadManager.openScratchpad();
-
- win.addEventListener("load", function() {
+ openScratchpad(function(win) {
win.Scratchpad.filename = "test.js";
win.Scratchpad.saved = true;
win.Scratchpad.close();
ok(win.closed, "scratchpad from file with no changes should close")
done();
- });
+ }, {noFocus: true});
}
function testUnsaved()
@@ -70,17 +71,11 @@ function testUnsaved()
function testUnsavedFileCancel()
{
- let win = ScratchpadManager.openScratchpad();
-
- win.addEventListener("load", function() {
+ openScratchpad(function(win) {
win.Scratchpad.filename = "test.js";
win.Scratchpad.saved = false;
- Services.prompt = {
- confirmEx: function() {
- return win.BUTTON_POSITION_CANCEL;
- }
- }
+ promptButton = win.BUTTON_POSITION_CANCEL;
win.Scratchpad.close();
@@ -88,14 +83,12 @@ function testUnsavedFileCancel()
win.close();
done();
- });
+ }, {noFocus: true});
}
function testUnsavedFileSave()
{
- let win = ScratchpadManager.openScratchpad();
-
- win.addEventListener("load", function() {
+ openScratchpad(function(win) {
win.Scratchpad.importFromFile(gFile, true, function(status, content) {
win.Scratchpad.filename = gFile.path;
win.Scratchpad.onTextSaved();
@@ -103,11 +96,7 @@ function testUnsavedFileSave()
let text = "new text";
win.Scratchpad.setText(text);
- Services.prompt = {
- confirmEx: function() {
- return win.BUTTON_POSITION_SAVE;
- }
- }
+ promptButton = win.BUTTON_POSITION_SAVE;
win.Scratchpad.close(function() {
readFile(gFile, function(savedContent) {
@@ -118,28 +107,22 @@ function testUnsavedFileSave()
ok(win.closed, 'pressing "Save" in dialog should close scratchpad');
});
- });
+ }, {noFocus: true});
}
function testUnsavedFileDontSave()
{
- let win = ScratchpadManager.openScratchpad();
-
- win.addEventListener("load", function() {
+ openScratchpad(function(win) {
win.Scratchpad.filename = gFile.path;
win.Scratchpad.saved = false;
- Services.prompt = {
- confirmEx: function() {
- return win.BUTTON_POSITION_DONT_SAVE;
- }
- }
+ promptButton = win.BUTTON_POSITION_DONT_SAVE;
win.Scratchpad.close();
ok(win.closed, 'pressing "Don\'t Save" in dialog should close scratchpad');
done();
- });
+ }, {noFocus: true});
}
function cleanup()
diff --git a/browser/devtools/scratchpad/test/browser_scratchpad_bug_660560_tab.js b/browser/devtools/scratchpad/test/browser_scratchpad_bug_660560_tab.js
index 2c505f91b65..3687c8173a2 100644
--- a/browser/devtools/scratchpad/test/browser_scratchpad_bug_660560_tab.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_bug_660560_tab.js
@@ -2,8 +2,6 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
-var ScratchpadManager = Scratchpad.ScratchpadManager;
-
function test()
{
waitForExplicitFinish();
@@ -16,14 +14,7 @@ function test()
Services.prefs.setIntPref("devtools.editor.tabsize", 5);
- gScratchpadWindow = Scratchpad.openScratchpad();
- gScratchpadWindow.addEventListener("load", function onScratchpadLoad() {
- gScratchpadWindow.removeEventListener("load", onScratchpadLoad, false);
-
- gScratchpadWindow.Scratchpad.addObserver({
- onReady: runTests
- });
- }, false);
+ openScratchpad(runTests);
}, true);
content.location = "data:text/html,Scratchpad test for the Tab key, bug 660560";
@@ -34,9 +25,6 @@ function runTests()
let sp = gScratchpadWindow.Scratchpad;
ok(sp, "Scratchpad object exists in new window");
- is(this.onReady, runTests, "the handler runs in the context of the observer");
- sp.removeObserver(this);
-
ok(sp.editor.hasFocus(), "the editor has focus");
sp.setText("window.foo;");
@@ -70,19 +58,12 @@ function runTests()
Services.prefs.setIntPref("devtools.editor.tabsize", 6);
Services.prefs.setBoolPref("devtools.editor.expandtab", false);
- gScratchpadWindow = Scratchpad.openScratchpad();
- gScratchpadWindow.addEventListener("load", function onScratchpadLoad() {
- gScratchpadWindow.removeEventListener("load", onScratchpadLoad, false);
- gScratchpadWindow.Scratchpad.addObserver({
- onReady: runTests2
- });
- }, false);
+ openScratchpad(runTests2);
}
function runTests2()
{
let sp = gScratchpadWindow.Scratchpad;
- sp.removeObserver(this);
sp.setText("window.foo;");
sp.editor.setCaretOffset(0);
diff --git a/browser/devtools/scratchpad/test/browser_scratchpad_bug_669612_unsaved.js b/browser/devtools/scratchpad/test/browser_scratchpad_bug_669612_unsaved.js
index 10823e5e2fd..71c8d6d24d6 100644
--- a/browser/devtools/scratchpad/test/browser_scratchpad_bug_669612_unsaved.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_bug_669612_unsaved.js
@@ -30,57 +30,37 @@ function test()
function testListeners()
{
- let win = ScratchpadManager.openScratchpad();
+ openScratchpad(function(aWin, aScratchpad) {
+ aScratchpad.setText("new text");
+ ok(!isStar(aWin), "no star if scratchpad isn't from a file");
- win.addEventListener("load", function onScratchpadLoad() {
- win.removeEventListener("load", onScratchpadLoad, false);
+ aScratchpad.onTextSaved();
+ ok(!isStar(aWin), "no star before changing text");
- win.Scratchpad.addObserver({
- onReady: function (aScratchpad) {
- aScratchpad.removeObserver(this);
+ aScratchpad.setText("new text2");
+ ok(isStar(aWin), "shows star if scratchpad text changes");
- aScratchpad.setText("new text");
- ok(!isStar(win), "no star if scratchpad isn't from a file");
+ aScratchpad.onTextSaved();
+ ok(!isStar(aWin), "no star if scratchpad was just saved");
- aScratchpad.onTextSaved();
- ok(!isStar(win), "no star before changing text");
+ aScratchpad.undo();
+ ok(isStar(aWin), "star if scratchpad undo");
- aScratchpad.setText("new text2");
- ok(isStar(win), "shows star if scratchpad text changes");
-
- aScratchpad.onTextSaved();
- ok(!isStar(win), "no star if scratchpad was just saved");
-
- aScratchpad.undo();
- ok(isStar(win), "star if scratchpad undo");
-
- win.close();
- done();
- }
- });
- }, false);
+ aWin.close();
+ done();
+ }, {noFocus: true});
}
function testErrorStatus()
{
- let win = ScratchpadManager.openScratchpad();
+ openScratchpad(function(aWin, aScratchpad) {
+ aScratchpad.onTextSaved(Components.results.NS_ERROR_FAILURE);
+ aScratchpad.setText("new text");
+ ok(!isStar(aWin), "no star if file save failed");
- win.addEventListener("load", function onScratchpadLoad() {
- win.removeEventListener("load", onScratchpadLoad, false);
-
- win.Scratchpad.addObserver({
- onReady: function (aScratchpad) {
- aScratchpad.removeObserver(this);
-
- aScratchpad.onTextSaved(Components.results.NS_ERROR_FAILURE);
- aScratchpad.setText("new text");
- ok(!isStar(win), "no star if file save failed");
-
- win.close();
- done();
- }
- });
- }, false);
+ aWin.close();
+ done();
+ }, {noFocus: true});
}
@@ -92,21 +72,13 @@ function testRestoreNotFromFile()
}];
let [win] = ScratchpadManager.restoreSession(session);
- win.addEventListener("load", function onScratchpadLoad() {
- win.removeEventListener("load", onScratchpadLoad, false);
+ openScratchpad(function(aWin, aScratchpad) {
+ aScratchpad.setText("new text");
+ ok(!isStar(win), "no star if restored scratchpad isn't from a file");
- win.Scratchpad.addObserver({
- onReady: function (aScratchpad) {
- aScratchpad.removeObserver(this);
-
- aScratchpad.setText("new text");
- ok(!isStar(win), "no star if restored scratchpad isn't from a file");
-
- win.close();
- done();
- }
- });
- }, false);
+ win.close();
+ done();
+ }, {window: win, noFocus: true});
}
function testRestoreFromFileSaved()
@@ -119,23 +91,15 @@ function testRestoreFromFileSaved()
}];
let [win] = ScratchpadManager.restoreSession(session);
- win.addEventListener("load", function onScratchpadLoad() {
- win.removeEventListener("load", onScratchpadLoad, false);
+ openScratchpad(function(aWin, aScratchpad) {
+ ok(!isStar(win), "no star before changing text in scratchpad restored from file");
- win.Scratchpad.addObserver({
- onReady: function (aScratchpad) {
- aScratchpad.removeObserver(this);
+ aScratchpad.setText("new text");
+ ok(isStar(win), "star when text changed from scratchpad restored from file");
- ok(!isStar(win), "no star before changing text in scratchpad restored from file");
-
- aScratchpad.setText("new text");
- ok(isStar(win), "star when text changed from scratchpad restored from file");
-
- win.close();
- done();
- }
- });
- }, false);
+ win.close();
+ done();
+ }, {window: win, noFocus: true});
}
function testRestoreFromFileUnsaved()
@@ -148,20 +112,12 @@ function testRestoreFromFileUnsaved()
}];
let [win] = ScratchpadManager.restoreSession(session);
- win.addEventListener("load", function onScratchpadLoad() {
- win.removeEventListener("load", onScratchpadLoad, false);
+ openScratchpad(function() {
+ ok(isStar(win), "star with scratchpad restored with unsaved text");
- win.Scratchpad.addObserver({
- onReady: function (aScratchpad) {
- aScratchpad.removeObserver(this);
-
- ok(isStar(win), "star with scratchpad restored with unsaved text");
-
- win.close();
- done();
- }
- });
- }, false);
+ win.close();
+ done();
+ }, {window: win, noFocus: true});
}
function isStar(win)
diff --git a/browser/devtools/scratchpad/test/browser_scratchpad_bug_679467_falsy.js b/browser/devtools/scratchpad/test/browser_scratchpad_bug_679467_falsy.js
index e4ab6734987..e19754fb4f2 100644
--- a/browser/devtools/scratchpad/test/browser_scratchpad_bug_679467_falsy.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_bug_679467_falsy.js
@@ -2,28 +2,21 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
-// Reference to the Scratchpad chrome window object.
-let gScratchpadWindow;
-
function test()
{
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
- gBrowser.selectedBrowser.addEventListener("load", function() {
- gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
-
- gScratchpadWindow = Scratchpad.openScratchpad();
- gScratchpadWindow.addEventListener("load", testFalsy, false);
+ gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+ gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+ openScratchpad(testFalsy);
}, true);
content.location = "data:text/html,
test falsy display() values in Scratchpad";
}
-function testFalsy(sp)
+function testFalsy()
{
- gScratchpadWindow.removeEventListener("load", testFalsy, false);
-
let sp = gScratchpadWindow.Scratchpad;
verifyFalsies(sp);
diff --git a/browser/devtools/scratchpad/test/browser_scratchpad_bug_699130_edit_ui_updates.js b/browser/devtools/scratchpad/test/browser_scratchpad_bug_699130_edit_ui_updates.js
index d9d4b001e10..06f95ef3b60 100644
--- a/browser/devtools/scratchpad/test/browser_scratchpad_bug_699130_edit_ui_updates.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_bug_699130_edit_ui_updates.js
@@ -13,9 +13,7 @@ function test()
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
-
- gScratchpadWindow = Scratchpad.openScratchpad();
- gScratchpadWindow.addEventListener("load", runTests, false);
+ openScratchpad(runTests);
}, true);
content.location = "data:text/html,test Edit menu updates Scratchpad - bug 699130";
@@ -23,8 +21,6 @@ function test()
function runTests()
{
- gScratchpadWindow.removeEventListener("load", runTests, false);
-
let sp = gScratchpadWindow.Scratchpad;
let doc = gScratchpadWindow.document;
let winUtils = gScratchpadWindow.QueryInterface(Ci.nsIInterfaceRequestor).
diff --git a/browser/devtools/scratchpad/test/browser_scratchpad_contexts.js b/browser/devtools/scratchpad/test/browser_scratchpad_contexts.js
index 4d9f8ebee2d..51919b65d12 100644
--- a/browser/devtools/scratchpad/test/browser_scratchpad_contexts.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_contexts.js
@@ -2,19 +2,14 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
-// Reference to the Scratchpad chrome window object.
-let gScratchpadWindow;
-
function test()
{
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
- gBrowser.selectedBrowser.addEventListener("load", function() {
- gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
-
- gScratchpadWindow = Scratchpad.openScratchpad();
- gScratchpadWindow.addEventListener("load", runTests, false);
+ gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+ gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+ openScratchpad(runTests);
}, true);
content.location = "data:text/html,test context switch in Scratchpad";
@@ -22,8 +17,6 @@ function test()
function runTests()
{
- gScratchpadWindow.removeEventListener("load", arguments.callee, false);
-
let sp = gScratchpadWindow.Scratchpad;
let contentMenu = gScratchpadWindow.document.getElementById("sp-menu-content");
@@ -42,7 +35,7 @@ function runTests()
is(contentMenu.getAttribute("checked"), "true",
"content menuitem is checked");
- ok(!chromeMenu.hasAttribute("checked"),
+ isnot(chromeMenu.getAttribute("checked"), "true",
"chrome menuitem is not checked");
ok(!notificationBox.currentNotification,
@@ -66,7 +59,7 @@ function runTests()
is(chromeMenu.getAttribute("checked"), "true",
"chrome menuitem is checked");
- ok(!contentMenu.hasAttribute("checked"),
+ isnot(contentMenu.getAttribute("checked"), "true",
"content menuitem is not checked");
ok(notificationBox.currentNotification,
diff --git a/browser/devtools/scratchpad/test/browser_scratchpad_execute_print.js b/browser/devtools/scratchpad/test/browser_scratchpad_execute_print.js
index 0acf948e95c..5278fcffc32 100644
--- a/browser/devtools/scratchpad/test/browser_scratchpad_execute_print.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_execute_print.js
@@ -7,11 +7,9 @@ function test()
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
- gBrowser.selectedBrowser.addEventListener("load", function() {
- gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
-
- gScratchpadWindow = Scratchpad.openScratchpad();
- gScratchpadWindow.addEventListener("load", runTests, false);
+ gBrowser.selectedBrowser.addEventListener("load", function onTabLoad() {
+ gBrowser.selectedBrowser.removeEventListener("load", onTabLoad, true);
+ openScratchpad(runTests);
}, true);
content.location = "data:text/html,
test run() and display() in Scratchpad";
@@ -19,8 +17,6 @@ function test()
function runTests()
{
- gScratchpadWindow.removeEventListener("load", arguments.callee, false);
-
let sp = gScratchpadWindow.Scratchpad;
content.wrappedJSObject.foobarBug636725 = 1;
diff --git a/browser/devtools/scratchpad/test/browser_scratchpad_files.js b/browser/devtools/scratchpad/test/browser_scratchpad_files.js
index e5ffddaed70..5e50c24bc4e 100644
--- a/browser/devtools/scratchpad/test/browser_scratchpad_files.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_files.js
@@ -5,9 +5,6 @@
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
-// Reference to the Scratchpad chrome window object.
-let gScratchpadWindow;
-
// Reference to the Scratchpad object.
let gScratchpad;
@@ -22,11 +19,9 @@ function test()
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
- gBrowser.selectedBrowser.addEventListener("load", function() {
- gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
-
- gScratchpadWindow = Scratchpad.openScratchpad();
- gScratchpadWindow.addEventListener("load", runTests, false);
+ gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+ gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+ openScratchpad(runTests);
}, true);
content.location = "data:text/html,
test file open and save in Scratchpad";
@@ -34,8 +29,6 @@ function test()
function runTests()
{
- gScratchpadWindow.removeEventListener("load", arguments.callee, false);
-
gScratchpad = gScratchpadWindow.Scratchpad;
// Create a temporary file.
diff --git a/browser/devtools/scratchpad/test/browser_scratchpad_initialization.js b/browser/devtools/scratchpad/test/browser_scratchpad_initialization.js
index b926598f1ce..67bca826a20 100644
--- a/browser/devtools/scratchpad/test/browser_scratchpad_initialization.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_initialization.js
@@ -2,21 +2,17 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
-// Reference to the Scratchpad chrome window object.
-let gScratchpadWindow;
-
function test()
{
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
- gBrowser.selectedBrowser.addEventListener("load", function() {
- gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+ gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+ gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
- ok(Scratchpad, "Scratchpad variable exists");
+ ok(window.Scratchpad, "Scratchpad variable exists");
- gScratchpadWindow = Scratchpad.openScratchpad();
- gScratchpadWindow.addEventListener("load", runTests, false);
+ openScratchpad(runTests);
}, true);
content.location = "data:text/html,initialization test for Scratchpad";
@@ -24,8 +20,6 @@ function test()
function runTests()
{
- gScratchpadWindow.removeEventListener("load", arguments.callee, false);
-
let sp = gScratchpadWindow.Scratchpad;
ok(sp, "Scratchpad object exists in new window");
is(typeof sp.run, "function", "Scratchpad.run() exists");
diff --git a/browser/devtools/scratchpad/test/browser_scratchpad_inspect.js b/browser/devtools/scratchpad/test/browser_scratchpad_inspect.js
index cd52d7a0349..ed4c19f0d08 100644
--- a/browser/devtools/scratchpad/test/browser_scratchpad_inspect.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_inspect.js
@@ -2,19 +2,14 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
-// Reference to the Scratchpad chrome window object.
-let gScratchpadWindow;
-
function test()
{
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
- gBrowser.selectedBrowser.addEventListener("load", function() {
- gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
-
- gScratchpadWindow = Scratchpad.openScratchpad();
- gScratchpadWindow.addEventListener("load", runTests, false);
+ gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+ gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+ openScratchpad(runTests);
}, true);
content.location = "data:text/html,
foobarBug636725" +
@@ -23,8 +18,6 @@ function test()
function runTests()
{
- gScratchpadWindow.removeEventListener("load", arguments.callee, false);
-
let sp = gScratchpadWindow.Scratchpad;
sp.setText("document");
@@ -34,8 +27,8 @@ function runTests()
let propPanel = document.querySelector(".scratchpad_propertyPanel");
ok(propPanel, "property panel is open");
- propPanel.addEventListener("popupshown", function() {
- propPanel.removeEventListener("popupshown", arguments.callee, false);
+ propPanel.addEventListener("popupshown", function onPopupShown() {
+ propPanel.removeEventListener("popupshown", onPopupShown, false);
let tree = propPanel.querySelector("tree");
ok(tree, "property panel tree found");
diff --git a/browser/devtools/scratchpad/test/browser_scratchpad_open.js b/browser/devtools/scratchpad/test/browser_scratchpad_open.js
index 2847c3ecb9d..5af2ffaf2c5 100644
--- a/browser/devtools/scratchpad/test/browser_scratchpad_open.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_open.js
@@ -2,8 +2,6 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
-var ScratchpadManager = Scratchpad.ScratchpadManager;
-
// only finish() when correct number of tests are done
const expected = 3;
var count = 0;
@@ -15,7 +13,6 @@ function done()
}
}
-
function test()
{
waitForExplicitFinish();
@@ -26,11 +23,7 @@ function test()
function testOpen()
{
- let win = ScratchpadManager.openScratchpad();
-
- win.addEventListener("load", function onScratchpadLoad() {
- win.removeEventListener("load", onScratchpadLoad, false);
-
+ openScratchpad(function(win) {
is(win.Scratchpad.filename, undefined, "Default filename is undefined");
is(win.Scratchpad.getText(),
win.Scratchpad.strings.GetStringFromName("scratchpadIntro"),
@@ -40,7 +33,7 @@ function testOpen()
win.close();
done();
- }, false);
+ }, {noFocus: true});
}
function testOpenWithState()
@@ -51,25 +44,19 @@ function testOpenWithState()
text: "test text"
};
- let win = ScratchpadManager.openScratchpad(state);
-
- win.addEventListener("load", function onScratchpadLoad() {
- win.removeEventListener("load", onScratchpadLoad, false);
-
+ openScratchpad(function(win) {
is(win.Scratchpad.filename, state.filename, "Filename loaded from state");
is(win.Scratchpad.executionContext, state.executionContext, "Execution context loaded from state");
is(win.Scratchpad.getText(), state.text, "Content loaded from state");
win.close();
done();
- }, false);
+ }, {state: state, noFocus: true});
}
function testOpenInvalidState()
{
- let state = 7;
-
- let win = ScratchpadManager.openScratchpad(state);
+ let win = openScratchpad(null, {state: 7});
ok(!win, "no scratchpad opened if state is not an object");
done();
}
diff --git a/browser/devtools/scratchpad/test/browser_scratchpad_restore.js b/browser/devtools/scratchpad/test/browser_scratchpad_restore.js
index c1e1b0b921d..a83c4213ce7 100644
--- a/browser/devtools/scratchpad/test/browser_scratchpad_restore.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_restore.js
@@ -48,11 +48,7 @@ function testRestore()
asyncMap(states, function(state, done) {
// Open some scratchpad windows
- let win = ScratchpadManager.openScratchpad(state);
- win.addEventListener("load", function onScratchpadLoad() {
- removeEventListener("load", onScratchpadLoad, false);
- done(win);
- }, false)
+ openScratchpad(done, {state: state, noFocus: true});
}, function(wins) {
// Then save the windows to session store
ScratchpadManager.saveOpenWindows();
@@ -74,12 +70,11 @@ function testRestore()
is(restoredWins.length, 3, "Three scratchad windows restored");
asyncMap(restoredWins, function(restoredWin, done) {
- restoredWin.addEventListener("load", function onScratchpadLoad() {
- restoredWin.removeEventListener("load", onScratchpadLoad, false);
- let state = restoredWin.Scratchpad.getState();
- restoredWin.close();
+ openScratchpad(function(aWin) {
+ let state = aWin.Scratchpad.getState();
+ aWin.close();
done(state);
- }, false);
+ }, {window: restoredWin, noFocus: true});
}, function(restoredStates) {
// Then make sure they were restored with the right states
ok(statesMatch(restoredStates, states),
diff --git a/browser/devtools/scratchpad/test/browser_scratchpad_tab_switch.js b/browser/devtools/scratchpad/test/browser_scratchpad_tab_switch.js
index 754adf99d15..2db5a3c5a54 100644
--- a/browser/devtools/scratchpad/test/browser_scratchpad_tab_switch.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_tab_switch.js
@@ -2,8 +2,6 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
-// Reference to the Scratchpad chrome window object.
-let gScratchpadWindow;
let tab1;
let tab2;
let sp;
@@ -14,15 +12,14 @@ function test()
tab1 = gBrowser.addTab();
gBrowser.selectedTab = tab1;
- gBrowser.selectedBrowser.addEventListener("load", function() {
- gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+ gBrowser.selectedBrowser.addEventListener("load", function onLoad1() {
+ gBrowser.selectedBrowser.removeEventListener("load", onLoad1, true);
tab2 = gBrowser.addTab();
gBrowser.selectedTab = tab2;
- gBrowser.selectedBrowser.addEventListener("load", function() {
- gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
- gScratchpadWindow = Scratchpad.openScratchpad();
- gScratchpadWindow.addEventListener("load", runTests, false);
+ gBrowser.selectedBrowser.addEventListener("load", function onLoad2() {
+ gBrowser.selectedBrowser.removeEventListener("load", onLoad2, true);
+ openScratchpad(runTests);
}, true);
content.location = "data:text/html,test context switch in Scratchpad tab 2";
}, true);
@@ -32,8 +29,6 @@ function test()
function runTests()
{
- gScratchpadWindow.removeEventListener("load", runTests, true);
-
sp = gScratchpadWindow.Scratchpad;
let contentMenu = gScratchpadWindow.document.getElementById("sp-menu-content");
@@ -52,7 +47,7 @@ function runTests()
is(contentMenu.getAttribute("checked"), "true",
"content menuitem is checked");
- ok(!browserMenu.hasAttribute("checked"),
+ isnot(browserMenu.getAttribute("checked"), "true",
"chrome menuitem is not checked");
is(notificationBox.currentNotification, null,
diff --git a/browser/devtools/scratchpad/test/browser_scratchpad_ui.js b/browser/devtools/scratchpad/test/browser_scratchpad_ui.js
index 14058607076..e388e0490ce 100644
--- a/browser/devtools/scratchpad/test/browser_scratchpad_ui.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_ui.js
@@ -2,19 +2,14 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
-// Reference to the Scratchpad chrome window object.
-let gScratchpadWindow;
-
function test()
{
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
- gBrowser.selectedBrowser.addEventListener("load", function() {
- gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
-
- gScratchpadWindow = Scratchpad.openScratchpad();
- gScratchpadWindow.addEventListener("load", runTests, false);
+ gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+ gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+ openScratchpad(runTests);
}, true);
content.location = "data:text/html,foobarBug636725" +
@@ -23,8 +18,6 @@ function test()
function runTests()
{
- gScratchpadWindow.removeEventListener("load", arguments.callee, false);
-
let sp = gScratchpadWindow.Scratchpad;
let doc = gScratchpadWindow.document;
diff --git a/browser/devtools/scratchpad/test/head.js b/browser/devtools/scratchpad/test/head.js
index e8244441c75..210dc57e094 100644
--- a/browser/devtools/scratchpad/test/head.js
+++ b/browser/devtools/scratchpad/test/head.js
@@ -6,6 +6,61 @@
let gScratchpadWindow; // Reference to the Scratchpad chrome window object
+/**
+ * Open a Scratchpad window.
+ *
+ * @param function aReadyCallback
+ * Optional. The function you want invoked when the Scratchpad instance
+ * is ready.
+ * @param object aOptions
+ * Optional. Options for opening the scratchpad:
+ * - window
+ * Provide this if there's already a Scratchpad window you want to wait
+ * loading for.
+ * - state
+ * Scratchpad state object. This is used when Scratchpad is open.
+ * - noFocus
+ * Boolean that tells you do not want the opened window to receive
+ * focus.
+ * @return nsIDOMWindow
+ * The new window object that holds Scratchpad. Note that the
+ * gScratchpadWindow global is also updated to reference the new window
+ * object.
+ */
+function openScratchpad(aReadyCallback, aOptions)
+{
+ aOptions = aOptions || {};
+
+ let win = aOptions.window ||
+ Scratchpad.ScratchpadManager.openScratchpad(aOptions.state);
+ if (!win) {
+ return;
+ }
+
+ let onLoad = function() {
+ win.removeEventListener("load", onLoad, false);
+
+ win.Scratchpad.addObserver({
+ onReady: function(aScratchpad) {
+ aScratchpad.removeObserver(this);
+
+ if (aOptions.noFocus) {
+ aReadyCallback(win, aScratchpad);
+ } else {
+ waitForFocus(aReadyCallback.bind(null, win, aScratchpad), win);
+ }
+ }
+ });
+ };
+
+ if (aReadyCallback) {
+ win.addEventListener("load", onLoad, false);
+ }
+
+ gScratchpadWindow = win;
+ return gScratchpadWindow;
+}
+
function cleanup()
{
if (gScratchpadWindow) {
diff --git a/browser/devtools/sourceeditor/orion/Makefile.dryice.js b/browser/devtools/sourceeditor/orion/Makefile.dryice.js
index cafc3e87bb6..efcb73f640c 100755
--- a/browser/devtools/sourceeditor/orion/Makefile.dryice.js
+++ b/browser/devtools/sourceeditor/orion/Makefile.dryice.js
@@ -45,12 +45,17 @@ 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/rulers.js",
ORION_EDITOR + "/orion/textview/undoStack.js",
ORION_EDITOR + "/orion/textview/textModel.js",
+ ORION_EDITOR + "/orion/textview/annotations.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",
@@ -69,6 +74,7 @@ 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",
],
diff --git a/browser/devtools/sourceeditor/orion/README b/browser/devtools/sourceeditor/orion/README
index c8703c947f4..2465f87bdb2 100644
--- a/browser/devtools/sourceeditor/orion/README
+++ b/browser/devtools/sourceeditor/orion/README
@@ -8,25 +8,11 @@ The Orion editor web site: http://www.eclipse.org/orion
To upgrade Orion to a newer version see the UPGRADE file.
-Orion version: git clone from 2011-10-26
- commit hash 0ab295660e1f7d33ca2bfb8558b3b7492d2c5aa5
- + patch for Eclipse Bug 358623 - Drag and Drop support:
- https://github.com/mihaisucan/orion.client/tree/bug-358623
- see https://bugs.eclipse.org/bugs/show_bug.cgi?id=358623
- + patch for Eclipse Bug 362286 - Monaco font line height:
- https://github.com/mihaisucan/orion.client/tree/bug-362286
- see https://bugs.eclipse.org/bugs/show_bug.cgi?id=362286
- + patch for Eclipse Bug 362107 - Ctrl-Up/Down failure on Linux:
- https://github.com/mihaisucan/orion.client/tree/bug-362107
- see https://bugs.eclipse.org/bugs/show_bug.cgi?id=362107
- + patch for Eclipse Bug 362428 - _getXToOffset() throws:
- https://github.com/mihaisucan/orion.client/tree/bug-362428
- see https://bugs.eclipse.org/bugs/show_bug.cgi?id=362428
- + patch for Eclipse Bug 362835 - Pasted HTML shows twice:
- https://github.com/mihaisucan/orion.client/tree/bug-362835
- see https://bugs.eclipse.org/bugs/show_bug.cgi?id=362835
- + patch for Eclipse Bug 363508 - Selection is broken after TextView hide/unhide
- see https://bugs.eclipse.org/bugs/show_bug.cgi?id=363508
+Orion version: git clone from 2011-12-09
+ commit hash d8a6dc01d9c561d6eb99f03b64c8c78ce785c59d
+ + patch for Eclipse Bug 366312 - right-clicking outside of the selection causes the caret to move
+ https://github.com/mihaisucan/orion.client/tree/bug-366312
+ see https://bugs.eclipse.org/bugs/show_bug.cgi?id=366312
# License
diff --git a/browser/devtools/sourceeditor/orion/mozilla.css b/browser/devtools/sourceeditor/orion/mozilla.css
index 8d476a8f96e..214a8477014 100644
--- a/browser/devtools/sourceeditor/orion/mozilla.css
+++ b/browser/devtools/sourceeditor/orion/mozilla.css
@@ -2,15 +2,124 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
.viewContainer {
- font-size: inherit; /* inherit browser's default monospace font size */
+ background: -moz-Dialog;
+ font-family: monospace;
+ font-size: inherit; /* inherit browser's default monospace font size */
}
+.view {
+ background: #fff;
+}
+
+.readonly > .view {
+ background: #f6f6f6;
+}
+
+/* Styles for rulers */
+.ruler {
+ background-color: white;
+}
+.ruler.lines {
+ border-right: 1px solid lightgray;
+ text-align: right;
+}
+
+/* Styles for the line number ruler */
.rulerLines {
- background: -moz-Dialog;
- color: -moz-DialogText;
- min-width: 1.4em;
- padding-left: 4px;
- padding-right: 4px;
- text-align: end;
+ background: -moz-Dialog;
+ color: -moz-DialogText;
+ min-width: 1.4em;
+ padding-left: 4px;
+ padding-right: 4px;
+ text-align: end;
+}
+
+.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: #EAF2FE;
+}
+
+.readonly .line_caret {
+ background: #fcfcfc;
+}
+
+/* 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;
+}
+
+.annotationRange.currentBracket {
+}
+
+.annotationRange.matchingBracket {
+ outline: 1px solid red;
}
diff --git a/browser/devtools/sourceeditor/orion/orion.css b/browser/devtools/sourceeditor/orion/orion.css
index 48207cf0a18..85515975528 100644
--- a/browser/devtools/sourceeditor/orion/orion.css
+++ b/browser/devtools/sourceeditor/orion/orion.css
@@ -3,9 +3,13 @@
}
.viewContainer {
+ background-color: #eeeeee;
font-family: monospace;
font-size: 10pt;
}
+::-webkit-scrollbar-corner {
+ background-color: #eeeeee;
+}
.viewContent {
}/* Styles for rulers */
@@ -25,16 +29,172 @@
text-align: right;
}
.ruler.overview {
+ border-left: 1px solid lightgray;
width: 14px;
}
/* Styles for the line number ruler */
.rulerLines {
- background-color: white;
}
.rulerLines.even
.rulerLines.odd {
-}.token_singleline_comment {
+}/* 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;
}
@@ -67,15 +227,6 @@
font-weight: bold;
}
-.token_bracket_outline {
- outline: 1px solid red;
-}
-
-.token_bracket {
- color: white;
- background-color: grey;
-}
-
.token_space {
/* images/white_space.png */
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAIAAABv85FHAAAABnRSTlMA/wAAAACkwsAdAAAAIUlEQVR4nGP4z8CAC+GUIEXuABhgkTuABEiRw2cmae4EAH05X7xDolNRAAAAAElFTkSuQmCC");
diff --git a/browser/devtools/sourceeditor/orion/orion.js b/browser/devtools/sourceeditor/orion/orion.js
index 7b73fb1f02f..93d2681bfc6 100644
--- a/browser/devtools/sourceeditor/orion/orion.js
+++ b/browser/devtools/sourceeditor/orion/orion.js
@@ -1,4 +1,289 @@
/*******************************************************************************
+ * @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[]} deps The array of dependency names.
+ * @param {Function} callback The definition function.
+ * @param {String} moduleName The mixin module name.
+ */
+if (!window.define) {
+ window.define = function(deps, callback, moduleName) {
+ var module = this;
+ var split = (moduleName || "").split("/"), i, j;
+ for (i = 0; i < split.length; 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([], 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};
+}, "orion/textview");
+/*******************************************************************************
+ * @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([], 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
+ };
+}, "orion/editor");
+/*******************************************************************************
+ * @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
@@ -12,38 +297,29 @@
/*global window define */
-/**
- * @namespace The global container for Orion APIs.
- */
-var orion = orion || {};
-/**
- * @namespace The container for textview APIs.
- */
-orion.textview = orion.textview || {};
-
-/**
- * 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
- */
-orion.textview.KeyBinding = (function() {
+define([], function() {
var isMac = window.navigator.platform.indexOf("Mac") !== -1;
- /** @private */
+
+ /**
+ * 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);
@@ -89,16 +365,10 @@ orion.textview.KeyBinding = (function() {
return true;
}
};
- return KeyBinding;
-}());
-
-if (typeof window !== "undefined" && typeof window.define !== "undefined") {
- define([], function() {
- return orion.textview;
- });
-}
-
+ return {KeyBinding: KeyBinding};
+}, "orion/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
@@ -108,49 +378,40 @@ if (typeof window !== "undefined" && typeof window.define !== "undefined") {
* Contributors: IBM Corporation - initial API and implementation
******************************************************************************/
-/*global window define setTimeout clearTimeout setInterval clearInterval Node */
+/*global define setTimeout clearTimeout setInterval clearInterval Node */
-/**
- * @namespace The global container for Orion APIs.
- */
-var orion = orion || {};
-/**
- * @namespace The container for textview APIs.
- */
-orion.textview = orion.textview || {};
+define(['orion/textview/tooltip'], function(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.
- *
- * See:
- * {@link orion.textview.LineNumberRuler}
- * {@link orion.textview.AnnotationRuler}
- * {@link orion.textview.OverviewRuler}
- * {@link orion.textview.TextView}
- * {@link orion.textview.TextView#addRuler}
- *
- * @name orion.textview.Ruler
- */
-orion.textview.Ruler = (function() {
- /** @private */
+ /**
+ * 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.
+ *
+ * See:
+ * {@link orion.textview.LineNumberRuler}
+ * {@link orion.textview.AnnotationRuler}
+ * {@link orion.textview.OverviewRuler}
+ * {@link orion.textview.TextView}
+ * {@link orion.textview.TextView#addRuler}
+ *
+ * @name orion.textview.Ruler
+ */
function Ruler (annotationModel, rulerLocation, rulerOverview, rulerStyle) {
this._location = rulerLocation || "left";
this._overview = rulerOverview || "page";
@@ -158,8 +419,11 @@ orion.textview.Ruler = (function() {
this._types = [];
this._view = null;
var self = this;
- this._annotationModelListener = {
- onChanged: function(e) {
+ this._listener = {
+ onTextModelChanged: function(e) {
+ self._onTextModelChanged(e);
+ },
+ onAnnotationModelChanged: function(e) {
self._onAnnotationModelChanged(e);
}
};
@@ -210,7 +474,7 @@ orion.textview.Ruler = (function() {
var annotation = annotations.next();
if (!this.isAnnotationTypeVisible(annotation.type)) { continue; }
var annotationLineStart = baseModel.getLineAtOffset(annotation.start);
- var annotationLineEnd = baseModel.getLineAtOffset(annotation.end - 1);
+ var annotationLineEnd = baseModel.getLineAtOffset(Math.max(annotation.start, annotation.end - 1));
for (var lineIndex = annotationLineStart; lineIndex<=annotationLineEnd; lineIndex++) {
var visualLineIndex = lineIndex;
if (model !== baseModel) {
@@ -332,11 +596,11 @@ orion.textview.Ruler = (function() {
*/
setAnnotationModel: function (annotationModel) {
if (this._annotationModel) {
- this._annotationModel.removeListener(this._annotationModelListener);
+ this._annotationModel.removEventListener("Changed", this._listener.onAnnotationModelChanged);
}
this._annotationModel = annotationModel;
if (this._annotationModel) {
- this._annotationModel.addListener(this._annotationModelListener);
+ this._annotationModel.addEventListener("Changed", this._listener.onAnnotationModelChanged);
}
},
/**
@@ -374,11 +638,11 @@ orion.textview.Ruler = (function() {
*/
setView: function (view) {
if (this._onTextModelChanged && this._view) {
- this._view.removeEventListener("ModelChanged", this, this._onTextModelChanged);
+ this._view.removeEventListener("ModelChanged", this._listener.onTextModelChanged);
}
this._view = view;
if (this._onTextModelChanged && this._view) {
- this._view.addEventListener("ModelChanged", this, this._onTextModelChanged);
+ this._view.addEventListener("ModelChanged", this._listener.onTextModelChanged);
}
},
/**
@@ -407,7 +671,7 @@ orion.textview.Ruler = (function() {
* @param {DOMEvent} e the mouse move event.
*/
onMouseMove: function(lineIndex, e) {
- var tooltip = orion.textview.Tooltip.getTooltip(this._view);
+ var tooltip = mTooltip.Tooltip.getTooltip(this._view);
if (!tooltip) { return; }
if (tooltip.isVisible() && this._tooltipLineIndex === lineIndex) { return; }
this._tooltipLineIndex = lineIndex;
@@ -426,7 +690,9 @@ orion.textview.Ruler = (function() {
* @param {Number} lineIndex the line index of the annotation under the pointer.
* @param {DOMEvent} e the mouse over event.
*/
- onMouseOver: this._onMouseMove,
+ onMouseOver: function(lineIndex, e) {
+ this.onMouseMove(lineIndex, e);
+ },
/**
* This event is sent when the mouse pointer exits a line annotation.
*
@@ -435,7 +701,7 @@ orion.textview.Ruler = (function() {
* @param {DOMEvent} e the mouse out event.
*/
onMouseOut: function(lineIndex, e) {
- var tooltip = orion.textview.Tooltip.getTooltip(this._view);
+ var tooltip = mTooltip.Tooltip.getTooltip(this._view);
if (!tooltip) { return; }
tooltip.setTarget(null);
},
@@ -566,37 +832,34 @@ orion.textview.Ruler = (function() {
return result;
}
};
- return Ruler;
-}());
-/**
- * Constructs a new line numbering 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
- */
-orion.textview.LineNumberRuler = (function() {
- /** @private */
+ /**
+ * 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) {
- orion.textview.Ruler.call(this, annotationModel, rulerLocation, "page", rulerStyle);
+ 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 orion.textview.Ruler();
+ LineNumberRuler.prototype = new Ruler();
/** @ignore */
LineNumberRuler.prototype.getAnnotations = function(startLine, endLine) {
- var result = orion.textview.Ruler.prototype.getAnnotations.call(this, 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;
@@ -628,74 +891,67 @@ orion.textview.LineNumberRuler = (function() {
this._view.redrawLines(startLine, model.getLineCount(), this);
}
};
- return LineNumberRuler;
-}());
-/**
- * @class This is class represents an annotation for the AnnotationRuler.
- *
- * See:
- * {@link orion.textview.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.
- */
-/**
- * Contructs a new annotation 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.
- *
- * See:
- * {@link orion.textview.Ruler}
- * {@link orion.textview.Annotation}
- *
- * @name orion.textview.AnnotationRuler
- */
-orion.textview.AnnotationRuler = (function() {
- /** @private */
- function AnnotationRuler (annotationModel, rulerLocation, rulerStyle) {
- orion.textview.Ruler.call(this, annotationModel, rulerLocation, "page", rulerStyle);
- }
- AnnotationRuler.prototype = new orion.textview.Ruler();
- return AnnotationRuler;
-}());
-
-/**
- * Contructs an 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 {String} [rulerLocation="left"] the location for the ruler.
- * @param {orion.textview.Style} [rulerStyle=undefined] the style for the ruler.
- * @param {orion.textview.AnnotationRuler} [annotationRuler] the annotation ruler for the overview.
- *
- * @augments orion.textview.Ruler
- * @class This objects implements an overview ruler.
- *
- * See:
- * {@link orion.textview.AnnotationRuler}
- * {@link orion.textview.Ruler}
- *
- * @name orion.textview.OverviewRuler
- */
-orion.textview.OverviewRuler = (function() {
- /** @private */
- function OverviewRuler (annotationModel, rulerLocation, rulerStyle) {
- orion.textview.Ruler.call(this, annotationModel, rulerLocation, "document", rulerStyle);
+ /**
+ * @class This is class represents an annotation for the AnnotationRuler.
+ *
+ * See:
+ * {@link orion.textview.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.
+ *
+ * See:
+ * {@link orion.textview.Ruler}
+ * {@link orion.textview.Annotation}
+ *
+ * @name orion.textview.AnnotationRuler
+ */
+ function AnnotationRuler (annotationModel, rulerLocation, rulerStyle) {
+ Ruler.call(this, annotationModel, rulerLocation, "page", rulerStyle);
}
- OverviewRuler.prototype = new orion.textview.Ruler();
+ 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.
+ *
+ * See:
+ * {@link orion.textview.AnnotationRuler}
+ * {@link orion.textview.Ruler}
+ *
+ * @name orion.textview.OverviewRuler
+ */
+ function OverviewRuler (annotationModel, rulerLocation, rulerStyle) {
+ Ruler.call(this, annotationModel, rulerLocation, "document", rulerStyle);
+ }
+ OverviewRuler.prototype = new Ruler();
/** @ignore */
OverviewRuler.prototype.getRulerStyle = function() {
@@ -719,7 +975,7 @@ orion.textview.OverviewRuler = (function() {
}
return "Line: " + (mapLine + 1);
}
- return orion.textview.Ruler.prototype._getTooltipContents.call(this, lineIndex, annotations);
+ return Ruler.prototype._getTooltipContents.call(this, lineIndex, annotations);
};
/** @ignore */
OverviewRuler.prototype._mergeAnnotation = function(previousAnnotation, annotation, annotationLineIndex, annotationLineCount) {
@@ -733,15 +989,27 @@ orion.textview.OverviewRuler = (function() {
}
return result;
};
- return OverviewRuler;
-}());
-orion.textview.FoldingRuler = (function() {
- /** @private */
+ /**
+ * Constructs a new folding 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.
+ *
+ * @augments orion.textview.Ruler
+ * @class This objects implements an overview ruler.
+ *
+ * See:
+ * {@link orion.textview.AnnotationRuler}
+ * {@link orion.textview.Ruler}
+ *
+ * @name orion.textview.OverviewRuler
+ */
function FoldingRuler (annotationModel, rulerLocation, rulerStyle) {
- orion.textview.AnnotationRuler.call(this, annotationModel, rulerLocation, rulerStyle);
+ AnnotationRuler.call(this, annotationModel, rulerLocation, rulerStyle);
}
- FoldingRuler.prototype = new orion.textview.AnnotationRuler();
+ FoldingRuler.prototype = new AnnotationRuler();
/** @ignore */
FoldingRuler.prototype.onClick = function(lineIndex, e) {
@@ -763,7 +1031,7 @@ orion.textview.FoldingRuler = (function() {
annotation = a;
}
if (annotation) {
- var tooltip = orion.textview.Tooltip.getTooltip(this._view);
+ var tooltip = mTooltip.Tooltip.getTooltip(this._view);
if (tooltip) {
tooltip.setTarget(null);
}
@@ -782,12 +1050,12 @@ orion.textview.FoldingRuler = (function() {
return null;
}
}
- return orion.textview.AnnotationRuler.prototype._getTooltipContents.call(this, lineIndex, annotations);
+ return AnnotationRuler.prototype._getTooltipContents.call(this, lineIndex, annotations);
};
/** @ignore */
FoldingRuler.prototype._onAnnotationModelChanged = function(e) {
if (e.textModelChangedEvent) {
- orion.textview.AnnotationRuler.prototype._onAnnotationModelChanged.call(this, e);
+ AnnotationRuler.prototype._onAnnotationModelChanged.call(this, e);
return;
}
var view = this._view;
@@ -815,15 +1083,16 @@ orion.textview.FoldingRuler = (function() {
}
};
- return FoldingRuler;
-}());
-
-if (typeof window !== "undefined" && typeof window.define !== "undefined") {
- define(['orion/textview/tooltip'], function() {
- return orion.textview;
- });
-}
+ return {
+ Ruler: Ruler,
+ AnnotationRuler: AnnotationRuler,
+ LineNumberRuler: LineNumberRuler,
+ OverviewRuler: OverviewRuler,
+ FoldingRuler: FoldingRuler
+ };
+}, "orion/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
@@ -833,33 +1102,10 @@ if (typeof window !== "undefined" && typeof window.define !== "undefined") {
* Contributors: IBM Corporation - initial API and implementation
******************************************************************************/
-/*global window define */
+/*global define */
-/**
- * @namespace The global container for Orion APIs.
- */
-var orion = orion || {};
-/**
- * @namespace The container for textview APIs.
- */
-orion.textview = orion.textview || {};
+define([], function() {
-/**
- * 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}
- *
- */
-orion.textview.UndoStack = (function() {
/**
* Constructs a new Change object.
*
@@ -867,53 +1113,50 @@ orion.textview.UndoStack = (function() {
* @name orion.textview.Change
* @private
*/
- var Change = (function() {
- 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;
- }
+ 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);
}
+ if (mapOffset < 0) { return; }
+ offset = mapOffset;
}
- };
- return Change;
- }());
+ view.setText(text, offset, offset + previousText.length);
+ if (select) {
+ view.setSelection(offset, offset + text.length);
+ }
+ }
+ };
/**
* Constructs a new CompoundChange object.
@@ -922,44 +1165,63 @@ orion.textview.UndoStack = (function() {
* @name orion.textview.CompoundChange
* @private
*/
- var CompoundChange = (function() {
- function CompoundChange (selection, caret) {
- this.selection = selection;
- this.caret = caret;
- this.changes = [];
- }
- CompoundChange.prototype = {
- /** @ignore */
- add: function (change) {
- this.changes.push(change);
- },
- /** @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.selection.start;
- var end = this.selection.end;
- view.setSelection(this.caret ? start : end, this.caret ? 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.selection.start;
- var end = this.selection.end;
- view.setSelection(this.caret ? start : end, this.caret ? end : start);
- }
+ 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);
}
- };
- return CompoundChange;
- }());
+ 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();
+ }
+ };
- /** @private */
+ /**
+ * 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;
@@ -970,14 +1232,16 @@ orion.textview.UndoStack = (function() {
}
this.model = model;
var self = this;
- this._modelListener = {
+ this._listener = {
onChanging: function(e) {
- self._onModelChanging(e);
+ self._onChanging(e);
+ },
+ onDestroy: function(e) {
+ self._onDestroy(e);
}
};
- model.addListener(this._modelListener);
- view._undoStack = this;
- view.addEventListener("Destroy", this, this._onDestroy);
+ model.addEventListener("Changing", this._listener.onChanging);
+ view.addEventListener("Destroy", this._listener.onDestroy);
}
UndoStack.prototype = /** @lends orion.textview.UndoStack.prototype */ {
/**
@@ -1064,6 +1328,9 @@ orion.textview.UndoStack = (function() {
* @see #startCompoundChange
*/
endCompoundChange: function() {
+ if (this.compoundChange) {
+ this.compoundChange.end(this.view);
+ }
this.compoundChange = undefined;
},
/**
@@ -1144,9 +1411,10 @@ orion.textview.UndoStack = (function() {
*/
startCompoundChange: function() {
this._commitUndo();
- var change = new CompoundChange(this.view.getSelection(), this.view.getCaretOffset());
+ var change = new CompoundChange();
this.add(change);
this.compoundChange = change;
+ this.compoundChange.start(this.view);
},
_commitUndo: function () {
if (this._undoStart !== undefined) {
@@ -1159,12 +1427,11 @@ orion.textview.UndoStack = (function() {
this._undoText = "";
}
},
- _onDestroy: function() {
- this.model.removeListener(this._modelListener);
- this.view.removeEventListener("Destroy", this, this._onDestroy);
- this.view._undoStack = null;
+ _onDestroy: function(evt) {
+ this.model.removeEventListener("Changing", this._listener.onChanging);
+ this.view.removeEventListener("Destroy", this._listener.onDestroy);
},
- _onModelChanging: function(e) {
+ _onChanging: function(e) {
var newText = e.text;
var start = e.start;
var removedCharCount = e.removedCharCount;
@@ -1199,15 +1466,13 @@ orion.textview.UndoStack = (function() {
this.add(new Change(start, newText, this.model.getText(start, start + removedCharCount)));
}
};
- return UndoStack;
-}());
-
-if (typeof window !== "undefined" && typeof window.define !== "undefined") {
- define([], function() {
- return orion.textview;
- });
-}
+
+ return {
+ UndoStack: UndoStack
+ };
+}, "orion/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
@@ -1219,40 +1484,32 @@ if (typeof window !== "undefined" && typeof window.define !== "undefined") {
* Silenio Quarti (IBM Corporation) - initial API and implementation
******************************************************************************/
-/*global window define */
+/*global define window*/
-/**
- * @namespace The global container for Orion APIs.
- */
-var orion = orion || {};
-/**
- * @namespace The container for textview APIs.
- */
-orion.textview = orion.textview || {};
-
-/**
- * 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.
- *
- * See:
- * {@link orion.textview.TextView}
- * {@link orion.textview.TextView#setModel}
- *
- */
-orion.textview.TextModel = (function() {
+define(['orion/textview/eventTarget'], function(mEventTarget) {
var isWindows = window.navigator.platform.indexOf("Win") !== -1;
- /** @private */
+ /**
+ * 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.
+ *
+ * See:
+ * {@link orion.textview.TextView}
+ * {@link orion.textview.TextView#setModel}
+ *
+ * @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._listeners = [];
this._lastLineIndex = -1;
this._text = [""];
this._lineOffsets = [0];
@@ -1261,33 +1518,6 @@ orion.textview.TextModel = (function() {
}
TextModel.prototype = /** @lends orion.textview.TextModel.prototype */ {
- /**
- * Adds a listener to the model.
- *
- * @param {Object} listener the listener to add.
- * @param {Function} [listener.onChanged] see {@link #onChanged}.
- * @param {Function} [listener.onChanging] see {@link #onChanging}.
- *
- * @see removeListener
- */
- addListener: function(listener) {
- this._listeners.push(listener);
- },
- /**
- * Removes a listener from the model.
- *
- * @param {Object} listener the listener to remove
- *
- * @see #addListener
- */
- removeListener: function(listener) {
- for (var i = 0; i < this._listeners.length; i++) {
- if (this._listeners[i] === listener) {
- this._listeners.splice(i, 1);
- return;
- }
- }
- },
/**
* Returns the number of characters in the model.
*
@@ -1512,12 +1742,7 @@ orion.textview.TextModel = (function() {
* @param {orion.textview.ModelChangingEvent} modelChangingEvent the changing event
*/
onChanging: function(modelChangingEvent) {
- for (var i = 0; i < this._listeners.length; i++) {
- var l = this._listeners[i];
- if (l && l.onChanging) {
- l.onChanging(modelChangingEvent);
- }
- }
+ return this.dispatchEvent(modelChangingEvent);
},
/**
* Notifies all listeners that the text has changed.
@@ -1534,12 +1759,7 @@ orion.textview.TextModel = (function() {
* @param {orion.textview.ModelChangedEvent} modelChangedEvent the changed event
*/
onChanged: function(modelChangedEvent) {
- for (var i = 0; i < this._listeners.length; i++) {
- var l = this._listeners[i];
- if (l && l.onChanged) {
- l.onChanged(modelChangedEvent);
- }
- }
+ return this.dispatchEvent(modelChangedEvent);
},
/**
* Sets the line delimiter that is used by the view
@@ -1616,6 +1836,7 @@ orion.textview.TextModel = (function() {
}
var modelChangingEvent = {
+ type: "Changing",
text: text,
start: eventStart,
removedCharCount: removedCharCount,
@@ -1679,6 +1900,7 @@ orion.textview.TextModel = (function() {
if (this._text.length === 0) { this._text = [""]; }
var modelChangedEvent = {
+ type: "Changed",
start: eventStart,
removedCharCount: removedCharCount,
addedCharCount: addedCharCount,
@@ -1688,16 +1910,631 @@ orion.textview.TextModel = (function() {
this.onChanged(modelChangedEvent);
}
};
+ mEventTarget.EventTarget.addMixin(TextModel.prototype);
- return TextModel;
-}());
+ return {TextModel: TextModel};
+}, "orion/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
+ ******************************************************************************/
-if (typeof window !== "undefined" && typeof window.define !== "undefined") {
- define([], function() {
- return orion.textview;
- });
-}
+/*global define */
+
+define(['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
.
+ *
+ * See:
+ * {@link orion.textview.AnnotationModel}
+ * {@link orion.textview.Ruler}
+ *
+ * @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 an annotation model.
+ *
+ * @param {textModel} textModel The text model.
+ *
+ * @class This object manages annotations for a TextModel
.
+ *
+ * See:
+ * {@link orion.textview.Annotation}
+ * {@link orion.textview.TextModel}
+ *
+ * @name orion.textview.AnnotationModel
+ * @borrows orion.textview.EventTarget#addEventListener as #addEventListener
+ * @borrows orion.textview.EventTarget#removeEventListener as #removeEventListener
+ * @borrows orion.textview.EventTarget#dispatchEvent as #dispatchEvent
+ */
+ function AnnotationModel(textModel) {
+ this._annotations = [];
+ var self = this;
+ this._listener = {
+ onChanged: function(modelChangedEvent) {
+ self._onChanged(modelChangedEvent);
+ }
+ };
+ this.setTextModel(textModel);
+ }
+
+ AnnotationModel.prototype = /** @lends orion.textview.AnnotationModel.prototype */ {
+ /**
+ * Adds an annotation to the annotation model.
+ * The annotation model listeners are notified of this change.
+ *
+ * @param {orion.textview.Annotation} annotation the annotation to be added.
+ *
+ * @see #removeAnnotation
+ */
+ addAnnotation: function(annotation) {
+ if (!annotation) { return; }
+ var annotations = this._annotations;
+ var index = this._binarySearch(annotations, annotation.start);
+ annotations.splice(index, 0, annotation);
+ var e = {
+ type: "Changed",
+ added: [annotation],
+ removed: [],
+ changed: []
+ };
+ this.onChanged(e);
+ },
+ /**
+ * Returns the text model.
+ *
+ * @return {orion.textview.TextModel} The text model.
+ *
+ * @see #setTextModel
+ */
+ getTextModel: function() {
+ return this._model;
+ },
+ /**
+ * @class This object represents an annotation iterator.
+ *
+ * See:
+ * {@link orion.textview.AnnotationModel#getAnnotations}
+ *
+ * @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
+ */
+ function AnnotationStyler (view, annotationModel) {
+ this._view = view;
+ this._annotationModel = annotationModel;
+ this._types = [];
+ 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 */ {
+ /**
+ * Adds an annotation type to the receiver.
+ *
+ * Only annotations of the specified types will be shown by
+ * this receiver.
+ *
+ *
+ * @param type {Object} the annotation type to be shown
+ *
+ * @see #removeAnnotationType
+ * @see #isAnnotationTypeVisible
+ */
+ addAnnotationType: function(type) {
+ this._types.push(type);
+ },
+ /**
+ * 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;
+ }
+ },
+ /**
+ * 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) {
+ for (var i = 0; i < this._types.length; i++) {
+ if (this._types[i] === type) {
+ return true;
+ }
+ }
+ return false;
+ },
+ /**
+ * Removes an annotation type from the receiver.
+ *
+ * @param {Object} type the annotation type to be removed
+ *
+ * @see #addAnnotationType
+ * @see #isAnnotationTypeVisible
+ */
+ removeAnnotationType: function(type) {
+ for (var i = 0; i < this._types.length; i++) {
+ if (this._types[i] === type) {
+ this._types.splice(i, 1);
+ break;
+ }
+ }
+ },
+ _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);
+ }
+ }
+ }
+ };
+
+ return {
+ FoldingAnnotation: FoldingAnnotation,
+ AnnotationModel: AnnotationModel,
+ AnnotationStyler: AnnotationStyler
+ };
+}, "orion/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
@@ -1707,19 +2544,10 @@ if (typeof window !== "undefined" && typeof window.define !== "undefined") {
* Contributors: IBM Corporation - initial API and implementation
******************************************************************************/
-/*global window define setTimeout clearTimeout setInterval clearInterval Node */
+/*global define setTimeout clearTimeout setInterval clearInterval Node */
-/**
- * @namespace The global container for Orion APIs.
- */
-var orion = orion || {};
-/**
- * @namespace The container for textview APIs.
- */
-orion.textview = orion.textview || {};
+define(['orion/textview/textView', 'orion/textview/textModel', 'orion/textview/projectionTextModel'], function(mTextView, mTextModel, mProjectionTextModel) {
-/** @ignore */
-orion.textview.Tooltip = (function() {
/** @private */
function Tooltip (view) {
this._view = view;
@@ -1816,14 +2644,15 @@ orion.textview.Tooltip = (function() {
(contentsDiv = this._htmlParent).innerHTML = contents;
} else if (contents instanceof Node) {
(contentsDiv = this._htmlParent).appendChild(contents);
- } else if (contents instanceof orion.textview.ProjectionTextModel) {
+ } else if (contents instanceof mProjectionTextModel.ProjectionTextModel) {
if (!this._contentsView) {
- this._emptyModel = new orion.textview.TextModel("");
+ 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 orion.textview.TextView({
+ 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"]
});
@@ -1831,7 +2660,12 @@ orion.textview.Tooltip = (function() {
newView._clientDiv.contentEditable = false;
//TODO need to find a better way of sharing the styler for multiple views
var view = this._view;
- newView.addEventListener("LineStyle", view, view.onLineStyle);
+ var listener = {
+ onLineStyle: function(e) {
+ view.onLineStyle(e);
+ }
+ };
+ newView.addEventListener("LineStyle", listener.onLineStyle);
}
var contentsView = this._contentsView;
contentsView.setModel(contents);
@@ -1893,16 +2727,16 @@ orion.textview.Tooltip = (function() {
annotation = annotations[0];
if (annotation.title) {
title = annotation.title.replace(//g, ">");
- return annotation.html + " " + title;
+ return "" + annotation.html + "
" + title + "";
} else {
- var newModel = new orion.textview.ProjectionTextModel(baseModel);
+ 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:";
+ var tooltipHTML = "
Multiple annotations:
";
for (var i = 0; i < annotations.length; i++) {
annotation = annotations[i];
title = annotation.title;
@@ -1910,7 +2744,7 @@ orion.textview.Tooltip = (function() {
title = getText(annotation.start, annotation.end);
}
title = title.replace(//g, ">");
- tooltipHTML += annotation.html + " " + title + "
";
+ tooltipHTML += "
" + annotation.html + "
" + title + "";
}
return tooltipHTML;
}
@@ -1935,14 +2769,10 @@ orion.textview.Tooltip = (function() {
return value || defaultValue;
}
};
- return Tooltip;
-}());
-
-if (typeof window !== "undefined" && typeof window.define !== "undefined") {
- define([], function() {
- return orion.textview;
- });
-}/*******************************************************************************
+ return {Tooltip: Tooltip};
+}, "orion/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
@@ -1952,35 +2782,12 @@ if (typeof window !== "undefined" && typeof window.define !== "undefined") {
* 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#358623 Bug#362286 Bug#362107 Bug#362428 Bug#362835 Bug#363508
+ * 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
******************************************************************************/
/*global window document navigator setTimeout clearTimeout XMLHttpRequest define */
-/**
- * @namespace The global container for Orion APIs.
- */
-var orion = orion || {};
-/**
- * @namespace The container for textview APIs.
- */
-orion.textview = orion.textview || {};
-
-/**
- * Constructs a new text view.
- *
- * @param options the view options.
- * @param {String|DOMElement} options.parent the parent element for the view, it can be either a DOM element or an ID for a DOM element.
- * @param {orion.textview.TextModel} [options.model] the text model for the view. If this options is not set the view creates an empty {@link orion.textview.TextModel}.
- * @param {Boolean} [options.readonly=false] whether or not the view is read-only.
- * @param {Boolean} [options.fullSelection=true] whether or not the view is in full selection mode.
- * @param {String|String[]} [options.stylesheet] one or more stylesheet URIs for the view.
- * @param {Number} [options.tabSize] The number of spaces in a tab.
- *
- * @class A TextView is a user interface for editing text.
- * @name orion.textview.TextView
- */
-orion.textview.TextView = (function() {
+define(['orion/textview/textModel', 'orion/textview/keyBinding', 'orion/textview/eventTarget'], function(mTextModel, mKeyBinding, mEventTarget) {
/** @private */
function addHandler(node, type, handler, capture) {
@@ -1998,19 +2805,22 @@ orion.textview.TextView = (function() {
node.detachEvent("on" + type, handler);
}
}
- var isIE = document.selection && window.ActiveXObject && /MSIE/.test(navigator.userAgent) ? document.documentMode : undefined;
- var isFirefox = parseFloat(navigator.userAgent.split("Firefox/")[1] || navigator.userAgent.split("Minefield/")[1]) || undefined;
- var isOpera = navigator.userAgent.indexOf("Opera") !== -1;
- var isChrome = navigator.userAgent.indexOf("Chrome") !== -1;
- var isSafari = navigator.userAgent.indexOf("Safari") !== -1;
- var isWebkit = navigator.userAgent.indexOf("WebKit") !== -1;
- var isPad = navigator.userAgent.indexOf("iPad") !== -1;
+ 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 isDnD = isFirefox || isWebkit; // drag and drop support
var platformDelimiter = isWindows ? "\r\n" : "\n";
/**
@@ -2019,185 +2829,108 @@ orion.textview.TextView = (function() {
* @class A Selection represents a range of selected text in the view.
* @name orion.textview.Selection
*/
- var Selection = (function() {
+ 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 */
- 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.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;
- 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;
}
- };
- return Selection;
- }());
-
- /**
- * Constructs a new EventTable object.
- *
- * @class
- * @name orion.textview.EventTable
- * @private
- */
- var EventTable = (function() {
+ if (this.start > this.end) {
+ var tmp = this.start;
+ this.start = this.end;
+ this.end = tmp;
+ this.caret = !this.caret;
+ }
+ },
/** @private */
- function EventTable(){
- this._types = {};
+ 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;
}
- EventTable.prototype = /** @lends EventTable.prototype */ {
- /** @private */
- addEventListener: function(type, context, func, data) {
- var state = this._types[type];
- if (!state) {
- state = this._types[type] = {level: 0, listeners: []};
- }
- var listener = {
- context: context,
- func: func,
- data: data
- };
- var listeners = state.listeners;
- listeners.push(listener);
- },
- /** @private */
- sendEvent: function(type, event) {
- var state = this._types[type];
- if (state) {
- var listeners = state.listeners;
- try {
- state.level++;
- if (listeners) {
- for (var i=0, len=listeners.length; i < len; i++) {
- var l = listeners[i];
- if (l && l.context && l.func) {
- l.func.call(l.context, event, l.data);
- }
- }
- }
- } 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);
- }
- }
- state.compact = false;
- }
- }
- }
- },
- /** @private */
- removeEventListener: function(type, context, func, data){
- var state = this._types[type];
- if (state) {
- var listeners = state.listeners;
- for (var i=0, len=listeners.length; i < len; i++) {
- var l = listeners[i];
- if (l && l.context === context && l.func === func && l.data === data) {
- if (state.level !== 0) {
- listeners[i] = null;
- state.compact = true;
- } else {
- listeners.splice(i, 1);
- }
- break;
- }
- }
- }
- }
- };
- return EventTable;
- }());
-
- /** @private */
+ };
+ /**
+ * @class This object describes the options for the text view.
+ *
+ * See:
+ * {@link orion.textview.TextView}
+ * {@link orion.textview.TextView#setOptions}
+ * {@link orion.textview.TextView#getOptions}
+ *
+ * @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 URIs for the view.
+ * @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 an event listener to the text view.
- *
- * @param {String} type the event type. The supported events are:
- *
- * - "Modify" See {@link #onModify}
- * - "Selection" See {@link #onSelection}
- * - "Scroll" See {@link #onScroll}
- * - "Verify" See {@link #onVerify}
- * - "Destroy" See {@link #onDestroy}
- * - "LineStyle" See {@link #onLineStyle}
- * - "ModelChanging" See {@link #onModelChanging}
- * - "ModelChanged" See {@link #onModelChanged}
- *
- * @param {Object} context the context of the function.
- * @param {Function} func the function that will be executed when the event happens.
- * The function should take an event as the first parameter and optional data as the second parameter.
- * @param {Object} [data] optional data passed to the function.
- *
- * @see #removeEventListener
- */
- addEventListener: function(type, context, func, data) {
- this._eventTable.addEventListener(type, context, func, data);
- },
/**
* Adds a ruler to the text view.
*
@@ -2212,6 +2945,7 @@ orion.textview.TextView = (function() {
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
@@ -2228,7 +2962,7 @@ orion.textview.TextView = (function() {
var document = this._frameDocument;
for (var lineIndex=0; lineIndex
true if the text view has focus, otherwise false
.
+ */
+ hasFocus: function() {
+ return this._hasFocus;
+ },
/**
* Returns all action names defined in the text view.
*
@@ -2466,6 +3206,7 @@ orion.textview.TextView = (function() {
* @see #setTopIndex
*/
getBottomIndex: function(fullyVisible) {
+ if (!this._clientDiv) { return 0; }
return this._getBottomIndex(fullyVisible);
},
/**
@@ -2483,6 +3224,7 @@ orion.textview.TextView = (function() {
* @see #convert
*/
getBottomPixel: function() {
+ if (!this._clientDiv) { return 0; }
return this._getScroll().y + this._getClientHeight();
},
/**
@@ -2513,6 +3255,7 @@ orion.textview.TextView = (function() {
* @see #convert
*/
getClientArea: function() {
+ if (!this._clientDiv) { return {x: 0, y: 0, width: 0, height: 0}; }
var scroll = this._getScroll();
return {x: scroll.x, y: scroll.y, width: this._getClientWidth(), height: this._getClientHeight()};
},
@@ -2530,6 +3273,7 @@ orion.textview.TextView = (function() {
* @see #convert
*/
getHorizontalPixel: function() {
+ if (!this._clientDiv) { return 0; }
return this._getScroll().x;
},
/**
@@ -2561,6 +3305,7 @@ orion.textview.TextView = (function() {
* @see #getLinePixel
*/
getLineHeight: function(lineIndex) {
+ if (!this._clientDiv) { return 0; }
return this._getLineHeight();
},
/**
@@ -2577,6 +3322,7 @@ orion.textview.TextView = (function() {
* @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;
@@ -2596,6 +3342,7 @@ orion.textview.TextView = (function() {
* @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);
@@ -2606,6 +3353,44 @@ orion.textview.TextView = (function() {
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.
*
@@ -2625,6 +3410,7 @@ orion.textview.TextView = (function() {
* @see #getLocationAtOffset
*/
getOffsetAtLocation: function(x, y) {
+ if (!this._clientDiv) { return 0; }
var scroll = this._getScroll();
var viewRect = this._viewDiv.getBoundingClientRect();
var viewPad = this._getViewPadding();
@@ -2689,6 +3475,7 @@ orion.textview.TextView = (function() {
* @see #setTopIndex
*/
getTopIndex: function(fullyVisible) {
+ if (!this._clientDiv) { return 0; }
return this._getTopIndex(fullyVisible);
},
/**
@@ -2706,6 +3493,7 @@ orion.textview.TextView = (function() {
* @see #convert
*/
getTopPixel: function() {
+ if (!this._clientDiv) { return 0; }
return this._getScroll().y;
},
/**
@@ -2727,6 +3515,7 @@ orion.textview.TextView = (function() {
* @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];
@@ -2761,9 +3550,30 @@ orion.textview.TextView = (function() {
* @event
* @param {orion.textview.ContextMenuEvent} contextMenuEvent the event
*/
- onContextMenu: function(contextMenuEvent) {
- this._eventTable.sendEvent("ContextMenu", contextMenuEvent);
+ 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.
*
@@ -2782,7 +3592,7 @@ orion.textview.TextView = (function() {
* @see #destroy
*/
onDestroy: function(destroyEvent) {
- this._eventTable.sendEvent("Destroy", destroyEvent);
+ return this.dispatchEvent(destroyEvent);
},
/**
* @class This object is used to define style information for the text view.
@@ -2834,7 +3644,25 @@ orion.textview.TextView = (function() {
* @param {orion.textview.LineStyleEvent} lineStyleEvent the event
*/
onLineStyle: function(lineStyleEvent) {
- this._eventTable.sendEvent("LineStyle", lineStyleEvent);
+ return this.dispatchEvent(lineStyleEvent);
+ },
+ /**
+ * @class This is the event sent when the text view has loaded its contents.
+ *
+ * See:
+ * {@link orion.textview.TextView}
+ * {@link orion.textview.TextView#event:onLoad}
+ *
+ * @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.
@@ -2859,7 +3687,7 @@ orion.textview.TextView = (function() {
* @param {orion.textview.ModelChangedEvent} modelChangedEvent the event
*/
onModelChanged: function(modelChangedEvent) {
- this._eventTable.sendEvent("ModelChanged", modelChangedEvent);
+ return this.dispatchEvent(modelChangedEvent);
},
/**
* @class This is the event sent when the text in the model is about to change.
@@ -2885,7 +3713,7 @@ orion.textview.TextView = (function() {
* @param {orion.textview.ModelChangingEvent} modelChangingEvent the event
*/
onModelChanging: function(modelChangingEvent) {
- this._eventTable.sendEvent("ModelChanging", modelChangingEvent);
+ return this.dispatchEvent(modelChangingEvent);
},
/**
* @class This is the event sent when the text is modified by the text view.
@@ -2907,7 +3735,22 @@ orion.textview.TextView = (function() {
* @param {orion.textview.ModifyEvent} modifyEvent the event
*/
onModify: function(modifyEvent) {
- this._eventTable.sendEvent("Modify", modifyEvent);
+ return this.dispatchEvent(modifyEvent);
+ },
+ onMouseDown: function(mouseEvent) {
+ return this.dispatchEvent(mouseEvent);
+ },
+ onMouseUp: function(mouseEvent) {
+ return this.dispatchEvent(mouseEvent);
+ },
+ onMouseMove: function(mouseEvent) {
+ return this.dispatchEvent(mouseEvent);
+ },
+ onMouseOver: function(mouseEvent) {
+ return this.dispatchEvent(mouseEvent);
+ },
+ onMouseOut: function(mouseEvent) {
+ return this.dispatchEvent(mouseEvent);
},
/**
* @class This is the event sent when the selection changes in the text view.
@@ -2928,7 +3771,7 @@ orion.textview.TextView = (function() {
* @param {orion.textview.SelectionEvent} selectionEvent the event
*/
onSelection: function(selectionEvent) {
- this._eventTable.sendEvent("Selection", selectionEvent);
+ return this.dispatchEvent(selectionEvent);
},
/**
* @class This is the event sent when the text view scrolls.
@@ -2949,7 +3792,7 @@ orion.textview.TextView = (function() {
* @param {orion.textview.ScrollEvent} scrollEvent the event
*/
onScroll: function(scrollEvent) {
- this._eventTable.sendEvent("Scroll", scrollEvent);
+ return this.dispatchEvent(scrollEvent);
},
/**
* @class This is the event sent when the text is about to be modified by the text view.
@@ -2979,7 +3822,77 @@ orion.textview.TextView = (function() {
* @param {orion.textview.VerifyEvent} verifyEvent the event
*/
onVerify: function(verifyEvent) {
- this._eventTable.sendEvent("Verify", verifyEvent);
+ return this.dispatchEvent(verifyEvent);
+ },
+ /**
+ * @class This is the event sent when the text view has unloaded its contents.
+ *
+ * See:
+ * {@link orion.textview.TextView}
+ * {@link orion.textview.TextView#event:onLoad}
+ *
+ * @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.
+ *
+ * See:
+ * {@link orion.textview.TextView}
+ * {@link orion.textview.TextView#event:onFocus}
+ *
+ * @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.
+ *
+ * See:
+ * {@link orion.textview.TextView}
+ * {@link orion.textview.TextView#event:onBlur}
+ *
+ * @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.
@@ -2989,6 +3902,10 @@ orion.textview.TextView = (function() {
*
* @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; }
@@ -3038,32 +3955,20 @@ orion.textview.TextView = (function() {
*
* @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(); }
- if (start === end) { return; }
var startLine = model.getLineAtOffset(start);
- var endLine = model.getLineAtOffset(Math.max(0, end - 1)) + 1;
+ var endLine = model.getLineAtOffset(Math.max(start, end - 1)) + 1;
this.redrawLines(startLine, endLine);
},
- /**
- * Removes an event listener from the text view.
- *
- * All the parameters must be the same ones used to add the listener.
- *
- *
- * @param {String} type the event type.
- * @param {Object} context the context of the function.
- * @param {Function} func the function that will be executed when the event happens.
- * @param {Object} [data] optional data passed to the function.
- *
- * @see #addEventListener
- */
- removeEventListener: function(type, context, func, data) {
- this._eventTable.removeEventListener(type, context, func, data);
- },
/**
* Removes a ruler from the text view.
*
@@ -3187,19 +4092,28 @@ orion.textview.TextView = (function() {
* @see #convert
*/
setHorizontalPixel: function(pixel) {
+ if (!this._clientDiv) { return; }
pixel = Math.max(0, pixel);
this._scrollView(pixel - this._getScroll().x, 0);
},
+ /**
+ * Sets whether the view should update the DOM.
+ *
+ * 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) {
- 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);
- this._queueUpdatePage();
+ this.redraw();
}
} else {
this._redrawCount++;
@@ -3213,13 +4127,15 @@ orion.textview.TextView = (function() {
setModel: function(model) {
if (!model) { return; }
if (model === this._model) { return; }
- this._model.removeListener(this._modelListener);
+ 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,
@@ -3230,6 +4146,7 @@ orion.textview.TextView = (function() {
this.onModelChanging(e);
this._model = model;
e = {
+ type: "ModelChanged",
start: 0,
removedCharCount: oldCharCount,
addedCharCount: newCharCount,
@@ -3237,10 +4154,55 @@ orion.textview.TextView = (function() {
addedLineCount: newLineCount
};
this.onModelChanged(e);
- this._model.addListener(this._modelListener);
+ 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) {
+ update.call(this, newValue);
+ 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.
*
@@ -3307,14 +4269,7 @@ orion.textview.TextView = (function() {
* force the clientDiv to loose and receive focus if the it is focused.
*/
if (isFirefox) {
- var clientDiv = this._clientDiv;
- if (clientDiv) {
- var hasFocus = this._hasFocus;
- if (hasFocus) { clientDiv.blur(); }
- clientDiv.contentEditable = false;
- clientDiv.contentEditable = true;
- if (hasFocus) { clientDiv.focus(); }
- }
+ this._fixCaret();
}
}
},
@@ -3331,6 +4286,7 @@ orion.textview.TextView = (function() {
* @see #getTopIndex
*/
setTopIndex: function(topIndex) {
+ if (!this._clientDiv) { return; }
var model = this._model;
if (model.getCharCount() === 0) {
return;
@@ -3361,6 +4317,7 @@ orion.textview.TextView = (function() {
* @see #convert
*/
setTopPixel: function(pixel) {
+ if (!this._clientDiv) { return; }
var lineHeight = this._getLineHeight();
var clientHeight = this._getClientHeight();
var lineCount = this._model.getLineCount();
@@ -3382,6 +4339,12 @@ orion.textview.TextView = (function() {
/**************************************** Event handlers *********************************/
_handleBodyMouseDown: function (e) {
if (!e) { e = window.event; }
+ if (isFirefox) {
+ 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
@@ -3389,7 +4352,7 @@ orion.textview.TextView = (function() {
* do not have this problem and stopping the click over the
* scrollbar for them causes mouse capture problems.
*/
- var topNode = isOpera ? this._clientDiv : this._overlayDiv || this._viewDiv;
+ var topNode = isOpera || (isFirefox && !this._overlayDiv) ? this._clientDiv : this._overlayDiv || this._viewDiv;
var temp = e.target ? e.target : e.srcElement;
while (temp) {
@@ -3409,8 +4372,26 @@ orion.textview.TextView = (function() {
setTimeout(function() { topNode.releaseCapture(); }, 0);
}
},
+ _handleBodyMouseUp: function (e) {
+ if (!e) { e = window.event; }
+ if (isFirefox) {
+ 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 the 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
@@ -3434,15 +4415,27 @@ orion.textview.TextView = (function() {
this._selDiv3.style.background = color;
}
}
+ if (!this._ignoreFocus) {
+ this.onBlur({type: "Blur"});
+ }
},
_handleContextMenu: function (e) {
if (!e) { e = window.event; }
- 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 - viewPad.top;
- this.onContextMenu({x: x, y: y, screenX: e.screenX, screenY: e.screenY});
+ 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;
},
@@ -3494,129 +4487,98 @@ orion.textview.TextView = (function() {
},
_handleDragStart: function (e) {
if (!e) { e = window.event; }
- if (isDnD) {
- var sel = this._getSelection();
- var text = !sel.isEmpty() ? this._getBaseText(sel.start, sel.end) : "";
- if (text) {
- e.dataTransfer.effectAllowed = "copyMove";
- e.dataTransfer.setData("text/plain", text);
- // TODO: generate a drag image to be a better visual indicatator of the drag operation.
- this._dragStartSelection = {start: sel.start, end: sel.end};
- this.focus();
- return;
- }
+ 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));
}
- if (e.preventDefault) { e.preventDefault(); }
- return false;
},
_handleDragEnd: function (e) {
if (!e) { e = window.event; }
- if (e.preventDefault) { e.preventDefault(); }
- var startSel = this._dragStartSelection;
- var drop = this._dropDestination;
- if (startSel && e.dataTransfer.dropEffect === "move") {
- var offset = 0;
- if (drop && drop.offset < Math.min(startSel.start, startSel.end)) {
- offset = drop.length;
- }
- var change = {
- text: "",
- start: startSel.start + offset,
- end: startSel.end + offset
- };
- this._modifyContent(change, false);
+ this._dropTarget = false;
+ this._dragOffset = -1;
+ if (this.isListening("DragEnd")) {
+ this.onDragEnd(this._createMouseEvent("DragEnd", e));
}
- if (this._undoStack && drop) {
- this._undoStack.endCompoundChange();
+ if (isFirefox) {
+ this._fixCaret();
}
- this._dragNode.draggable = false;
- this._dragStartSelection = null;
- this._dropDestination = null;
- return false;
},
_handleDragEnter: function (e) {
if (!e) { e = window.event; }
- if (e.preventDefault) { e.preventDefault(); }
- var types = e.dataTransfer.types;
- var allowed = false;
- var types = isDnD ? e.dataTransfer.types : null;
- if (types) {
- // Firefox gives a .types of type StringList, while Webkit gives us an actual string.
- allowed = types.contains ? types.contains("text/plain") : types.indexOf("text/plain");
+ var prevent = true;
+ this._dropTarget = true;
+ if (this.isListening("DragEnter")) {
+ prevent = false;
+ this.onDragEnter(this._createMouseEvent("DragEnter", e));
}
- if (allowed) {
- e.dataTransfer.dropEffect = "copyMove";
- this.focus();
- return true;
+ /*
+ * 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;
}
- e.dataTransfer.dropEffect = "none";
- return false;
},
_handleDragOver: function (e) {
if (!e) { e = window.event; }
- if (e.preventDefault) { e.preventDefault(); }
- var allowed = false;
- var types = isDnD ? e.dataTransfer.types : null;
- if (types) {
- allowed = types.contains ? types.contains("text/plain") : types.indexOf("text/plain");
+ var prevent = true;
+ if (this.isListening("DragOver")) {
+ prevent = false;
+ this.onDragOver(this._createMouseEvent("DragOver", e));
}
- if (!allowed) {
- e.dataTransfer.dropEffect = "none";
+ /*
+ * 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;
}
-
- var destLine = this._getYToLine(e.clientY);
- var destOffset = this._getXToOffset(destLine, e.clientX);
-
- var startSel = this._dragStartSelection;
- if (startSel && startSel.start <= destOffset && destOffset <= startSel.end) {
- e.dataTransfer.dropEffect = "none";
- return false;
+ },
+ _handleDragLeave: function (e) {
+ if (!e) { e = window.event; }
+ this._dropTarget = false;
+ if (this.isListening("DragLeave")) {
+ this.onDragLeave(this._createMouseEvent("DragLeave", e));
}
-
- if (!startSel) {
- // Hide the selection when the user drags something coming from the outside.
- // TODO: make sure the cursor is actually visible. It's not visible in Firefox during drag, only in Chrome...
- this.setSelection(destOffset, destOffset, true);
- }
-
- return true;
},
_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(); }
- var allowed = false;
- var types = isDnD ? e.dataTransfer.types : null;
- if (types) {
- allowed = types.contains ? types.contains("text/plain") : types.indexOf("text/plain");
- }
- if (!allowed) {
- return false;
- }
-
- var destLine = this._getYToLine(e.clientY);
- var destOffset = this._getXToOffset(destLine, e.clientX);
- var startSel = this._dragStartSelection;
-
- if (startSel && startSel.start <= destOffset && destOffset <= startSel.end) {
- return false;
- }
-
- var text = e.dataTransfer.getData("text/plain");
- this.setSelection(destOffset, destOffset, true);
-
- if (startSel) {
- this._dropDestination = {offset: destOffset, length: text.length};
- if (this._undoStack) {
- this._undoStack.startCompoundChange();
- }
- } else {
- this._dragNode.draggable = false;
- }
-
- this._doContent(text);
- this.focus();
- return true;
+ return false;
},
_handleDocFocus: function (e) {
if (!e) { e = window.event; }
@@ -3642,6 +4604,9 @@ orion.textview.TextView = (function() {
this._selDiv3.style.background = color;
}
}
+ if (!this._ignoreFocus) {
+ this.onFocus({type: "Focus"});
+ }
},
_handleKeyDown: function (e) {
if (!e) { e = window.event; }
@@ -3662,7 +4627,7 @@ orion.textview.TextView = (function() {
this._setLinksVisible(false);
}
if (e.keyCode === 229) {
- if (this.readonly) {
+ if (this._readonly) {
if (e.preventDefault) { e.preventDefault(); }
return false;
}
@@ -3775,21 +4740,26 @@ orion.textview.TextView = (function() {
_handleLoad: function (e) {
var state = this._getVisible();
if (state === "visible" || (state === "hidden" && isWebkit)) {
- this._createView(!e);
+ this._createView();
}
},
_handleMouse: function (e) {
+ var result = true;
var target = this._frameWindow;
- if (isIE) { target = this._clientDiv; }
+ 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) {
- this._setGrab(target);
- this._setSelectionTo(e.clientX, e.clientY, e.shiftKey);
+ 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
@@ -3810,9 +4780,13 @@ orion.textview.TextView = (function() {
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") {
@@ -3821,59 +4795,82 @@ orion.textview.TextView = (function() {
return;
}
}
- var left = e.which ? e.button === 0 : e.button === 1;
this._commitIME();
- if (left) {
- var deltaX = Math.abs(this._lastMouseX - e.clientX);
- var deltaY = Math.abs(this._lastMouseY - e.clientY);
- var time = e.timeStamp ? e.timeStamp : new Date().getTime();
- if ((time - this._lastMouseTime) <= this._clickTime && deltaX <= this._clickDist && deltaY <= this._clickDist) {
+
+ 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;
}
- this._lastMouseX = e.clientX;
- this._lastMouseY = e.clientY;
- this._lastMouseTime = time;
-
- // Selection drag support
- if (isDnD && this._clickCount === 1) {
- var inSelection = false;
- var selection = this._getSelection();
- if (!selection.isEmpty()) {
- var clickLine = this._getYToLine(e.clientY);
- var clickOffset = this._getXToOffset(clickLine, e.clientX);
- inSelection = selection.start < clickOffset && clickOffset < selection.end;
- }
-
- // Webkit fails to allow dragging if .draggable is set to true during mousedown.
- // But Firefox makes it a requirement to set .draggable to true.
- this._dragNode.draggable = !isWebkit && inSelection;
-
- if (inSelection) {
- return; // allow the dragstart event
- }
- }
- if (this._dragNode && this._dragNode.draggable) {
- this._dragNode.draggable = false;
- }
-
- this._isMouseDown = true;
- this._handleMouse(e);
- if (isOpera || isChrome) {
+ if (this._handleMouse(e) && (isOpera || isChrome || (isFirefox && !this._overlayDiv))) {
if (!this._hasFocus) {
this.focus();
}
- }
- if (e.preventDefault) {
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; }
- this._setLinksVisible(!this._isMouseDown && (isMac ? e.metaKey : e.ctrlKey));
- if (!this._isMouseDown) {
+ 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));
+ if (!this._isMouseDown || this._dragOffset !== -1) {
return;
}
/*
@@ -3946,21 +4943,36 @@ orion.textview.TextView = (function() {
}
}
},
+ _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._dragNode && this._dragNode.draggable) {
- this._dragNode.draggable = false;
- if (!this._dragStartSelection) {
- this._setSelectionTo(e.clientX, e.clientY, false);
- }
- this.focus();
+ 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();
@@ -3978,6 +4990,17 @@ orion.textview.TextView = (function() {
* 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) {
@@ -4062,7 +5085,11 @@ orion.textview.TextView = (function() {
* Bug in IE,
*/
var self = this;
- setTimeout(function() {self._updateDOMSelection();}, 0);
+ this._ignoreFocus = true;
+ setTimeout(function() {
+ self._updateDOMSelection();
+ this._ignoreFocus = false;
+ }, 0);
}
if (e.preventDefault) { e.preventDefault(); }
return false;
@@ -4130,8 +5157,9 @@ orion.textview.TextView = (function() {
this._hScroll = scroll.x;
this._vScroll = scroll.y;
this._commitIME();
- this._updatePage();
+ this._updatePage(oldY === scroll.y);
var e = {
+ type: "Scroll",
oldValue: {x: oldX, y: oldY},
newValue: scroll
};
@@ -4320,7 +5348,7 @@ orion.textview.TextView = (function() {
}
}, 0);
}
-// e.preventDefault();
+// e.preventDefault();
},
/************************************ Actions ******************************************/
@@ -4360,12 +5388,22 @@ orion.textview.TextView = (function() {
var model = this._model;
var caret = selection.getCaret();
var lineIndex = model.getLineAtOffset(caret);
- if (caret === model.getLineStart(lineIndex)) {
+ var lineStart = model.getLineStart(lineIndex);
+ if (caret === lineStart) {
if (lineIndex > 0) {
selection.extend(model.getLineEnd(lineIndex - 1));
}
} else {
- selection.extend(this._getOffset(caret, args.unit, -1));
+ 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);
@@ -4491,7 +5529,7 @@ orion.textview.TextView = (function() {
if (lineIndex + 1 < model.getLineCount()) {
var scrollX = this._getScroll().x;
var x = this._columnX;
- if (x === -1 || args.select || args.wholeLine) {
+ if (x === -1 || args.wholeLine || (args.select && isIE)) {
var offset = args.wholeLine ? model.getLineEnd(lineIndex + 1) : caret;
x = this._getOffsetToX(offset) + scrollX;
}
@@ -4510,7 +5548,7 @@ orion.textview.TextView = (function() {
if (lineIndex > 0) {
var scrollX = this._getScroll().x;
var x = this._columnX;
- if (x === -1 || args.select || args.wholeLine) {
+ if (x === -1 || args.wholeLine || (args.select && isIE)) {
var offset = args.wholeLine ? model.getLineStart(lineIndex - 1) : caret;
x = this._getOffsetToX(offset) + scrollX;
}
@@ -4535,7 +5573,7 @@ orion.textview.TextView = (function() {
var scrollLines = Math.min(lineCount - caretLine - 1, lines);
scrollLines = Math.max(1, scrollLines);
var x = this._columnX;
- if (x === -1 || args.select) {
+ if (x === -1 || (args.select && isIE)) {
x = this._getOffsetToX(caret) + scroll.x;
}
selection.extend(this._getXToOffset(caretLine + scrollLines, x - scroll.x));
@@ -4562,7 +5600,7 @@ orion.textview.TextView = (function() {
var lines = Math.floor(clientHeight / lineHeight);
var scrollLines = Math.max(1, Math.min(caretLine, lines));
var x = this._columnX;
- if (x === -1 || args.select) {
+ if (x === -1 || (args.select && isIE)) {
x = this._getOffsetToX(caret) + scroll.x;
}
selection.extend(this._getXToOffset(caretLine - scrollLines, x - scroll.x));
@@ -4574,11 +5612,19 @@ orion.textview.TextView = (function() {
return true;
},
_doPaste: function(e) {
- var text = this._getClipboardText(e);
- if (text) {
- this._doContent(text);
- }
- return text !== null;
+ 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;
@@ -4616,12 +5662,27 @@ orion.textview.TextView = (function() {
return true;
},
_doTab: function (args) {
- this._doContent("\t");
+ 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) {
+ _applyStyle: function(style, node, reset) {
+ if (reset) {
+ var attrs = node.attributes;
+ for (var i= attrs.length; i-->0;) {
+ node.removeAttributeNode(attrs[i]);
+ }
+ }
if (!style) {
return;
}
@@ -4710,7 +5771,17 @@ orion.textview.TextView = (function() {
if (h4 > h3) {
fontStyle = 3;
}
- this._largestFontStyle = fontStyle;
+ 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;
},
@@ -4754,6 +5825,40 @@ orion.textview.TextView = (function() {
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
@@ -4805,7 +5910,7 @@ orion.textview.TextView = (function() {
}
},
_createActions: function () {
- var KeyBinding = orion.textview.KeyBinding;
+ var KeyBinding = mKeyBinding.KeyBinding;
//no duplicate keybindings
var bindings = this._keyBindings = [];
@@ -4827,20 +5932,27 @@ orion.textview.TextView = (function() {
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});
- if (isLinux) {
- bindings.push({name: "lineStartUp", keyBinding: new KeyBinding(38, true), predefined: true});
- bindings.push({name: "lineEndDown", keyBinding: new KeyBinding(40, true), 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});
@@ -4858,6 +5970,11 @@ orion.textview.TextView = (function() {
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});
@@ -4936,8 +6053,6 @@ orion.textview.TextView = (function() {
{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: "lineStartUp", defaultHandler: function() {return self._doLineUp({select: false, wholeLine:true});}},
- {name: "lineEndDown", defaultHandler: function() {return self._doLineDown({select: false, wholeLine:true});}},
{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});}},
@@ -4982,49 +6097,20 @@ orion.textview.TextView = (function() {
{name: "paste", defaultHandler: function() {return self._doPaste();}}
];
},
- _createLine: function(parent, sibling, document, lineIndex, model) {
+ _createLine: function(parent, div, document, lineIndex, model) {
var lineText = model.getLine(lineIndex);
var lineStart = model.getLineStart(lineIndex);
- var e = {textView: this, lineIndex: lineIndex, lineText: lineText, lineStart: lineStart};
+ var e = {type:"LineStyle", textView: this, lineIndex: lineIndex, lineText: lineText, lineStart: lineStart};
this.onLineStyle(e);
- var child = document.createElement("DIV");
- child.lineIndex = lineIndex;
- this._applyStyle(e.style, child);
- if (lineText.length !== 0) {
- var start = 0;
- var tabSize = this._customTabSize;
- if (tabSize && tabSize !== 8) {
- var tabIndex = lineText.indexOf("\t"), ignoreChars = 0;
- while (tabIndex !== -1) {
- this._createRanges(child, document, e.ranges, start, tabIndex, lineText, lineStart);
- var spacesCount = tabSize - ((tabIndex + ignoreChars) % tabSize);
- var spaces = "\u00A0";
- for (var i = 1; i < spacesCount; i++) {
- spaces += " ";
- }
- var tabSpan = document.createElement("SPAN");
- tabSpan.appendChild(document.createTextNode(spaces));
- tabSpan.ignoreChars = spacesCount - 1;
- ignoreChars += tabSpan.ignoreChars;
- if (e.ranges) {
- for (var j = 0; j < e.ranges.length; j++) {
- var range = e.ranges[j];
- var styleStart = range.start - lineStart;
- var styleEnd = range.end - lineStart;
- if (styleStart > tabIndex) { break; }
- if (styleStart <= tabIndex && tabIndex < styleEnd) {
- this._applyStyle(range.style, tabSpan);
- break;
- }
- }
- }
- child.appendChild(tabSpan);
- start = tabIndex + 1;
- tabIndex = lineText.indexOf("\t", start);
- }
- }
- this._createRanges(child, document, e.ranges, start, lineText.length, lineText, lineStart);
+ 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:
@@ -5034,14 +6120,6 @@ orion.textview.TextView = (function() {
* 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 span = document.createElement("SPAN");
- span.ignoreChars = 1;
- if ((this._largestFontStyle & 1) !== 0) {
- span.style.fontStyle = "italic";
- }
- if ((this._largestFontStyle & 2) !== 0) {
- span.style.fontWeight = "bold";
- }
var c = " ";
if (!this._fullSelection && isIE < 9) {
/*
@@ -5061,13 +6139,76 @@ orion.textview.TextView = (function() {
*/
c = "\u200C";
}
- span.appendChild(document.createTextNode(c));
- child.appendChild(span);
+ ranges.push({text: c, style: this._largestFontStyle, ignoreChars: 1});
- parent.insertBefore(child, sibling);
- return child;
+ 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;
+ }
+ delete div.modelChangedEvent;
+ }
+ 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;
+ delete oldSpan._rectsCache;
+ 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(parent, document, ranges, start, end, text, lineStart) {
+ _createRanges: function(ranges, text, start, end, lineStart, data) {
if (start >= end) { return; }
if (ranges) {
for (var i = 0; i < ranges.length; i++) {
@@ -5080,18 +6221,55 @@ orion.textview.TextView = (function() {
styleStart = Math.max(start, styleStart);
styleEnd = Math.min(end, styleEnd);
if (start < styleStart) {
- parent.appendChild(this._createRange(parent, document, text.substring(start, styleStart), null));
+ this._createRange(text, start, styleStart, null, data);
}
- parent.appendChild(this._createRange(parent, document, text.substring(styleStart, styleEnd), range.style));
+ 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) {
- parent.appendChild(this._createRange(parent, document, text.substring(start, end), null));
+ this._createRange(text, start, end, null, data);
}
},
- _createRange: function(parent, document, text, style) {
+ _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";
@@ -5103,6 +6281,9 @@ orion.textview.TextView = (function() {
addHandler(child, "click", function(e) { return self._handleLinkClick(e); }, false);
}
child.viewStyle = style;
+ if (ignoreChars) {
+ child.ignoreChars = ignoreChars;
+ }
return child;
},
_createRuler: function(ruler) {
@@ -5172,7 +6353,10 @@ orion.textview.TextView = (function() {
* parent is not connected to the document. Only create it when the load
* event is trigged.
*/
- addHandler(frame, "load", this._loadHandler = function(e) { self._handleLoad(e); });
+ 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
@@ -5188,9 +6372,11 @@ orion.textview.TextView = (function() {
}
parent.appendChild(frame);
/* create synchronously if possible */
- this._handleLoad();
+ if (this._sync) {
+ this._handleLoad();
+ }
},
- _getFrameHTML: function(sync) {
+ _getFrameHTML: function() {
var html = [];
html.push("");
html.push("");
@@ -5206,9 +6392,8 @@ orion.textview.TextView = (function() {
if (this._stylesheet) {
var stylesheet = typeof(this._stylesheet) === "string" ? [this._stylesheet] : this._stylesheet;
for (var i = 0; i < stylesheet.length; i++) {
- if (sync) {
+ if (this._sync) {
try {
- //Force CSS to be loaded synchronously so lineHeight can be calculated
var objXml = new XMLHttpRequest();
if (objXml.overrideMimeType) {
objXml.overrideMimeType("text/css");
@@ -5226,29 +6411,64 @@ orion.textview.TextView = (function() {
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("