mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 15:23:51 +00:00
Merge mozilla-central to mozilla-inbound
This commit is contained in:
commit
4d6108d41a
@ -11,7 +11,7 @@ origin uitour 1 https://www.mozilla.org
|
||||
origin uitour 1 https://self-repair.mozilla.org
|
||||
origin uitour 1 https://support.mozilla.org
|
||||
origin uitour 1 https://addons.mozilla.org
|
||||
origin uitour 1 https://services.addons.mozilla.org
|
||||
origin uitour 1 https://discovery.addons.mozilla.org
|
||||
origin uitour 1 about:home
|
||||
|
||||
# XPInstall
|
||||
|
@ -23,6 +23,10 @@ var gEMEHandler = {
|
||||
Services.prefs.getPrefType("media.eme.clearkey.enabled") &&
|
||||
!Services.prefs.getBoolPref("media.eme.clearkey.enabled")) {
|
||||
Services.prefs.setBoolPref("media.eme.clearkey.enabled", true);
|
||||
} else if (keySystem == "com.widevine.alpha" &&
|
||||
Services.prefs.getPrefType("media.gmp-widevinecdm.enabled") &&
|
||||
!Services.prefs.getBoolPref("media.gmp-widevinecdm.enabled")) {
|
||||
Services.prefs.setBoolPref("media.gmp-widevinecdm.enabled", true);
|
||||
}
|
||||
}
|
||||
browser.reload();
|
||||
|
@ -140,7 +140,12 @@
|
||||
<panel type="autocomplete" id="PopupSearchAutoComplete" noautofocus="true" hidden="true"/>
|
||||
|
||||
<!-- for url bar autocomplete -->
|
||||
<panel type="autocomplete-richlistbox" id="PopupAutoCompleteRichResult" noautofocus="true" hidden="true" flip="none">
|
||||
<panel type="autocomplete-richlistbox"
|
||||
id="PopupAutoCompleteRichResult"
|
||||
noautofocus="true"
|
||||
hidden="true"
|
||||
flip="none"
|
||||
level="parent">
|
||||
#ifdef NIGHTLY_BUILD
|
||||
<hbox id="urlbar-search-footer" flex="1" align="stretch" pack="end">
|
||||
<button id="urlbar-search-settings" label="&changeSearchSettings.button;"
|
||||
|
@ -251,15 +251,32 @@ var AboutReaderListener = {
|
||||
addEventListener("DOMContentLoaded", this, false);
|
||||
addEventListener("pageshow", this, false);
|
||||
addEventListener("pagehide", this, false);
|
||||
addMessageListener("Reader:ParseDocument", this);
|
||||
addMessageListener("Reader:ToggleReaderMode", this);
|
||||
addMessageListener("Reader:PushState", this);
|
||||
},
|
||||
|
||||
receiveMessage: function(message) {
|
||||
switch (message.name) {
|
||||
case "Reader:ParseDocument":
|
||||
this._articlePromise = ReaderMode.parseDocument(content.document).catch(Cu.reportError);
|
||||
content.document.location = "about:reader?url=" + encodeURIComponent(message.data.url);
|
||||
case "Reader:ToggleReaderMode":
|
||||
let url = content.document.location.href;
|
||||
if (!this.isAboutReader) {
|
||||
this._articlePromise = ReaderMode.parseDocument(content.document).catch(Cu.reportError);
|
||||
content.document.location = "about:reader?url=" + encodeURIComponent(url);
|
||||
} else {
|
||||
let originalURL = ReaderMode.getOriginalUrl(url);
|
||||
let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
|
||||
let sh = webNav.sessionHistory;
|
||||
if (webNav.canGoBack) {
|
||||
let prevEntry = sh.getEntryAtIndex(sh.index - 1, false);
|
||||
let prevURL = prevEntry.URI.spec;
|
||||
if (prevURL && (prevURL == originalURL || !originalURL)) {
|
||||
webNav.goBack();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
content.document.location = originalURL;
|
||||
}
|
||||
break;
|
||||
|
||||
case "Reader:PushState":
|
||||
|
@ -13,7 +13,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=575561
|
||||
<a href="http://example.org/browser/browser/base/content/test/general/dummy_page.html" target="foo">different domain (with target)</a>
|
||||
<a href="http://www.example.com/browser/browser/base/content/test/general/dummy_page.html">same domain (www prefix)</a>
|
||||
<a href="data:text/html,<!DOCTYPE html><html><body>Another Page</body></html>">data: URI</a>
|
||||
<a href="about:mozilla">about: URI</a>
|
||||
<iframe src="app_subframe_bug575561.html"></iframe>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -39,7 +39,6 @@ add_task(function*() {
|
||||
isnot(result, null, "Expect a keyword result");
|
||||
|
||||
let types = new Set(result.getAttribute("type").split(/\s+/));
|
||||
Assert.ok(types.has("action"));
|
||||
Assert.ok(types.has("keyword"));
|
||||
is(result.getAttribute("actiontype"), "keyword", "Expect correct `actiontype` attribute");
|
||||
is(result.getAttribute("title"), "example.com", "Expect correct title");
|
||||
|
@ -23,7 +23,7 @@ add_task(function* switchToTab() {
|
||||
|
||||
ok(gURLBar.popup.richlistbox.children.length > 1, "Should get at least 2 results");
|
||||
let result = gURLBar.popup.richlistbox.children[1];
|
||||
is(result.getAttribute("type"), "action switchtab", "Expect right type attribute");
|
||||
is(result.getAttribute("type"), "switchtab", "Expect right type attribute");
|
||||
is(result.label, "about:about about:about Tab", "Result a11y label should be: <title> <url> Tab");
|
||||
|
||||
gURLBar.popup.hidePopup();
|
||||
|
@ -30,7 +30,7 @@ add_task(function*() {
|
||||
},
|
||||
input: "tagtest1",
|
||||
expected: {
|
||||
type: "bookmark-tag",
|
||||
type: "bookmark",
|
||||
typeImageVisible: true,
|
||||
},
|
||||
}, {
|
||||
@ -52,7 +52,7 @@ add_task(function*() {
|
||||
},
|
||||
input: "tagtest3",
|
||||
expected: {
|
||||
type: "bookmark-tag",
|
||||
type: "bookmark",
|
||||
typeImageVisible: true,
|
||||
},
|
||||
}, {
|
||||
@ -63,7 +63,7 @@ add_task(function*() {
|
||||
},
|
||||
input: "* tagtest4",
|
||||
expected: {
|
||||
type: "bookmark-tag",
|
||||
type: "bookmark",
|
||||
typeImageVisible: true,
|
||||
},
|
||||
}, {
|
||||
|
@ -38,7 +38,7 @@ add_task(function* () {
|
||||
is(gBrowser.tabs.length, initialTabsCount, "No additional tabs were opened.");
|
||||
|
||||
readerButton.click();
|
||||
yield promiseTabLoadEvent(tab);
|
||||
yield BrowserTestUtils.waitForContentEvent(tab.linkedBrowser, "pageshow");
|
||||
|
||||
// Ensure no new tabs are opened when exiting reader mode in a pinned tab
|
||||
is(gBrowser.tabs.length, initialTabsCount, "No additional tabs were opened.");
|
||||
|
@ -38,7 +38,13 @@ add_task(function*() {
|
||||
|
||||
// Pinned: Link to an about: URI should not open a new tab
|
||||
// Tests link to about:mozilla
|
||||
yield testLink(6, true, false);
|
||||
yield testLink(function(doc) {
|
||||
let link = doc.createElement("a");
|
||||
link.textContent = "Link to Mozilla";
|
||||
link.href = "about:mozilla";
|
||||
doc.body.appendChild(link);
|
||||
return link;
|
||||
}, true, false, false, "about:robots");
|
||||
});
|
||||
|
||||
var waitForPageLoad = Task.async(function*(browser, linkLocation) {
|
||||
@ -57,8 +63,8 @@ var waitForTabOpen = Task.async(function*() {
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
var testLink = Task.async(function*(aLinkIndex, pinTab, expectNewTab, testSubFrame) {
|
||||
let appTab = gBrowser.addTab(TEST_URL, {skipAnimation: true});
|
||||
var testLink = Task.async(function*(aLinkIndexOrFunction, pinTab, expectNewTab, testSubFrame, aURL = TEST_URL) {
|
||||
let appTab = gBrowser.addTab(aURL, {skipAnimation: true});
|
||||
if (pinTab)
|
||||
gBrowser.pinTab(appTab);
|
||||
gBrowser.selectedTab = appTab;
|
||||
@ -69,7 +75,12 @@ var testLink = Task.async(function*(aLinkIndex, pinTab, expectNewTab, testSubFra
|
||||
if (testSubFrame)
|
||||
browser = browser.contentDocument.querySelector("iframe");
|
||||
|
||||
let link = browser.contentDocument.querySelectorAll("a")[aLinkIndex];
|
||||
let link;
|
||||
if (typeof aLinkIndexOrFunction == "function") {
|
||||
link = aLinkIndexOrFunction(browser.contentDocument);
|
||||
} else {
|
||||
link = browser.contentDocument.querySelectorAll("a")[aLinkIndexOrFunction];
|
||||
}
|
||||
|
||||
let promise;
|
||||
if (expectNewTab)
|
||||
|
@ -4,9 +4,22 @@
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
gBrowser.selectedTab = gBrowser.addTab("data:text/html,<iframe width='700' height='700' src='about:certerror?e=nssBadCert&u='></iframe>");
|
||||
gBrowser.selectedTab = gBrowser.addTab("data:text/html,<iframe width='700' height='700'></iframe>");
|
||||
// Open a html page with about:certerror in an iframe
|
||||
BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(testIframeCert);
|
||||
BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(function() {
|
||||
return ContentTask.spawn(gBrowser.selectedBrowser, "", function() {
|
||||
return new Promise(resolve => {
|
||||
info("Running content task");
|
||||
let listener = e => {
|
||||
removeEventListener('AboutNetErrorLoad', listener, false, true);
|
||||
resolve();
|
||||
};
|
||||
addEventListener('AboutNetErrorLoad', listener, false, true);
|
||||
let iframe = content.document.querySelector("iframe");
|
||||
iframe.src = "https://expired.example.com/";
|
||||
});
|
||||
}).then(testIframeCert);
|
||||
});
|
||||
}
|
||||
|
||||
function testIframeCert(e) {
|
||||
|
@ -71,9 +71,11 @@ add_task(function* test_reader_button() {
|
||||
|
||||
// Switch page back out of reader mode.
|
||||
readerButton.click();
|
||||
yield promiseTabLoadEvent(tab);
|
||||
yield BrowserTestUtils.waitForContentEvent(tab.linkedBrowser, "pageshow");
|
||||
is(gBrowser.selectedBrowser.currentURI.spec, url,
|
||||
"Original page loaded after clicking active reader mode button");
|
||||
"Back to the original page after clicking active reader mode button");
|
||||
ok(gBrowser.selectedBrowser.canGoForward,
|
||||
"Moved one step back in the session history.");
|
||||
|
||||
// Load a new tab that is NOT reader-able.
|
||||
let newTab = gBrowser.selectedTab = gBrowser.addTab();
|
||||
|
@ -40,7 +40,7 @@ add_task(function*() {
|
||||
let result = gURLBar.popup.richlistbox.children[1];
|
||||
|
||||
isnot(result, null, "Expect a search result");
|
||||
is(result.getAttribute("type"), "action searchengine favicon", "Expect correct `type` attribute");
|
||||
is(result.getAttribute("type"), "searchengine", "Expect correct `type` attribute");
|
||||
|
||||
let titleHbox = result._titleText.parentNode.parentNode;
|
||||
ok(titleHbox.classList.contains("ac-title"), "Title hbox sanity check");
|
||||
|
@ -87,11 +87,8 @@ function* checkInput(inputStr) {
|
||||
Assert.equal(item.getAttribute("title"), inputStr.replace("\\","/"), "title");
|
||||
Assert.equal(item.getAttribute("text"), inputStr, "text");
|
||||
|
||||
let itemTypeStr = item.getAttribute("type");
|
||||
let itemTypes = itemTypeStr.split(" ").sort();
|
||||
Assert.equal(itemTypes.toString(),
|
||||
["action", "heuristic", "visiturl"].toString(),
|
||||
"type");
|
||||
let itemType = item.getAttribute("type");
|
||||
Assert.equal(itemType, "visiturl");
|
||||
|
||||
Assert.equal(item._titleText.textContent, inputStr.replace("\\","/"), "Visible title");
|
||||
Assert.equal(item._actionText.textContent, "Visit", "Visible action");
|
||||
|
@ -602,8 +602,6 @@ var FullZoomHelper = {
|
||||
* The tab to load into.
|
||||
* @param [optional] url
|
||||
* The url to load, or the current url.
|
||||
* @param [optional] event
|
||||
* The load event type to wait for. Defaults to "load".
|
||||
* @return {Promise} resolved when the event is handled.
|
||||
* @resolves to the received event
|
||||
* @rejects if a valid load event is not received within a meaningful interval
|
||||
|
@ -34,10 +34,8 @@ struct RedirEntry {
|
||||
URI_SAFE_FOR_UNTRUSTED_CONTENT in the third argument to each map item below
|
||||
unless your about: page really needs chrome privileges. Security review is
|
||||
required before adding new map entries without
|
||||
URI_SAFE_FOR_UNTRUSTED_CONTENT. Also note, however, that adding
|
||||
URI_SAFE_FOR_UNTRUSTED_CONTENT will allow random web sites to link to that
|
||||
URI. If you want to prevent this, add MAKE_UNLINKABLE as well.
|
||||
*/
|
||||
URI_SAFE_FOR_UNTRUSTED_CONTENT.
|
||||
*/
|
||||
static RedirEntry kRedirMap[] = {
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
{ "blocked", "chrome://browser/content/blockedSite.xhtml",
|
||||
@ -81,10 +79,12 @@ static RedirEntry kRedirMap[] = {
|
||||
nsIAboutModule::ALLOW_SCRIPT },
|
||||
{ "sync-tabs", "chrome://browser/content/sync/aboutSyncTabs.xul",
|
||||
nsIAboutModule::ALLOW_SCRIPT },
|
||||
// Linkable because of indexeddb use (bug 1228118)
|
||||
{ "home", "chrome://browser/content/abouthome/aboutHome.xhtml",
|
||||
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
|
||||
nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
|
||||
nsIAboutModule::ALLOW_SCRIPT |
|
||||
nsIAboutModule::MAKE_LINKABLE |
|
||||
nsIAboutModule::ENABLE_INDEXED_DB },
|
||||
// the newtab's actual URL will be determined when the channel is created
|
||||
{ "newtab", "about:blank",
|
||||
@ -99,15 +99,19 @@ static RedirEntry kRedirMap[] = {
|
||||
#endif
|
||||
{ "accounts", "chrome://browser/content/aboutaccounts/aboutaccounts.xhtml",
|
||||
nsIAboutModule::ALLOW_SCRIPT },
|
||||
// Linkable because of indexeddb use (bug 1228118)
|
||||
{ "loopconversation", "chrome://loop/content/panels/conversation.html",
|
||||
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
|
||||
nsIAboutModule::ALLOW_SCRIPT |
|
||||
nsIAboutModule::HIDE_FROM_ABOUTABOUT |
|
||||
nsIAboutModule::MAKE_LINKABLE |
|
||||
nsIAboutModule::ENABLE_INDEXED_DB },
|
||||
// Linkable because of indexeddb use (bug 1228118)
|
||||
{ "looppanel", "chrome://loop/content/panels/panel.html",
|
||||
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
|
||||
nsIAboutModule::ALLOW_SCRIPT |
|
||||
nsIAboutModule::HIDE_FROM_ABOUTABOUT |
|
||||
nsIAboutModule::MAKE_LINKABLE |
|
||||
nsIAboutModule::ENABLE_INDEXED_DB,
|
||||
// Shares an IndexedDB origin with about:loopconversation.
|
||||
"loopconversation" },
|
||||
@ -115,7 +119,6 @@ static RedirEntry kRedirMap[] = {
|
||||
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
|
||||
nsIAboutModule::ALLOW_SCRIPT |
|
||||
nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
|
||||
nsIAboutModule::MAKE_UNLINKABLE |
|
||||
nsIAboutModule::HIDE_FROM_ABOUTABOUT },
|
||||
};
|
||||
static const int kRedirTotal = ArrayLength(kRedirMap);
|
||||
|
@ -74,9 +74,10 @@ CommandList.prototype = {
|
||||
let os = PlatformInfo.os == "win" ? "windows" : PlatformInfo.os;
|
||||
for (let name of Object.keys(manifest.commands)) {
|
||||
let command = manifest.commands[name];
|
||||
let shortcut = command.suggested_key[os] || command.suggested_key.default;
|
||||
commands.set(name, {
|
||||
description: command.description,
|
||||
shortcut: command.suggested_key[os] || command.suggested_key.default,
|
||||
shortcut: shortcut.replace(/\s+/g, ""),
|
||||
});
|
||||
}
|
||||
return commands;
|
||||
|
@ -40,7 +40,7 @@ var gMenuBuilder = {
|
||||
this.xulMenu = xulMenu;
|
||||
for (let [, root] of gRootItems) {
|
||||
let rootElement = this.buildElementWithChildren(root, contextData);
|
||||
if (!rootElement.childNodes.length) {
|
||||
if (!rootElement.firstChild.childNodes.length) {
|
||||
// If the root has no visible children, there is no reason to show
|
||||
// the root menu item itself either.
|
||||
continue;
|
||||
|
@ -907,39 +907,44 @@ global.AllWindowEvents = {
|
||||
}
|
||||
},
|
||||
|
||||
removeListener(type, listener) {
|
||||
if (type == "domwindowopened") {
|
||||
removeListener(eventType, listener) {
|
||||
if (eventType == "domwindowopened") {
|
||||
return WindowListManager.removeOpenListener(listener);
|
||||
} else if (type == "domwindowclosed") {
|
||||
} else if (eventType == "domwindowclosed") {
|
||||
return WindowListManager.removeCloseListener(listener);
|
||||
}
|
||||
|
||||
let listeners = this._listeners.get(type);
|
||||
let listeners = this._listeners.get(eventType);
|
||||
listeners.delete(listener);
|
||||
if (listeners.size == 0) {
|
||||
this._listeners.delete(type);
|
||||
this._listeners.delete(eventType);
|
||||
if (this._listeners.size == 0) {
|
||||
WindowListManager.removeOpenListener(this.openListener);
|
||||
}
|
||||
}
|
||||
|
||||
// Unregister listener from all existing windows.
|
||||
let useCapture = eventType === "focus" || eventType === "blur";
|
||||
for (let window of WindowListManager.browserWindows()) {
|
||||
if (type == "progress") {
|
||||
if (eventType == "progress") {
|
||||
window.gBrowser.removeTabsProgressListener(listener);
|
||||
} else {
|
||||
window.removeEventListener(type, listener);
|
||||
window.removeEventListener(eventType, listener, useCapture);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/* eslint-disable mozilla/balanced-listeners */
|
||||
addWindowListener(window, eventType, listener) {
|
||||
let useCapture = eventType === "focus" || eventType === "blur";
|
||||
|
||||
if (eventType == "progress") {
|
||||
window.gBrowser.addTabsProgressListener(listener);
|
||||
} else {
|
||||
window.addEventListener(eventType, listener);
|
||||
window.addEventListener(eventType, listener, useCapture);
|
||||
}
|
||||
},
|
||||
/* eslint-enable mozilla/balanced-listeners */
|
||||
|
||||
// Runs whenever the "load" event fires for a new window.
|
||||
openListener(window) {
|
||||
|
@ -29,11 +29,16 @@ extensions.registerSchemaAPI("windows", null, (extension, context) => {
|
||||
}).api(),
|
||||
|
||||
onFocusChanged: new EventManager(context, "windows.onFocusChanged", fire => {
|
||||
// FIXME: This will send multiple messages for a single focus change.
|
||||
// Keep track of the last windowId used to fire an onFocusChanged event
|
||||
let lastOnFocusChangedWindowId;
|
||||
|
||||
let listener = event => {
|
||||
let window = WindowManager.topWindow;
|
||||
let windowId = window ? WindowManager.getId(window) : WindowManager.WINDOW_ID_NONE;
|
||||
fire(windowId);
|
||||
if (windowId !== lastOnFocusChangedWindowId) {
|
||||
fire(windowId);
|
||||
lastOnFocusChangedWindowId = windowId;
|
||||
}
|
||||
};
|
||||
AllWindowEvents.addListener("focus", listener);
|
||||
AllWindowEvents.addListener("blur", listener);
|
||||
|
@ -11,7 +11,7 @@
|
||||
"choices": [
|
||||
{
|
||||
"type": "string",
|
||||
"pattern": "^(Alt|Ctrl|Command|MacCtr)\\+(Shift\\+)?([A-Z0-9]|Comma|Period|Home|End|PageUp|PageDown|Space|Insert|Delete|Up|Down|Left|Right)$"
|
||||
"pattern": "^\\s*(Alt|Ctrl|Command|MacCtr)\\s*\\+\\s*(Shift\\s*\\+\\s*)?([A-Z0-9]|Comma|Period|Home|End|PageUp|PageDown|Space|Insert|Delete|Up|Down|Left|Right)\\s*$"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -12,6 +12,7 @@ support-files =
|
||||
file_bypass_cache.sjs
|
||||
file_language_fr_en.html
|
||||
file_language_ja.html
|
||||
file_dummy.html
|
||||
|
||||
[browser_ext_browserAction_context.js]
|
||||
[browser_ext_browserAction_disabled.js]
|
||||
|
@ -22,11 +22,16 @@ add_task(function* test_user_defined_commands() {
|
||||
},
|
||||
"unrecognized_property": "with-a-random-value",
|
||||
},
|
||||
"toggle-feature-with-whitespace-in-suggested-key": {
|
||||
"suggested_key": {
|
||||
"default": " Alt + Shift + 2 ",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
background: function() {
|
||||
browser.commands.onCommand.addListener((commandName) => {
|
||||
browser.commands.onCommand.addListener(commandName => {
|
||||
browser.test.sendMessage("oncommand", commandName);
|
||||
});
|
||||
browser.test.sendMessage("ready");
|
||||
@ -53,11 +58,11 @@ add_task(function* test_user_defined_commands() {
|
||||
let keysetID = `ext-keyset-id-${makeWidgetId(extension.id)}`;
|
||||
let keyset = win1.document.getElementById(keysetID);
|
||||
ok(keyset != null, "Expected keyset to exist");
|
||||
is(keyset.childNodes.length, 2, "Expected keyset to have 2 children");
|
||||
is(keyset.childNodes.length, 3, "Expected keyset to have 3 children");
|
||||
|
||||
keyset = win2.document.getElementById(keysetID);
|
||||
ok(keyset != null, "Expected keyset to exist");
|
||||
is(keyset.childNodes.length, 2, "Expected keyset to have 2 children");
|
||||
is(keyset.childNodes.length, 3, "Expected keyset to have 3 children");
|
||||
|
||||
// Confirm that the commands are registered to both windows.
|
||||
yield focusWindow(win1);
|
||||
@ -70,6 +75,10 @@ add_task(function* test_user_defined_commands() {
|
||||
message = yield extension.awaitMessage("oncommand");
|
||||
is(message, "toggle-feature-using-alt-shift-comma", "Expected onCommand listener to fire with correct message");
|
||||
|
||||
EventUtils.synthesizeKey("2", {altKey: true, shiftKey: true});
|
||||
message = yield extension.awaitMessage("oncommand");
|
||||
is(message, "toggle-feature-with-whitespace-in-suggested-key", "Expected onCommand listener to fire with correct message");
|
||||
|
||||
yield extension.unload();
|
||||
|
||||
// Confirm that the keysets have been removed from both windows after the extension is unloaded.
|
||||
|
@ -2,6 +2,67 @@
|
||||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
"use strict";
|
||||
|
||||
|
||||
add_task(function* () {
|
||||
let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser,
|
||||
"http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html");
|
||||
|
||||
gBrowser.selectedTab = tab1;
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
"permissions": ["contextMenus"],
|
||||
},
|
||||
|
||||
background: function() {
|
||||
browser.contextMenus.create({
|
||||
id: "clickme",
|
||||
title: "Click me!",
|
||||
contexts: ["image"],
|
||||
});
|
||||
browser.test.notifyPass();
|
||||
},
|
||||
});
|
||||
|
||||
yield extension.startup();
|
||||
yield extension.awaitFinish();
|
||||
|
||||
let contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
|
||||
let popupShownPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popupshown");
|
||||
yield BrowserTestUtils.synthesizeMouseAtCenter("#img1", {
|
||||
type: "contextmenu",
|
||||
button: 2,
|
||||
}, gBrowser.selectedBrowser);
|
||||
yield popupShownPromise;
|
||||
|
||||
let item = contentAreaContextMenu.getElementsByAttribute("label", "Click me!");
|
||||
is(item.length, 1, "contextMenu item for image was found");
|
||||
|
||||
let popupHiddenPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popuphidden");
|
||||
EventUtils.synthesizeMouseAtCenter(item[0], {});
|
||||
yield popupHiddenPromise;
|
||||
|
||||
contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
|
||||
popupShownPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popupshown");
|
||||
yield BrowserTestUtils.synthesizeMouseAtCenter("body", {
|
||||
type: "contextmenu",
|
||||
button: 2,
|
||||
}, gBrowser.selectedBrowser);
|
||||
yield popupShownPromise;
|
||||
|
||||
item = contentAreaContextMenu.getElementsByAttribute("label", "Click me!");
|
||||
is(item.length, 0, "no contextMenu item for image was found");
|
||||
|
||||
// click something to close the context menu
|
||||
popupHiddenPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popuphidden");
|
||||
EventUtils.synthesizeMouseAtCenter(document.getElementById("context-selectall"), {});
|
||||
yield popupHiddenPromise;
|
||||
|
||||
yield extension.unload();
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab1);
|
||||
});
|
||||
|
||||
/* globals content */
|
||||
/* eslint-disable mozilla/no-cpows-in-tests */
|
||||
add_task(function* () {
|
||||
|
@ -119,7 +119,8 @@ add_task(function* tabsSendMessageNoExceptionOnNonExistentTab() {
|
||||
},
|
||||
|
||||
background: function() {
|
||||
browser.tabs.create({url: "about:robots"}, tab => {
|
||||
let url = "http://example.com/mochitest/browser/browser/components/extensions/test/browser/file_dummy.html";
|
||||
browser.tabs.create({url}, tab => {
|
||||
let exception;
|
||||
try {
|
||||
browser.tabs.sendMessage(tab.id, "message");
|
||||
|
@ -5,7 +5,6 @@
|
||||
add_task(function* testWindowsEvents() {
|
||||
function background() {
|
||||
browser.windows.onCreated.addListener(function listener(window) {
|
||||
browser.windows.onCreated.removeListener(listener);
|
||||
browser.test.assertTrue(Number.isInteger(window.id),
|
||||
"Window object's id is an integer");
|
||||
browser.test.assertEq("normal", window.type,
|
||||
@ -13,8 +12,21 @@ add_task(function* testWindowsEvents() {
|
||||
browser.test.sendMessage("window-created", window.id);
|
||||
});
|
||||
|
||||
let lastWindowId;
|
||||
browser.windows.onFocusChanged.addListener(function listener(windowId) {
|
||||
browser.test.assertTrue(lastWindowId !== windowId,
|
||||
"onFocusChanged fired once for the given window");
|
||||
lastWindowId = windowId;
|
||||
browser.test.assertTrue(Number.isInteger(windowId),
|
||||
"windowId is an integer");
|
||||
browser.windows.getLastFocused().then(window => {
|
||||
browser.test.assertEq(windowId, window.id,
|
||||
"Last focused window has the correct id");
|
||||
browser.test.sendMessage(`window-focus-changed-${windowId}`);
|
||||
});
|
||||
});
|
||||
|
||||
browser.windows.onRemoved.addListener(function listener(windowId) {
|
||||
browser.windows.onRemoved.removeListener(listener);
|
||||
browser.test.assertTrue(Number.isInteger(windowId),
|
||||
"windowId is an integer");
|
||||
browser.test.sendMessage(`window-removed-${windowId}`);
|
||||
@ -30,10 +42,29 @@ add_task(function* testWindowsEvents() {
|
||||
|
||||
yield extension.startup();
|
||||
yield extension.awaitMessage("ready");
|
||||
|
||||
let {WindowManager} = Cu.import("resource://gre/modules/Extension.jsm", {});
|
||||
let currentWindow = window;
|
||||
let currentWindowId = WindowManager.getId(currentWindow);
|
||||
|
||||
let win1 = yield BrowserTestUtils.openNewBrowserWindow();
|
||||
let windowId = yield extension.awaitMessage("window-created");
|
||||
let win1Id = yield extension.awaitMessage("window-created");
|
||||
yield extension.awaitMessage(`window-focus-changed-${win1Id}`);
|
||||
|
||||
let win2 = yield BrowserTestUtils.openNewBrowserWindow();
|
||||
let win2Id = yield extension.awaitMessage("window-created");
|
||||
yield extension.awaitMessage(`window-focus-changed-${win2Id}`);
|
||||
|
||||
yield focusWindow(win1);
|
||||
yield extension.awaitMessage(`window-focus-changed-${win1Id}`);
|
||||
|
||||
yield BrowserTestUtils.closeWindow(win2);
|
||||
yield extension.awaitMessage(`window-removed-${win2Id}`);
|
||||
|
||||
yield BrowserTestUtils.closeWindow(win1);
|
||||
yield extension.awaitMessage(`window-removed-${windowId}`);
|
||||
yield extension.awaitMessage(`window-removed-${win1Id}`);
|
||||
|
||||
yield extension.awaitMessage(`window-focus-changed-${currentWindowId}`);
|
||||
yield extension.awaitFinish("windows.events");
|
||||
yield extension.unload();
|
||||
});
|
||||
|
@ -0,0 +1,9 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Dummy test page</title>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"></meta>
|
||||
</head>
|
||||
<body>
|
||||
<p>Dummy test page</p>
|
||||
</body>
|
||||
</html>
|
@ -9,14 +9,14 @@
|
||||
*/
|
||||
add_task(function () {
|
||||
// This URL has the following frames:
|
||||
// + about:mozilla (static)
|
||||
// + about:robots (static)
|
||||
// + about:rights (dynamic iframe)
|
||||
// + data:text/html,A (static)
|
||||
// + data:text/html,B (static)
|
||||
// + data:text/html,C (dynamic iframe)
|
||||
const URL = "data:text/html;charset=utf-8," +
|
||||
"<frameset cols=50%25,50%25><frame src=about%3Amozilla>" +
|
||||
"<frame src=about%3Arobots></frameset>" +
|
||||
"<frameset cols=50%25,50%25><frame src='data:text/html,A'>" +
|
||||
"<frame src='data:text/html,B'></frameset>" +
|
||||
"<script>var i=document.createElement('iframe');" +
|
||||
"i.setAttribute('src', 'about%3Arights');" +
|
||||
"i.setAttribute('src', 'data:text/html,C');" +
|
||||
"document.body.appendChild(i);</script>";
|
||||
|
||||
// Add a new tab with two "static" and one "dynamic" frame.
|
||||
@ -29,8 +29,8 @@ add_task(function () {
|
||||
|
||||
// Check URLs.
|
||||
ok(entries[0].url.startsWith("data:text/html"), "correct root url");
|
||||
is(entries[0].children[0].url, "about:mozilla", "correct url for 1st frame");
|
||||
is(entries[0].children[1].url, "about:robots", "correct url for 2nd frame");
|
||||
is(entries[0].children[0].url, "data:text/html,A", "correct url for 1st frame");
|
||||
is(entries[0].children[1].url, "data:text/html,B", "correct url for 2nd frame");
|
||||
|
||||
// Check the number of children.
|
||||
is(entries.length, 1, "there is one root entry ...");
|
||||
@ -47,13 +47,13 @@ add_task(function () {
|
||||
*/
|
||||
add_task(function () {
|
||||
// This URL has the following frames:
|
||||
// + about:mozilla (static iframe)
|
||||
// + about:rights (dynamic iframe)
|
||||
// + data:text/html,A (static)
|
||||
// + data:text/html,C (dynamic iframe)
|
||||
const URL = "data:text/html;charset=utf-8," +
|
||||
"<iframe name=t src=about%3Amozilla></iframe>" +
|
||||
"<a id=lnk href=about%3Arobots target=t>clickme</a>" +
|
||||
"<iframe name=t src='data:text/html,A'></iframe>" +
|
||||
"<a id=lnk href='data:text/html,B' target=t>clickme</a>" +
|
||||
"<script>var i=document.createElement('iframe');" +
|
||||
"i.setAttribute('src', 'about%3Arights');" +
|
||||
"i.setAttribute('src', 'data:text/html,C');" +
|
||||
"document.body.appendChild(i);</script>";
|
||||
|
||||
// Add a new tab with one "static" and one "dynamic" frame.
|
||||
|
296
browser/extensions/loop/bootstrap.js
vendored
296
browser/extensions/loop/bootstrap.js
vendored
@ -55,7 +55,7 @@ var WindowListener = {
|
||||
*/
|
||||
setupBrowserUI: function(window) {
|
||||
let document = window.document;
|
||||
let gBrowser = window.gBrowser;
|
||||
let { gBrowser, gURLBar } = window;
|
||||
let xhrClass = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"];
|
||||
let FileReader = window.FileReader;
|
||||
let menuItem = null;
|
||||
@ -176,6 +176,15 @@ var WindowListener = {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when a closing room has just been created, so we offer the
|
||||
* user the chance to modify the name. For that we need to open the panel.
|
||||
* Showing the proper layout is done on panel.jsx
|
||||
*/
|
||||
renameRoom: function() {
|
||||
this.openPanel();
|
||||
},
|
||||
|
||||
/**
|
||||
* Opens the panel for Loop and sizes it appropriately.
|
||||
*
|
||||
@ -332,6 +341,7 @@ var WindowListener = {
|
||||
|
||||
Services.obs.addObserver(this, "loop-status-changed", false);
|
||||
|
||||
this.maybeAddCopyPanel();
|
||||
this.updateToolbarState();
|
||||
},
|
||||
|
||||
@ -364,6 +374,113 @@ var WindowListener = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Maybe add the copy panel if it's not throttled and passes other checks.
|
||||
* @return {Promise} Resolved when decided and maybe panel-added.
|
||||
*/
|
||||
maybeAddCopyPanel() {
|
||||
// Don't bother adding the copy panel if we're in private browsing or
|
||||
// we've already shown it.
|
||||
if (PrivateBrowsingUtils.isWindowPrivate(window) ||
|
||||
Services.prefs.getBoolPref("loop.copy.shown")) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return Throttler.check("loop.copy").then(() => this.addCopyPanel());
|
||||
},
|
||||
|
||||
/**
|
||||
* Hook into the location bar copy command to open up the copy panel.
|
||||
* @param {Function} onClickHandled Optional callback for finished clicks.
|
||||
*/
|
||||
addCopyPanel(onClickHandled) {
|
||||
// Make a copy of the loop panel as a starting point for the copy panel.
|
||||
let copy = this.panel.cloneNode(false);
|
||||
copy.id = "loop-copy-notification-panel";
|
||||
this.panel.parentNode.appendChild(copy);
|
||||
|
||||
// Record a telemetry copy panel action.
|
||||
let addTelemetry = bucket => {
|
||||
this.LoopAPI.sendMessageToHandler({
|
||||
data: ["LOOP_COPY_PANEL_ACTIONS", this.constants.COPY_PANEL[bucket]],
|
||||
name: "TelemetryAddValue"
|
||||
});
|
||||
};
|
||||
|
||||
// Handle events from the copy panel iframe content.
|
||||
let onIframe = iframe => {
|
||||
// Watch for events from the copy panel when loaded.
|
||||
iframe.addEventListener("DOMContentLoaded", function onLoad() {
|
||||
iframe.removeEventListener("DOMContentLoaded", onLoad);
|
||||
|
||||
// Size the panel to fit the rendered content adjusting for borders.
|
||||
iframe.contentWindow.requestAnimationFrame(() => {
|
||||
let height = iframe.contentDocument.documentElement.offsetHeight;
|
||||
height += copy.boxObject.height - iframe.boxObject.height;
|
||||
copy.style.height = height + "px";
|
||||
});
|
||||
|
||||
// Hide the copy panel then show the loop panel.
|
||||
iframe.contentWindow.addEventListener("CopyPanelClick", event => {
|
||||
iframe.parentNode.hidePopup();
|
||||
|
||||
// Show the Loop panel if the user wants it.
|
||||
let { accept, stop } = event.detail;
|
||||
if (accept) {
|
||||
LoopUI.openPanel();
|
||||
}
|
||||
|
||||
// Stop showing the panel if the user says so.
|
||||
if (stop) {
|
||||
LoopUI.removeCopyPanel();
|
||||
Services.prefs.setBoolPref("loop.copy.shown", true);
|
||||
}
|
||||
|
||||
// Generate the appropriate NO_AGAIN, NO_NEVER, YES_AGAIN,
|
||||
// YES_NEVER probe based on the user's action.
|
||||
let probe = (accept ? "YES" : "NO") + "_" + (stop ? "NEVER" : "AGAIN");
|
||||
addTelemetry(probe);
|
||||
|
||||
// For testing, indicate that handling the click has finished.
|
||||
try {
|
||||
onClickHandled(event.detail);
|
||||
} catch (ex) {
|
||||
// Do nothing.
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Override the default behavior of the copy command.
|
||||
let controller = gURLBar._copyCutController;
|
||||
controller._doCommand = controller.doCommand;
|
||||
controller.doCommand = () => {
|
||||
// Do the normal behavior first.
|
||||
controller._doCommand.apply(controller, arguments);
|
||||
|
||||
// Open up the copy panel at the loop button.
|
||||
addTelemetry("SHOWN");
|
||||
LoopUI.PanelFrame.showPopup(window, LoopUI.toolbarButton.anchor, "loop-copy",
|
||||
null, "chrome://loop/content/panels/copy.html", null, onIframe);
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes the copy panel copy hook and the panel.
|
||||
*/
|
||||
removeCopyPanel() {
|
||||
let controller = gURLBar && gURLBar._copyCutController;
|
||||
if (controller && controller._doCommand) {
|
||||
controller.doCommand = controller._doCommand;
|
||||
delete controller._doCommand;
|
||||
}
|
||||
|
||||
let copy = document.getElementById("loop-copy-notification-panel");
|
||||
if (copy) {
|
||||
copy.parentNode.removeChild(copy);
|
||||
}
|
||||
},
|
||||
|
||||
// Implements nsIObserver
|
||||
observe: function(subject, topic, data) {
|
||||
if (topic != "loop-status-changed") {
|
||||
@ -519,23 +636,29 @@ var WindowListener = {
|
||||
this.activeSound.load();
|
||||
this.activeSound.play();
|
||||
|
||||
this.activeSound.addEventListener("ended", () => this.activeSound = undefined, false);
|
||||
this.activeSound.addEventListener("ended", () => { this.activeSound = undefined; }, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Start listening to selected tab changes and notify any content page that's
|
||||
* listening to 'BrowserSwitch' push messages.
|
||||
* listening to 'BrowserSwitch' push messages. Also sets up a "joined"
|
||||
* and "left" listener for LoopRooms so that we can toggle the infobar
|
||||
* sharing messages when people come and go.
|
||||
*
|
||||
* Push message parameters:
|
||||
* - {Integer} windowId The new windowId for the browser.
|
||||
* @param {(String)} roomToken The current room that the link generator is connecting to.
|
||||
*/
|
||||
startBrowserSharing: function() {
|
||||
startBrowserSharing: function(roomToken) {
|
||||
if (!this._listeningToTabSelect) {
|
||||
gBrowser.tabContainer.addEventListener("TabSelect", this);
|
||||
this._listeningToTabSelect = true;
|
||||
|
||||
titleChangedListener = this.handleDOMTitleChanged.bind(this);
|
||||
|
||||
this._roomsListener = this.handleRoomJoinedOrLeft.bind(this);
|
||||
|
||||
this.LoopRooms.on("joined", this._roomsListener);
|
||||
this.LoopRooms.on("left", this._roomsListener);
|
||||
|
||||
// Watch for title changes as opposed to location changes as more
|
||||
// metadata about the page is available when this event fires.
|
||||
this.mm.addMessageListener("loop@mozilla.org:DOMTitleChanged",
|
||||
@ -549,7 +672,8 @@ var WindowListener = {
|
||||
gBrowser.addEventListener("click", this);
|
||||
}
|
||||
|
||||
this._maybeShowBrowserSharingInfoBar();
|
||||
this._currentRoomToken = roomToken;
|
||||
this._maybeShowBrowserSharingInfoBar(roomToken);
|
||||
|
||||
// Get the first window Id for the listener.
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
@ -577,6 +701,8 @@ var WindowListener = {
|
||||
|
||||
this._hideBrowserSharingInfoBar();
|
||||
gBrowser.tabContainer.removeEventListener("TabSelect", this);
|
||||
this.LoopRooms.off("joined", this._roomsListener);
|
||||
this.LoopRooms.off("left", this._roomsListener);
|
||||
|
||||
if (titleChangedListener) {
|
||||
this.mm.removeMessageListener("loop@mozilla.org:DOMTitleChanged",
|
||||
@ -591,6 +717,8 @@ var WindowListener = {
|
||||
|
||||
this._listeningToTabSelect = false;
|
||||
this._browserSharePaused = false;
|
||||
this._currentRoomToken = null;
|
||||
|
||||
this._sendTelemetryEventsIfNeeded();
|
||||
},
|
||||
|
||||
@ -719,27 +847,53 @@ var WindowListener = {
|
||||
return str;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set correct strings for infobar notification based on if paused or empty.
|
||||
*/
|
||||
|
||||
_setInfoBarStrings: function(nonOwnerParticipants, sharePaused) {
|
||||
let message;
|
||||
if (nonOwnerParticipants) {
|
||||
// More than just the owner in the room.
|
||||
message = this._getString(
|
||||
sharePaused ? "infobar_screenshare_stop_sharing_message2" :
|
||||
"infobar_screenshare_browser_message3");
|
||||
|
||||
} else {
|
||||
// Just the owner in the room.
|
||||
message = this._getString(
|
||||
sharePaused ? "infobar_screenshare_stop_no_guest_message" :
|
||||
"infobar_screenshare_no_guest_message");
|
||||
}
|
||||
let label = this._getString(
|
||||
sharePaused ? "infobar_button_restart_label2" : "infobar_button_stop_label2");
|
||||
let accessKey = this._getString(
|
||||
sharePaused ? "infobar_button_restart_accesskey" : "infobar_button_stop_accesskey");
|
||||
|
||||
return { message: message, label: label, accesskey: accessKey };
|
||||
},
|
||||
|
||||
/**
|
||||
* Indicates if tab sharing is paused.
|
||||
* Set by tab pause button, startBrowserSharing and stopBrowserSharing.
|
||||
* Defaults to false as link generator(owner) enters room we are sharing tabs.
|
||||
*/
|
||||
_browserSharePaused: false,
|
||||
|
||||
/**
|
||||
* Shows an infobar notification at the top of the browser window that warns
|
||||
* the user that their browser tabs are being broadcasted through the current
|
||||
* conversation.
|
||||
* @param {String} currentRoomToken Room we are currently joined.
|
||||
* @return {void}
|
||||
*/
|
||||
_maybeShowBrowserSharingInfoBar: function() {
|
||||
// Pre-load strings
|
||||
let pausedStrings = {
|
||||
label: this._getString("infobar_button_restart_label2"),
|
||||
accesskey: this._getString("infobar_button_restart_accesskey"),
|
||||
message: this._getString("infobar_screenshare_stop_sharing_message")
|
||||
};
|
||||
let unpausedStrings = {
|
||||
label: this._getString("infobar_button_stop_label2"),
|
||||
accesskey: this._getString("infobar_button_stop_accesskey"),
|
||||
message: this._getString("infobar_screenshare_browser_message2")
|
||||
};
|
||||
let initStrings =
|
||||
this._browserSharePaused ? pausedStrings : unpausedStrings;
|
||||
|
||||
_maybeShowBrowserSharingInfoBar: function(currentRoomToken) {
|
||||
this._hideBrowserSharingInfoBar();
|
||||
|
||||
let participantsCount = this.LoopRooms.getNumParticipants(currentRoomToken);
|
||||
|
||||
let initStrings = this._setInfoBarStrings(participantsCount > 1, this._browserSharePaused);
|
||||
|
||||
let box = gBrowser.getNotificationBox();
|
||||
let bar = box.appendNotification(
|
||||
initStrings.message, // label
|
||||
@ -749,11 +903,12 @@ var WindowListener = {
|
||||
box.PRIORITY_WARNING_LOW, // priority
|
||||
[{ // buttons (Pause, Stop)
|
||||
label: initStrings.label,
|
||||
accessKey: initStrings.accessKey,
|
||||
accessKey: initStrings.accesskey,
|
||||
isDefault: false,
|
||||
callback: (event, buttonInfo, buttonNode) => {
|
||||
this._browserSharePaused = !this._browserSharePaused;
|
||||
let stringObj = this._browserSharePaused ? pausedStrings : unpausedStrings;
|
||||
let guestPresent = this.LoopRooms.getNumParticipants(this._currentRoomToken) > 1;
|
||||
let stringObj = this._setInfoBarStrings(guestPresent, this._browserSharePaused);
|
||||
bar.label = stringObj.message;
|
||||
bar.classList.toggle("paused", this._browserSharePaused);
|
||||
buttonNode.label = stringObj.label;
|
||||
@ -820,6 +975,18 @@ var WindowListener = {
|
||||
gBrowser.selectedBrowser.outerWindowID);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles updating of the sharing infobar when the room participants
|
||||
* change.
|
||||
*/
|
||||
handleRoomJoinedOrLeft: function() {
|
||||
// Don't attempt to show it if we're not actively sharing.
|
||||
if (!this._listeningToTabSelect) {
|
||||
return;
|
||||
}
|
||||
this._maybeShowBrowserSharingInfoBar(this._currentRoomToken);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles events from the frame script.
|
||||
*
|
||||
@ -840,8 +1007,9 @@ var WindowListener = {
|
||||
* Handles events from gBrowser.
|
||||
*/
|
||||
handleEvent: function(event) {
|
||||
|
||||
switch (event.type) {
|
||||
case "TabSelect":
|
||||
case "TabSelect": {
|
||||
let wasVisible = false;
|
||||
// Hide the infobar from the previous tab.
|
||||
if (event.detail.previousTab) {
|
||||
@ -857,9 +1025,10 @@ var WindowListener = {
|
||||
if (wasVisible) {
|
||||
// If the infobar was visible before, we should show it again after the
|
||||
// switch.
|
||||
this._maybeShowBrowserSharingInfoBar();
|
||||
this._maybeShowBrowserSharingInfoBar(this._currentRoomToken);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "mousemove":
|
||||
this.handleMousemove(event);
|
||||
break;
|
||||
@ -971,6 +1140,9 @@ var WindowListener = {
|
||||
|
||||
LoopUI.init();
|
||||
window.LoopUI = LoopUI;
|
||||
|
||||
// Export the Throttler to allow tests to overwrite parts of it.
|
||||
window.LoopThrottler = Throttler;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -981,6 +1153,7 @@ var WindowListener = {
|
||||
*/
|
||||
tearDownBrowserUI: function(window) {
|
||||
if (window.LoopUI) {
|
||||
window.LoopUI.removeCopyPanel();
|
||||
window.LoopUI.removeMenuItem();
|
||||
|
||||
// This stops the frame script being loaded to new tabs, but doesn't
|
||||
@ -1015,6 +1188,77 @@ var WindowListener = {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Provide a way to throttle functionality using DNS to distribute 3 numbers for
|
||||
* various distributions channels. DNS is used to scale distribution of the
|
||||
* numbers as an A record pointing to a loopback address (127.*.*.*). Prefs are
|
||||
* used to control behavior (what domain to check) and keep state (a ticket
|
||||
* number to track if it needs to initialize, to wait for its turn, or is
|
||||
* completed).
|
||||
*/
|
||||
let Throttler = {
|
||||
// Each 8-bit block of the IP address allows for 0% rollout (value 0) to 100%
|
||||
// rollout (value 255).
|
||||
TICKET_LIMIT: 255,
|
||||
|
||||
// Allow the DNS service to be overwritten for testing.
|
||||
_dns: Cc["@mozilla.org/network/dns-service;1"].getService(Ci.nsIDNSService),
|
||||
|
||||
/**
|
||||
* Check if a given feature should be throttled or not.
|
||||
* @param {string} [prefPrefix] Start of the preference name for the feature.
|
||||
* @return {Promise} Resolved on success, and rejected on throttled.
|
||||
*/
|
||||
check(prefPrefix) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Initialize the ticket (0-254) if it doesn't have a valid value yet.
|
||||
let prefTicket = prefPrefix + ".ticket";
|
||||
let ticket = Services.prefs.getIntPref(prefTicket);
|
||||
if (ticket < 0) {
|
||||
ticket = Math.floor(Math.random() * this.TICKET_LIMIT);
|
||||
Services.prefs.setIntPref(prefTicket, ticket);
|
||||
}
|
||||
// Short circuit if the special ticket value indicates we're good to go.
|
||||
else if (ticket >= this.TICKET_LIMIT) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle responses from the DNS resolution service request.
|
||||
let onDNS = (request, record) => {
|
||||
// Use a specific part of the A-record IP address depending on the
|
||||
// channel. I.e., 127.[release/other].[beta].[aurora/nightly].
|
||||
let index = 1;
|
||||
switch (Services.prefs.getCharPref("app.update.channel")) {
|
||||
case "beta":
|
||||
index = 2;
|
||||
break;
|
||||
case "aurora":
|
||||
case "nightly":
|
||||
index = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
// Select the 1 out of 4 parts of the "."-separated IP address to check
|
||||
// if the 8-bit threshold (0-255) exceeds the ticket (0-254).
|
||||
let threshold = record && record.getNextAddrAsString().split(".")[index];
|
||||
if (threshold && ticket < threshold) {
|
||||
// Remember that we're good to go to avoid future DNS checks.
|
||||
Services.prefs.setIntPref(prefTicket, this.TICKET_LIMIT);
|
||||
resolve();
|
||||
}
|
||||
else {
|
||||
reject();
|
||||
}
|
||||
};
|
||||
|
||||
// Look up the DNS A-record of a throttler hostname to decide to show.
|
||||
this._dns.asyncResolve(Services.prefs.getCharPref(prefPrefix + ".throttler"),
|
||||
this._dns.RESOLVE_DISABLE_IPV6, onDNS, Services.tm.mainThread);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates the loop button on the toolbar. Due to loop being a system-addon
|
||||
* CustomizableUI already has a placement location for the button, so that
|
||||
|
@ -567,11 +567,11 @@ var LoopRoomsInternal = {
|
||||
eventEmitter.emit("delete", room);
|
||||
eventEmitter.emit("delete:" + room.roomToken, room);
|
||||
} else {
|
||||
yield this.addOrUpdateRoom(room, !!orig);
|
||||
|
||||
if (orig) {
|
||||
checkForParticipantsUpdate(orig, room);
|
||||
}
|
||||
|
||||
yield this.addOrUpdateRoom(room, !!orig);
|
||||
}
|
||||
}
|
||||
|
||||
@ -594,6 +594,27 @@ var LoopRoomsInternal = {
|
||||
return gGetAllPromise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Request information about number of participants joined to a specific room from the server.
|
||||
* Used to determine infobar message state on the number of participants in the room.
|
||||
*
|
||||
* @param {String} roomToken Room identifier
|
||||
* @return {Number} Count of participants in the room.
|
||||
*/
|
||||
getNumParticipants: function(roomToken) {
|
||||
try {
|
||||
if (this.rooms && this.rooms.has(roomToken)) {
|
||||
return this.rooms.get(roomToken).participants.length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
catch (ex) {
|
||||
// No room, log error and send back 0 to indicate none in room.
|
||||
MozLoopService.log.error("No room found in current session: ", ex);
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Request information about a specific room from the server. It will be
|
||||
* returned from the cache if it's already in it.
|
||||
@ -630,10 +651,9 @@ var LoopRoomsInternal = {
|
||||
eventEmitter.emit("delete", room);
|
||||
eventEmitter.emit("delete:" + room.roomToken, room);
|
||||
} else {
|
||||
checkForParticipantsUpdate(room, data);
|
||||
extend(room, data);
|
||||
yield this.addOrUpdateRoom(data, !needsUpdate);
|
||||
|
||||
yield this.addOrUpdateRoom(room, !needsUpdate);
|
||||
checkForParticipantsUpdate(room, data);
|
||||
}
|
||||
callback(null, room);
|
||||
}.bind(this)).catch(callback);
|
||||
@ -1173,6 +1193,10 @@ this.LoopRooms = {
|
||||
return LoopRoomsInternal.maybeRefresh(user);
|
||||
},
|
||||
|
||||
getNumParticipants: function(roomToken) {
|
||||
return LoopRoomsInternal.getNumParticipants(roomToken);
|
||||
},
|
||||
|
||||
/**
|
||||
* This method is only useful for unit tests to set the rooms cache to contain
|
||||
* a list of fake room data that can be asserted in tests.
|
||||
@ -1223,14 +1247,18 @@ this.LoopRooms = {
|
||||
* @param {Map} roomsCache The new cache data to set for testing purposes. If
|
||||
* not specified, it will reset the cache.
|
||||
*/
|
||||
_setRoomsCache: function(roomsCache) {
|
||||
_setRoomsCache: function(roomsCache, orig) {
|
||||
LoopRoomsInternal.rooms.clear();
|
||||
gDirty = true;
|
||||
|
||||
if (roomsCache) {
|
||||
// Need a clone as the internal map is read-only.
|
||||
for (let [key, value] of roomsCache) {
|
||||
|
||||
LoopRoomsInternal.rooms.set(key, value);
|
||||
if (orig) {
|
||||
checkForParticipantsUpdate(orig, value);
|
||||
}
|
||||
}
|
||||
gGetAllPromise = null;
|
||||
gDirty = false;
|
||||
|
@ -154,5 +154,7 @@ LoopRoomsCache.prototype = {
|
||||
cache[sessionType][roomToken].key = roomKey;
|
||||
return yield this._setCache(cache);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
})
|
||||
};
|
||||
|
@ -124,6 +124,16 @@ const updateSocialProvidersCache = function() {
|
||||
return gSocialProviders;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks that [browser.js]'s global variable `gMultiProcessBrowser` is active,
|
||||
* instead of checking on first available browser element.
|
||||
* :see bug 1257243 comment 5:
|
||||
*/
|
||||
const isMultiProcessActive = function() {
|
||||
let win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
return !!win.gMultiProcessBrowser;
|
||||
};
|
||||
|
||||
var gAppVersionInfo = null;
|
||||
var gBrowserSharingListeners = new Set();
|
||||
var gBrowserSharingWindows = new Set();
|
||||
@ -160,7 +170,7 @@ const kMessageHandlers = {
|
||||
* @param {Object} message Message meant for the handler function, containing
|
||||
* the following parameters in its `data` property:
|
||||
* [
|
||||
* {Number} windowId The window ID of the chat window
|
||||
* {String} roomToken The room ID to start browser sharing and listeners.
|
||||
* ]
|
||||
* @param {Function} reply Callback function, invoked with the result of this
|
||||
* message handler. The result will be sent back to
|
||||
@ -187,9 +197,11 @@ const kMessageHandlers = {
|
||||
return;
|
||||
}
|
||||
|
||||
// get room token from message
|
||||
let [windowId] = message.data;
|
||||
|
||||
win.LoopUI.startBrowserSharing();
|
||||
// For rooms, the windowId === roomToken. If we change the type of place we're
|
||||
// sharing from in the future, we may need to change this.
|
||||
win.LoopUI.startBrowserSharing(windowId);
|
||||
|
||||
// Point new tab to load about:home to avoid accidentally sharing top sites.
|
||||
NewTabURL.override("about:home");
|
||||
@ -422,6 +434,7 @@ const kMessageHandlers = {
|
||||
*/
|
||||
GetAllConstants: function(message, reply) {
|
||||
reply({
|
||||
COPY_PANEL: COPY_PANEL,
|
||||
LOOP_SESSION_TYPE: LOOP_SESSION_TYPE,
|
||||
LOOP_MAU_TYPE: LOOP_MAU_TYPE,
|
||||
ROOM_CREATE: ROOM_CREATE,
|
||||
@ -638,8 +651,23 @@ const kMessageHandlers = {
|
||||
*/
|
||||
GetSelectedTabMetadata: function(message, reply) {
|
||||
let win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
win.messageManager.addMessageListener("PageMetadata:PageDataResult", function onPageDataResult(msg) {
|
||||
win.messageManager.removeMessageListener("PageMetadata:PageDataResult", onPageDataResult);
|
||||
let browser = win && win.gBrowser.selectedBrowser;
|
||||
if (!win || !browser) {
|
||||
MozLoopService.log.error("Error occurred whilst fetching page metadata");
|
||||
reply();
|
||||
return;
|
||||
}
|
||||
|
||||
// non-remote pages have no metadata
|
||||
if (!browser.getAttribute("remote") === "true") {
|
||||
reply(null);
|
||||
}
|
||||
|
||||
win.messageManager.addMessageListener("PageMetadata:PageDataResult",
|
||||
function onPageDataResult(msg) {
|
||||
|
||||
win.messageManager.removeMessageListener("PageMetadata:PageDataResult",
|
||||
onPageDataResult);
|
||||
let pageData = msg.json;
|
||||
win.LoopUI.getFavicon(function(err, favicon) {
|
||||
if (err && err !== "favicon not found for uri") {
|
||||
@ -754,9 +782,31 @@ const kMessageHandlers = {
|
||||
* the senders' channel.
|
||||
*/
|
||||
IsMultiProcessActive: function(message, reply) {
|
||||
reply(isMultiProcessActive());
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks that the current tab can be shared.
|
||||
* Non-shareable tabs are the non-remote ones when e10s is enabled.
|
||||
*
|
||||
* @param {Object} message Message meant for the handler function,
|
||||
* with no data attached.
|
||||
* @param {Function} reply Callback function, invoked with the result of
|
||||
* the check. The result will be sent back to
|
||||
* the senders' channel.
|
||||
*/
|
||||
IsTabShareable: function(message, reply) {
|
||||
let win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
let browser = win && win.gBrowser.selectedBrowser;
|
||||
reply(!!(browser && browser.getAttribute("remote") == "true"));
|
||||
if (!win || !browser) {
|
||||
reply(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let e10sActive = isMultiProcessActive();
|
||||
let tabRemote = browser.getAttribute("remote") === "true";
|
||||
|
||||
reply(!e10sActive || (e10sActive && tabRemote));
|
||||
},
|
||||
|
||||
/**
|
||||
@ -985,6 +1035,23 @@ const kMessageHandlers = {
|
||||
reply();
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when a closing room has just been created, so user can change
|
||||
* the name of the room to be stored.
|
||||
*
|
||||
* @param {Object} message Message meant for the handler function, shouldn't
|
||||
contain any data.
|
||||
* @param {Function} reply Callback function, invoked with the result of this
|
||||
* message handler. The result will be sent back to
|
||||
* the senders' channel.
|
||||
*/
|
||||
SetNameNewRoom: function(message, reply) {
|
||||
let win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
win && win.LoopUI.renameRoom();
|
||||
|
||||
reply();
|
||||
},
|
||||
|
||||
/**
|
||||
* Used to record the screen sharing state for a window so that it can
|
||||
* be reflected on the toolbar button.
|
||||
|
@ -408,6 +408,8 @@ var MozLoopPushHandler = {
|
||||
this._mockWebSocket = options.mockWebSocket;
|
||||
}
|
||||
|
||||
this.pushServerUri = Services.prefs.getCharPref("dom.push.serverURL");
|
||||
|
||||
this._openSocket();
|
||||
},
|
||||
|
||||
@ -764,62 +766,12 @@ var MozLoopPushHandler = {
|
||||
// For tests, use the mock instance.
|
||||
this._pushSocket = new PushSocket(this._mockWebSocket);
|
||||
|
||||
let performOpen = () => {
|
||||
consoleLog.info("PushHandler: attempt to open websocket to PushServer: ", this.pushServerUri);
|
||||
this._pushSocket.connect(this.pushServerUri,
|
||||
(aMsg) => this._onMsg(aMsg),
|
||||
() => this._onStart(),
|
||||
(aCode, aReason) => this._onClose(aCode, aReason));
|
||||
};
|
||||
consoleLog.info("PushHandler: attempt to open websocket to PushServer: ", this.pushServerUri);
|
||||
this._pushSocket.connect(this.pushServerUri,
|
||||
(aMsg) => this._onMsg(aMsg),
|
||||
() => this._onStart(),
|
||||
(aCode, aReason) => this._onClose(aCode, aReason));
|
||||
|
||||
let pushServerURLFetchError = () => {
|
||||
consoleLog.warn("PushHandler: Could not retrieve push server URL from Loop server, will retry");
|
||||
this._pushSocket = undefined;
|
||||
this._retryManager.retry(() => this._openSocket());
|
||||
return;
|
||||
};
|
||||
|
||||
try {
|
||||
this.pushServerUri = Services.prefs.getCharPref("loop.debug.pushserver");
|
||||
}
|
||||
catch (e) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
if (!this.pushServerUri) {
|
||||
// Get push server to use from the Loop server
|
||||
let pushUrlEndpoint = Services.prefs.getCharPref("loop.server") + "/push-server-config";
|
||||
let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(
|
||||
Ci.nsIXMLHttpRequest);
|
||||
req.open("GET", pushUrlEndpoint);
|
||||
req.onload = () => {
|
||||
if (req.status >= 200 && req.status < 300) {
|
||||
let pushServerConfig;
|
||||
try {
|
||||
pushServerConfig = JSON.parse(req.responseText);
|
||||
} catch (e) {
|
||||
consoleLog.warn("PushHandler: Error parsing JSON response for push server URL");
|
||||
pushServerURLFetchError();
|
||||
}
|
||||
if (pushServerConfig.pushServerURI) {
|
||||
this._retryManager.reset();
|
||||
this.pushServerUri = pushServerConfig.pushServerURI;
|
||||
performOpen();
|
||||
} else {
|
||||
consoleLog.warn("PushHandler: push server URL config lacks pushServerURI parameter");
|
||||
pushServerURLFetchError();
|
||||
}
|
||||
} else {
|
||||
consoleLog.warn("PushHandler: push server URL retrieve error: " + req.status);
|
||||
pushServerURLFetchError();
|
||||
}
|
||||
};
|
||||
req.onerror = pushServerURLFetchError;
|
||||
req.send();
|
||||
} else {
|
||||
// this.pushServerUri already set -- just open the channel
|
||||
performOpen();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -69,6 +69,24 @@ const SHARING_SCREEN = {
|
||||
RESUMED: 1
|
||||
};
|
||||
|
||||
/**
|
||||
* Values that we segment copy panel action telemetry probes into.
|
||||
*
|
||||
* @enum {Number}
|
||||
*/
|
||||
const COPY_PANEL = {
|
||||
// Copy panel was shown to the user.
|
||||
SHOWN: 0,
|
||||
// User selected "no" and to allow the panel to show again.
|
||||
NO_AGAIN: 1,
|
||||
// User selected "no" and to never show the panel.
|
||||
NO_NEVER: 2,
|
||||
// User selected "yes" and to allow the panel to show again.
|
||||
YES_AGAIN: 3,
|
||||
// User selected "yes" and to never show the panel.
|
||||
YES_NEVER: 4
|
||||
};
|
||||
|
||||
/**
|
||||
* Values that we segment MAUs telemetry probes into.
|
||||
*
|
||||
@ -106,13 +124,14 @@ Cu.import("resource://gre/modules/FxAccountsOAuthClient.jsm");
|
||||
Cu.importGlobalProperties(["URL"]);
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["MozLoopService", "LOOP_SESSION_TYPE", "LOOP_MAU_TYPE",
|
||||
"TWO_WAY_MEDIA_CONN_LENGTH", "SHARING_ROOM_URL", "SHARING_SCREEN",
|
||||
"TWO_WAY_MEDIA_CONN_LENGTH", "SHARING_ROOM_URL", "SHARING_SCREEN", "COPY_PANEL",
|
||||
"ROOM_CREATE", "ROOM_DELETE"];
|
||||
|
||||
XPCOMUtils.defineConstant(this, "LOOP_SESSION_TYPE", LOOP_SESSION_TYPE);
|
||||
XPCOMUtils.defineConstant(this, "TWO_WAY_MEDIA_CONN_LENGTH", TWO_WAY_MEDIA_CONN_LENGTH);
|
||||
XPCOMUtils.defineConstant(this, "SHARING_ROOM_URL", SHARING_ROOM_URL);
|
||||
XPCOMUtils.defineConstant(this, "SHARING_SCREEN", SHARING_SCREEN);
|
||||
XPCOMUtils.defineConstant(this, "COPY_PANEL", COPY_PANEL);
|
||||
XPCOMUtils.defineConstant(this, "ROOM_CREATE", ROOM_CREATE);
|
||||
XPCOMUtils.defineConstant(this, "ROOM_DELETE", ROOM_DELETE);
|
||||
XPCOMUtils.defineConstant(this, "LOOP_MAU_TYPE", LOOP_MAU_TYPE);
|
||||
@ -1335,7 +1354,7 @@ this.MozLoopService = {
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
initialize: Task.async(function*(addonVersion) {
|
||||
initialize: Task.async(function* (addonVersion) {
|
||||
// Ensure we don't setup things like listeners more than once.
|
||||
if (gServiceInitialized) {
|
||||
return Promise.resolve();
|
||||
@ -1463,7 +1482,7 @@ this.MozLoopService = {
|
||||
* Can be called more than once (e.g. if the initial setup fails at some phase).
|
||||
* @param {Deferred} deferredInitialization
|
||||
*/
|
||||
delayedInitialize: Task.async(function*(deferredInitialization) {
|
||||
delayedInitialize: Task.async(function* (deferredInitialization) {
|
||||
log.debug("delayedInitialize");
|
||||
// Set or clear an error depending on how deferredInitialization gets resolved.
|
||||
// We do this first so that it can handle the early returns below.
|
||||
@ -1815,7 +1834,7 @@ this.MozLoopService = {
|
||||
*
|
||||
* @return {Promise} that resolves when the FxA logout flow is complete.
|
||||
*/
|
||||
logOutFromFxA: Task.async(function*() {
|
||||
logOutFromFxA: Task.async(function* () {
|
||||
log.debug("logOutFromFxA");
|
||||
try {
|
||||
yield MozLoopServiceInternal.unregisterFromLoopServer(LOOP_SESSION_TYPE.FXA);
|
||||
|
22
browser/extensions/loop/chrome/content/panels/copy.html
Normal file
22
browser/extensions/loop/chrome/content/panels/copy.html
Normal file
@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<base href="chrome://loop/content">
|
||||
<link rel="stylesheet" type="text/css" href="shared/css/reset.css">
|
||||
<link rel="stylesheet" type="text/css" href="shared/css/common.css">
|
||||
<link rel="stylesheet" type="text/css" href="panels/css/copy.css">
|
||||
</head>
|
||||
<body class="panel">
|
||||
|
||||
<div id="main"></div>
|
||||
|
||||
<script type="text/javascript" src="panels/vendor/l10n.js"></script>
|
||||
<script type="text/javascript" src="shared/vendor/react.js"></script>
|
||||
<script type="text/javascript" src="panels/js/copy.js"></script>
|
||||
</body>
|
||||
</html>
|
49
browser/extensions/loop/chrome/content/panels/css/copy.css
Normal file
49
browser/extensions/loop/chrome/content/panels/css/copy.css
Normal file
@ -0,0 +1,49 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
html {
|
||||
font-family: menu;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.copy-body {
|
||||
display: flex;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.copy-logo {
|
||||
height: 32px;
|
||||
-moz-margin-end: 1.5rem;
|
||||
}
|
||||
|
||||
.copy-message {
|
||||
color: #000;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.copy-toggle-label {
|
||||
display: block;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.copy-toggle-label input {
|
||||
-moz-margin-start: 0;
|
||||
}
|
||||
|
||||
.copy-button {
|
||||
background: #efefef;
|
||||
border: 0;
|
||||
line-height: 1rem;
|
||||
outline: 1px solid #d1d1d1;
|
||||
padding: 1.5rem;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.copy-button:active,
|
||||
.copy-button:focus,
|
||||
.copy-button:hover {
|
||||
background: #00a9dc;
|
||||
color: #fff;
|
||||
outline-color: #0097c5;
|
||||
}
|
@ -96,6 +96,70 @@ body {
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
/* Rename closing room panel */
|
||||
.rename-newRoom > img {
|
||||
margin-left: 16px;
|
||||
position: absolute;
|
||||
width: 32px;
|
||||
}
|
||||
html[dir="rtl"] .rename-newRoom > img {
|
||||
margin-left: 0;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.rename-container {
|
||||
margin: 16px;
|
||||
/* margin is img-width + 16px separation */
|
||||
padding-left: 48px;
|
||||
}
|
||||
html[dir="rtl"] .rename-container {
|
||||
padding-left: 0;
|
||||
padding-right: 48px;
|
||||
}
|
||||
|
||||
.rename-container > .rename-header {
|
||||
color: #000;
|
||||
font-size: 1.2em;
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.rename-container > .rename-prompt {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.rename-container > .input-group {
|
||||
background: #fff;
|
||||
border: 1px solid #d1d1d1;
|
||||
border-radius: 2px;
|
||||
margin-left: -2px;
|
||||
width: 100%;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
html[dir="rtl"] .rename-container > .input-group {
|
||||
margin-left: 0;
|
||||
margin-right: -2px;
|
||||
}
|
||||
.rename-container > .input-group.focused {
|
||||
border: 1px solid #0078f9;
|
||||
box-shadow: 0 0 0 3px #9eccfe;
|
||||
}
|
||||
|
||||
.input-group > .rename-input {
|
||||
border: 0px none;
|
||||
font-size: 1.4rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.rename-button {
|
||||
background: #efefef;
|
||||
border-top: 1px solid #d1d1d1;
|
||||
display: block;
|
||||
padding: 12px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Content area and input fields */
|
||||
|
||||
.content-area {
|
||||
@ -724,7 +788,6 @@ img.fte-hello-web-share {
|
||||
}
|
||||
|
||||
.fte-get-started-container {
|
||||
width: 330px; /* XXXremove me after this comment exists on 1.1 */
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
background: #fbfbfb;
|
||||
|
@ -1,15 +0,0 @@
|
||||
{
|
||||
"ecmaFeatures": {
|
||||
// These are on for this directory for .jsm and content/js files.
|
||||
"blockBindings": true,
|
||||
"arrowFunctions": true,
|
||||
"destructuring": true,
|
||||
"generators": true,
|
||||
"spread": true,
|
||||
"restParams": true,
|
||||
"objectLiteralShorthandMethods": true
|
||||
},
|
||||
"rules": {
|
||||
"generator-star-spacing": [2, "after"]
|
||||
}
|
||||
}
|
120
browser/extensions/loop/chrome/content/panels/js/copy.js
Normal file
120
browser/extensions/loop/chrome/content/panels/js/copy.js
Normal file
@ -0,0 +1,120 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* global Components */
|
||||
let { utils: Cu } = Components;
|
||||
|
||||
var loop = loop || {};
|
||||
loop.copy = (mozL10n => {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Copy panel view.
|
||||
*/
|
||||
|
||||
let CopyView = React.createClass({
|
||||
displayName: "CopyView",
|
||||
|
||||
mixins: [React.addons.PureRenderMixin],
|
||||
|
||||
getInitialState() {
|
||||
return { checked: false };
|
||||
},
|
||||
|
||||
/**
|
||||
* Send a message to chrome/bootstrap to handle copy panel events.
|
||||
* @param {Boolean} accept True if the user clicked accept.
|
||||
*/
|
||||
_dispatch(accept) {
|
||||
window.dispatchEvent(new CustomEvent("CopyPanelClick", {
|
||||
detail: {
|
||||
accept,
|
||||
stop: this.state.checked
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
handleAccept() {
|
||||
this._dispatch(true);
|
||||
},
|
||||
|
||||
handleCancel() {
|
||||
this._dispatch(false);
|
||||
},
|
||||
|
||||
handleToggle() {
|
||||
this.setState({ checked: !this.state.checked });
|
||||
},
|
||||
|
||||
render() {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ className: "copy-content" },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "copy-body" },
|
||||
React.createElement("img", { className: "copy-logo", src: "shared/img/helloicon.svg" }),
|
||||
React.createElement(
|
||||
"h1",
|
||||
{ className: "copy-message" },
|
||||
mozL10n.get("copy_panel_message"),
|
||||
React.createElement(
|
||||
"label",
|
||||
{ className: "copy-toggle-label" },
|
||||
React.createElement("input", { onChange: this.handleToggle, type: "checkbox" }),
|
||||
mozL10n.get("copy_panel_dont_show_again_label")
|
||||
)
|
||||
)
|
||||
),
|
||||
React.createElement(
|
||||
"button",
|
||||
{ className: "copy-button", onClick: this.handleCancel },
|
||||
mozL10n.get("copy_panel_cancel_button_label")
|
||||
),
|
||||
React.createElement(
|
||||
"button",
|
||||
{ className: "copy-button", onClick: this.handleAccept },
|
||||
mozL10n.get("copy_panel_accept_button_label")
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Copy panel initialization.
|
||||
*/
|
||||
function init() {
|
||||
// Wait for all LoopAPI message requests to complete before continuing init.
|
||||
let { LoopAPI } = Cu.import("chrome://loop/content/modules/MozLoopAPI.jsm", {});
|
||||
let requests = ["GetAllStrings", "GetLocale", "GetPluralRule"];
|
||||
return Promise.all(requests.map(name => new Promise(resolve => {
|
||||
LoopAPI.sendMessageToHandler({ name }, resolve);
|
||||
}))).then(results => {
|
||||
// Extract individual requested values to initialize l10n.
|
||||
let [stringBundle, locale, pluralRule] = results;
|
||||
mozL10n.initialize({
|
||||
getStrings(key) {
|
||||
if (!(key in stringBundle)) {
|
||||
console.error("No string found for key:", key);
|
||||
}
|
||||
return JSON.stringify({ textContent: stringBundle[key] || "" });
|
||||
},
|
||||
locale,
|
||||
pluralRule
|
||||
});
|
||||
|
||||
React.render(React.createElement(CopyView, null), document.querySelector("#main"));
|
||||
|
||||
document.documentElement.setAttribute("dir", mozL10n.language.direction);
|
||||
document.documentElement.setAttribute("lang", mozL10n.language.code);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
CopyView,
|
||||
init
|
||||
};
|
||||
})(document.mozL10n);
|
||||
|
||||
document.addEventListener("DOMContentLoaded", loop.copy.init);
|
@ -242,6 +242,7 @@ loop.shared.desktopViews = function (mozL10n) {
|
||||
locationForMetrics: this.props.locationForMetrics,
|
||||
roomData: this.props.roomData });
|
||||
}
|
||||
return null;
|
||||
})()
|
||||
),
|
||||
React.createElement(SocialShareDropdown, {
|
||||
|
@ -36,16 +36,18 @@ loop.panel = function (_, mozL10n) {
|
||||
},
|
||||
|
||||
renderGettingStartedButton: function () {
|
||||
if (!this.props.displayRoomListContent) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ className: "fte-button-container" },
|
||||
React.createElement(Button, { additionalClass: "fte-get-started-button",
|
||||
caption: mozL10n.get("first_time_experience_button_label2"),
|
||||
htmlId: "fte-button",
|
||||
onClick: this.handleButtonClick })
|
||||
);
|
||||
if (this.props.displayRoomListContent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ className: "fte-button-container" },
|
||||
React.createElement(Button, { additionalClass: "fte-get-started-button",
|
||||
caption: mozL10n.get("first_time_experience_button_label2"),
|
||||
htmlId: "fte-button",
|
||||
onClick: this.handleButtonClick })
|
||||
);
|
||||
},
|
||||
|
||||
render: function () {
|
||||
@ -444,6 +446,18 @@ loop.panel = function (_, mozL10n) {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Aux function to retrieve the name of a room
|
||||
*/
|
||||
function _getRoomTitle(room) {
|
||||
if (!room) {
|
||||
return mozL10n.get("room_name_untitled_page");
|
||||
}
|
||||
|
||||
var urlData = (room.decryptedContext.urls || [])[0] || {};
|
||||
return room.decryptedContext.roomName || urlData.description || urlData.location || mozL10n.get("room_name_untitled_page");
|
||||
}
|
||||
|
||||
/**
|
||||
* Room list entry.
|
||||
*
|
||||
@ -465,15 +479,10 @@ loop.panel = function (_, mozL10n) {
|
||||
return {
|
||||
editMode: false,
|
||||
eventPosY: 0,
|
||||
newRoomName: this._getRoomTitle()
|
||||
newRoomName: _getRoomTitle(this.props.room)
|
||||
};
|
||||
},
|
||||
|
||||
_getRoomTitle: function () {
|
||||
var urlData = (this.props.room.decryptedContext.urls || [])[0] || {};
|
||||
return this.props.room.decryptedContext.roomName || urlData.description || urlData.location || mozL10n.get("room_name_untitled_page");
|
||||
},
|
||||
|
||||
_isActive: function () {
|
||||
return this.props.room.participants.length > 0;
|
||||
},
|
||||
@ -491,10 +500,6 @@ loop.panel = function (_, mozL10n) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.dispatcher.dispatch(new sharedActions.OpenRoom({
|
||||
roomToken: this.props.room.roomToken
|
||||
}));
|
||||
|
||||
// Open url if needed.
|
||||
loop.requestMulti(["getSelectedTabMetadata"], ["GettingStartedURL", null, {}]).then(function (results) {
|
||||
var contextURL = this.props.room.decryptedContext.urls && this.props.room.decryptedContext.urls[0].location;
|
||||
@ -505,6 +510,12 @@ loop.panel = function (_, mozL10n) {
|
||||
loop.request("OpenURL", contextURL);
|
||||
}
|
||||
this.closeWindow();
|
||||
|
||||
// open the room after the (possible) tab change to be able to
|
||||
// share when opening from non-remote tab.
|
||||
this.props.dispatcher.dispatch(new sharedActions.OpenRoom({
|
||||
roomToken: this.props.room.roomToken
|
||||
}));
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
@ -566,7 +577,7 @@ loop.panel = function (_, mozL10n) {
|
||||
"room-active": this._isActive(),
|
||||
"room-opened": this.props.isOpenedRoom
|
||||
});
|
||||
var roomTitle = this._getRoomTitle();
|
||||
var roomTitle = _getRoomTitle(this.props.room);
|
||||
|
||||
return React.createElement(
|
||||
"div",
|
||||
@ -791,6 +802,7 @@ loop.panel = function (_, mozL10n) {
|
||||
if (Object.prototype.toString.call(props[propName]) !== "[object Object]" && !_.isNull(props[propName])) {
|
||||
return new Error("Required prop `" + propName + "` was not correctly specified in `" + componentName + "`.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -860,6 +872,8 @@ loop.panel = function (_, mozL10n) {
|
||||
if (this.state.rooms.length > 5) {
|
||||
return React.createElement("div", { className: "room-list-blur" });
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
render: function () {
|
||||
@ -957,8 +971,14 @@ loop.panel = function (_, mozL10n) {
|
||||
},
|
||||
|
||||
handleCreateButtonClick: function () {
|
||||
var createRoomAction = new sharedActions.CreateRoom();
|
||||
// check that tab is remote, open about:home if not
|
||||
loop.request("IsTabShareable").then(shareable => {
|
||||
if (!shareable) {
|
||||
loop.request("OpenURL", "about:home");
|
||||
}
|
||||
});
|
||||
|
||||
var createRoomAction = new sharedActions.CreateRoom();
|
||||
createRoomAction.urls = [{
|
||||
location: this.state.url,
|
||||
description: this.state.description,
|
||||
@ -1140,12 +1160,108 @@ loop.panel = function (_, mozL10n) {
|
||||
}
|
||||
});
|
||||
|
||||
var RenameRoomView = React.createClass({
|
||||
displayName: "RenameRoomView",
|
||||
|
||||
mixins: [sharedMixins.WindowCloseMixin],
|
||||
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
roomName: React.PropTypes.string.isRequired,
|
||||
roomToken: React.PropTypes.string.isRequired
|
||||
},
|
||||
|
||||
getInitialState: function () {
|
||||
return { focused: false };
|
||||
},
|
||||
|
||||
componentDidMount: function () {
|
||||
this.getDOMNode().querySelector("input").focus();
|
||||
},
|
||||
|
||||
handleBlur: function () {
|
||||
this.setState({ focused: false });
|
||||
},
|
||||
|
||||
handleFocus: function () {
|
||||
this.setState({ focused: true });
|
||||
this.getDOMNode().querySelector("input").select();
|
||||
},
|
||||
|
||||
handleKeyDown: function (event) {
|
||||
if (event.which === 13) {
|
||||
this.handleNameChange();
|
||||
}
|
||||
},
|
||||
|
||||
handleNameChange: function () {
|
||||
let token = this.props.roomToken,
|
||||
name = this.getDOMNode().querySelector("input").value || "";
|
||||
|
||||
if (name !== this.props.roomName) {
|
||||
this.props.dispatcher.dispatch(new sharedActions.UpdateRoomContext({
|
||||
roomToken: token,
|
||||
newRoomName: name
|
||||
}));
|
||||
}
|
||||
|
||||
this.closeWindow();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
let inputClass = classNames({
|
||||
"input-group": true,
|
||||
"focused": this.state.focused
|
||||
});
|
||||
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ className: "rename-newRoom" },
|
||||
React.createElement("img", { src: "shared/img/helloicon.svg" }),
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "rename-container" },
|
||||
React.createElement(
|
||||
"h2",
|
||||
{ className: "rename-header" },
|
||||
mozL10n.get("door_hanger_bye")
|
||||
),
|
||||
React.createElement(
|
||||
"p",
|
||||
{ className: "rename-subheader" },
|
||||
mozL10n.get("door_hanger_return2")
|
||||
),
|
||||
React.createElement(
|
||||
"p",
|
||||
{ className: "rename-prompt" },
|
||||
mozL10n.get("door_hanger_current")
|
||||
),
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: inputClass },
|
||||
React.createElement("input", { className: "rename-input",
|
||||
defaultValue: this.props.roomName,
|
||||
onBlur: this.handleBlur,
|
||||
onFocus: this.handleFocus,
|
||||
onKeyDown: this.handleKeyDown,
|
||||
type: "text" })
|
||||
)
|
||||
),
|
||||
React.createElement(Button, { additionalClass: "rename-button",
|
||||
caption: mozL10n.get("door_hanger_button2"),
|
||||
onClick: this.handleNameChange })
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Panel view.
|
||||
*/
|
||||
var PanelView = React.createClass({
|
||||
displayName: "PanelView",
|
||||
|
||||
mixins: [Backbone.Events, sharedMixins.DocumentVisibilityMixin],
|
||||
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
// Only used for the ui-showcase:
|
||||
@ -1169,6 +1285,7 @@ loop.panel = function (_, mozL10n) {
|
||||
gettingStartedSeen: loop.getStoredRequest(["GetLoopPref", "gettingStarted.latestFTUVersion"]) >= FTU_VERSION,
|
||||
multiProcessActive: loop.getStoredRequest(["IsMultiProcessActive"]),
|
||||
remoteAutoStart: loop.getStoredRequest(["GetLoopPref", "remote.autostart"]),
|
||||
renameRoom: null,
|
||||
sharePanelOpened: false
|
||||
};
|
||||
},
|
||||
@ -1237,8 +1354,35 @@ loop.panel = function (_, mozL10n) {
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
_onClosingNewRoom: function () {
|
||||
// If we close a recently created room, we offer the chance of renaming it
|
||||
let storeState = this.props.roomStore.getStoreState();
|
||||
if (!storeState.closingNewRoom || !storeState.openedRoom) {
|
||||
return;
|
||||
}
|
||||
|
||||
let closedRoom = storeState.rooms.filter(function (room) {
|
||||
// closing room is the last that was opened.
|
||||
return storeState.openedRoom === room.roomToken;
|
||||
})[0];
|
||||
|
||||
this.setState({
|
||||
renameRoom: storeState.closingNewRoom && {
|
||||
token: storeState.openedRoom,
|
||||
name: _getRoomTitle(closedRoom)
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onDocumentHidden: function () {
|
||||
// consider closing panel any other way than click OK button
|
||||
// or Enter key the same as cancel renaming the room
|
||||
this.setState({ renameRoom: null });
|
||||
},
|
||||
|
||||
componentWillMount: function () {
|
||||
this.updateServiceErrors();
|
||||
this.listenTo(this.props.roomStore, "change:closingNewRoom", this._onClosingNewRoom);
|
||||
},
|
||||
|
||||
componentDidMount: function () {
|
||||
@ -1247,6 +1391,7 @@ loop.panel = function (_, mozL10n) {
|
||||
|
||||
componentWillUnmount: function () {
|
||||
loop.unsubscribe("LoopStatusChanged", this._onStatusChanged);
|
||||
this.stopListening(this.props.roomStore);
|
||||
},
|
||||
|
||||
handleContextMenu: function (e) {
|
||||
@ -1284,10 +1429,18 @@ loop.panel = function (_, mozL10n) {
|
||||
React.createElement(ToSView, null)
|
||||
);
|
||||
}
|
||||
|
||||
if (!this.state.hasEncryptionKey) {
|
||||
return React.createElement(SignInRequestView, null);
|
||||
}
|
||||
|
||||
if (this.state.renameRoom) {
|
||||
return React.createElement(RenameRoomView, {
|
||||
dispatcher: this.props.dispatcher,
|
||||
roomName: this.state.renameRoom.name,
|
||||
roomToken: this.state.renameRoom.token });
|
||||
}
|
||||
|
||||
var cssClasses = classNames({
|
||||
"panel-content": true,
|
||||
"showing-share-panel": this.state.sharePanelOpened
|
||||
@ -1392,6 +1545,7 @@ loop.panel = function (_, mozL10n) {
|
||||
init: init,
|
||||
NewRoomView: NewRoomView,
|
||||
PanelView: PanelView,
|
||||
RenameRoomView: RenameRoomView,
|
||||
RoomEntry: RoomEntry,
|
||||
RoomEntryContextButtons: RoomEntryContextButtons,
|
||||
RoomList: RoomList,
|
||||
|
@ -103,6 +103,7 @@ loop.store = loop.store || {};
|
||||
|
||||
this._notifications = options.notifications;
|
||||
this._constants = options.constants;
|
||||
this._gotAllRooms = false;
|
||||
|
||||
if (options.activeRoomStore) {
|
||||
this.activeRoomStore = options.activeRoomStore;
|
||||
@ -114,7 +115,9 @@ loop.store = loop.store || {};
|
||||
getInitialStoreState: function() {
|
||||
return {
|
||||
activeRoom: this.activeRoomStore ? this.activeRoomStore.getStoreState() : {},
|
||||
closingNewRoom: false,
|
||||
error: null,
|
||||
lastCreatedRoom: null,
|
||||
openedRoom: null,
|
||||
pendingCreation: false,
|
||||
pendingInitialRetrieval: true,
|
||||
@ -153,6 +156,7 @@ loop.store = loop.store || {};
|
||||
_onRoomAdded: function(addedRoomData) {
|
||||
addedRoomData.participants = addedRoomData.participants || [];
|
||||
addedRoomData.ctime = addedRoomData.ctime || new Date().getTime();
|
||||
|
||||
this.dispatchAction(new sharedActions.UpdateRoomList({
|
||||
// Ensure the room isn't part of the list already, then add it.
|
||||
roomList: this._storeState.rooms.filter(function(room) {
|
||||
@ -165,7 +169,20 @@ loop.store = loop.store || {};
|
||||
* Clears the current active room.
|
||||
*/
|
||||
_onRoomClose: function() {
|
||||
let state = this.getStoreState();
|
||||
|
||||
// If the room getting closed has been just created, then open the panel.
|
||||
if (state.lastCreatedRoom && state.openedRoom === state.lastCreatedRoom) {
|
||||
this.setStoreState({
|
||||
closingNewRoom: true
|
||||
});
|
||||
loop.request("SetNameNewRoom");
|
||||
}
|
||||
|
||||
// reset state for closed room
|
||||
this.setStoreState({
|
||||
closingNewRoom: false,
|
||||
lastCreatedRoom: null,
|
||||
openedRoom: null
|
||||
});
|
||||
},
|
||||
@ -268,6 +285,11 @@ loop.store = loop.store || {};
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep the token for the last created room.
|
||||
this.setStoreState({
|
||||
lastCreatedRoom: result.roomToken
|
||||
});
|
||||
|
||||
this.dispatchAction(new sharedActions.CreatedRoom({
|
||||
decryptedContext: result.decryptedContext,
|
||||
roomToken: result.roomToken,
|
||||
@ -467,6 +489,12 @@ loop.store = loop.store || {};
|
||||
* Gather the list of all available rooms from the Loop API.
|
||||
*/
|
||||
getAllRooms: function() {
|
||||
// XXX Ideally, we'd have a specific command to "start up" the room store
|
||||
// to get the rooms. We should address this alongside bug 1074665.
|
||||
if (this._gotAllRooms) {
|
||||
return;
|
||||
}
|
||||
|
||||
loop.request("Rooms:GetAll", null).then(function(result) {
|
||||
var action;
|
||||
|
||||
@ -480,6 +508,8 @@ loop.store = loop.store || {};
|
||||
|
||||
this.dispatchAction(action);
|
||||
|
||||
this._gotAllRooms = true;
|
||||
|
||||
// We can only start listening to room events after getAll() has been
|
||||
// called executed first.
|
||||
this.startListeningToRoomEvents();
|
||||
|
@ -1,12 +1,4 @@
|
||||
{
|
||||
"ecmaFeatures": {
|
||||
// since the code here is running only on known versions of
|
||||
// Firefox, we can use newer ECMAscript features here
|
||||
"arrowFunctions": true,
|
||||
"blockBindings": true,
|
||||
"destructuring": true,
|
||||
"forOf": true
|
||||
},
|
||||
"rules": {
|
||||
// This is useful for some of the tests, e.g.
|
||||
// expect(new Foo()).to.Throw(/error/)
|
||||
|
125
browser/extensions/loop/chrome/content/panels/test/copy_test.js
Normal file
125
browser/extensions/loop/chrome/content/panels/test/copy_test.js
Normal file
@ -0,0 +1,125 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
describe("loop.copy", function() {
|
||||
"use strict";
|
||||
|
||||
var expect = chai.expect;
|
||||
var CopyView = loop.copy.CopyView;
|
||||
var l10n = navigator.mozL10n || document.mozL10n;
|
||||
var TestUtils = React.addons.TestUtils;
|
||||
var sandbox;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox = LoopMochaUtils.createSandbox();
|
||||
sandbox.stub(l10n, "get");
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
loop.shared.mixins.setRootObject(window);
|
||||
sandbox.restore();
|
||||
LoopMochaUtils.restore();
|
||||
});
|
||||
|
||||
describe("#init", function() {
|
||||
beforeEach(function() {
|
||||
sandbox.stub(React, "render");
|
||||
sandbox.stub(document.mozL10n, "initialize");
|
||||
});
|
||||
|
||||
it("should initalize L10n", function() {
|
||||
loop.copy.init();
|
||||
|
||||
sinon.assert.calledOnce(document.mozL10n.initialize);
|
||||
});
|
||||
|
||||
it("should render the copy view", function() {
|
||||
loop.copy.init();
|
||||
|
||||
sinon.assert.calledOnce(React.render);
|
||||
sinon.assert.calledWith(React.render, sinon.match(function(value) {
|
||||
return value.type === CopyView;
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("#render", function() {
|
||||
var view;
|
||||
|
||||
beforeEach(function() {
|
||||
view = TestUtils.renderIntoDocument(React.createElement(CopyView));
|
||||
});
|
||||
|
||||
it("should have an unchecked box", function() {
|
||||
expect(view.getDOMNode().querySelector("input[type=checkbox]").checked).eql(false);
|
||||
});
|
||||
|
||||
it("should have two buttons", function() {
|
||||
expect(view.getDOMNode().querySelectorAll("button").length).eql(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("handleChanges", function() {
|
||||
var view;
|
||||
beforeEach(function() {
|
||||
view = TestUtils.renderIntoDocument(React.createElement(CopyView));
|
||||
});
|
||||
|
||||
it("should have default state !checked", function() {
|
||||
expect(view.state.checked).to.be.false;
|
||||
});
|
||||
|
||||
it("should have checked state after change", function() {
|
||||
TestUtils.Simulate.change(view.getDOMNode().querySelector("input"), {
|
||||
target: { checked: true }
|
||||
});
|
||||
|
||||
expect(view.state.checked).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe("handleClicks", function() {
|
||||
var view;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox.stub(window, "dispatchEvent");
|
||||
view = TestUtils.renderIntoDocument(React.createElement(CopyView));
|
||||
});
|
||||
|
||||
function checkDispatched(detail) {
|
||||
sinon.assert.calledOnce(window.dispatchEvent);
|
||||
sinon.assert.calledWith(window.dispatchEvent, sinon.match.has("detail", detail));
|
||||
}
|
||||
|
||||
it("should dispatch accept !stop on accept", function() {
|
||||
TestUtils.Simulate.click(view.getDOMNode().querySelector("button:last-child"));
|
||||
|
||||
checkDispatched({ accept: true, stop: false });
|
||||
});
|
||||
|
||||
it("should dispatch !accept !stop on cancel", function() {
|
||||
TestUtils.Simulate.click(view.getDOMNode().querySelector("button"));
|
||||
|
||||
checkDispatched({ accept: false, stop: false });
|
||||
});
|
||||
|
||||
it("should dispatch accept stop on checked accept", function() {
|
||||
TestUtils.Simulate.change(view.getDOMNode().querySelector("input"), {
|
||||
target: { checked: true }
|
||||
});
|
||||
TestUtils.Simulate.click(view.getDOMNode().querySelector("button:last-child"));
|
||||
|
||||
checkDispatched({ accept: true, stop: true });
|
||||
});
|
||||
|
||||
it("should dispatch !accept stop on checked cancel", function() {
|
||||
TestUtils.Simulate.change(view.getDOMNode().querySelector("input"), {
|
||||
target: { checked: true }
|
||||
});
|
||||
TestUtils.Simulate.click(view.getDOMNode().querySelector("button"));
|
||||
|
||||
checkDispatched({ accept: false, stop: true });
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,29 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
// Create fake Components to test chrome-privileged React components.
|
||||
window.Components = {
|
||||
utils: {
|
||||
import: function() {
|
||||
return {
|
||||
LoopAPI: {
|
||||
sendMessageToHandler: function({ name }, callback) {
|
||||
switch (name) {
|
||||
case "GetLocale":
|
||||
return callback("en-US");
|
||||
case "GetPluralRule":
|
||||
return callback(1);
|
||||
default:
|
||||
return callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
@ -51,6 +51,9 @@
|
||||
<script src="/add-on/shared/js/dispatcher.js"></script>
|
||||
<script src="/add-on/shared/js/otSdkDriver.js"></script>
|
||||
|
||||
<!-- Load fake-components after loading utils to avoid chrome detection. -->
|
||||
<script src="/add-on/panels/test/fake-components.js"></script>
|
||||
|
||||
<!-- Stores need to be loaded before the views that uses them -->
|
||||
<script src="/add-on/shared/js/store.js"></script>
|
||||
<script src="/add-on/panels/js/roomStore.js"></script>
|
||||
@ -66,12 +69,14 @@
|
||||
<script src="/add-on/panels/js/roomViews.js"></script>
|
||||
<script src="/add-on/panels/js/feedbackViews.js"></script>
|
||||
<script src="/add-on/panels/js/conversation.js"></script>
|
||||
<script src="/add-on/panels/js/copy.js"></script>
|
||||
<script src="/add-on/panels/js/panel.js"></script>
|
||||
<script src="/add-on/panels/js/slideshow.js"></script>
|
||||
|
||||
<!-- Test scripts -->
|
||||
<script src="conversationAppStore_test.js"></script>
|
||||
<script src="conversation_test.js"></script>
|
||||
<script src="copy_test.js"></script>
|
||||
<script src="feedbackViews_test.js"></script>
|
||||
<script src="panel_test.js"></script>
|
||||
<script src="desktopViews_test.js"></script>
|
||||
@ -83,6 +88,7 @@
|
||||
// Stop the default init functions running to avoid conflicts in tests
|
||||
document.removeEventListener('DOMContentLoaded', loop.panel.init);
|
||||
document.removeEventListener('DOMContentLoaded', loop.conversation.init);
|
||||
document.removeEventListener('DOMContentLoaded', loop.copy.init);
|
||||
document.removeEventListener("DOMContentLoaded", loop.slideshow.init);
|
||||
|
||||
LoopMochaUtils.addErrorCheckingTests();
|
||||
|
@ -7,35 +7,105 @@ var expect = chai.expect;
|
||||
describe("document.mozL10n", function() {
|
||||
"use strict";
|
||||
|
||||
var l10nOptions;
|
||||
var sandbox, l10nOptions;
|
||||
|
||||
beforeEach(function() {
|
||||
l10nOptions = {
|
||||
locale: "en-US",
|
||||
getStrings: function(key) {
|
||||
if (key === "plural") {
|
||||
return '{"textContent":"{{num}} plural form;{{num}} plural forms"}';
|
||||
sandbox = LoopMochaUtils.createSandbox();
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe("#initialize", function() {
|
||||
beforeEach(function() {
|
||||
sandbox.stub(console, "error");
|
||||
});
|
||||
|
||||
it("should correctly set the plural form", function() {
|
||||
l10nOptions = {
|
||||
locale: "en-US",
|
||||
getStrings: function(key) {
|
||||
if (key === "plural") {
|
||||
return '{"textContent":"{{num}} form;{{num}} forms;{{num}} form 2"}';
|
||||
}
|
||||
|
||||
return '{"textContent":"' + key + '"}';
|
||||
},
|
||||
pluralRule: 14
|
||||
};
|
||||
|
||||
document.mozL10n.initialize(l10nOptions);
|
||||
|
||||
expect(document.mozL10n.get("plural", { num: 39 })).eql("39 form 2");
|
||||
});
|
||||
|
||||
it("should log an error if the rule number is invalid", function() {
|
||||
l10nOptions = {
|
||||
locale: "en-US",
|
||||
getStrings: function(key) {
|
||||
if (key === "plural") {
|
||||
return '{"textContent":"{{num}} form;{{num}} forms;{{num}} form 2"}';
|
||||
}
|
||||
|
||||
return '{"textContent":"' + key + '"}';
|
||||
},
|
||||
pluralRule: NaN
|
||||
};
|
||||
|
||||
document.mozL10n.initialize(l10nOptions);
|
||||
|
||||
sinon.assert.calledOnce(console.error);
|
||||
});
|
||||
|
||||
it("should use rule 0 if the rule number is invalid", function() {
|
||||
l10nOptions = {
|
||||
locale: "en-US",
|
||||
getStrings: function(key) {
|
||||
if (key === "plural") {
|
||||
return '{"textContent":"{{num}} form;{{num}} forms;{{num}} form 2"}';
|
||||
}
|
||||
|
||||
return '{"textContent":"' + key + '"}';
|
||||
},
|
||||
pluralRule: NaN
|
||||
};
|
||||
|
||||
document.mozL10n.initialize(l10nOptions);
|
||||
|
||||
expect(document.mozL10n.get("plural", { num: 39 })).eql("39 form");
|
||||
});
|
||||
});
|
||||
|
||||
describe("#get", function() {
|
||||
beforeEach(function() {
|
||||
l10nOptions = {
|
||||
locale: "en-US",
|
||||
getStrings: function(key) {
|
||||
if (key === "plural") {
|
||||
return '{"textContent":"{{num}} plural form;{{num}} plural forms"}';
|
||||
}
|
||||
|
||||
return '{"textContent":"' + key + '"}';
|
||||
},
|
||||
getPluralForm: function(num, string) {
|
||||
return string.split(";")[num === 0 ? 0 : 1];
|
||||
}
|
||||
};
|
||||
|
||||
return '{"textContent":"' + key + '"}';
|
||||
},
|
||||
getPluralForm: function(num, string) {
|
||||
return string.split(";")[num === 0 ? 0 : 1];
|
||||
}
|
||||
};
|
||||
document.mozL10n.initialize(l10nOptions);
|
||||
});
|
||||
|
||||
document.mozL10n.initialize(l10nOptions);
|
||||
});
|
||||
it("should get a simple string", function() {
|
||||
expect(document.mozL10n.get("test")).eql("test");
|
||||
});
|
||||
|
||||
it("should get a simple string", function() {
|
||||
expect(document.mozL10n.get("test")).eql("test");
|
||||
});
|
||||
it("should get a plural form", function() {
|
||||
expect(document.mozL10n.get("plural", { num: 10 })).eql("10 plural forms");
|
||||
});
|
||||
|
||||
it("should get a plural form", function() {
|
||||
expect(document.mozL10n.get("plural", { num: 10 })).eql("10 plural forms");
|
||||
});
|
||||
|
||||
it("should correctly get a plural form for num = 0", function() {
|
||||
expect(document.mozL10n.get("plural", { num: 0 })).eql("0 plural form");
|
||||
it("should correctly get a plural form for num = 0", function() {
|
||||
expect(document.mozL10n.get("plural", { num: 0 })).eql("0 plural form");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -34,7 +34,10 @@ describe("loop.panel", function() {
|
||||
close: sandbox.stub(),
|
||||
addEventListener: function() {},
|
||||
removeEventListener: function() {},
|
||||
document: { addEventListener: function() {} },
|
||||
document: {
|
||||
addEventListener: function() {},
|
||||
removeEventListener: function() {}
|
||||
},
|
||||
setTimeout: function(callback) { callback(); }
|
||||
};
|
||||
loop.shared.mixins.setRootObject(fakeWindow);
|
||||
@ -77,6 +80,7 @@ describe("loop.panel", function() {
|
||||
GetHasEncryptionKey: function() { return true; },
|
||||
HangupAllChatWindows: function() {},
|
||||
IsMultiProcessActive: sinon.stub(),
|
||||
IsTabShareable: sinon.stub(),
|
||||
LoginToFxA: sinon.stub(),
|
||||
LogoutFromFxA: sinon.stub(),
|
||||
NotifyUITour: sinon.stub(),
|
||||
@ -300,6 +304,24 @@ describe("loop.panel", function() {
|
||||
.to.have.length.of(0);
|
||||
});
|
||||
|
||||
it("should reset renaming state when closing the panel", function() {
|
||||
var view = createTestPanelView();
|
||||
view.setState({
|
||||
renameRoom: {
|
||||
name: "fakeName",
|
||||
token: "fakeToken"
|
||||
}
|
||||
});
|
||||
|
||||
view._onDocumentVisibilityChanged({
|
||||
target: {
|
||||
hidden: true
|
||||
}
|
||||
});
|
||||
|
||||
expect(view.state.renameRoom).eql(null);
|
||||
});
|
||||
|
||||
describe("AccountLink", function() {
|
||||
it("should trigger the FxA sign in/up process when clicking the link",
|
||||
function() {
|
||||
@ -660,7 +682,6 @@ describe("loop.panel", function() {
|
||||
}).to.not.Throw();
|
||||
});
|
||||
|
||||
|
||||
it("should render a GettingStarted view when gettingStarted.latestFTUVersion is less than FTU_VERSION", function() {
|
||||
loop.storedRequests["GetLoopPref|gettingStarted.latestFTUVersion"] = 0;
|
||||
var view = createTestPanelView();
|
||||
@ -712,6 +733,53 @@ describe("loop.panel", function() {
|
||||
}).to.not.Throw();
|
||||
});
|
||||
|
||||
it("should render an RenameRoomView when a new room is closed",
|
||||
function() {
|
||||
roomStore.setStoreState({
|
||||
openedRoom: "fakeToken"
|
||||
});
|
||||
|
||||
var view = createTestPanelView();
|
||||
roomStore.setStoreState({
|
||||
closingNewRoom: true
|
||||
});
|
||||
|
||||
expect(function() {
|
||||
TestUtils.findRenderedComponentWithType(view, loop.panel.RenameRoomView);
|
||||
}).to.not.Throw();
|
||||
});
|
||||
|
||||
it("should not render an RenameRoomView when no room open",
|
||||
function() {
|
||||
roomStore.setStoreState({
|
||||
openedRoom: null
|
||||
});
|
||||
|
||||
var view = createTestPanelView();
|
||||
roomStore.setStoreState({
|
||||
closingNewRoom: true
|
||||
});
|
||||
|
||||
expect(function() {
|
||||
TestUtils.findRenderedComponentWithType(view, loop.panel.RenameRoomView);
|
||||
}).to.Throw();
|
||||
});
|
||||
|
||||
it("should not render an RenameRoomView when no room closing",
|
||||
function() {
|
||||
roomStore.setStoreState({
|
||||
openedRoom: "fakeToken"
|
||||
});
|
||||
|
||||
var view = createTestPanelView();
|
||||
roomStore.setStoreState({
|
||||
closingNewRoom: false
|
||||
});
|
||||
|
||||
expect(function() {
|
||||
TestUtils.findRenderedComponentWithType(view, loop.panel.RenameRoomView);
|
||||
}).to.Throw();
|
||||
});
|
||||
});
|
||||
|
||||
describe("GettingStartedView", function() {
|
||||
@ -1116,6 +1184,89 @@ describe("loop.panel", function() {
|
||||
expect(roomEntry.getDOMNode().textContent).eql("https://fakeurl.com");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Room entry click", function() {
|
||||
var roomEntry;
|
||||
|
||||
beforeEach(function() {
|
||||
// Stub to prevent warnings due to stores not being set up to handle
|
||||
// the actions we are triggering.
|
||||
sandbox.stub(dispatcher, "dispatch");
|
||||
});
|
||||
|
||||
it("should require MetaData", function() {
|
||||
roomEntry = mountRoomEntry({
|
||||
isOpenedRoom: false,
|
||||
room: new loop.store.Room(roomData)
|
||||
});
|
||||
roomEntry.handleClickEntry(fakeEvent);
|
||||
|
||||
sinon.assert.calledOnce(requestStubs.GetSelectedTabMetadata);
|
||||
});
|
||||
|
||||
it("should close the Panel", function() {
|
||||
roomEntry = mountRoomEntry({
|
||||
isOpenedRoom: false,
|
||||
room: new loop.store.Room(roomData)
|
||||
});
|
||||
sandbox.stub(roomEntry, "closeWindow");
|
||||
roomEntry.handleClickEntry(fakeEvent);
|
||||
|
||||
sinon.assert.calledOnce(roomEntry.closeWindow);
|
||||
});
|
||||
|
||||
it("should dispatch the OpenRoom action", function() {
|
||||
roomEntry = mountRoomEntry({
|
||||
isOpenedRoom: false,
|
||||
room: new loop.store.Room(roomData)
|
||||
});
|
||||
roomEntry.handleClickEntry(fakeEvent);
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.OpenRoom({
|
||||
roomToken: roomData.roomToken
|
||||
}));
|
||||
});
|
||||
|
||||
// if current URL same as ROOM, dont open TAB
|
||||
it("should NOT open new tab if we already in same URL", function() {
|
||||
requestStubs.GetSelectedTabMetadata.returns({
|
||||
url: "fakeURL"
|
||||
});
|
||||
roomData.decryptedContext.urls = [{
|
||||
location: "fakeURL"
|
||||
}];
|
||||
|
||||
roomEntry = mountRoomEntry({
|
||||
isOpenedRoom: false,
|
||||
room: new loop.store.Room(roomData)
|
||||
});
|
||||
|
||||
roomEntry.handleClickEntry(fakeEvent);
|
||||
sinon.assert.calledOnce(requestStubs.GetSelectedTabMetadata);
|
||||
sinon.assert.notCalled(requestStubs.OpenURL);
|
||||
});
|
||||
|
||||
it("should open new tab if different URL", function() {
|
||||
requestStubs.GetSelectedTabMetadata.returns({
|
||||
url: "notTheSameURL"
|
||||
});
|
||||
roomData.decryptedContext.urls = [{
|
||||
location: "fakeURL"
|
||||
}];
|
||||
|
||||
roomEntry = mountRoomEntry({
|
||||
isOpenedRoom: false,
|
||||
room: new loop.store.Room(roomData)
|
||||
});
|
||||
|
||||
roomEntry.handleClickEntry(fakeEvent);
|
||||
sinon.assert.calledOnce(requestStubs.GetSelectedTabMetadata);
|
||||
sinon.assert.calledOnce(requestStubs.OpenURL);
|
||||
sinon.assert.calledWithExactly(requestStubs.OpenURL, "fakeURL");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("loop.panel.RoomList", function() {
|
||||
@ -1353,7 +1504,6 @@ describe("loop.panel", function() {
|
||||
|
||||
expect(topPosTest).to.equal(false);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@ -1383,6 +1533,49 @@ describe("loop.panel", function() {
|
||||
}, extraProps)));
|
||||
}
|
||||
|
||||
it("should open new tab when Starting Conversation on a non-remote tab",
|
||||
function() {
|
||||
LoopMochaUtils.stubLoopRequest({
|
||||
IsTabShareable: function() {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
var view = createTestComponent({
|
||||
inRoom: false,
|
||||
pendingOperation: false
|
||||
});
|
||||
// Simulate being visible
|
||||
view.onDocumentVisible();
|
||||
// Simulate click on button
|
||||
var node = view.getDOMNode();
|
||||
TestUtils.Simulate.click(node.querySelector(".new-room-button"));
|
||||
|
||||
sinon.assert.calledOnce(requestStubs.OpenURL);
|
||||
sinon.assert.calledWithExactly(requestStubs.OpenURL, "about:home");
|
||||
});
|
||||
|
||||
it("should stay in same tab when Starting Conversation on a remote tab",
|
||||
function() {
|
||||
LoopMochaUtils.stubLoopRequest({
|
||||
IsTabShareable: function() {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
var view = createTestComponent({
|
||||
inRoom: false,
|
||||
pendingOperation: false
|
||||
});
|
||||
// Simulate being visible
|
||||
view.onDocumentVisible();
|
||||
// Simulate click on button
|
||||
var node = view.getDOMNode();
|
||||
TestUtils.Simulate.click(node.querySelector(".new-room-button"));
|
||||
|
||||
sinon.assert.notCalled(requestStubs.OpenURL);
|
||||
});
|
||||
|
||||
it("should dispatch a CreateRoom action with context when clicking on the " +
|
||||
"Start a conversation button", function() {
|
||||
var favicon = "data:image/x-icon;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
|
||||
@ -1494,7 +1687,7 @@ describe("loop.panel", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("ConversationDropdown", function() {
|
||||
describe("loop.panel.ConversationDropdown", function() {
|
||||
var view;
|
||||
|
||||
function createTestComponent() {
|
||||
@ -1541,7 +1734,7 @@ describe("loop.panel", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("RoomEntryContextButtons", function() {
|
||||
describe("loop.panel.RoomEntryContextButtons", function() {
|
||||
var view, dispatcher;
|
||||
|
||||
function createTestComponent(extraProps) {
|
||||
@ -1613,7 +1806,7 @@ describe("loop.panel", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("SharePanelView", function() {
|
||||
describe("loop.panel.SharePanelView", function() {
|
||||
var view, dispatcher, roomStore;
|
||||
|
||||
function createTestComponent(extraProps) {
|
||||
@ -1760,4 +1953,114 @@ describe("loop.panel", function() {
|
||||
sinon.assert.calledOnce(view.props.onSharePanelDisplayChange);
|
||||
});
|
||||
});
|
||||
|
||||
describe("loop.panel.RenameRoomView", function() {
|
||||
var dispatcher,
|
||||
view;
|
||||
|
||||
function mountTestComponent(name, token) {
|
||||
return TestUtils.renderIntoDocument(
|
||||
React.createElement(
|
||||
loop.panel.RenameRoomView, {
|
||||
dispatcher: dispatcher,
|
||||
roomName: name,
|
||||
roomToken: token
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
dispatcher = new loop.Dispatcher();
|
||||
sandbox.stub(dispatcher, "dispatch");
|
||||
});
|
||||
|
||||
it("should highlight container and select text when input gets the focus",
|
||||
function() {
|
||||
view = mountTestComponent("fakeName", "fakeToken");
|
||||
var input = view.getDOMNode().querySelector("input");
|
||||
sandbox.stub(input, "select");
|
||||
|
||||
TestUtils.Simulate.focus(input);
|
||||
|
||||
expect(view.state.focused).eql(true);
|
||||
sinon.assert.notCalled(dispatcher.dispatch);
|
||||
sinon.assert.called(input.select);
|
||||
});
|
||||
|
||||
it("should stop highlighting container when input lose focus", function() {
|
||||
view = mountTestComponent("fakeName", "fakeToken");
|
||||
var input = view.getDOMNode().querySelector("input");
|
||||
|
||||
TestUtils.Simulate.focus(input);
|
||||
TestUtils.Simulate.blur(input);
|
||||
|
||||
expect(view.state.focused).eql(false);
|
||||
sinon.assert.notCalled(dispatcher.dispatch);
|
||||
});
|
||||
|
||||
it("should update the room name when OK button is pressed", function() {
|
||||
view = mountTestComponent("fakeName", "fakeToken");
|
||||
var input = view.getDOMNode().querySelector("input");
|
||||
var button = view.getDOMNode().querySelector(".rename-button");
|
||||
|
||||
input.value = "notTheFakeName";
|
||||
TestUtils.Simulate.change(input);
|
||||
TestUtils.Simulate.click(button);
|
||||
|
||||
sinon.assert.called(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.UpdateRoomContext({
|
||||
roomToken: "fakeToken",
|
||||
newRoomName: "notTheFakeName"
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("should NOT update the room name when name is unchanged" +
|
||||
" and OK button is pressed", function() {
|
||||
view = mountTestComponent("fakeName", "fakeToken");
|
||||
var input = view.getDOMNode().querySelector("input");
|
||||
var button = view.getDOMNode().querySelector(".rename-button");
|
||||
|
||||
input.value = "fakeName";
|
||||
TestUtils.Simulate.change(input);
|
||||
TestUtils.Simulate.click(button);
|
||||
|
||||
sinon.assert.notCalled(dispatcher.dispatch);
|
||||
});
|
||||
|
||||
it("should update the room name when Enter key is pressed", function() {
|
||||
view = mountTestComponent("fakeName", "fakeToken");
|
||||
var input = view.getDOMNode().querySelector("input");
|
||||
|
||||
input.value = "notTheFakeName";
|
||||
TestUtils.Simulate.change(input);
|
||||
TestUtils.Simulate.keyDown(input, {
|
||||
key: "Enter",
|
||||
which: 13
|
||||
});
|
||||
|
||||
sinon.assert.called(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch, new sharedActions.UpdateRoomContext({
|
||||
roomToken: "fakeToken",
|
||||
newRoomName: "notTheFakeName"
|
||||
}));
|
||||
});
|
||||
|
||||
it("should NOT update the room name when name is unchanged" +
|
||||
" and Enter key is pressed", function() {
|
||||
view = mountTestComponent("fakeName", "fakeToken");
|
||||
var input = view.getDOMNode().querySelector("input");
|
||||
|
||||
input.value = "fakeName";
|
||||
TestUtils.Simulate.change(input);
|
||||
TestUtils.Simulate.keyDown(input, {
|
||||
key: "Enter",
|
||||
which: 13
|
||||
});
|
||||
|
||||
sinon.assert.notCalled(dispatcher.dispatch);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -59,6 +59,7 @@ describe("loop.store.RoomStore", function() {
|
||||
if (prefName === "debug.dispatcher") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
NotifyUITour: function() {},
|
||||
OpenURL: sinon.stub(),
|
||||
@ -73,6 +74,7 @@ describe("loop.store.RoomStore", function() {
|
||||
"Rooms:Rename": sinon.stub(),
|
||||
"Rooms:PushSubscription": sinon.stub(),
|
||||
"SetLoopPref": sinon.stub(),
|
||||
"SetNameNewRoom": sinon.stub(),
|
||||
TelemetryAddValue: sinon.stub()
|
||||
});
|
||||
|
||||
@ -178,6 +180,49 @@ describe("loop.store.RoomStore", function() {
|
||||
});
|
||||
|
||||
describe("close", function() {
|
||||
it("should request rename if closing room is last created", function() {
|
||||
store.setStoreState({
|
||||
openedRoom: "fakeToken",
|
||||
lastCreatedRoom: "fakeToken"
|
||||
});
|
||||
|
||||
sinon.stub(store, "setStoreState");
|
||||
|
||||
LoopMochaUtils.publish("Rooms:Close");
|
||||
|
||||
sinon.assert.calledTwice(store.setStoreState);
|
||||
sinon.assert.calledWithExactly(store.setStoreState.getCall(0),
|
||||
{ closingNewRoom: true });
|
||||
sinon.assert.calledOnce(requestStubs.SetNameNewRoom);
|
||||
});
|
||||
|
||||
it("should not request rename if last created and opened room are null", function() {
|
||||
store.setStoreState({
|
||||
lastCreatedRoom: null,
|
||||
openedRoom: null
|
||||
});
|
||||
|
||||
sinon.stub(store, "setStoreState");
|
||||
|
||||
LoopMochaUtils.publish("Rooms:Close");
|
||||
|
||||
sinon.assert.notCalled(requestStubs.SetNameNewRoom);
|
||||
});
|
||||
|
||||
it("should end up updating closingNewRoom state to false", function() {
|
||||
store.setStoreState({ closingNewRoom: "true" });
|
||||
LoopMochaUtils.publish("Rooms:Close");
|
||||
|
||||
expect(store.getStoreState().closingNewRoom).to.eql(false);
|
||||
});
|
||||
|
||||
it("should update the lastCreatedRoom state to null", function() {
|
||||
store.setStoreState({ lastCreatedRoom: "fake1234" });
|
||||
LoopMochaUtils.publish("Rooms:Close");
|
||||
|
||||
expect(store.getStoreState().lastCreatedRoom).to.eql(null);
|
||||
});
|
||||
|
||||
it("should update the openedRoom state to null", function() {
|
||||
store.setStoreState({ openedRoom: "fake1234" });
|
||||
LoopMochaUtils.publish("Rooms:Close");
|
||||
@ -287,6 +332,12 @@ describe("loop.store.RoomStore", function() {
|
||||
expect(store.getStoreState().pendingCreation).eql(true);
|
||||
});
|
||||
|
||||
it("should store the room token as the last created", function() {
|
||||
store.createRoom(new sharedActions.CreateRoom(fakeRoomCreationData));
|
||||
|
||||
expect(store.getStoreState().lastCreatedRoom).eql("fakeToken");
|
||||
});
|
||||
|
||||
it("should dispatch a CreatedRoom action once the operation is done",
|
||||
function() {
|
||||
store.createRoom(new sharedActions.CreateRoom(fakeRoomCreationData));
|
||||
@ -357,9 +408,9 @@ describe("loop.store.RoomStore", function() {
|
||||
sinon.assert.calledWithExactly(requestStubs.TelemetryAddValue,
|
||||
"LOOP_ROOM_CREATE", 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#createdRoom", function() {
|
||||
describe("#createdRoom", function() {
|
||||
beforeEach(function() {
|
||||
sandbox.stub(dispatcher, "dispatch");
|
||||
});
|
||||
@ -608,7 +659,6 @@ describe("loop.store.RoomStore", function() {
|
||||
sinon.assert.calledWithExactly(requestStubs.TelemetryAddValue.getCall(0),
|
||||
"LOOP_SHARING_ROOM_URL", 4);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("#shareRoomUrl", function() {
|
||||
@ -699,6 +749,15 @@ describe("loop.store.RoomStore", function() {
|
||||
expect(store.getStoreState().rooms).to.have.length.of(3);
|
||||
});
|
||||
|
||||
it("should not fetch the room list if called a second time", function() {
|
||||
requestStubs["Rooms:GetAll"].returns(fakeRoomList);
|
||||
|
||||
store.getAllRooms(new sharedActions.GetAllRooms());
|
||||
store.getAllRooms(new sharedActions.GetAllRooms());
|
||||
|
||||
sinon.assert.calledOnce(requestStubs["Rooms:GetAll"]);
|
||||
});
|
||||
|
||||
it("should order the room list using ctime desc", function() {
|
||||
requestStubs["Rooms:GetAll"].returns(fakeRoomList);
|
||||
|
||||
|
@ -120,7 +120,7 @@
|
||||
if ((ret == undefined) || (ret == "")) {
|
||||
// Display a message in the error console
|
||||
console.error("Index #" + index + " of '" + str + "' for value " + num +
|
||||
" is invalid -- plural rule #" + aRuleNum);
|
||||
" is invalid -- plural rule #" + ret);
|
||||
|
||||
// Default to the first entry (which might be empty, but not undefined).
|
||||
ret = words[0];
|
||||
@ -210,7 +210,15 @@
|
||||
// Fallback to a working - synchronous - implementation of retrieving the
|
||||
// plural form of a string.
|
||||
if (!gL10nDetails.getPluralForm && ("pluralRule" in gL10nDetails)) {
|
||||
gPluralFunc = kPluralFunctions[gL10nDetails.pluralRule][1];
|
||||
var ruleNum = gL10nDetails.pluralRule;
|
||||
|
||||
// Default to "all plural" if the value is out of bounds or invalid
|
||||
if (ruleNum < 0 || ruleNum >= kPluralFunctions.length || isNaN(ruleNum)) {
|
||||
console.error(["Invalid rule number: ", ruleNum, " -- defaulting to 0"]);
|
||||
ruleNum = 0;
|
||||
}
|
||||
|
||||
gPluralFunc = kPluralFunctions[ruleNum][1];
|
||||
gL10nDetails.getPluralForm = fallbackGetPluralForm;
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,9 @@ pref("loop.retry_delay.start", 60000);
|
||||
pref("loop.retry_delay.limit", 300000);
|
||||
pref("loop.ping.interval", 1800000);
|
||||
pref("loop.ping.timeout", 10000);
|
||||
pref("loop.copy.shown", false);
|
||||
pref("loop.copy.throttler", "copy.loop.services.mozilla.com");
|
||||
pref("loop.copy.ticket", -1);
|
||||
pref("loop.debug.loglevel", "Error");
|
||||
pref("loop.debug.dispatcher", false);
|
||||
pref("loop.debug.sdk", false);
|
||||
|
@ -545,10 +545,6 @@ html[dir="rtl"] .context-content {
|
||||
}
|
||||
|
||||
.context-wrapper {
|
||||
border: 2px solid #ebebeb;
|
||||
border-radius: 4px;
|
||||
background: #fafafa;
|
||||
padding: 1.1rem .8rem;
|
||||
/* Use the flex row mode to position the elements next to each other. */
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
@ -556,6 +552,15 @@ html[dir="rtl"] .context-content {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.context-content > .context-wrapper {
|
||||
border: 2px solid #ebebeb;
|
||||
border-radius: 4px;
|
||||
background: #fafafa;
|
||||
padding: 1.1rem .8rem;
|
||||
font-size: 1.3rem;
|
||||
line-height: 1.4rem;
|
||||
}
|
||||
|
||||
.context-wrapper > .context-preview {
|
||||
float: left;
|
||||
/* 16px is standard height/width for a favicon */
|
||||
|
@ -196,6 +196,7 @@ loop.shared.actions = (function() {
|
||||
* A stream from local or remote media has been created.
|
||||
*/
|
||||
MediaStreamCreated: Action.define("mediaStreamCreated", {
|
||||
hasAudio: Boolean,
|
||||
hasVideo: Boolean,
|
||||
isLocal: Boolean,
|
||||
srcMediaElement: Object
|
||||
|
@ -109,6 +109,7 @@ loop.store.ActiveRoomStore = (function(mozL10n) {
|
||||
"localVideoDimensions",
|
||||
"mediaConnected",
|
||||
"receivingScreenShare",
|
||||
"remoteAudioEnabled",
|
||||
"remotePeerDisconnected",
|
||||
"remoteSrcMediaElement",
|
||||
"remoteVideoDimensions",
|
||||
@ -130,6 +131,7 @@ loop.store.ActiveRoomStore = (function(mozL10n) {
|
||||
roomState: ROOM_STATES.INIT,
|
||||
audioMuted: false,
|
||||
videoMuted: false,
|
||||
remoteAudioEnabled: false,
|
||||
remoteVideoEnabled: false,
|
||||
failureReason: undefined,
|
||||
// Whether or not Firefox can handle this room in the conversation
|
||||
@ -808,6 +810,7 @@ loop.store.ActiveRoomStore = (function(mozL10n) {
|
||||
mediaStreamCreated: function(actionData) {
|
||||
if (actionData.isLocal) {
|
||||
this.setStoreState({
|
||||
localAudioEnabled: actionData.hasAudio,
|
||||
localVideoEnabled: actionData.hasVideo,
|
||||
localSrcMediaElement: actionData.srcMediaElement
|
||||
});
|
||||
@ -815,6 +818,7 @@ loop.store.ActiveRoomStore = (function(mozL10n) {
|
||||
}
|
||||
|
||||
this.setStoreState({
|
||||
remoteAudioEnabled: actionData.hasAudio,
|
||||
remoteVideoEnabled: actionData.hasVideo,
|
||||
remoteSrcMediaElement: actionData.srcMediaElement
|
||||
});
|
||||
@ -1099,7 +1103,9 @@ loop.store.ActiveRoomStore = (function(mozL10n) {
|
||||
* @param {sharedActions.LeaveRoom} actionData
|
||||
*/
|
||||
leaveRoom: function(actionData) {
|
||||
this._leaveRoom(ROOM_STATES.ENDED, false, actionData && actionData.windowStayingOpen);
|
||||
this._leaveRoom(ROOM_STATES.ENDED,
|
||||
false,
|
||||
actionData && actionData.windowStayingOpen);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,3 @@
|
||||
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
@ -91,10 +89,14 @@ loop.shared.views.LinkifiedTextView = function () {
|
||||
// Bug 1196143 - formatURL sanitizes(decodes) the URL from IDN homographic attacks.
|
||||
sanitizeURL = loop.shared.utils.formatURL(result[0]);
|
||||
if (sanitizeURL && sanitizeURL.location) {
|
||||
var linkAttributes = this._generateLinkAttributes(sanitizeURL.location);
|
||||
elements.push(React.createElement(
|
||||
"a",
|
||||
_extends({}, this._generateLinkAttributes(sanitizeURL.location), {
|
||||
key: reactElementsCounter++ }),
|
||||
{ href: linkAttributes.href,
|
||||
key: reactElementsCounter++,
|
||||
onClick: linkAttributes.onClick,
|
||||
rel: linkAttributes.rel,
|
||||
target: linkAttributes.target },
|
||||
sanitizeURL.location
|
||||
));
|
||||
} else {
|
||||
|
@ -640,6 +640,7 @@ loop.OTSdkDriver = (function() {
|
||||
sdkSubscriberObject.on("videoDisabled", this._onVideoDisabled.bind(this));
|
||||
|
||||
this.dispatcher.dispatch(new sharedActions.MediaStreamCreated({
|
||||
hasAudio: sdkSubscriberObject.stream[STREAM_PROPERTIES.HAS_AUDIO],
|
||||
hasVideo: sdkSubscriberObject.stream[STREAM_PROPERTIES.HAS_VIDEO],
|
||||
isLocal: false,
|
||||
srcMediaElement: sdkSubscriberVideo
|
||||
@ -850,8 +851,10 @@ loop.OTSdkDriver = (function() {
|
||||
|
||||
var sdkLocalVideo = this._mockPublisherEl.querySelector("video");
|
||||
var hasVideo = event.stream[STREAM_PROPERTIES.HAS_VIDEO];
|
||||
var hasAudio = event.stream[STREAM_PROPERTIES.HAS_AUDIO];
|
||||
|
||||
this.dispatcher.dispatch(new sharedActions.MediaStreamCreated({
|
||||
hasAudio: hasAudio,
|
||||
hasVideo: hasVideo,
|
||||
isLocal: true,
|
||||
srcMediaElement: sdkLocalVideo
|
||||
@ -998,20 +1001,28 @@ loop.OTSdkDriver = (function() {
|
||||
// Nothing to do for the success case.
|
||||
return;
|
||||
}
|
||||
if (!(error.message && error.message === "DENIED")) {
|
||||
// We free up the publisher here in case the store wants to try
|
||||
// grabbing the media again.
|
||||
if (this.publisher) {
|
||||
this.publisher.off("accessAllowed accessDenied accessDialogOpened streamCreated");
|
||||
this.publisher.destroy();
|
||||
delete this.publisher;
|
||||
delete this._mockPublisherEl;
|
||||
}
|
||||
this.dispatcher.dispatch(new sharedActions.ConnectionFailure({
|
||||
reason: FAILURE_DETAILS.UNABLE_TO_PUBLISH_MEDIA
|
||||
}));
|
||||
this._notifyMetricsEvent("sdk.exception." + error.code + "." + error.message);
|
||||
|
||||
if (error.message && error.message === "DENIED") {
|
||||
// In the DENIED case, this will be handled by _onPublishDenied.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.publisher) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We free up the publisher here in case the store wants to try
|
||||
// grabbing the media again.
|
||||
this.publisher.off("accessAllowed accessDenied accessDialogOpened streamCreated");
|
||||
this.publisher.destroy();
|
||||
delete this.publisher;
|
||||
delete this._mockPublisherEl;
|
||||
|
||||
this.dispatcher.dispatch(new sharedActions.ConnectionFailure({
|
||||
reason: FAILURE_DETAILS.UNABLE_TO_PUBLISH_MEDIA
|
||||
}));
|
||||
|
||||
// Exceptions are logged via the _onOTException handler.
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1030,6 +1041,11 @@ loop.OTSdkDriver = (function() {
|
||||
delete this._mockPublisherEl;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles exceptions being raised by the OT SDK.
|
||||
*
|
||||
* @param {OT.Event} event
|
||||
*/
|
||||
_onOTException: function(event) {
|
||||
switch (event.code) {
|
||||
case OT.ExceptionCodes.PUBLISHER_ICE_WORKFLOW_FAILED:
|
||||
@ -1047,6 +1063,17 @@ loop.OTSdkDriver = (function() {
|
||||
// attempt failed.
|
||||
this._notifyMetricsEvent("sdk.exception." + event.code);
|
||||
break;
|
||||
case OT.ExceptionCodes.UNABLE_TO_PUBLISH:
|
||||
// Don't report errors for GetUserMedia events as these are expected if
|
||||
// the user denies the prompt.
|
||||
if (event.message !== "GetUserMedia") {
|
||||
var baseException = "sdk.exception.";
|
||||
if (event.target && event.target === this.screenshare) {
|
||||
baseException += "screen.";
|
||||
}
|
||||
this._notifyMetricsEvent(baseException + event.code + "." + event.message);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
this._notifyMetricsEvent("sdk.exception." + event.code);
|
||||
break;
|
||||
@ -1164,7 +1191,8 @@ loop.OTSdkDriver = (function() {
|
||||
this.dispatcher.dispatch(new sharedActions.ScreenSharingState({
|
||||
state: SCREEN_SHARE_STATES.INACTIVE
|
||||
}));
|
||||
this._notifyMetricsEvent("sdk.exception.screen." + error.code + "." + error.message);
|
||||
|
||||
// Exceptions are logged via the _onOTException handler.
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,3 @@
|
||||
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
@ -60,13 +58,6 @@ loop.shared.views.chat = function (mozL10n) {
|
||||
"text-chat-notif": this.props.contentType === CHAT_CONTENT_TYPES.NOTIFICATION
|
||||
});
|
||||
|
||||
var optionalProps = {};
|
||||
if (loop.shared.utils.isDesktop()) {
|
||||
optionalProps.linkClickHandler = function (url) {
|
||||
loop.request("OpenURL", url);
|
||||
};
|
||||
}
|
||||
|
||||
if (this.props.contentType === CHAT_CONTENT_TYPES.CONTEXT_TILE) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
@ -99,11 +90,19 @@ loop.shared.views.chat = function (mozL10n) {
|
||||
);
|
||||
}
|
||||
|
||||
var linkClickHandler;
|
||||
if (loop.shared.utils.isDesktop()) {
|
||||
linkClickHandler = function (url) {
|
||||
loop.request("OpenURL", url);
|
||||
};
|
||||
}
|
||||
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ className: classes },
|
||||
React.createElement(sharedViews.LinkifiedTextView, _extends({}, optionalProps, {
|
||||
rawText: this.props.message })),
|
||||
React.createElement(sharedViews.LinkifiedTextView, {
|
||||
linkClickHandler: linkClickHandler,
|
||||
rawText: this.props.message }),
|
||||
React.createElement("span", { className: "text-chat-arrow" }),
|
||||
this.props.showTimestamp ? this._renderTimestamp() : null
|
||||
);
|
||||
@ -216,11 +215,6 @@ loop.shared.views.chat = function (mozL10n) {
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "text-chat-scroller" },
|
||||
loop.shared.utils.isDesktop() ? null : React.createElement(
|
||||
"p",
|
||||
{ className: "welcome-message" },
|
||||
mozL10n.get("rooms_welcome_text_chat_label", { clientShortname: mozL10n.get("clientShortname2") })
|
||||
),
|
||||
this.props.messageList.map(function (entry, i) {
|
||||
if (entry.type === CHAT_MESSAGE_TYPES.SPECIAL) {
|
||||
if (!this.props.showInitialContext) {
|
||||
|
@ -427,6 +427,37 @@ if (inChrome) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Formats a url for context url links.
|
||||
*
|
||||
* @param {String} url The url to format.
|
||||
* @return {Object} Sanitized url object containing the hostname,
|
||||
* full location and protocol
|
||||
*/
|
||||
function formatSanitizedContextURL(url) {
|
||||
if (!url) {
|
||||
return null;
|
||||
}
|
||||
// Bug 1196143 - formatURL sanitizes(decodes) the URL from IDN homographic attacks.
|
||||
// Try catch to not produce output if invalid url
|
||||
try {
|
||||
var sanitizedURL = loop.shared.utils.formatURL(url, true);
|
||||
} catch (ex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Only allow specific types of URLs.
|
||||
if (!sanitizedURL ||
|
||||
(sanitizedURL.protocol !== "http:" &&
|
||||
sanitizedURL.protocol !== "https:" &&
|
||||
sanitizedURL.protocol !== "ftp:")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return sanitizedURL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and opens a mailto: url with call URL information prefilled.
|
||||
* Note: This only works for Desktop.
|
||||
@ -795,6 +826,7 @@ if (inChrome) {
|
||||
composeCallUrlEmail: composeCallUrlEmail,
|
||||
findParentNode: findParentNode,
|
||||
formatDate: formatDate,
|
||||
formatSanitizedContextURL: formatSanitizedContextURL,
|
||||
formatURL: formatURL,
|
||||
getBoolPreference: getBoolPreference,
|
||||
getOS: getOS,
|
||||
|
@ -1,5 +1,3 @@
|
||||
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
@ -54,6 +52,7 @@ loop.shared.views = function (_, mozL10n) {
|
||||
|
||||
propTypes: {
|
||||
action: React.PropTypes.func.isRequired,
|
||||
disabled: React.PropTypes.bool,
|
||||
muted: React.PropTypes.bool.isRequired,
|
||||
scope: React.PropTypes.string.isRequired,
|
||||
title: React.PropTypes.string,
|
||||
@ -62,7 +61,11 @@ loop.shared.views = function (_, mozL10n) {
|
||||
},
|
||||
|
||||
getDefaultProps: function () {
|
||||
return { muted: false, visible: true };
|
||||
return {
|
||||
disabled: false,
|
||||
muted: false,
|
||||
visible: true
|
||||
};
|
||||
},
|
||||
|
||||
handleClick: function () {
|
||||
@ -77,8 +80,9 @@ loop.shared.views = function (_, mozL10n) {
|
||||
"media-control": true,
|
||||
"transparent-button": true,
|
||||
"local-media": this.props.scope === "local",
|
||||
"muted": this.props.muted,
|
||||
"hide": !this.props.visible
|
||||
"muted": this.props.muted || this.props.disabled,
|
||||
"hide": !this.props.visible,
|
||||
"disabled": this.props.disabled
|
||||
};
|
||||
classesObj["btn-mute-" + this.props.type] = true;
|
||||
return cx(classesObj);
|
||||
@ -89,7 +93,7 @@ loop.shared.views = function (_, mozL10n) {
|
||||
return this.props.title;
|
||||
}
|
||||
|
||||
var prefix = this.props.muted ? "unmute" : "mute";
|
||||
var prefix = this.props.muted || this.props.disabled ? "unmute" : "mute";
|
||||
var suffix = this.props.type === "video" ? "button_title2" : "button_title";
|
||||
var msgId = [prefix, this.props.scope, this.props.type, suffix].join("_");
|
||||
return mozL10n.get(msgId);
|
||||
@ -235,16 +239,28 @@ loop.shared.views = function (_, mozL10n) {
|
||||
displayName: "AudioMuteButton",
|
||||
|
||||
propTypes: {
|
||||
disabled: React.PropTypes.bool,
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher),
|
||||
muted: React.PropTypes.bool.isRequired
|
||||
},
|
||||
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
disabled: false
|
||||
};
|
||||
},
|
||||
|
||||
toggleAudio: function () {
|
||||
if (this.props.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.dispatcher.dispatch(new sharedActions.SetMute({ type: "audio", enabled: this.props.muted }));
|
||||
},
|
||||
|
||||
render: function () {
|
||||
return React.createElement(MediaControlButton, { action: this.toggleAudio,
|
||||
disabled: this.props.disabled,
|
||||
muted: this.props.muted,
|
||||
scope: "local",
|
||||
type: "audio" });
|
||||
@ -255,17 +271,29 @@ loop.shared.views = function (_, mozL10n) {
|
||||
displayName: "VideoMuteButton",
|
||||
|
||||
propTypes: {
|
||||
disabled: React.PropTypes.bool,
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher),
|
||||
muted: React.PropTypes.bool.isRequired
|
||||
},
|
||||
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
disabled: false
|
||||
};
|
||||
},
|
||||
|
||||
toggleVideo: function () {
|
||||
if (this.props.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.dispatcher.dispatch(new sharedActions.SetMute({ type: "video", enabled: this.props.muted }));
|
||||
},
|
||||
|
||||
render: function () {
|
||||
return React.createElement(MediaControlButton, { action: this.toggleVideo,
|
||||
muted: this.props.muted,
|
||||
disabled: this.props.disabled,
|
||||
muted: this.props.muted || this.props.disabled,
|
||||
scope: "local",
|
||||
type: "video" });
|
||||
}
|
||||
@ -560,6 +588,63 @@ loop.shared.views = function (_, mozL10n) {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Renders the a href link for context.
|
||||
*
|
||||
* @property {Boolean} allowClick Set to true to allow the url to be clicked.
|
||||
* @property {String} description The description for the context url.
|
||||
* @property {Function} handleClick Function for handling click operation when
|
||||
* context link is clicked
|
||||
* @property {String} thumbnail The thumbnail url (expected to be a data url) to
|
||||
* display. If not specified, a fallback url will be
|
||||
* shown.
|
||||
* @property {String} url The url to be displayed. If not present or invalid,
|
||||
* the context link will not be clickable
|
||||
*/
|
||||
var ContextUrlLink = React.createClass({
|
||||
displayName: "ContextUrlLink",
|
||||
|
||||
mixins: [React.addons.PureRenderMixin],
|
||||
|
||||
propTypes: {
|
||||
allowClick: React.PropTypes.bool.isRequired,
|
||||
children: React.PropTypes.node,
|
||||
description: React.PropTypes.string,
|
||||
handleClick: React.PropTypes.func,
|
||||
url: React.PropTypes.string
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var sanitizedURL = loop.shared.utils.formatSanitizedContextURL(this.props.url);
|
||||
|
||||
var opts = {};
|
||||
opts.classNames = classNames({
|
||||
"context-wrapper": true,
|
||||
"clicks-allowed": this.props.allowClick
|
||||
});
|
||||
if (this.props.allowClick && sanitizedURL) {
|
||||
opts.href = sanitizedURL.location;
|
||||
}
|
||||
if (this.props.handleClick) {
|
||||
opts.onClick = this.props.handleClick;
|
||||
}
|
||||
if (this.props.description) {
|
||||
opts.title = this.props.description;
|
||||
}
|
||||
|
||||
return React.createElement(
|
||||
"a",
|
||||
{ className: opts.classNames,
|
||||
href: opts.href,
|
||||
onClick: opts.onClick,
|
||||
rel: "noreferrer",
|
||||
target: "_blank",
|
||||
title: opts.title },
|
||||
this.props.children
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Renders a url that's part of context on the display.
|
||||
*
|
||||
@ -580,7 +665,7 @@ loop.shared.views = function (_, mozL10n) {
|
||||
|
||||
propTypes: {
|
||||
allowClick: React.PropTypes.bool.isRequired,
|
||||
description: React.PropTypes.string.isRequired,
|
||||
description: React.PropTypes.string,
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher),
|
||||
thumbnail: React.PropTypes.string,
|
||||
url: React.PropTypes.string
|
||||
@ -600,49 +685,34 @@ loop.shared.views = function (_, mozL10n) {
|
||||
},
|
||||
|
||||
render: function () {
|
||||
// Bug 1196143 - formatURL sanitizes(decodes) the URL from IDN homographic attacks.
|
||||
// Try catch to not produce output if invalid url
|
||||
try {
|
||||
var sanitizedURL = loop.shared.utils.formatURL(this.props.url, true);
|
||||
} catch (ex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Only allow specific types of URLs.
|
||||
if (!sanitizedURL || sanitizedURL.protocol !== "http:" && sanitizedURL.protocol !== "https:" && sanitizedURL.protocol !== "ftp:") {
|
||||
return null;
|
||||
}
|
||||
|
||||
var description = this.props.description || null;
|
||||
var thumbnail = this.props.thumbnail;
|
||||
var url = this.props.url || null;
|
||||
var sanitizedURL = loop.shared.utils.formatSanitizedContextURL(url) || {};
|
||||
var hostname = sanitizedURL.hostname || null;
|
||||
|
||||
if (!thumbnail) {
|
||||
thumbnail = "shared/img/icons-16x16.svg#globe";
|
||||
}
|
||||
|
||||
var wrapperClasses = classNames({
|
||||
"context-wrapper": true,
|
||||
"clicks-allowed": this.props.allowClick
|
||||
});
|
||||
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ className: "context-content" },
|
||||
React.createElement(
|
||||
"a",
|
||||
{ className: wrapperClasses,
|
||||
href: this.props.allowClick ? this.props.url : null,
|
||||
onClick: this.handleLinkClick,
|
||||
rel: "noreferrer",
|
||||
target: "_blank" },
|
||||
ContextUrlLink,
|
||||
{ allowClick: this.props.allowClick,
|
||||
description: description,
|
||||
handleClick: this.handleLinkClick,
|
||||
url: url },
|
||||
React.createElement("img", { className: "context-preview", src: thumbnail }),
|
||||
React.createElement(
|
||||
"span",
|
||||
{ className: "context-info" },
|
||||
this.props.description,
|
||||
description,
|
||||
React.createElement(
|
||||
"span",
|
||||
{ className: "context-url" },
|
||||
sanitizedURL.hostname
|
||||
hostname
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -834,11 +904,6 @@ loop.shared.views = function (_, mozL10n) {
|
||||
return React.createElement("div", { className: "no-video" });
|
||||
}
|
||||
|
||||
var optionalProps = {};
|
||||
if (this.props.posterUrl) {
|
||||
optionalProps.poster = this.props.posterUrl;
|
||||
}
|
||||
|
||||
// For now, always mute media. For local media, we should be muted anyway,
|
||||
// as we don't want to hear ourselves speaking.
|
||||
//
|
||||
@ -853,9 +918,9 @@ loop.shared.views = function (_, mozL10n) {
|
||||
{ className: "remote-video-box" },
|
||||
this.state.videoElementSize && this.props.shareCursor ? React.createElement(RemoteCursorView, {
|
||||
videoElementSize: this.state.videoElementSize }) : null,
|
||||
React.createElement("video", _extends({}, optionalProps, {
|
||||
className: this.props.mediaType + "-video",
|
||||
muted: true }))
|
||||
React.createElement("video", { className: this.props.mediaType + "-video",
|
||||
muted: true,
|
||||
poster: this.props.posterUrl })
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -1232,6 +1297,7 @@ loop.shared.views = function (_, mozL10n) {
|
||||
Button: Button,
|
||||
ButtonGroup: ButtonGroup,
|
||||
Checkbox: Checkbox,
|
||||
ContextUrlLink: ContextUrlLink,
|
||||
ContextUrlView: ContextUrlView,
|
||||
ConversationToolbar: ConversationToolbar,
|
||||
HangUpControlButton: HangUpControlButton,
|
||||
|
@ -1352,6 +1352,7 @@ describe("loop.store.ActiveRoomStore", function() {
|
||||
expect(store.getStoreState()).to.not.have.property("localSrcMediaElement");
|
||||
|
||||
store.mediaStreamCreated(new sharedActions.MediaStreamCreated({
|
||||
hasAudio: false,
|
||||
hasVideo: false,
|
||||
isLocal: true,
|
||||
srcMediaElement: fakeStreamElement
|
||||
@ -1363,17 +1364,20 @@ describe("loop.store.ActiveRoomStore", function() {
|
||||
|
||||
it("should set the local video enabled", function() {
|
||||
store.setStoreState({
|
||||
localAudioEnabled: false,
|
||||
localVideoEnabled: false,
|
||||
remoteVideoEnabled: false
|
||||
});
|
||||
|
||||
store.mediaStreamCreated(new sharedActions.MediaStreamCreated({
|
||||
hasAudio: true,
|
||||
hasVideo: true,
|
||||
isLocal: true,
|
||||
srcMediaElement: fakeStreamElement
|
||||
}));
|
||||
|
||||
expect(store.getStoreState().localVideoEnabled).eql(true);
|
||||
expect(store.getStoreState().localAudioEnabled).eql(true);
|
||||
expect(store.getStoreState().remoteVideoEnabled).eql(false);
|
||||
});
|
||||
|
||||
@ -1381,6 +1385,7 @@ describe("loop.store.ActiveRoomStore", function() {
|
||||
expect(store.getStoreState()).to.not.have.property("remoteSrcMediaElement");
|
||||
|
||||
store.mediaStreamCreated(new sharedActions.MediaStreamCreated({
|
||||
hasAudio: false,
|
||||
hasVideo: false,
|
||||
isLocal: false,
|
||||
srcMediaElement: fakeStreamElement
|
||||
@ -1397,6 +1402,7 @@ describe("loop.store.ActiveRoomStore", function() {
|
||||
});
|
||||
|
||||
store.mediaStreamCreated(new sharedActions.MediaStreamCreated({
|
||||
hasAudio: true,
|
||||
hasVideo: true,
|
||||
isLocal: false,
|
||||
srcMediaElement: fakeStreamElement
|
||||
@ -1404,6 +1410,7 @@ describe("loop.store.ActiveRoomStore", function() {
|
||||
|
||||
expect(store.getStoreState().localVideoEnabled).eql(false);
|
||||
expect(store.getStoreState().remoteVideoEnabled).eql(true);
|
||||
expect(store.getStoreState().remoteAudioEnabled).eql(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -34,6 +34,7 @@ REDIRECTIONS = {
|
||||
"/shared/vendor": "/chrome/content/shared/vendor",
|
||||
"/add-on/panels/vendor": "/chrome/content/panels/vendor",
|
||||
"/add-on/panels/js": "/chrome/content/panels/js",
|
||||
"/add-on/panels/test": "/chrome/content/panels/test",
|
||||
"/add-on/shared/js": "/chrome/content/shared/js",
|
||||
"/add-on/shared/vendor": "/chrome/content/shared/vendor",
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ describe("loop.shared.views.LinkifiedTextView", function() {
|
||||
var markup = renderToMarkup("http://example.com", {});
|
||||
|
||||
expect(markup).to.equal(
|
||||
'<p><a href="http://example.com/" target="_blank" rel="noreferrer">http://example.com/</a></p>');
|
||||
'<p><a href="http://example.com/" rel="noreferrer" target="_blank">http://example.com/</a></p>');
|
||||
});
|
||||
});
|
||||
|
||||
@ -94,7 +94,7 @@ describe("loop.shared.views.LinkifiedTextView", function() {
|
||||
var markup = renderToMarkup("http://example.com", {});
|
||||
|
||||
expect(markup).to.equal(
|
||||
'<p><a href="http://example.com/" target="_blank" rel="noreferrer">http://example.com/</a></p>');
|
||||
'<p><a href="http://example.com/" rel="noreferrer" target="_blank">http://example.com/</a></p>');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -154,6 +154,7 @@ var LoopMochaUtils = (function(global, _) {
|
||||
}
|
||||
invokeListenerCallbacks({ data: [seq, result] });
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -223,25 +223,12 @@ describe("loop.OTSdkDriver", function() {
|
||||
it("should dispatch ConnectionFailure if an error occurred", function() {
|
||||
sdk.initPublisher.callArgWith(2, { message: "FAKE" });
|
||||
|
||||
sinon.assert.calledTwice(dispatcher.dispatch);
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.ConnectionFailure({
|
||||
reason: FAILURE_DETAILS.UNABLE_TO_PUBLISH_MEDIA
|
||||
}));
|
||||
});
|
||||
|
||||
it("should notify metrics if an error occurred", function() {
|
||||
sdk.initPublisher.callArgWith(2, { code: 123, message: "FAKE" });
|
||||
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.ConnectionStatus({
|
||||
event: "sdk.exception.123.FAKE",
|
||||
state: "starting",
|
||||
connections: 0,
|
||||
sendStreams: 0,
|
||||
recvStreams: 0
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("#setMute", function() {
|
||||
@ -315,25 +302,12 @@ describe("loop.OTSdkDriver", function() {
|
||||
it("should dispatch ConnectionFailure if an error occurred", function() {
|
||||
sdk.initPublisher.callArgWith(2, { code: 123, message: "FAKE" });
|
||||
|
||||
sinon.assert.calledTwice(dispatcher.dispatch);
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.ScreenSharingState({
|
||||
state: SCREEN_SHARE_STATES.INACTIVE
|
||||
}));
|
||||
});
|
||||
|
||||
it("should notify metrics if an error occurred", function() {
|
||||
sdk.initPublisher.callArgWith(2, { code: 123, message: "FAKE" });
|
||||
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.ConnectionStatus({
|
||||
event: "sdk.exception.screen.123.FAKE",
|
||||
state: "starting",
|
||||
connections: 0,
|
||||
sendStreams: 0,
|
||||
recvStreams: 0
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("Screenshare Access Denied", function() {
|
||||
@ -879,6 +853,7 @@ describe("loop.OTSdkDriver", function() {
|
||||
beforeEach(function() {
|
||||
fakeConnection = "fakeConnection";
|
||||
fakeStream = {
|
||||
hasAudio: true,
|
||||
hasVideo: true,
|
||||
videoType: "camera",
|
||||
videoDimensions: { width: 1, height: 2 }
|
||||
@ -1037,6 +1012,7 @@ describe("loop.OTSdkDriver", function() {
|
||||
|
||||
driver._mockPublisherEl.appendChild(fakeMockVideo);
|
||||
stream = {
|
||||
hasAudio: true,
|
||||
hasVideo: true,
|
||||
videoType: "camera",
|
||||
videoDimensions: { width: 1, height: 2 }
|
||||
@ -1061,6 +1037,7 @@ describe("loop.OTSdkDriver", function() {
|
||||
sinon.assert.called(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.MediaStreamCreated({
|
||||
hasAudio: true,
|
||||
hasVideo: true,
|
||||
isLocal: true,
|
||||
srcMediaElement: fakeMockVideo
|
||||
@ -1074,6 +1051,7 @@ describe("loop.OTSdkDriver", function() {
|
||||
sinon.assert.called(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.MediaStreamCreated({
|
||||
hasAudio: true,
|
||||
hasVideo: false,
|
||||
isLocal: true,
|
||||
srcMediaElement: fakeMockVideo
|
||||
@ -1147,12 +1125,14 @@ describe("loop.OTSdkDriver", function() {
|
||||
driver.session = session;
|
||||
fakeStream.connection = fakeConnection;
|
||||
fakeStream.hasVideo = true;
|
||||
fakeStream.hasAudio = true;
|
||||
|
||||
session.trigger("streamCreated", { stream: fakeStream });
|
||||
|
||||
sinon.assert.called(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.MediaStreamCreated({
|
||||
hasAudio: true,
|
||||
hasVideo: true,
|
||||
isLocal: false,
|
||||
srcMediaElement: videoElement
|
||||
@ -1168,6 +1148,7 @@ describe("loop.OTSdkDriver", function() {
|
||||
sinon.assert.called(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.MediaStreamCreated({
|
||||
hasAudio: true,
|
||||
hasVideo: false,
|
||||
isLocal: false,
|
||||
srcMediaElement: videoElement
|
||||
@ -1893,6 +1874,56 @@ describe("loop.OTSdkDriver", function() {
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("Unable to Publish", function() {
|
||||
it("should not do anything if the message is 'GetUserMedia'", function() {
|
||||
sdk.trigger("exception", {
|
||||
code: OT.ExceptionCodes.UNABLE_TO_PUBLISH,
|
||||
message: "GetUserMedia"
|
||||
});
|
||||
|
||||
sinon.assert.notCalled(dispatcher.dispatch);
|
||||
});
|
||||
|
||||
it("should notify metrics", function() {
|
||||
sdk.trigger("exception", {
|
||||
code: OT.ExceptionCodes.UNABLE_TO_PUBLISH,
|
||||
message: "General Media Fail"
|
||||
});
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.ConnectionStatus({
|
||||
event: "sdk.exception." + OT.ExceptionCodes.UNABLE_TO_PUBLISH +
|
||||
".General Media Fail",
|
||||
state: "starting",
|
||||
connections: 0,
|
||||
sendStreams: 0,
|
||||
recvStreams: 0
|
||||
}));
|
||||
});
|
||||
|
||||
it("should notify metrics with a special screen indication for screen shares", function() {
|
||||
driver.screenshare = { fake: true };
|
||||
|
||||
sdk.trigger("exception", {
|
||||
code: OT.ExceptionCodes.UNABLE_TO_PUBLISH,
|
||||
message: "General Media Fail 2",
|
||||
target: driver.screenshare
|
||||
});
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.ConnectionStatus({
|
||||
event: "sdk.exception.screen." + OT.ExceptionCodes.UNABLE_TO_PUBLISH +
|
||||
".General Media Fail 2",
|
||||
state: "starting",
|
||||
connections: 0,
|
||||
sendStreams: 0,
|
||||
recvStreams: 0
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -349,6 +349,8 @@ describe("loop.shared.utils", function() {
|
||||
return "body_context";
|
||||
case "share_email_footer2":
|
||||
return "footer";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
});
|
||||
LoopMochaUtils.stubLoopRequest(requestStubs = {
|
||||
|
@ -96,6 +96,34 @@ describe("loop.shared.views", function() {
|
||||
|
||||
expect(comp.getDOMNode().classList.contains("muted")).eql(true);
|
||||
});
|
||||
|
||||
it("should render a muted and disabled local video button", function() {
|
||||
var comp = TestUtils.renderIntoDocument(
|
||||
React.createElement(sharedViews.MediaControlButton, {
|
||||
scope: "local",
|
||||
type: "video",
|
||||
action: function() {},
|
||||
disabled: true,
|
||||
muted: true
|
||||
}));
|
||||
|
||||
expect(comp.getDOMNode().classList.contains("muted")).eql(true);
|
||||
expect(comp.getDOMNode().classList.contains("disabled")).eql(true);
|
||||
});
|
||||
|
||||
it("should render a muted local audio button", function() {
|
||||
var comp = TestUtils.renderIntoDocument(
|
||||
React.createElement(sharedViews.MediaControlButton, {
|
||||
scope: "local",
|
||||
type: "audio",
|
||||
action: function() {},
|
||||
disabled: true,
|
||||
muted: true
|
||||
}));
|
||||
|
||||
expect(comp.getDOMNode().classList.contains("muted")).eql(true);
|
||||
expect(comp.getDOMNode().classList.contains("disabled")).eql(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("AudioMuteButton", function() {
|
||||
@ -453,6 +481,30 @@ describe("loop.shared.views", function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
describe("ContextUrlLink", function() {
|
||||
var view;
|
||||
|
||||
function mountTestComponent(extraProps) {
|
||||
var props = _.extend({
|
||||
allowClick: true,
|
||||
description: "test",
|
||||
url: "http://example.com"
|
||||
}, extraProps);
|
||||
return TestUtils.renderIntoDocument(
|
||||
React.createElement(sharedViews.ContextUrlLink, props));
|
||||
}
|
||||
|
||||
it("should not have any children if none are passed", function() {
|
||||
view = mountTestComponent({
|
||||
allowClick: true,
|
||||
url: "http://wonderful.invalid"
|
||||
});
|
||||
|
||||
var contextHasChildren = view.getDOMNode().childNodes.length;
|
||||
|
||||
expect(contextHasChildren).eql(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("ContextUrlView", function() {
|
||||
var view;
|
||||
@ -475,7 +527,7 @@ describe("loop.shared.views", function() {
|
||||
});
|
||||
|
||||
var wrapper = view.getDOMNode().querySelector(".context-wrapper");
|
||||
|
||||
console.log(view.getDOMNode().querySelector(".context-wrapper").childNodes);
|
||||
expect(wrapper.classList.contains("clicks-allowed")).eql(true);
|
||||
});
|
||||
|
||||
@ -490,29 +542,45 @@ describe("loop.shared.views", function() {
|
||||
expect(wrapper.classList.contains("clicks-allowed")).eql(false);
|
||||
});
|
||||
|
||||
it("should allow context to be clickable if the url is valid", function() {
|
||||
view = mountTestComponent({
|
||||
allowClick: true,
|
||||
url: "http://example.com/"
|
||||
});
|
||||
|
||||
expect(view.getDOMNode().querySelector(".context-wrapper").getAttribute("href"))
|
||||
.eql("http://example.com/");
|
||||
});
|
||||
|
||||
it("should display nothing if the url is invalid", function() {
|
||||
view = mountTestComponent({
|
||||
allowClick: true,
|
||||
url: "fjrTykyw"
|
||||
});
|
||||
|
||||
expect(view.getDOMNode()).eql(null);
|
||||
expect(view.getDOMNode().querySelector(".context-wrapper").getAttribute("href"))
|
||||
.eql(null);
|
||||
});
|
||||
|
||||
it("should display nothing if it is an about url", function() {
|
||||
view = mountTestComponent({
|
||||
allowClick: true,
|
||||
url: "about:config"
|
||||
});
|
||||
|
||||
expect(view.getDOMNode()).eql(null);
|
||||
expect(view.getDOMNode().querySelector(".context-wrapper").getAttribute("href"))
|
||||
.eql(null);
|
||||
});
|
||||
|
||||
it("should display nothing if it is a javascript url", function() {
|
||||
/* eslint-disable no-script-url */
|
||||
view = mountTestComponent({
|
||||
allowClick: true,
|
||||
url: "javascript:alert('hello')"
|
||||
});
|
||||
|
||||
expect(view.getDOMNode()).eql(null);
|
||||
expect(view.getDOMNode().querySelector(".context-wrapper").getAttribute("href"))
|
||||
.eql(null);
|
||||
/* eslint-enable no-script-url */
|
||||
});
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Backbone.js 1.3.2
|
||||
// Backbone.js 1.3.3
|
||||
|
||||
// (c) 2010-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
||||
// Backbone may be freely distributed under the MIT license.
|
||||
@ -44,7 +44,7 @@
|
||||
var slice = Array.prototype.slice;
|
||||
|
||||
// Current version of the library. Keep in sync with `package.json`.
|
||||
Backbone.VERSION = '1.3.2';
|
||||
Backbone.VERSION = '1.3.3';
|
||||
|
||||
// For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns
|
||||
// the `$` variable.
|
||||
|
File diff suppressed because one or more lines are too long
@ -193,16 +193,16 @@ rooms_signout_alert=Açıq söhbətlər qapatılacaq
|
||||
room_name_untitled_page=Adsız Səhifə
|
||||
|
||||
## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
|
||||
door_hanger_return=Hələlik! Bu paylaşılmış sessiyaya istədiyiniz vaxt Hello panelindən qayıda bilərsiz.
|
||||
door_hanger_prompt_name=Rahat yadda qalan ad vermək istərdiniz? Hazırki adı:
|
||||
door_hanger_button=Tamam
|
||||
door_hanger_bye=Hələlik!
|
||||
door_hanger_return2=Bu paylaşılmış sessiyaya istədiyiniz vaxt Hello panelindən qayıda bilərsiz. Buna asan yadda qalan ad vermək istərdiniz?
|
||||
door_hanger_current=Hazırki ad:
|
||||
door_hanger_button2=Tamam!
|
||||
|
||||
# Infobar strings
|
||||
|
||||
infobar_screenshare_no_guest_message=Yoldaşınız daxil olduqda üzərinə kliklədiyiniz hər bir vərəqi görəcəklər.
|
||||
infobar_screenshare_browser_message2=Vərəqlərinizi paylaşırsınız. Üzərinə kliklədiyiniz hər vərəq yoldaşlarınız tərəfindən görünəcək
|
||||
infobar_screenshare_browser_message3=Artıq vərəqlərinizi paylaşırsınız. Yoldaşınız üzərinə kliklədiyiniz hər bir vərəqi görə biləcək.
|
||||
infobar_screenshare_stop_sharing_message=Artıq vərəqlərinizi paylaşmırsınız
|
||||
infobar_button_restart_label2=Paylaşmanı yenidən başlat
|
||||
infobar_button_restart_accesskey=e
|
||||
infobar_button_stop_label2=Paylaşmanı dayandır
|
||||
@ -210,6 +210,13 @@ infobar_button_stop_accesskey=S
|
||||
infobar_button_disconnect_label=Ayrıl
|
||||
infobar_button_disconnect_accesskey=D
|
||||
|
||||
# Copy panel strings
|
||||
|
||||
copy_panel_message=Bu Web səhifəni paylaşmaq istəyirsiz? Səyyah vərəqinizi yoldaşınızla paylaşın.
|
||||
copy_panel_dont_show_again_label=Bunu təkrar göstərmə
|
||||
copy_panel_cancel_button_label=İndi deyil
|
||||
copy_panel_accept_button_label=Bəli, necə edəcəyimi göstər
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
e10s_not_supported_button_label=Yeni Pəncərə Aç
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
# Panel Strings
|
||||
|
||||
|
||||
|
||||
## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
|
||||
## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
|
||||
## use "..." if \u2026 doesn't suit traditions in your locale.
|
||||
@ -31,6 +29,8 @@ panel_disconnect_button=Изключване
|
||||
## First Time Experience Slides
|
||||
## LOCALIZATION_NOTE(fte_slide_1_copy): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
## LOCALIZATION_NOTE(fte_slide_2_copy2): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
## LOCALIZATION_NOTE(fte_slide_3_copy): {{clientSuperShortname}}
|
||||
## will be replaced by the super short brand name.
|
||||
## LOCALIZATION_NOTE(fte_slide_4_title): {{clientSuperShortname}}
|
||||
@ -112,6 +112,9 @@ hangup_button_caption2=Изход
|
||||
infobar_button_disconnect_label=Изключване
|
||||
infobar_button_disconnect_accesskey=И
|
||||
|
||||
# Copy panel strings
|
||||
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
# Panel Strings
|
||||
|
||||
|
||||
|
||||
## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
|
||||
## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
|
||||
## use "..." if \u2026 doesn't suit traditions in your locale.
|
||||
@ -44,8 +42,10 @@ fte_slide_1_title=বন্ধুর সাথে পাতাটি ব্র
|
||||
## LOCALIZATION_NOTE(fte_slide_1_copy): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
fte_slide_1_copy=যখনই আপনি কোন উপহারের জন্য কেনাকাটা বা কোথায় বেড়ানোর পরিকল্পনা করছেন, {{clientShortname2}} আপনাকে দ্রুত সময়ের মধ্যে তাৎক্ষণিক সিদ্ধান্ত নিতে সাহায্য করবে।
|
||||
fte_slide_2_title=একই পাতায় পেতে
|
||||
fte_slide_2_copy=কোন আইডিয়া শেয়ার করতে, বিকল্পগুলো তুলনা করছে এবং একটি সিধান্তে আসতে বিল্ট-ইন টেক্সট অথবা ভিডিও চ্যাট ব্যবহার করুন।
|
||||
fte_slide_2_title2=ওয়েবে শেয়ার করার জন্যই তৈরি হয়েছে
|
||||
## LOCALIZATION_NOTE(fte_slide_2_copy2): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
fte_slide_2_copy2=এখন আপনি যখনই একজন বন্ধুকে একটি সেশনে আমন্ত্রন জানাবেন, {{clientShortname2}} স্বয়ংক্রিয়াভাবে সকল ওয়েবসাইটও শেয়ার করবে, যা আপনি ওই মূহুর্তে ব্রাউজ করছেন। পরিকল্পনা করুন, কেনাকাটা করুন, সিদ্ধান্ত নিন, সবাই একসাথে।
|
||||
fte_slide_3_title=লিঙ্ক শেয়ার করে বন্ধুদের আলাপে আমন্ত্রণ জানান
|
||||
## LOCALIZATION_NOTE(fte_slide_3_copy): {{clientSuperShortname}}
|
||||
## will be replaced by the super short brand name.
|
||||
@ -57,9 +57,7 @@ fte_slide_4_title=শুরু করতে {{clientSuperShortname}} আইক
|
||||
## will be replaced by the brand short name.
|
||||
fte_slide_4_copy=যখনই আপনি কোন পাতা পাবেন আলোচনা করার জন্যে, লিঙ্ক তৈরি করতে {{brandShortname}} এ ক্লিক করুন। তারপর সেই লিঙ্ক যেকোন ভাবে আপনার বন্ধুর কাছে পাঠিয়ে দিন।
|
||||
|
||||
invite_header_text_bold=আপনার সাথে এই পাতা ব্রাউজে যোগদানে কাউকে আমন্ত্রণ জানান!
|
||||
invite_header_text_bold2=কাউকে আমন্ত্রণ জানান আপনার সাথে যোগ দিতে ।
|
||||
invite_header_text3=ফায়ারফক্স হ্যালো ব্যবহার করতে দুজনের প্রয়োজন হয়, আপনার সাথে ওয়েব ব্রাউজ করতে আপনার বন্ধুকে লিঙ্ক পাঠান।
|
||||
invite_header_text4=এই লিঙ্কটি শেয়ার করুন যেন আপনারা একসাথে ওয়েব ব্রাউজ করতে পারেন।
|
||||
## LOCALIZATION_NOTE(invite_copy_link_button, invite_copied_link_button,
|
||||
## invite_email_link_button, invite_facebook_button2): These labels appear under
|
||||
@ -107,7 +105,8 @@ share_add_service_button=একটি সার্ভিস যুক্ত ক
|
||||
## These menu items are displayed from a panel's context menu for a conversation.
|
||||
copy_link_menuitem=লিঙ্ক কপি করুন
|
||||
email_link_menuitem=ইমেইল লিঙ্ক
|
||||
delete_conversation_menuitem2=অপসারণ
|
||||
edit_name_menuitem=নাম সম্পাদনা করুন
|
||||
delete_conversation_menuitem2=মুছে ফেলুন
|
||||
|
||||
panel_footer_signin_or_signup_link=সাইন ইন / সাইন আপ
|
||||
|
||||
@ -194,16 +193,18 @@ rooms_signout_alert=উন্মুক্ত কথোপকথন বন্ধ
|
||||
room_name_untitled_page=শিরোনামহীন পাতা
|
||||
|
||||
## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
|
||||
door_hanger_return=আবার দেখা হবে! Hello প্যানেলের মাধ্যমে আপনি এই শেয়ারকৃত সেশনে যেকোন সময় ফিরে আসতে পারবেন।
|
||||
door_hanger_prompt_name=সহজভাবে মনে রাখার জন্য কোনো নাম দিতে চান? বর্তমান নাম:
|
||||
door_hanger_button=ঠিক আছে
|
||||
door_hanger_bye=আবার দেখা হবে!
|
||||
door_hanger_return2=Hello প্যানেলের মাধ্যমে আপনি শেয়ারকৃত এই সেশনে যেকোন সময় ফিরে আসতে পারবেন। সহজে মনে রাখতে এর কোন নাম দিতে চান?
|
||||
door_hanger_current=সঠিক নাম:
|
||||
door_hanger_button2=ঠিক আছে!
|
||||
|
||||
# Infobar strings
|
||||
|
||||
infobar_screenshare_no_guest_message=যখনই আপনার বন্ধু যোগ দেবে, তারা আপনি কি ট্যাব ক্লিক করছেন তা দেখতে পাবে।
|
||||
infobar_screenshare_browser_message2=আপনি আপনার ট্যাব শেয়ার করছেন। যেকোনো ট্যাব ক্লিক করলে তা আপনার বন্ধু দেখতে পারবে।
|
||||
infobar_screenshare_browser_message3=আপনি এখন আপনার ট্যাব শেয়ার করছেন। আপনে যেই ট্যাবেই ক্লিক করুন তা আপনার বন্ধু দেখতে পাবে।
|
||||
infobar_screenshare_stop_sharing_message=আপনি আপনার ট্যাব আর শেয়ার করছেন না
|
||||
infobar_screenshare_stop_sharing_message2=আপনি আর ট্যাব শেয়ার করছেন না।
|
||||
infobar_screenshare_stop_no_guest_message=আপনি ট্যাব শেয়ার করা বন্ধ করেছেন। আপনার বন্ধুরা যখন যুক্ত হবে, আপনি যতক্ষণ না কোন কিছু আবার শেয়ার করছেন তারা কিছু দেখতে পাবে না।
|
||||
infobar_button_restart_label2=শেয়ার করা পুনরায় শুরু করুন
|
||||
infobar_button_restart_accesskey=R
|
||||
infobar_button_stop_label2=শেয়ার করা বন্ধ করুন
|
||||
@ -211,6 +212,13 @@ infobar_button_stop_accesskey=S
|
||||
infobar_button_disconnect_label=বিচ্ছিন্ন
|
||||
infobar_button_disconnect_accesskey=D
|
||||
|
||||
# Copy panel strings
|
||||
|
||||
copy_panel_message=ওয়েব পেজ শেয়ার করা প্রয়োজন? আপনার ব্রাউজারের ট্যাব বন্ধুর সাথে শেয়ার করুন।
|
||||
copy_panel_dont_show_again_label=এটি পুনরায় দেখাবে না
|
||||
copy_panel_cancel_button_label=এখন নয়
|
||||
copy_panel_accept_button_label=হ্যাঁ, আমাকে দেখাও কিভাবে হল
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
e10s_not_supported_button_label=নতুন উইন্ডো খুলুন
|
||||
@ -257,6 +265,9 @@ rooms_room_joined_owner_not_connected_label=আপনার বন্ধু আ
|
||||
|
||||
self_view_hidden_message=সেলফ-ভিউ লুকানো কিন্তু এখনো ছবি পাঠাবে; দেখাতে উইন্ডোর আকার পরিবর্তন করুন
|
||||
|
||||
peer_left_session=আপনার বন্ধু চলে গেছেন।
|
||||
peer_unexpected_quit=আপনার বন্ধু অপ্রত্যাশিতভাবে বিচ্ছিন্ন হয়েছেন।
|
||||
|
||||
## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
|
||||
## as this will be replaced by clientShortname2.
|
||||
tos_failure_message={{clientShortname}} আপনার দেশের বিদ্যমান নয়।
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
# Panel Strings
|
||||
|
||||
|
||||
## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
|
||||
## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
|
||||
## use "..." if \u2026 doesn't suit traditions in your locale.
|
||||
@ -19,18 +18,28 @@
|
||||
## will be replaced by the super short brandname.
|
||||
|
||||
|
||||
## LOCALIZATION_NOTE(first_time_experience_subheading2): Message inviting the
|
||||
## LOCALIZATION_NOTE(first_time_experience_subheading2, first_time_experience_subheading_button_above): Message inviting the
|
||||
## user to create his or her first conversation.
|
||||
|
||||
## LOCALIZATION_NOTE(first_time_experience_content): Message describing
|
||||
## LOCALIZATION_NOTE(first_time_experience_content, first_time_experience_content2): Message describing
|
||||
## ways to use Hello project.
|
||||
|
||||
## First Time Experience Slides
|
||||
## LOCALIZATION_NOTE(fte_slide_1_copy): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
## LOCALIZATION_NOTE(fte_slide_2_copy2): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
## LOCALIZATION_NOTE(fte_slide_3_copy): {{clientSuperShortname}}
|
||||
## will be replaced by the super short brand name.
|
||||
## LOCALIZATION_NOTE(fte_slide_4_title): {{clientSuperShortname}}
|
||||
## will be replaced by the super short brand name.
|
||||
## LOCALIZATION_NOTE(fte_slide_4_copy): {{brandShortname}}
|
||||
## will be replaced by the brand short name.
|
||||
|
||||
## LOCALIZATION_NOTE(invite_copy_link_button, invite_copied_link_button,
|
||||
## invite_email_link_button, invite_facebook_button2): These labels appear under
|
||||
## an iconic button for the invite view.
|
||||
|
||||
# Status text
|
||||
|
||||
# Error bars
|
||||
## LOCALIZATION NOTE(session_expired_error_description,could_not_authenticate,password_changed_question,try_again_later,could_not_connect,check_internet_connection,login_expired,service_not_available,problem_accessing_account):
|
||||
## These may be displayed at the top of the panel.
|
||||
@ -98,12 +107,8 @@ hangup_button_caption2=প্রস্থান করুন
|
||||
# Infobar strings
|
||||
|
||||
|
||||
# Context in conversation strings
|
||||
# Copy panel strings
|
||||
|
||||
## LOCALIZATION NOTE(no_conversations_message_heading2): Title shown when user
|
||||
## has no conversations available.
|
||||
## LOCALIZATION NOTE(no_conversations_start_message2): Subheading inviting the
|
||||
## user to start a new conversation.
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
# Panel Strings
|
||||
|
||||
|
||||
## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
|
||||
## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
|
||||
## use "..." if \u2026 doesn't suit traditions in your locale.
|
||||
@ -19,18 +18,28 @@
|
||||
## will be replaced by the super short brandname.
|
||||
|
||||
|
||||
## LOCALIZATION_NOTE(first_time_experience_subheading2): Message inviting the
|
||||
## LOCALIZATION_NOTE(first_time_experience_subheading2, first_time_experience_subheading_button_above): Message inviting the
|
||||
## user to create his or her first conversation.
|
||||
|
||||
## LOCALIZATION_NOTE(first_time_experience_content): Message describing
|
||||
## LOCALIZATION_NOTE(first_time_experience_content, first_time_experience_content2): Message describing
|
||||
## ways to use Hello project.
|
||||
|
||||
## First Time Experience Slides
|
||||
## LOCALIZATION_NOTE(fte_slide_1_copy): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
## LOCALIZATION_NOTE(fte_slide_2_copy2): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
## LOCALIZATION_NOTE(fte_slide_3_copy): {{clientSuperShortname}}
|
||||
## will be replaced by the super short brand name.
|
||||
## LOCALIZATION_NOTE(fte_slide_4_title): {{clientSuperShortname}}
|
||||
## will be replaced by the super short brand name.
|
||||
## LOCALIZATION_NOTE(fte_slide_4_copy): {{brandShortname}}
|
||||
## will be replaced by the brand short name.
|
||||
|
||||
## LOCALIZATION_NOTE(invite_copy_link_button, invite_copied_link_button,
|
||||
## invite_email_link_button, invite_facebook_button2): These labels appear under
|
||||
## an iconic button for the invite view.
|
||||
|
||||
# Status text
|
||||
|
||||
# Error bars
|
||||
## LOCALIZATION NOTE(session_expired_error_description,could_not_authenticate,password_changed_question,try_again_later,could_not_connect,check_internet_connection,login_expired,service_not_available,problem_accessing_account):
|
||||
## These may be displayed at the top of the panel.
|
||||
@ -112,12 +121,8 @@ tour_label=Obilazak
|
||||
# Infobar strings
|
||||
|
||||
|
||||
# Context in conversation strings
|
||||
# Copy panel strings
|
||||
|
||||
## LOCALIZATION NOTE(no_conversations_message_heading2): Title shown when user
|
||||
## has no conversations available.
|
||||
## LOCALIZATION NOTE(no_conversations_start_message2): Subheading inviting the
|
||||
## user to start a new conversation.
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
# Panel Strings
|
||||
|
||||
|
||||
## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
|
||||
## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
|
||||
## use "..." if \u2026 doesn't suit traditions in your locale.
|
||||
@ -19,18 +18,28 @@
|
||||
## will be replaced by the super short brandname.
|
||||
|
||||
|
||||
## LOCALIZATION_NOTE(first_time_experience_subheading2): Message inviting the
|
||||
## LOCALIZATION_NOTE(first_time_experience_subheading2, first_time_experience_subheading_button_above): Message inviting the
|
||||
## user to create his or her first conversation.
|
||||
|
||||
## LOCALIZATION_NOTE(first_time_experience_content): Message describing
|
||||
## LOCALIZATION_NOTE(first_time_experience_content, first_time_experience_content2): Message describing
|
||||
## ways to use Hello project.
|
||||
|
||||
## First Time Experience Slides
|
||||
## LOCALIZATION_NOTE(fte_slide_1_copy): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
## LOCALIZATION_NOTE(fte_slide_2_copy2): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
## LOCALIZATION_NOTE(fte_slide_3_copy): {{clientSuperShortname}}
|
||||
## will be replaced by the super short brand name.
|
||||
## LOCALIZATION_NOTE(fte_slide_4_title): {{clientSuperShortname}}
|
||||
## will be replaced by the super short brand name.
|
||||
## LOCALIZATION_NOTE(fte_slide_4_copy): {{brandShortname}}
|
||||
## will be replaced by the brand short name.
|
||||
|
||||
## LOCALIZATION_NOTE(invite_copy_link_button, invite_copied_link_button,
|
||||
## invite_email_link_button, invite_facebook_button2): These labels appear under
|
||||
## an iconic button for the invite view.
|
||||
|
||||
# Status text
|
||||
|
||||
# Error bars
|
||||
## LOCALIZATION NOTE(session_expired_error_description,could_not_authenticate,password_changed_question,try_again_later,could_not_connect,check_internet_connection,login_expired,service_not_available,problem_accessing_account):
|
||||
## These may be displayed at the top of the panel.
|
||||
@ -98,12 +107,8 @@ hangup_button_caption2=Surt
|
||||
# Infobar strings
|
||||
|
||||
|
||||
# Context in conversation strings
|
||||
# Copy panel strings
|
||||
|
||||
## LOCALIZATION NOTE(no_conversations_message_heading2): Title shown when user
|
||||
## has no conversations available.
|
||||
## LOCALIZATION NOTE(no_conversations_start_message2): Subheading inviting the
|
||||
## user to start a new conversation.
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
|
@ -194,16 +194,18 @@ rooms_signout_alert=Otevřené konverzace budou uzavřeny
|
||||
room_name_untitled_page=Stránka bez názvu
|
||||
|
||||
## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
|
||||
door_hanger_return=Na viděnou! K této sdílené relaci se můžete kdykoliv vrátit pomocí panelu Hello.
|
||||
door_hanger_prompt_name=Chcete ji pro snazší zapamatování pojmenovat? Aktuální název:
|
||||
door_hanger_button=OK
|
||||
door_hanger_bye=Uvidíme se později!
|
||||
door_hanger_return2=K této sdílené relaci se můžete kdykoli vrátit přes panel Hello. Chcete jí dát název, který si lépe zapamatujete?
|
||||
door_hanger_current=Současný název:
|
||||
door_hanger_button2=OK!
|
||||
|
||||
# Infobar strings
|
||||
|
||||
infobar_screenshare_no_guest_message=Jakmile se vaší přátelé připojí, budou moci vidět všechny panely, na které klepnete.
|
||||
infobar_screenshare_browser_message2=Sdílíte své panely. Jakýkoliv panel, na který klepnete, může být viděn vašimi přáteli
|
||||
infobar_screenshare_browser_message3=Nyní spolu sdílíte své panely. Váš přítel uvidí každý panel, na který klepnete.
|
||||
infobar_screenshare_stop_sharing_message=Už nadále nesdílíte své panely
|
||||
infobar_screenshare_stop_sharing_message2=Již své panely nesdílíte.
|
||||
infobar_screenshare_stop_no_guest_message=Sdílení svých panelů jste ukončili. Když se vaši přátelé připojí, nic neuvidí, dokud své panely opět nezačnete sdílet.
|
||||
infobar_button_restart_label2=Restartovat sdílení
|
||||
infobar_button_restart_accesskey=e
|
||||
infobar_button_stop_label2=Zastavit sdílení
|
||||
@ -211,6 +213,13 @@ infobar_button_stop_accesskey=s
|
||||
infobar_button_disconnect_label=Odpojit
|
||||
infobar_button_disconnect_accesskey=O
|
||||
|
||||
# Copy panel strings
|
||||
|
||||
copy_panel_message=Potřebujete sdílet tuto webovou stránku? Sdílejte tento panel s přáteli.
|
||||
copy_panel_dont_show_again_label=Znovu nezobrazovat
|
||||
copy_panel_cancel_button_label=Nyní ne
|
||||
copy_panel_accept_button_label=Ano, ukažte mi, jak na to
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
e10s_not_supported_button_label=Otevřít nové okno
|
||||
|
@ -193,16 +193,12 @@ rooms_signout_alert=Bydd sgyrsiau agored yn cael eu cau
|
||||
room_name_untitled_page=Tudalen Ddideitl
|
||||
|
||||
## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
|
||||
door_hanger_return=Hwyl am y tro! Gallwch ddychwelyd i'r sesiwn yma ar unrhyw adeg drwy banel Hello.
|
||||
door_hanger_prompt_name=Hoffech chi roi enw iddo fel i fod yn haws ei gofio? Enw cyfredol:
|
||||
door_hanger_button=Iawn
|
||||
|
||||
# Infobar strings
|
||||
|
||||
infobar_screenshare_no_guest_message=Cyn gynted a bo'ch ffrind yn ymuno, bydd modd iddyn nhw weld unrhyw dab rydych yn clicio arno.
|
||||
infobar_screenshare_browser_message2=Rydych yn rhannu eich tabiau. Mae modd i'ch ffrindiau weld unrhyw dab rydych yn clicio arno
|
||||
infobar_screenshare_browser_message3=Rydych nawr yn rhannu eich tabiau. Bydd eich ffrind yn gweld unrhyw dab fyddwch chi'n clicio arno.
|
||||
infobar_screenshare_stop_sharing_message=Nid ydych bellach yn rhannu eich tabiau
|
||||
infobar_button_restart_label2=Ail gychwyn rhannu
|
||||
infobar_button_restart_accesskey=A
|
||||
infobar_button_stop_label2=Peidio rhannu
|
||||
@ -210,6 +206,9 @@ infobar_button_stop_accesskey=P
|
||||
infobar_button_disconnect_label=Datgysylltu
|
||||
infobar_button_disconnect_accesskey=D
|
||||
|
||||
# Copy panel strings
|
||||
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
e10s_not_supported_button_label=Agor Ffenestr Newydd
|
||||
|
@ -193,16 +193,16 @@ rooms_signout_alert=Åbne samtaler vil blive slettet
|
||||
room_name_untitled_page=Unavngiven side
|
||||
|
||||
## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
|
||||
door_hanger_return=Vi ses senere! Du kan til enhver tid vende tilbage til denne delte session ved at klikke på Hello-panelet.
|
||||
door_hanger_prompt_name=Vil du give den et navn, der er nemmere at huske? Nuværende navn:
|
||||
door_hanger_button=OK
|
||||
door_hanger_bye=På gensyn!
|
||||
door_hanger_return2=Du kan vendte tilbage til denne delte session når som helst via Hello-panelet. Vil du give den et navn, der er nemmere at huske?
|
||||
door_hanger_current=Nuværende navn:
|
||||
door_hanger_button2=OK!
|
||||
|
||||
# Infobar strings
|
||||
|
||||
infobar_screenshare_no_guest_message=Lige så snart din ven deltager, vil hun kunne se de faneblade, du klikker på.
|
||||
infobar_screenshare_browser_message2=Du deler dine faneblade. Når du klikker på et faneblad kan dine venner se det
|
||||
infobar_screenshare_browser_message3=Du deler nu dine faneblade. Din ven vil kunne se de faneblade, du klikker på.
|
||||
infobar_screenshare_stop_sharing_message=Du deler ikke dine faneblade længere
|
||||
infobar_button_restart_label2=Genstart deling
|
||||
infobar_button_restart_accesskey=e
|
||||
infobar_button_stop_label2=Stop med at dele
|
||||
@ -210,6 +210,13 @@ infobar_button_stop_accesskey=S
|
||||
infobar_button_disconnect_label=Afbryd
|
||||
infobar_button_disconnect_accesskey=A
|
||||
|
||||
# Copy panel strings
|
||||
|
||||
copy_panel_message=Har du brug for at dele dette websted? Del fanebladet i din browser med en ven.
|
||||
copy_panel_dont_show_again_label=Vis ikke dette igen
|
||||
copy_panel_cancel_button_label=Ikke nu
|
||||
copy_panel_accept_button_label=Ja, vis mig hvordan
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
e10s_not_supported_button_label=Åbn nyt vindue
|
||||
|
@ -194,16 +194,18 @@ rooms_signout_alert=Geöffnete Gespräche werden beendet.
|
||||
room_name_untitled_page=Seite ohne Namen
|
||||
|
||||
## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
|
||||
door_hanger_return=Bis später! Sie können jederzeit über die Hello-Ansicht in diese geteilte Sitzung zurückkehren.
|
||||
door_hanger_prompt_name=Wollen Sie Ihr einen einfacher zu merkenden Namen geben? Derzeitiger Name:
|
||||
door_hanger_button=OK
|
||||
door_hanger_bye=Bis später!
|
||||
door_hanger_return2=Sie können diese geteilte Sitzung jederzeit über den Hello-Bereich wieder aufrufen. Möchten Sie ihr einen Namen geben, den Sie sich besser merken können?
|
||||
door_hanger_current=Aktueller Name:
|
||||
door_hanger_button2=Ok!
|
||||
|
||||
# Infobar strings
|
||||
|
||||
infobar_screenshare_no_guest_message=Sobald Ihr Freund dabei ist, kann er jeden Tab sehen, den Sie anklicken.
|
||||
infobar_screenshare_browser_message2=Sie geben Ihre Tabs weiter. Jeder von Ihnen angeklickte Tab wird von Ihren Freunden gesehen.
|
||||
infobar_screenshare_browser_message3=Sie geben jetzt Ihre Tabs weiter. Ihr Freund kann alle Tabs sehen, die Sie anklicken.
|
||||
infobar_screenshare_stop_sharing_message=Sie geben Ihre Tabs nicht mehr weiter.
|
||||
infobar_screenshare_stop_sharing_message2=Sie geben Ihre Tabs nicht mehr weiter.
|
||||
infobar_screenshare_stop_no_guest_message=Sie geben Ihre Tabs nicht mehr weiter. Wenn Ihr Freund das Gespräch betritt, kann dieser Ihre Tabs nicht sehen, bis Sie diese wieder weitergeben.
|
||||
infobar_button_restart_label2=Wieder weitergeben
|
||||
infobar_button_restart_accesskey=W
|
||||
infobar_button_stop_label2=Nicht mehr weitergeben
|
||||
@ -211,6 +213,13 @@ infobar_button_stop_accesskey=N
|
||||
infobar_button_disconnect_label=Verbindung trennen
|
||||
infobar_button_disconnect_accesskey=t
|
||||
|
||||
# Copy panel strings
|
||||
|
||||
copy_panel_message=Möchten Sie diese Webseite teilen? Teilen Sie Ihren Browser-Tab mit einem Freund.
|
||||
copy_panel_dont_show_again_label=Nicht mehr anzeigen
|
||||
copy_panel_cancel_button_label=Jetzt nicht.
|
||||
copy_panel_accept_button_label=Ja, zeig mir, wie es geht.
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
e10s_not_supported_button_label=Neues Fenster öffnen
|
||||
|
@ -193,16 +193,18 @@ rooms_signout_alert=Wócynjone rozgrona se zacyniju
|
||||
room_name_untitled_page=Bok bźez titela
|
||||
|
||||
## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
|
||||
door_hanger_return=Až do chyle! Móžośo se kuždy cas pśez wokno Hello k toś tomu źělonemu pósejźenjeju wrośiś.
|
||||
door_hanger_prompt_name=By wy pšosym mě pódał, kótarež dajo se sj lažčej spomnjeś? Aktualne mě:
|
||||
door_hanger_button=W pórěźe
|
||||
door_hanger_bye=Až do chyle!
|
||||
door_hanger_return2=Móžośo se k toś tomu źělonemu pósejźenjeju kuždy cas pśez wobłuk Hello wrośiś. Cośo jomu mě daś, kótarež móžośo se lažcej spomnjeś?
|
||||
door_hanger_current=Aktualne mě:
|
||||
door_hanger_button2=W pórěźe!
|
||||
|
||||
# Infobar strings
|
||||
|
||||
infobar_screenshare_no_guest_message=Gaž se waš pśijaśel pśizamkujo, móžotej rejtark wiźeś, na kótaryž kliknjośo.
|
||||
infobar_screenshare_browser_message2=Źěliśo swóje rejtarki. Waše pśijaśele mógu kuždy rejtark wiźeś, na kótaryž kliknjośo
|
||||
infobar_screenshare_browser_message3=Źělitej něnto swóje rejtarki. Waš pśijaśel buźo rejtark wiźeś, na kótaryž kliknjośo.
|
||||
infobar_screenshare_stop_sharing_message=Njeźěliśo wěcej swóje rejtarki
|
||||
infobar_screenshare_stop_sharing_message2=Njeźěliśo wěcej swóje rejtarki.
|
||||
infobar_screenshare_stop_no_guest_message=Njeźěliśo wěcej swóje rejtarki. Gaž se waš pśijaśel pśizamkujo, njamóžo nic wiźeś, daniž zasej njeźěliśo.
|
||||
infobar_button_restart_label2=Źělenje znowego startowaś
|
||||
infobar_button_restart_accesskey=n
|
||||
infobar_button_stop_label2=Źělenje zastajiś
|
||||
@ -210,6 +212,13 @@ infobar_button_stop_accesskey=S
|
||||
infobar_button_disconnect_label=Zwisk źěliś
|
||||
infobar_button_disconnect_accesskey=Z
|
||||
|
||||
# Copy panel strings
|
||||
|
||||
copy_panel_message=Cośo toś ten webbok źěliś? Źělśo swój wobglědowakowy rejtark z pśijaśelom.
|
||||
copy_panel_dont_show_again_label=Wěcej se njepokazaś
|
||||
copy_panel_cancel_button_label=Nic něnto
|
||||
copy_panel_accept_button_label=Jo, pokažćo mě kak
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
e10s_not_supported_button_label=Nowe wokno wócyniś
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
# Panel Strings
|
||||
|
||||
|
||||
## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
|
||||
## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
|
||||
## use "..." if \u2026 doesn't suit traditions in your locale.
|
||||
@ -19,18 +18,28 @@
|
||||
## will be replaced by the super short brandname.
|
||||
|
||||
|
||||
## LOCALIZATION_NOTE(first_time_experience_subheading2): Message inviting the
|
||||
## LOCALIZATION_NOTE(first_time_experience_subheading2, first_time_experience_subheading_button_above): Message inviting the
|
||||
## user to create his or her first conversation.
|
||||
|
||||
## LOCALIZATION_NOTE(first_time_experience_content): Message describing
|
||||
## LOCALIZATION_NOTE(first_time_experience_content, first_time_experience_content2): Message describing
|
||||
## ways to use Hello project.
|
||||
|
||||
## First Time Experience Slides
|
||||
## LOCALIZATION_NOTE(fte_slide_1_copy): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
## LOCALIZATION_NOTE(fte_slide_2_copy2): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
## LOCALIZATION_NOTE(fte_slide_3_copy): {{clientSuperShortname}}
|
||||
## will be replaced by the super short brand name.
|
||||
## LOCALIZATION_NOTE(fte_slide_4_title): {{clientSuperShortname}}
|
||||
## will be replaced by the super short brand name.
|
||||
## LOCALIZATION_NOTE(fte_slide_4_copy): {{brandShortname}}
|
||||
## will be replaced by the brand short name.
|
||||
|
||||
## LOCALIZATION_NOTE(invite_copy_link_button, invite_copied_link_button,
|
||||
## invite_email_link_button, invite_facebook_button2): These labels appear under
|
||||
## an iconic button for the invite view.
|
||||
|
||||
# Status text
|
||||
|
||||
# Error bars
|
||||
## LOCALIZATION NOTE(session_expired_error_description,could_not_authenticate,password_changed_question,try_again_later,could_not_connect,check_internet_connection,login_expired,service_not_available,problem_accessing_account):
|
||||
## These may be displayed at the top of the panel.
|
||||
@ -112,12 +121,8 @@ tour_label=Περιήγηση
|
||||
# Infobar strings
|
||||
|
||||
|
||||
# Context in conversation strings
|
||||
# Copy panel strings
|
||||
|
||||
## LOCALIZATION NOTE(no_conversations_message_heading2): Title shown when user
|
||||
## has no conversations available.
|
||||
## LOCALIZATION NOTE(no_conversations_start_message2): Subheading inviting the
|
||||
## user to start a new conversation.
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
# Panel Strings
|
||||
|
||||
|
||||
|
||||
## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
|
||||
## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
|
||||
## use "..." if \u2026 doesn't suit traditions in your locale.
|
||||
@ -44,8 +42,10 @@ fte_slide_1_title=Browse Web pages with a friend
|
||||
## LOCALIZATION_NOTE(fte_slide_1_copy): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
fte_slide_1_copy=Whether you’re planning a trip or shopping for a gift, {{clientShortname2}} lets you make faster decisions in real time.
|
||||
fte_slide_2_title=Get on the same page
|
||||
fte_slide_2_copy=Use the built-in text or video chat to share ideas, compare options and come to an agreement.
|
||||
fte_slide_2_title2=Made for sharing the Web
|
||||
## LOCALIZATION_NOTE(fte_slide_2_copy2): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
fte_slide_2_copy2=Now when you invite a friend to a session, {{clientShortname2}} will automatically share any Web page you’re viewing. Plan. Shop. Decide. Together.
|
||||
fte_slide_3_title=Invite a friend by sending a link
|
||||
## LOCALIZATION_NOTE(fte_slide_3_copy): {{clientSuperShortname}}
|
||||
## will be replaced by the super short brand name.
|
||||
@ -57,9 +57,7 @@ fte_slide_4_title=Find the {{clientSuperShortname}} icon to get started
|
||||
## will be replaced by the brand short name.
|
||||
fte_slide_4_copy=Once you’ve found a page you want to discuss, click the icon in {{brandShortname}} to create a link. Then send it to your friend however you like!
|
||||
|
||||
invite_header_text_bold=Invite someone to browse this page with you!
|
||||
invite_header_text_bold2=Invite a friend to join you!
|
||||
invite_header_text3=It takes two to use Firefox Hello, so send a friend a link to browse the Web with you!
|
||||
invite_header_text4=Share this link so you can start browsing the Web together.
|
||||
## LOCALIZATION_NOTE(invite_copy_link_button, invite_copied_link_button,
|
||||
## invite_email_link_button, invite_facebook_button2): These labels appear under
|
||||
@ -107,6 +105,7 @@ share_add_service_button=Add a Service
|
||||
## These menu items are displayed from a panel's context menu for a conversation.
|
||||
copy_link_menuitem=Copy Link
|
||||
email_link_menuitem=Email Link
|
||||
edit_name_menuitem=Edit name
|
||||
delete_conversation_menuitem2=Delete
|
||||
|
||||
panel_footer_signin_or_signup_link=Sign In or Sign Up
|
||||
@ -194,16 +193,16 @@ rooms_signout_alert=Open conversations will be closed
|
||||
room_name_untitled_page=Untitled Page
|
||||
|
||||
## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
|
||||
door_hanger_return=See you later! You can return to this shared session at any time through the Hello panel.
|
||||
door_hanger_prompt_name=Would you like to give it a name that's easier to remember? Current name:
|
||||
door_hanger_button=OK
|
||||
door_hanger_bye=See you later!
|
||||
door_hanger_return2=You can return to this shared session at any time through the Hello panel. Would you like to give it a name that’s easier to remember?
|
||||
door_hanger_current=Current name:
|
||||
door_hanger_button2=OK!
|
||||
|
||||
# Infobar strings
|
||||
|
||||
infobar_screenshare_no_guest_message=As soon as your friend joins, they will be able to see any tab you click on.
|
||||
infobar_screenshare_browser_message2=You are sharing your tabs. Any tab you click on can be seen by your friends
|
||||
infobar_screenshare_browser_message3=You are now sharing your tabs. Your friend will see any tab you click on.
|
||||
infobar_screenshare_stop_sharing_message=You are no longer sharing your tabs
|
||||
infobar_button_restart_label2=Restart sharing
|
||||
infobar_button_restart_accesskey=e
|
||||
infobar_button_stop_label2=Stop sharing
|
||||
@ -211,6 +210,13 @@ infobar_button_stop_accesskey=S
|
||||
infobar_button_disconnect_label=Disconnect
|
||||
infobar_button_disconnect_accesskey=D
|
||||
|
||||
# Copy panel strings
|
||||
|
||||
copy_panel_message=Need to share this Web page? Share your browser tab with a friend.
|
||||
copy_panel_dont_show_again_label=Don’t show this again
|
||||
copy_panel_cancel_button_label=Not now
|
||||
copy_panel_accept_button_label=Yes, show me how
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
e10s_not_supported_button_label=Launch New Window
|
||||
|
@ -193,16 +193,18 @@ rooms_signout_alert=Open conversations will be closed
|
||||
room_name_untitled_page=Untitled Page
|
||||
|
||||
## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
|
||||
door_hanger_return=See you later! You can return to this shared session at any time through the Hello panel.
|
||||
door_hanger_prompt_name=Would you like to give it a name that’s easier to remember? Current name:
|
||||
door_hanger_button=OK
|
||||
door_hanger_bye=See you later!
|
||||
door_hanger_return2=You can return to this shared session at any time through the Hello panel. Would you like to give it a name that’s easier to remember?
|
||||
door_hanger_current=Current name:
|
||||
door_hanger_button2=OK!
|
||||
|
||||
# Infobar strings
|
||||
|
||||
infobar_screenshare_no_guest_message=As soon as your friend joins, they will be able to see any tab you click on.
|
||||
infobar_screenshare_browser_message2=You are sharing your tabs. Any tab you click on can be seen by your friends
|
||||
infobar_screenshare_browser_message3=You are now sharing your tabs. Your friend will see any tab you click on.
|
||||
infobar_screenshare_stop_sharing_message=You are no longer sharing your tabs
|
||||
infobar_screenshare_stop_sharing_message2=You are no longer sharing your tabs.
|
||||
infobar_screenshare_stop_no_guest_message=You have stopped sharing your tabs. When your friend joins, they won’t be able to see anything until you restart sharing.
|
||||
infobar_button_restart_label2=Restart sharing
|
||||
infobar_button_restart_accesskey=R
|
||||
infobar_button_stop_label2=Stop sharing
|
||||
@ -210,6 +212,13 @@ infobar_button_stop_accesskey=S
|
||||
infobar_button_disconnect_label=Disconnect
|
||||
infobar_button_disconnect_accesskey=D
|
||||
|
||||
# Copy panel strings
|
||||
|
||||
copy_panel_message=Need to share this Web page? Share your browser tab with a friend.
|
||||
copy_panel_dont_show_again_label=Don’t show this again
|
||||
copy_panel_cancel_button_label=Not now
|
||||
copy_panel_accept_button_label=Yes, show me how
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
e10s_not_supported_button_label=Launch New Window
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
# Panel Strings
|
||||
|
||||
|
||||
## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
|
||||
## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
|
||||
## use "..." if \u2026 doesn't suit traditions in your locale.
|
||||
@ -19,18 +18,28 @@
|
||||
## will be replaced by the super short brandname.
|
||||
|
||||
|
||||
## LOCALIZATION_NOTE(first_time_experience_subheading2): Message inviting the
|
||||
## LOCALIZATION_NOTE(first_time_experience_subheading2, first_time_experience_subheading_button_above): Message inviting the
|
||||
## user to create his or her first conversation.
|
||||
|
||||
## LOCALIZATION_NOTE(first_time_experience_content): Message describing
|
||||
## LOCALIZATION_NOTE(first_time_experience_content, first_time_experience_content2): Message describing
|
||||
## ways to use Hello project.
|
||||
|
||||
## First Time Experience Slides
|
||||
## LOCALIZATION_NOTE(fte_slide_1_copy): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
## LOCALIZATION_NOTE(fte_slide_2_copy2): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
## LOCALIZATION_NOTE(fte_slide_3_copy): {{clientSuperShortname}}
|
||||
## will be replaced by the super short brand name.
|
||||
## LOCALIZATION_NOTE(fte_slide_4_title): {{clientSuperShortname}}
|
||||
## will be replaced by the super short brand name.
|
||||
## LOCALIZATION_NOTE(fte_slide_4_copy): {{brandShortname}}
|
||||
## will be replaced by the brand short name.
|
||||
|
||||
## LOCALIZATION_NOTE(invite_copy_link_button, invite_copied_link_button,
|
||||
## invite_email_link_button, invite_facebook_button2): These labels appear under
|
||||
## an iconic button for the invite view.
|
||||
|
||||
# Status text
|
||||
|
||||
# Error bars
|
||||
## LOCALIZATION NOTE(session_expired_error_description,could_not_authenticate,password_changed_question,try_again_later,could_not_connect,check_internet_connection,login_expired,service_not_available,problem_accessing_account):
|
||||
## These may be displayed at the top of the panel.
|
||||
@ -98,12 +107,8 @@ hangup_button_caption2=Eliri
|
||||
# Infobar strings
|
||||
|
||||
|
||||
# Context in conversation strings
|
||||
# Copy panel strings
|
||||
|
||||
## LOCALIZATION NOTE(no_conversations_message_heading2): Title shown when user
|
||||
## has no conversations available.
|
||||
## LOCALIZATION NOTE(no_conversations_start_message2): Subheading inviting the
|
||||
## user to start a new conversation.
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
|
@ -194,16 +194,18 @@ rooms_signout_alert=Las conversaciones abiertas serán cerradas
|
||||
room_name_untitled_page=Página sin título
|
||||
|
||||
## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
|
||||
door_hanger_return=¡Hasta pronto! Puedes regresar a esta sesión compartida en cualquier momento a través del panel Hello.
|
||||
door_hanger_prompt_name=¿Quieres darle un nombre para que sea más fácil de recordar? Nombre actual:
|
||||
door_hanger_button=Aceptar
|
||||
door_hanger_bye=¡Hasta pronto!
|
||||
door_hanger_return2=Puedes regresar a esta sesión compartida en cualquier momento a través del panel de Hello. ¿Te gustaría darle un nombre para que puedas encontrarla fácilmente?
|
||||
door_hanger_current=Nombre actual:
|
||||
door_hanger_button2=¡Aceptar!
|
||||
|
||||
# Infobar strings
|
||||
|
||||
infobar_screenshare_no_guest_message=Tan pronto como se una tu amigo, podrá ver cualquier pestaña en la que hagas clic.
|
||||
infobar_screenshare_browser_message2=Estás compartiendo sus pestañas. Cualquier pestaña en la que hagas clic podrá ser vista por tus amigos
|
||||
infobar_screenshare_browser_message3=Ahora estás compartiendo tus pestañas. Tu amigo verá cualquier pestaña en la que hagas clic.
|
||||
infobar_screenshare_stop_sharing_message=Ya no estás compartiendo tus pestañas
|
||||
infobar_screenshare_stop_sharing_message2=Ya no estás compartiendo tus pestañas.
|
||||
infobar_screenshare_stop_no_guest_message=Has dejado de compartir tus pestañas. Cuando tu amigo se una, no podrá ver nada hasta que vuelvas a compartirlas.
|
||||
infobar_button_restart_label2=Reanudar la compartición
|
||||
infobar_button_restart_accesskey=R
|
||||
infobar_button_stop_label2=Dejar de compartir
|
||||
@ -211,6 +213,13 @@ infobar_button_stop_accesskey=S
|
||||
infobar_button_disconnect_label=Desconectarse
|
||||
infobar_button_disconnect_accesskey=D
|
||||
|
||||
# Copy panel strings
|
||||
|
||||
copy_panel_message=¿Necesitas compartir esta página Web? Comparte tu pestaña del navegador con un amigo.
|
||||
copy_panel_dont_show_again_label=No volver a mostrar este mensaje
|
||||
copy_panel_cancel_button_label=Ahora no
|
||||
copy_panel_accept_button_label=Sí, muéstrenme cómo
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
e10s_not_supported_button_label=Lanzar nueva ventana
|
||||
|
@ -49,7 +49,7 @@ fte_slide_2_copy2=Ahora cuando invites a un amigo a una sesión, {{clientShortna
|
||||
fte_slide_3_title=Invita a un amigo enviando un enlace
|
||||
## LOCALIZATION_NOTE(fte_slide_3_copy): {{clientSuperShortname}}
|
||||
## will be replaced by the super short brand name.
|
||||
fte_slide_3_copy={{clientSuperShortnae}} funciona con la mayoría de los navegadores de escritorio. No es necesario tener cuenta y todo el mundo se conecta gratuitamente.
|
||||
fte_slide_3_copy={{clientSuperShortname}} funciona con la mayoría de los navegadores de escritorio. No es necesario tener cuenta y todo el mundo se conecta gratuitamente.
|
||||
## LOCALIZATION_NOTE(fte_slide_4_title): {{clientSuperShortname}}
|
||||
## will be replaced by the super short brand name.
|
||||
fte_slide_4_title=Busca el icono de {{clientSuperShortname}} para comenzar
|
||||
@ -193,16 +193,18 @@ rooms_signout_alert=Se cerrarán las conversaciones abiertas
|
||||
room_name_untitled_page=Página sin título
|
||||
|
||||
## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
|
||||
door_hanger_return=¡Hasta luego! Puedes volver a esta sesión compartida cuando quieras en el panel Hello.
|
||||
door_hanger_prompt_name=¿Te gustaría darle un nombre más fácil de recordar? Nombre actual:
|
||||
door_hanger_button=Aceptar
|
||||
door_hanger_bye=¡Hasta luego!
|
||||
door_hanger_return2=Puedes volver a esta sesión compartida cuando quieras a través del panel Hello. ¿Quieres darle un nombre para recordarla mejor?
|
||||
door_hanger_current=Nombre actual:
|
||||
door_hanger_button2=¡Vale!
|
||||
|
||||
# Infobar strings
|
||||
|
||||
infobar_screenshare_no_guest_message=Tan pronto se una tu amigo, podrá ver cualquier pestaña en la que pulses tú.
|
||||
infobar_screenshare_browser_message2=Estás compartiendo tus pestañas. Cualquier pestaña en la que pulses puede ser vista por tus amigos
|
||||
infobar_screenshare_browser_message3=Ahora estás compartiendo tus pestañas. Tu amigo verá cualquier pestaña en la que pulses.
|
||||
infobar_screenshare_stop_sharing_message=Ya no compartes tus pestañas
|
||||
infobar_screenshare_stop_sharing_message2=Ya no estás compartiendo tus pestañas.
|
||||
infobar_screenshare_stop_no_guest_message=Has dejado de compartir tus pestañas. Cuando se unan tus amigos, no podrán ver nada hasta que vuelvas a compartirlas.
|
||||
infobar_button_restart_label2=Volver a compartir
|
||||
infobar_button_restart_accesskey=e
|
||||
infobar_button_stop_label2=Dejar de compartir
|
||||
@ -210,6 +212,13 @@ infobar_button_stop_accesskey=S
|
||||
infobar_button_disconnect_label=Desconectar
|
||||
infobar_button_disconnect_accesskey=D
|
||||
|
||||
# Copy panel strings
|
||||
|
||||
copy_panel_message=¿Necesitas compartir esta página? Comparte tu pestaña con un amigo.
|
||||
copy_panel_dont_show_again_label=No volver a mostrar
|
||||
copy_panel_cancel_button_label=Ahora no
|
||||
copy_panel_accept_button_label=Si, quiero verlo
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
e10s_not_supported_button_label=Ejecutar una nueva ventana
|
||||
|
@ -193,16 +193,16 @@ rooms_signout_alert=Las conversaciones abiertas se cerrarán
|
||||
room_name_untitled_page=Página sin título
|
||||
|
||||
## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
|
||||
door_hanger_return=¡Nos vemos luego! Puedes regresar a esta sesión compartida cuando quieras a través del panel de Hello.
|
||||
door_hanger_prompt_name=¿Te gustaría probar un nombre más sencillo para recordar? Nombre actual:
|
||||
door_hanger_button=Aceptar
|
||||
door_hanger_bye=¡Nos vemos después!
|
||||
door_hanger_return2=Puedes regresar a esta sesión compartida en cualquier momento a través del panel de Hello. ¿Te gustaría darle un nombre más sencillo de recordar?
|
||||
door_hanger_current=Nombre actual:
|
||||
door_hanger_button2=¡De acuerdo!
|
||||
|
||||
# Infobar strings
|
||||
|
||||
infobar_screenshare_no_guest_message=Tan pronto como tus amigos se vayan uniendo, serán capaces de ver cualquier pestaña en la que hagas clic.
|
||||
infobar_screenshare_browser_message2=Estás compartiendo tus pestañas. Cualquier pestaña en la que des clic puede ser vista por tus amigos
|
||||
infobar_screenshare_browser_message3=Estás compartiendo tus pestañas. Tu amigo podrá ver cualquier pestaña en la que hagas clic.
|
||||
infobar_screenshare_stop_sharing_message=Ya no estás compartiendo tus pestañas
|
||||
infobar_button_restart_label2=Volver a compartir
|
||||
infobar_button_restart_accesskey=e
|
||||
infobar_button_stop_label2=Dejar de compartir
|
||||
@ -210,6 +210,13 @@ infobar_button_stop_accesskey=c
|
||||
infobar_button_disconnect_label=Desconectar
|
||||
infobar_button_disconnect_accesskey=D
|
||||
|
||||
# Copy panel strings
|
||||
|
||||
copy_panel_message=¿Necesitas compartir esta página Web? Comparte te pestaña de navegación con un amigo.
|
||||
copy_panel_dont_show_again_label=No mostrar de nuevo
|
||||
copy_panel_cancel_button_label=Ahora no
|
||||
copy_panel_accept_button_label=Sí, muéstrame como
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
e10s_not_supported_button_label=Abrir una nueva ventana
|
||||
|
@ -194,16 +194,16 @@ rooms_signout_alert=Avatud vestlused suletakse
|
||||
room_name_untitled_page=Nimeta leht
|
||||
|
||||
## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
|
||||
door_hanger_return=Näeme hiljem! Saad sellesse jagatud sessiooni naasta mis tahes ajal Hello paneeli kaudu.
|
||||
door_hanger_prompt_name=Kas soovid sellele nime anda, et oleks kergem meeles pidada? Praegune nimi:
|
||||
door_hanger_button=Olgu
|
||||
door_hanger_bye=Nägemiseni!
|
||||
door_hanger_return2=Saad sellesse seanssi mis tahes ajal Hello paneeli kaudu naasta. Kas soovid sellele kergesti meeldejääva nime anda?
|
||||
door_hanger_current=Praegune nimi:
|
||||
door_hanger_button2=Olgu!
|
||||
|
||||
# Infobar strings
|
||||
|
||||
infobar_screenshare_no_guest_message=Niipea kui su sõber liitub, näeb ta sinu valitud kaarti.
|
||||
infobar_screenshare_browser_message2=Jagad enda kaarte. Sinu sõbrad näevad kaarti, mille oled valinud.
|
||||
infobar_screenshare_browser_message3=Jagad nüüd enda kaarte. Sinu sõber näeb kaarti, mille oled valinud.
|
||||
infobar_screenshare_stop_sharing_message=Sa ei jaga enam kaarte.
|
||||
infobar_button_restart_label2=Alusta jagamist uuesti
|
||||
infobar_button_restart_accesskey=s
|
||||
infobar_button_stop_label2=Lõpeta jagamine
|
||||
@ -211,6 +211,13 @@ infobar_button_stop_accesskey=L
|
||||
infobar_button_disconnect_label=Lõpeta ühendus
|
||||
infobar_button_disconnect_accesskey=L
|
||||
|
||||
# Copy panel strings
|
||||
|
||||
copy_panel_message=Sul on tarvis seda veebilehte jagada? Jaga veebilehitseja kaarti sõbraga.
|
||||
copy_panel_dont_show_again_label=Ära seda rohkem kuva
|
||||
copy_panel_cancel_button_label=Mitte praegu
|
||||
copy_panel_accept_button_label=Jah, näita kuidas
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
e10s_not_supported_button_label=Ava uus aken
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
# Panel Strings
|
||||
|
||||
|
||||
## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
|
||||
## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
|
||||
## use "..." if \u2026 doesn't suit traditions in your locale.
|
||||
@ -19,18 +18,28 @@
|
||||
## will be replaced by the super short brandname.
|
||||
|
||||
|
||||
## LOCALIZATION_NOTE(first_time_experience_subheading2): Message inviting the
|
||||
## LOCALIZATION_NOTE(first_time_experience_subheading2, first_time_experience_subheading_button_above): Message inviting the
|
||||
## user to create his or her first conversation.
|
||||
|
||||
## LOCALIZATION_NOTE(first_time_experience_content): Message describing
|
||||
## LOCALIZATION_NOTE(first_time_experience_content, first_time_experience_content2): Message describing
|
||||
## ways to use Hello project.
|
||||
|
||||
## First Time Experience Slides
|
||||
## LOCALIZATION_NOTE(fte_slide_1_copy): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
## LOCALIZATION_NOTE(fte_slide_2_copy2): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
## LOCALIZATION_NOTE(fte_slide_3_copy): {{clientSuperShortname}}
|
||||
## will be replaced by the super short brand name.
|
||||
## LOCALIZATION_NOTE(fte_slide_4_title): {{clientSuperShortname}}
|
||||
## will be replaced by the super short brand name.
|
||||
## LOCALIZATION_NOTE(fte_slide_4_copy): {{brandShortname}}
|
||||
## will be replaced by the brand short name.
|
||||
|
||||
## LOCALIZATION_NOTE(invite_copy_link_button, invite_copied_link_button,
|
||||
## invite_email_link_button, invite_facebook_button2): These labels appear under
|
||||
## an iconic button for the invite view.
|
||||
|
||||
# Status text
|
||||
|
||||
# Error bars
|
||||
## LOCALIZATION NOTE(session_expired_error_description,could_not_authenticate,password_changed_question,try_again_later,could_not_connect,check_internet_connection,login_expired,service_not_available,problem_accessing_account):
|
||||
## These may be displayed at the top of the panel.
|
||||
@ -112,12 +121,8 @@ tour_label=Itzulia
|
||||
# Infobar strings
|
||||
|
||||
|
||||
# Context in conversation strings
|
||||
# Copy panel strings
|
||||
|
||||
## LOCALIZATION NOTE(no_conversations_message_heading2): Title shown when user
|
||||
## has no conversations available.
|
||||
## LOCALIZATION NOTE(no_conversations_start_message2): Subheading inviting the
|
||||
## user to start a new conversation.
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
|
@ -193,16 +193,16 @@ rooms_signout_alert=گفتوگوهای باز، بسته خواهند شد
|
||||
room_name_untitled_page=صفحه بینام
|
||||
|
||||
## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
|
||||
door_hanger_return=بعدا میبنیمت! شما میتوانید از طریق قسمت Hello در هر زمانی به این نشست اشتراک گذاشته شده بازگردید.
|
||||
door_hanger_prompt_name=آیا مایلید که نامی انتخاب کنید تا بعدا راحتتر آن را پیدا کنید؟ نام فعلی:
|
||||
door_hanger_button=تایید
|
||||
door_hanger_bye=تا بعد!
|
||||
door_hanger_return2=شما میتوانید در هر زمانی از طریق پنل Hello به این نشست به اشتراک گذاشته شده برگردید. آیا مایلید برای راحتی بیشتر یک نام برایش انتخاب کنید؟
|
||||
door_hanger_current=نام فعلی:
|
||||
door_hanger_button2=تایید!
|
||||
|
||||
# Infobar strings
|
||||
|
||||
infobar_screenshare_no_guest_message=هر موقع دوستان شما متصل شوند، آنها قادر خواهند بود هر زبانهای که رویاش کلیک میکنید را ببینند.
|
||||
infobar_screenshare_browser_message2=شما در حال اشتراکگذاری زبانههای خود هستید. هر زبانهای که بر رویاش کلیک کنید، توسط دوستانتان دید میشود
|
||||
infobar_screenshare_browser_message3=شما هماکنون در حال اشتراکگذاری زبانههای خود هستید. دوستان شما هر زبانهای که رویاش کلیک کنید را خواهند دید.
|
||||
infobar_screenshare_stop_sharing_message=شما دیگر در حال اشتراکگذاری زبانههای خود نیستید
|
||||
infobar_button_restart_label2=راهاندازی مجدد اشتراکگذاری
|
||||
infobar_button_restart_accesskey=e
|
||||
infobar_button_stop_label2=توقف اشتراکگذاری
|
||||
@ -210,6 +210,13 @@ infobar_button_stop_accesskey=S
|
||||
infobar_button_disconnect_label=قطع ارتباط
|
||||
infobar_button_disconnect_accesskey=D
|
||||
|
||||
# Copy panel strings
|
||||
|
||||
copy_panel_message=میخواهید این صفحه وب را بهاشتراک بگذارید؟ زبانه مرورگر خود را با یک دوست بهاشتراک بگذارید.
|
||||
copy_panel_dont_show_again_label=این را دوباره نمایش نده
|
||||
copy_panel_cancel_button_label=الان نه
|
||||
copy_panel_accept_button_label=بله، به من نشان بده
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
e10s_not_supported_button_label=اجرا یک پنجره جدید
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
# Panel Strings
|
||||
|
||||
|
||||
## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
|
||||
## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
|
||||
## use "..." if \u2026 doesn't suit traditions in your locale.
|
||||
@ -19,18 +18,28 @@
|
||||
## will be replaced by the super short brandname.
|
||||
|
||||
|
||||
## LOCALIZATION_NOTE(first_time_experience_subheading2): Message inviting the
|
||||
## LOCALIZATION_NOTE(first_time_experience_subheading2, first_time_experience_subheading_button_above): Message inviting the
|
||||
## user to create his or her first conversation.
|
||||
|
||||
## LOCALIZATION_NOTE(first_time_experience_content): Message describing
|
||||
## LOCALIZATION_NOTE(first_time_experience_content, first_time_experience_content2): Message describing
|
||||
## ways to use Hello project.
|
||||
|
||||
## First Time Experience Slides
|
||||
## LOCALIZATION_NOTE(fte_slide_1_copy): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
## LOCALIZATION_NOTE(fte_slide_2_copy2): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
## LOCALIZATION_NOTE(fte_slide_3_copy): {{clientSuperShortname}}
|
||||
## will be replaced by the super short brand name.
|
||||
## LOCALIZATION_NOTE(fte_slide_4_title): {{clientSuperShortname}}
|
||||
## will be replaced by the super short brand name.
|
||||
## LOCALIZATION_NOTE(fte_slide_4_copy): {{brandShortname}}
|
||||
## will be replaced by the brand short name.
|
||||
|
||||
## LOCALIZATION_NOTE(invite_copy_link_button, invite_copied_link_button,
|
||||
## invite_email_link_button, invite_facebook_button2): These labels appear under
|
||||
## an iconic button for the invite view.
|
||||
|
||||
# Status text
|
||||
|
||||
# Error bars
|
||||
## LOCALIZATION NOTE(session_expired_error_description,could_not_authenticate,password_changed_question,try_again_later,could_not_connect,check_internet_connection,login_expired,service_not_available,problem_accessing_account):
|
||||
## These may be displayed at the top of the panel.
|
||||
@ -112,12 +121,8 @@ tour_label=Njillu
|
||||
# Infobar strings
|
||||
|
||||
|
||||
# Context in conversation strings
|
||||
# Copy panel strings
|
||||
|
||||
## LOCALIZATION NOTE(no_conversations_message_heading2): Title shown when user
|
||||
## has no conversations available.
|
||||
## LOCALIZATION NOTE(no_conversations_start_message2): Subheading inviting the
|
||||
## user to start a new conversation.
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
|
@ -24,6 +24,7 @@ sign_in_again_button=Kirjaudu
|
||||
sign_in_again_use_as_guest_button2=Käytä {{clientSuperShortname}}-palvelua vierastunnuksilla
|
||||
|
||||
panel_browse_with_friend_button=Selaa sivua kaverin kanssa
|
||||
panel_disconnect_button=Katkaise
|
||||
|
||||
## LOCALIZATION_NOTE(first_time_experience_subheading2, first_time_experience_subheading_button_above): Message inviting the
|
||||
## user to create his or her first conversation.
|
||||
@ -37,6 +38,7 @@ first_time_experience_button_label2=Katso miten se toimii
|
||||
## First Time Experience Slides
|
||||
## LOCALIZATION_NOTE(fte_slide_1_copy): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
fte_slide_2_title2=Tehty verkon jakamista varten
|
||||
## LOCALIZATION_NOTE(fte_slide_2_copy2): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
## LOCALIZATION_NOTE(fte_slide_3_copy): {{clientSuperShortname}}
|
||||
@ -93,6 +95,7 @@ share_add_service_button=Lisää palvelu
|
||||
## These menu items are displayed from a panel's context menu for a conversation.
|
||||
copy_link_menuitem=Kopioi linkki
|
||||
email_link_menuitem=Lähetä linkki
|
||||
edit_name_menuitem=Muokkaa nimeä
|
||||
delete_conversation_menuitem2=Poista
|
||||
|
||||
panel_footer_signin_or_signup_link=Kirjaudu tai rekisteröidy
|
||||
@ -180,15 +183,23 @@ rooms_signout_alert=Avoimet keskustelut suljetaan
|
||||
room_name_untitled_page=Nimetön sivu
|
||||
|
||||
## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
|
||||
door_hanger_return=Nähdään myöhemmin! Voit palata tähän jaettuun istuntoon milloin tahansa Hello-paneelin kautta.
|
||||
door_hanger_prompt_name=Haluatko antaa sille nimen, joka on helpompi muistaa? Nykyinen nimi:
|
||||
door_hanger_button=OK
|
||||
door_hanger_bye=Nähdään taas!
|
||||
door_hanger_current=Nykyinen nimi:
|
||||
door_hanger_button2=Okei!
|
||||
|
||||
# Infobar strings
|
||||
|
||||
infobar_screenshare_browser_message2=Jaat parhaillaan välilehtiäsi. Kaverisi näkevät kaikki välilehdet, joita napsautat
|
||||
infobar_button_stop_label2=Lopeta jakaminen
|
||||
infobar_button_stop_accesskey=L
|
||||
infobar_button_disconnect_label=Katkaise yhteys
|
||||
infobar_button_disconnect_accesskey=K
|
||||
|
||||
# Copy panel strings
|
||||
|
||||
copy_panel_dont_show_again_label=Älä näytä tätä uudelleen
|
||||
copy_panel_cancel_button_label=Ei nyt
|
||||
copy_panel_accept_button_label=Kyllä, näytä miten
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
@ -234,6 +245,7 @@ rooms_room_join_label=Liity keskusteluun
|
||||
|
||||
self_view_hidden_message=Omanäkymä piilotettu, mutta lähetetään edelleen. Muuta ikkunan kokoa nähdäksesi.
|
||||
|
||||
peer_left_session=Kaverisi lähti.
|
||||
|
||||
## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
|
||||
## as this will be replaced by clientShortname2.
|
||||
|
@ -193,16 +193,18 @@ rooms_signout_alert=Les conversations ouvertes vont être fermées
|
||||
room_name_untitled_page=Page sans titre
|
||||
|
||||
## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
|
||||
door_hanger_return=À bientôt ! Vous pouvez accéder à cette session partagée à n’importe quel moment depuis le panneau Hello.
|
||||
door_hanger_prompt_name=Voulez-vous lui donner un nom pour la mémoriser plus facilement ? Nom actuel :
|
||||
door_hanger_button=OK
|
||||
door_hanger_bye=À bientôt !
|
||||
door_hanger_return2=Vous pouvez rejoindre cette session partagée à n’importe moment depuis le panneau Hello. Voulez-vous lui donner un nom facile à retenir ?
|
||||
door_hanger_current=Nom actuel :
|
||||
door_hanger_button2=Ok
|
||||
|
||||
# Infobar strings
|
||||
|
||||
infobar_screenshare_no_guest_message=Dès que l’autre personne suivra le lien, elle pourra voir tous les onglets sur lesquels vous cliquerez.
|
||||
infobar_screenshare_browser_message2=Vous partagez vos onglets. Vos amis pourront voir tous les onglets sur lesquels vous cliquez.
|
||||
infobar_screenshare_browser_message3=Vous partagez à présent vos onglets. Votre contact verra tous les onglets sur lesquels vous cliquerez.
|
||||
infobar_screenshare_stop_sharing_message=Vous ne partagez plus vos onglets
|
||||
infobar_screenshare_stop_sharing_message2=Vous ne partagez plus vos onglets.
|
||||
infobar_screenshare_stop_no_guest_message=Vous avez arrêté de partager vos onglets. Lorsqu’une autre personne rejoindra la conversation, elle ne pourra rien voir jusqu’à ce que vous recommenciez à partager vos onglets.
|
||||
infobar_button_restart_label2=Recommencer à partager
|
||||
infobar_button_restart_accesskey=e
|
||||
infobar_button_stop_label2=Arrêter le partage
|
||||
@ -210,6 +212,13 @@ infobar_button_stop_accesskey=A
|
||||
infobar_button_disconnect_label=Déconnexion
|
||||
infobar_button_disconnect_accesskey=D
|
||||
|
||||
# Copy panel strings
|
||||
|
||||
copy_panel_message=Besoin de partager cette page web ? Partagez l’onglet du navigateur avec un ami.
|
||||
copy_panel_dont_show_again_label=Ne plus afficher ce message
|
||||
copy_panel_cancel_button_label=Plus tard
|
||||
copy_panel_accept_button_label=Oui, me montrer comment faire
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
e10s_not_supported_button_label=Ouvrir une nouvelle fenêtre
|
||||
|
@ -193,16 +193,18 @@ rooms_signout_alert=Iepene petearen sille sluten wurde
|
||||
room_name_untitled_page=Titelleaze side
|
||||
|
||||
## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
|
||||
door_hanger_return=Oant letter! Jo kinne hjir altyd wer nei dizze dield sesje weromkeare fia it Hello-paniel.
|
||||
door_hanger_prompt_name=Wolle jo it in makliker te ûnthâlden namme jaan? Aktuele namme:
|
||||
door_hanger_button=OK
|
||||
door_hanger_bye=Oant sjen!
|
||||
door_hanger_return2=Jo kinne op elts momint weromkeare nei dizze dielde sesje fia it Hello-paniel. Wolle jo it in namme jaan dy't makliker te ûnthâlden is?
|
||||
door_hanger_current=Aktuele namme:
|
||||
door_hanger_button2=OK!
|
||||
|
||||
# Infobar strings
|
||||
|
||||
infobar_screenshare_no_guest_message=Sa gau as jo freon dielnimt, kin dizze elts ljepblêd sjen wêrop jo klikke.
|
||||
infobar_screenshare_browser_message2=Jo diele jo ljepblêden. Elts ljepblêd dat jo oanklikke kin sjoen wurde troch jo freonen
|
||||
infobar_screenshare_browser_message3=Jo diele no jo ljepblêden. Jo freon sjocht elts ljepblêd wêrop jo klikke.
|
||||
infobar_screenshare_stop_sharing_message=Jo diele net langer jo ljepblêden
|
||||
infobar_screenshare_stop_sharing_message2=Jo diele net langer jo ljepblêden.
|
||||
infobar_screenshare_stop_no_guest_message=Jo diele net langer jo ljepblêden. As jo freon dielnimt, sil der neat te sjen wêze oant jo opnij diele.
|
||||
infobar_button_restart_label2=Dielen ferfetsje
|
||||
infobar_button_restart_accesskey=D
|
||||
infobar_button_stop_label2=Dielen stopje
|
||||
@ -210,6 +212,13 @@ infobar_button_stop_accesskey=S
|
||||
infobar_button_disconnect_label=Ferbining ferbrekke
|
||||
infobar_button_disconnect_accesskey=F
|
||||
|
||||
# Copy panel strings
|
||||
|
||||
copy_panel_message=Wolle jo dizze webside diele? Diel jo browserljepblêd mei in freon.
|
||||
copy_panel_dont_show_again_label=Net mear toane
|
||||
copy_panel_cancel_button_label=No net
|
||||
copy_panel_accept_button_label=Ja, lit it my sjen
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
e10s_not_supported_button_label=Nij finster iepenje
|
||||
|
@ -193,16 +193,18 @@ rooms_signout_alert=Iepene petearen sille sluten wurde
|
||||
room_name_untitled_page=Titelleaze side
|
||||
|
||||
## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
|
||||
door_hanger_return=Oant letter! Jo kinne hjir altyd wer nei dizze dield sesje weromkeare fia it Hello-paniel.
|
||||
door_hanger_prompt_name=Wolle jo it in makliker te ûnthâlden namme jaan? Aktuele namme:
|
||||
door_hanger_button=OK
|
||||
door_hanger_bye=Oant sjen!
|
||||
door_hanger_return2=Jo kinne op elts momint weromkeare nei dizze dielde sesje fia it Hello-paniel. Wolle jo it in namme jaan dy't makliker te ûnthâlden is?
|
||||
door_hanger_current=Aktuele namme:
|
||||
door_hanger_button2=OK!
|
||||
|
||||
# Infobar strings
|
||||
|
||||
infobar_screenshare_no_guest_message=Sa gau as jo freon dielnimt, kin dizze elts ljepblêd sjen wêrop jo klikke.
|
||||
infobar_screenshare_browser_message2=Jo diele jo ljepblêden. Elts ljepblêd dat jo oanklikke kin sjoen wurde troch jo freonen
|
||||
infobar_screenshare_browser_message3=Jo diele no jo ljepblêden. Jo freon sjocht elts ljepblêd wêrop jo klikke.
|
||||
infobar_screenshare_stop_sharing_message=Jo diele net langer jo ljepblêden
|
||||
infobar_screenshare_stop_sharing_message2=Jo diele net langer jo ljepblêden.
|
||||
infobar_screenshare_stop_no_guest_message=Jo diele net langer jo ljepblêden. As jo freon dielnimt, sil der neat te sjen wêze oant jo opnij diele.
|
||||
infobar_button_restart_label2=Dielen ferfetsje
|
||||
infobar_button_restart_accesskey=D
|
||||
infobar_button_stop_label2=Dielen stopje
|
||||
@ -210,6 +212,13 @@ infobar_button_stop_accesskey=S
|
||||
infobar_button_disconnect_label=Ferbining ferbrekke
|
||||
infobar_button_disconnect_accesskey=F
|
||||
|
||||
# Copy panel strings
|
||||
|
||||
copy_panel_message=Wolle jo dizze webside diele? Diel jo browserljepblêd mei in freon.
|
||||
copy_panel_dont_show_again_label=Net mear toane
|
||||
copy_panel_cancel_button_label=No net
|
||||
copy_panel_accept_button_label=Ja, lit it my sjen
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
e10s_not_supported_button_label=Nij finster iepenje
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
# Panel Strings
|
||||
|
||||
|
||||
|
||||
## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
|
||||
## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
|
||||
## use "..." if \u2026 doesn't suit traditions in your locale.
|
||||
@ -44,8 +42,8 @@ fte_slide_1_title=Brabhsaich duilleagan-lìn còmhla ri caraid
|
||||
## LOCALIZATION_NOTE(fte_slide_1_copy): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
fte_slide_1_copy=Co-dhiù a bheil thu airson turas a chur air dòigh no prèasant a cheannach, nì thu co-dhùnaidhean nas luaithe le {{clientShortname2}}.
|
||||
fte_slide_2_title=Iomair an aon ràmh
|
||||
fte_slide_2_copy=Cleachd a’ chabadaich teacsa no bhideo ’na broinn gu beachdan a cho-roinneadh ’s coimeas a dhèanamh eadar roghainnean agus co-dhùnadh a ruigsinn.
|
||||
## LOCALIZATION_NOTE(fte_slide_2_copy2): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
fte_slide_3_title=Thoir cuireadh dha charaid le ceangal
|
||||
## LOCALIZATION_NOTE(fte_slide_3_copy): {{clientSuperShortname}}
|
||||
## will be replaced by the super short brand name.
|
||||
@ -57,9 +55,7 @@ fte_slide_4_title=Lorg ìomhaigheag {{clientSuperShortname}} airson tòiseachadh
|
||||
## will be replaced by the brand short name.
|
||||
fte_slide_4_copy=Nuair a bhios tu air duilleag a lorg a bu toigh leat bruidhinn oirre, briog air an ìomhaigheag aig {{brandShortname}} gus ceangal a chruthachadh. Cuir e dhan charaid agad an uairsin air dòigh sam bith a thogras tu!
|
||||
|
||||
invite_header_text_bold=Thoir cuireadh do chuideigin a rùrachadh na duilleige seo còmhla riut!
|
||||
invite_header_text_bold2=Thoir cuireadh do charaid ach an ceangail e riut!
|
||||
invite_header_text3=Feumaidh tu co-dhiù dithis mus obraich Firefox Hello, nach cuir thu ceangal gu caraid a rùraicheas an lìon còmhla riut?
|
||||
invite_header_text4=Co-roinn an ceangal seo ach am brabhsaich sibh an lìon còmhla.
|
||||
## LOCALIZATION_NOTE(invite_copy_link_button, invite_copied_link_button,
|
||||
## invite_email_link_button, invite_facebook_button2): These labels appear under
|
||||
@ -194,16 +190,12 @@ rooms_signout_alert=Thèid còmhraidhean fosgailte a dhùnadh
|
||||
room_name_untitled_page=Duilleag gun tiotal
|
||||
|
||||
## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
|
||||
door_hanger_return=Na bi fada gun tilleadh! Is urrainn dhut tilleadh gun t-seisean cho-roinnte seo uair sam bith slighe panail Hello.
|
||||
door_hanger_prompt_name=An doir thu ainm air a bhios nas fhasa ri chuimhneachadh? Na tha air an-dràsta:
|
||||
door_hanger_button=Bheir
|
||||
|
||||
# Infobar strings
|
||||
|
||||
infobar_screenshare_no_guest_message=Nuair a bhios do charaidean air an tighinn a-steach, chì iad gach taba a bhriogas tu air.
|
||||
infobar_screenshare_browser_message2=Tha thu a’ co-roinneadh nan tabaichean agad. Chì do charaidean taba sam bith a bhriogas tu air
|
||||
infobar_screenshare_browser_message3=Tha thu a’ co-roinneadh nan tabaichean agad a-nis. Chì do charaid gach taba a bhriogas tu air.
|
||||
infobar_screenshare_stop_sharing_message=Chan eil thu a’ co-roinneadh nan tabaichean agad tuilleadh
|
||||
infobar_button_restart_label2=Tòisich air co-roinneadh
|
||||
infobar_button_restart_accesskey=A
|
||||
infobar_button_stop_label2=Sguir a cho-roinneadh
|
||||
@ -211,6 +203,9 @@ infobar_button_stop_accesskey=S
|
||||
infobar_button_disconnect_label=Dì-cheangail
|
||||
infobar_button_disconnect_accesskey=D
|
||||
|
||||
# Copy panel strings
|
||||
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
e10s_not_supported_button_label=Fosgail uinneag ùr
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
# Panel Strings
|
||||
|
||||
|
||||
## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
|
||||
## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
|
||||
## use "..." if \u2026 doesn't suit traditions in your locale.
|
||||
@ -19,18 +18,28 @@
|
||||
## will be replaced by the super short brandname.
|
||||
|
||||
|
||||
## LOCALIZATION_NOTE(first_time_experience_subheading2): Message inviting the
|
||||
## LOCALIZATION_NOTE(first_time_experience_subheading2, first_time_experience_subheading_button_above): Message inviting the
|
||||
## user to create his or her first conversation.
|
||||
|
||||
## LOCALIZATION_NOTE(first_time_experience_content): Message describing
|
||||
## LOCALIZATION_NOTE(first_time_experience_content, first_time_experience_content2): Message describing
|
||||
## ways to use Hello project.
|
||||
|
||||
## First Time Experience Slides
|
||||
## LOCALIZATION_NOTE(fte_slide_1_copy): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
## LOCALIZATION_NOTE(fte_slide_2_copy2): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
## LOCALIZATION_NOTE(fte_slide_3_copy): {{clientSuperShortname}}
|
||||
## will be replaced by the super short brand name.
|
||||
## LOCALIZATION_NOTE(fte_slide_4_title): {{clientSuperShortname}}
|
||||
## will be replaced by the super short brand name.
|
||||
## LOCALIZATION_NOTE(fte_slide_4_copy): {{brandShortname}}
|
||||
## will be replaced by the brand short name.
|
||||
|
||||
## LOCALIZATION_NOTE(invite_copy_link_button, invite_copied_link_button,
|
||||
## invite_email_link_button, invite_facebook_button2): These labels appear under
|
||||
## an iconic button for the invite view.
|
||||
|
||||
# Status text
|
||||
|
||||
# Error bars
|
||||
## LOCALIZATION NOTE(session_expired_error_description,could_not_authenticate,password_changed_question,try_again_later,could_not_connect,check_internet_connection,login_expired,service_not_available,problem_accessing_account):
|
||||
## These may be displayed at the top of the panel.
|
||||
@ -98,12 +107,8 @@ hangup_button_caption2=יציאה
|
||||
# Infobar strings
|
||||
|
||||
|
||||
# Context in conversation strings
|
||||
# Copy panel strings
|
||||
|
||||
## LOCALIZATION NOTE(no_conversations_message_heading2): Title shown when user
|
||||
## has no conversations available.
|
||||
## LOCALIZATION NOTE(no_conversations_start_message2): Subheading inviting the
|
||||
## user to start a new conversation.
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
# Panel Strings
|
||||
|
||||
|
||||
## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
|
||||
## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
|
||||
## use "..." if \u2026 doesn't suit traditions in your locale.
|
||||
@ -19,18 +18,28 @@
|
||||
## will be replaced by the super short brandname.
|
||||
|
||||
|
||||
## LOCALIZATION_NOTE(first_time_experience_subheading2): Message inviting the
|
||||
## LOCALIZATION_NOTE(first_time_experience_subheading2, first_time_experience_subheading_button_above): Message inviting the
|
||||
## user to create his or her first conversation.
|
||||
|
||||
## LOCALIZATION_NOTE(first_time_experience_content): Message describing
|
||||
## LOCALIZATION_NOTE(first_time_experience_content, first_time_experience_content2): Message describing
|
||||
## ways to use Hello project.
|
||||
|
||||
## First Time Experience Slides
|
||||
## LOCALIZATION_NOTE(fte_slide_1_copy): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
## LOCALIZATION_NOTE(fte_slide_2_copy2): {{clientShortname2}}
|
||||
## will be replaced by the short name 2.
|
||||
## LOCALIZATION_NOTE(fte_slide_3_copy): {{clientSuperShortname}}
|
||||
## will be replaced by the super short brand name.
|
||||
## LOCALIZATION_NOTE(fte_slide_4_title): {{clientSuperShortname}}
|
||||
## will be replaced by the super short brand name.
|
||||
## LOCALIZATION_NOTE(fte_slide_4_copy): {{brandShortname}}
|
||||
## will be replaced by the brand short name.
|
||||
|
||||
## LOCALIZATION_NOTE(invite_copy_link_button, invite_copied_link_button,
|
||||
## invite_email_link_button, invite_facebook_button2): These labels appear under
|
||||
## an iconic button for the invite view.
|
||||
|
||||
# Status text
|
||||
|
||||
# Error bars
|
||||
## LOCALIZATION NOTE(session_expired_error_description,could_not_authenticate,password_changed_question,try_again_later,could_not_connect,check_internet_connection,login_expired,service_not_available,problem_accessing_account):
|
||||
## These may be displayed at the top of the panel.
|
||||
@ -112,12 +121,8 @@ tour_label=दौरा
|
||||
# Infobar strings
|
||||
|
||||
|
||||
# Context in conversation strings
|
||||
# Copy panel strings
|
||||
|
||||
## LOCALIZATION NOTE(no_conversations_message_heading2): Title shown when user
|
||||
## has no conversations available.
|
||||
## LOCALIZATION NOTE(no_conversations_start_message2): Subheading inviting the
|
||||
## user to start a new conversation.
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
|
@ -193,16 +193,18 @@ rooms_signout_alert=Wočinjene rozmołwy so začinja
|
||||
room_name_untitled_page=Strona bjez titula
|
||||
|
||||
## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
|
||||
door_hanger_return=Hač potom! Móžeće so k tutomu zhromadnemu posedźenju přez wokno Hello kóždy čas wróćić.
|
||||
door_hanger_prompt_name=Byšće prošu mjeno podać, kotrež da so sej lóšo spomjatkować? Aktualne mjeno:
|
||||
door_hanger_button=W porjadku
|
||||
door_hanger_bye=Hač potom!
|
||||
door_hanger_return2=Móžeće so k tutomu dźělenemu posedźenju kóždy čas přez wobłuk Hello wróćić. Chceće jemu mjeno dać, kotrež móžeće sej lóšo spomjatkować?
|
||||
door_hanger_current=Aktualne mjeno:
|
||||
door_hanger_button2=W porjadku!
|
||||
|
||||
# Infobar strings
|
||||
|
||||
infobar_screenshare_no_guest_message=Tak ruče kaž so waš přećel přidruža, móžetaj rajtark widźeć, na kotryž kliknjeće.
|
||||
infobar_screenshare_browser_message2=Dźěliće swoje rajtarki. Waši přećeljo móža kóždy rajtark widźeć, na kotryž kliknjeće
|
||||
infobar_screenshare_browser_message3=Dźělitaj nětko swoje rajtarki. Waš přećel budźe rajtark widźeć, na kotryž kliknjeće.
|
||||
infobar_screenshare_stop_sharing_message=Hižo swoje rajtarki njedźěliće
|
||||
infobar_screenshare_stop_sharing_message2=Swoje rajtarki hižo njedźěliće.
|
||||
infobar_screenshare_stop_no_guest_message=Swoje rajtarki hižo njedźěliće. Hdyž so waš přećel přidruža, njemóže ničo widźeć, doniž zaso njedźěliće.
|
||||
infobar_button_restart_label2=Dźělenje znowa startować
|
||||
infobar_button_restart_accesskey=n
|
||||
infobar_button_stop_label2=Dźělenje zastajić
|
||||
@ -210,6 +212,13 @@ infobar_button_stop_accesskey=S
|
||||
infobar_button_disconnect_label=Zwisk dźělić
|
||||
infobar_button_disconnect_accesskey=Z
|
||||
|
||||
# Copy panel strings
|
||||
|
||||
copy_panel_message=Chceće tutu webstronu dźělić? Dźělće swój wobhladowakowy rajtark z přećelom.
|
||||
copy_panel_dont_show_again_label=Hižo so njepokazać
|
||||
copy_panel_cancel_button_label=Nic nětko
|
||||
copy_panel_accept_button_label=Haj, pokazajće mi kak
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
e10s_not_supported_button_label=Nowe wokno wočinić
|
||||
|
@ -193,16 +193,18 @@ rooms_signout_alert=A nyitott beszélgetések bezáródnak
|
||||
room_name_untitled_page=Névtelen oldal
|
||||
|
||||
## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
|
||||
door_hanger_return=Viszontlátásra! Bármikor visszatérhet ehhez a megosztott munkamenethez a Hello panelen.
|
||||
door_hanger_prompt_name=Szeretne egy könnyebben megjegyezhető nevet adni neki? A jelenlegi név:
|
||||
door_hanger_button=OK
|
||||
door_hanger_bye=Később találkozunk!
|
||||
door_hanger_return2=A Hello panel segítségével bármikor visszatérhet a megosztott munkamenethez. Szeretne egy könnyebben megjegyezhető nevet megadni?
|
||||
door_hanger_current=Jelenlegi név:
|
||||
door_hanger_button2=OK!
|
||||
|
||||
# Infobar strings
|
||||
|
||||
infobar_screenshare_no_guest_message=Amint ismerőse bejelentkezik, máris láthatja azokat a lapjait, amelyekre kattint.
|
||||
infobar_screenshare_browser_message2=Megosztja a lapjait. Ismerősei látni fogják bármely lap tartalmát, amelyre rákattint.
|
||||
infobar_screenshare_browser_message3=Mostmár megosztja a lapjait. Ismerőse láthatja bármely lap tartalmát, amelyre rákattint.
|
||||
infobar_screenshare_stop_sharing_message=Már nem osztja meg böngészőlapjait
|
||||
infobar_screenshare_stop_sharing_message2=Már nem osztja meg böngészőlapjait.
|
||||
infobar_screenshare_stop_no_guest_message=Leállította a böngészőlapjai megosztását. Amikor egy ismerőse csatlakozik, akkor nem fog semmit sem látni addig, amíg újra nem indítja a megosztást.
|
||||
infobar_button_restart_label2=Megosztás újrakezdése
|
||||
infobar_button_restart_accesskey=r
|
||||
infobar_button_stop_label2=Megosztás leállítása
|
||||
@ -210,6 +212,13 @@ infobar_button_stop_accesskey=L
|
||||
infobar_button_disconnect_label=Bontás
|
||||
infobar_button_disconnect_accesskey=o
|
||||
|
||||
# Copy panel strings
|
||||
|
||||
copy_panel_message=Meg kell osztani ezt a weboldalt? Ossza meg a böngészőlapját egy ismerősével.
|
||||
copy_panel_dont_show_again_label=Ne mutassa ezt újra
|
||||
copy_panel_cancel_button_label=Most nem
|
||||
copy_panel_accept_button_label=Igen, mutassa meg hogyan
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
e10s_not_supported_button_label=Új ablak indítása
|
||||
|
@ -193,16 +193,18 @@ rooms_signout_alert=Զրույցները կփակվեն
|
||||
room_name_untitled_page=Անանուն էջ
|
||||
|
||||
## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
|
||||
door_hanger_return=Առայժմ: Կարող եք ցանկացած ժամանակ վերադառնալ համաօգտագործվող այս աշխատաշրջան՝ Hello վահանակի միջոցով:
|
||||
door_hanger_prompt_name=Ցանկանու՞մ եք տալ անուն, որ հեշտ հիշվի: Ընթացիկ անունը՝
|
||||
door_hanger_button=Լավ
|
||||
door_hanger_bye=Կտեսնվենք ավելի ուշ
|
||||
door_hanger_return2=Կարող եք վերադառնալ համաօգտագործվող այս աշխատաշրջանին ցանկացած ժամանակ՝ Hello-ի վահանակի միջոցով: Ցանկանու՞մ եք տալ անուն՝ հեշտ հիշելու համար:
|
||||
door_hanger_current=Ընթացիկ անուն.
|
||||
door_hanger_button2=Լավ
|
||||
|
||||
# Infobar strings
|
||||
|
||||
infobar_screenshare_no_guest_message=Երբ ձեր ընկերը միանա՝ կկարողանա տեսնել բոլոր այն ներդիրները, որոնց դուք կսեղմեք:
|
||||
infobar_screenshare_browser_message2=Դուք համաօգտագործում եք ձեր ներդիրները: Ցանկացած ներդիր, որ սեղմում եք կտեսնեն ձեր ընկերները:
|
||||
infobar_screenshare_browser_message3=Այժմ դուք համաօգտագործում եք ձեր ներդիրները: Ձեր ընկերը կտեսնի բոլոր այն ներդիրները, որոնց դուք կսեղմեք:
|
||||
infobar_screenshare_stop_sharing_message=Դուք այլևս չեք համաօգտագործում ձեր ներդիրները
|
||||
infobar_screenshare_stop_sharing_message2=Դուք այլևս չեք համաօգտագործում ձեր ներդիրները:
|
||||
infobar_screenshare_stop_no_guest_message=Դուք այլևս չեք համաօգտագործում ձեր ներդիրները: Երբ ձեր ընկերը միանա՝ դրանք չի տեսնի, մինչև որ չվերսկսեք համաօգտագործումը:
|
||||
infobar_button_restart_label2=Վերսկսել համաօգտագործումը
|
||||
infobar_button_restart_accesskey=e
|
||||
infobar_button_stop_label2=Կանգնեցնել համաօգտագործումը
|
||||
@ -210,6 +212,13 @@ infobar_button_stop_accesskey=S
|
||||
infobar_button_disconnect_label=Կապախզել
|
||||
infobar_button_disconnect_accesskey=D
|
||||
|
||||
# Copy panel strings
|
||||
|
||||
copy_panel_message=Ցանկանու՞մ եք համաօգտագործել այս վեբ էջը: Համաօգտագործեք դիտարկիչի ներդիրը ընկերոջ հետ:
|
||||
copy_panel_dont_show_again_label=Այլևս չցուցադրել
|
||||
copy_panel_cancel_button_label=Ոչ հիմա
|
||||
copy_panel_accept_button_label=Այո, ցույց տալ, թե ինչպես
|
||||
|
||||
# E10s not supported strings
|
||||
|
||||
e10s_not_supported_button_label=Բացել նոր պատուհան
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user