diff --git a/browser/extensions/activity-stream/common/Actions.jsm b/browser/extensions/activity-stream/common/Actions.jsm index d400d6769d16..56ac20e88834 100644 --- a/browser/extensions/activity-stream/common/Actions.jsm +++ b/browser/extensions/activity-stream/common/Actions.jsm @@ -3,47 +3,19 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; -this.MAIN_MESSAGE_TYPE = "ActivityStream:Main"; -this.CONTENT_MESSAGE_TYPE = "ActivityStream:Content"; -this.UI_CODE = 1; -this.BACKGROUND_PROCESS = 2; - -/** - * globalImportContext - Are we in UI code (i.e. react, a dom) or some kind of background process? - * Use this in action creators if you need different logic - * for ui/background processes. - */ -const globalImportContext = typeof Window === "undefined" ? BACKGROUND_PROCESS : UI_CODE; -// Export for tests -this.globalImportContext = globalImportContext; +const MAIN_MESSAGE_TYPE = "ActivityStream:Main"; +const CONTENT_MESSAGE_TYPE = "ActivityStream:Content"; const actionTypes = [ - "BLOCK_URL", - "BOOKMARK_URL", - "DELETE_BOOKMARK_BY_ID", - "DELETE_HISTORY_URL", "INIT", - "LOCALE_UPDATED", + "UNINIT", "NEW_TAB_INITIAL_STATE", "NEW_TAB_LOAD", "NEW_TAB_UNLOAD", - "NEW_TAB_VISIBLE", - "OPEN_NEW_WINDOW", - "OPEN_PRIVATE_WINDOW", "PERFORM_SEARCH", - "PLACES_BOOKMARK_ADDED", - "PLACES_BOOKMARK_CHANGED", - "PLACES_BOOKMARK_REMOVED", - "PLACES_HISTORY_CLEARED", - "PLACES_LINK_BLOCKED", - "PLACES_LINK_DELETED", "SCREENSHOT_UPDATED", "SEARCH_STATE_UPDATED", - "TELEMETRY_PERFORMANCE_EVENT", - "TELEMETRY_UNDESIRED_EVENT", - "TELEMETRY_USER_EVENT", - "TOP_SITES_UPDATED", - "UNINIT" + "TOP_SITES_UPDATED" // The line below creates an object like this: // { // INIT: "INIT", @@ -76,14 +48,14 @@ function _RouteMessage(action, options) { * * @param {object} action Any redux action (required) * @param {object} options - * @param {string} fromTarget The id of the content port from which the action originated. (optional) + * @param {string} options.fromTarget The id of the content port from which the action originated. (optional) * @return {object} An action with added .meta properties */ -function SendToMain(action, fromTarget) { +function SendToMain(action, options = {}) { return _RouteMessage(action, { from: CONTENT_MESSAGE_TYPE, to: MAIN_MESSAGE_TYPE, - fromTarget + fromTarget: options.fromTarget }); } @@ -118,59 +90,12 @@ function SendToContent(action, target) { }); } -/** - * UserEvent - A telemetry ping indicating a user action. This should only - * be sent from the UI during a user session. - * - * @param {object} data Fields to include in the ping (source, etc.) - * @return {object} An SendToMain action - */ -function UserEvent(data) { - return SendToMain({ - type: actionTypes.TELEMETRY_USER_EVENT, - data - }); -} - -/** - * UndesiredEvent - A telemetry ping indicating an undesired state. - * - * @param {object} data Fields to include in the ping (value, etc.) - * @param {int} importContext (For testing) Override the import context for testing. - * @return {object} An action. For UI code, a SendToMain action. - */ -function UndesiredEvent(data, importContext = globalImportContext) { - const action = { - type: actionTypes.TELEMETRY_UNDESIRED_EVENT, - data - }; - return importContext === UI_CODE ? SendToMain(action) : action; -} - -/** - * PerfEvent - A telemetry ping indicating a performance-related event. - * - * @param {object} data Fields to include in the ping (value, etc.) - * @param {int} importContext (For testing) Override the import context for testing. - * @return {object} An action. For UI code, a SendToMain action. - */ -function PerfEvent(data, importContext = globalImportContext) { - const action = { - type: actionTypes.TELEMETRY_PERFORMANCE_EVENT, - data - }; - return importContext === UI_CODE ? SendToMain(action) : action; -} - this.actionTypes = actionTypes; this.actionCreators = { - BroadcastToContent, - UserEvent, - UndesiredEvent, - PerfEvent, + SendToMain, SendToContent, - SendToMain + BroadcastToContent }; // These are helpers to test for certain kinds of actions @@ -199,9 +124,6 @@ this.actionUtils = { } return false; }, - getPortIdOfSender(action) { - return (action.meta && action.meta.fromTarget) || null; - }, _RouteMessage }; @@ -209,9 +131,6 @@ this.EXPORTED_SYMBOLS = [ "actionTypes", "actionCreators", "actionUtils", - "globalImportContext", - "UI_CODE", - "BACKGROUND_PROCESS", "MAIN_MESSAGE_TYPE", "CONTENT_MESSAGE_TYPE" ]; diff --git a/browser/extensions/activity-stream/common/Reducers.jsm b/browser/extensions/activity-stream/common/Reducers.jsm index cfd8d066524f..048aa1112afe 100644 --- a/browser/extensions/activity-stream/common/Reducers.jsm +++ b/browser/extensions/activity-stream/common/Reducers.jsm @@ -6,52 +6,20 @@ const {actionTypes: at} = Components.utils.import("resource://activity-stream/common/Actions.jsm", {}); const INITIAL_STATE = { - App: { - // Have we received real data from the app yet? - initialized: false, - // The locale of the browser - locale: "", - // Localized strings with defaults - strings: {}, - // The version of the system-addon - version: null - }, TopSites: { - // Have we received real data from history yet? - initialized: false, - // The history (and possibly default) links + init: false, rows: [] }, Search: { - // The search engine currently set by the browser currentEngine: { name: "", icon: "" }, - // All possible search engines engines: [] } }; -function App(prevState = INITIAL_STATE.App, action) { - switch (action.type) { - case at.INIT: - return Object.assign({}, action.data || {}, {initialized: true}); - case at.LOCALE_UPDATED: { - if (!action.data) { - return prevState; - } - let {locale, strings} = action.data; - return Object.assign({}, prevState, { - locale, - strings - }); - } - default: - return prevState; - } -} - +// TODO: Handle some real actions here, once we have a TopSites feed working function TopSites(prevState = INITIAL_STATE.TopSites, action) { let hasMatch; let newRows; @@ -60,7 +28,7 @@ function TopSites(prevState = INITIAL_STATE.TopSites, action) { if (!action.data) { return prevState; } - return Object.assign({}, prevState, {initialized: true, rows: action.data}); + return Object.assign({}, prevState, {init: true, rows: action.data}); case at.SCREENSHOT_UPDATED: newRows = prevState.rows.map(row => { if (row.url === action.data.url) { @@ -70,31 +38,6 @@ function TopSites(prevState = INITIAL_STATE.TopSites, action) { return row; }); return hasMatch ? Object.assign({}, prevState, {rows: newRows}) : prevState; - case at.PLACES_BOOKMARK_ADDED: - newRows = prevState.rows.map(site => { - if (site.url === action.data.url) { - const {bookmarkGuid, bookmarkTitle, lastModified} = action.data; - return Object.assign({}, site, {bookmarkGuid, bookmarkTitle, bookmarkDateCreated: lastModified}); - } - return site; - }); - return Object.assign({}, prevState, {rows: newRows}); - case at.PLACES_BOOKMARK_REMOVED: - newRows = prevState.rows.map(site => { - if (site.url === action.data.url) { - const newSite = Object.assign({}, site); - delete newSite.bookmarkGuid; - delete newSite.bookmarkTitle; - delete newSite.bookmarkDateCreated; - return newSite; - } - return site; - }); - return Object.assign({}, prevState, {rows: newRows}); - case at.PLACES_LINK_DELETED: - case at.PLACES_LINK_BLOCKED: - newRows = prevState.rows.filter(val => val.url !== action.data.url); - return Object.assign({}, prevState, {rows: newRows}); default: return prevState; } @@ -117,6 +60,6 @@ function Search(prevState = INITIAL_STATE.Search, action) { } } this.INITIAL_STATE = INITIAL_STATE; -this.reducers = {TopSites, App, Search}; +this.reducers = {TopSites, Search}; this.EXPORTED_SYMBOLS = ["reducers", "INITIAL_STATE"]; diff --git a/browser/extensions/activity-stream/data/content/activity-stream.bundle.js b/browser/extensions/activity-stream/data/content/activity-stream.bundle.js index 8591201e977a..e9c00838f8be 100644 --- a/browser/extensions/activity-stream/data/content/activity-stream.bundle.js +++ b/browser/extensions/activity-stream/data/content/activity-stream.bundle.js @@ -63,7 +63,7 @@ /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = 15); +/******/ return __webpack_require__(__webpack_require__.s = 10); /******/ }) /************************************************************************/ /******/ ([ @@ -74,6 +74,12 @@ module.exports = React; /***/ }), /* 1 */ +/***/ (function(module, exports) { + +module.exports = ReactRedux; + +/***/ }), +/* 2 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82,22 +88,10 @@ module.exports = React; * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -var MAIN_MESSAGE_TYPE = "ActivityStream:Main"; -var CONTENT_MESSAGE_TYPE = "ActivityStream:Content"; -var UI_CODE = 1; -var BACKGROUND_PROCESS = 2; +const MAIN_MESSAGE_TYPE = "ActivityStream:Main"; +const CONTENT_MESSAGE_TYPE = "ActivityStream:Content"; -/** - * globalImportContext - Are we in UI code (i.e. react, a dom) or some kind of background process? - * Use this in action creators if you need different logic - * for ui/background processes. - */ - -const globalImportContext = typeof Window === "undefined" ? BACKGROUND_PROCESS : UI_CODE; -// Export for tests - - -const actionTypes = ["BLOCK_URL", "BOOKMARK_URL", "DELETE_BOOKMARK_BY_ID", "DELETE_HISTORY_URL", "INIT", "LOCALE_UPDATED", "NEW_TAB_INITIAL_STATE", "NEW_TAB_LOAD", "NEW_TAB_UNLOAD", "NEW_TAB_VISIBLE", "OPEN_NEW_WINDOW", "OPEN_PRIVATE_WINDOW", "PERFORM_SEARCH", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_CHANGED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINK_BLOCKED", "PLACES_LINK_DELETED", "SCREENSHOT_UPDATED", "SEARCH_STATE_UPDATED", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "TOP_SITES_UPDATED", "UNINIT" +const actionTypes = ["INIT", "UNINIT", "NEW_TAB_INITIAL_STATE", "NEW_TAB_LOAD", "NEW_TAB_UNLOAD", "PERFORM_SEARCH", "SCREENSHOT_UPDATED", "SEARCH_STATE_UPDATED", "TOP_SITES_UPDATED" // The line below creates an object like this: // { // INIT: "INIT", @@ -132,14 +126,16 @@ function _RouteMessage(action, options) { * * @param {object} action Any redux action (required) * @param {object} options - * @param {string} fromTarget The id of the content port from which the action originated. (optional) + * @param {string} options.fromTarget The id of the content port from which the action originated. (optional) * @return {object} An action with added .meta properties */ -function SendToMain(action, fromTarget) { +function SendToMain(action) { + let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + return _RouteMessage(action, { from: CONTENT_MESSAGE_TYPE, to: MAIN_MESSAGE_TYPE, - fromTarget + fromTarget: options.fromTarget }); } @@ -174,61 +170,10 @@ function SendToContent(action, target) { }); } -/** - * UserEvent - A telemetry ping indicating a user action. This should only - * be sent from the UI during a user session. - * - * @param {object} data Fields to include in the ping (source, etc.) - * @return {object} An SendToMain action - */ -function UserEvent(data) { - return SendToMain({ - type: actionTypes.TELEMETRY_USER_EVENT, - data - }); -} - -/** - * UndesiredEvent - A telemetry ping indicating an undesired state. - * - * @param {object} data Fields to include in the ping (value, etc.) - * @param {int} importContext (For testing) Override the import context for testing. - * @return {object} An action. For UI code, a SendToMain action. - */ -function UndesiredEvent(data) { - let importContext = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : globalImportContext; - - const action = { - type: actionTypes.TELEMETRY_UNDESIRED_EVENT, - data - }; - return importContext === UI_CODE ? SendToMain(action) : action; -} - -/** - * PerfEvent - A telemetry ping indicating a performance-related event. - * - * @param {object} data Fields to include in the ping (value, etc.) - * @param {int} importContext (For testing) Override the import context for testing. - * @return {object} An action. For UI code, a SendToMain action. - */ -function PerfEvent(data) { - let importContext = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : globalImportContext; - - const action = { - type: actionTypes.TELEMETRY_PERFORMANCE_EVENT, - data - }; - return importContext === UI_CODE ? SendToMain(action) : action; -} - var actionCreators = { - BroadcastToContent, - UserEvent, - UndesiredEvent, - PerfEvent, + SendToMain, SendToContent, - SendToMain + BroadcastToContent }; // These are helpers to test for certain kinds of actions @@ -258,33 +203,39 @@ var actionUtils = { } return false; }, - getPortIdOfSender(action) { - return action.meta && action.meta.fromTarget || null; - }, _RouteMessage }; module.exports = { actionTypes, actionCreators, actionUtils, - globalImportContext, - UI_CODE, - BACKGROUND_PROCESS, MAIN_MESSAGE_TYPE, CONTENT_MESSAGE_TYPE }; -/***/ }), -/* 2 */ -/***/ (function(module, exports) { - -module.exports = ReactRedux; - /***/ }), /* 3 */ -/***/ (function(module, exports) { +/***/ (function(module, exports, __webpack_require__) { -module.exports = ReactIntl; +"use strict"; + + +const React = __webpack_require__(0); +const TopSites = __webpack_require__(8); +const Search = __webpack_require__(7); + +const Base = () => React.createElement( + "div", + { className: "outer-wrapper" }, + React.createElement( + "main", + null, + React.createElement(Search, null), + React.createElement(TopSites, null) + ) +); + +module.exports = Base; /***/ }), /* 4 */ @@ -293,162 +244,15 @@ module.exports = ReactIntl; "use strict"; -const React = __webpack_require__(0); - -var _require = __webpack_require__(2); - -const connect = _require.connect; - -var _require2 = __webpack_require__(3); - -const addLocaleData = _require2.addLocaleData, - IntlProvider = _require2.IntlProvider; - -const TopSites = __webpack_require__(12); -const Search = __webpack_require__(11); - -// Locales that should be displayed RTL -const RTL_LIST = ["ar", "he", "fa", "ur"]; - -// Add the locale data for pluralization and relative-time formatting for now, -// this just uses english locale data. We can make this more sophisticated if -// more features are needed. -function addLocaleDataForReactIntl(_ref) { - let locale = _ref.locale; - - addLocaleData([{ locale, parentLocale: "en" }]); - document.documentElement.lang = locale; - document.documentElement.dir = RTL_LIST.indexOf(locale.split("-")[0]) >= 0 ? "rtl" : "ltr"; -} - -class Base extends React.Component { - componentDidMount() { - // Also wait for the preloaded page to show, so the tab's title updates - addEventListener("visibilitychange", () => this.updateTitle(this.props.App), { once: true }); - } - componentWillUpdate(_ref2) { - let App = _ref2.App; - - if (App.locale !== this.props.App.locale) { - addLocaleDataForReactIntl(App); - this.updateTitle(App); - } - } - - updateTitle(_ref3) { - let strings = _ref3.strings; - - document.title = strings.newtab_page_title; - } - - render() { - var _props$App = this.props.App; - let locale = _props$App.locale, - strings = _props$App.strings, - initialized = _props$App.initialized; - - if (!initialized) { - return null; - } - - return React.createElement( - IntlProvider, - { key: locale, locale: locale, messages: strings }, - React.createElement( - "div", - { className: "outer-wrapper" }, - React.createElement( - "main", - null, - React.createElement(Search, null), - React.createElement(TopSites, null) - ) - ) - ); - } -} - -module.exports = connect(state => ({ App: state.App }))(Base); - -/***/ }), -/* 5 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var _require = __webpack_require__(1); - -const at = _require.actionTypes; - - -const VISIBLE = "visible"; -const VISIBILITY_CHANGE_EVENT = "visibilitychange"; - -module.exports = class DetectUserSessionStart { - constructor() { - let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - - // Overrides for testing - this.sendAsyncMessage = options.sendAsyncMessage || window.sendAsyncMessage; - this.document = options.document || document; - - this._onVisibilityChange = this._onVisibilityChange.bind(this); - } - - /** - * sendEventOrAddListener - Notify immediately if the page is already visible, - * or else set up a listener for when visibility changes. - * This is needed for accurate session tracking for telemetry, - * because tabs are pre-loaded. - */ - sendEventOrAddListener() { - if (this.document.visibilityState === VISIBLE) { - // If the document is already visible, to the user, send a notification - // immediately that a session has started. - this._sendEvent(); - } else { - // If the document is not visible, listen for when it does become visible. - this.document.addEventListener(VISIBILITY_CHANGE_EVENT, this._onVisibilityChange); - } - } - - /** - * _sendEvent - Sends a message to the main process to indicate the current tab - * is now visible to the user. - */ - _sendEvent() { - this.sendAsyncMessage("ActivityStream:ContentToMain", { type: at.NEW_TAB_VISIBLE }); - } - - /** - * _onVisibilityChange - If the visibility has changed to visible, sends a notification - * and removes the event listener. This should only be called once per tab. - */ - _onVisibilityChange() { - if (this.document.visibilityState === VISIBLE) { - this._sendEvent(); - this.document.removeEventListener(VISIBILITY_CHANGE_EVENT, this._onVisibilityChange); - } - } -}; - -/***/ }), -/* 6 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - /* globals sendAsyncMessage, addMessageListener */ -var _require = __webpack_require__(14); +var _require = __webpack_require__(9); const createStore = _require.createStore, combineReducers = _require.combineReducers, applyMiddleware = _require.applyMiddleware; -var _require2 = __webpack_require__(1); +var _require2 = __webpack_require__(2); const au = _require2.actionUtils; @@ -514,7 +318,7 @@ module.exports.OUTGOING_MESSAGE_NAME = OUTGOING_MESSAGE_NAME; module.exports.INCOMING_MESSAGE_NAME = INCOMING_MESSAGE_NAME; /***/ }), -/* 7 */ +/* 5 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -523,65 +327,26 @@ module.exports.INCOMING_MESSAGE_NAME = INCOMING_MESSAGE_NAME; * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -var _require = __webpack_require__(1); +var _require = __webpack_require__(2); const at = _require.actionTypes; const INITIAL_STATE = { - App: { - // Have we received real data from the app yet? - initialized: false, - // The locale of the browser - locale: "", - // Localized strings with defaults - strings: {}, - // The version of the system-addon - version: null - }, TopSites: { - // Have we received real data from history yet? - initialized: false, - // The history (and possibly default) links + init: false, rows: [] }, Search: { - // The search engine currently set by the browser currentEngine: { name: "", icon: "" }, - // All possible search engines engines: [] } }; -function App() { - let prevState = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : INITIAL_STATE.App; - let action = arguments[1]; - - switch (action.type) { - case at.INIT: - return Object.assign({}, action.data || {}, { initialized: true }); - case at.LOCALE_UPDATED: - { - if (!action.data) { - return prevState; - } - var _action$data = action.data; - let locale = _action$data.locale, - strings = _action$data.strings; - - return Object.assign({}, prevState, { - locale, - strings - }); - } - default: - return prevState; - } -} - +// TODO: Handle some real actions here, once we have a TopSites feed working function TopSites() { let prevState = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : INITIAL_STATE.TopSites; let action = arguments[1]; @@ -593,7 +358,7 @@ function TopSites() { if (!action.data) { return prevState; } - return Object.assign({}, prevState, { initialized: true, rows: action.data }); + return Object.assign({}, prevState, { init: true, rows: action.data }); case at.SCREENSHOT_UPDATED: newRows = prevState.rows.map(row => { if (row.url === action.data.url) { @@ -603,35 +368,6 @@ function TopSites() { return row; }); return hasMatch ? Object.assign({}, prevState, { rows: newRows }) : prevState; - case at.PLACES_BOOKMARK_ADDED: - newRows = prevState.rows.map(site => { - if (site.url === action.data.url) { - var _action$data2 = action.data; - const bookmarkGuid = _action$data2.bookmarkGuid, - bookmarkTitle = _action$data2.bookmarkTitle, - lastModified = _action$data2.lastModified; - - return Object.assign({}, site, { bookmarkGuid, bookmarkTitle, bookmarkDateCreated: lastModified }); - } - return site; - }); - return Object.assign({}, prevState, { rows: newRows }); - case at.PLACES_BOOKMARK_REMOVED: - newRows = prevState.rows.map(site => { - if (site.url === action.data.url) { - const newSite = Object.assign({}, site); - delete newSite.bookmarkGuid; - delete newSite.bookmarkTitle; - delete newSite.bookmarkDateCreated; - return newSite; - } - return site; - }); - return Object.assign({}, prevState, { rows: newRows }); - case at.PLACES_LINK_DELETED: - case at.PLACES_LINK_BLOCKED: - newRows = prevState.rows.filter(val => val.url !== action.data.url); - return Object.assign({}, prevState, { rows: newRows }); default: return prevState; } @@ -647,9 +383,9 @@ function Search() { if (!action.data) { return prevState; } - var _action$data3 = action.data; - let currentEngine = _action$data3.currentEngine, - engines = _action$data3.engines; + var _action$data = action.data; + let currentEngine = _action$data.currentEngine, + engines = _action$data.engines; return Object.assign({}, prevState, { currentEngine, @@ -660,20 +396,20 @@ function Search() { return prevState; } } -var reducers = { TopSites, App, Search }; +var reducers = { TopSites, Search }; module.exports = { reducers, INITIAL_STATE }; /***/ }), -/* 8 */ +/* 6 */ /***/ (function(module, exports) { module.exports = ReactDOM; /***/ }), -/* 9 */ +/* 7 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -681,218 +417,22 @@ module.exports = ReactDOM; const React = __webpack_require__(0); -class ContextMenu extends React.Component { - constructor(props) { - super(props); - this.hideContext = this.hideContext.bind(this); - } - hideContext() { - this.props.onUpdate(false); - } - componentWillMount() { - this.hideContext(); - } - componentDidUpdate(prevProps) { - if (this.props.visible && !prevProps.visible) { - setTimeout(() => { - window.addEventListener("click", this.hideContext); - }, 0); - } - if (!this.props.visible && prevProps.visible) { - window.removeEventListener("click", this.hideContext); - } - } - componentDidUnmount() { - window.removeEventListener("click", this.hideContext); - } - onKeyDown(event, option) { - switch (event.key) { - case "Tab": - // tab goes down in context menu, shift + tab goes up in context menu - // if we're on the last item, one more tab will close the context menu - // similarly, if we're on the first item, one more shift + tab will close it - if (event.shiftKey && option.first || !event.shiftKey && option.last) { - this.hideContext(); - } - break; - case "Enter": - this.hideContext(); - option.onClick(); - break; - } - } - render() { - return React.createElement( - "span", - { hidden: !this.props.visible, className: "context-menu" }, - React.createElement( - "ul", - { role: "menu", className: "context-menu-list" }, - this.props.options.map((option, i) => { - if (option.type === "separator") { - return React.createElement("li", { key: i, className: "separator" }); - } - return React.createElement( - "li", - { role: "menuitem", className: "context-menu-item", key: i }, - React.createElement( - "a", - { tabIndex: "0", - onKeyDown: e => this.onKeyDown(e, option), - onClick: () => { - this.hideContext(); - option.onClick(); - } }, - option.icon && React.createElement("span", { className: `icon icon-spacer icon-${option.icon}` }), - option.label - ) - ); - }) - ) - ); - } -} +var _require = __webpack_require__(1); -module.exports = ContextMenu; +const connect = _require.connect; -/***/ }), -/* 10 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -const React = __webpack_require__(0); - -var _require = __webpack_require__(3); - -const injectIntl = _require.injectIntl; - -const ContextMenu = __webpack_require__(9); - -var _require2 = __webpack_require__(1); +var _require2 = __webpack_require__(2); const actionTypes = _require2.actionTypes, actionCreators = _require2.actionCreators; -class LinkMenu extends React.Component { - getBookmarkStatus(site) { - return site.bookmarkGuid ? { - id: "menu_action_remove_bookmark", - icon: "bookmark-remove", - action: "DELETE_BOOKMARK_BY_ID", - data: site.bookmarkGuid - } : { - id: "menu_action_bookmark", - icon: "bookmark", - action: "BOOKMARK_URL", - data: site.url - }; - } - getDefaultContextMenu(site) { - return [{ - id: "menu_action_open_new_window", - icon: "new-window", - action: "OPEN_NEW_WINDOW", - data: { url: site.url } - }, { - id: "menu_action_open_private_window", - icon: "new-window-private", - action: "OPEN_PRIVATE_WINDOW", - data: { url: site.url } - }]; - } - getOptions() { - var _props = this.props; - const dispatch = _props.dispatch, - site = _props.site; - - // default top sites have a limited set of context menu options - - let options = this.getDefaultContextMenu(site); - - // all other top sites have all the following context menu options - if (!site.isDefault) { - options = [this.getBookmarkStatus(site), { type: "separator" }, ...options, { type: "separator" }, { - id: "menu_action_dismiss", - icon: "dismiss", - action: "BLOCK_URL", - data: site.url - }, { - id: "menu_action_delete", - icon: "delete", - action: "DELETE_HISTORY_URL", - data: site.url - }]; - } - options.forEach(option => { - let action = option.action, - data = option.data, - id = option.id, - type = option.type; - // Convert message ids to localized labels and add onClick function - - if (!type && id) { - option.label = this.props.intl.formatMessage(option); - option.onClick = () => dispatch(actionCreators.SendToMain({ type: actionTypes[action], data })); - } - }); - - // this is for a11y - we want to know which item is the first and which item - // is the last, so we can close the context menu accordingly - options[0].first = true; - options[options.length - 1].last = true; - return options; - } - render() { - return React.createElement(ContextMenu, { - visible: this.props.visible, - onUpdate: this.props.onUpdate, - options: this.getOptions() }); - } -} - -module.exports = injectIntl(LinkMenu); -module.exports._unconnected = LinkMenu; - -/***/ }), -/* 11 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -const React = __webpack_require__(0); - -var _require = __webpack_require__(2); - -const connect = _require.connect; - -var _require2 = __webpack_require__(3); - -const FormattedMessage = _require2.FormattedMessage, - injectIntl = _require2.injectIntl; - -var _require3 = __webpack_require__(1); - -const actionTypes = _require3.actionTypes, - actionCreators = _require3.actionCreators; - - -class Search extends React.Component { - constructor(props) { - super(props); - this.state = { searchString: "" }; - this.onClick = this.onClick.bind(this); - this.onChange = this.onChange.bind(this); - } - - componentWillMount() { - // Trigger initialization of ContentSearch in case it hasn't happened yet - dispatchEvent(new CustomEvent("ContentSearchClient", { detail: {} })); - } +const Search = React.createClass({ + displayName: "Search", + getInitialState() { + return { searchString: "" }; + }, performSearch(options) { let searchData = { engineName: options.engineName, @@ -901,58 +441,34 @@ class Search extends React.Component { healthReportKey: "newtab" }; this.props.dispatch(actionCreators.SendToMain({ type: actionTypes.PERFORM_SEARCH, data: searchData })); - } + }, onClick(event) { const currentEngine = this.props.Search.currentEngine; event.preventDefault(); this.performSearch({ engineName: currentEngine.name, searchString: this.state.searchString }); - } + }, onChange(event) { this.setState({ searchString: event.target.value }); - } + }, render() { return React.createElement( "form", { className: "search-wrapper" }, - React.createElement( - "label", - { htmlFor: "search-input", className: "search-label" }, - React.createElement( - "span", - { className: "sr-only" }, - React.createElement(FormattedMessage, { id: "search_web_placeholder" }) - ) - ), - React.createElement("input", { - id: "search-input", - maxLength: "256", + React.createElement("span", { className: "search-label" }), + React.createElement("input", { value: this.state.searchString, type: "search", onChange: this.onChange, - placeholder: this.props.intl.formatMessage({ id: "search_web_placeholder" }), - title: this.props.intl.formatMessage({ id: "search_web_placeholder" }), - type: "search", - value: this.state.searchString }), - React.createElement( - "button", - { - className: "search-button", - onClick: this.onClick, - title: this.props.intl.formatMessage({ id: "search_button" }) }, - React.createElement( - "span", - { className: "sr-only" }, - React.createElement(FormattedMessage, { id: "search_button" }) - ) - ) + maxLength: "256", title: "Submit search", + placeholder: "Search the Web" }), + React.createElement("button", { onClick: this.onClick }) ); } -} +}); -module.exports = connect(state => ({ Search: state.Search }))(injectIntl(Search)); -module.exports._unconnected = Search; +module.exports = connect(state => ({ Search: state.Search }))(Search); /***/ }), -/* 12 */ +/* 8 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -960,79 +476,13 @@ module.exports._unconnected = Search; const React = __webpack_require__(0); -var _require = __webpack_require__(2); +var _require = __webpack_require__(1); const connect = _require.connect; -var _require2 = __webpack_require__(3); -const FormattedMessage = _require2.FormattedMessage; - -const shortURL = __webpack_require__(13); -const LinkMenu = __webpack_require__(10); - -class TopSite extends React.Component { - constructor(props) { - super(props); - this.state = { showContextMenu: false, activeTile: null }; - } - toggleContextMenu(event, index) { - this.setState({ showContextMenu: true, activeTile: index }); - } - render() { - var _props = this.props; - const link = _props.link, - index = _props.index, - dispatch = _props.dispatch; - - const isContextMenuOpen = this.state.showContextMenu && this.state.activeTile === index; - const title = shortURL(link); - const screenshotClassName = `screenshot${link.screenshot ? " active" : ""}`; - const topSiteOuterClassName = `top-site-outer${isContextMenuOpen ? " active" : ""}`; - const style = { backgroundImage: link.screenshot ? `url(${link.screenshot})` : "none" }; - return React.createElement( - "li", - { className: topSiteOuterClassName, key: link.url }, - React.createElement( - "a", - { href: link.url }, - React.createElement( - "div", - { className: "tile", "aria-hidden": true }, - React.createElement( - "span", - { className: "letter-fallback" }, - title[0] - ), - React.createElement("div", { className: screenshotClassName, style: style }) - ), - React.createElement( - "div", - { className: "title" }, - title - ) - ), - React.createElement( - "button", - { className: "context-menu-button", - onClick: e => { - e.preventDefault(); - this.toggleContextMenu(e, index); - } }, - React.createElement( - "span", - { className: "sr-only" }, - `Open context menu for ${title}` - ) - ), - React.createElement(LinkMenu, { - dispatch: dispatch, - visible: isContextMenuOpen, - onUpdate: val => this.setState({ showContextMenu: val }), - site: link, - index: index }) - ); - } +function displayURL(url) { + return new URL(url).hostname.replace(/^www./, ""); } const TopSites = props => React.createElement( @@ -1041,84 +491,72 @@ const TopSites = props => React.createElement( React.createElement( "h3", { className: "section-title" }, - React.createElement(FormattedMessage, { id: "header_top_sites" }) + "Top Sites" ), React.createElement( "ul", { className: "top-sites-list" }, - props.TopSites.rows.map((link, index) => React.createElement(TopSite, { dispatch: props.dispatch, key: link.url, link: link, index: index })) + props.TopSites.rows.map(link => { + const title = displayURL(link.url); + const className = `screenshot${link.screenshot ? " active" : ""}`; + const style = { backgroundImage: link.screenshot ? `url(${link.screenshot})` : "none" }; + return React.createElement( + "li", + { key: link.url }, + React.createElement( + "a", + { href: link.url }, + React.createElement( + "div", + { className: "tile" }, + React.createElement( + "span", + { className: "letter-fallback", ariaHidden: true }, + title[0] + ), + React.createElement("div", { className: className, style: style }) + ), + React.createElement( + "div", + { className: "title" }, + title + ) + ) + ); + }) ) ); module.exports = connect(state => ({ TopSites: state.TopSites }))(TopSites); -module.exports._unconnected = TopSites; -module.exports.TopSite = TopSite; /***/ }), -/* 13 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -/** - * shortURL - Creates a short version of a link's url, used for display purposes - * e.g. {url: http://www.foosite.com, eTLD: "com"} => "foosite" - * - * @param {obj} link A link object - * {str} link.url (required)- The url of the link - * {str} link.eTLD (required) - The tld of the link - * e.g. for https://foo.org, the tld would be "org" - * Note that this property is added in various queries for ActivityStream - * via Services.eTLD.getPublicSuffix - * {str} link.hostname (optional) - The hostname of the url - * e.g. for http://www.hello.com/foo/bar, the hostname would be "www.hello.com" - * @return {str} A short url - */ -module.exports = function shortURL(link) { - if (!link.url && !link.hostname) { - return ""; - } - const eTLD = link.eTLD; - - const hostname = (link.hostname || new URL(link.url).hostname).replace(/^www\./i, ""); - - // Remove the eTLD (e.g., com, net) and the preceding period from the hostname - const eTLDLength = (eTLD || "").length || hostname.match(/\.com$/) && 3; - const eTLDExtra = eTLDLength > 0 ? -(eTLDLength + 1) : Infinity; - return hostname.slice(0, eTLDExtra).toLowerCase(); -}; - -/***/ }), -/* 14 */ +/* 9 */ /***/ (function(module, exports) { module.exports = Redux; /***/ }), -/* 15 */ +/* 10 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +/* globals addMessageListener, removeMessageListener */ const React = __webpack_require__(0); -const ReactDOM = __webpack_require__(8); -const Base = __webpack_require__(4); +const ReactDOM = __webpack_require__(6); +const Base = __webpack_require__(3); -var _require = __webpack_require__(2); +var _require = __webpack_require__(1); const Provider = _require.Provider; -const initStore = __webpack_require__(6); +const initStore = __webpack_require__(4); -var _require2 = __webpack_require__(7); +var _require2 = __webpack_require__(5); const reducers = _require2.reducers; -const DetectUserSessionStart = __webpack_require__(5); - -new DetectUserSessionStart().sendEventOrAddListener(); const store = initStore(reducers); diff --git a/browser/extensions/activity-stream/data/content/activity-stream.css b/browser/extensions/activity-stream/data/content/activity-stream.css index fdabfa596a4f..c1cad86f6191 100644 --- a/browser/extensions/activity-stream/data/content/activity-stream.css +++ b/browser/extensions/activity-stream/data/content/activity-stream.css @@ -6,9 +6,6 @@ html { *::after { box-sizing: inherit; } -*::-moz-focus-inner { - border: 0; } - body { margin: 0; } @@ -20,29 +17,6 @@ input { [hidden] { display: none !important; } -.icon { - display: inline-block; - width: 16px; - height: 16px; - background-size: 16px; - background-position: center center; - background-repeat: no-repeat; - vertical-align: middle; } - .icon.icon-spacer { - margin-inline-end: 8px; } - .icon.icon-bookmark { - background-image: url("assets/glyph-bookmark-16.svg"); } - .icon.icon-bookmark-remove { - background-image: url("assets/glyph-bookmark-remove-16.svg"); } - .icon.icon-delete { - background-image: url("assets/glyph-delete-16.svg"); } - .icon.icon-dismiss { - background-image: url("assets/glyph-dismiss-16.svg"); } - .icon.icon-new-window { - background-image: url("assets/glyph-newWindow-16.svg"); } - .icon.icon-new-window-private { - background-image: url("assets/glyph-newWindow-private-16.svg"); } - html, body, #root { @@ -160,81 +134,49 @@ main { display: inline-block; margin: 0 0 18px; margin-inline-end: 32px; } - .top-sites-list .top-site-outer { - position: relative; } - .top-sites-list .top-site-outer > a { - display: block; - color: inherit; - outline: none; } - .top-sites-list .top-site-outer > a.active .tile, .top-sites-list .top-site-outer > a:focus .tile { - box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1), 0 0 0 5px rgba(0, 0, 0, 0.1); - transition: box-shadow 150ms; } - .top-sites-list .top-site-outer .context-menu-button { - cursor: pointer; - position: absolute; - top: -13.5px; - offset-inline-end: -13.5px; - width: 27px; - height: 27px; - background-color: #FFF; - background-image: url("assets/glyph-more-16.svg"); - background-position: 65%; - background-repeat: no-repeat; - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, 0.2); - border-radius: 100%; - box-shadow: 0 2px 0 rgba(0, 0, 0, 0.1); - transform: scale(0.25); - opacity: 0; - transition-property: transform, opacity; - transition-duration: 200ms; - z-index: 399; } - .top-sites-list .top-site-outer .context-menu-button:focus, .top-sites-list .top-site-outer .context-menu-button:active { - transform: scale(1); - opacity: 1; } - .top-sites-list .top-site-outer:hover .tile, .top-sites-list .top-site-outer:active .tile, .top-sites-list .top-site-outer:focus .tile, .top-sites-list .top-site-outer.active .tile { + .top-sites-list a { + display: block; + color: inherit; } + .top-sites-list .tile { + position: relative; + height: 96px; + width: 96px; + border-radius: 6px; + box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); + color: #A0A0A0; + font-weight: 200; + font-size: 32px; + text-transform: uppercase; + display: flex; + align-items: center; + justify-content: center; } + .top-sites-list .tile:hover { box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1), 0 0 0 5px rgba(0, 0, 0, 0.1); transition: box-shadow 150ms; } - .top-sites-list .top-site-outer:hover .context-menu-button, .top-sites-list .top-site-outer:active .context-menu-button, .top-sites-list .top-site-outer:focus .context-menu-button, .top-sites-list .top-site-outer.active .context-menu-button { - transform: scale(1); + .top-sites-list .screenshot { + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + background-color: #FFF; + border-radius: 6px; + box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); + background-size: 250%; + background-position: top left; + transition: opacity 1s; + opacity: 0; } + .top-sites-list .screenshot.active { opacity: 1; } - .top-sites-list .top-site-outer .tile { - position: relative; - height: 96px; - width: 96px; - border-radius: 6px; - box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); - color: #A0A0A0; - font-weight: 200; - font-size: 32px; - text-transform: uppercase; - display: flex; - align-items: center; - justify-content: center; } - .top-sites-list .top-site-outer .screenshot { - position: absolute; - top: 0; - left: 0; - height: 100%; - width: 100%; - background-color: #FFF; - border-radius: 6px; - box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); - background-size: 250%; - background-position: top left; - transition: opacity 1s; - opacity: 0; } - .top-sites-list .top-site-outer .screenshot.active { - opacity: 1; } - .top-sites-list .top-site-outer .title { - height: 30px; - line-height: 30px; - text-align: center; - white-space: nowrap; - font-size: 11px; - overflow: hidden; - text-overflow: ellipsis; - width: 96px; } + .top-sites-list .title { + height: 30px; + line-height: 30px; + text-align: center; + white-space: nowrap; + font-size: 11px; + overflow: hidden; + text-overflow: ellipsis; + width: 96px; } .search-wrapper { cursor: default; @@ -349,9 +291,11 @@ main { .search-wrapper input:focus { border-color: #0996F8; box-shadow: 0 0 0 2px #0996F8; + transition: box-shadow 150ms; z-index: 1; } .search-wrapper input:focus + button { z-index: 1; + transition: box-shadow 150ms; box-shadow: 0 0 0 2px #0996F8; background-color: #0996F8; background-image: url("assets/glyph-forward-16-white.svg"); @@ -375,52 +319,16 @@ main { border: 0; width: 36px; padding: 0; + transition: box-shadow 150ms; box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.1); background: #FFF url("assets/glyph-forward-16.svg") no-repeat center center; background-size: 16px 16px; } .search-wrapper button:hover { z-index: 1; + transition: box-shadow 150ms; box-shadow: 0 1px 0 0 rgba(0, 0, 1, 0.5); background-color: #0996F8; background-image: url("assets/glyph-forward-16-white.svg"); color: #FFF; } .search-wrapper button:dir(rtl) { transform: scaleX(-1); } - -.context-menu { - display: block; - position: absolute; - font-size: 14px; - box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(0, 0, 0, 0.2); - top: 6.75px; - offset-inline-start: 100%; - margin-inline-start: 5px; - z-index: 10000; - background: #FBFBFB; - border-radius: 5px; } - .context-menu > ul { - margin: 0; - padding: 5px 0; - list-style: none; } - .context-menu > ul > li { - margin: 0; - width: 100%; } - .context-menu > ul > li.separator { - margin: 5px 0; - border-bottom: 1px solid rgba(0, 0, 0, 0.2); } - .context-menu > ul > li > a { - outline: none; - cursor: pointer; - color: inherit; - white-space: nowrap; - padding: 3px 12px; - line-height: 16px; - display: flex; - align-items: center; } - .context-menu > ul > li > a:hover, .context-menu > ul > li > a:focus { - background: #2B99FF; - color: #FFF; } - .context-menu > ul > li > a:hover a, .context-menu > ul > li > a:focus a { - color: #383E49; } - .context-menu > ul > li > a:hover:hover, .context-menu > ul > li > a:hover:focus, .context-menu > ul > li > a:focus:hover, .context-menu > ul > li > a:focus:focus { - color: #FFF; } diff --git a/browser/extensions/activity-stream/data/content/activity-stream.html b/browser/extensions/activity-stream/data/content/activity-stream.html index 3cbbb59202c9..2cebfbea096a 100644 --- a/browser/extensions/activity-stream/data/content/activity-stream.html +++ b/browser/extensions/activity-stream/data/content/activity-stream.html @@ -2,14 +2,13 @@ - + New Tab
- diff --git a/browser/extensions/activity-stream/data/content/assets/glyph-bookmark-16.svg b/browser/extensions/activity-stream/data/content/assets/glyph-bookmark-16.svg deleted file mode 100644 index 8faf0495d65b..000000000000 --- a/browser/extensions/activity-stream/data/content/assets/glyph-bookmark-16.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/browser/extensions/activity-stream/data/content/assets/glyph-bookmark-remove-16.svg b/browser/extensions/activity-stream/data/content/assets/glyph-bookmark-remove-16.svg deleted file mode 100644 index 7dbe9a53f228..000000000000 --- a/browser/extensions/activity-stream/data/content/assets/glyph-bookmark-remove-16.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/browser/extensions/activity-stream/data/content/assets/glyph-delete-16.svg b/browser/extensions/activity-stream/data/content/assets/glyph-delete-16.svg deleted file mode 100644 index a309f9efb7cb..000000000000 --- a/browser/extensions/activity-stream/data/content/assets/glyph-delete-16.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/browser/extensions/activity-stream/data/content/assets/glyph-dismiss-16.svg b/browser/extensions/activity-stream/data/content/assets/glyph-dismiss-16.svg deleted file mode 100644 index 94495fe27ca3..000000000000 --- a/browser/extensions/activity-stream/data/content/assets/glyph-dismiss-16.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/browser/extensions/activity-stream/data/content/assets/glyph-more-16.svg b/browser/extensions/activity-stream/data/content/assets/glyph-more-16.svg deleted file mode 100644 index 9770ff831b43..000000000000 --- a/browser/extensions/activity-stream/data/content/assets/glyph-more-16.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/browser/extensions/activity-stream/data/content/assets/glyph-newWindow-16.svg b/browser/extensions/activity-stream/data/content/assets/glyph-newWindow-16.svg deleted file mode 100644 index 32a552ae5013..000000000000 --- a/browser/extensions/activity-stream/data/content/assets/glyph-newWindow-16.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/browser/extensions/activity-stream/data/content/assets/glyph-newWindow-private-16.svg b/browser/extensions/activity-stream/data/content/assets/glyph-newWindow-private-16.svg deleted file mode 100644 index 1efb98c6947a..000000000000 --- a/browser/extensions/activity-stream/data/content/assets/glyph-newWindow-private-16.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/browser/extensions/activity-stream/data/locales.json b/browser/extensions/activity-stream/data/locales.json deleted file mode 100644 index 830e1bd9fbba..000000000000 --- a/browser/extensions/activity-stream/data/locales.json +++ /dev/null @@ -1,2997 +0,0 @@ -{ - "ach": { - "newtab_page_title": "Dirica matidi manyen", - "default_label_loading": "Tye ka cano…", - "header_top_sites": "Kakube maloyo", - "header_highlights": "Wiye madito", - "type_label_visited": "Kilimo", - "type_label_bookmarked": "Kiketo alamabuk", - "type_label_synced": "Kiribo ki i nyonyo mukene", - "type_label_open": "Tye ayaba", - "type_label_topic": "Lok", - "menu_action_bookmark": "Alamabuk", - "menu_action_remove_bookmark": "Kwany alamabuk", - "menu_action_copy_address": "Lok kabedo", - "menu_action_email_link": "Kakube me email…", - "menu_action_open_new_window": "Yab i dirica manyen", - "menu_action_open_private_window": "Yab i dirica manyen me mung", - "menu_action_dismiss": "Kwer", - "menu_action_delete": "Kwany ki ii gin mukato", - "search_for_something_with": "Yeny pi {search_term} ki:", - "search_button": "Yeny", - "search_header": "Yeny me {search_engine_name}", - "search_web_placeholder": "Yeny kakube", - "search_settings": "Lok ter me yeny", - "welcome_title": "Wajoli i dirica matidi manyen", - "welcome_body": "Firefox bi tic ki kabedo man me nyuto alamabukke mamegi, coc akwana, vidio, ki potbukke ma ilimo cokcoki ma pi gi tego loyo, wek i dok ii gi ma yot.", - "welcome_label": "Tye ka kube ki wiye madito mamegi", - "time_label_less_than_minute": " ۱ دقیقه", - "time_label_minute": "{number} د", - "time_label_hour": "{number} س", - "time_label_day": "{number} ر", - "settings_pane_button_label": "صفحهٔ زبانه جدید را سفارشی کنید", - "settings_pane_header": "تنظیمات زبانه جدید", - "settings_pane_body": "انتخاب کنید که چه چیزی هنگام باز کردن زبانه جدید ببینید.", - "settings_pane_search_header": "جست‌وجو", - "settings_pane_search_body": "وب را از زبانه جدید خود جست‌وجو کنید.", - "settings_pane_topsites_header": "سایت‌های برتر", - "settings_pane_topsites_body": "به وب‌سایت‌هایی که بیشترین بازدید از آنها را داشتید دسترسی داشته باشید.", - "settings_pane_topsites_options_showmore": "نمایش دو ردیفی", - "settings_pane_highlights_header": "برجسته‌ها", - "settings_pane_highlights_body": "نگاهی دوباره به سابقه مرور اخیرتان و نشانک‌های تازه ایجاد شده.", - "settings_pane_done_button": "انجام شد", - "edit_topsites_button_text": "ويرايش", - "edit_topsites_button_label": "قسمت سایت‌های برتر را سفارشی کنید", - "edit_topsites_showmore_button": "نمایش بیشتر", - "edit_topsites_showless_button": "نمایش کمتر", - "edit_topsites_done_button": "انجام شد", - "edit_topsites_pin_button": "چسباندن این سایت", - "edit_topsites_edit_button": "ویرایش این سایت", - "edit_topsites_dismiss_button": "نادیده گرفتن این سایت" - }, - "ff": {}, - "fi": { - "newtab_page_title": "Uusi välilehti", - "default_label_loading": "Ladataan…", - "header_top_sites": "Ykkössivustot", - "header_highlights": "Nostot", - "type_label_visited": "Vierailtu", - "type_label_bookmarked": "Kirjanmerkki", - "type_label_synced": "Synkronoitu toiselta laitteelta", - "type_label_open": "Avoin", - "type_label_topic": "Aihe", - "menu_action_bookmark": "Lisää kirjanmerkki", - "menu_action_remove_bookmark": "Poista kirjanmerkki", - "menu_action_copy_address": "Kopioi osoite", - "menu_action_email_link": "Lähetä linkki…", - "menu_action_open_new_window": "Avaa uuteen ikkunaan", - "menu_action_open_private_window": "Avaa uuteen yksityiseen ikkunaan", - "menu_action_dismiss": "Hylkää", - "menu_action_delete": "Poista historiasta", - "search_for_something_with": "Hae {search_term} palvelusta:", - "search_header": "{search_engine_name}-haku", - "search_web_placeholder": "Verkkohaku", - "search_settings": "Muuta hakuasetuksia", - "welcome_title": "Tervetuloa uuteen välilehteen", - "welcome_body": "Firefox käyttää tätä tilaa näyttämään olennaisimmat kirjanmerkit, artikkelit, videot ja sivut, joita olet katsellut, jotta pääset niihin takaisin nopeasti.", - "welcome_label": "Tunnistetaan nostojasi", - "time_label_less_than_minute": "<1 min", - "time_label_minute": "{number} min", - "time_label_hour": "{number} h", - "time_label_day": "{number} pv" - }, - "fr": { - "newtab_page_title": "Nouvel onglet", - "default_label_loading": "Chargement…", - "header_top_sites": "Sites les plus visités", - "header_highlights": "Éléments-clés", - "type_label_visited": "Visité", - "type_label_bookmarked": "Ajouté aux marque-pages", - "type_label_synced": "Synchronisé depuis un autre appareil", - "type_label_open": "Ouvert", - "type_label_topic": "Thème", - "menu_action_bookmark": "Marquer cette page", - "menu_action_remove_bookmark": "Supprimer le marque-page", - "menu_action_copy_address": "Copier l’adresse", - "menu_action_email_link": "Envoyer un lien par courriel…", - "menu_action_open_new_window": "Ouvrir dans une nouvelle fenêtre", - "menu_action_open_private_window": "Ouvrir dans une nouvelle fenêtre privée", - "menu_action_dismiss": "Retirer", - "menu_action_delete": "Supprimer de l’historique", - "search_for_something_with": "Recherche pour {search_term} avec :", - "search_button": "Rechercher", - "search_header": "Recherche {search_engine_name}", - "search_web_placeholder": "Rechercher sur le Web", - "search_settings": "Paramètres de recherche", - "welcome_title": "Bienvenue sur la page Nouvel onglet", - "welcome_body": "Firefox utilisera cet espace pour afficher des éléments pertinents, comme des marque-pages, des articles, des vidéos, et des pages que vous avez visitées, afin que vous les retrouviez facilement.", - "welcome_label": "Identification des éléments-clés", - "time_label_less_than_minute": "<1 min", - "time_label_minute": "{number} min", - "time_label_hour": "{number} h", - "time_label_day": "{number} j", - "settings_pane_button_label": "Personnaliser la page Nouvel onglet", - "settings_pane_header": "Préférences Nouvel onglet", - "settings_pane_body": "Choisissez ce qui s’affiche à l’ouverture d’un nouvel onglet.", - "settings_pane_search_header": "Recherche", - "settings_pane_search_body": "Effectuez une recherche sur le Web à partir du nouvel onglet.", - "settings_pane_topsites_header": "Sites les plus visités", - "settings_pane_topsites_body": "Accédez aux sites que vous consultez le plus.", - "settings_pane_topsites_options_showmore": "Afficher deux lignes", - "settings_pane_highlights_header": "Éléments-clés", - "settings_pane_highlights_body": "Revenir sur votre historique de navigation récent et sur vos marque-pages nouvellement créés.", - "settings_pane_done_button": "Terminé", - "edit_topsites_button_text": "Modifier", - "edit_topsites_button_label": "Personnaliser la section Sites les plus visités", - "edit_topsites_showmore_button": "Afficher plus", - "edit_topsites_showless_button": "Afficher moins", - "edit_topsites_done_button": "Terminé", - "edit_topsites_pin_button": "Épingler ce site", - "edit_topsites_edit_button": "Modifier ce site", - "edit_topsites_dismiss_button": "Retirer ce site" - }, - "fy-NL": { - "newtab_page_title": "Nij ljepblêd", - "default_label_loading": "Lade…", - "header_top_sites": "Topwebsites", - "header_highlights": "Hichtepunten", - "type_label_visited": "Besocht", - "type_label_bookmarked": "Blêdwizer makke", - "type_label_synced": "Syngronisearre fan oar apparaat ôf", - "type_label_open": "Iepene", - "type_label_topic": "Underwerp", - "menu_action_bookmark": "Blêdwizer", - "menu_action_remove_bookmark": "Blêdwizer fuortsmite", - "menu_action_copy_address": "Adres kopiearje", - "menu_action_email_link": "Keppeling e-maile…", - "menu_action_open_new_window": "Iepenje yn in nij finster", - "menu_action_open_private_window": "Iepenje yn in nij priveefinster", - "menu_action_dismiss": "Fuortsmite", - "menu_action_delete": "Fuortsmite út skiednis", - "search_for_something_with": "Sykje nei {search_term} mei:", - "search_button": "Sykje", - "search_header": "{search_engine_name} trochsykje", - "search_web_placeholder": "Sykje op it web", - "search_settings": "Sykynstellingen wizigje", - "welcome_title": "Wolkom by it nije ljepblêd", - "welcome_body": "Firefox brûkt dizze romte om jo meast relevante blêdwizers, artikelen, fideo’s en siden dy't jo koartlyn besocht hawwe wer te jaan, sadat jo dizze ienfâldichwei weromfine kinne.", - "welcome_label": "Jo hichtepunten oantsjutte", - "time_label_less_than_minute": "< 1 m", - "time_label_minute": "{number} m", - "time_label_hour": "{number} o", - "time_label_day": "{number} d", - "settings_pane_button_label": "Jo side foar nije ljepblêden oanpasse", - "settings_pane_header": "Nij ljepblêdfoarkarren", - "settings_pane_body": "Kies wat jo sjogge as jo in nij ljepblêd iepenje.", - "settings_pane_search_header": "Sykje", - "settings_pane_search_body": "Sykje op it web fan jo nije ljepblêd út.", - "settings_pane_topsites_header": "Topwebsites", - "settings_pane_topsites_body": "Benaderje de websites dy't jo it meast besykje.", - "settings_pane_topsites_options_showmore": "Twa rigen toane", - "settings_pane_highlights_header": "Hichtepunten", - "settings_pane_highlights_body": "Sjoch werom nei jo resinte sneupskiednis en nij makke blêdwizers.", - "settings_pane_done_button": "Dien", - "edit_topsites_button_text": "Bewurkje", - "edit_topsites_button_label": "Jo seksje Topwebsites oanpasse", - "edit_topsites_showmore_button": "Mear toane", - "edit_topsites_showless_button": "Minder toane", - "edit_topsites_done_button": "Dien", - "edit_topsites_pin_button": "Dizze side fêstsette", - "edit_topsites_edit_button": "Dizze side bewurkje", - "edit_topsites_dismiss_button": "Dizze side fuortsmite" - }, - "ga-IE": { - "newtab_page_title": "Cluaisín Nua", - "default_label_loading": "Á Luchtú…", - "header_top_sites": "Barrshuímh", - "header_highlights": "Buaicphointí", - "type_label_visited": "Feicthe", - "type_label_bookmarked": "Leabharmharcáilte", - "type_label_synced": "Sioncronaithe ó ghléas eile", - "type_label_open": "Oscailte", - "type_label_topic": "Ábhar", - "menu_action_bookmark": "Cruthaigh leabharmharc", - "menu_action_remove_bookmark": "Scrios Leabharmharc", - "menu_action_copy_address": "Cóipeáil an Seoladh", - "menu_action_email_link": "Seol an Nasc trí Ríomhphost…", - "menu_action_open_new_window": "Oscail i bhFuinneog Nua", - "menu_action_open_private_window": "Oscail i bhFuinneog Nua Phríobháideach", - "menu_action_dismiss": "Ruaig", - "menu_action_delete": "Scrios ón Stair", - "search_for_something_with": "Déan cuardach ar {search_term} le:", - "search_header": "Cuardach {search_engine_name}", - "search_web_placeholder": "Cuardaigh an Gréasán", - "search_settings": "Socruithe Cuardaigh", - "welcome_title": "Fáilte go dtí cluaisín nua", - "welcome_body": "Úsáidfidh Firefox an spás seo chun na leabharmharcanna, ailt, físeáin, agus leathanaigh is tábhachtaí a thaispeáint duit, ionas go mbeidh tú in ann filleadh orthu gan stró.", - "welcome_label": "Buaicphointí á lorg", - "time_label_less_than_minute": "< 1 n", - "time_label_minute": "{number}n", - "time_label_hour": "{number}u", - "time_label_day": "{number}l" - }, - "gd": { - "newtab_page_title": "Taba ùr", - "default_label_loading": "’Ga luchdadh…", - "header_top_sites": "Brod nan làrach", - "header_highlights": "Highlights", - "type_label_visited": "Na thadhail thu air", - "type_label_bookmarked": "’Nan comharran-lìn", - "type_label_synced": "Sioncronaichte o uidheam eile", - "type_label_open": "Fosgailte", - "type_label_topic": "Cuspair", - "menu_action_bookmark": "Comharra-lìn", - "menu_action_remove_bookmark": "Thoir an comharra-lìn air falbh", - "menu_action_copy_address": "Dèan lethbhreac dhen t-seòladh", - "menu_action_email_link": "Cuir an ceangal air a’ phost-d…", - "menu_action_open_new_window": "Fosgail ann an uinneag ùr", - "menu_action_open_private_window": "Fosgail ann an uinneag phrìobhaideach ùr", - "menu_action_dismiss": "Leig seachad", - "menu_action_delete": "Sguab às an eachdraidh", - "search_for_something_with": "Lorg {search_term} le:", - "search_header": "Lorg le {search_engine_name}", - "search_web_placeholder": "Lorg air an lìon", - "search_settings": "Atharraich roghainnean an luirg", - "welcome_title": "Fàilte gun taba ùr", - "welcome_body": "Seallaidh Firefox na comharran-lìn, artaigealan, videothan is duilleagan as iomchaidhe dhut, an fheadhainn air an do thadhail thu o chionn goirid, ach an ruig thu iad gu luath.", - "welcome_label": "Ag aithneachadh nan highlights agad", - "time_label_less_than_minute": "<1m", - "time_label_minute": "{number}m", - "time_label_hour": "{number}u", - "time_label_day": "{number}l" - }, - "gl": {}, - "gn": {}, - "gu-IN": { - "newtab_page_title": "નવું ટૅબ", - "default_label_loading": "લોડ કરી રહ્યું છે...", - "header_top_sites": "ટોપ સાઇટ્સ", - "header_highlights": "હાઇલાઇટ્સ", - "type_label_visited": "જોવામા આવેલ:", - "type_label_bookmarked": "બુકમાર્ક્સ", - "type_label_synced": "બીજા ઉપકરણ થી સમન્વયિત કરેલ છે", - "type_label_open": "ખોલો", - "type_label_topic": "વિષય", - "menu_action_bookmark": "બુકમાર્ક", - "menu_action_remove_bookmark": "બુકમાર્ક કાઢો", - "menu_action_copy_address": "સરનામું કૉપિ કરો", - "menu_action_email_link": "ઇમેલ કડી…", - "menu_action_open_new_window": "નવી વિન્ડોમાં ખોલો", - "menu_action_open_private_window": "ખાનગી વિન્ડોમાં ખોલો", - "menu_action_dismiss": "રદ કરો", - "menu_action_delete": "ઇતિહાસમાંથી દૂર કરો", - "search_for_something_with": "શોધ કરો {search_term} ની સાથે:", - "search_header": "{search_engine_name} શોધ કરો", - "search_web_placeholder": "વેબ પર શોધો", - "search_settings": "શોધ ના સેટિંગ્સ બદલો", - "welcome_title": "નવી વિન્ડોમાં આપનું સ્વાગત છે", - "welcome_body": "ફાયરફોક્સ, તમારા સૌથી સંબંધિત બુકમાર્ક્સ, લેખો, વિડિઓઝ, અને પૃષ્ઠો જે તમે તાજેતરમાં મુલાકાત લીધી એ બતાવવા માટે આ જગ્યાનો ઉપયોગ કરશે જેથી તમે પાછા તેમને સરળતાથી મેળવી શકો છો.", - "welcome_label": "તમારા હાઇલાઇટ્સ ઓળખવા", - "time_label_less_than_minute": "<1મિનિટ", - "time_label_minute": "{number}મિનિટ", - "time_label_hour": "{number}કલાક", - "time_label_day": "{number}દિવસ" - }, - "he": { - "newtab_page_title": "לשונית חדשה", - "default_label_loading": "בטעינה…", - "header_top_sites": "אתרים מובילים", - "header_highlights": "המלצות", - "type_label_visited": "ביקורים קודמים", - "type_label_bookmarked": "נוצרה סימניה", - "type_label_synced": "סונכרן מהתקן אחר", - "type_label_open": "פתיחה", - "type_label_topic": "נושא", - "menu_action_bookmark": "סימניה", - "menu_action_remove_bookmark": "הסרת סימניה", - "menu_action_copy_address": "העתקת כתובת", - "menu_action_email_link": "שליחת קישור בדוא״ל…", - "menu_action_open_new_window": "פתיחה בחלון חדש", - "menu_action_open_private_window": "פתיחה בלשונית פרטית חדשה", - "menu_action_dismiss": "ביטול", - "menu_action_delete": "מחיקה מההיסטוריה", - "search_for_something_with": "חיפוש אחר {search_term} עם:", - "search_header": "חיפוש ב־{search_engine_name}", - "search_web_placeholder": "חיפוש ברשת", - "search_settings": "שינוי הגדרות חיפוש", - "welcome_title": "ברוכים הבאים לדף הלשונית החדשה", - "welcome_body": "Firefox ישתמש באזור זה כדי להציג את הסימניות הרלוונטיות ביותר, מאמרים, סרטוני וידאו ודפים שביקרת בהם לאחרונה, כך שניתן יהיה לגשת אליהם שוב בקלות.", - "time_label_less_than_minute": "פחות מדקה", - "time_label_minute": "{number} דקות", - "time_label_hour": "{number} שעות", - "time_label_day": "{number} ימים" - }, - "hi-IN": {}, - "hr": { - "newtab_page_title": "Nova kartica", - "default_label_loading": "Učitavanje…", - "header_top_sites": "Najbolje stranice", - "header_highlights": "Istaknuto", - "type_label_visited": "Posjećeno", - "type_label_bookmarked": "Zabilježeno", - "type_label_synced": "Sinkronizirano s drugog uređaja", - "type_label_open": "Otvori", - "type_label_topic": "Tema", - "menu_action_bookmark": "Zabilježi stranicu", - "menu_action_remove_bookmark": "Ukloni zabilješku", - "menu_action_copy_address": "Kopiraj adresu", - "menu_action_email_link": "Pošalji poveznicu e-poštom…", - "menu_action_open_new_window": "Otvori u novom prozoru", - "menu_action_open_private_window": "Otvori u novom privatnom prozoru", - "menu_action_dismiss": "Odbaci", - "menu_action_delete": "Obriši iz povijesti", - "search_for_something_with": "Traži {search_term} s:", - "search_header": "{search_engine_name} pretraživanje", - "search_web_placeholder": "Pretraži web", - "search_settings": "Promijeni postavke pretraživanja", - "welcome_title": "Dobro došli u novu karticu", - "welcome_body": "Firefox će koristiti ovaj prostor kako bi vam pokazao najbitnije zabilješke, članke, video uratke i stranice koje ste nedavno posjetili, tako da se možete lako vratiti na njih.", - "welcome_label": "Identificiranje istaknutog", - "time_label_less_than_minute": "<1m", - "time_label_minute": "{number}m", - "time_label_hour": "{number}h", - "time_label_day": "{number}d" - }, - "hsb": { - "newtab_page_title": "Nowy rajtark", - "default_label_loading": "Začituje so…", - "header_top_sites": "Najhusćišo wopytane sydła", - "header_highlights": "Wjerški", - "type_label_visited": "Wopytany", - "type_label_bookmarked": "Jako zapołožka składowany", - "type_label_synced": "Z druheho grata synchronizowany", - "type_label_open": "Wočinjeny", - "type_label_topic": "Tema", - "menu_action_bookmark": "Zapołožki składować", - "menu_action_remove_bookmark": "Zapołožku wotstronić", - "menu_action_copy_address": "Adresu kopěrować", - "menu_action_email_link": "Wotkaz e-mejlować…", - "menu_action_open_new_window": "W nowym woknje wočinić", - "menu_action_open_private_window": "W nowym priwatnym woknje wočinić", - "menu_action_dismiss": "Zaćisnyć", - "menu_action_delete": "Z historije zhašeć", - "search_for_something_with": "Za {search_term} pytać z:", - "search_button": "Pytać", - "search_header": "Z {search_engine_name} pytać", - "search_web_placeholder": "Web přepytać", - "search_settings": "Pytanske nastajenja změnić", - "welcome_title": "Witajće k nowemu rajtarkej", - "welcome_body": "Firefox budźe tutón rum wužiwać, zo by waše najwažniše zapołožki, nastawki, wideja a runje wopytane strony pokazał, zo byšće móhł so lochko k nim wróćić.", - "welcome_label": "Wuběranje wašich najwažnišich stronow", - "time_label_less_than_minute": "< 1 min", - "time_label_minute": "{number} m", - "time_label_hour": "{number} h", - "time_label_day": "", - "settings_pane_button_label": "Stronu wašeho noweho rajtarka přiměrić", - "settings_pane_header": "Nastajenja noweho rajtarka", - "settings_pane_body": "Wubjerće, štož chceće widźeć, hdyž nowy rajtark wočinjeće.", - "settings_pane_search_header": "Pytać", - "settings_pane_search_body": "Přepytajće web ze swojeho noweho rajtarka.", - "settings_pane_topsites_header": "Najhusćišo wopytane sydła", - "settings_pane_topsites_body": "Wočińće websydła, kotrež sće najhusćišo wopytał.", - "settings_pane_topsites_options_showmore": "Dwě lince pokazać", - "settings_pane_highlights_header": "Wjerški", - "settings_pane_highlights_body": "Wobhladajće sej najnowšu přehladowansku historiju a nowo wutworjene zapołožki.", - "settings_pane_done_button": "Hotowo", - "edit_topsites_button_text": "Wobdźěłać", - "edit_topsites_button_label": "Přiměrće wotrězk swojich najhusćišo wopytanych sydłow", - "edit_topsites_showmore_button": "Wjace pokazać", - "edit_topsites_showless_button": "Mjenje pokazać", - "edit_topsites_done_button": "Hotowo", - "edit_topsites_pin_button": "Tute sydło připjeć", - "edit_topsites_edit_button": "Tute sydło wobdźěłać", - "edit_topsites_dismiss_button": "Sydło zaćisnyć" - }, - "hu": { - "newtab_page_title": "Új lap", - "default_label_loading": "Betöltés…", - "header_top_sites": "Népszerű oldalak", - "header_highlights": "Kiemelések", - "type_label_visited": "Látogatott", - "type_label_bookmarked": "Könyvjelzőzött", - "type_label_synced": "Másik eszközről szinkronizálva", - "type_label_open": "Megnyitás", - "type_label_topic": "Téma", - "menu_action_bookmark": "Könyvjelzőzés", - "menu_action_remove_bookmark": "Könyvjelző eltávolítása", - "menu_action_copy_address": "Cím másolása", - "menu_action_email_link": "Hivatkozás küldése e-mailben…", - "menu_action_open_new_window": "Megnyitás új ablakban", - "menu_action_open_private_window": "Megnyitás új privát ablakban", - "menu_action_dismiss": "Elutasítás", - "menu_action_delete": "Törlés az előzményekből", - "search_for_something_with": "„{search_term}” keresése ezzel:", - "search_button": "Keresés", - "search_header": "{search_engine_name} keresés", - "search_web_placeholder": "Keresés a weben", - "search_settings": "Keresési beállítások módosítása", - "welcome_title": "Üdvözöljük az új lapon", - "welcome_body": "A Firefox ezt a területet a leginkább releváns könyvjelzők, cikkek, videók és nemrég látogatott oldalak megjelenítésére fogja használni, így könnyedén visszatalálhat hozzájuk.", - "welcome_label": "A kiemeléseinek azonosítása", - "time_label_less_than_minute": "<1 p", - "time_label_minute": "{number} p", - "time_label_hour": "{number} ó", - "time_label_day": "{number} n", - "settings_pane_button_label": "Az Új lap oldal személyre szabása", - "settings_pane_header": "Új lap beállításai", - "settings_pane_body": "Válassza ki, hogy mit lát, amikor megnyit egy új lapot.", - "settings_pane_search_header": "Keresés", - "settings_pane_search_body": "Keresés a weben az új lapon.", - "settings_pane_topsites_header": "Népszerű oldalak", - "settings_pane_topsites_body": "A leggyakrabban látogatott webhelyek elérése.", - "settings_pane_topsites_options_showmore": "Két sor megjelenítése", - "settings_pane_highlights_header": "Kiemelések", - "settings_pane_highlights_body": "A böngészési előzmények, és az újonnan létrehozott könyvjelzők visszanézése.", - "settings_pane_done_button": "Kész", - "edit_topsites_button_text": "Szerkesztés", - "edit_topsites_button_label": "A Népszerű oldalak rész testreszabása", - "edit_topsites_showmore_button": "Több megjelenítése", - "edit_topsites_showless_button": "Kevesebb megjelenítése", - "edit_topsites_done_button": "Kész", - "edit_topsites_pin_button": "Webhely rögzítése", - "edit_topsites_edit_button": "Webhely szerkesztése", - "edit_topsites_dismiss_button": "Webhely eltávolítása" - }, - "hy-AM": { - "newtab_page_title": "Նոր ներդիր", - "default_label_loading": "Բեռնվում է...", - "header_top_sites": "Լավագույն կայքեր", - "header_highlights": "Գունանշում", - "type_label_visited": "Այցելած", - "type_label_bookmarked": "Էջանշված", - "type_label_synced": "Համաժամեցված այլ սարքից", - "type_label_open": "Բացել", - "type_label_topic": "Թեմա", - "menu_action_bookmark": "Էջանիշ", - "menu_action_remove_bookmark": "Հեռացնել էջանիշը", - "menu_action_copy_address": "Պատճենել հասցեն", - "menu_action_email_link": "Ուղարկել հղումը...", - "menu_action_open_new_window": "Բացել Նոր Պատուհանով", - "menu_action_open_private_window": "Բացել Նոր Գաղտնի դիտարկմամբ", - "menu_action_dismiss": "Բաց թողնել", - "menu_action_delete": "Ջնջել Պատմությունից", - "search_for_something_with": "Որոնել {search_term}-ը հետևյալով՝", - "search_header": "{search_engine_name}-ի որոնում", - "search_web_placeholder": "Որոնել առցանց", - "search_settings": "Փոխել որոնման կարգավորումները", - "welcome_title": "Բարի գալուստ նոր ներդիր", - "welcome_body": "Firefox-ը կօգտագործի այս բացատը՝ ցուցադրելու ձեզ համար առավել կարևոր էջանիշերը, հոդվածները և ձեր այցելած վերջին էջերը, որպեսզի հեշտությամբ վերադառնաք դրանց:", - "welcome_label": "Նույնացնում է ձեր գունանշումը", - "time_label_less_than_minute": "<1 ր", - "time_label_minute": "{number} ր", - "time_label_hour": "{number} ժ", - "time_label_day": "{number} օր" - }, - "id": { - "newtab_page_title": "Tab Baru", - "default_label_loading": "Memuat…", - "header_top_sites": "Situs Teratas", - "header_highlights": "Sorotan", - "type_label_visited": "Dikunjungi", - "type_label_bookmarked": "Dimarkahi", - "type_label_synced": "Disinkronkan dari perangkat lain", - "type_label_open": "Buka", - "type_label_topic": "Topik", - "menu_action_bookmark": "Markah", - "menu_action_remove_bookmark": "Hapus Markah", - "menu_action_copy_address": "Salin Alamat", - "menu_action_email_link": "Emailkan Tautan…", - "menu_action_open_new_window": "Buka di Jendela Baru", - "menu_action_open_private_window": "Buka di Jendela Penjelajahan Pribadi Baru", - "menu_action_dismiss": "Tutup", - "menu_action_delete": "Hapus dari Riwayat", - "search_for_something_with": "Cari {search_term} lewat:", - "search_button": "Cari", - "search_header": "Pencarian {search_engine_name}", - "search_web_placeholder": "Cari di Web", - "search_settings": "Ubah Pengaturan Pencarian", - "welcome_title": "Selamat datang di tab baru", - "welcome_body": "Firefox akan menggunakan ruang ini untuk menampilkan markah, artikel, video, dan laman yang baru-baru ini dikunjungi, yang paling relevan agar Anda bisa kembali mengunjunginya dengan mudah.", - "welcome_label": "Mengidentifikasi Sorotan Anda", - "time_label_less_than_minute": "<1 mnt", - "time_label_minute": "{number} mnt", - "time_label_hour": "{number} jam", - "time_label_day": "{number} hr", - "settings_pane_button_label": "Ubahsuai laman Tab Baru Anda", - "settings_pane_header": "Preferensi Tab Baru", - "settings_pane_body": "Pilih apa yang Anda lihat ketika Anda membuka tab baru.", - "settings_pane_search_header": "Pencarian", - "settings_pane_search_body": "Cari Web dari tab baru Anda.", - "settings_pane_topsites_header": "Situs Teratas", - "settings_pane_topsites_body": "Mengakses situs web yang paling sering Anda kunjungi.", - "settings_pane_topsites_options_showmore": "Tampilkan dua baris", - "settings_pane_highlights_header": "Sorotan", - "settings_pane_highlights_body": "Melihat kembali pada riwayat peramban terbaru dan markah yang baru dibuat.", - "settings_pane_done_button": "Selesai", - "edit_topsites_button_text": "Sunting", - "edit_topsites_button_label": "Ubahsuai bagian Situs Teratas Anda", - "edit_topsites_showmore_button": "Tampilkan lainnya", - "edit_topsites_showless_button": "Tampilkan lebih sedikit", - "edit_topsites_done_button": "Selesai", - "edit_topsites_pin_button": "Sematkan situs ini", - "edit_topsites_edit_button": "Sunting situs ini", - "edit_topsites_dismiss_button": "Abaikan situs ini" - }, - "is": {}, - "it": { - "newtab_page_title": "Nuova scheda", - "default_label_loading": "Caricamento…", - "header_top_sites": "Siti principali", - "header_highlights": "In evidenza", - "type_label_visited": "Visitato", - "type_label_bookmarked": "Nei segnalibri", - "type_label_synced": "Sincronizzato da un altro dispositivo", - "type_label_open": "Apri", - "type_label_topic": "Argomento", - "menu_action_bookmark": "Aggiungi ai segnalibri", - "menu_action_remove_bookmark": "Elimina segnalibro", - "menu_action_copy_address": "Copia indirizzo", - "menu_action_email_link": "Invia link per email…", - "menu_action_open_new_window": "Apri in una nuova finestra", - "menu_action_open_private_window": "Apri in una nuova finestra anonima", - "menu_action_dismiss": "Rimuovi", - "menu_action_delete": "Elimina dalla cronologia", - "search_for_something_with": "Cerca {search_term} con:", - "search_button": "Cerca", - "search_header": "Ricerca {search_engine_name}", - "search_web_placeholder": "Cerca sul Web", - "search_settings": "Cambia impostazioni di ricerca", - "welcome_title": "Benvenuto nella nuova scheda", - "welcome_body": "Firefox utilizzerà questo spazio per visualizzare gli elementi più significativi, come segnalibri, articoli, video e pagine visitate di recente, in modo che siano sempre facili da raggiungere.", - "welcome_label": "Identificazione elementi in evidenza…", - "time_label_less_than_minute": "<1m", - "time_label_minute": "{number}m", - "time_label_hour": "{number}h", - "time_label_day": "{number}g", - "settings_pane_button_label": "Personalizza la pagina Nuova scheda", - "settings_pane_header": "Preferenze Nuova scheda", - "settings_pane_body": "Scegli quali elementi visualizzare all’apertura di una nuova scheda.", - "settings_pane_search_header": "Ricerca", - "settings_pane_search_body": "Avvia ricerche in una nuova scheda.", - "settings_pane_topsites_header": "Siti principali", - "settings_pane_topsites_body": "Accedi ai siti che visiti più spesso.", - "settings_pane_topsites_options_showmore": "Visualizza due righe", - "settings_pane_highlights_header": "In evidenza", - "settings_pane_highlights_body": "Visualizza gli elementi più recenti nella cronologia e gli ultimi segnalibri memorizzati.", - "settings_pane_done_button": "Fatto", - "edit_topsites_button_text": "Modifica", - "edit_topsites_button_label": "Personalizza la sezione Siti principali", - "edit_topsites_showmore_button": "Visualizza altri", - "edit_topsites_showless_button": "Nascondi altri", - "edit_topsites_done_button": "Fatto", - "edit_topsites_pin_button": "Aggiungi sito alla bacheca", - "edit_topsites_edit_button": "Modifica questo sito", - "edit_topsites_dismiss_button": "Ignora questo sito" - }, - "ja": { - "newtab_page_title": "新しいタブ", - "default_label_loading": "読み込み中...", - "header_top_sites": "トップサイト", - "header_highlights": "ハイライト", - "type_label_visited": "訪問済み", - "type_label_bookmarked": "ブックマーク", - "type_label_synced": "他の端末から同期", - "type_label_open": "開く", - "type_label_topic": "トピック", - "menu_action_bookmark": "ブックマーク", - "menu_action_remove_bookmark": "ブックマークを削除", - "menu_action_copy_address": "URL をコピー", - "menu_action_email_link": "URL をメールで送信...", - "menu_action_open_new_window": "新しいウィンドウで開く", - "menu_action_open_private_window": "新しいプライベートウィンドウで開く", - "menu_action_dismiss": "閉じる", - "menu_action_delete": "履歴から削除", - "search_for_something_with": "{search_term} を検索:", - "search_button": "検索", - "search_header": "{search_engine_name} 検索", - "search_web_placeholder": "ウェブを検索", - "search_settings": "検索設定を変更", - "welcome_title": "新しいタブへようこそ", - "welcome_body": "Firefox はこのスペースを使って、関連性の高いブックマーク、記事、動画、最近訪れたページを表示し、それらのコンテンツへ簡単に戻れるようにします。", - "welcome_label": "あなたのハイライトを確認しています", - "time_label_less_than_minute": "1 分以内", - "time_label_minute": "{number} 分", - "time_label_hour": "{number} 時間", - "time_label_day": "{number} 日", - "settings_pane_button_label": "新しいタブページをカスタマイズ", - "settings_pane_header": "新しいタブの設定", - "settings_pane_body": "新しいタブを開いたときに表示する内容を選択します。", - "settings_pane_search_header": "検索", - "settings_pane_search_body": "新しいタブからウェブを検索します。", - "settings_pane_topsites_header": "トップサイト", - "settings_pane_topsites_body": "よく訪れるサイトへアクセス。", - "settings_pane_topsites_options_showmore": "2 行で表示", - "settings_pane_highlights_header": "ハイライト", - "settings_pane_highlights_body": "最近の閲覧履歴と新たに作成されたブックマークを振り返りましょう。", - "settings_pane_done_button": "完了", - "edit_topsites_button_text": "編集", - "edit_topsites_button_label": "トップサイトの項目をカスタマイズ", - "edit_topsites_showmore_button": "もっと見る", - "edit_topsites_showless_button": "折りたたむ", - "edit_topsites_done_button": "完了", - "edit_topsites_pin_button": "このサイトをピン留め", - "edit_topsites_edit_button": "このサイトを編集", - "edit_topsites_dismiss_button": "このサイトを削除" - }, - "ka": {}, - "kab": { - "newtab_page_title": "Iccer amaynut", - "default_label_loading": "Asali…", - "header_top_sites": "Ismal ifazen", - "header_highlights": "Iferdisen tisura", - "type_label_visited": "Yettwarza", - "type_label_bookmarked": "Yettwacreḍ", - "type_label_synced": "Yemtawi seg ibenk-nniḍen", - "type_label_open": "Yeldi", - "type_label_topic": "Asentel", - "menu_action_bookmark": "Creḍ asebter-agi", - "menu_action_remove_bookmark": "Kkes tacreṭ-agi", - "menu_action_copy_address": "Nγel tansa", - "menu_action_email_link": "Azen aseγwen s yimayl…", - "menu_action_open_new_window": "Ldei deg usfaylu amaynut", - "menu_action_open_private_window": "Ldi deg usfaylu uslig amaynut", - "menu_action_dismiss": "Kkes", - "menu_action_delete": "Kkes seg umazray", - "search_for_something_with": "Nadi γef {search_term} s:", - "search_button": "Nadi", - "search_header": "Anadi {search_engine_name}", - "search_web_placeholder": "Nadi di Web", - "search_settings": "Snifel iγewwaṛen n unadi", - "welcome_title": "Ansuf ar yiccer amaynut", - "welcome_body": "Firefox ad iseqdec tallunt akken ad d-yesken akk ticraḍ n isebtar iwulmen, imagraden, tividyutin, akked isebtar aniɣer terziḍ melmi kan, ihi tzemreḍ ad d-uɣaleḍ ɣer-sen s wudem fessusen.", - "welcome_label": "Asulu n iferdisen tisura", - "time_label_less_than_minute": "<1 n tesdat", - "time_label_minute": "{number} n tesdatin", - "time_label_hour": "{number} n isragen", - "time_label_day": "{number}n wussan", - "settings_pane_button_label": "Sagen asebter n yiccer-ik amaynut", - "settings_pane_header": "Ismenyifen n yiccer amaynut", - "settings_pane_body": "Fren ayen ara twaliḍ ticki teldiḍ iccer imaynut.", - "settings_pane_search_header": "Nadi", - "settings_pane_search_body": "Nadi di Web seg iccer-ik amaynut.", - "settings_pane_topsites_header": "Ismal ifazen", - "settings_pane_topsites_body": "Kcem ar yesmal web i trezzuḍ s waṭas.", - "settings_pane_topsites_options_showmore": "Sken sin izirigen", - "settings_pane_highlights_header": "Asebrureq", - "settings_pane_highlights_body": "Wali ar deffir ar umazray n tunigin yezrin akked tecraḍ i terniḍ melmi kan.", - "settings_pane_done_button": "Immed", - "edit_topsites_button_text": "Ẓreg", - "edit_topsites_button_label": "Sagen tigezmi n ismal ifazen", - "edit_topsites_showmore_button": "Sken ugar", - "edit_topsites_showless_button": "Sken qel", - "edit_topsites_done_button": "Immed", - "edit_topsites_pin_button": "Ṭṭef asmel-agi", - "edit_topsites_edit_button": "Ẓreg asmel-agi", - "edit_topsites_dismiss_button": "Anef i usmel-agi" - }, - "kk": { - "newtab_page_title": "Жаңа бет", - "default_label_loading": "Жүктелуде…", - "header_top_sites": "Топ сайттар", - "header_highlights": "Бастысы", - "type_label_visited": "Қаралған", - "type_label_bookmarked": "Бетбелгілерде", - "type_label_synced": "Басқа құрылғыдан синхрондалған", - "type_label_open": "Ашу", - "type_label_topic": "Тақырып", - "menu_action_bookmark": "Бетбелгілерге қосу", - "menu_action_remove_bookmark": "Бетбелгіні өшіру", - "menu_action_copy_address": "Адресін көшіріп алу", - "menu_action_email_link": "Сілтемені эл. поштамен жіберу…", - "menu_action_open_new_window": "Жаңа терезеде ашу", - "menu_action_open_private_window": "Жаңа жекелік терезесінде ашу", - "menu_action_dismiss": "Тайдыру", - "menu_action_delete": "Тарихтан өшіру", - "search_for_something_with": "{search_term} ұғымын көмегімен іздеу:", - "search_button": "Іздеу", - "search_header": "{search_engine_name} іздеуі", - "search_web_placeholder": "Интернетте іздеу", - "search_settings": "Іздеу баптауларын өзгерту", - "welcome_title": "Жаңа бетке қош келдіңіз", - "welcome_body": "Firefox бұл орында ең маңызды бетбелгілер, мақалалар, видеолар және жуырда қаралған беттерді көрсетеді, оның көмегімен сіз оларға оңай түрде орала аласыз.", - "welcome_label": "Ең басты нәрселерді анықтау", - "time_label_less_than_minute": "<1 минут", - "time_label_minute": "{number} минут", - "time_label_hour": "{number} сағат", - "time_label_day": "{number} күн", - "settings_pane_button_label": "Жаңа бетті баптаңыз", - "settings_pane_header": "Жаңа бет баптаулары", - "settings_pane_body": "Жаңа бетті ашқан кезде нені көретініңізді таңдаңыз.", - "settings_pane_search_header": "Іздеу", - "settings_pane_search_body": "Жаңа беттен интернеттен іздеңіз.", - "settings_pane_topsites_header": "Топ сайттар", - "settings_pane_topsites_body": "Көбірек қаралатын сайттарға қатынау.", - "settings_pane_topsites_options_showmore": "Екі жолды көрсету", - "settings_pane_highlights_header": "Бастысы", - "settings_pane_highlights_body": "Жуырдағы шолу тарихы мен жаңа жасалған бетбелгілерге қарау.", - "settings_pane_done_button": "Дайын", - "edit_topsites_button_text": "Түзету", - "edit_topsites_button_label": "Топ сайттар санатын баптау", - "edit_topsites_showmore_button": "Көбірек көрсету", - "edit_topsites_showless_button": "Азырақ көрсету", - "edit_topsites_done_button": "Дайын", - "edit_topsites_pin_button": "Бұл сайтты жапсыру", - "edit_topsites_edit_button": "Бұл сайтты түзету", - "edit_topsites_dismiss_button": "Бұл сайтты тайдыру" - }, - "km": { - "newtab_page_title": "ផ្ទាំង​ថ្មី", - "default_label_loading": "កំពុង​ផ្ទុក...", - "header_top_sites": "វិបសាយ​លើ​គេ", - "header_highlights": "ការ​រំលេច", - "type_label_visited": "បាន​ចូល​មើល", - "type_label_bookmarked": "បាន​ចំណាំ", - "type_label_synced": "បាន​ធ្វើ​សមកាលកម្ម​ពី​ឧបករណ៍​ផ្សេង​ទៀត", - "type_label_open": "បើក", - "type_label_topic": "ប្រធានបទ", - "menu_action_bookmark": "ចំណាំ", - "menu_action_remove_bookmark": "លុប​ចំណាំ​ចេញ", - "menu_action_copy_address": "ចម្លង​អាសយដ្ឋាន", - "menu_action_email_link": "តំណ​អ៊ីមែល...", - "menu_action_open_new_window": "បើក​នៅ​ក្នុង​បង្អួច​ថ្មី", - "menu_action_open_private_window": "បើក​នៅ​ក្នុង​បង្អួច​ឯកជន​ថ្មី", - "menu_action_dismiss": "បោះបង់ចោល", - "menu_action_delete": "លុប​ពី​ប្រវត្តិ", - "search_for_something_with": "ស្វែងរក {search_term} ជាមួយ៖", - "search_header": "{search_engine_name} ស្វែងរក", - "search_web_placeholder": "ស្វែងរក​បណ្ដាញ", - "search_settings": "ផ្លាស់ប្ដូរ​ការ​កំណត់​ស្វែងរក", - "welcome_title": "ស្វាគមន៍​មក​កាន់​ផ្ទាំង​ថ្មី", - "welcome_body": "Firefox នឹង​ប្រើប្រាស់​កន្លែង​ទំនេរ​នេះ ដើម្បី​បង្ហាញ​ចំណាំ អត្ថបទ វីដេអូ និង​ទំព័រ​ដែល​ទាក់ទង​អ្នក​បំផុត ដែល​អ្នក​បាន​ចូល​មើល​ថ្មីៗ​នេះ ដូច្នេះ​អ្នក​អាច​ត្រឡប់​ទៅ​​កាន់​​វា​​វិញ​បាន​យ៉ាងងាយស្រួល។", - "welcome_label": "កំពុង​បញ្ជាក់​ការ​រំលេច​របស់​អ្នក", - "time_label_less_than_minute": "<1 នាទី", - "time_label_minute": "{number} នាទី", - "time_label_hour": "{number} ម៉ោង", - "time_label_day": "{number} ថ្ងៃ" - }, - "kn": {}, - "ko": { - "newtab_page_title": "새 탭", - "default_label_loading": "읽는 중…", - "header_top_sites": "상위 사이트", - "header_highlights": "하이라이트", - "type_label_visited": "방문한 사이트", - "type_label_bookmarked": "즐겨찾기", - "type_label_synced": "다른 기기에서 동기화", - "type_label_open": "열기", - "type_label_topic": "주제", - "menu_action_bookmark": "즐겨찾기", - "menu_action_remove_bookmark": "즐겨찾기 삭제", - "menu_action_copy_address": "주소 복사", - "menu_action_email_link": "메일로 링크 보내기…", - "menu_action_open_new_window": "새 창에서 열기", - "menu_action_open_private_window": "새 사생활 보호 창에서 열기", - "menu_action_dismiss": "닫기", - "menu_action_delete": "방문 기록에서 삭제", - "search_for_something_with": "다음에서 {search_term} 검색:", - "search_header": "{search_engine_name} 검색", - "search_web_placeholder": "웹 검색", - "search_settings": "검색 설정 바꾸기", - "welcome_title": "새 탭을 소개합니다", - "welcome_body": "최근에 방문한 관련있는 즐겨찾기나 글, 동영상, 페이지를 Firefox가 여기에 표시해서 쉽게 다시 찾아볼 수 있게 할 것입니다.", - "welcome_label": "하이라이트 확인", - "time_label_less_than_minute": "<1분", - "time_label_minute": "{number}분", - "time_label_hour": "{number}시", - "time_label_day": "{number}일" - }, - "ku": {}, - "lij": { - "newtab_page_title": "Neuvo Feuggio", - "default_label_loading": "Carego…", - "header_top_sites": "I megio sciti", - "header_highlights": "In evidensa", - "type_label_visited": "Vixitou", - "type_label_bookmarked": "Azonto a-i segnalibbri", - "type_label_synced": "Scincronizou da 'n atro dispoxitivo", - "type_label_open": "Arvi", - "type_label_topic": "Argomento", - "menu_action_bookmark": "Azonzi a-i segnalibbri", - "menu_action_remove_bookmark": "Scancella segnalibbro", - "menu_action_copy_address": "Còpia indirisso", - "menu_action_email_link": "Manda colegamento…", - "menu_action_open_new_window": "Arvi in neuvo barcon", - "menu_action_open_private_window": "Arvi in neuvo barcon privou", - "menu_action_dismiss": "Scancella", - "menu_action_delete": "Scancella da-a stöia", - "search_for_something_with": "Çerca {search_term} con:", - "search_button": "Çerca", - "search_header": "Riçerca {search_engine_name}", - "search_web_placeholder": "Çerca inta Ræ", - "search_settings": "Cangia inpostaçioin de riçerca", - "welcome_title": "Benvegnuo into neuvo feuggio", - "welcome_body": "Firefox o deuviâ sto spaçio pe mostrâ i elementi ciù scignificativi, comme segnalibbri, articoli, video e pagine vixitatæ da pöco in sa, in mòddo che segian de longo ciù façili da razonze.", - "welcome_label": "Identificaçion elementi in evidensa", - "time_label_less_than_minute": "<1m", - "time_label_minute": "{number}m", - "time_label_hour": "{number}h", - "time_label_day": "{number}d", - "settings_pane_button_label": "Personalizza a teu pagina Neuvo feuggio", - "settings_pane_header": "Preferense neuvo feuggio", - "settings_pane_body": "Çerni cöse ti veu vedde quande t'arvi 'n neuvo feuggio.", - "settings_pane_search_header": "Çerca", - "settings_pane_search_body": "Çerca inta Ræ da-o teu neuvo feuggio.", - "settings_pane_topsites_header": "I megio sciti", - "settings_pane_topsites_body": "Acedi a-i sciti che ti vixiti ciù de spesso.", - "settings_pane_topsites_options_showmore": "Fanni vedde doe righe", - "settings_pane_highlights_header": "In evidensa", - "settings_pane_highlights_body": "Veddi i elementi ciù neuvi inta stöia e i urtimi segnalibbri creæ.", - "settings_pane_done_button": "Fæto", - "edit_topsites_button_text": "Cangia", - "edit_topsites_button_label": "Personalizza a seçion I Megio Sciti", - "edit_topsites_showmore_button": "Fanni vedde de ciù", - "edit_topsites_showless_button": "Fanni vedde de meno", - "edit_topsites_done_button": "Fæto", - "edit_topsites_pin_button": "Azonzi sto scito", - "edit_topsites_edit_button": "Cangia sto scito", - "edit_topsites_dismiss_button": "Ignòra sto scito" - }, - "lo": { - "newtab_page_title": "ແທັບໃຫມ່", - "default_label_loading": "ກຳລັງໂຫລດ…", - "header_top_sites": "ເວັບໄຊຕ໌ຍອດນິຍົມ", - "header_highlights": "ຈຸດເດັ່ນ", - "type_label_visited": "ເຂົ້າໄປເບິງມາແລ້ວ", - "type_label_bookmarked": "ບຸກມາກໄວ້ແລ້ວ", - "type_label_synced": "ໄດ້ Sync ມາຈາກອຸປະກອນອື່ນ", - "type_label_open": "ເປີດ", - "type_label_topic": "ຫົວ​ຂໍ້", - "menu_action_bookmark": "ບຸກມາກ", - "menu_action_remove_bookmark": "ລຶບບຸກມາກອອກ", - "menu_action_copy_address": "ສຳເນົາທີ່ຢູ່", - "menu_action_email_link": "ລີ້ງອີເມວ…", - "menu_action_open_new_window": "ເປີດລີ້ງໃນຫນ້າຕ່າງໃຫມ່", - "menu_action_open_private_window": "ເປີດໃນຫນ້າຕ່າງສ່ວນຕົວໃຫມ່", - "menu_action_dismiss": "ຍົກເລີກ", - "menu_action_delete": "ລຶບອອກຈາກປະຫວັດການນຳໃຊ້", - "search_for_something_with": "ຄົ້ນຫາສໍາລັບ {search_term} ດ້ວຍ:", - "search_header": "ຄົ້ນຫາ {search_engine_name}", - "search_web_placeholder": "ຄົ້ນຫາເວັບ", - "search_settings": "ປ່ຽນການຕັ້ງຄ່າການຄົ້ນຫາ", - "welcome_title": "ຍິນດີຕອນຮັບເຂົ້າສູ່ແຖບໃຫມ່", - "welcome_label": "ກໍາລັງລະບຸລາຍການເດັ່ນຂອງທ່ານ", - "time_label_less_than_minute": "<1 ນາທີ", - "time_label_minute": "{number} ນາທີ", - "time_label_hour": "{number} ຊົ່ວໂມງ", - "time_label_day": "{number} ມື້" - }, - "lt": { - "newtab_page_title": "Nauja kortelė", - "default_label_loading": "Įkeliama…", - "header_top_sites": "Lankomiausios svetainės", - "header_highlights": "Akcentai", - "type_label_visited": "Aplankyti", - "type_label_bookmarked": "Adresyne", - "type_label_synced": "Sinchronizuoti iš kito įrenginio", - "type_label_open": "Atviri", - "type_label_topic": "Tema", - "menu_action_bookmark": "Įrašyti į adresyną", - "menu_action_remove_bookmark": "Pašalinti iš adresyno", - "menu_action_copy_address": "Kopijuoti adresą", - "menu_action_email_link": "Siųsti saitą el. paštu…", - "menu_action_open_new_window": "Atverti naujame lange", - "menu_action_open_private_window": "Atverti naujame privačiajame lange", - "menu_action_dismiss": "Paslėpti", - "menu_action_delete": "Pašalinti iš istorijos", - "search_for_something_with": "Ieškoti „{search_term}“ per:", - "search_button": "Ieškoti", - "search_header": "{search_engine_name} paieška", - "search_web_placeholder": "Ieškokite saityne", - "search_settings": "Keisti paieškos nuostatas", - "welcome_title": "Sveiki, čia nauja kortelė", - "welcome_body": "„Firefox“ naudos šią vietą jums aktualiausių adresyno įrašų, straipsnių, vaizdo įrašų bei neseniai lankytų tinklalapių rodymui, kad galėtumėte lengvai į juos sugrįžti.", - "welcome_label": "Nustatomi jūsų akcentai", - "time_label_less_than_minute": "<1 min.", - "time_label_minute": "{number} min.", - "time_label_hour": "{number} val.", - "time_label_day": "{number} d.", - "settings_pane_button_label": "Tinkinkite savo naujos kortelės puslapį", - "settings_pane_header": "Naujos kortelės nuostatos", - "settings_pane_body": "Pasirinkite, ką matysite atvėrę naują kortelę.", - "settings_pane_search_header": "Paieška", - "settings_pane_search_body": "Ieškokite saityne tiesiai iš naujos kortelės.", - "settings_pane_topsites_header": "Lankomiausios svetainės", - "settings_pane_topsites_body": "Pasiekite jūsų dažniausiai lankomas svetaines.", - "settings_pane_topsites_options_showmore": "Rodyti dvi eilutes", - "settings_pane_highlights_header": "Akcentai", - "settings_pane_highlights_body": "Pažvelkite į savo naujausią naršymo istoriją bei paskiausiai pridėtus adresyno įrašus.", - "settings_pane_done_button": "Atlikta", - "edit_topsites_button_text": "Keisti", - "edit_topsites_button_label": "Tinkinkite savo lankomiausių svetainių skiltį", - "edit_topsites_showmore_button": "Rodyti daugiau", - "edit_topsites_showless_button": "Rodyti mažiau", - "edit_topsites_done_button": "Atlikta", - "edit_topsites_pin_button": "Įsegti šią svetainę", - "edit_topsites_edit_button": "Redaguoti šią svetainę", - "edit_topsites_dismiss_button": "Paslėpti šią svetainę" - }, - "ltg": {}, - "lv": {}, - "mai": {}, - "mk": {}, - "ml": {}, - "mn": {}, - "mr": {}, - "ms": { - "newtab_page_title": "Tab Baru", - "default_label_loading": "Memuatkan…", - "header_top_sites": "Laman Teratas", - "header_highlights": "Serlahan", - "type_label_visited": "Dilawati", - "type_label_bookmarked": "Ditandabuku", - "type_label_synced": "Sync dari peranti lain", - "type_label_open": "Buka", - "type_label_topic": "Topik", - "menu_action_bookmark": "Tandabuku", - "menu_action_remove_bookmark": "Alihkeluar Tandabuku", - "menu_action_copy_address": "Salin Alamat", - "menu_action_email_link": "Pautan E-mel…", - "menu_action_open_new_window": "Buka dalam Tetingkap Baru", - "menu_action_open_private_window": "Buka dalam Tetingkap Peribadi Baru", - "menu_action_dismiss": "Abai", - "menu_action_delete": "Hapuskan sejarah", - "search_for_something_with": "Cari {search_term} dengan:", - "search_button": "Cari", - "search_header": "{search_engine_name} Cari", - "search_web_placeholder": "Cari dalam Web", - "search_settings": "Ubah Tetapan Carian", - "welcome_title": "Selamat Datang ke tab baru", - "welcome_body": "Firefox akan menggunakan ruang ini untuk mempamerkan tandabuku, artikel, video dan halaman yang paling berkaitan dan terkini anda lawati supaya anda boleh mendapatkannya semula dengan mudah.", - "welcome_label": "Mengenalpasti Serlahan anda", - "time_label_less_than_minute": "<1m", - "time_label_minute": "{number}m", - "time_label_hour": "{number}h", - "time_label_day": "{number}d", - "settings_pane_button_label": "Sesuaikan halaman Tab Baru anda", - "settings_pane_header": "Keutamaan Tab Baru", - "settings_pane_body": "Pilih paparan apabila anda buka tab baru.", - "settings_pane_search_header": "Cari", - "settings_pane_search_body": "Carian Web dari tab baru anda.", - "settings_pane_topsites_header": "Laman Teratas", - "settings_pane_topsites_body": "Akses laman web yang paling banyak dilawati.", - "settings_pane_topsites_options_showmore": "Papar dua baris", - "settings_pane_highlights_header": "Serlahan", - "settings_pane_highlights_body": "Papar semula sejarah pelayaran terkini dan tandabuku yang baru diwujudkan.", - "settings_pane_done_button": "Siap", - "edit_topsites_button_text": "Edit", - "edit_topsites_button_label": "Sesuaikan bahagian Laman Teratas anda", - "edit_topsites_showmore_button": "Papar selanjutnya", - "edit_topsites_showless_button": "Papar minima", - "edit_topsites_done_button": "Siap", - "edit_topsites_pin_button": "Pin laman ini", - "edit_topsites_edit_button": "Edit laman ini", - "edit_topsites_dismiss_button": "Buang laman ini" - }, - "my": {}, - "nb-NO": { - "newtab_page_title": "Ny fane", - "default_label_loading": "Laster …", - "header_top_sites": "Mest besøkte nettsider", - "header_highlights": "Høydepunkter", - "type_label_visited": "Besøkt", - "type_label_bookmarked": "Bokmerket", - "type_label_synced": "Synkronisert fra annen enhet", - "type_label_open": "Åpne", - "type_label_topic": "Emne", - "menu_action_bookmark": "Bokmerke", - "menu_action_remove_bookmark": "Fjern bokmerke", - "menu_action_copy_address": "Kopier adresse", - "menu_action_email_link": "Send lenke på e-post …", - "menu_action_open_new_window": "Åpne i nytt vindu", - "menu_action_open_private_window": "Åpne i nytt privat vindu", - "menu_action_dismiss": "Avslå", - "menu_action_delete": "Slett fra historikk", - "search_for_something_with": "Søk etter {search_term} med:", - "search_header": "{search_engine_name}-søk", - "search_web_placeholder": "Søk på nettet", - "search_settings": "Endre søkeinnstillinger", - "welcome_title": "Velkommen til ny fane", - "welcome_body": "Firefox vil bruke denne plassen til å vise deg de mest relevante bokmerkene, artiklene, videoene og sidene du nettopp har besøkt, slik at du enkelt kan finne tilbake til de.", - "welcome_label": "Identifiserer dine høydepunkter", - "time_label_less_than_minute": "<1 m", - "time_label_minute": "{number} m", - "time_label_hour": "{number} t", - "time_label_day": "{number} d" - }, - "ne-NP": { - "newtab_page_title": "नयाँ ट्याब", - "default_label_loading": "लोड हुदैँछ...", - "header_top_sites": "शीर्ष साइटहरु", - "header_highlights": "विशेषताहरू", - "type_label_visited": "भ्रमण गरिएको", - "type_label_bookmarked": "पुस्तकचिनो लागाइएको", - "type_label_synced": "अर्को यण्त्रबाट समक्रमण गरिएको", - "type_label_open": "खोल्नुहोस्", - "type_label_topic": "शीर्षक", - "menu_action_bookmark": "पुस्तकचिनो", - "menu_action_remove_bookmark": "पुस्तकचिनो हटाउनुहोस्", - "menu_action_copy_address": "ठेगाना प्रतिलिपि गर्नुहोस्", - "menu_action_email_link": "लिङ्कलाई इमेल गर्नुहोस्...", - "menu_action_open_new_window": "नयाँ सञ्झ्यालमा खोल्नुहोस्", - "menu_action_open_private_window": "नयाँ निजी सञ्झ्यालमा खोल्नुहोस्", - "menu_action_dismiss": "खारेज गर्नुहोस्", - "menu_action_delete": "इतिहासबाट मेट्नुहोस्", - "search_for_something_with": "{search_term} खोज्न प्रयोग गर्नुहोस्:", - "search_header": "{search_engine_name} खोजी", - "search_web_placeholder": "वेबमा खोज्नुहोस्", - "search_settings": "खोजी सेटिङ परिवर्तन गर्नुहोस्", - "welcome_title": "नयाँ ट्याबमा स्वागत छ", - "welcome_label": "तपाईँका विशेषताहरु पत्ता लगाउँदै", - "time_label_less_than_minute": "< १ मिनेट", - "time_label_minute": "{number} मिनेट", - "time_label_hour": "{number} घण्टा", - "time_label_day": "{number} दिन" - }, - "nl": { - "newtab_page_title": "Nieuw tabblad", - "default_label_loading": "Laden…", - "header_top_sites": "Topwebsites", - "header_highlights": "Highlights", - "type_label_visited": "Bezocht", - "type_label_bookmarked": "Bladwijzer gemaakt", - "type_label_synced": "Gesynchroniseerd vanaf ander apparaat", - "type_label_open": "Open", - "type_label_topic": "Onderwerp", - "menu_action_bookmark": "Bladwijzer maken", - "menu_action_remove_bookmark": "Bladwijzer verwijderen", - "menu_action_copy_address": "Adres kopiëren", - "menu_action_email_link": "Koppeling e-mailen…", - "menu_action_open_new_window": "Openen in een nieuw venster", - "menu_action_open_private_window": "Openen in een nieuw privévenster", - "menu_action_dismiss": "Verwijderen", - "menu_action_delete": "Verwijderen uit geschiedenis", - "search_for_something_with": "Zoeken naar {search_term} met:", - "search_button": "Zoeken", - "search_header": "{search_engine_name} doorzoeken", - "search_web_placeholder": "Zoeken op het web", - "search_settings": "Zoekinstellingen wijzigen", - "welcome_title": "Welkom bij het nieuwe tabblad", - "welcome_body": "Firefox gebruikt deze ruimte om uw meest relevante bladwijzers, artikelen, video’s en pagina’s die u onlangs hebt bezocht weer te geven, zodat u deze eenvoudig kunt terugvinden.", - "welcome_label": "Uw highlights aanduiden", - "time_label_less_than_minute": "< 1 m", - "time_label_minute": "{number} m", - "time_label_hour": "{number} u", - "time_label_day": "{number} d", - "settings_pane_button_label": "Uw Nieuw-tabbladpagina aanpassen", - "settings_pane_header": "Nieuw-tabbladvoorkeuren", - "settings_pane_body": "Kiezen wat u ziet bij het openen van een nieuw tabblad.", - "settings_pane_search_header": "Zoeken", - "settings_pane_search_body": "Het web doorzoeken vanaf uw nieuwe tabblad.", - "settings_pane_topsites_header": "Topwebsites", - "settings_pane_topsites_body": "De websites benaderen die u het vaakst bezoekt.", - "settings_pane_topsites_options_showmore": "Twee rijen tonen", - "settings_pane_highlights_header": "Highlights", - "settings_pane_highlights_body": "Terugkijken naar uw recente navigatiegeschiedenis en nieuw aangemaakte bladwijzers.", - "settings_pane_done_button": "Gereed", - "edit_topsites_button_text": "Bewerken", - "edit_topsites_button_label": "Uw sectie Topwebsites aanpassen", - "edit_topsites_showmore_button": "Meer tonen", - "edit_topsites_showless_button": "Minder tonen", - "edit_topsites_done_button": "Gereed", - "edit_topsites_pin_button": "Deze website vastmaken", - "edit_topsites_edit_button": "Deze website bewerken", - "edit_topsites_dismiss_button": "Deze website verwijderen" - }, - "nn-NO": { - "newtab_page_title": "Ny flik", - "default_label_loading": "Lastar…", - "header_top_sites": "Mest vitja", - "header_highlights": "Høgdepunkt", - "type_label_visited": "Vitja", - "type_label_bookmarked": "Bokmerkte", - "type_label_synced": "Synkronisert frå ei anna eining", - "type_label_open": "Opna", - "type_label_topic": "Emne", - "menu_action_bookmark": "Bokmerke", - "menu_action_remove_bookmark": "Fjern bokmerke", - "menu_action_copy_address": "Kopier adresse", - "menu_action_email_link": "E-postlenke…", - "menu_action_open_new_window": "Opna i nytt vindauge", - "menu_action_open_private_window": "Opna i eit nytt privat vindauge", - "menu_action_dismiss": "Avslå", - "menu_action_delete": "Slett frå historikk", - "search_for_something_with": "Søk etter {search_term} med:", - "search_button": "Søk", - "search_header": "{search_engine_name}", - "search_web_placeholder": "Søk på nettet", - "search_settings": "Endra søkjeinnstillingar", - "welcome_title": "Velkomen til ny fane", - "welcome_body": "Firefox vil bruka denne plassen til å visa deg dei mest relevante bokmerka, artiklane, videoane og sidene du nettopp har vitja, slik at du enkelt kan finna tilbake til dei.", - "welcome_label": "Identifiserer høgdepunkta dine", - "time_label_less_than_minute": "<1 min.", - "time_label_minute": "{number} m", - "time_label_hour": "{number} t", - "time_label_day": "{number} d", - "settings_pane_button_label": "Tilpass sida for Ny fane", - "settings_pane_header": "Innstillingar for Ny fane", - "settings_pane_body": "Vel kva som skal visast når du opnar ei ny fane.", - "settings_pane_search_header": "Søk", - "settings_pane_search_body": "Søk på nettet frå den nye fana di.", - "settings_pane_topsites_header": "Mest besøkte", - "settings_pane_topsites_body": "Tilgang til nettsidene du besøker mest.", - "settings_pane_topsites_options_showmore": "Vis to rader", - "settings_pane_highlights_header": "Høgdepunkt", - "settings_pane_highlights_body": "Sjå tilbake på nyleg nettlesarhistorikk og nyleg oppretta bokmerke.", - "settings_pane_done_button": "Ferdig", - "edit_topsites_button_text": "Rediger", - "edit_topsites_button_label": "Tilpass seksjonen Mest besøkte", - "edit_topsites_showmore_button": "Vis meir", - "edit_topsites_showless_button": "Vis mindre", - "edit_topsites_done_button": "Ferdig", - "edit_topsites_pin_button": "Fest sida", - "edit_topsites_edit_button": "Rediger denne nettsida", - "edit_topsites_dismiss_button": "Avvis denne nettsida" - }, - "or": {}, - "pa-IN": { - "newtab_page_title": "ਨਵੀਂ ਟੈਬ", - "default_label_loading": "…ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ", - "header_top_sites": "ਸਿਖਰਲੀਆਂ ਸਾਈਟਾਂ", - "header_highlights": "ਹਾਈਲਾਈਟ", - "type_label_visited": "ਖੋਲ੍ਹੀਆਂ", - "type_label_bookmarked": "ਬੁੱਕਮਾਰਕ ਕੀਤੀਆਂ", - "type_label_synced": "ਹੋਰ ਡਿਵਾਈਸ ਤੋਂ ਸਿੰਕ ਕੀਤੀਆਂ", - "type_label_open": "ਖੋਲ੍ਹੋ", - "type_label_topic": "ਵਿਸ਼ੇ", - "menu_action_bookmark": "ਬੁੱਕਮਾਰਕ", - "menu_action_remove_bookmark": "ਬੁੱਕਮਾਰਕ ਨੂੰ ਹਟਾਓ", - "menu_action_copy_address": "ਸਿਰਨਾਵੇਂ ਨੂੰ ਕਾਪੀ ਕਰੋ", - "menu_action_email_link": "…ਲਿੰਕ ਨੂੰ ਈਮੇਲ ਕਰੋ", - "menu_action_open_new_window": "ਨਵੀਂ ਵਿੰਡੋ ਵਿੱਚ ਖੋਲ੍ਹੋ", - "menu_action_open_private_window": "ਨਵੀਂ ਪ੍ਰਾਈਵੇਟ ਵਿੰਡੋ ਵਿੱਚ ਖੋਲ੍ਹੋ", - "menu_action_dismiss": "ਰੱਦ ਕਰੋ", - "menu_action_delete": "ਅਤੀਤ ਵਿੱਚੋਂ ਹਟਾਓ", - "search_for_something_with": "{search_term} ਨੂੰ ਇਸ ਨਾਲ ਖੋਜੋ:", - "search_button": "ਖੋਜੋ", - "search_header": "{search_engine_name} ਖੋਜ", - "search_web_placeholder": "ਵੈੱਬ ਨੂੰ ਖੋਜੋ", - "search_settings": "ਖੋਜ ਸੈਟਿੰਗਾਂ ਨੂੰ ਬਦਲੋ", - "welcome_title": "ਨਵੀਂ ਟੈਬ ਉੱਤੇ ਜੀ ਆਇਆਂ ਨੂੰ", - "welcome_body": "ਫਾਇਰਫਾਕਸ ਇਸ ਥਾਂ ਨੂੰ ਤੁਹਾਡੇ ਲਈ ਸਭ ਤੋਂ ਵੱਧ ਢੁੱਕਵੇਂ ਬੁੱਕਮਾਰਕ, ਲੇਖ, ਵੀਡੀਓ ਅਤੇ ਸਫ਼ੇ ਵਿਖਾਉਣ ਲਈ ਵਰਤੇਗਾ, ਜਿਹਨਾਂ ਨੂੰ ਤੁਸੀਂ ਹਾਲ ਵਿੱਚ ਹੀ ਖੋਲ੍ਹਿਆ ਹੈ ਤਾਂ ਕਿ ਤੁਸੀਂ ਉਹਨਾਂ ਉੱਤੇ ਸੌਖੀ ਤਰ੍ਹਾਂ ਵਾਪਸ ਜਾ ਸਕੋ।", - "welcome_label": "ਤੁਹਾਡੇ ਹਾਈਲਾਈਟ ਨੂੰ ਪਛਾਣਿਆ ਜਾ ਰਿਹਾ ਹੈ", - "time_label_less_than_minute": "<1ਮਿੰ", - "time_label_minute": "{number}ਮਿੰ", - "time_label_hour": "{number}ਘੰ", - "time_label_day": "{number}ਦਿ", - "settings_pane_button_label": "ਆਪਣੇ ਨਵੀਂ ਟੈਬ ਸਫ਼ੇ ਨੂੰ ਆਪਣੇ ਮੁਤਾਬਕ ਢਾਲੋ", - "settings_pane_header": "ਨਵੀਂ ਟੈਬ ਲਈ ਪਸੰਦਾਂ", - "settings_pane_body": "ਉਹ ਚੁਣੋ, ਜੋ ਤੁਸੀਂ ਨਵੀਂ ਟੈਬ ਨੂੰ ਖੋਲ੍ਹਣ ਦੇ ਬਾਅਦ ਵੇਖਣਾ ਚਾਹੁੰਦੇ ਹੋ।", - "settings_pane_search_header": "ਖੋਜੋ", - "settings_pane_search_body": "ਆਪਣੀ ਨਵੀਂ ਟੈਬ ਤੋਂ ਵੈੱਬ ਨੂੰ ਖੋਜੋ।", - "settings_pane_topsites_header": "ਸਿਖਰਲੀਆਂ ਸਾਈਟਾਂ", - "settings_pane_topsites_body": "ਵੈੱਬਸਾਈਟਾਂ, ਜਿਹਨਾਂ ਨੂੰ ਤੁਸੀਂ ਸਭ ਤੋਂ ਵੱਧ ਖੋਲ੍ਹਿਆ ਹੈ, ਲਈ ਪਹੁੰਚ।", - "settings_pane_topsites_options_showmore": "ਦੋ ਕਤਾਰਾਂ ਵੇਖਾਓ", - "settings_pane_highlights_header": "ਹਾਈਲਾਈਟ", - "settings_pane_highlights_body": "ਆਪਣੇ ਹਾਲ ਦੇ ਬਰਾਊਜ਼ ਕਰਨ ਦੇ ਅਤੀਤ ਅਤੇ ਨਵੇਂ ਬਣਾਏ ਬੁੱਕਮਾਰਕਾਂ ਉੱਤੇ ਝਲਕ ਮਾਰੋ।", - "settings_pane_done_button": "ਮੁਕੰਮਲ", - "edit_topsites_button_text": "ਸੋਧੋ", - "edit_topsites_button_label": "ਆਪਣੇ ਸਿਖਰਲੀਆਂ ਸਾਈਟਾਂ ਭਾਗ ਨੂੰ ਲੋੜ ਮੁਤਾਬਕ ਢਾਲੋ", - "edit_topsites_showmore_button": "ਹੋਰ ਵੇਖਾਓ", - "edit_topsites_showless_button": "ਘੱਟ ਵੇਖਾਓ", - "edit_topsites_done_button": "ਮੁਕੰਮਲ", - "edit_topsites_pin_button": "ਇਸ ਸਾਈਟ ਨੂੰ ਟੰਗੋ", - "edit_topsites_edit_button": "ਇਹ ਸਾਈਟ ਨੂੰ ਸੋਧੋ", - "edit_topsites_dismiss_button": "ਇਸ ਸਾਈਟ ਰੱਦ ਕਰੋ" - }, - "pl": { - "newtab_page_title": "Nowa karta", - "default_label_loading": "Wczytywanie…", - "header_top_sites": "Popularne", - "header_highlights": "Wyróżnione", - "type_label_visited": "Odwiedzone", - "type_label_bookmarked": "Zakładka", - "type_label_synced": "Z innego urządzenia", - "type_label_open": "Otwarte", - "type_label_topic": "Temat", - "menu_action_bookmark": "Dodaj zakładkę", - "menu_action_remove_bookmark": "Usuń zakładkę", - "menu_action_copy_address": "Kopiuj adres", - "menu_action_email_link": "Wyślij odnośnik…", - "menu_action_open_new_window": "Otwórz w nowym oknie", - "menu_action_open_private_window": "Otwórz w nowym oknie prywatnym", - "menu_action_dismiss": "Odrzuć", - "menu_action_delete": "Usuń z historii", - "search_for_something_with": "Szukaj „{search_term}” w:", - "search_button": "Szukaj", - "search_header": "Wyszukiwanie w {search_engine_name}", - "search_web_placeholder": "Szukaj", - "search_settings": "Zmień ustawienia wyszukiwania", - "welcome_title": "Witamy w nowej karcie", - "welcome_body": "W tym miejscu Firefox będzie wyświetlał najciekawsze zakładki, artykuły, filmy i niedawno odwiedzone strony, aby można było do nich łatwo wrócić.", - "welcome_label": "Wykrywanie ulubionych treści użytkownika", - "time_label_less_than_minute": "<1 min", - "time_label_minute": "{number} min", - "time_label_hour": "{number} godz.", - "time_label_day": "{number} d.", - "settings_pane_button_label": "Dostosuj stronę nowej karty", - "settings_pane_header": "Preferencje nowej karty", - "settings_pane_body": "Wybierz, co widać po otwarciu nowej karty.", - "settings_pane_search_header": "Wyszukiwanie", - "settings_pane_search_body": "Szukaj w Internecie na nowej karcie.", - "settings_pane_topsites_header": "Popularne", - "settings_pane_topsites_body": "Otwieraj najczęściej odwiedzane strony.", - "settings_pane_topsites_options_showmore": "Dwa rzędy", - "settings_pane_highlights_header": "Wyróżnione", - "settings_pane_highlights_body": "Przeglądaj historię i nowo dodane zakładki.", - "settings_pane_done_button": "Gotowe", - "edit_topsites_button_text": "Edytuj", - "edit_topsites_button_label": "Dostosuj często odwiedzane strony", - "edit_topsites_showmore_button": "Więcej", - "edit_topsites_showless_button": "Mniej", - "edit_topsites_done_button": "Gotowe", - "edit_topsites_pin_button": "Przypnij tę stronę", - "edit_topsites_edit_button": "Edytuj tę stronę", - "edit_topsites_dismiss_button": "Odrzuć tę stronę" - }, - "pt-BR": { - "newtab_page_title": "Nova aba", - "default_label_loading": "Carregando…", - "header_top_sites": "Sites preferidos", - "header_highlights": "Destaques", - "type_label_visited": "Visitado", - "type_label_bookmarked": "Favorito", - "type_label_synced": "Sincronizado a partir de outro dispositivo", - "type_label_open": "Abrir", - "type_label_topic": "Tópico", - "menu_action_bookmark": "Favoritos", - "menu_action_remove_bookmark": "Remover favorito", - "menu_action_copy_address": "Copiar endereço", - "menu_action_email_link": "Enviar link por e-mail…", - "menu_action_open_new_window": "Abrir em uma nova janela", - "menu_action_open_private_window": "Abrir em uma nova janela privativa", - "menu_action_dismiss": "Dispensar", - "menu_action_delete": "Excluir do histórico", - "search_for_something_with": "Pesquisar por {search_term} com:", - "search_button": "Pesquisar", - "search_header": "Pesquisa {search_engine_name}", - "search_web_placeholder": "Pesquisar na Web", - "search_settings": "Alterar configurações de pesquisa", - "welcome_title": "Bem-vindo a nova aba", - "welcome_body": "O Firefox irá usar este espaço para lhe mostrar os seus favoritos, artigos, vídeos, e páginas mais relevantes que visitou recentemente, para que possa regressar a estes mais facilmente.", - "welcome_label": "Identificando seus destaques", - "time_label_less_than_minute": "<1m", - "time_label_minute": "{number}m", - "time_label_hour": "{number}h", - "time_label_day": "{number}d", - "settings_pane_button_label": "Personalize sua página de nova aba", - "settings_pane_header": "Preferências de novas abas", - "settings_pane_body": "Escolha o que ver quando abrir uma nova aba.", - "settings_pane_search_header": "Pesquisar", - "settings_pane_search_body": "Pesquise na Web a partir da sua nova aba.", - "settings_pane_topsites_header": "Sites preferidos", - "settings_pane_topsites_body": "Acesse os sites que você mais visita.", - "settings_pane_topsites_options_showmore": "Mostrar duas linhas", - "settings_pane_highlights_header": "Destaques", - "settings_pane_highlights_body": "Veja o seu histórico de navegação recente e favoritos recentemente criados.", - "settings_pane_done_button": "Concluir", - "edit_topsites_button_text": "Editar", - "edit_topsites_button_label": "Personalizar a sua seção de sites preferidos", - "edit_topsites_showmore_button": "Mostrar mais", - "edit_topsites_showless_button": "Mostrar menos", - "edit_topsites_done_button": "Concluir", - "edit_topsites_pin_button": "Fixar este site", - "edit_topsites_edit_button": "Editar este site", - "edit_topsites_dismiss_button": "Descartar este site" - }, - "pt-PT": { - "newtab_page_title": "Novo separador", - "default_label_loading": "A carregar…", - "header_top_sites": "Sites mais visitados", - "header_highlights": "Destaques", - "type_label_visited": "Visitados", - "type_label_bookmarked": "Guardados nos marcadores", - "type_label_synced": "Sincronizado a partir de outro dispositivo", - "type_label_open": "Abertos", - "type_label_topic": "Tópico", - "menu_action_bookmark": "Adicionar aos marcadores", - "menu_action_remove_bookmark": "Remover marcador", - "menu_action_copy_address": "Copiar endereço", - "menu_action_email_link": "Enviar ligação por email…", - "menu_action_open_new_window": "Abrir em nova janela", - "menu_action_open_private_window": "Abrir em nova janela privada", - "menu_action_dismiss": "Dispensar", - "menu_action_delete": "Eliminar do histórico", - "search_for_something_with": "Pesquisar por {search_term} com:", - "search_button": "Pesquisar", - "search_header": "Pesquisa {search_engine_name}", - "search_web_placeholder": "Pesquisar na Web", - "search_settings": "Alterar definições de pesquisa", - "welcome_title": "Bem-vindo ao novo separador", - "welcome_body": "O Firefox irá utilizar este espaço para lhe mostrar os seus marcadores, artigos, vídeos, e páginas mais relevantes que visitou recentemente, para que possa regressar a estes mais facilmente.", - "welcome_label": "A identificar os seus destaques", - "time_label_less_than_minute": "<1m", - "time_label_minute": "{number}m", - "time_label_hour": "{number}h", - "time_label_day": "{number}d", - "settings_pane_button_label": "Personalizar a sua página de novo separador", - "settings_pane_header": "Novas preferências de separador", - "settings_pane_body": "Escolha o que ver quando abre um novo separador.", - "settings_pane_search_header": "Pesquisar", - "settings_pane_search_body": "Pesquise na Web a partir do seu novo separador.", - "settings_pane_topsites_header": "Sites mais visitados", - "settings_pane_topsites_body": "Aceda aos websites que mais visita.", - "settings_pane_topsites_options_showmore": "Mostrar duas linhas", - "settings_pane_highlights_header": "Destaques", - "settings_pane_highlights_body": "Veja o seu histórico de navegação recente e marcadores recentemente criados.", - "settings_pane_done_button": "Feito", - "edit_topsites_button_text": "Editar", - "edit_topsites_button_label": "Personalizar a sua secção de sites mais visitados", - "edit_topsites_showmore_button": "Mostrar mais", - "edit_topsites_showless_button": "Mostrar menos", - "edit_topsites_done_button": "Feito", - "edit_topsites_pin_button": "Afixar este site", - "edit_topsites_edit_button": "Editar este site", - "edit_topsites_dismiss_button": "Descartar este site" - }, - "rm": { - "newtab_page_title": "Nov tab", - "default_label_loading": "Chargiar…", - "header_top_sites": "Paginas preferidas", - "header_highlights": "Accents", - "type_label_visited": "Visità", - "type_label_bookmarked": "Cun segnapagina", - "type_label_synced": "Sincronisà dad auters apparats", - "type_label_open": "Avert", - "type_label_topic": "Tema", - "menu_action_bookmark": "Marcar sco segnapagina", - "menu_action_remove_bookmark": "Allontanar il segnapagina", - "menu_action_copy_address": "Copiar l'adressa", - "menu_action_email_link": "Trametter la colliaziun per e-mail…", - "menu_action_open_new_window": "Avrir en ina nova fanestra", - "menu_action_open_private_window": "Avrir en ina nova fanestra privata", - "menu_action_dismiss": "Serrar", - "menu_action_delete": "Stizzar da la cronologia", - "search_for_something_with": "Tschertgar {search_term} cun:", - "search_button": "Tschertgar", - "search_header": "Tschertga da {search_engine_name}", - "search_web_placeholder": "Tschertgar en il Web", - "search_settings": "Midar las preferenzas per tschertgar", - "welcome_title": "Bainvegni sin in nov tab", - "welcome_body": "Firefox utilisescha quest plaz per ta mussar ils segnapaginas, ils artitgels, ils videos e las paginas las pli relevantas che ti has visità dacurt, uschè che ti pos turnar a moda simpla tar quellas.", - "welcome_label": "Identifitgar tes accents", - "time_label_less_than_minute": "< 1 min", - "time_label_minute": "{number} min", - "time_label_hour": "{number} uras", - "time_label_day": "{number} dis", - "settings_pane_button_label": "Persunalisar tia pagina per novs tabs", - "settings_pane_header": "Preferenzas per novs tabs", - "settings_pane_body": "Tscherna tge che ti vesas sche ti avras in nov tab.", - "settings_pane_search_header": "Tschertgar", - "settings_pane_search_body": "Tschertgar en l'internet da tes nov tab.", - "settings_pane_topsites_header": "Paginas preferidas", - "settings_pane_topsites_body": "Acceder las websites che ti visitas il pli savens.", - "settings_pane_topsites_options_showmore": "Mussar duas colonnas", - "settings_pane_highlights_header": "Accents", - "settings_pane_highlights_body": "Dar in sguard enavos sin websites visitadas dacurt e sin segnapaginas creads dacurt.", - "settings_pane_done_button": "Finì", - "edit_topsites_button_text": "Modifitgar", - "edit_topsites_button_label": "Persunalisar la secziun da paginas preferidas", - "edit_topsites_showmore_button": "Mussar dapli", - "edit_topsites_showless_button": "Mussar pli pauc", - "edit_topsites_done_button": "Finì", - "edit_topsites_pin_button": "Fixar questa pagina", - "edit_topsites_edit_button": "Modifitgar questa pagina", - "edit_topsites_dismiss_button": "Allontanar questa pagina" - }, - "ro": { - "newtab_page_title": "Filă nouă", - "default_label_loading": "Se încarcă…", - "header_top_sites": "Site-uri de top", - "header_highlights": "Evidențieri", - "type_label_visited": "Vizitate", - "type_label_bookmarked": "Însemnat", - "type_label_synced": "Sincronizat de pe alt dispozitiv", - "type_label_open": "Deschise", - "type_label_topic": "Subiect", - "menu_action_bookmark": "Însemnează", - "menu_action_remove_bookmark": "Elimină semnul de carte", - "menu_action_copy_address": "Copiază adresa", - "menu_action_email_link": "Deschide linkul…", - "menu_action_open_new_window": "Deschide într-o fereastră nouă", - "menu_action_open_private_window": "Deschide într-o fereastră privată nouă", - "menu_action_dismiss": "Înlătură", - "menu_action_delete": "Șterge din istoric", - "search_for_something_with": "Caută {search_term} cu: ", - "search_header": "Căutare {search_engine_name}", - "search_web_placeholder": "Caută pe web", - "search_settings": "Schimbă setările de căutare", - "welcome_title": "Bun venit în noua filă", - "welcome_body": "Firefox va folosi acest spațiu pentru a arăta cele mai relevante semne de carte, articole, videouri și pagini vizitate recent pentru a reveni la acestea ușor.", - "welcome_label": "Se identifică evidențierile tale", - "time_label_less_than_minute": "<1m", - "time_label_minute": "{number}m", - "time_label_hour": "{number}h", - "time_label_day": "{number}d" - }, - "ru": { - "newtab_page_title": "Новая вкладка", - "default_label_loading": "Загрузка…", - "header_top_sites": "Топ сайтов", - "header_highlights": "Избранные", - "type_label_visited": "Посещено", - "type_label_bookmarked": "В закладках", - "type_label_synced": "Синхронизировано с другого устройства", - "type_label_open": "Открыта", - "type_label_topic": "Тема", - "menu_action_bookmark": "Добавить в закладки", - "menu_action_remove_bookmark": "Удалить закладку", - "menu_action_copy_address": "Скопировать ссылку", - "menu_action_email_link": "Отправить ссылку…", - "menu_action_open_new_window": "Открыть в новом окне", - "menu_action_open_private_window": "Открыть в новом приватном окне", - "menu_action_dismiss": "Скрыть", - "menu_action_delete": "Удалить из истории", - "search_for_something_with": "Искать {search_term} в:", - "search_button": "Искать", - "search_header": "Искать в {search_engine_name}", - "search_web_placeholder": "Искать в Интернете", - "search_settings": "Изменить настройки поиска", - "welcome_title": "Добро пожаловать на новую вкладку", - "welcome_body": "Firefox будет использовать это место, чтобы отображать самые актуальные закладки, статьи, видео и страницы, которые вы недавно посетили, чтобы вы смогли легко попасть на них снова.", - "welcome_label": "Определение вашего избранного", - "time_label_less_than_minute": "<1 мин.", - "time_label_minute": "{number} мин.", - "time_label_hour": "{number} ч.", - "time_label_day": "{number} д.", - "settings_pane_button_label": "Настроить свою страницу новой вкладки", - "settings_pane_header": "Настройки новой вкладки", - "settings_pane_body": "Выберите, что показывать при открытии новой вкладки.", - "settings_pane_search_header": "Поиск", - "settings_pane_search_body": "Поиск в Интернете с вашей новой вкладки.", - "settings_pane_topsites_header": "Топ сайтов", - "settings_pane_topsites_body": "Получите доступ к сайтам, которые вы посещаете чаще всего.", - "settings_pane_topsites_options_showmore": "Показать в два ряда", - "settings_pane_highlights_header": "Избранные", - "settings_pane_highlights_body": "Посмотрите на вашу недавнюю историю веб-сёрфинга и недавно сделанные закладки.", - "settings_pane_done_button": "Готово", - "edit_topsites_button_text": "Изменить", - "edit_topsites_button_label": "Настроить свой топ сайтов", - "edit_topsites_showmore_button": "Показать больше", - "edit_topsites_showless_button": "Показать меньше", - "edit_topsites_done_button": "Готово", - "edit_topsites_pin_button": "Закрепить этот сайт", - "edit_topsites_edit_button": "Изменить этот сайт", - "edit_topsites_dismiss_button": "Скрыть этот сайт" - }, - "si": {}, - "sk": { - "newtab_page_title": "Nová karta", - "default_label_loading": "Načítava sa…", - "header_top_sites": "Top stránky", - "header_highlights": "Vybrané stránky", - "type_label_visited": "Navštívené", - "type_label_bookmarked": "V záložkách", - "type_label_synced": "Synchronizované z ďalšieho zariadenia", - "type_label_open": "Otvorené", - "type_label_topic": "Téma", - "menu_action_bookmark": "Pridať medzi záložky", - "menu_action_remove_bookmark": "Odstrániť záložku", - "menu_action_copy_address": "Kopírovať adresu", - "menu_action_email_link": "Odoslať odkaz e-mailom…", - "menu_action_open_new_window": "Otvoriť v novom okne", - "menu_action_open_private_window": "Otvoriť v novom okne režimu Súkromné prehliadanie", - "menu_action_dismiss": "Skryť", - "menu_action_delete": "Odstrániť z histórie", - "search_for_something_with": "Hľadať {search_term} pomocou:", - "search_button": "Hľadať", - "search_header": "Vyhľadávanie pomocou {search_engine_name}", - "search_web_placeholder": "Vyhľadávanie na webe", - "search_settings": "Zmeniť nastavenia vyhľadávania", - "welcome_title": "Vitajte na stránke novej karty", - "welcome_body": "Firefox bude na tomto mieste zobrazovať často zobrazované záložky, články, videá a stránky, ktoré ste nedávno navštívili. Váš prístup k nim je tak omnoho ľahší.", - "welcome_label": "Identifikácia vybraných stránok", - "time_label_less_than_minute": "< 1 min", - "time_label_minute": "{number} min", - "time_label_hour": "{number} hod", - "time_label_day": "{number} d", - "settings_pane_button_label": "Prispôsobte si svoju stránku Nová karta", - "settings_pane_header": "Nastavenia Novej karty", - "settings_pane_body": "Vyberte si, čo chcete vidieť keď otvoríte novú kartu.", - "settings_pane_search_header": "Vyhľadávanie", - "settings_pane_search_body": "Vyhľadávanie zo stránky novej karty.", - "settings_pane_topsites_header": "Top stránky", - "settings_pane_topsites_body": "Prístup k webovým stránkam, ktoré navštevujete najčastejšie.", - "settings_pane_topsites_options_showmore": "Zobraziť dva riadky", - "settings_pane_highlights_header": "Vybrané stránky", - "settings_pane_highlights_body": "Pozrite sa na vašu nedávnu históriu prehliadania a na novovytvorené záložky.", - "settings_pane_done_button": "Hotovo", - "edit_topsites_button_text": "Upraviť", - "edit_topsites_button_label": "Upravte sekciu Top stránky", - "edit_topsites_showmore_button": "Zobraziť viac", - "edit_topsites_showless_button": "Zobraziť menej", - "edit_topsites_done_button": "Hotovo", - "edit_topsites_pin_button": "Pripnúť túto stránku", - "edit_topsites_edit_button": "Upraviť túto stránku", - "edit_topsites_dismiss_button": "Odstrániť túto stránku" - }, - "sl": { - "newtab_page_title": "Nov zavihek", - "default_label_loading": "Nalaganje …", - "header_top_sites": "Glavne strani", - "header_highlights": "Poudarki", - "type_label_visited": "Obiskano", - "type_label_bookmarked": "Med zaznamki", - "type_label_synced": "Sinhronizirano z druge naprave", - "type_label_open": "Odpri", - "type_label_topic": "Tema", - "menu_action_bookmark": "Dodaj med zaznamke", - "menu_action_remove_bookmark": "Odstrani zaznamek", - "menu_action_copy_address": "Kopiraj naslov", - "menu_action_email_link": "Pošlji povezavo po e-pošti …", - "menu_action_open_new_window": "Odpri v novem oknu", - "menu_action_open_private_window": "Odpri v novem zasebnem oknu", - "menu_action_dismiss": "Opusti", - "menu_action_delete": "Izbriši iz zgodovine", - "search_for_something_with": "Išči \"{search_term}\" z iskalnikom:", - "search_button": "Iskanje", - "search_header": "Iskanje {search_engine_name}", - "search_web_placeholder": "Iskanje po spletu", - "search_settings": "Spremeni nastavitve iskanja", - "welcome_title": "Dobrodošli v novem zavihku", - "welcome_body": "Na tem prostoru bo Firefox prikazoval najustreznejše zaznamke, članke, videoposnetke in nedavno obiskane strani, tako da jih lahko pozneje znova hitro najdete.", - "welcome_label": "Zbiranje poudarkov", - "time_label_less_than_minute": "<1 min", - "time_label_minute": "{number} min", - "time_label_hour": "{number} ur", - "time_label_day": "{number} dni", - "settings_pane_button_label": "Prilagodite stran novega zavihka", - "settings_pane_header": "Nastavitve novega zavihka", - "settings_pane_body": "Izberite, kaj naj se prikaže, ko odprete nov zavihek.", - "settings_pane_search_header": "Išči", - "settings_pane_search_body": "Iščite po spletu s strani novega zavihka.", - "settings_pane_topsites_header": "Glavne strani", - "settings_pane_topsites_body": "Priročen dostop do najbolj obiskanih strani.", - "settings_pane_topsites_options_showmore": "Prikaži dve vrsti", - "settings_pane_highlights_header": "Poudarki", - "settings_pane_highlights_body": "Pogled nazaj na nedavno zgodovino brskanja in novo ustvarjene zaznamke.", - "settings_pane_done_button": "Končano", - "edit_topsites_button_text": "Uredi", - "edit_topsites_button_label": "Prilagodite odsek Glavne strani", - "edit_topsites_showmore_button": "Prikaži več", - "edit_topsites_showless_button": "Prikaži manj", - "edit_topsites_done_button": "Končano", - "edit_topsites_pin_button": "Pripni to stran", - "edit_topsites_edit_button": "Uredi to stran", - "edit_topsites_dismiss_button": "Odstrani to stran" - }, - "son": {}, - "sq": { - "newtab_page_title": "Skedë e Re", - "default_label_loading": "Po ngarkohet…", - "header_top_sites": "Sajte Kryesues", - "header_highlights": "Highlights", - "type_label_visited": "Të vizituara", - "type_label_bookmarked": "Të faqeruajtura", - "type_label_synced": "Njëkohësuar prej pajisjeje tjetër", - "type_label_open": "Hape", - "type_label_topic": "Temë", - "menu_action_bookmark": "Faqerojtës", - "menu_action_remove_bookmark": "Hiqe Faqerojtësin", - "menu_action_copy_address": "Kopjoje Adresën", - "menu_action_email_link": "Dërgoni Lidhje me Email…", - "menu_action_open_new_window": "Hape në Dritare të Re", - "menu_action_open_private_window": "Hape në Dritare të Re Private", - "menu_action_dismiss": "Hidhe tej", - "menu_action_delete": "Fshije prej Historiku", - "search_for_something_with": "Kërko për {search_term} me:", - "search_header": "Kërkim me {search_engine_name}", - "search_web_placeholder": "Kërkoni në Web", - "search_settings": "Ndryshoji Rregullimet e Kërkimit", - "welcome_title": "Mirë se vini te skedë e re", - "welcome_body": "Firefox-i do ta përdorë këtë hapësirë për t’ju shfaqur faqerojtësit, artikujt, videot dhe faqet më me peshë që keni vizituar së fundi, që kështu të mund të ktheheni lehtë në to.", - "welcome_label": "Po identifikohen Highlights tuaj", - "time_label_less_than_minute": "<1m", - "time_label_minute": "{number}m", - "time_label_hour": "{number}h", - "time_label_day": "{number}d" - }, - "sr": { - "newtab_page_title": "Нови језичак", - "default_label_loading": "Учитавање…", - "header_top_sites": "Популарни сајтови", - "header_highlights": "Истакнути", - "type_label_visited": "Посећене", - "type_label_bookmarked": "Забележено", - "type_label_synced": "Синхронизовано са другог уређаја", - "type_label_open": "Отвори", - "type_label_topic": "Тема", - "menu_action_bookmark": "Забележи", - "menu_action_remove_bookmark": "Уклони забелешку", - "menu_action_copy_address": "Копирај адресу", - "menu_action_email_link": "Веза е-поште…", - "menu_action_open_new_window": "Отвори у новом прозору", - "menu_action_open_private_window": "Отвори у новом приватном прозору", - "menu_action_dismiss": "Занемари", - "menu_action_delete": "Уклони из историјата", - "search_for_something_with": "Претражите {search_term} са:", - "search_button": "Претражи", - "search_header": "{search_engine_name} претрага", - "search_web_placeholder": "Претражујте веб", - "search_settings": "Измените подешавања претраге", - "welcome_title": "Добродошли на нови језичак", - "welcome_body": "Firefox ће користити овај простор да вам приказује најрелевантне језичке, чланке, видео клипове и странице које сте недавно посетили, како бисте им се лако могли вратити.", - "welcome_label": "Учитавам ваше истакнуте ставке", - "time_label_less_than_minute": "<1m", - "time_label_minute": "{number}m", - "time_label_hour": "{number}h", - "time_label_day": "{number}d", - "settings_pane_button_label": "Прилагодите страницу новог језичка", - "settings_pane_header": "Поставке новог језичка", - "settings_pane_body": "Изаберите шта желите да видите када отворите нови језичак.", - "settings_pane_search_header": "Претрага", - "settings_pane_search_body": "Претражујте веб из вашег новог језичка.", - "settings_pane_topsites_header": "Популарни сајтови", - "settings_pane_topsites_body": "Приступите најпосећенијим веб сајтовима.", - "settings_pane_topsites_options_showmore": "Прикажи два реда", - "settings_pane_highlights_header": "Истакнути", - "settings_pane_highlights_body": "Прегледајте ваш скорашњи историјат и нове забелешке.", - "settings_pane_done_button": "Готово", - "edit_topsites_button_text": "Уреди", - "edit_topsites_button_label": "Прилагодите секцију популарних сајтова", - "edit_topsites_showmore_button": "Прикажи више", - "edit_topsites_showless_button": "Прикажи мање", - "edit_topsites_done_button": "Готово", - "edit_topsites_pin_button": "Закачи овај сајт", - "edit_topsites_edit_button": "Уреди овај сајт", - "edit_topsites_dismiss_button": "Одбаци овај сајт" - }, - "sv-SE": { - "newtab_page_title": "Ny flik", - "default_label_loading": "Laddar…", - "header_top_sites": "Mest besökta", - "header_highlights": "Höjdpunkter", - "type_label_visited": "Besökta", - "type_label_bookmarked": "Bokmärkta", - "type_label_synced": "Synkroniserade från en annan enhet", - "type_label_open": "Öppna", - "type_label_topic": "Ämne", - "menu_action_bookmark": "Bokmärke", - "menu_action_remove_bookmark": "Ta bort bokmärke", - "menu_action_copy_address": "Kopiera adress", - "menu_action_email_link": "E-posta länk…", - "menu_action_open_new_window": "Öppna i nytt fönster", - "menu_action_open_private_window": "Öppna i nytt privat fönster", - "menu_action_dismiss": "Avfärda", - "menu_action_delete": "Ta bort från historik", - "search_for_something_with": "Sök efter {search_term} med:", - "search_button": "Sök", - "search_header": "{search_engine_name}", - "search_web_placeholder": "Sök på webben", - "search_settings": "Ändra sökinställningar", - "welcome_title": "Välkommen till ny flik", - "welcome_body": "Firefox kommer att använda detta utrymme för att visa dina mest relevanta bokmärken, artiklar, videor och sidor du nyligen besökt, så du kan hitta dem lätt.", - "welcome_label": "Identifierar dina höjdpunkter", - "time_label_less_than_minute": "<1 min", - "time_label_minute": "{number} min", - "time_label_hour": "{number} h", - "time_label_day": "{number} d", - "settings_pane_button_label": "Anpassa sidan för Ny flik", - "settings_pane_header": "Inställningar Ny flik", - "settings_pane_body": "Välj vad som ska visas när du öppnar en ny flik.", - "settings_pane_search_header": "Sök", - "settings_pane_search_body": "Sök på webben från din nya flik.", - "settings_pane_topsites_header": "Mest besökta", - "settings_pane_topsites_body": "Åtkomst till de webbplatser du besökt mest.", - "settings_pane_topsites_options_showmore": "Visa två rader", - "settings_pane_highlights_header": "Höjdpunkter", - "settings_pane_highlights_body": "Titta tillbaka på din senaste webbhistorik och nyskapade bokmärken.", - "settings_pane_done_button": "Klar", - "edit_topsites_button_text": "Redigera", - "edit_topsites_button_label": "Anpassa avsnittet Mest besökta", - "edit_topsites_showmore_button": "Visa mer", - "edit_topsites_showless_button": "Visa mindre", - "edit_topsites_done_button": "Klar", - "edit_topsites_pin_button": "Fäst denna webbplats", - "edit_topsites_edit_button": "Redigera denna webbplats", - "edit_topsites_dismiss_button": "Avfärda denna webbplats" - }, - "ta": {}, - "ta-LK": {}, - "te": { - "newtab_page_title": "కొత్త ట్యాబు", - "default_label_loading": "వస్తోంది…", - "header_top_sites": "మేటి సైట్లు", - "header_highlights": "ముఖ్యాంశాలు", - "type_label_visited": "సందర్శించినవి", - "type_label_bookmarked": "ఇష్టాంశము చేయబడినది", - "type_label_synced": "మరో పరికరం నుంచి సమకాలీకరించి తెచ్చుకున్నవి", - "type_label_open": "తెరువు", - "type_label_topic": "విషయం", - "menu_action_bookmark": "ఇష్టాంశము", - "menu_action_remove_bookmark": "ఇష్టాంశాన్ని తొలగించు", - "menu_action_copy_address": "చిరునామా కాపీ చెయ్యండి", - "menu_action_email_link": "ఈమెయిలు లింకు…", - "menu_action_open_new_window": "కొత్త విండోలో తెరువు", - "menu_action_open_private_window": "కొత్త వ్యక్తిగత విండోలో తెరువు", - "menu_action_dismiss": "విస్మరించు", - "menu_action_delete": "చరిత్ర నుంచి తీసివేయి", - "search_for_something_with": "{search_term} కోసం దీని సాయంతో వెతుకు:", - "search_header": "{search_engine_name} శోధన", - "search_web_placeholder": "వెబ్ లో వెతకండి", - "search_settings": "శోధన అమరికలు మార్చు", - "welcome_title": "కొత్త ట్యాబుకు స్వాగతం", - "welcome_body": "సముచితమైన మీ ఇష్టాంశాలను, వ్యాసాలను, వీడియోలను, ఇంకా మీరు ఇటీవలే చూసిన పేజీలను మీకు తేలిగ్గా అందుబాటులో ఉంచేందుకు Firefox ఈ జాగాని వాడుకుంటుంది.", - "welcome_label": "మీ ముఖ్యాంశాలను గుర్తిస్తున్నది", - "time_label_less_than_minute": "<1ని", - "time_label_minute": "{number}ని", - "time_label_hour": "{number}గం", - "time_label_day": "{number}రో" - }, - "th": { - "newtab_page_title": "แท็บใหม่", - "default_label_loading": "กำลังโหลด…", - "header_top_sites": "ไซต์เด่น", - "header_highlights": "รายการเด่น", - "type_label_visited": "เยี่ยมชมแล้ว", - "type_label_bookmarked": "คั่นหน้าแล้ว", - "type_label_synced": "ซิงค์จากอุปกรณ์อื่น", - "type_label_open": "เปิด", - "type_label_topic": "หัวข้อ", - "menu_action_bookmark": "ที่คั่นหน้า", - "menu_action_remove_bookmark": "เอาที่คั่นหน้าออก", - "menu_action_copy_address": "คัดลอกที่อยู่", - "menu_action_email_link": "ส่งอีเมลลิงก์…", - "menu_action_open_new_window": "เปิดในหน้าต่างใหม่", - "menu_action_open_private_window": "เปิดในหน้าต่างส่วนตัวใหม่", - "menu_action_dismiss": "ยกเลิก", - "menu_action_delete": "ลบออกจากประวัติ", - "search_for_something_with": "ค้นหาสำหรับ {search_term} ด้วย:", - "search_button": "ค้นหา", - "search_header": "ค้นหา {search_engine_name}", - "search_web_placeholder": "ค้นหาเว็บ", - "search_settings": "เปลี่ยนการตั้งค่าการค้นหา", - "welcome_title": "ยินดีต้อนรับสู่แท็บใหม่", - "welcome_body": "Firefox จะใช้พื้นที่นี้เพื่อแสดงที่คั่นหน้า, บทความ, วิดีโอ และหน้าที่คุณเพิ่งเยี่ยมชมที่เกี่ยวข้องกับคุณมากที่สุด เพื่อให้คุณสามารถกลับมาชมได้อย่างง่ายดาย", - "welcome_label": "กำลังระบุรายการเด่นของคุณ", - "time_label_less_than_minute": "<1 นาที", - "time_label_minute": "{number} นาที", - "time_label_hour": "{number} ชั่วโมง", - "time_label_day": "{number} วัน", - "settings_pane_button_label": "ปรับแต่งหน้าแท็บใหม่ของคุณ", - "settings_pane_header": "ตั้งค่าแท็บใหม่", - "settings_pane_body": "เลือกสิ่งที่คุณเห็นเมื่อคุณเปิดแท็บใหม่", - "settings_pane_search_header": "ค้นหา", - "settings_pane_search_body": "ค้นหาเว็บจากแท็บใหม่ของคุณ", - "settings_pane_topsites_header": "ไซต์เด่น", - "settings_pane_topsites_body": "เข้าถึงเว็บไซต์ที่คุณเยี่ยมชมมากที่สุด", - "settings_pane_topsites_options_showmore": "แสดงสองแถว", - "settings_pane_highlights_header": "รายการเด่น", - "settings_pane_done_button": "เสร็จสิ้น", - "edit_topsites_button_text": "แก้ไข", - "edit_topsites_button_label": "ปรับแต่งส่วนไซต์เด่นของคุณ", - "edit_topsites_showmore_button": "แสดงเพิ่มเติม", - "edit_topsites_showless_button": "แสดงน้อยลง", - "edit_topsites_done_button": "เสร็จสิ้น", - "edit_topsites_pin_button": "ปักหมุดไซต์นี้", - "edit_topsites_edit_button": "แก้ไขไซต์นี้", - "edit_topsites_dismiss_button": "ไม่สนใจไซต์นี้" - }, - "tl": { - "newtab_page_title": "Bagong Tab", - "default_label_loading": "Pagkarga…", - "header_top_sites": "Tuktok na mga Site", - "header_highlights": "Highlights", - "type_label_visited": "Binisita", - "type_label_bookmarked": "Bookmarked", - "type_label_synced": "Naka-sync mula sa ibang kagamitan", - "type_label_open": "Bukas", - "type_label_topic": "Topiko", - "menu_action_bookmark": "Bookmark", - "menu_action_remove_bookmark": "Alisin ang Bookmark", - "menu_action_copy_address": "Kopyahin ang Address", - "menu_action_email_link": "Email Link…", - "menu_action_open_new_window": "Buksan sa isang Bagong Window", - "menu_action_open_private_window": "Buksan sa isang Pribadong Bago na Window", - "menu_action_dismiss": "Paalisin", - "menu_action_delete": "Tanggalin mula History", - "search_for_something_with": "Maghanap ng mga {search_term} na may:", - "search_button": "Hanapin", - "search_header": "{search_engine_name} Hanapin", - "search_web_placeholder": "Hanapin sa Web", - "search_settings": "Baguhin ang mga Setting ng Paghahanap", - "welcome_title": "Maligayang pagdating sa bagong tab", - "welcome_body": "Firefox ay gagamit ng puwang upang ipakita ang iyong mga pinaka-kaugnay na bookmark, artikulo, video, at mga pahina ng kamakailan na iyong binisita, kaya maaari kang bumalik sa mga ito ng madali.", - "welcome_label": "Ang pagkilala sa iyong Highlights", - "time_label_less_than_minute": "<1m", - "time_label_minute": "{number}m", - "time_label_hour": "{number}h", - "time_label_day": "{number}d", - "settings_pane_button_label": "I-customize ang iyong pahina ng Bagong Tab", - "settings_pane_header": "Bagong Kagustuhan na Tab", - "settings_pane_body": "Piliin kung ano ang makikita mo kapag binuksan mo ang isang bagong tab.", - "settings_pane_search_header": "Paghahanap", - "settings_pane_search_body": "Hanapin sa Web mula sa iyong bagong tab.", - "settings_pane_topsites_header": "Tuktok na mga Site", - "settings_pane_topsites_body": "Ma-access ang mga website na karamihang binibisita.", - "settings_pane_topsites_options_showmore": "Ipakita ang dalawang mga hanay", - "settings_pane_highlights_header": "Highlights", - "settings_pane_highlights_body": "Titingnan mo ang iyong kamakailang kasaysayan ng pagba-browse at nilikhang bagong bookmark.", - "settings_pane_done_button": "Tapos", - "edit_topsites_button_text": "I-edit", - "edit_topsites_button_label": "I-customize ang iyong Tuktok na mga seksyon ng Sites", - "edit_topsites_showmore_button": "Magpakita ng higit pa", - "edit_topsites_showless_button": "Magpakita ng mas kaunti", - "edit_topsites_done_button": "Tapos", - "edit_topsites_pin_button": "I-pin sa site na ito", - "edit_topsites_edit_button": "I-edit ang site na ito", - "edit_topsites_dismiss_button": "I-dismiss sa site na ito" - }, - "tr": { - "newtab_page_title": "Yeni Sekme", - "default_label_loading": "Yükleniyor…", - "header_top_sites": "En Sık Kullanılan Siteler", - "header_highlights": "Öne Çıkanlar", - "type_label_visited": "Ziyaret edildi", - "type_label_bookmarked": "Yer imlerine eklendi", - "type_label_synced": "Başka bir cihazdan eşitlendi", - "type_label_open": "Açık", - "type_label_topic": "Konu", - "menu_action_bookmark": "Yer imlerine ekle", - "menu_action_remove_bookmark": "Yer imini sil", - "menu_action_copy_address": "Adresi kopyala", - "menu_action_email_link": "Bağlantıyı e-postayla gönder…", - "menu_action_open_new_window": "Yeni pencerede aç", - "menu_action_open_private_window": "Yeni gizli pencerede aç", - "menu_action_dismiss": "Kapat", - "menu_action_delete": "Geçmişten sil", - "search_for_something_with": "{search_term} terimini şununla ara:", - "search_button": "Ara", - "search_header": "{search_engine_name} Araması", - "search_web_placeholder": "Web'de ara", - "search_settings": "Arama ayarlarını değiştir", - "welcome_title": "Yeni sekmeye hoş geldiniz", - "welcome_body": "Firefox son zamanlarda ziyaret ettiğiniz ve sık kullandığınız yer imlerini, makaleleri, videoları ve sayfaları onlara tekrar kolayca geri dönebilmeniz için bu alanda gösterecektir.", - "welcome_label": "Öne Çıkanlar'ınızı tanıyın", - "time_label_less_than_minute": "<1 dk", - "time_label_minute": "{number} dk", - "time_label_hour": "{number} sa", - "time_label_day": "{number} g", - "settings_pane_button_label": "Yeni Sekme sayfanızı özelleştirin", - "settings_pane_header": "Yeni Sekme Tercihleri", - "settings_pane_body": "Yeni bir sekme açtığınızda neleri göreceğinizi seçin.", - "settings_pane_search_header": "Arama", - "settings_pane_search_body": "Yeni sekme üzerinden web'de arama yapın.", - "settings_pane_topsites_header": "Sık Kullandıklarınız", - "settings_pane_topsites_body": "En sık ziyaret ettiğiniz web sitelerine erişin.", - "settings_pane_topsites_options_showmore": "İki satır göster", - "settings_pane_highlights_header": "Öne Çıkanlar", - "settings_pane_highlights_body": "Yakın zamandaki gezinti geçmişinize ve yeni eklediğiniz yer imlerine göz atın.", - "settings_pane_done_button": "Tamam", - "edit_topsites_button_text": "Düzenle", - "edit_topsites_button_label": "Sık Kullandıklarınız bölümünü özelleştirin", - "edit_topsites_showmore_button": "Daha fazla göster", - "edit_topsites_showless_button": "Daha az göster", - "edit_topsites_done_button": "Tamam", - "edit_topsites_pin_button": "Bu siteyi sabitle", - "edit_topsites_edit_button": "Bu siteyi düzenle", - "edit_topsites_dismiss_button": "Bu siteyi görmezden gel" - }, - "uk": { - "newtab_page_title": "Нова вкладка", - "default_label_loading": "Завантаження…", - "header_top_sites": "Популярні сайти", - "header_highlights": "Обране", - "type_label_visited": "Відвідано", - "type_label_bookmarked": "Закладено", - "type_label_synced": "Синхронізовано з іншого пристрою", - "type_label_open": "Відкрито", - "type_label_topic": "Тема", - "menu_action_bookmark": "Додати до закладок", - "menu_action_remove_bookmark": "Вилучити закладку", - "menu_action_copy_address": "Копіювати адресу", - "menu_action_email_link": "Надіслати посилання…", - "menu_action_open_new_window": "Відкрити в новому вікні", - "menu_action_open_private_window": "Відкрити в приватному вікні", - "menu_action_dismiss": "Сховати", - "menu_action_delete": "Видалити з історії", - "search_for_something_with": "Шукати {search_term} з:", - "search_button": "Пошук", - "search_header": "Шукати з {search_engine_name}", - "search_web_placeholder": "Пошук в Інтернеті", - "search_settings": "Змінити налаштування пошуку", - "welcome_title": "Вітаємо на новій вкладці", - "welcome_body": "Firefox буде використовувати її для показу найважливіших закладок, статей, відео, а також нещодавно відвіданих сторінок, щоб ви могли з легкістю повернутися до них.", - "welcome_label": "Визначення обраного", - "time_label_less_than_minute": "<1 хв", - "time_label_minute": "{number} хв", - "time_label_hour": "{number} г", - "time_label_day": "{number} д", - "settings_pane_button_label": "Налаштуйте свою сторінку нової вкладки", - "settings_pane_header": "Налаштування нової вкладки", - "settings_pane_body": "Оберіть, що показувати при відкритті нової вкладки.", - "settings_pane_search_header": "Пошук", - "settings_pane_search_body": "Пошук в Інтернеті з нової вкладки.", - "settings_pane_topsites_header": "Популярні сайти", - "settings_pane_topsites_body": "Доступ до найчастіше відвідуваних веб-сайтів.", - "settings_pane_topsites_options_showmore": "Показувати два рядки", - "settings_pane_highlights_header": "Обране", - "settings_pane_highlights_body": "Огляд нещодавньої історії перегляду та нових закладок.", - "settings_pane_done_button": "Готово", - "edit_topsites_button_text": "Змінити", - "edit_topsites_button_label": "Налаштувати розділ популярних сайтів", - "edit_topsites_showmore_button": "Показати більше", - "edit_topsites_showless_button": "Показати менше", - "edit_topsites_done_button": "Готово", - "edit_topsites_pin_button": "Закріпити цей сайт", - "edit_topsites_edit_button": "Змінити цей сайт", - "edit_topsites_dismiss_button": "Відхилити цей сайт" - }, - "ur": { - "newtab_page_title": "نیا ٹیب", - "default_label_loading": "لوڈ کر رہا ہے…", - "header_top_sites": "بہترین سائٹیں", - "header_highlights": "شہ سرخياں", - "type_label_visited": "دورہ شدہ", - "type_label_bookmarked": "نشان شدہ", - "type_label_synced": "کسی دوسرے آلے سے ہمہ وقت ساز کیا گیا ہے", - "type_label_open": "کھولیں", - "type_label_topic": "عنوان", - "menu_action_bookmark": "نشانی", - "menu_action_remove_bookmark": "نشانى ہٹائيں", - "menu_action_copy_address": "پتہ نقل کریں", - "menu_action_email_link": "ربط ای میل کریں…", - "menu_action_open_new_window": "نئے دریچے میں کھولیں", - "menu_action_open_private_window": "نئی نجی دریچے میں کھولیں", - "menu_action_dismiss": "برخاست کریں", - "menu_action_delete": "تاریخ سے حذف کریں", - "search_for_something_with": "ساتھ {search_term} کے لئے تلاش کریں:", - "search_button": "تلاش", - "search_header": "{search_engine_name} پر تلاش کریں", - "search_web_placeholder": "ويب پر تلاش کريں", - "search_settings": "تلاش کی سیٹکگیں تبدیل کریں", - "welcome_title": "نئے ٹیب میں خوش آمدید", - "welcome_body": "اس جگہ کا استعمال کرنے ہوئے Firefox آپکی متعلقہ نشانیاں، عبارات، وڈیوز اور صفحات جن کا حال ہی میں ص آُپ نے دورہ کیا ہے دکھائے گا۔ تاکہ آپ ان تک واپس آسانی سے پہنچ سکیں۔", - "welcome_label": "آپکی جھلکیوں کی نشاندہی کر رہا ہے", - "time_label_less_than_minute": "<1m", - "time_label_minute": "{number}m", - "time_label_hour": "{number}h", - "time_label_day": "{number}d", - "settings_pane_button_label": "اپنے نئے ٹیب کہ صفحہ کی تخصیص کریں", - "settings_pane_search_header": "تلاش", - "settings_pane_search_body": "اپنے نئے ٹیب سے وہب پر تلاش کریں۔", - "settings_pane_topsites_header": "بہترین سائٹیں", - "settings_pane_topsites_options_showmore": "دو قطاریں دکھائیں", - "settings_pane_highlights_header": "شہ سرخياں", - "settings_pane_done_button": "ہوگیا", - "edit_topsites_button_text": "تدوین", - "edit_topsites_done_button": "ہوگیا", - "edit_topsites_edit_button": "اس سائٹ کی تدوین کریں", - "edit_topsites_dismiss_button": "اس سائٹ کو برخاست کریں" - }, - "uz": {}, - "vi": {}, - "wo": {}, - "xh": {}, - "zh-CN": { - "newtab_page_title": "新标签页", - "default_label_loading": "载入中…", - "header_top_sites": "常用网站", - "header_highlights": "集锦", - "type_label_visited": "访问过", - "type_label_bookmarked": "加了书签", - "type_label_synced": "从其他设备同步过来的", - "type_label_open": "打开", - "type_label_topic": "主题", - "menu_action_bookmark": "添加书签", - "menu_action_remove_bookmark": "移除书签", - "menu_action_copy_address": "复制地址", - "menu_action_email_link": "用邮件发送链接…", - "menu_action_open_new_window": "在新窗口中打开", - "menu_action_open_private_window": "在新的隐私浏览窗口中打开", - "menu_action_dismiss": "隐藏", - "menu_action_delete": "从历史记录中删除", - "search_for_something_with": "搜索 {search_term},使用:", - "search_button": "搜索", - "search_header": "{search_engine_name} 搜索", - "search_web_placeholder": "在网络上搜索", - "search_settings": "更改搜索设置", - "welcome_title": "欢迎使用新标签页", - "welcome_body": "Firefox 会在这里显示对您最有用的书签、文章、视频和访问过的页面,便于您回到这些网站。", - "welcome_label": "正在为您准备集锦", - "time_label_less_than_minute": "1 分钟内", - "time_label_minute": "{number} 分钟前", - "time_label_hour": "{number} 小时前", - "time_label_day": "{number} 天前", - "settings_pane_button_label": "定制您的新标签页", - "settings_pane_header": "新标签页选项", - "settings_pane_body": "选择您在打开新标签页时看到什么。", - "settings_pane_search_header": "搜索", - "settings_pane_search_body": "从您的新标签页在网络上搜索。", - "settings_pane_topsites_header": "常用网站", - "settings_pane_topsites_body": "访问您经常造访的网站。", - "settings_pane_topsites_options_showmore": "双行显示", - "settings_pane_highlights_header": "集锦", - "settings_pane_highlights_body": "回顾您的最近浏览和新增书签。", - "settings_pane_done_button": "完成", - "edit_topsites_button_text": "编辑", - "edit_topsites_button_label": "定制您的“常用网站”区域", - "edit_topsites_showmore_button": "显示更多", - "edit_topsites_showless_button": "显示更少", - "edit_topsites_done_button": "完成", - "edit_topsites_pin_button": "固定此网站", - "edit_topsites_edit_button": "编辑此网站", - "edit_topsites_dismiss_button": "隐藏此网站" - }, - "zh-TW": { - "newtab_page_title": "新分頁", - "default_label_loading": "載入中…", - "header_top_sites": "熱門網站", - "header_highlights": "精選網站", - "type_label_visited": "造訪過的網站", - "type_label_bookmarked": "已加入書籤", - "type_label_synced": "從其他裝置同步過來", - "type_label_open": "開啟", - "type_label_topic": "主題", - "menu_action_bookmark": "書籤", - "menu_action_remove_bookmark": "移除書籤", - "menu_action_copy_address": "複製網址", - "menu_action_email_link": "郵寄鏈結…", - "menu_action_open_new_window": "用新視窗開啟", - "menu_action_open_private_window": "用新隱私視窗開啟", - "menu_action_dismiss": "隱藏", - "menu_action_delete": "從瀏覽紀錄刪除", - "search_for_something_with": "搜尋 {search_term} 使用:", - "search_button": "搜尋", - "search_header": "{search_engine_name} 搜尋", - "search_web_placeholder": "搜尋 Web", - "search_settings": "變更搜尋選項", - "welcome_title": "歡迎來到新分頁", - "welcome_body": "Firefox 會使用此空間來顯示與您最相關的書籤、文章、影片以及您最近造訪的頁面,這樣您就可以快速回到這些網站。", - "welcome_label": "找出您的精選網站", - "time_label_less_than_minute": "不到 1 分鐘內", - "time_label_minute": "{number} 分鐘", - "time_label_hour": "{number} 小時", - "time_label_day": "{number} 天", - "settings_pane_button_label": "自訂您的新分頁頁面", - "settings_pane_header": "新分頁偏好設定", - "settings_pane_body": "選擇開啟新分頁時想看到什麼。", - "settings_pane_search_header": "搜尋", - "settings_pane_search_body": "直接在新分頁頁面搜尋網頁。", - "settings_pane_topsites_header": "熱門網站", - "settings_pane_topsites_body": "前往您最常造訪的網站。", - "settings_pane_topsites_options_showmore": "顯示兩行", - "settings_pane_highlights_header": "精選網站", - "settings_pane_highlights_body": "看看您最近的瀏覽紀錄,以及新建立的書籤項目。", - "settings_pane_done_button": "完成", - "edit_topsites_button_text": "編輯", - "edit_topsites_button_label": "自訂您的「熱門網站」區塊", - "edit_topsites_showmore_button": "顯示更多", - "edit_topsites_showless_button": "顯示更少", - "edit_topsites_done_button": "完成", - "edit_topsites_pin_button": "釘選此網站", - "edit_topsites_edit_button": "編輯此網站", - "edit_topsites_dismiss_button": "忽略此網站" - }, - "zu": {} -} \ No newline at end of file diff --git a/browser/extensions/activity-stream/jar.mn b/browser/extensions/activity-stream/jar.mn index 2dea506abf4d..8546dcb750f9 100644 --- a/browser/extensions/activity-stream/jar.mn +++ b/browser/extensions/activity-stream/jar.mn @@ -9,7 +9,6 @@ content/vendor/Redux.jsm (./vendor/Redux.jsm) content/vendor/react.js (./vendor/react.js) content/vendor/react-dom.js (./vendor/react-dom.js) - content/vendor/react-intl.js (./vendor/react-intl.js) content/vendor/redux.js (./vendor/redux.js) content/vendor/react-redux.js (./vendor/react-redux.js) content/data/ (./data/*) diff --git a/browser/extensions/activity-stream/lib/ActivityStream.jsm b/browser/extensions/activity-stream/lib/ActivityStream.jsm index 270d4a57c1b9..18035bdf27b6 100644 --- a/browser/extensions/activity-stream/lib/ActivityStream.jsm +++ b/browser/extensions/activity-stream/lib/ActivityStream.jsm @@ -1,7 +1,8 @@ /* 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/. */ -/* globals LocalizationFeed, NewTabInit, SearchFeed, TelemetryFeed, TopSitesFeed, XPCOMUtils */ +/* globals XPCOMUtils, NewTabInit, TopSitesFeed, SearchFeed */ + "use strict"; const {utils: Cu} = Components; @@ -10,23 +11,12 @@ const {Store} = Cu.import("resource://activity-stream/lib/Store.jsm", {}); const {actionTypes: at} = Cu.import("resource://activity-stream/common/Actions.jsm", {}); // Feeds -XPCOMUtils.defineLazyModuleGetter(this, "LocalizationFeed", - "resource://activity-stream/lib/LocalizationFeed.jsm"); - XPCOMUtils.defineLazyModuleGetter(this, "NewTabInit", "resource://activity-stream/lib/NewTabInit.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "PlacesFeed", - "resource://activity-stream/lib/PlacesFeed.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "SearchFeed", - "resource://activity-stream/lib/SearchFeed.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "TelemetryFeed", - "resource://activity-stream/lib/TelemetryFeed.jsm"); - XPCOMUtils.defineLazyModuleGetter(this, "TopSitesFeed", "resource://activity-stream/lib/TopSitesFeed.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "SearchFeed", + "resource://activity-stream/lib/SearchFeed.jsm"); const feeds = { // When you add a feed here: @@ -36,12 +26,9 @@ const feeds = { // 2. The value should be a function that returns a feed. // 3. You should use XPCOMUtils.defineLazyModuleGetter to import the Feed, // so it isn't loaded until the feed is enabled. - "feeds.localization": () => new LocalizationFeed(), "feeds.newtabinit": () => new NewTabInit(), - "feeds.places": () => new PlacesFeed(), - "feeds.search": () => new SearchFeed(), - "feeds.telemetry": () => new TelemetryFeed(), - "feeds.topsites": () => new TopSitesFeed() + "feeds.topsites": () => new TopSitesFeed(), + "feeds.search": () => new SearchFeed() }; this.ActivityStream = class ActivityStream { @@ -54,7 +41,7 @@ this.ActivityStream = class ActivityStream { * @param {string} options.version Version of the add-on. e.g. "0.1.0" * @param {string} options.newTabURL URL of New Tab page on which A.S. is displayed. e.g. "about:newtab" */ - constructor(options = {}) { + constructor(options) { this.initialized = false; this.options = options; this.store = new Store(); @@ -63,10 +50,7 @@ this.ActivityStream = class ActivityStream { init() { this.initialized = true; this.store.init(this.feeds); - this.store.dispatch({ - type: at.INIT, - data: {version: this.options.version} - }); + this.store.dispatch({type: at.INIT}); } uninit() { this.store.dispatch({type: at.UNINIT}); diff --git a/browser/extensions/activity-stream/lib/ActivityStreamMessageChannel.jsm b/browser/extensions/activity-stream/lib/ActivityStreamMessageChannel.jsm index 49e45c6f0011..c6ce0f7930ca 100644 --- a/browser/extensions/activity-stream/lib/ActivityStreamMessageChannel.jsm +++ b/browser/extensions/activity-stream/lib/ActivityStreamMessageChannel.jsm @@ -89,7 +89,7 @@ this.ActivityStreamMessageChannel = class ActivityStreamMessageChannel { * @param {string} targetId The portID of the port that sent the message */ onActionFromContent(action, targetId) { - this.dispatch(ac.SendToMain(action, targetId)); + this.dispatch(ac.SendToMain(action, {fromTarget: targetId})); } /** @@ -196,7 +196,7 @@ this.ActivityStreamMessageChannel = class ActivityStreamMessageChannel { action._target = msg.target; this.onActionFromContent(action, portID); } -}; +} this.DEFAULT_OPTIONS = DEFAULT_OPTIONS; this.EXPORTED_SYMBOLS = ["ActivityStreamMessageChannel", "DEFAULT_OPTIONS"]; diff --git a/browser/extensions/activity-stream/lib/LocalizationFeed.jsm b/browser/extensions/activity-stream/lib/LocalizationFeed.jsm deleted file mode 100644 index 665c7ac93188..000000000000 --- a/browser/extensions/activity-stream/lib/LocalizationFeed.jsm +++ /dev/null @@ -1,74 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - /* globals Services, XPCOMUtils */ -"use strict"; - -const {utils: Cu} = Components; -const {actionTypes: at, actionCreators: ac} = Cu.import("resource://activity-stream/common/Actions.jsm", {}); - -Cu.importGlobalProperties(["fetch"]); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "Services", - "resource://gre/modules/Services.jsm"); - -// What is our default locale for the app? -const DEFAULT_LOCALE = "en-US"; -// Event from LocaleService when locales are assigned -const LOCALES_CHANGE_TOPIC = "intl:requested-locales-changed"; -// Where is the packaged locales json with all strings? -const LOCALES_FILE = "resource://activity-stream/data/locales.json"; - -this.LocalizationFeed = class LocalizationFeed { - async init() { - Services.obs.addObserver(this, LOCALES_CHANGE_TOPIC); - - let response = await fetch(LOCALES_FILE); - this.allStrings = await response.json(); - - this.updateLocale(); - } - uninit() { - Services.obs.removeObserver(this, LOCALES_CHANGE_TOPIC); - } - - updateLocale() { - let locale = Services.locale.getRequestedLocale() || DEFAULT_LOCALE; - let strings = this.allStrings[locale]; - - // Use the default strings for any that are missing - if (locale !== DEFAULT_LOCALE) { - strings = Object.assign({}, this.allStrings[DEFAULT_LOCALE], strings || {}); - } - - this.store.dispatch(ac.BroadcastToContent({ - type: at.LOCALE_UPDATED, - data: { - locale, - strings - } - })); - } - - observe(subject, topic, data) { - switch (topic) { - case LOCALES_CHANGE_TOPIC: - this.updateLocale(); - break; - } - } - - onAction(action) { - switch (action.type) { - case at.INIT: - this.init(); - break; - case at.UNINIT: - this.uninit(); - break; - } - } -}; - -this.EXPORTED_SYMBOLS = ["LocalizationFeed"]; diff --git a/browser/extensions/activity-stream/lib/PlacesFeed.jsm b/browser/extensions/activity-stream/lib/PlacesFeed.jsm deleted file mode 100644 index 009c74cde115..000000000000 --- a/browser/extensions/activity-stream/lib/PlacesFeed.jsm +++ /dev/null @@ -1,213 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - /* globals ContentSearch, XPCOMUtils, PlacesUtils, NewTabUtils, Services */ -"use strict"; - -const {utils: Cu, interfaces: Ci} = Components; -const {actionTypes: at, actionCreators: ac} = Cu.import("resource://activity-stream/common/Actions.jsm", {}); - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils", - "resource://gre/modules/NewTabUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", - "resource://gre/modules/PlacesUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Services", - "resource://gre/modules/Services.jsm"); - -const LINK_BLOCKED_EVENT = "newtab-linkBlocked"; - -/** - * Observer - a wrapper around history/bookmark observers to add the QueryInterface. - */ -class Observer { - constructor(dispatch, observerInterface) { - this.dispatch = dispatch; - this.QueryInterface = XPCOMUtils.generateQI([observerInterface, Ci.nsISupportsWeakReference]); - } -} - -/** - * HistoryObserver - observes events from PlacesUtils.history - */ -class HistoryObserver extends Observer { - constructor(dispatch) { - super(dispatch, Ci.nsINavHistoryObserver); - } - - /** - * onDeleteURI - Called when an link is deleted from history. - * - * @param {obj} uri A URI object representing the link's url - * {str} uri.spec The URI as a string - */ - onDeleteURI(uri) { - this.dispatch({ - type: at.PLACES_LINK_DELETED, - data: {url: uri.spec} - }); - } - - /** - * onClearHistory - Called when the user clears their entire history. - */ - onClearHistory() { - this.dispatch({type: at.PLACES_HISTORY_CLEARED}); - } -} - -/** - * BookmarksObserver - observes events from PlacesUtils.bookmarks - */ -class BookmarksObserver extends Observer { - constructor(dispatch) { - super(dispatch, Ci.nsINavBookmarkObserver); - } - - /** - * onItemAdded - Called when a bookmark is added - * - * @param {str} id - * @param {str} folderId - * @param {int} index - * @param {int} type Indicates if the bookmark is an actual bookmark, - * a folder, or a separator. - * @param {str} uri - * @param {str} title - * @param {int} dateAdded - * @param {str} guid The unique id of the bookmark - */ - async onItemAdded(...args) { - const type = args[3]; - const guid = args[7]; - if (type !== PlacesUtils.bookmarks.TYPE_BOOKMARK) { - return; - } - try { - // bookmark: {bookmarkGuid, bookmarkTitle, lastModified, url} - const bookmark = await NewTabUtils.activityStreamProvider.getBookmark(guid); - this.dispatch({type: at.PLACES_BOOKMARK_ADDED, data: bookmark}); - } catch (e) { - Cu.reportError(e); - } - } - - /** - * onItemRemoved - Called when a bookmark is removed - * - * @param {str} id - * @param {str} folderId - * @param {int} index - * @param {int} type Indicates if the bookmark is an actual bookmark, - * a folder, or a separator. - * @param {str} uri - * @param {str} guid The unique id of the bookmark - */ - onItemRemoved(id, folderId, index, type, uri, guid) { - if (type === PlacesUtils.bookmarks.TYPE_BOOKMARK) { - this.dispatch({ - type: at.PLACES_BOOKMARK_REMOVED, - data: {url: uri.spec, bookmarkGuid: guid} - }); - } - } - - /** - * onItemChanged - Called when a bookmark is modified - * - * @param {str} id description - * @param {str} property The property that was modified (e.g. uri, title) - * @param {bool} isAnnotation - * @param {any} value - * @param {int} lastModified - * @param {int} type Indicates if the bookmark is an actual bookmark, - * a folder, or a separator. - * @param {int} parent - * @param {str} guid The unique id of the bookmark - */ - async onItemChanged(...args) { - const property = args[1]; - const type = args[5]; - const guid = args[7]; - - // Only process this event if it is a TYPE_BOOKMARK, and uri or title was the property changed. - if (type !== PlacesUtils.bookmarks.TYPE_BOOKMARK || !["uri", "title"].includes(property)) { - return; - } - try { - // bookmark: {bookmarkGuid, bookmarkTitle, lastModified, url} - const bookmark = await NewTabUtils.activityStreamProvider.getBookmark(guid); - this.dispatch({type: at.PLACES_BOOKMARK_CHANGED, data: bookmark}); - } catch (e) { - Cu.reportError(e); - } - } -} - -class PlacesFeed { - constructor() { - this.historyObserver = new HistoryObserver(action => this.store.dispatch(ac.BroadcastToContent(action))); - this.bookmarksObserver = new BookmarksObserver(action => this.store.dispatch(ac.BroadcastToContent(action))); - } - - addObservers() { - PlacesUtils.history.addObserver(this.historyObserver, true); - PlacesUtils.bookmarks.addObserver(this.bookmarksObserver, true); - Services.obs.addObserver(this, LINK_BLOCKED_EVENT); - } - - removeObservers() { - PlacesUtils.history.removeObserver(this.historyObserver); - PlacesUtils.bookmarks.removeObserver(this.bookmarksObserver); - Services.obs.removeObserver(this, LINK_BLOCKED_EVENT); - } - - /** - * observe - An observer for the LINK_BLOCKED_EVENT. - * Called when a link is blocked. - * - * @param {null} subject - * @param {str} topic The name of the event - * @param {str} value The data associated with the event - */ - observe(subject, topic, value) { - if (topic === LINK_BLOCKED_EVENT) { - this.store.dispatch(ac.BroadcastToContent({ - type: at.PLACES_LINK_BLOCKED, - data: {url: value} - })); - } - } - - onAction(action) { - switch (action.type) { - case at.INIT: - this.addObservers(); - break; - case at.UNINIT: - this.removeObservers(); - break; - case at.BLOCK_URL: - NewTabUtils.activityStreamLinks.blockURL({url: action.data}); - break; - case at.BOOKMARK_URL: - NewTabUtils.activityStreamLinks.addBookmark(action.data); - break; - case at.DELETE_BOOKMARK_BY_ID: - NewTabUtils.activityStreamLinks.deleteBookmark(action.data); - break; - case at.DELETE_HISTORY_URL: - NewTabUtils.activityStreamLinks.deleteHistoryEntry(action.data); - break; - } - } -} - -this.PlacesFeed = PlacesFeed; - -// Exported for testing only -PlacesFeed.HistoryObserver = HistoryObserver; -PlacesFeed.BookmarksObserver = BookmarksObserver; - -this.EXPORTED_SYMBOLS = ["PlacesFeed"]; diff --git a/browser/extensions/activity-stream/lib/SearchFeed.jsm b/browser/extensions/activity-stream/lib/SearchFeed.jsm index 6890b0536960..992c07f69384 100644 --- a/browser/extensions/activity-stream/lib/SearchFeed.jsm +++ b/browser/extensions/activity-stream/lib/SearchFeed.jsm @@ -18,19 +18,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "Services", this.SearchFeed = class SearchFeed { addObservers() { Services.obs.addObserver(this, SEARCH_ENGINE_TOPIC); - - // Notice when ContentSearch.init would be lazily loaded from nsBrowserGlue - this.contentSearch = new Promise(resolve => Services.mm.addMessageListener( - "ContentSearch", (this._onMessage = () => { - Services.mm.removeMessageListener("ContentSearch", this._onMessage); - resolve(ContentSearch); - }))); } removeObservers() { Services.obs.removeObserver(this, SEARCH_ENGINE_TOPIC); - Services.mm.removeMessageListener("ContentSearch", this._onMessage); } - observe(subject, topic, data) { switch (topic) { case SEARCH_ENGINE_TOPIC: @@ -40,10 +31,8 @@ this.SearchFeed = class SearchFeed { break; } } - async getState() { - // Wait for ContentSearch to be lazily loaded before getting state - const state = await (await this.contentSearch).currentStateObj(true); + const state = await ContentSearch.currentStateObj(true); const engines = state.engines.map(engine => ({ name: engine.name, icon: engine.iconBuffer @@ -58,12 +47,11 @@ this.SearchFeed = class SearchFeed { performSearch(browser, data) { ContentSearch.performSearch({target: browser}, data); } - - async onAction(action) { + onAction(action) { switch (action.type) { case at.INIT: this.addObservers(); - await this.getState(); + this.getState(); break; case at.PERFORM_SEARCH: this.performSearch(action._target.browser, action.data); diff --git a/browser/extensions/activity-stream/lib/Store.jsm b/browser/extensions/activity-stream/lib/Store.jsm index 9bac91a0207c..7bb534ccc196 100644 --- a/browser/extensions/activity-stream/lib/Store.jsm +++ b/browser/extensions/activity-stream/lib/Store.jsm @@ -31,9 +31,9 @@ this.Store = class Store { // Bind each redux method so we can call it directly from the Store. E.g., // store.dispatch() will call store._store.dispatch(); ["dispatch", "getState", "subscribe"].forEach(method => { - this[method] = function(...args) { + this[method] = (...args) => { return this._store[method](...args); - }.bind(this); + }; }); this.feeds = new Map(); this._feedFactories = null; diff --git a/browser/extensions/activity-stream/lib/TelemetryFeed.jsm b/browser/extensions/activity-stream/lib/TelemetryFeed.jsm deleted file mode 100644 index 3fe2a3ff899e..000000000000 --- a/browser/extensions/activity-stream/lib/TelemetryFeed.jsm +++ /dev/null @@ -1,162 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -/* globals XPCOMUtils, gUUIDGenerator, ClientID */ - -"use strict"; - -const {utils: Cu} = Components; -const {actionTypes: at, actionUtils: au} = Cu.import("resource://activity-stream/common/Actions.jsm", {}); - -Cu.import("resource://gre/modules/ClientID.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator", - "@mozilla.org/uuid-generator;1", - "nsIUUIDGenerator"); -XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySender", - "resource://activity-stream/lib/TelemetrySender.jsm"); - -this.TelemetryFeed = class TelemetryFeed { - constructor(options) { - this.sessions = new Map(); - this.telemetryClientId = null; - this.telemetrySender = null; - } - - async init() { - // TelemetrySender adds pref observers, so we initialize it after INIT - this.telemetrySender = new TelemetrySender(); - - const id = await ClientID.getClientID(); - this.telemetryClientId = id; - } - - /** - * addSession - Start tracking a new session - * - * @param {string} id the portID of the open session - */ - addSession(id) { - this.sessions.set(id, { - start_time: Components.utils.now(), - session_id: String(gUUIDGenerator.generateUUID()), - page: "about:newtab" // TODO: Handle about:home - }); - } - - /** - * endSession - Stop tracking a session - * - * @param {string} portID the portID of the session that just closed - */ - endSession(portID) { - const session = this.sessions.get(portID); - - if (!session) { - // It's possible the tab was never visible – in which case, there was no user session. - return; - } - - session.session_duration = Math.round(Components.utils.now() - session.start_time); - this.sendEvent(this.createSessionEndEvent(session)); - this.sessions.delete(portID); - } - - /** - * createPing - Create a ping with common properties - * - * @param {string} id The portID of the session, if a session is relevant (optional) - * @return {obj} A telemetry ping - */ - createPing(portID) { - const appInfo = this.store.getState().App; - const ping = { - client_id: this.telemetryClientId, - addon_version: appInfo.version, - locale: appInfo.locale - }; - - // If the ping is part of a user session, add session-related info - if (portID) { - const session = this.sessions.get(portID); - Object.assign(ping, { - session_id: session.session_id, - page: session.page - }); - } - return ping; - } - - createUserEvent(action) { - return Object.assign( - this.createPing(au.getPortIdOfSender(action)), - action.data, - {action: "activity_stream_user_event"} - ); - } - - createUndesiredEvent(action) { - return Object.assign( - this.createPing(au.getPortIdOfSender(action)), - {value: 0}, // Default value - action.data, - {action: "activity_stream_undesired_event"} - ); - } - - createPerformanceEvent(action) { - return Object.assign( - this.createPing(au.getPortIdOfSender(action)), - action.data, - {action: "activity_stream_performance_event"} - ); - } - - createSessionEndEvent(session) { - return Object.assign( - this.createPing(), - { - session_id: session.session_id, - page: session.page, - session_duration: session.session_duration, - action: "activity_stream_session" - } - ); - } - - sendEvent(event) { - this.telemetrySender.sendPing(event); - } - - onAction(action) { - switch (action.type) { - case at.INIT: - this.init(); - break; - case at.NEW_TAB_VISIBLE: - this.addSession(au.getPortIdOfSender(action)); - break; - case at.NEW_TAB_UNLOAD: - this.endSession(au.getPortIdOfSender(action)); - break; - case at.TELEMETRY_UNDESIRED_EVENT: - this.sendEvent(this.createUndesiredEvent(action)); - break; - case at.TELEMETRY_USER_EVENT: - this.sendEvent(this.createUserEvent(action)); - break; - case at.TELEMETRY_PERFORMANCE_EVENT: - this.sendEvent(this.createPerformanceEvent(action)); - break; - } - } - - uninit() { - this.telemetrySender.uninit(); - this.telemetrySender = null; - // TODO: Send any unfinished sessions - } -}; - -this.EXPORTED_SYMBOLS = ["TelemetryFeed"]; diff --git a/browser/extensions/activity-stream/lib/TelemetrySender.jsm b/browser/extensions/activity-stream/lib/TelemetrySender.jsm deleted file mode 100644 index 9864b3b8413c..000000000000 --- a/browser/extensions/activity-stream/lib/TelemetrySender.jsm +++ /dev/null @@ -1,99 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -/* globals Preferences, Services, XPCOMUtils */ - -const {interfaces: Ci, utils: Cu} = Components; - -Cu.import("resource://gre/modules/Preferences.jsm"); -Cu.importGlobalProperties(["fetch"]); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/Console.jsm"); // eslint-disable-line no-console - -// This is intentionally a different pref-branch than the SDK-based add-on -// used, to avoid extra weirdness for people who happen to have the SDK-based -// installed. Though maybe we should just forcibly disable the old add-on? -const PREF_BRANCH = "browser.newtabpage.activity-stream."; - -const ENDPOINT_PREF = "telemetry.ping.endpoint"; -const TELEMETRY_PREF = "telemetry"; -const LOGGING_PREF = "telemetry.log"; - -/** - * Observe various notifications and send them to a telemetry endpoint. - * - * @param {Object} args - optional arguments - * @param {Function} args.prefInitHook - if present, will be called back - * inside the Prefs constructor. Typically used from tests - * to save off a pointer to a fake Prefs instance so that - * stubs and spies can be inspected by the test code. - * - */ -function TelemetrySender(args) { - let prefArgs = {branch: PREF_BRANCH}; - if (args) { - if ("prefInitHook" in args) { - prefArgs.initHook = args.prefInitHook; - } - } - - this._prefs = new Preferences(prefArgs); - - this.enabled = this._prefs.get(TELEMETRY_PREF); - this._onTelemetryPrefChange = this._onTelemetryPrefChange.bind(this); - this._prefs.observe(TELEMETRY_PREF, this._onTelemetryPrefChange); - - this.logging = this._prefs.get(LOGGING_PREF); - this._onLoggingPrefChange = this._onLoggingPrefChange.bind(this); - this._prefs.observe(LOGGING_PREF, this._onLoggingPrefChange); - - this._pingEndpoint = this._prefs.get(ENDPOINT_PREF); -} - -TelemetrySender.prototype = { - - _onLoggingPrefChange(prefVal) { - this.logging = prefVal; - }, - - _onTelemetryPrefChange(prefVal) { - this.enabled = prefVal; - }, - - async sendPing(data) { - if (this.logging) { - // performance related pings cause a lot of logging, so we mute them - if (data.action !== "activity_stream_performance") { - console.log(`TELEMETRY PING: ${JSON.stringify(data)}\n`); // eslint-disable-line no-console - } - } - if (!this.enabled) { - return Promise.resolve(); - } - return fetch(this._pingEndpoint, {method: "POST", body: data}).then(response => { - if (!response.ok) { - Cu.reportError(`Ping failure with HTTP response code: ${response.status}`); - } - }).catch(e => { - Cu.reportError(`Ping failure with error: ${e}`); - }); - }, - - uninit() { - try { - this._prefs.ignore(TELEMETRY_PREF, this._onTelemetryPrefChange); - this._prefs.ignore(LOGGING_PREF, this._onLoggingPrefChange); - } catch (e) { - Cu.reportError(e); - } - } -}; - -this.TelemetrySender = TelemetrySender; -this.TelemetrySenderConstants = { - ENDPOINT_PREF, - TELEMETRY_PREF, - LOGGING_PREF -}; -this.EXPORTED_SYMBOLS = ["TelemetrySender", "TelemetrySenderConstants"]; diff --git a/browser/extensions/activity-stream/lib/TopSitesFeed.jsm b/browser/extensions/activity-stream/lib/TopSitesFeed.jsm index 7382fe84fca7..88888564f7bb 100644 --- a/browser/extensions/activity-stream/lib/TopSitesFeed.jsm +++ b/browser/extensions/activity-stream/lib/TopSitesFeed.jsm @@ -58,10 +58,6 @@ this.TopSitesFeed = class TopSitesFeed { this.getScreenshot(link.url); } } - openNewWindow(action, isPrivate = false) { - const win = action._target.browser.ownerGlobal; - win.openLinkIn(action.data.url, "window", {private: isPrivate}); - } onAction(action) { let realRows; switch (action.type) { @@ -77,13 +73,6 @@ this.TopSitesFeed = class TopSitesFeed { this.refresh(action); } break; - case at.OPEN_NEW_WINDOW: - this.openNewWindow(action); - break; - case at.OPEN_PRIVATE_WINDOW: { - this.openNewWindow(action, true); - break; - } } } }; diff --git a/browser/extensions/activity-stream/test/schemas/pings.js b/browser/extensions/activity-stream/test/schemas/pings.js deleted file mode 100644 index 429c83cbdbee..000000000000 --- a/browser/extensions/activity-stream/test/schemas/pings.js +++ /dev/null @@ -1,57 +0,0 @@ -const Joi = require("joi-browser"); - -const baseKeys = { - client_id: Joi.string().required(), - addon_version: Joi.string().required(), - locale: Joi.string().required(), - session_id: Joi.string(), - page: Joi.valid(["about:home", "about:newtab"]) -}; - -const BasePing = Joi.object().keys(baseKeys).options({allowUnknown: true}); - -const UserEventPing = Joi.object().keys(Object.assign({}, baseKeys, { - session_id: baseKeys.session_id.required(), - page: baseKeys.page.required(), - source: Joi.string().required(), - event: Joi.string().required(), - action: Joi.valid("activity_stream_user_event").required(), - metadata_source: Joi.string(), - highlight_type: Joi.valid(["bookmarks", "recommendation", "history"]), - recommender_type: Joi.string() -})); - -const UndesiredPing = Joi.object().keys(Object.assign({}, baseKeys, { - source: Joi.string().required(), - event: Joi.string().required(), - action: Joi.valid("activity_stream_undesired_event").required(), - value: Joi.number().required() -})); - -const PerfPing = Joi.object().keys(Object.assign({}, baseKeys, { - source: Joi.string(), - event: Joi.string().required(), - action: Joi.valid("activity_stream_performance_event").required(), - value: Joi.number().required() -})); - -const SessionPing = Joi.object().keys(Object.assign({}, baseKeys, { - session_id: baseKeys.session_id.required(), - page: baseKeys.page.required(), - session_duration: Joi.number().integer().required(), - action: Joi.valid("activity_stream_session").required() -})); - -function assertMatchesSchema(ping, schema) { - assert.isNull(Joi.validate(ping, schema).error); -} - -module.exports = { - baseKeys, - BasePing, - UndesiredPing, - UserEventPing, - PerfPing, - SessionPing, - assertMatchesSchema -}; diff --git a/browser/extensions/activity-stream/test/unit/common/Actions.test.js b/browser/extensions/activity-stream/test/unit/common/Actions.test.js index 49756cd52020..954fb77d1625 100644 --- a/browser/extensions/activity-stream/test/unit/common/Actions.test.js +++ b/browser/extensions/activity-stream/test/unit/common/Actions.test.js @@ -1,26 +1,10 @@ const { - actionTypes: at, actionCreators: ac, actionUtils: au, MAIN_MESSAGE_TYPE, - CONTENT_MESSAGE_TYPE, - UI_CODE, - BACKGROUND_PROCESS, - globalImportContext + CONTENT_MESSAGE_TYPE } = require("common/Actions.jsm"); -describe("Actions", () => { - it("should set globalImportContext to UI_CODE", () => { - assert.equal(globalImportContext, UI_CODE); - }); -}); - -describe("ActionTypes", () => { - it("should be in alpha order", () => { - assert.equal(Object.keys(at).join(", "), Object.keys(at).sort().join(", ")); - }); -}); - describe("ActionCreators", () => { describe("_RouteMessage", () => { it("should throw if options are not passed as the second param", () => { @@ -49,11 +33,6 @@ describe("ActionCreators", () => { meta: {from: CONTENT_MESSAGE_TYPE, to: MAIN_MESSAGE_TYPE} }); }); - it("should add the fromTarget if it was supplied", () => { - const action = {type: "FOO", data: "BAR"}; - const newAction = ac.SendToMain(action, "port123"); - assert.equal(newAction.meta.fromTarget, "port123"); - }); describe("isSendToMain", () => { it("should return true if action is SendToMain", () => { const newAction = ac.SendToMain({type: "FOO"}); @@ -111,50 +90,4 @@ describe("ActionCreators", () => { }); }); }); - describe("UserEvent", () => { - it("should include the given data", () => { - const data = {action: "foo"}; - assert.equal(ac.UserEvent(data).data, data); - }); - it("should wrap with SendToMain", () => { - const action = ac.UserEvent({action: "foo"}); - assert.isTrue(au.isSendToMain(action), "isSendToMain"); - }); - }); - describe("UndesiredEvent", () => { - it("should include the given data", () => { - const data = {action: "foo"}; - assert.equal(ac.UndesiredEvent(data).data, data); - }); - it("should wrap with SendToMain if in UI code", () => { - assert.isTrue(au.isSendToMain(ac.UndesiredEvent({action: "foo"})), "isSendToMain"); - }); - it("should not wrap with SendToMain if in UI code", () => { - const action = ac.UndesiredEvent({action: "foo"}, BACKGROUND_PROCESS); - assert.isFalse(au.isSendToMain(action), "isSendToMain"); - }); - }); - describe("PerfEvent", () => { - it("should include the right data", () => { - const data = {action: "foo"}; - assert.equal(ac.UndesiredEvent(data).data, data); - }); - it("should wrap with SendToMain if in UI code", () => { - assert.isTrue(au.isSendToMain(ac.PerfEvent({action: "foo"})), "isSendToMain"); - }); - it("should not wrap with SendToMain if in UI code", () => { - const action = ac.PerfEvent({action: "foo"}, BACKGROUND_PROCESS); - assert.isFalse(au.isSendToMain(action), "isSendToMain"); - }); - }); -}); - -describe("ActionUtils", () => { - describe("getPortIdOfSender", () => { - it("should return the PortID from a SendToMain action", () => { - const portID = "foo123"; - const result = au.getPortIdOfSender(ac.SendToMain({type: "FOO"}, portID)); - assert.equal(result, portID); - }); - }); }); diff --git a/browser/extensions/activity-stream/test/unit/common/Reducers.test.js b/browser/extensions/activity-stream/test/unit/common/Reducers.test.js index 22978712f8d7..29e995a4e8ce 100644 --- a/browser/extensions/activity-stream/test/unit/common/Reducers.test.js +++ b/browser/extensions/activity-stream/test/unit/common/Reducers.test.js @@ -1,34 +1,8 @@ const {reducers, INITIAL_STATE} = require("common/Reducers.jsm"); -const {TopSites, Search, App} = reducers; +const {TopSites, Search} = reducers; const {actionTypes: at} = require("common/Actions.jsm"); describe("Reducers", () => { - describe("App", () => { - it("should return the initial state", () => { - const nextState = App(undefined, {type: "FOO"}); - assert.equal(nextState, INITIAL_STATE.App); - }); - it("should not set initialized to true on INIT", () => { - const nextState = App(undefined, {type: "INIT"}); - assert.propertyVal(nextState, "initialized", true); - }); - it("should set initialized, version, and locale on INIT", () => { - const action = {type: "INIT", data: {version: "1.2.3"}}; - const nextState = App(undefined, action); - assert.propertyVal(nextState, "version", "1.2.3"); - }); - it("should not update state for empty action.data on LOCALE_UPDATED", () => { - const nextState = App(undefined, {type: at.LOCALE_UPDATED}); - assert.equal(nextState, INITIAL_STATE.App); - }); - it("should set locale, strings on LOCALE_UPDATE", () => { - const strings = {}; - const action = {type: "LOCALE_UPDATED", data: {locale: "zh-CN", strings}}; - const nextState = App(undefined, action); - assert.propertyVal(nextState, "locale", "zh-CN"); - assert.propertyVal(nextState, "strings", strings); - }); - }); describe("TopSites", () => { it("should return the initial state", () => { const nextState = TopSites(undefined, {type: "FOO"}); @@ -55,58 +29,6 @@ describe("Reducers", () => { const nextState = TopSites(oldState, action); assert.deepEqual(nextState, oldState); }); - it("should bookmark an item on PLACES_BOOKMARK_ADDED", () => { - const oldState = {rows: [{url: "foo.com"}, {url: "bar.com"}]}; - const action = { - type: at.PLACES_BOOKMARK_ADDED, - data: { - url: "bar.com", - bookmarkGuid: "bookmark123", - bookmarkTitle: "Title for bar.com", - lastModified: 1234567 - } - }; - const nextState = TopSites(oldState, action); - const newRow = nextState.rows[1]; - // new row has bookmark data - assert.equal(newRow.url, action.data.url); - assert.equal(newRow.bookmarkGuid, action.data.bookmarkGuid); - assert.equal(newRow.bookmarkTitle, action.data.bookmarkTitle); - assert.equal(newRow.bookmarkDateCreated, action.data.lastModified); - - // old row is unchanged - assert.equal(nextState.rows[0], oldState.rows[0]); - }); - it("should remove a bookmark on PLACES_BOOKMARK_REMOVED", () => { - const oldState = { - rows: [{url: "foo.com"}, { - url: "bar.com", - bookmarkGuid: "bookmark123", - bookmarkTitle: "Title for bar.com", - lastModified: 123456 - }] - }; - const action = {type: at.PLACES_BOOKMARK_REMOVED, data: {url: "bar.com"}}; - const nextState = TopSites(oldState, action); - const newRow = nextState.rows[1]; - // new row no longer has bookmark data - assert.equal(newRow.url, oldState.rows[1].url); - assert.isUndefined(newRow.bookmarkGuid); - assert.isUndefined(newRow.bookmarkTitle); - assert.isUndefined(newRow.bookmarkDateCreated); - - // old row is unchanged - assert.deepEqual(nextState.rows[0], oldState.rows[0]); - }); - it("should remove a link on PLACES_LINK_BLOCKED and PLACES_LINK_DELETED", () => { - const events = [at.PLACES_LINK_BLOCKED, at.PLACES_LINK_DELETED]; - events.forEach(event => { - const oldState = {rows: [{url: "foo.com"}, {url: "bar.com"}]}; - const action = {type: event, data: {url: "bar.com"}}; - const nextState = TopSites(oldState, action); - assert.deepEqual(nextState.rows, [{url: "foo.com"}]); - }); - }); }); describe("Search", () => { it("should return the initial state", () => { diff --git a/browser/extensions/activity-stream/test/unit/content-src/components/ContextMenu.test.jsx b/browser/extensions/activity-stream/test/unit/content-src/components/ContextMenu.test.jsx deleted file mode 100644 index aa6864ba2692..000000000000 --- a/browser/extensions/activity-stream/test/unit/content-src/components/ContextMenu.test.jsx +++ /dev/null @@ -1,51 +0,0 @@ -const React = require("react"); -const {shallow, mount} = require("enzyme"); -const ContextMenu = require("content-src/components/ContextMenu/ContextMenu"); -const DEFAULT_PROPS = { - onUpdate: () => {}, - visible: false, - options: [], - tabbableOptionsLength: 0 -}; - -describe("", () => { - it("shoud be hidden by default", () => { - const wrapper = shallow(); - assert.isTrue(wrapper.find(".context-menu").props().hidden); - }); - it("should be visible if props.visible is true", () => { - const wrapper = shallow(); - assert.isFalse(wrapper.find(".context-menu").props().hidden); - }); - it("should render all the options provided", () => { - const options = [{label: "item1"}, {type: "separator"}, {label: "item2"}]; - const wrapper = shallow(); - assert.lengthOf(wrapper.find(".context-menu-list").children(), 3); - }); - it("should not add a link for a separator", () => { - const options = [{label: "item1"}, {type: "separator"}]; - const wrapper = shallow(); - assert.lengthOf(wrapper.find(".separator"), 1); - }); - it("should add a link for all types that are not separators", () => { - const options = [{label: "item1"}, {type: "separator"}]; - const wrapper = shallow(); - assert.lengthOf(wrapper.find(".context-menu-item"), 1); - }); - it("should add an icon to items that need icons", () => { - const options = [{label: "item1", icon: "icon1"}, {type: "separator"}]; - const wrapper = shallow(); - assert.lengthOf(wrapper.find(".icon-icon1"), 1); - }); - it("should be tabbable", () => { - const options = [{label: "item1", icon: "icon1"}, {type: "separator"}]; - const wrapper = shallow(); - assert.equal(wrapper.find(".context-menu-item").props().role, "menuitem"); - }); - it("should call onUpdate with false when an option is clicked", () => { - const wrapper = mount(); - wrapper.find(".context-menu-item").simulate("click"); - const onUpdate = wrapper.prop("onUpdate"); - assert.calledOnce(onUpdate); - }); -}); diff --git a/browser/extensions/activity-stream/test/unit/content-src/components/LinkMenu.test.jsx b/browser/extensions/activity-stream/test/unit/content-src/components/LinkMenu.test.jsx deleted file mode 100644 index 9b38f2f0669b..000000000000 --- a/browser/extensions/activity-stream/test/unit/content-src/components/LinkMenu.test.jsx +++ /dev/null @@ -1,37 +0,0 @@ -const React = require("react"); -const {shallowWithIntl} = require("test/unit/utils"); -const {_unconnected: LinkMenu} = require("content-src/components/LinkMenu/LinkMenu"); -const ContextMenu = require("content-src/components/ContextMenu/ContextMenu"); - -describe("", () => { - let wrapper; - beforeEach(() => { - wrapper = shallowWithIntl( {}} />); - }); - it("should render a ContextMenu element", () => { - assert.ok(wrapper.find(ContextMenu)); - }); - it("should pass visible, onUpdate, and options to ContextMenu", () => { - assert.ok(wrapper.find(ContextMenu)); - const contextMenuProps = wrapper.find(ContextMenu).props(); - ["visible", "onUpdate", "options"].forEach(prop => assert.property(contextMenuProps, prop)); - }); - it("should give ContextMenu the correct tabbable options length for a11y", () => { - const options = wrapper.find(ContextMenu).props().options; - const firstItem = options[0]; - const lastItem = options[options.length - 1]; - const middleItem = options[Math.ceil(options.length / 2)]; - - // first item should have {first: true} - assert.isTrue(firstItem.first); - assert.ok(!firstItem.last); - - // last item should have {last: true} - assert.isTrue(lastItem.last); - assert.ok(!lastItem.first); - - // middle items should have neither - assert.ok(!middleItem.first); - assert.ok(!middleItem.last); - }); -}); diff --git a/browser/extensions/activity-stream/test/unit/content-src/components/Search.test.jsx b/browser/extensions/activity-stream/test/unit/content-src/components/Search.test.jsx deleted file mode 100644 index aafc87efce35..000000000000 --- a/browser/extensions/activity-stream/test/unit/content-src/components/Search.test.jsx +++ /dev/null @@ -1,63 +0,0 @@ -const React = require("react"); -const {mountWithIntl, shallowWithIntl} = require("test/unit/utils"); -const {_unconnected: Search} = require("content-src/components/Search/Search"); -const {actionTypes: at, actionUtils: au} = require("common/Actions.jsm"); - -const DEFAULT_PROPS = { - Search: { - currentEngine: { - name: "google", - icon: "google.jpg" - } - }, - dispatch() {} -}; - -describe("", () => { - it("should render a Search element", () => { - const wrapper = shallowWithIntl(); - assert.ok(wrapper.exists()); - }); - - describe("#performSearch", () => { - function clickButtonAndGetAction(wrapper) { - const dispatch = wrapper.prop("dispatch"); - wrapper.find(".search-button").simulate("click"); - assert.calledOnce(dispatch); - return dispatch.firstCall.args[0]; - } - it("should send a SendToMain action with type PERFORM_SEARCH when you click the search button", () => { - const wrapper = mountWithIntl(); - - const action = clickButtonAndGetAction(wrapper); - - assert.propertyVal(action, "type", at.PERFORM_SEARCH); - assert.isTrue(au.isSendToMain(action)); - }); - it("should send an action with the right engineName ", () => { - const props = {Search: {currentEngine: {name: "foo123"}}, dispatch: sinon.spy()}; - const wrapper = mountWithIntl(); - - const action = clickButtonAndGetAction(wrapper); - - assert.propertyVal(action.data, "engineName", "foo123"); - }); - it("should send an action with the right searchString ", () => { - const wrapper = mountWithIntl(); - wrapper.setState({searchString: "hello123"}); - - const action = clickButtonAndGetAction(wrapper); - - assert.propertyVal(action.data, "searchString", "hello123"); - }); - }); - - it("should update state.searchString on a change event", () => { - const wrapper = mountWithIntl(); - const inputEl = wrapper.find("#search-input"); - // as the value in the input field changes, it will update the search string - inputEl.simulate("change", {target: {value: "hello"}}); - - assert.equal(wrapper.state().searchString, "hello"); - }); -}); diff --git a/browser/extensions/activity-stream/test/unit/content-src/components/TopSites.test.jsx b/browser/extensions/activity-stream/test/unit/content-src/components/TopSites.test.jsx deleted file mode 100644 index ee366a4f5641..000000000000 --- a/browser/extensions/activity-stream/test/unit/content-src/components/TopSites.test.jsx +++ /dev/null @@ -1,97 +0,0 @@ -const React = require("react"); -const {shallow} = require("enzyme"); -const {_unconnected: TopSites, TopSite} = require("content-src/components/TopSites/TopSites"); -const LinkMenu = require("content-src/components/LinkMenu/LinkMenu"); - -const DEFAULT_PROPS = { - TopSites: {rows: []}, - dispatch() {} -}; - -describe("", () => { - it("should render a TopSites element", () => { - const wrapper = shallow(); - assert.ok(wrapper.exists()); - }); - it("should render a TopSite for each link with the right url", () => { - const rows = [{url: "https://foo.com"}, {url: "https://bar.com"}]; - - const wrapper = shallow(); - - const links = wrapper.find(TopSite); - assert.lengthOf(links, 2); - links.forEach((link, i) => assert.equal(link.props().link.url, rows[i].url)); - }); -}); - -describe("", () => { - let link; - beforeEach(() => { - link = {url: "https://foo.com", screenshot: "foo.jpg"}; - }); - - it("should render a TopSite", () => { - const wrapper = shallow(); - assert.ok(wrapper.exists()); - }); - it("should add the right url", () => { - link.url = "https://www.foobar.org"; - const wrapper = shallow(); - assert.propertyVal(wrapper.find("a").props(), "href", "https://www.foobar.org"); - }); - it("should render a shortened title based off the url", () => { - link.url = "https://www.foobar.org"; - link.eTLD = "org"; - const wrapper = shallow(); - const titleEl = wrapper.find(".title"); - - assert.equal(titleEl.text(), "foobar"); - }); - it("should render the first letter of the title as a fallback for missing screenshots", () => { - const wrapper = shallow(); - assert.equal(wrapper.find(".letter-fallback").text(), "f"); - }); - it("should render a screenshot with the .active class, if it is provided", () => { - const wrapper = shallow(); - const screenshotEl = wrapper.find(".screenshot"); - - assert.propertyVal(screenshotEl.props().style, "backgroundImage", "url(foo.jpg)"); - assert.isTrue(screenshotEl.hasClass("active")); - }); - it("should not add the .active class to the screenshot element if no screenshot prop is provided", () => { - link.screenshot = null; - const wrapper = shallow(); - assert.isFalse(wrapper.find(".screenshot").hasClass("active")); - }); - it("should have .active class, on top-site-outer if context menu is open", () => { - const wrapper = shallow(); - wrapper.setState({showContextMenu: true, activeTile: 1}); - const topSiteEl = wrapper.find(".top-site-outer"); - assert.isTrue(topSiteEl.hasClass("active")); - }); - it("should not add .active class, on top-site-outer if context menu is closed", () => { - const wrapper = shallow(); - wrapper.setState({showContextMenu: false, activeTile: 1}); - const topSiteEl = wrapper.find(".top-site-outer"); - assert.isFalse(topSiteEl.hasClass("active")); - }); - it("should render a context menu button", () => { - const wrapper = shallow(); - assert.ok(wrapper.find(".context-menu-button")); - }); - it("should render a link menu when button is clicked", () => { - const wrapper = shallow(); - let button = wrapper.find(".context-menu-button"); - button.simulate("click", {preventDefault: () => {}}); - assert.isTrue(wrapper.find(LinkMenu).props().visible); - }); - it("should not render a link menu by default", () => { - const wrapper = shallow(); - assert.isFalse(wrapper.find(LinkMenu).props().visible); - }); - it("should pass visible, onUpdate, site, and index to LinkMenu", () => { - const wrapper = shallow(); - const linkMenuProps = wrapper.find(LinkMenu).props(); - ["visible", "onUpdate", "site", "index"].forEach(prop => assert.property(linkMenuProps, prop)); - }); -}); diff --git a/browser/extensions/activity-stream/test/unit/content-src/lib/detect-user-session-start.test.js b/browser/extensions/activity-stream/test/unit/content-src/lib/detect-user-session-start.test.js deleted file mode 100644 index 3766a548a4a6..000000000000 --- a/browser/extensions/activity-stream/test/unit/content-src/lib/detect-user-session-start.test.js +++ /dev/null @@ -1,56 +0,0 @@ -const DetectUserSessionStart = require("content-src/lib/detect-user-session-start"); -const {actionTypes: at} = require("common/Actions.jsm"); - -describe("detectUserSessionStart", () => { - describe("#sendEventOrAddListener", () => { - it("should call ._sendEvent immediately if the document is visible", () => { - const mockDocument = {visibilityState: "visible"}; - const instance = new DetectUserSessionStart({document: mockDocument}); - sinon.stub(instance, "_sendEvent"); - - instance.sendEventOrAddListener(); - - assert.calledOnce(instance._sendEvent); - }); - it("should add an event listener on visibility changes the document is not visible", () => { - const mockDocument = {visibilityState: "hidden", addEventListener: sinon.spy()}; - const instance = new DetectUserSessionStart({document: mockDocument}); - sinon.stub(instance, "_sendEvent"); - - instance.sendEventOrAddListener(); - - assert.notCalled(instance._sendEvent); - assert.calledWith(mockDocument.addEventListener, "visibilitychange", instance._onVisibilityChange); - }); - }); - describe("#_sendEvent", () => { - it("should send an async message with the NEW_TAB_VISIBLE event", () => { - const sendAsyncMessage = sinon.spy(); - const instance = new DetectUserSessionStart({sendAsyncMessage}); - - instance._sendEvent(); - - assert.calledWith(sendAsyncMessage, "ActivityStream:ContentToMain", {type: at.NEW_TAB_VISIBLE}); - }); - }); - describe("_onVisibilityChange", () => { - it("should not send an event if visiblity is not visible", () => { - const instance = new DetectUserSessionStart({document: {visibilityState: "hidden"}}); - sinon.stub(instance, "_sendEvent"); - - instance._onVisibilityChange(); - - assert.notCalled(instance._sendEvent); - }); - it("should send an event and remove the event listener if visibility is visible", () => { - const mockDocument = {visibilityState: "visible", removeEventListener: sinon.spy()}; - const instance = new DetectUserSessionStart({document: mockDocument}); - sinon.stub(instance, "_sendEvent"); - - instance._onVisibilityChange(); - - assert.calledOnce(instance._sendEvent); - assert.calledWith(mockDocument.removeEventListener, "visibilitychange", instance._onVisibilityChange); - }); - }); -}); diff --git a/browser/extensions/activity-stream/test/unit/content-src/lib/short-url.test.js b/browser/extensions/activity-stream/test/unit/content-src/lib/short-url.test.js deleted file mode 100644 index c9f6800cbecd..000000000000 --- a/browser/extensions/activity-stream/test/unit/content-src/lib/short-url.test.js +++ /dev/null @@ -1,28 +0,0 @@ -const shortURL = require("content-src/lib/short-url"); - -describe("shortURL", () => { - it("should return a blank string if url and hostname is falsey", () => { - assert.equal(shortURL({url: ""}), ""); - assert.equal(shortURL({hostname: null}), ""); - }); - - it("should remove the eTLD, if provided", () => { - assert.equal(shortURL({hostname: "com.blah.com", eTLD: "com"}), "com.blah"); - }); - - it("should use the hostname, if provided", () => { - assert.equal(shortURL({hostname: "foo.com", url: "http://bar.com", eTLD: "com"}), "foo"); - }); - - it("should get the hostname from .url if necessary", () => { - assert.equal(shortURL({url: "http://bar.com", eTLD: "com"}), "bar"); - }); - - it("should not strip out www if not first subdomain", () => { - assert.equal(shortURL({hostname: "foo.www.com", eTLD: "com"}), "foo.www"); - }); - - it("should convert to lowercase", () => { - assert.equal(shortURL({url: "HTTP://FOO.COM", eTLD: "com"}), "foo"); - }); -}); diff --git a/browser/extensions/activity-stream/test/unit/lib/ActivityStream.test.js b/browser/extensions/activity-stream/test/unit/lib/ActivityStream.test.js index 157dc8cf83af..7a3bada159e8 100644 --- a/browser/extensions/activity-stream/test/unit/lib/ActivityStream.test.js +++ b/browser/extensions/activity-stream/test/unit/lib/ActivityStream.test.js @@ -4,16 +4,15 @@ describe("ActivityStream", () => { let sandbox; let as; let ActivityStream; - function Fake() {} + function NewTabInit() {} + function TopSitesFeed() {} + function SearchFeed() {} before(() => { sandbox = sinon.sandbox.create(); ({ActivityStream} = injector({ - "lib/LocalizationFeed.jsm": {LocalizationFeed: Fake}, - "lib/NewTabInit.jsm": {NewTabInit: Fake}, - "lib/PlacesFeed.jsm": {PlacesFeed: Fake}, - "lib/SearchFeed.jsm": {SearchFeed: Fake}, - "lib/TelemetryFeed.jsm": {TelemetryFeed: Fake}, - "lib/TopSitesFeed.jsm": {TopSitesFeed: Fake} + "lib/NewTabInit.jsm": {NewTabInit}, + "lib/TopSitesFeed.jsm": {TopSitesFeed}, + "lib/SearchFeed.jsm": {SearchFeed} })); }); @@ -41,17 +40,6 @@ describe("ActivityStream", () => { it("should call .store.init", () => { assert.calledOnce(as.store.init); }); - it("should emit an INIT event with the right version", () => { - as = new ActivityStream({version: "1.2.3"}); - sandbox.stub(as.store, "init"); - sandbox.stub(as.store, "dispatch"); - - as.init(); - - assert.calledOnce(as.store.dispatch); - const action = as.store.dispatch.firstCall.args[0]; - assert.propertyVal(action.data, "version", "1.2.3"); - }); }); describe("#uninit", () => { beforeEach(() => { @@ -66,29 +54,17 @@ describe("ActivityStream", () => { }); }); describe("feeds", () => { - it("should create a Localization feed", () => { - const feed = as.feeds["feeds.localization"](); - assert.instanceOf(feed, Fake); - }); it("should create a NewTabInit feed", () => { const feed = as.feeds["feeds.newtabinit"](); - assert.instanceOf(feed, Fake); - }); - it("should create a Places feed", () => { - const feed = as.feeds["feeds.places"](); - assert.instanceOf(feed, Fake); + assert.instanceOf(feed, NewTabInit); }); it("should create a TopSites feed", () => { const feed = as.feeds["feeds.topsites"](); - assert.instanceOf(feed, Fake); - }); - it("should create a Telemetry feed", () => { - const feed = as.feeds["feeds.telemetry"](); - assert.instanceOf(feed, Fake); + assert.instanceOf(feed, TopSitesFeed); }); it("should create a Search feed", () => { const feed = as.feeds["feeds.search"](); - assert.instanceOf(feed, Fake); + assert.instanceOf(feed, SearchFeed); }); }); }); diff --git a/browser/extensions/activity-stream/test/unit/lib/LocalizationFeed.test.js b/browser/extensions/activity-stream/test/unit/lib/LocalizationFeed.test.js deleted file mode 100644 index 0dc407c39b54..000000000000 --- a/browser/extensions/activity-stream/test/unit/lib/LocalizationFeed.test.js +++ /dev/null @@ -1,128 +0,0 @@ -"use strict"; -const {LocalizationFeed} = require("lib/LocalizationFeed.jsm"); -const {GlobalOverrider} = require("test/unit/utils"); -const {actionTypes: at} = require("common/Actions.jsm"); - -const DEFAULT_LOCALE = "en-US"; -const TEST_STRINGS = { - [DEFAULT_LOCALE]: { - foo: "Foo", - too: "Too" - }, - "it": { - foo: "Bar", - too: "Boo" - }, - "ru": {foo: "Baz"} -}; - -describe("Localization Feed", () => { - let feed; - let globals; - before(() => { - globals = new GlobalOverrider(); - }); - beforeEach(() => { - feed = new LocalizationFeed(); - feed.store = {dispatch: sinon.spy()}; - }); - afterEach(() => { - globals.restore(); - }); - - it("should fetch strings on init", async () => { - sinon.stub(feed, "updateLocale"); - fetch.returns(Promise.resolve({json() { return Promise.resolve(TEST_STRINGS); }})); - - await feed.init(); - - assert.deepEqual(feed.allStrings, TEST_STRINGS); - assert.calledOnce(feed.updateLocale); - }); - - describe("#updateLocale", () => { - beforeEach(() => { - feed.allStrings = TEST_STRINGS; - }); - - it("should dispatch with locale and strings for default", () => { - const locale = DEFAULT_LOCALE; - feed.updateLocale(); - - assert.calledOnce(feed.store.dispatch); - const arg = feed.store.dispatch.firstCall.args[0]; - assert.propertyVal(arg, "type", at.LOCALE_UPDATED); - assert.propertyVal(arg.data, "locale", locale); - assert.deepEqual(arg.data.strings, TEST_STRINGS[locale]); - }); - it("should use strings for other locale", () => { - const locale = "it"; - global.Services.locale.getRequestedLocale.returns(locale); - - feed.updateLocale(); - - assert.calledOnce(feed.store.dispatch); - const arg = feed.store.dispatch.firstCall.args[0]; - assert.propertyVal(arg, "type", at.LOCALE_UPDATED); - assert.propertyVal(arg.data, "locale", locale); - assert.deepEqual(arg.data.strings, TEST_STRINGS[locale]); - }); - it("should use some fallback strings for partial locale", () => { - const locale = "ru"; - global.Services.locale.getRequestedLocale.returns(locale); - - feed.updateLocale(); - - assert.calledOnce(feed.store.dispatch); - const arg = feed.store.dispatch.firstCall.args[0]; - assert.propertyVal(arg, "type", at.LOCALE_UPDATED); - assert.propertyVal(arg.data, "locale", locale); - assert.deepEqual(arg.data.strings, { - foo: TEST_STRINGS[locale].foo, - too: TEST_STRINGS[DEFAULT_LOCALE].too - }); - }); - it("should use all default strings for unknown locale", () => { - const locale = "xyz"; - global.Services.locale.getRequestedLocale.returns(locale); - - feed.updateLocale(); - - assert.calledOnce(feed.store.dispatch); - const arg = feed.store.dispatch.firstCall.args[0]; - assert.propertyVal(arg, "type", at.LOCALE_UPDATED); - assert.propertyVal(arg.data, "locale", locale); - assert.deepEqual(arg.data.strings, TEST_STRINGS[DEFAULT_LOCALE]); - }); - }); - - describe("#observe", () => { - it("should update locale on locale change event", () => { - sinon.stub(feed, "updateLocale"); - - feed.observe(null, "intl:requested-locales-changed"); - - assert.calledOnce(feed.updateLocale); - }); - it("shouldn't update locale on other event", () => { - sinon.stub(feed, "updateLocale"); - - feed.observe(null, "some-other-notification"); - - assert.notCalled(feed.updateLocale); - }); - }); - - describe("#onAction", () => { - it("should addObserver on INIT", () => { - feed.onAction({type: at.INIT}); - - assert.calledOnce(global.Services.obs.addObserver); - }); - it("should removeObserver on UNINIT", () => { - feed.onAction({type: at.UNINIT}); - - assert.calledOnce(global.Services.obs.removeObserver); - }); - }); -}); diff --git a/browser/extensions/activity-stream/test/unit/lib/PlacesFeed.test.js b/browser/extensions/activity-stream/test/unit/lib/PlacesFeed.test.js deleted file mode 100644 index f76aeea1397e..000000000000 --- a/browser/extensions/activity-stream/test/unit/lib/PlacesFeed.test.js +++ /dev/null @@ -1,208 +0,0 @@ -const {PlacesFeed} = require("lib/PlacesFeed.jsm"); -const {HistoryObserver, BookmarksObserver} = PlacesFeed; -const {GlobalOverrider} = require("test/unit/utils"); -const {actionTypes: at} = require("common/Actions.jsm"); - -const FAKE_BOOKMARK = {bookmarkGuid: "xi31", bookmarkTitle: "Foo", lastModified: 123214232, url: "foo.com"}; -const TYPE_BOOKMARK = 0; // This is fake, for testing - -const BLOCKED_EVENT = "newtab-linkBlocked"; // The event dispatched in NewTabUtils when a link is blocked; - -describe("PlacesFeed", () => { - let globals; - let sandbox; - let feed; - beforeEach(() => { - globals = new GlobalOverrider(); - sandbox = globals.sandbox; - globals.set("NewTabUtils", { - activityStreamProvider: {getBookmark() {}}, - activityStreamLinks: { - addBookmark: sandbox.spy(), - deleteBookmark: sandbox.spy(), - deleteHistoryEntry: sandbox.spy(), - blockURL: sandbox.spy() - } - }); - globals.set("PlacesUtils", { - history: {addObserver: sandbox.spy(), removeObserver: sandbox.spy()}, - bookmarks: {TYPE_BOOKMARK, addObserver: sandbox.spy(), removeObserver: sandbox.spy()} - }); - - feed = new PlacesFeed(); - feed.store = {dispatch: sinon.spy()}; - }); - afterEach(() => globals.restore()); - - it("should have a HistoryObserver that dispatches to the store", () => { - assert.instanceOf(feed.historyObserver, HistoryObserver); - const action = {type: "FOO"}; - - feed.historyObserver.dispatch(action); - - assert.calledOnce(feed.store.dispatch); - assert.equal(feed.store.dispatch.firstCall.args[0].type, action.type); - }); - - it("should have a BookmarksObserver that dispatch to the store", () => { - assert.instanceOf(feed.bookmarksObserver, BookmarksObserver); - const action = {type: "FOO"}; - - feed.bookmarksObserver.dispatch(action); - - assert.calledOnce(feed.store.dispatch); - assert.equal(feed.store.dispatch.firstCall.args[0].type, action.type); - }); - - describe("#onAction", () => { - it("should add bookmark, history, blocked observers on INIT", () => { - feed.onAction({type: at.INIT}); - - assert.calledWith(global.PlacesUtils.history.addObserver, feed.historyObserver, true); - assert.calledWith(global.PlacesUtils.bookmarks.addObserver, feed.bookmarksObserver, true); - assert.calledWith(global.Services.obs.addObserver, feed, BLOCKED_EVENT); - }); - it("should remove bookmark, history, blocked observers on UNINIT", () => { - feed.onAction({type: at.UNINIT}); - - assert.calledWith(global.PlacesUtils.history.removeObserver, feed.historyObserver); - assert.calledWith(global.PlacesUtils.bookmarks.removeObserver, feed.bookmarksObserver); - assert.calledWith(global.Services.obs.removeObserver, feed, BLOCKED_EVENT); - }); - it("should block a url on BLOCK_URL", () => { - feed.onAction({type: at.BLOCK_URL, data: "apple.com"}); - assert.calledWith(global.NewTabUtils.activityStreamLinks.blockURL, {url: "apple.com"}); - }); - it("should bookmark a url on BOOKMARK_URL", () => { - feed.onAction({type: at.BOOKMARK_URL, data: "pear.com"}); - assert.calledWith(global.NewTabUtils.activityStreamLinks.addBookmark, "pear.com"); - }); - it("should delete a bookmark on DELETE_BOOKMARK_BY_ID", () => { - feed.onAction({type: at.DELETE_BOOKMARK_BY_ID, data: "g123kd"}); - assert.calledWith(global.NewTabUtils.activityStreamLinks.deleteBookmark, "g123kd"); - }); - it("should delete a history entry on DELETE_HISTORY_URL", () => { - feed.onAction({type: at.DELETE_HISTORY_URL, data: "guava.com"}); - assert.calledWith(global.NewTabUtils.activityStreamLinks.deleteHistoryEntry, "guava.com"); - }); - }); - - describe("#observe", () => { - it("should dispatch a PLACES_LINK_BLOCKED action with the url of the blocked link", () => { - feed.observe(null, BLOCKED_EVENT, "foo123.com"); - assert.equal(feed.store.dispatch.firstCall.args[0].type, at.PLACES_LINK_BLOCKED); - assert.deepEqual(feed.store.dispatch.firstCall.args[0].data, {url: "foo123.com"}); - }); - it("should not call dispatch if the topic is something other than BLOCKED_EVENT", () => { - feed.observe(null, "someotherevent"); - assert.notCalled(feed.store.dispatch); - }); - }); - - describe("HistoryObserver", () => { - let dispatch; - let observer; - beforeEach(() => { - dispatch = sandbox.spy(); - observer = new HistoryObserver(dispatch); - }); - it("should have a QueryInterface property", () => { - assert.property(observer, "QueryInterface"); - }); - describe("#onDeleteURI", () => { - it("should dispatch a PLACES_LINK_DELETED action with the right url", () => { - observer.onDeleteURI({spec: "foo.com"}); - assert.calledWith(dispatch, {type: at.PLACES_LINK_DELETED, data: {url: "foo.com"}}); - }); - }); - describe("#onClearHistory", () => { - it("should dispatch a PLACES_HISTORY_CLEARED action", () => { - observer.onClearHistory(); - assert.calledWith(dispatch, {type: at.PLACES_HISTORY_CLEARED}); - }); - }); - }); - - describe("BookmarksObserver", () => { - let dispatch; - let observer; - beforeEach(() => { - dispatch = sandbox.spy(); - observer = new BookmarksObserver(dispatch); - }); - it("should have a QueryInterface property", () => { - assert.property(observer, "QueryInterface"); - }); - describe("#onItemAdded", () => { - beforeEach(() => { - // Make sure getBookmark returns our fake bookmark if it is called with the expected guid - sandbox.stub(global.NewTabUtils.activityStreamProvider, "getBookmark") - .withArgs(FAKE_BOOKMARK.guid).returns(Promise.resolve(FAKE_BOOKMARK)); - }); - it("should dispatch a PLACES_BOOKMARK_ADDED action with the bookmark data", async () => { - // Yes, onItemAdded has at least 8 arguments. See function definition for docs. - const args = [null, null, null, TYPE_BOOKMARK, null, null, null, FAKE_BOOKMARK.guid]; - await observer.onItemAdded(...args); - - assert.calledWith(dispatch, {type: at.PLACES_BOOKMARK_ADDED, data: FAKE_BOOKMARK}); - }); - it("should catch errors gracefully", async () => { - const e = new Error("test error"); - global.NewTabUtils.activityStreamProvider.getBookmark.restore(); - sandbox.stub(global.NewTabUtils.activityStreamProvider, "getBookmark") - .returns(Promise.reject(e)); - - const args = [null, null, null, TYPE_BOOKMARK, null, null, null, FAKE_BOOKMARK.guid]; - await observer.onItemAdded(...args); - - assert.calledWith(global.Components.utils.reportError, e); - }); - it("should ignore events that are not of TYPE_BOOKMARK", async () => { - const args = [null, null, null, "nottypebookmark"]; - await observer.onItemAdded(...args); - assert.notCalled(dispatch); - }); - }); - describe("#onItemRemoved", () => { - it("should ignore events that are not of TYPE_BOOKMARK", async () => { - await observer.onItemRemoved(null, null, null, "nottypebookmark", null, "123foo"); - assert.notCalled(dispatch); - }); - it("should dispatch a PLACES_BOOKMARK_REMOVED action with the right URL and bookmarkGuid", () => { - observer.onItemRemoved(null, null, null, TYPE_BOOKMARK, {spec: "foo.com"}, "123foo"); - assert.calledWith(dispatch, {type: at.PLACES_BOOKMARK_REMOVED, data: {bookmarkGuid: "123foo", url: "foo.com"}}); - }); - }); - describe("#onItemChanged", () => { - beforeEach(() => { - sandbox.stub(global.NewTabUtils.activityStreamProvider, "getBookmark") - .withArgs(FAKE_BOOKMARK.guid).returns(Promise.resolve(FAKE_BOOKMARK)); - }); - it("should dispatch a PLACES_BOOKMARK_CHANGED action with the bookmark data", async () => { - const args = [null, "title", null, null, null, TYPE_BOOKMARK, null, FAKE_BOOKMARK.guid]; - await observer.onItemChanged(...args); - - assert.calledWith(dispatch, {type: at.PLACES_BOOKMARK_CHANGED, data: FAKE_BOOKMARK}); - }); - it("should catch errors gracefully", async () => { - const e = new Error("test error"); - global.NewTabUtils.activityStreamProvider.getBookmark.restore(); - sandbox.stub(global.NewTabUtils.activityStreamProvider, "getBookmark") - .returns(Promise.reject(e)); - - const args = [null, "title", null, null, null, TYPE_BOOKMARK, null, FAKE_BOOKMARK.guid]; - await observer.onItemChanged(...args); - - assert.calledWith(global.Components.utils.reportError, e); - }); - it("should ignore events that are not of TYPE_BOOKMARK", async () => { - await observer.onItemChanged(null, "title", null, null, null, "nottypebookmark"); - assert.notCalled(dispatch); - }); - it("should ignore events that are not changes to uri/title", async () => { - await observer.onItemChanged(null, "tags", null, null, null, TYPE_BOOKMARK); - assert.notCalled(dispatch); - }); - }); - }); -}); diff --git a/browser/extensions/activity-stream/test/unit/lib/SearchFeed.test.js b/browser/extensions/activity-stream/test/unit/lib/SearchFeed.test.js index 42f286231606..d1e710cbb221 100644 --- a/browser/extensions/activity-stream/test/unit/lib/SearchFeed.test.js +++ b/browser/extensions/activity-stream/test/unit/lib/SearchFeed.test.js @@ -20,9 +20,8 @@ describe("Search Feed", () => { afterEach(() => globals.reset()); after(() => globals.restore()); - it("should call get state (with true) from the content search provider on INIT", async() => { - await feed.onAction({type: at.INIT}); - + it("should call get state (with true) from the content search provider on INIT", () => { + feed.onAction({type: at.INIT}); // calling currentStateObj with 'true' allows us to return a data uri for the // icon, instead of an array buffer assert.calledWith(global.ContentSearch.currentStateObj, true); @@ -42,25 +41,19 @@ describe("Search Feed", () => { feed.onAction({type: at.UNINIT}); assert.calledOnce(feed.removeObservers); }); - it("should add event handlers on INIT", () => { + it("should call services.obs.addObserver on INIT", () => { feed.onAction({type: at.INIT}); - assert.calledOnce(global.Services.obs.addObserver); - assert.calledOnce(global.Services.mm.addMessageListener); }); - it("should remove event handlers on UNINIT", () => { + it("should call services.obs.removeObserver on UNINIT", () => { feed.onAction({type: at.UNINIT}); - assert.calledOnce(global.Services.obs.removeObserver); - assert.calledOnce(global.Services.mm.removeMessageListener); - }); - it("should dispatch one event with the state", async() => { - feed.contentSearch = Promise.resolve(global.ContentSearch); - - await feed.getState(); - - assert.calledOnce(feed.store.dispatch); }); + it("should dispatch one event with the state", () => ( + feed.getState().then(() => { + assert.calledOnce(feed.store.dispatch); + }) + )); it("should perform a search on PERFORM_SEARCH", () => { sinon.stub(feed, "performSearch"); feed.onAction({_target: {browser: {}}, type: at.PERFORM_SEARCH}); diff --git a/browser/extensions/activity-stream/test/unit/lib/TelemetryFeed.test.js b/browser/extensions/activity-stream/test/unit/lib/TelemetryFeed.test.js deleted file mode 100644 index bfa2b267f617..000000000000 --- a/browser/extensions/activity-stream/test/unit/lib/TelemetryFeed.test.js +++ /dev/null @@ -1,262 +0,0 @@ -const injector = require("inject!lib/TelemetryFeed.jsm"); -const {GlobalOverrider} = require("test/unit/utils"); -const {actionCreators: ac, actionTypes: at} = require("common/Actions.jsm"); -const { - BasePing, - UndesiredPing, - UserEventPing, - PerfPing, - SessionPing, - assertMatchesSchema -} = require("test/schemas/pings"); - -const FAKE_TELEMETRY_ID = "foo123"; -const FAKE_UUID = "{foo-123-foo}"; - -describe("TelemetryFeed", () => { - let globals; - let sandbox; - let store = {getState() { return {App: {version: "1.0.0", locale: "en-US"}}; }}; - let instance; - class TelemetrySender {sendPing() {} uninit() {}} - const {TelemetryFeed} = injector({"lib/TelemetrySender.jsm": {TelemetrySender}}); - - function addSession(id) { - instance.addSession(id); - return instance.sessions.get(id); - } - - beforeEach(() => { - globals = new GlobalOverrider(); - sandbox = globals.sandbox; - globals.set("ClientID", {getClientID: sandbox.spy(async () => FAKE_TELEMETRY_ID)}); - globals.set("gUUIDGenerator", {generateUUID: () => FAKE_UUID}); - instance = new TelemetryFeed(); - instance.store = store; - }); - afterEach(() => { - globals.restore(); - }); - describe("#init", () => { - it("should add .telemetrySender, a TelemetrySender instance", async () => { - assert.isNull(instance.telemetrySender); - await instance.init(); - assert.instanceOf(instance.telemetrySender, TelemetrySender); - }); - it("should add .telemetryClientId from the ClientID module", async () => { - assert.isNull(instance.telemetryClientId); - await instance.init(); - assert.equal(instance.telemetryClientId, FAKE_TELEMETRY_ID); - }); - }); - describe("#addSession", () => { - it("should add a session", () => { - addSession("foo"); - assert.isTrue(instance.sessions.has("foo")); - }); - it("should set the start_time", () => { - sandbox.spy(Components.utils, "now"); - const session = addSession("foo"); - assert.calledOnce(Components.utils.now); - assert.equal(session.start_time, Components.utils.now.firstCall.returnValue); - }); - it("should set the session_id", () => { - sandbox.spy(global.gUUIDGenerator, "generateUUID"); - const session = addSession("foo"); - assert.calledOnce(global.gUUIDGenerator.generateUUID); - assert.equal(session.session_id, global.gUUIDGenerator.generateUUID.firstCall.returnValue); - }); - it("should set the page", () => { - const session = addSession("foo"); - assert.equal(session.page, "about:newtab"); // This is hardcoded for now. - }); - }); - describe("#endSession", () => { - it("should not throw if there is no session for the given port ID", () => { - assert.doesNotThrow(() => instance.endSession("doesn't exist")); - }); - it("should add a session_duration", () => { - sandbox.stub(instance, "sendEvent"); - const session = addSession("foo"); - instance.endSession("foo"); - assert.property(session, "session_duration"); - }); - it("should remove the session from .sessions", () => { - sandbox.stub(instance, "sendEvent"); - addSession("foo"); - instance.endSession("foo"); - assert.isFalse(instance.sessions.has("foo")); - }); - it("should call createSessionSendEvent and sendEvent with the sesssion", () => { - sandbox.stub(instance, "sendEvent"); - sandbox.stub(instance, "createSessionEndEvent"); - const session = addSession("foo"); - instance.endSession("foo"); - - // Did we call sendEvent with the result of createSessionEndEvent? - assert.calledWith(instance.createSessionEndEvent, session); - assert.calledWith(instance.sendEvent, instance.createSessionEndEvent.firstCall.returnValue); - }); - }); - describe("ping creators", () => { - beforeEach(async () => await instance.init()); - describe("#createPing", () => { - it("should create a valid base ping without a session if no portID is supplied", () => { - const ping = instance.createPing(); - assertMatchesSchema(ping, BasePing); - assert.notProperty(ping, "session_id"); - }); - it("should create a valid base ping with session info if a portID is supplied", () => { - // Add a session - const portID = "foo"; - instance.addSession(portID); - const sessionID = instance.sessions.get(portID).session_id; - - // Create a ping referencing the session - const ping = instance.createPing(portID); - assertMatchesSchema(ping, BasePing); - - // Make sure we added the right session-related stuff to the ping - assert.propertyVal(ping, "session_id", sessionID); - assert.propertyVal(ping, "page", "about:newtab"); - }); - }); - describe("#createUserEvent", () => { - it("should create a valid event", () => { - const portID = "foo"; - const data = {source: "TOP_SITES", event: "CLICK"}; - const action = ac.SendToMain(ac.UserEvent(data), portID); - const session = addSession(portID); - const ping = instance.createUserEvent(action); - - // Is it valid? - assertMatchesSchema(ping, UserEventPing); - // Does it have the right session_id? - assert.propertyVal(ping, "session_id", session.session_id); - }); - }); - describe("#createUndesiredEvent", () => { - it("should create a valid event without a session", () => { - const action = ac.UndesiredEvent({source: "TOP_SITES", event: "MISSING_IMAGE", value: 10}); - const ping = instance.createUndesiredEvent(action); - - // Is it valid? - assertMatchesSchema(ping, UndesiredPing); - // Does it have the right value? - assert.propertyVal(ping, "value", 10); - }); - it("should create a valid event with a session", () => { - const portID = "foo"; - const data = {source: "TOP_SITES", event: "MISSING_IMAGE", value: 10}; - const action = ac.SendToMain(ac.UndesiredEvent(data), portID); - const session = addSession(portID); - const ping = instance.createUndesiredEvent(action); - - // Is it valid? - assertMatchesSchema(ping, UndesiredPing); - // Does it have the right session_id? - assert.propertyVal(ping, "session_id", session.session_id); - // Does it have the right value? - assert.propertyVal(ping, "value", 10); - }); - }); - describe("#createPerformanceEvent", () => { - it("should create a valid event without a session", () => { - const action = ac.PerfEvent({event: "SCREENSHOT_FINISHED", value: 100}); - const ping = instance.createPerformanceEvent(action); - - // Is it valid? - assertMatchesSchema(ping, PerfPing); - // Does it have the right value? - assert.propertyVal(ping, "value", 100); - }); - it("should create a valid event with a session", () => { - const portID = "foo"; - const data = {event: "PAGE_LOADED", value: 100}; - const action = ac.SendToMain(ac.PerfEvent(data), portID); - const session = addSession(portID); - const ping = instance.createPerformanceEvent(action); - - // Is it valid? - assertMatchesSchema(ping, PerfPing); - // Does it have the right session_id? - assert.propertyVal(ping, "session_id", session.session_id); - // Does it have the right value? - assert.propertyVal(ping, "value", 100); - }); - }); - describe("#createSessionEndEvent", () => { - it("should create a valid event", () => { - const ping = instance.createSessionEndEvent({ - session_id: FAKE_UUID, - page: "about:newtab", - session_duration: 12345 - }); - // Is it valid? - assertMatchesSchema(ping, SessionPing); - assert.propertyVal(ping, "session_id", FAKE_UUID); - assert.propertyVal(ping, "page", "about:newtab"); - assert.propertyVal(ping, "session_duration", 12345); - }); - }); - }); - describe("#sendEvent", () => { - it("should call telemetrySender", async () => { - await instance.init(); - sandbox.stub(instance.telemetrySender, "sendPing"); - const event = {}; - instance.sendEvent(event); - assert.calledWith(instance.telemetrySender.sendPing, event); - }); - }); - describe("#uninit", () => { - it("should call .telemetrySender.uninit and remove it", async () => { - await instance.init(); - const stub = sandbox.stub(instance.telemetrySender, "uninit"); - instance.uninit(); - assert.calledOnce(stub); - assert.isNull(instance.telemetrySender); - }); - }); - describe("#onAction", () => { - it("should call .init() on an INIT action", () => { - const stub = sandbox.stub(instance, "init"); - instance.onAction({type: at.INIT}); - assert.calledOnce(stub); - }); - it("should call .addSession() on a NEW_TAB_VISIBLE action", () => { - const stub = sandbox.stub(instance, "addSession"); - instance.onAction(ac.SendToMain({type: at.NEW_TAB_VISIBLE}, "port123")); - assert.calledWith(stub, "port123"); - }); - it("should call .endSession() on a NEW_TAB_UNLOAD action", () => { - const stub = sandbox.stub(instance, "endSession"); - instance.onAction(ac.SendToMain({type: at.NEW_TAB_UNLOAD}, "port123")); - assert.calledWith(stub, "port123"); - }); - it("should send an event on an TELEMETRY_UNDESIRED_EVENT action", () => { - const sendEvent = sandbox.stub(instance, "sendEvent"); - const eventCreator = sandbox.stub(instance, "createUndesiredEvent"); - const action = {type: at.TELEMETRY_UNDESIRED_EVENT}; - instance.onAction(action); - assert.calledWith(eventCreator, action); - assert.calledWith(sendEvent, eventCreator.returnValue); - }); - it("should send an event on an TELEMETRY_USER_EVENT action", () => { - const sendEvent = sandbox.stub(instance, "sendEvent"); - const eventCreator = sandbox.stub(instance, "createUserEvent"); - const action = {type: at.TELEMETRY_USER_EVENT}; - instance.onAction(action); - assert.calledWith(eventCreator, action); - assert.calledWith(sendEvent, eventCreator.returnValue); - }); - it("should send an event on an TELEMETRY_PERFORMANCE_EVENT action", () => { - const sendEvent = sandbox.stub(instance, "sendEvent"); - const eventCreator = sandbox.stub(instance, "createPerformanceEvent"); - const action = {type: at.TELEMETRY_PERFORMANCE_EVENT}; - instance.onAction(action); - assert.calledWith(eventCreator, action); - assert.calledWith(sendEvent, eventCreator.returnValue); - }); - }); -}); diff --git a/browser/extensions/activity-stream/test/unit/lib/TelemetrySender.test.js b/browser/extensions/activity-stream/test/unit/lib/TelemetrySender.test.js index 2fe8853b3d42..7e012f9e82c1 100644 --- a/browser/extensions/activity-stream/test/unit/lib/TelemetrySender.test.js +++ b/browser/extensions/activity-stream/test/unit/lib/TelemetrySender.test.js @@ -2,8 +2,7 @@ // http://creativecommons.org/publicdomain/zero/1.0/ const {GlobalOverrider, FakePrefs} = require("test/unit/utils"); -const {TelemetrySender, TelemetrySenderConstants} = require("lib/TelemetrySender.jsm"); -const {ENDPOINT_PREF, TELEMETRY_PREF, LOGGING_PREF} = TelemetrySenderConstants; +const {TelemetrySender} = require("lib/TelemetrySender.jsm"); /** * A reference to the fake preferences object created by the TelemetrySender @@ -19,11 +18,27 @@ describe("TelemetrySender", () => { let globals; let tSender; let fetchStub; + const observerTopics = ["user-action-event", "performance-event", + "tab-session-complete", "undesired-event"]; const fakeEndpointUrl = "http://127.0.0.1/stuff"; const fakePingJSON = JSON.stringify({action: "fake_action", monkey: 1}); const fakeFetchHttpErrorResponse = {ok: false, status: 400}; const fakeFetchSuccessResponse = {ok: true, status: 200}; + function assertNotificationObserversAdded() { + observerTopics.forEach(topic => { + assert.calledWithExactly( + global.Services.obs.addObserver, tSender, topic, true); + }); + } + + function assertNotificationObserversRemoved() { + observerTopics.forEach(topic => { + assert.calledWithExactly( + global.Services.obs.removeObserver, tSender, topic); + }); + } + before(() => { globals = new GlobalOverrider(); @@ -43,52 +58,61 @@ describe("TelemetrySender", () => { after(() => globals.restore()); - it("should construct the Prefs object", () => { + it("should construct the Prefs object with the right branch", () => { globals.sandbox.spy(global, "Preferences"); tSender = new TelemetrySender(tsArgs); assert.calledOnce(global.Preferences); + assert.calledWith(global.Preferences, + sinon.match.has("branch", "browser.newtabpage.activity-stream")); }); it("should set the enabled prop to false if the pref is false", () => { - FakePrefs.prototype.prefs = {}; - FakePrefs.prototype.prefs[TELEMETRY_PREF] = false; + FakePrefs.prototype.prefs = {telemetry: false}; tSender = new TelemetrySender(tsArgs); assert.isFalse(tSender.enabled); }); + it("should not add notification observers if the enabled pref is false", () => { + FakePrefs.prototype.prefs = {telemetry: false}; + + tSender = new TelemetrySender(tsArgs); + + assert.notCalled(global.Services.obs.addObserver); + }); + it("should set the enabled prop to true if the pref is true", () => { - FakePrefs.prototype.prefs = {}; - FakePrefs.prototype.prefs[TELEMETRY_PREF] = true; + FakePrefs.prototype.prefs = {telemetry: true}; tSender = new TelemetrySender(tsArgs); assert.isTrue(tSender.enabled); }); - describe("#sendPing()", () => { + it("should add all notification observers if the enabled pref is true", () => { + FakePrefs.prototype.prefs = {telemetry: true}; + + tSender = new TelemetrySender(tsArgs); + + assertNotificationObserversAdded(); + }); + + describe("#_sendPing()", () => { beforeEach(() => { - FakePrefs.prototype.prefs = {}; - FakePrefs.prototype.prefs[TELEMETRY_PREF] = true; - FakePrefs.prototype.prefs[ENDPOINT_PREF] = fakeEndpointUrl; + FakePrefs.prototype.prefs = { + "telemetry": true, + "telemetry.ping.endpoint": fakeEndpointUrl + }; tSender = new TelemetrySender(tsArgs); }); - it("should not send if the TelemetrySender is disabled", async () => { - tSender.enabled = false; - - await tSender.sendPing(fakePingJSON); - - assert.notCalled(fetchStub); - }); - it("should POST given ping data to telemetry.ping.endpoint pref w/fetch", async () => { fetchStub.resolves(fakeFetchSuccessResponse); - await tSender.sendPing(fakePingJSON); + await tSender._sendPing(fakePingJSON); assert.calledOnce(fetchStub); assert.calledWithExactly(fetchStub, fakeEndpointUrl, @@ -98,7 +122,7 @@ describe("TelemetrySender", () => { it("should log HTTP failures using Cu.reportError", async () => { fetchStub.resolves(fakeFetchHttpErrorResponse); - await tSender.sendPing(fakePingJSON); + await tSender._sendPing(fakePingJSON); assert.called(Components.utils.reportError); }); @@ -106,42 +130,79 @@ describe("TelemetrySender", () => { it("should log an error using Cu.reportError if fetch rejects", async () => { fetchStub.rejects("Oh noes!"); - await tSender.sendPing(fakePingJSON); + await tSender._sendPing(fakePingJSON); assert.called(Components.utils.reportError); }); it("should log if logging is on && if action is not activity_stream_performance", async () => { - globals.sandbox.stub(console, "log"); - FakePrefs.prototype.prefs = {}; - FakePrefs.prototype.prefs[TELEMETRY_PREF] = true; - FakePrefs.prototype.prefs[LOGGING_PREF] = true; + FakePrefs.prototype.prefs = { + "telemetry": true, + "performance.log": true + }; fetchStub.resolves(fakeFetchSuccessResponse); tSender = new TelemetrySender(tsArgs); - await tSender.sendPing(fakePingJSON); + await tSender._sendPing(fakePingJSON); assert.called(console.log); // eslint-disable-line no-console }); }); + describe("#observe()", () => { + before(() => { + globals.sandbox.stub(TelemetrySender.prototype, "_sendPing"); + }); + + observerTopics.forEach(topic => { + it(`should call this._sendPing with data for ${topic}`, () => { + const fakeSubject = "fakeSubject"; + tSender = new TelemetrySender(tsArgs); + + tSender.observe(fakeSubject, topic, fakePingJSON); + + assert.calledOnce(TelemetrySender.prototype._sendPing); + assert.calledWithExactly(TelemetrySender.prototype._sendPing, + fakePingJSON); + }); + }); + + it("should not call this._sendPing for 'nonexistent-topic'", () => { + const fakeSubject = "fakeSubject"; + tSender = new TelemetrySender(tsArgs); + + tSender.observe(fakeSubject, "nonexistent-topic", fakePingJSON); + + assert.notCalled(TelemetrySender.prototype._sendPing); + }); + }); + describe("#uninit()", () => { it("should remove the telemetry pref listener", () => { tSender = new TelemetrySender(tsArgs); - assert.property(fakePrefs.observers, TELEMETRY_PREF); + assert.property(fakePrefs.observers, "telemetry"); tSender.uninit(); - assert.notProperty(fakePrefs.observers, TELEMETRY_PREF); + assert.notProperty(fakePrefs.observers, "telemetry"); }); - it("should remove the telemetry log listener", () => { + it("should remove all notification observers if telemetry pref is true", () => { + FakePrefs.prototype.prefs = {telemetry: true}; tSender = new TelemetrySender(tsArgs); - assert.property(fakePrefs.observers, LOGGING_PREF); tSender.uninit(); - assert.notProperty(fakePrefs.observers, TELEMETRY_PREF); + assertNotificationObserversRemoved(); + }); + + it("should not remove notification observers if telemetry pref is false", () => { + FakePrefs.prototype.prefs = {telemetry: false}; + tSender = new TelemetrySender(tsArgs); + + tSender.uninit(); + + assert.notCalled(global.Services.obs.removeObserver); }); it("should call Cu.reportError if this._prefs.ignore throws", () => { @@ -157,42 +218,51 @@ describe("TelemetrySender", () => { describe("Misc pref changes", () => { describe("telemetry changes from true to false", () => { beforeEach(() => { - FakePrefs.prototype.prefs = {}; - FakePrefs.prototype.prefs[TELEMETRY_PREF] = true; + FakePrefs.prototype.prefs = {"telemetry": true}; tSender = new TelemetrySender(tsArgs); assert.propertyVal(tSender, "enabled", true); }); it("should set the enabled property to false", () => { - fakePrefs.set(TELEMETRY_PREF, false); + fakePrefs.set("telemetry", false); assert.propertyVal(tSender, "enabled", false); }); + + it("should remove all notification observers", () => { + fakePrefs.set("telemetry", false); + + assertNotificationObserversRemoved(); + }); }); describe("telemetry changes from false to true", () => { beforeEach(() => { - FakePrefs.prototype.prefs = {}; - FakePrefs.prototype.prefs[TELEMETRY_PREF] = false; + FakePrefs.prototype.prefs = {"telemetry": false}; tSender = new TelemetrySender(tsArgs); assert.propertyVal(tSender, "enabled", false); }); it("should set the enabled property to true", () => { - fakePrefs.set(TELEMETRY_PREF, true); + fakePrefs.set("telemetry", true); assert.propertyVal(tSender, "enabled", true); }); + + it("should add all topic observers", () => { + fakePrefs.set("telemetry", true); + + assertNotificationObserversAdded(); + }); }); describe("performance.log changes from false to true", () => { it("should change this.logging from false to true", () => { - FakePrefs.prototype.prefs = {}; - FakePrefs.prototype.prefs[LOGGING_PREF] = false; + FakePrefs.prototype.prefs = {"performance.log": false}; tSender = new TelemetrySender(tsArgs); assert.propertyVal(tSender, "logging", false); - fakePrefs.set(LOGGING_PREF, true); + fakePrefs.set("performance.log", true); assert.propertyVal(tSender, "logging", true); }); diff --git a/browser/extensions/activity-stream/test/unit/lib/TopSitesFeed.test.js b/browser/extensions/activity-stream/test/unit/lib/TopSitesFeed.test.js index 0564c0dd10ba..13a0b52d44cf 100644 --- a/browser/extensions/activity-stream/test/unit/lib/TopSitesFeed.test.js +++ b/browser/extensions/activity-stream/test/unit/lib/TopSitesFeed.test.js @@ -112,27 +112,5 @@ describe("Top Sites Feed", () => { feed.onAction({type: at.NEW_TAB_LOAD}); assert.notCalled(feed.refresh); }); - it("should call openNewWindow with the correct url on OPEN_NEW_WINDOW", () => { - sinon.stub(feed, "openNewWindow"); - const openWindowAction = {type: at.OPEN_NEW_WINDOW, data: {url: "foo.com"}}; - feed.onAction(openWindowAction); - assert.calledWith(feed.openNewWindow, openWindowAction); - }); - it("should call openNewWindow with the correct url and privacy args on OPEN_PRIVATE_WINDOW", () => { - sinon.stub(feed, "openNewWindow"); - const openWindowAction = {type: at.OPEN_PRIVATE_WINDOW, data: {url: "foo.com"}}; - feed.onAction(openWindowAction); - assert.calledWith(feed.openNewWindow, openWindowAction, true); - }); - it("should call openNewWindow with the correct url on OPEN_NEW_WINDOW", () => { - const openWindowAction = { - type: at.OPEN_NEW_WINDOW, - data: {url: "foo.com"}, - _target: {browser: {ownerGlobal: {openLinkIn: () => {}}}} - }; - sinon.stub(openWindowAction._target.browser.ownerGlobal, "openLinkIn"); - feed.onAction(openWindowAction); - assert.calledOnce(openWindowAction._target.browser.ownerGlobal.openLinkIn); - }); }); }); diff --git a/browser/extensions/activity-stream/test/unit/unit-entry.js b/browser/extensions/activity-stream/test/unit/unit-entry.js index dca5f8e45169..832c3e6d116f 100644 --- a/browser/extensions/activity-stream/test/unit/unit-entry.js +++ b/browser/extensions/activity-stream/test/unit/unit-entry.js @@ -13,8 +13,7 @@ overrider.set({ utils: { import: overrider.sandbox.spy(), importGlobalProperties: overrider.sandbox.spy(), - reportError: overrider.sandbox.spy(), - now: () => window.performance.now() + reportError: overrider.sandbox.spy() } }, XPCOMUtils: { @@ -22,14 +21,9 @@ overrider.set({ defineLazyServiceGetter: overrider.sandbox.spy(), generateQI: overrider.sandbox.stub().returns(() => {}) }, + console: {log: overrider.sandbox.spy()}, dump: overrider.sandbox.spy(), - fetch: overrider.sandbox.stub(), Services: { - locale: {getRequestedLocale: overrider.sandbox.stub()}, - mm: { - addMessageListener: overrider.sandbox.spy((msg, cb) => cb()), - removeMessageListener: overrider.sandbox.spy() - }, obs: { addObserver: overrider.sandbox.spy(), removeObserver: overrider.sandbox.spy() diff --git a/browser/extensions/activity-stream/test/unit/utils.js b/browser/extensions/activity-stream/test/unit/utils.js index 3bc4f7796208..dd5e4a3a4cc1 100644 --- a/browser/extensions/activity-stream/test/unit/utils.js +++ b/browser/extensions/activity-stream/test/unit/utils.js @@ -1,10 +1,3 @@ -const React = require("react"); -const {mount, shallow} = require("enzyme"); -const {IntlProvider, intlShape} = require("react-intl"); -const messages = require("data/locales.json")["en-US"]; -const intlProvider = new IntlProvider({locale: "en", messages}); -const {intl} = intlProvider.getChildContext(); - /** * GlobalOverrider - Utility that allows you to override properties on the global object. * See unit-entry.js for example usage. @@ -122,28 +115,8 @@ function addNumberReducer(prevState = 0, action) { return action.type === "ADD" ? prevState + action.data : prevState; } -/** - * Helper functions to test components that need IntlProvider as an ancestor - */ -function nodeWithIntlProp(node) { - return React.cloneElement(node, {intl}); -} - -function shallowWithIntl(node) { - return shallow(nodeWithIntlProp(node), {context: {intl}}); -} - -function mountWithIntl(node) { - return mount(nodeWithIntlProp(node), { - context: {intl}, - childContextTypes: {intl: intlShape} - }); -} - module.exports = { FakePrefs, GlobalOverrider, - addNumberReducer, - mountWithIntl, - shallowWithIntl + addNumberReducer }; diff --git a/browser/extensions/activity-stream/vendor/REACT_INTL_LICENSE b/browser/extensions/activity-stream/vendor/REACT_INTL_LICENSE deleted file mode 100644 index 0da75135fba7..000000000000 --- a/browser/extensions/activity-stream/vendor/REACT_INTL_LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright 2014 Yahoo Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of the Yahoo Inc. nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL YAHOO! INC. BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/browser/extensions/activity-stream/vendor/react-intl.js b/browser/extensions/activity-stream/vendor/react-intl.js deleted file mode 100644 index 38a95b053483..000000000000 --- a/browser/extensions/activity-stream/vendor/react-intl.js +++ /dev/null @@ -1,9 +0,0 @@ -/* - * ReactIntl v2.2.3 - * - * Copyright 2017, Yahoo Inc. - * Copyrights licensed under the New BSD License. - * See the accompanying LICENSE file for terms. - */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],e):e(t.ReactIntl=t.ReactIntl||{},t.React)}(this,function(t,e){"use strict";function r(t){var e,r,n,o,a=Array.prototype.slice.call(arguments,1);for(e=0,r=a.length;e0&&void 0!==arguments[0]?arguments[0]:[],e=Array.isArray(t)?t:[t];e.forEach(function(t){t&&t.locale&&(u.__addLocaleData(t),c.__addLocaleData(t))})}function p(t){for(var e=(t||"").split("-");e.length>0;){if(h(e.join("-")))return!0;e.pop()}return!1}function h(t){var e=t&&t.toLowerCase();return!(!u.__localeData__[e]||!c.__localeData__[e])}function m(t){return(""+t).replace(Ut,function(t){return St[t]})}function d(t,e){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return e.reduce(function(e,n){return t.hasOwnProperty(n)?e[n]=t[n]:r.hasOwnProperty(n)&&(e[n]=r[n]),e},{})}function y(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},e=t.intl;Lt(e,"[React Intl] Could not find required `intl` object. needs to exist in the component ancestry.")}function v(t,e){if(t===e)return!0;if("object"!==("undefined"==typeof t?"undefined":it(t))||null===t||"object"!==("undefined"==typeof e?"undefined":it(e))||null===e)return!1;var r=Object.keys(t),n=Object.keys(e);if(r.length!==n.length)return!1;for(var o=Object.prototype.hasOwnProperty.bind(e),a=0;a3&&void 0!==arguments[3]?arguments[3]:{},u=i.intl,s=void 0===u?{}:u,c=l.intl,f=void 0===c?{}:c;return!v(e,n)||!v(r,o)||!(f===s||v(d(f,It),d(s,It)))}function _(t){return t.displayName||t.name||"Component"}function b(t){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=r.intlPropName,o=void 0===n?"intl":n,a=r.withRef,i=void 0!==a&&a,l=function(e){function r(t,e){lt(this,r);var n=ht(this,(r.__proto__||Object.getPrototypeOf(r)).call(this,t,e));return y(e),n}return ft(r,e),ut(r,[{key:"getWrappedInstance",value:function(){return Lt(i,"[React Intl] To access the wrapped instance, the `{withRef: true}` option must be set when calling: `injectIntl()`"),this.refs.wrappedInstance}},{key:"render",value:function(){return U.createElement(t,ct({},this.props,st({},o,this.context.intl),{ref:i?"wrappedInstance":null}))}}]),r}(e.Component);return l.displayName="InjectIntl("+_(t)+")",l.contextTypes={intl:At},l.WrappedComponent=t,l}function w(t){return t}function F(t){return u.prototype._resolveLocale(t)}function O(t){return u.prototype._findPluralRuleFunction(t)}function x(t){var e=Zt(null);return function(){var r=Array.prototype.slice.call(arguments),n=P(r),o=n&&e[n];return o||(o=new(qt.apply(t,[null].concat(r))),n&&(e[n]=o)),o}}function P(t){if("undefined"!=typeof JSON){var e,r,n,o=[];for(e=0,r=t.length;e3&&void 0!==arguments[3]?arguments[3]:{},o=t.locale,a=t.formats,i=n.format,l=new Date(r),u=i&&C(a,"date",i),s=d(n,Bt,u);try{return e.getDateTimeFormat(o,s).format(l)}catch(t){}return String(l)}function N(t,e,r){var n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},o=t.locale,a=t.formats,i=n.format,l=new Date(r),u=i&&C(a,"time",i),s=d(n,Bt,u);s.hour||s.minute||s.second||(s=ct({},s,{hour:"numeric",minute:"numeric"}));try{return e.getDateTimeFormat(o,s).format(l)}catch(t){}return String(l)}function M(t,e,r){var n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},o=t.locale,a=t.formats,i=n.format,l=new Date(r),u=new Date(n.now),s=i&&C(a,"relative",i),f=d(n,$t,s),p=ct({},c.thresholds);j(Kt);try{return e.getRelativeFormat(o,f).format(l,{now:isFinite(u)?u:e.now()})}catch(t){}finally{j(p)}return String(l)}function R(t,e,r){var n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},o=t.locale,a=t.formats,i=n.format,l=i&&C(a,"number",i),u=d(n,Jt,l);try{return e.getNumberFormat(o,u).format(r)}catch(t){}return String(r)}function k(t,e,r){var n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},o=t.locale,a=d(n,zt);try{return e.getPluralFormat(o,a).format(r)}catch(t){}return"other"}function D(t,e){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},o=t.locale,a=t.formats,i=t.messages,l=t.defaultLocale,u=t.defaultFormats,s=r.id,c=r.defaultMessage;Lt(s,"[React Intl] An `id` must be provided to format a message.");var f=i&&i[s],p=Object.keys(n).length>0;if(!p)return f||c||s;var h=void 0;if(f)try{var m=e.getMessageFormat(f,o,a);h=m.format(n)}catch(t){}if(!h&&c)try{var d=e.getMessageFormat(c,l,u);h=d.format(n)}catch(t){}return h||f||c||s}function E(t,e,r){var n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},o=Object.keys(n).reduce(function(t,e){var r=n[e];return t[e]="string"==typeof r?m(r):r,t},{});return D(t,e,r,o)}function L(t){var e=Math.abs(t);return ee&&(zt=0,Kt={line:1,column:1,seenCR:!1}),r(Kt,zt,e),zt=e),Kt}function n(t){JtQt&&(Qt=Jt,Xt=[]),Xt.push(t))}function o(n,o,a){function i(t){var e=1;for(t.sort(function(t,e){return t.descriptione.description?1:0});e1?i.slice(0,-1).join(", ")+" or "+i[t.length-1]:i[0],o=e?'"'+r(e)+'"':"end of input","Expected "+n+" but "+o+" found."}var u=r(a),s=a1?arguments[1]:{},N={},M={start:a},R=a,k=function(t){return{type:"messageFormatPattern",elements:t}},D=N,E=function(t){var e,r,n,o,a,i="";for(e=0,n=t.length;e=0)return!0;if("string"==typeof t){var e=/s$/.test(t)&&t.substr(0,t.length-1);if(e&&tt.call(nt,e)>=0)throw new Error('"'+t+'" is not a valid IntlRelativeFormat `units` value, did you mean: '+e)}throw new Error('"'+t+'" is not a valid IntlRelativeFormat `units` value, it must be one of: "'+nt.join('", "')+'"')},c.prototype._resolveLocale=function(t){"string"==typeof t&&(t=[t]),t=(t||[]).concat(c.defaultLocale);var e,r,n,o,a=c.__localeData__;for(e=0,r=t.length;e=0)return t;throw new Error('"'+t+'" is not a valid IntlRelativeFormat `style` value, it must be one of: "'+ot.join('", "')+'"')},c.prototype._selectUnits=function(t){var e,r,n;for(e=0,r=nt.length;e=0||Object.prototype.hasOwnProperty.call(t,n)&&(r[n]=t[n]);return r},ht=function(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e},mt=function(t){if(Array.isArray(t)){for(var e=0,r=Array(t.length);e":">","<":"<",'"':""","'":"'"},Ut=/[&><"']/g,Gt=function t(e){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};lt(this,t);var n="ordinal"===r.style,o=O(F(e));this.format=function(t){return o(t,n)}},qt=Function.prototype.bind||function(t){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var e=Array.prototype.slice.call(arguments,1),r=this,n=function(){},o=function(){return r.apply(this instanceof n?this:t,e.concat(Array.prototype.slice.call(arguments)))};return this.prototype&&(n.prototype=this.prototype),o.prototype=new n,o},Ht=Object.prototype.hasOwnProperty,Wt=function(){try{return!!Object.defineProperty({},"a",{})}catch(t){return!1}}(),Vt=Wt?Object.defineProperty:function(t,e,r){"get"in r&&t.__defineGetter__?t.__defineGetter__(e,r.get):(!Ht.call(t,e)||"value"in r)&&(t[e]=r.value)},Zt=Object.create||function(t,e){function r(){}var n,o;r.prototype=t,n=new r;for(o in e)Ht.call(e,o)&&Vt(n,o,e[o]);return n},Bt=Object.keys(Nt),Jt=Object.keys(Mt),$t=Object.keys(Rt),zt=Object.keys(kt),Kt={second:60,minute:60,hour:24,day:30,month:12},Qt=Object.freeze({formatDate:A,formatTime:N,formatRelative:M,formatNumber:R,formatPlural:k,formatMessage:D,formatHTMLMessage:E}),Xt=Object.keys(jt),Yt=Object.keys(Ct),te={formats:{},messages:{},textComponent:"span",defaultLocale:"en",defaultFormats:{}},ee=function(t){function r(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};lt(this,r);var n=ht(this,(r.__proto__||Object.getPrototypeOf(r)).call(this,t,e));Lt("undefined"!=typeof Intl,"[React Intl] The `Intl` APIs must be available in the runtime, and do not appear to be built-in. An `Intl` polyfill should be loaded.\nSee: http://formatjs.io/guides/runtime-environments/");var o=e.intl,a=void 0;a=isFinite(t.initialNow)?Number(t.initialNow):o?o.now():Date.now();var i=o||{},l=i.formatters,s=void 0===l?{getDateTimeFormat:x(Intl.DateTimeFormat),getNumberFormat:x(Intl.NumberFormat),getMessageFormat:x(u),getRelativeFormat:x(c),getPluralFormat:x(Gt)}:l;return n.state=ct({},s,{now:function(){return n._didDisplay?Date.now():a}}),n}return ft(r,t),ut(r,[{key:"getConfig",value:function(){var t=this.context.intl,e=d(this.props,Xt,t);for(var r in te)void 0===e[r]&&(e[r]=te[r]);if(!p(e.locale)){var n=e,o=(n.locale,n.defaultLocale),a=n.defaultFormats;e=ct({},e,{locale:o,formats:a,messages:te.messages})}return e}},{key:"getBoundFormatFns",value:function(t,e){return Yt.reduce(function(r,n){return r[n]=Qt[n].bind(null,t,e),r},{})}},{key:"getChildContext",value:function(){var t=this.getConfig(),e=this.getBoundFormatFns(t,this.state),r=this.state,n=r.now,o=pt(r,["now"]);return{intl:ct({},t,e,{formatters:o,now:n})}}},{key:"shouldComponentUpdate",value:function(){for(var t=arguments.length,e=Array(t),r=0;r1?o-1:0),i=1;i0;d&&!function(){var t=Math.floor(1099511627776*Math.random()).toString(16),r=function(){var e=0;return function(){return"ELEMENT-"+t+"-"+(e+=1)}}();p="@__"+t+"__@",h={},m={},Object.keys(u).forEach(function(t){var n=u[t];if(e.isValidElement(n)){var o=r();h[t]=p+o+p,m[o]=n}else h[t]=n})}();var y={id:a,description:i,defaultMessage:l},v=r(y,h||u),g=void 0,_=m&&Object.keys(m).length>0;return g=_?v.split(p).filter(function(t){return!!t}).map(function(t){return m[t]||t}):[v],"function"==typeof f?f.apply(void 0,mt(g)):e.createElement.apply(void 0,[c,null].concat(mt(g)))}}]),r}(e.Component);pe.displayName="FormattedMessage",pe.contextTypes={intl:At},pe.defaultProps={values:{}};var he=function(t){function e(t,r){lt(this,e);var n=ht(this,(e.__proto__||Object.getPrototypeOf(e)).call(this,t,r));return y(r),n}return ft(e,t),ut(e,[{key:"shouldComponentUpdate",value:function(t){var e=this.props.values,r=t.values;if(!v(r,e))return!0;for(var n=ct({},t,{values:e}),o=arguments.length,a=Array(o>1?o-1:0),i=1;i