mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-21 01:05:45 +00:00
Bug 1204173
- Replace Fluxify with Redux. r=jlongster
--HG-- rename : browser/devtools/debugger/content/stores/event-listeners.js => browser/devtools/debugger/content/actions/event-listeners.js rename : browser/devtools/debugger/content/stores/index.js => browser/devtools/debugger/content/reducers/index.js rename : browser/devtools/shared/create-dispatcher.js => browser/devtools/shared/redux/create-store.js rename : browser/devtools/shared/fluxify/logMiddleware.js => browser/devtools/shared/redux/middleware/log.js rename : browser/devtools/shared/fluxify/thunkMiddleware.js => browser/devtools/shared/redux/middleware/thunk.js rename : browser/devtools/shared/fluxify/waitUntilService.js => browser/devtools/shared/redux/middleware/wait-service.js rename : browser/devtools/shared/D3_LICENSE => browser/devtools/shared/vendor/D3_LICENSE rename : browser/devtools/webaudioeditor/lib/DAGRE_D3_LICENSE => browser/devtools/shared/vendor/DAGRE_D3_LICENSE rename : browser/devtools/shared/d3.js => browser/devtools/shared/vendor/d3.js rename : browser/devtools/webaudioeditor/lib/dagre-d3.js => browser/devtools/shared/vendor/dagre-d3.js rename : browser/devtools/shared/fluxify/moz.build => browser/devtools/shared/vendor/moz.build rename : browser/devtools/shared/content/react-dev.js => browser/devtools/shared/vendor/react-dev.js rename : browser/devtools/shared/content/react.js => browser/devtools/shared/vendor/react.js
This commit is contained in:
parent
339cd10454
commit
57885a539c
@ -3,43 +3,15 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const constants = require('../constants');
|
||||
const promise = require('promise');
|
||||
const { rdpInvoke, asPaused } = require('../utils');
|
||||
const constants = require("../constants");
|
||||
const { rdpInvoke, asPaused } = require("../utils");
|
||||
const { reportException } = require("devtools/toolkit/DevToolsUtils");
|
||||
|
||||
const FETCH_EVENT_LISTENERS_DELAY = 200; // ms
|
||||
|
||||
const initialState = {
|
||||
activeEventNames: [],
|
||||
listeners: [],
|
||||
fetchingListeners: false,
|
||||
};
|
||||
|
||||
function update(state = initialState, action, emitChange) {
|
||||
switch(action.type) {
|
||||
case constants.UPDATE_EVENT_BREAKPOINTS:
|
||||
state.activeEventNames = action.eventNames;
|
||||
emitChange('activeEventNames', state.activeEventNames);
|
||||
break;
|
||||
case constants.FETCH_EVENT_LISTENERS:
|
||||
if (action.status === "begin") {
|
||||
state.fetchingListeners = true;
|
||||
}
|
||||
else if (action.status === "done") {
|
||||
state.fetchingListeners = false;
|
||||
state.listeners = action.listeners;
|
||||
emitChange('listeners', state.listeners);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return state;
|
||||
};
|
||||
|
||||
function fetchEventListeners() {
|
||||
return (dispatch, getState) => {
|
||||
// Make sure we're not sending a batch of closely repeated requests.
|
||||
// Make sure we"re not sending a batch of closely repeated requests.
|
||||
// This can easily happen whenever new sources are fetched.
|
||||
setNamedTimeout("event-listeners-fetch", FETCH_EVENT_LISTENERS_DELAY, () => {
|
||||
// In case there is still a request of listeners going on (it
|
||||
@ -81,7 +53,7 @@ const _getListeners = Task.async(function*() {
|
||||
const response = yield rdpInvoke(gThreadClient, gThreadClient.eventListeners);
|
||||
|
||||
// Make sure all the listeners are sorted by the event type, since
|
||||
// they're not guaranteed to be clustered together.
|
||||
// they"re not guaranteed to be clustered together.
|
||||
response.listeners.sort((a, b) => a.type > b.type ? 1 : -1);
|
||||
|
||||
// Add all the listeners in the debugger view event linsteners container.
|
||||
@ -94,8 +66,8 @@ const _getListeners = Task.async(function*() {
|
||||
} else if (listener.function.class == "Function") {
|
||||
definitionSite = yield _getDefinitionSite(listener.function);
|
||||
if (!definitionSite) {
|
||||
// We don't know where this listener comes from so don't show it in
|
||||
// the UI as breaking on it doesn't work (bug 942899).
|
||||
// We don"t know where this listener comes from so don"t show it in
|
||||
// the UI as breaking on it doesn"t work (bug 942899).
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -141,7 +113,4 @@ function updateEventBreakpoints(eventNames) {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
update: update,
|
||||
actions: { updateEventBreakpoints, fetchEventListeners }
|
||||
}
|
||||
module.exports = { updateEventBreakpoints, fetchEventListeners };
|
@ -0,0 +1,37 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const constants = require('../constants');
|
||||
|
||||
const FETCH_EVENT_LISTENERS_DELAY = 200; // ms
|
||||
|
||||
const initialState = {
|
||||
activeEventNames: [],
|
||||
listeners: [],
|
||||
fetchingListeners: false,
|
||||
};
|
||||
|
||||
function update(state = initialState, action, emit) {
|
||||
switch(action.type) {
|
||||
case constants.UPDATE_EVENT_BREAKPOINTS:
|
||||
state.activeEventNames = action.eventNames;
|
||||
emit("@redux:activeEventNames", state.activeEventNames);
|
||||
break;
|
||||
case constants.FETCH_EVENT_LISTENERS:
|
||||
if (action.status === "begin") {
|
||||
state.fetchingListeners = true;
|
||||
}
|
||||
else if (action.status === "done") {
|
||||
state.fetchingListeners = false;
|
||||
state.listeners = action.listeners;
|
||||
emit("@redux:listeners", state.listeners);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
module.exports = update;
|
@ -5,4 +5,4 @@
|
||||
|
||||
const eventListeners = require('./event-listeners');
|
||||
|
||||
module.exports = { eventListeners };
|
||||
exports.eventListeners = eventListeners;
|
@ -3,26 +3,25 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const actions = require('../stores/event-listeners').actions;
|
||||
const bindActionCreators = require('devtools/shared/fluxify/bindActionCreators');
|
||||
const actions = require('../actions/event-listeners');
|
||||
const { bindActionCreators } = require('devtools/shared/vendor/redux');
|
||||
|
||||
/**
|
||||
* Functions handling the event listeners UI.
|
||||
*/
|
||||
function EventListenersView(dispatcher, DebuggerController) {
|
||||
function EventListenersView(store, DebuggerController) {
|
||||
dumpn("EventListenersView was instantiated");
|
||||
|
||||
this.actions = bindActionCreators(actions, dispatcher.dispatch);
|
||||
this.getState = () => dispatcher.getState().eventListeners;
|
||||
|
||||
this.Breakpoints = DebuggerController.Breakpoints;
|
||||
|
||||
dispatcher.onChange({
|
||||
"eventListeners": { "listeners": this.renderListeners }
|
||||
}, this);
|
||||
this.actions = bindActionCreators(actions, store.dispatch);
|
||||
this.getState = () => store.getState().eventListeners;
|
||||
|
||||
this._onCheck = this._onCheck.bind(this);
|
||||
this._onClick = this._onClick.bind(this);
|
||||
this._onListeners = this._onListeners.bind(this);
|
||||
|
||||
this.Breakpoints = DebuggerController.Breakpoints;
|
||||
this.controller = DebuggerController;
|
||||
this.controller.on("@redux:listeners", this._onListeners);
|
||||
}
|
||||
|
||||
EventListenersView.prototype = Heritage.extend(WidgetMethods, {
|
||||
@ -53,8 +52,10 @@ EventListenersView.prototype = Heritage.extend(WidgetMethods, {
|
||||
destroy: function() {
|
||||
dumpn("Destroying the EventListenersView");
|
||||
|
||||
this.controller.off("@redux:listeners", this._onListeners);
|
||||
this.widget.removeEventListener("check", this._onCheck, false);
|
||||
this.widget.removeEventListener("click", this._onClick, false);
|
||||
this.controller = this.Breakpoints = null;
|
||||
},
|
||||
|
||||
renderListeners: function(listeners) {
|
||||
@ -285,6 +286,13 @@ EventListenersView.prototype = Heritage.extend(WidgetMethods, {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when listeners change.
|
||||
*/
|
||||
_onListeners: function(_, listeners) {
|
||||
this.renderListeners(listeners);
|
||||
},
|
||||
|
||||
_eventCheckboxTooltip: "",
|
||||
_onSelectorString: "",
|
||||
_inSourceString: "",
|
||||
|
@ -1280,7 +1280,7 @@ SourceScripts.prototype = {
|
||||
|
||||
// Make sure the events listeners are up to date.
|
||||
if (DebuggerView.instrumentsPaneTab == "events-tab") {
|
||||
dispatcher.dispatch(actions.fetchEventListeners());
|
||||
store.dispatch(actions.fetchEventListeners());
|
||||
}
|
||||
|
||||
// Signal that a new source has been added.
|
||||
@ -2055,6 +2055,7 @@ var Prefs = new ViewHelpers.Prefs("devtools", {
|
||||
* Convenient way of emitting events from the panel window.
|
||||
*/
|
||||
EventEmitter.decorate(this);
|
||||
EventEmitter.decorate(DebuggerController);
|
||||
|
||||
/**
|
||||
* Preliminary setup for the DebuggerController object.
|
||||
|
@ -35,16 +35,19 @@ const RESIZE_REFRESH_RATE = 50; // ms
|
||||
const PROMISE_DEBUGGER_URL =
|
||||
"chrome://browser/content/devtools/promisedebugger/promise-debugger.xhtml";
|
||||
|
||||
const createDispatcher = require('devtools/shared/create-dispatcher')();
|
||||
const stores = require('./content/stores/index');
|
||||
const dispatcher = createDispatcher(stores);
|
||||
const waitUntilService = require('devtools/shared/fluxify/waitUntilService');
|
||||
const debuggerControllerEmit = DebuggerController.emit.bind(DebuggerController);
|
||||
const createStore = require("devtools/shared/redux/create-store")();
|
||||
const { combineEmittingReducers } = require("devtools/shared/redux/reducers");
|
||||
const reducers = require("./content/reducers/index");
|
||||
const store = createStore(combineEmittingReducers(reducers, debuggerControllerEmit));
|
||||
const { NAME: WAIT_UNTIL_NAME } = require("devtools/shared/redux/middleware/wait-service");
|
||||
|
||||
const services = {
|
||||
WAIT_UNTIL: waitUntilService.name
|
||||
WAIT_UNTIL: WAIT_UNTIL_NAME
|
||||
};
|
||||
|
||||
const EventListenersView = require('./content/views/event-listeners-view');
|
||||
const actions = require('./content/stores/event-listeners').actions;
|
||||
const actions = require('./content/actions/event-listeners');
|
||||
|
||||
/**
|
||||
* Object defining the debugger view components.
|
||||
@ -626,7 +629,7 @@ var DebuggerView = {
|
||||
*/
|
||||
_onInstrumentsPaneTabSelect: function() {
|
||||
if (this._instrumentsPane.selectedTab.id == "events-tab") {
|
||||
dispatcher.dispatch(actions.fetchEventListeners());
|
||||
store.dispatch(actions.fetchEventListeners());
|
||||
}
|
||||
},
|
||||
|
||||
@ -928,4 +931,4 @@ ResultsPanelContainer.prototype = Heritage.extend(WidgetMethods, {
|
||||
top: 0
|
||||
});
|
||||
|
||||
DebuggerView.EventListeners = new EventListenersView(dispatcher, DebuggerController);
|
||||
DebuggerView.EventListeners = new EventListenersView(store, DebuggerController);
|
||||
|
@ -18,9 +18,13 @@ EXTRA_JS_MODULES.devtools.debugger.content.views += [
|
||||
'content/views/event-listeners-view.js'
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES.devtools.debugger.content.stores += [
|
||||
'content/stores/event-listeners.js',
|
||||
'content/stores/index.js'
|
||||
EXTRA_JS_MODULES.devtools.debugger.content.reducers += [
|
||||
'content/reducers/event-listeners.js',
|
||||
'content/reducers/index.js'
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES.devtools.debugger.content.actions += [
|
||||
'content/actions/event-listeners.js',
|
||||
]
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ['test/mochitest/browser.ini']
|
||||
|
@ -13,7 +13,7 @@ function test() {
|
||||
let gDebugger = aPanel.panelWin;
|
||||
let gView = gDebugger.DebuggerView;
|
||||
let gEvents = gView.EventListeners;
|
||||
let gDispatcher = gDebugger.dispatcher;
|
||||
let gStore = gDebugger.store;
|
||||
let constants = gDebugger.require('./content/constants');
|
||||
|
||||
Task.spawn(function*() {
|
||||
@ -26,7 +26,7 @@ function test() {
|
||||
|
||||
function testFetchOnFocus() {
|
||||
return Task.spawn(function*() {
|
||||
let fetched = afterDispatch(gDispatcher, constants.FETCH_EVENT_LISTENERS);
|
||||
let fetched = afterDispatch(gStore, constants.FETCH_EVENT_LISTENERS);
|
||||
|
||||
gView.toggleInstrumentsPane({ visible: true, animated: false }, 1);
|
||||
is(gView.instrumentsPaneHidden, false,
|
||||
@ -45,7 +45,7 @@ function test() {
|
||||
|
||||
function testFetchOnReloadWhenFocused() {
|
||||
return Task.spawn(function*() {
|
||||
let fetched = afterDispatch(gDispatcher, constants.FETCH_EVENT_LISTENERS);
|
||||
let fetched = afterDispatch(gStore, constants.FETCH_EVENT_LISTENERS);
|
||||
|
||||
let reloading = once(gDebugger.gTarget, "will-navigate");
|
||||
let reloaded = waitForSourcesAfterReload();
|
||||
@ -76,7 +76,7 @@ function test() {
|
||||
|
||||
function testFetchOnReloadWhenNotFocused() {
|
||||
return Task.spawn(function*() {
|
||||
gDispatcher.dispatch({
|
||||
gStore.dispatch({
|
||||
type: gDebugger.services.WAIT_UNTIL,
|
||||
predicate: action => {
|
||||
return (action.type === constants.FETCH_EVENT_LISTENERS ||
|
||||
|
@ -12,13 +12,13 @@ function test() {
|
||||
let gDebugger = aPanel.panelWin;
|
||||
let gView = gDebugger.DebuggerView;
|
||||
let gEvents = gView.EventListeners;
|
||||
let gDispatcher = gDebugger.dispatcher;
|
||||
let gStore = gDebugger.store;
|
||||
let constants = gDebugger.require('./content/constants');
|
||||
|
||||
Task.spawn(function*() {
|
||||
yield waitForSourceShown(aPanel, ".html");
|
||||
|
||||
let fetched = afterDispatch(gDispatcher, constants.FETCH_EVENT_LISTENERS);
|
||||
let fetched = afterDispatch(gStore, constants.FETCH_EVENT_LISTENERS);
|
||||
gView.toggleInstrumentsPane({ visible: true, animated: false }, 1);
|
||||
yield fetched;
|
||||
|
||||
|
@ -14,14 +14,14 @@ function test() {
|
||||
let gView = gDebugger.DebuggerView;
|
||||
let gController = gDebugger.DebuggerController
|
||||
let gEvents = gView.EventListeners;
|
||||
let gDispatcher = gDebugger.dispatcher;
|
||||
let getState = gDispatcher.getState;
|
||||
let gStore = gDebugger.store;
|
||||
let getState = gStore.getState;
|
||||
let constants = gDebugger.require('./content/constants');
|
||||
|
||||
Task.spawn(function*() {
|
||||
yield waitForSourceShown(aPanel, ".html");
|
||||
|
||||
let fetched = afterDispatch(gDispatcher, constants.FETCH_EVENT_LISTENERS);
|
||||
let fetched = afterDispatch(gStore, constants.FETCH_EVENT_LISTENERS);
|
||||
gView.toggleInstrumentsPane({ visible: true, animated: false }, 1);
|
||||
yield fetched;
|
||||
|
||||
@ -34,7 +34,7 @@ function test() {
|
||||
testEventGroup("mouseEvents", false);
|
||||
testEventArrays("change,click,keydown,keyup", "");
|
||||
|
||||
let updated = afterDispatch(gDispatcher, constants.UPDATE_EVENT_BREAKPOINTS);
|
||||
let updated = afterDispatch(gStore, constants.UPDATE_EVENT_BREAKPOINTS);
|
||||
EventUtils.sendMouseEvent({ type: "click" }, getItemCheckboxNode(0), gDebugger);
|
||||
yield updated;
|
||||
|
||||
@ -47,7 +47,7 @@ function test() {
|
||||
testEventGroup("mouseEvents", false);
|
||||
testEventArrays("change,click,keydown,keyup", "change");
|
||||
|
||||
updated = afterDispatch(gDispatcher, constants.UPDATE_EVENT_BREAKPOINTS);
|
||||
updated = afterDispatch(gStore, constants.UPDATE_EVENT_BREAKPOINTS);
|
||||
EventUtils.sendMouseEvent({ type: "click" }, getItemCheckboxNode(0), gDebugger);
|
||||
yield updated;
|
||||
|
||||
|
@ -15,14 +15,14 @@ function test() {
|
||||
let gView = gDebugger.DebuggerView;
|
||||
let gController = gDebugger.DebuggerController
|
||||
let gEvents = gView.EventListeners;
|
||||
let gDispatcher = gDebugger.dispatcher;
|
||||
let getState = gDispatcher.getState;
|
||||
let gStore = gDebugger.store;
|
||||
let getState = gStore.getState;
|
||||
let constants = gDebugger.require('./content/constants');
|
||||
|
||||
Task.spawn(function*() {
|
||||
yield waitForSourceShown(aPanel, ".html");
|
||||
|
||||
let fetched = afterDispatch(gDispatcher, constants.FETCH_EVENT_LISTENERS);
|
||||
let fetched = afterDispatch(gStore, constants.FETCH_EVENT_LISTENERS);
|
||||
gView.toggleInstrumentsPane({ visible: true, animated: false }, 1);
|
||||
yield fetched;
|
||||
|
||||
@ -35,7 +35,7 @@ function test() {
|
||||
testEventGroup("mouseEvents", false);
|
||||
testEventArrays("change,click,keydown,keyup", "");
|
||||
|
||||
let updated = afterDispatch(gDispatcher, constants.UPDATE_EVENT_BREAKPOINTS);
|
||||
let updated = afterDispatch(gStore, constants.UPDATE_EVENT_BREAKPOINTS);
|
||||
EventUtils.sendMouseEvent({ type: "click" }, getGroupCheckboxNode("interactionEvents"), gDebugger);
|
||||
yield updated;
|
||||
|
||||
@ -48,7 +48,7 @@ function test() {
|
||||
testEventGroup("mouseEvents", false);
|
||||
testEventArrays("change,click,keydown,keyup", "change");
|
||||
|
||||
updated = afterDispatch(gDispatcher, constants.UPDATE_EVENT_BREAKPOINTS);
|
||||
updated = afterDispatch(gStore, constants.UPDATE_EVENT_BREAKPOINTS);
|
||||
EventUtils.sendMouseEvent({ type: "click" }, getGroupCheckboxNode("interactionEvents"), gDebugger);
|
||||
yield updated;
|
||||
|
||||
@ -61,7 +61,7 @@ function test() {
|
||||
testEventGroup("mouseEvents", false);
|
||||
testEventArrays("change,click,keydown,keyup", "");
|
||||
|
||||
updated = afterDispatch(gDispatcher, constants.UPDATE_EVENT_BREAKPOINTS);
|
||||
updated = afterDispatch(gStore, constants.UPDATE_EVENT_BREAKPOINTS);
|
||||
EventUtils.sendMouseEvent({ type: "click" }, getGroupCheckboxNode("keyboardEvents"), gDebugger);
|
||||
yield updated;
|
||||
|
||||
@ -74,7 +74,7 @@ function test() {
|
||||
testEventGroup("mouseEvents", false);
|
||||
testEventArrays("change,click,keydown,keyup", "keydown,keyup");
|
||||
|
||||
updated = afterDispatch(gDispatcher, constants.UPDATE_EVENT_BREAKPOINTS);
|
||||
updated = afterDispatch(gStore, constants.UPDATE_EVENT_BREAKPOINTS);
|
||||
EventUtils.sendMouseEvent({ type: "click" }, getGroupCheckboxNode("keyboardEvents"), gDebugger);
|
||||
yield updated;
|
||||
|
||||
|
@ -15,14 +15,14 @@ function test() {
|
||||
let gController = gDebugger.DebuggerController
|
||||
let gEvents = gView.EventListeners;
|
||||
let gBreakpoints = gController.Breakpoints;
|
||||
let gDispatcher = gDebugger.dispatcher;
|
||||
let getState = gDispatcher.getState;
|
||||
let gStore = gDebugger.store;
|
||||
let getState = gStore.getState;
|
||||
let constants = gDebugger.require('./content/constants');
|
||||
|
||||
Task.spawn(function*() {
|
||||
yield waitForSourceShown(aPanel, ".html");
|
||||
|
||||
let fetched = afterDispatch(gDispatcher, constants.FETCH_EVENT_LISTENERS);
|
||||
let fetched = afterDispatch(gStore, constants.FETCH_EVENT_LISTENERS);
|
||||
gView.toggleInstrumentsPane({ visible: true, animated: false }, 1);
|
||||
yield fetched;
|
||||
|
||||
@ -35,7 +35,7 @@ function test() {
|
||||
testEventGroup("mouseEvents", false);
|
||||
testEventArrays("change,click,keydown,keyup", "");
|
||||
|
||||
let updated = afterDispatch(gDispatcher, constants.UPDATE_EVENT_BREAKPOINTS);
|
||||
let updated = afterDispatch(gStore, constants.UPDATE_EVENT_BREAKPOINTS);
|
||||
EventUtils.sendMouseEvent({ type: "click" }, getItemCheckboxNode(0), gDebugger);
|
||||
EventUtils.sendMouseEvent({ type: "click" }, getItemCheckboxNode(1), gDebugger);
|
||||
EventUtils.sendMouseEvent({ type: "click" }, getItemCheckboxNode(2), gDebugger);
|
||||
@ -51,7 +51,7 @@ function test() {
|
||||
testEventArrays("change,click,keydown,keyup", "change,click,keydown");
|
||||
|
||||
reload(aPanel);
|
||||
yield afterDispatch(gDispatcher, constants.FETCH_EVENT_LISTENERS);
|
||||
yield afterDispatch(gStore, constants.FETCH_EVENT_LISTENERS);
|
||||
|
||||
testEventItem(0, true);
|
||||
testEventItem(1, true);
|
||||
@ -62,7 +62,7 @@ function test() {
|
||||
testEventGroup("mouseEvents", false);
|
||||
testEventArrays("change,click,keydown,keyup", "change,click,keydown");
|
||||
|
||||
updated = afterDispatch(gDispatcher, constants.UPDATE_EVENT_BREAKPOINTS);
|
||||
updated = afterDispatch(gStore, constants.UPDATE_EVENT_BREAKPOINTS);
|
||||
EventUtils.sendMouseEvent({ type: "click" }, getItemCheckboxNode(0), gDebugger);
|
||||
EventUtils.sendMouseEvent({ type: "click" }, getItemCheckboxNode(1), gDebugger);
|
||||
EventUtils.sendMouseEvent({ type: "click" }, getItemCheckboxNode(2), gDebugger);
|
||||
@ -78,7 +78,7 @@ function test() {
|
||||
testEventArrays("change,click,keydown,keyup", "");
|
||||
|
||||
reload(aPanel);
|
||||
yield afterDispatch(gDispatcher, constants.FETCH_EVENT_LISTENERS);
|
||||
yield afterDispatch(gStore, constants.FETCH_EVENT_LISTENERS);
|
||||
|
||||
testEventItem(0, false);
|
||||
testEventItem(1, false);
|
||||
|
@ -13,15 +13,15 @@ function test() {
|
||||
let gDebugger = aPanel.panelWin;
|
||||
let gView = gDebugger.DebuggerView;
|
||||
let gEvents = gView.EventListeners;
|
||||
let gDispatcher = gDebugger.dispatcher;
|
||||
let getState = gDispatcher.getState;
|
||||
let gStore = gDebugger.store;
|
||||
let getState = gStore.getState;
|
||||
let constants = gDebugger.require('./content/constants');
|
||||
|
||||
Task.spawn(function*() {
|
||||
yield waitForSourceShown(aPanel, ".html");
|
||||
yield callInTab(gTab, "addBodyClickEventListener");
|
||||
|
||||
let fetched = afterDispatch(gDispatcher, constants.FETCH_EVENT_LISTENERS);
|
||||
let fetched = afterDispatch(gStore, constants.FETCH_EVENT_LISTENERS);
|
||||
gView.toggleInstrumentsPane({ visible: true, animated: false }, 1);
|
||||
yield fetched;
|
||||
yield ensureThreadClientState(aPanel, "attached");
|
||||
@ -31,7 +31,7 @@ function test() {
|
||||
is(gView.instrumentsPaneTab, "events-tab",
|
||||
"The events tab should be selected.");
|
||||
|
||||
let updated = afterDispatch(gDispatcher, constants.UPDATE_EVENT_BREAKPOINTS);
|
||||
let updated = afterDispatch(gStore, constants.UPDATE_EVENT_BREAKPOINTS);
|
||||
EventUtils.sendMouseEvent({ type: "click" }, getItemCheckboxNode(1), gDebugger);
|
||||
yield updated;
|
||||
yield ensureThreadClientState(aPanel, "attached");
|
||||
|
@ -31,13 +31,13 @@ add_task(function* () {
|
||||
|
||||
let [,, panel, win] = yield initDebugger(tab);
|
||||
let gDebugger = panel.panelWin;
|
||||
let gDispatcher = gDebugger.dispatcher;
|
||||
let gStore = gDebugger.store;
|
||||
let constants = gDebugger.require('./content/constants');
|
||||
let eventListeners = gDebugger.require('./content/stores/event-listeners');
|
||||
let fetched = afterDispatch(gDispatcher, constants.FETCH_EVENT_LISTENERS);
|
||||
let actions = gDebugger.require('./content/actions/event-listeners');
|
||||
let fetched = afterDispatch(gStore, constants.FETCH_EVENT_LISTENERS);
|
||||
|
||||
info("Scheduling event listener fetch.");
|
||||
gDispatcher.dispatch(eventListeners.actions.fetchEventListeners());
|
||||
gStore.dispatch(actions.fetchEventListeners());
|
||||
|
||||
info("Waiting for updated event listeners to arrive.");
|
||||
yield fetched;
|
||||
|
@ -1192,10 +1192,10 @@ function source(sourceClient) {
|
||||
return rdpInvoke(sourceClient, sourceClient.source);
|
||||
}
|
||||
|
||||
function afterDispatch(dispatcher, type) {
|
||||
function afterDispatch(store, type) {
|
||||
info("Waiting on dispatch: " + type);
|
||||
return new Promise(resolve => {
|
||||
dispatcher.dispatch({
|
||||
store.dispatch({
|
||||
// Normally we would use `services.WAIT_UNTIL`, but use the
|
||||
// internal name here so tests aren't forced to always pass it
|
||||
// in
|
||||
|
@ -3,6 +3,8 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
browser.jar:
|
||||
content/browser/devtools/d3.js (shared/vendor/d3.js)
|
||||
content/browser/devtools/dagre-d3.js (shared/vendor/dagre-d3.js)
|
||||
content/browser/devtools/widgets.css (shared/widgets/widgets.css)
|
||||
content/browser/devtools/widgets/VariablesView.xul (shared/widgets/VariablesView.xul)
|
||||
content/browser/devtools/markup-view.xhtml (markupview/markup-view.xhtml)
|
||||
@ -84,9 +86,7 @@ browser.jar:
|
||||
content/browser/devtools/canvasdebugger.js (canvasdebugger/canvasdebugger.js)
|
||||
content/browser/devtools/canvasdebugger/snapshotslist.js (canvasdebugger/snapshotslist.js)
|
||||
content/browser/devtools/canvasdebugger/callslist.js (canvasdebugger/callslist.js)
|
||||
content/browser/devtools/d3.js (shared/d3.js)
|
||||
content/browser/devtools/webaudioeditor.xul (webaudioeditor/webaudioeditor.xul)
|
||||
content/browser/devtools/dagre-d3.js (webaudioeditor/lib/dagre-d3.js)
|
||||
content/browser/devtools/webaudioeditor/includes.js (webaudioeditor/includes.js)
|
||||
content/browser/devtools/webaudioeditor/models.js (webaudioeditor/models.js)
|
||||
content/browser/devtools/webaudioeditor/controller.js (webaudioeditor/controller.js)
|
||||
|
@ -23,6 +23,7 @@ catch(e) {
|
||||
};
|
||||
}
|
||||
|
||||
const VENDOR_CONTENT_URL = "resource:///modules/devtools/shared/vendor";
|
||||
|
||||
/*
|
||||
* Create a loader to be used in a browser environment. This evaluates
|
||||
@ -55,8 +56,8 @@ function BrowserLoader(baseURI, window) {
|
||||
let dynamicPaths = {};
|
||||
if (appConstants.DEBUG_JS_MODULES) {
|
||||
// Load in the dev version of React
|
||||
dynamicPaths["devtools/shared/content/react"] =
|
||||
"resource:///modules/devtools/shared/content/react-dev.js";
|
||||
dynamicPaths["devtools/shared/vendor/react"] =
|
||||
"resource:///modules/devtools/vendor/react-dev.js";
|
||||
}
|
||||
|
||||
const opts = {
|
||||
@ -67,8 +68,9 @@ function BrowserLoader(baseURI, window) {
|
||||
invisibleToDebugger: loaderOptions.invisibleToDebugger,
|
||||
require: (id, require) => {
|
||||
const uri = require.resolve(id);
|
||||
|
||||
if (!uri.startsWith(baseURI) &&
|
||||
!uri.startsWith("resource:///modules/devtools/shared/content")) {
|
||||
!uri.startsWith(VENDOR_CONTENT_URL)) {
|
||||
return devtools.require(uri);
|
||||
}
|
||||
return require(uri);
|
||||
|
@ -1,28 +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/. */
|
||||
"use strict";
|
||||
|
||||
function bindActionCreator(actionCreator, dispatch) {
|
||||
return (...args) => dispatch(actionCreator(...args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps action creator functions into a function that automatically
|
||||
* dispatches the created action with `dispatch`. Normally action
|
||||
* creators simply return actions, but wrapped functions will
|
||||
* automatically dispatch.
|
||||
*
|
||||
* @param {object} actionCreators
|
||||
* An object of action creator functions (names as keys)
|
||||
* @param {function} dispatch
|
||||
*/
|
||||
function bindActionCreators(actionCreators, dispatch) {
|
||||
let actions = {};
|
||||
for (let k of Object.keys(actionCreators)) {
|
||||
actions[k] = bindActionCreator(actionCreators[k], dispatch);
|
||||
}
|
||||
return actions;
|
||||
}
|
||||
|
||||
module.exports = bindActionCreators;
|
@ -1,247 +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/. */
|
||||
"use strict";
|
||||
|
||||
const { entries, compose } = require("devtools/toolkit/DevToolsUtils");
|
||||
|
||||
/**
|
||||
* A store creator that creates a dispatch function that runs the
|
||||
* provided middlewares before actually dispatching. This allows
|
||||
* simple middleware to augment the kinds of actions that can
|
||||
* be dispatched. This would be used like this:
|
||||
* `createDispatcher = applyMiddleware([fooMiddleware, ...])(createDispatcher)`
|
||||
*
|
||||
* Middlewares are simple functions that are provided `dispatch` and
|
||||
* `getState` functions. They create functions that accept actions and
|
||||
* can re-dispatch them in any way they want. A common scenario is
|
||||
* asynchronously dispatching multiple actions. Here is a full
|
||||
* middleware:
|
||||
*
|
||||
* function thunkMiddleware({ dispatch, getState }) {
|
||||
* return next => action => {
|
||||
* return typeof action === 'function' ?
|
||||
* action(dispatch, getState) :
|
||||
* next(action);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* `next` is essentially a `dispatch` function, but it calls the next
|
||||
* middelware in the chain (or the real `dispatch` function). Using
|
||||
* this middleware, you can return a function that gives you a
|
||||
* dispatch function:
|
||||
*
|
||||
* function actionCreator(timeout) {
|
||||
* return (dispatch, getState) => {
|
||||
* dispatch({ type: TIMEOUT, status: "start" });
|
||||
* setTimeout(() => dispatch({ type: TIMEOUT, status: "end" }),
|
||||
* timeout);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
function applyMiddleware(...middlewares) {
|
||||
return next => stores => {
|
||||
const dispatcher = next(stores);
|
||||
let dispatch = dispatcher.dispatch;
|
||||
|
||||
const api = {
|
||||
getState: dispatcher.getState,
|
||||
dispatch: action => dispatch(action)
|
||||
};
|
||||
const chain = middlewares.map(middleware => middleware(api));
|
||||
dispatch = compose(...chain)(dispatcher.dispatch);
|
||||
|
||||
return Object.assign({}, dispatcher, { dispatch: dispatch });
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The heart of the system. This creates a dispatcher which is the
|
||||
* interface between views and stores. Views can use a dispatcher
|
||||
* instance to dispatch actions, which know nothing about the stores.
|
||||
* Actions are broadcasted to all registered stores, and stores can
|
||||
* handle the action and update their state. The dispatcher gives
|
||||
* stores an `emitChange` function, which signifies that a piece of
|
||||
* state has changed. The dispatcher will notify all views that are
|
||||
* listening to that piece of state, registered with `onChange`.
|
||||
*
|
||||
* Views generally are stateless, pure components (eventually React
|
||||
* components). They simply take state and render it.
|
||||
*
|
||||
* Stores make up the entire app state, and are all grouped together
|
||||
* into a single app state atom, returned by the dispatcher's
|
||||
* `getState` function. The shape of the app state is determined by
|
||||
* the `stores` object passed in to the dispatcher, so if
|
||||
* `{ foo: fooStore }` was passed to `createDispatcher` the app state
|
||||
* would be `{ foo: fooState }`
|
||||
*
|
||||
* Actions are just JavaScript object with a `type` property and any
|
||||
* other fields pertinent to the action. Action creators should
|
||||
* generally be used to create actions, which are just functions that
|
||||
* return the action object. Additionally, the `bindActionCreators`
|
||||
* module provides a function for automatically binding action
|
||||
* creators to a dispatch function, so calling them automatically
|
||||
* dispatches. For example:
|
||||
*
|
||||
* ```js
|
||||
* // Manually dispatch
|
||||
* dispatcher.dispatch({ type: constants.ADD_ITEM, item: item });
|
||||
* // Using an action creator
|
||||
* dispatcher.dispatch(addItem(item));
|
||||
*
|
||||
* // Using an action creator bound to dispatch
|
||||
* actions = bindActionCreators({ addItem: addItem });
|
||||
* actions.addItem(item);
|
||||
* ```
|
||||
*
|
||||
* Our system expects stores to exist as an `update` function. You
|
||||
* should define an update function in a module, and optionally
|
||||
* any action creators that are useful to go along with it. Here is
|
||||
* an example store file:
|
||||
*
|
||||
* ```js
|
||||
* const initialState = { items: [] };
|
||||
* function update(state = initialState, action, emitChange) {
|
||||
* if (action.type === constants.ADD_ITEM) {
|
||||
* state.items.push(action.item);
|
||||
* emitChange("items", state.items);
|
||||
* }
|
||||
*
|
||||
* return state;
|
||||
* }
|
||||
*
|
||||
* function addItem(item) {
|
||||
* return {
|
||||
* type: constants.ADD_ITEM,
|
||||
* item: item
|
||||
* };
|
||||
* }
|
||||
*
|
||||
* module.exports = {
|
||||
* update: update,
|
||||
* actions: { addItem }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Lastly, "constants" are simple strings that specify action names.
|
||||
* Usually the entire set of available action types are specified in
|
||||
* a `constants.js` file, so they are available globally. Use
|
||||
* variables that are the same name as the string, for example
|
||||
* `const ADD_ITEM = "ADD_ITEM"`.
|
||||
*
|
||||
* This entire system was inspired by Redux, which hopefully we will
|
||||
* eventually use once things get cleaned up enough. You should read
|
||||
* its docs, and keep in mind that it calls stores "reducers" and the
|
||||
* dispatcher instance is called a single store.
|
||||
* http://rackt.github.io/redux/
|
||||
*/
|
||||
function createDispatcher(stores) {
|
||||
const state = {};
|
||||
const listeners = {};
|
||||
let enqueuedChanges = [];
|
||||
let isDispatching = false;
|
||||
|
||||
// Validate the stores to make sure they have the right shape,
|
||||
// and accumulate the initial state
|
||||
entries(stores).forEach(([name, store]) => {
|
||||
if (!store || typeof store.update !== "function") {
|
||||
throw new Error("Error creating dispatcher: store \"" + name +
|
||||
"\" does not have an `update` function");
|
||||
}
|
||||
|
||||
state[name] = store.update(undefined, {});
|
||||
});
|
||||
|
||||
function getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
function emitChange(storeName, dataName, payload) {
|
||||
enqueuedChanges.push([storeName, dataName, payload]);
|
||||
}
|
||||
|
||||
function onChange(paths, view) {
|
||||
entries(paths).forEach(([storeName, data]) => {
|
||||
if (!stores[storeName]) {
|
||||
throw new Error("Error adding onChange handler to store: store " +
|
||||
"\"" + storeName + "\" does not exist");
|
||||
}
|
||||
|
||||
if (!listeners[storeName]) {
|
||||
listeners[storeName] = [];
|
||||
}
|
||||
|
||||
if (typeof data == 'function') {
|
||||
listeners[storeName].push(data.bind(view));
|
||||
}
|
||||
else {
|
||||
entries(data).forEach(([watchedName, handler]) => {
|
||||
listeners[storeName].push((payload, dataName, storeName) => {
|
||||
if (dataName === watchedName) {
|
||||
handler.call(view, payload, dataName, storeName);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush any enqueued state changes from the dispatch cycle. Listeners
|
||||
* are not immediately notified of changes, only after dispatching
|
||||
* is completed, to ensure that all state is consistent (in the case
|
||||
* of multiple stores changes at once).
|
||||
*/
|
||||
function flushChanges() {
|
||||
enqueuedChanges.forEach(([storeName, dataName, payload]) => {
|
||||
if (listeners[storeName]) {
|
||||
listeners[storeName].forEach(listener => {
|
||||
listener(payload, dataName, storeName);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
enqueuedChanges = [];
|
||||
}
|
||||
|
||||
function dispatch(action) {
|
||||
if (isDispatching) {
|
||||
throw new Error('Cannot dispatch in the middle of a dispatch');
|
||||
}
|
||||
if (!action.type) {
|
||||
throw new Error(
|
||||
'action type is null, ' +
|
||||
'did you make a typo when publishing this action? ' +
|
||||
JSON.stringify(action, null, 2)
|
||||
);
|
||||
}
|
||||
|
||||
isDispatching = true;
|
||||
try {
|
||||
entries(stores).forEach(([name, store]) => {
|
||||
state[name] = store.update(
|
||||
state[name],
|
||||
action,
|
||||
emitChange.bind(null, name)
|
||||
);
|
||||
});
|
||||
}
|
||||
finally {
|
||||
isDispatching = false;
|
||||
}
|
||||
|
||||
flushChanges();
|
||||
}
|
||||
|
||||
return {
|
||||
getState,
|
||||
dispatch,
|
||||
onChange
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createDispatcher: createDispatcher,
|
||||
applyMiddleware: applyMiddleware
|
||||
};
|
@ -1,28 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
const CC = Components.Constructor;
|
||||
|
||||
const { require } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
const createDispatcher = require('devtools/shared/create-dispatcher')({ log: true });
|
||||
const waitUntilService = require('devtools/shared/fluxify/waitUntilService');
|
||||
const services = {
|
||||
WAIT_UNTIL: waitUntilService.name
|
||||
};
|
||||
|
||||
const Services = require("Services");
|
||||
const { waitForTick, waitForTime } = require("devtools/toolkit/DevToolsUtils");
|
||||
|
||||
var loadSubScript = Cc[
|
||||
'@mozilla.org/moz/jssubscript-loader;1'
|
||||
].getService(Ci.mozIJSSubScriptLoader).loadSubScript;
|
||||
|
||||
function getFileUrl(name, allowMissing=false) {
|
||||
let file = do_get_file(name, allowMissing);
|
||||
return Services.io.newFileURI(file).spec;
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// This file should be loaded with `loadSubScript` because it contains
|
||||
// stateful stores that need a new instance per test.
|
||||
|
||||
const NumberStore = {
|
||||
update: (state = 1, action, emitChange) => {
|
||||
switch(action.type) {
|
||||
case constants.ADD_NUMBER: {
|
||||
const newState = state + action.value;
|
||||
emitChange('number', newState);
|
||||
return newState;
|
||||
}
|
||||
case constants.DOUBLE_NUMBER: {
|
||||
const newState = state * 2;
|
||||
emitChange('number', newState);
|
||||
return newState;
|
||||
}}
|
||||
|
||||
return state;
|
||||
},
|
||||
|
||||
constants: {
|
||||
ADD_NUMBER: 'ADD_NUMBER',
|
||||
DOUBLE_NUMBER: 'DOUBLE_NUMBER'
|
||||
}
|
||||
};
|
||||
|
||||
const itemsInitialState = {
|
||||
list: []
|
||||
};
|
||||
const ItemStore = {
|
||||
update: (state = itemsInitialState, action, emitChange) => {
|
||||
switch(action.type) {
|
||||
case constants.ADD_ITEM:
|
||||
state.list.push(action.item);
|
||||
emitChange('list', state.list);
|
||||
return state;
|
||||
}
|
||||
|
||||
return state;
|
||||
},
|
||||
|
||||
constants: {
|
||||
ADD_ITEM: 'ADD_ITEM'
|
||||
}
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
loadSubScript(getFileUrl("stores-for-testing.js"));
|
||||
const constants = Object.assign(
|
||||
{},
|
||||
NumberStore.constants,
|
||||
ItemStore.constants
|
||||
);
|
||||
const stores = { number: NumberStore,
|
||||
items: ItemStore };
|
||||
|
||||
function addNumber(num) {
|
||||
return {
|
||||
type: constants.ADD_NUMBER,
|
||||
value: num
|
||||
}
|
||||
}
|
||||
|
||||
function addItem(item) {
|
||||
return {
|
||||
type: constants.ADD_ITEM,
|
||||
item: item
|
||||
};
|
||||
}
|
||||
|
||||
// Tests
|
||||
|
||||
function run_test() {
|
||||
testInitialValue();
|
||||
testDispatch();
|
||||
testEmitChange();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function testInitialValue() {
|
||||
do_print("Testing initial value");
|
||||
const dispatcher = createDispatcher(stores);
|
||||
equal(dispatcher.getState().number, 1);
|
||||
}
|
||||
|
||||
function testDispatch() {
|
||||
do_print("Testing dispatch");
|
||||
|
||||
const dispatcher = createDispatcher(stores);
|
||||
dispatcher.dispatch(addNumber(5));
|
||||
equal(dispatcher.getState().number, 6);
|
||||
|
||||
dispatcher.dispatch(addNumber(2));
|
||||
equal(dispatcher.getState().number, 8);
|
||||
|
||||
// It should ignore unknown action types
|
||||
dispatcher.dispatch({ type: "FOO" });
|
||||
equal(dispatcher.getState().number, 8);
|
||||
}
|
||||
|
||||
function testEmitChange() {
|
||||
do_print("Testing change emittters");
|
||||
const dispatcher = createDispatcher(stores);
|
||||
let listenerRan = false;
|
||||
|
||||
const numberView = {
|
||||
x: 3,
|
||||
renderNumber: function(num) {
|
||||
ok(this.x, 3, "listener ran in context of view");
|
||||
ok(num, 10);
|
||||
listenerRan = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Views can listen to changes in state by specifying which part of
|
||||
// the state to listen to and giving a handler function. The
|
||||
// function will be run with the view as `this`.
|
||||
dispatcher.onChange({
|
||||
"number": numberView.renderNumber
|
||||
}, numberView);
|
||||
|
||||
dispatcher.dispatch(addNumber(9));
|
||||
ok(listenerRan, "number listener actually ran");
|
||||
listenerRan = false;
|
||||
|
||||
const itemsView = {
|
||||
renderList: function(items) {
|
||||
ok(items.length, 1);
|
||||
ok(items[0].name = "james");
|
||||
listenerRan = true;
|
||||
}
|
||||
}
|
||||
|
||||
// You can listen to deeper sections of the state by nesting objects
|
||||
// to specify the path to that state. You can do this 1 level deep;
|
||||
// you cannot arbitrarily nest state listeners.
|
||||
dispatcher.onChange({
|
||||
"items": {
|
||||
"list": itemsView.renderList
|
||||
}
|
||||
}, itemsView);
|
||||
|
||||
dispatcher.dispatch(addItem({ name: "james" }));
|
||||
ok(listenerRan, "items listener actually ran");
|
||||
}
|
@ -1,114 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
loadSubScript(getFileUrl('stores-for-testing.js'));
|
||||
const constants = NumberStore.constants;
|
||||
const stores = { number: NumberStore };
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* testThunkDispatch() {
|
||||
do_print("Testing thunk dispatch");
|
||||
// The thunk middleware allows you to return a function from an
|
||||
// action creator which takes `dispatch` and `getState` functions as
|
||||
// arguments. This allows the action creator to fire multiple
|
||||
// actions manually with `dispatch`, possibly asynchronously.
|
||||
|
||||
function addNumberLater(num) {
|
||||
return dispatch => {
|
||||
// Just do it in the next tick, no need to wait too long
|
||||
waitForTick().then(() => {
|
||||
dispatch({
|
||||
type: constants.ADD_NUMBER,
|
||||
value: num
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function addNumber(num) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: constants.ADD_NUMBER,
|
||||
value: getState().number > 10 ? (num * 2) : num
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
const dispatcher = createDispatcher(stores);
|
||||
equal(dispatcher.getState().number, 1);
|
||||
dispatcher.dispatch(addNumberLater(5));
|
||||
equal(dispatcher.getState().number, 1, "state should not have changed");
|
||||
yield waitForTick();
|
||||
equal(dispatcher.getState().number, 6, "state should have changed");
|
||||
|
||||
dispatcher.dispatch(addNumber(5));
|
||||
equal(dispatcher.getState().number, 11);
|
||||
dispatcher.dispatch(addNumber(2));
|
||||
// 2 * 2 should have actually been added because the state is
|
||||
// greater than 10 (the action creator changes the value based on
|
||||
// the current state)
|
||||
equal(dispatcher.getState().number, 15);
|
||||
});
|
||||
|
||||
add_task(function* testWaitUntilService() {
|
||||
do_print("Testing waitUntil service");
|
||||
// The waitUntil service allows you to queue functions to be run at a
|
||||
// later time, depending on a predicate. As actions comes through
|
||||
// the system, you predicate will be called with each action. Once
|
||||
// your predicate returns true, the queued function will be run and
|
||||
// removed from the pending queue.
|
||||
|
||||
function addWhenDoubled(num) {
|
||||
return {
|
||||
type: services.WAIT_UNTIL,
|
||||
predicate: action => action.type === constants.DOUBLE_NUMBER,
|
||||
run: (dispatch, getState, action) => {
|
||||
ok(action.type, constants.DOUBLE_NUMBER);
|
||||
ok(getState(), 10);
|
||||
|
||||
dispatch({
|
||||
type: constants.ADD_NUMBER,
|
||||
value: 2
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function addWhenGreaterThan(threshold, num) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: services.WAIT_UNTIL,
|
||||
predicate: () => getState().number > threshold,
|
||||
run: () => {
|
||||
dispatch({
|
||||
type: constants.ADD_NUMBER,
|
||||
value: num
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const dispatcher = createDispatcher(stores);
|
||||
|
||||
// Add a pending action that adds 2 after the number is doubled
|
||||
equal(dispatcher.getState().number, 1);
|
||||
dispatcher.dispatch(addWhenDoubled(2));
|
||||
equal(dispatcher.getState().number, 1);
|
||||
dispatcher.dispatch({ type: constants.DOUBLE_NUMBER });
|
||||
// Note how the pending function we added ran synchronously. It
|
||||
// should have added 2 after doubling 1, so 1 * 2 + 2 = 4
|
||||
equal(dispatcher.getState().number, 4);
|
||||
|
||||
// Add a pending action that adds 5 once the number is greater than 10
|
||||
dispatcher.dispatch(addWhenGreaterThan(10, 5));
|
||||
equal(dispatcher.getState().number, 4);
|
||||
dispatcher.dispatch({ type: constants.ADD_NUMBER, value: 10 });
|
||||
// Again, the pending function we added ran synchronously. It should
|
||||
// have added 5 more after 10 was added, since the number was
|
||||
// greater than 10.
|
||||
equal(dispatcher.getState().number, 19);
|
||||
});
|
@ -1,12 +0,0 @@
|
||||
[DEFAULT]
|
||||
tags = devtools
|
||||
head = head.js
|
||||
tail =
|
||||
firefox-appdir = browser
|
||||
skip-if = toolkit == 'android' || toolkit == 'gonk'
|
||||
|
||||
support-files =
|
||||
stores-for-testing.js
|
||||
|
||||
[test_dispatcher.js]
|
||||
[test_middlewares.js]
|
@ -7,6 +7,11 @@
|
||||
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
|
||||
XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
|
||||
|
||||
DIRS += [
|
||||
'redux',
|
||||
'vendor',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES.devtools += [
|
||||
'AppCacheUtils.jsm',
|
||||
'Curl.jsm',
|
||||
@ -32,7 +37,6 @@ EXTRA_JS_MODULES.devtools += [
|
||||
EXTRA_JS_MODULES.devtools.shared += [
|
||||
'autocomplete-popup.js',
|
||||
'browser-loader.js',
|
||||
'create-dispatcher.js',
|
||||
'devices.js',
|
||||
'doorhanger.js',
|
||||
'frame-script-utils.js',
|
||||
@ -49,15 +53,6 @@ EXTRA_JS_MODULES.devtools.shared += [
|
||||
'undo.js'
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES.devtools.shared.content += [
|
||||
'content/react.js'
|
||||
]
|
||||
|
||||
if CONFIG['DEBUG_JS_MODULES']:
|
||||
EXTRA_JS_MODULES.devtools.shared.content += [
|
||||
'content/react-dev.js'
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES.devtools.shared.widgets += [
|
||||
'widgets/BarGraphWidget.js',
|
||||
'widgets/CubicBezierPresets.js',
|
||||
@ -74,5 +69,3 @@ EXTRA_JS_MODULES.devtools.shared.widgets += [
|
||||
'widgets/Tooltip.js',
|
||||
'widgets/TreeWidget.js',
|
||||
]
|
||||
|
||||
DIRS += ['fluxify']
|
||||
|
@ -3,11 +3,10 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const fluxify = require('./fluxify/dispatcher');
|
||||
const thunkMiddleware = require('./fluxify/thunkMiddleware');
|
||||
const logMiddleware = require('./fluxify/logMiddleware');
|
||||
const waitUntilService = require('./fluxify/waitUntilService')
|
||||
const { compose } = require('devtools/toolkit/DevToolsUtils');
|
||||
const { createStore, applyMiddleware } = require("devtools/shared/vendor/redux");
|
||||
const { thunk } = require("./middleware/thunk");
|
||||
const { waitUntilService } = require("./middleware/wait-service");
|
||||
const { log } = require("./middleware/log");
|
||||
|
||||
/**
|
||||
* This creates a dispatcher with all the standard middleware in place
|
||||
@ -16,16 +15,21 @@ const { compose } = require('devtools/toolkit/DevToolsUtils');
|
||||
*
|
||||
* @param {object} opts - boolean configuration flags
|
||||
* - log: log all dispatched actions to console
|
||||
* - middleware: array of middleware to be included in the redux store
|
||||
*/
|
||||
module.exports = (opts={}) => {
|
||||
const middleware = [
|
||||
thunkMiddleware,
|
||||
waitUntilService.service
|
||||
thunk,
|
||||
waitUntilService
|
||||
];
|
||||
|
||||
if (opts.log) {
|
||||
middleware.push(logMiddleware);
|
||||
middleware.push(log);
|
||||
}
|
||||
|
||||
return fluxify.applyMiddleware(...middleware)(fluxify.createDispatcher);
|
||||
if (opts.middleware) {
|
||||
opts.middleware.forEach(fn => middleware.push(fn));
|
||||
}
|
||||
|
||||
return applyMiddleware(...middleware)(createStore);
|
||||
}
|
@ -7,11 +7,11 @@
|
||||
* A middleware that logs all actions coming through the system
|
||||
* to the console.
|
||||
*/
|
||||
function logMiddleware({ dispatch, getState }) {
|
||||
function log({ dispatch, getState }) {
|
||||
return next => action => {
|
||||
console.log('[DISPATCH]', JSON.stringify(action));
|
||||
console.log("[DISPATCH]", JSON.stringify(action));
|
||||
next(action);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = logMiddleware;
|
||||
exports.log = log;
|
@ -9,12 +9,11 @@
|
||||
* allowing the action to create multiple actions (most likely
|
||||
* asynchronously).
|
||||
*/
|
||||
function thunkMiddleware({ dispatch, getState }) {
|
||||
function thunk({ dispatch, getState }) {
|
||||
return next => action => {
|
||||
return typeof action === "function"
|
||||
? action(dispatch, getState)
|
||||
: next(action);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = thunkMiddleware;
|
||||
exports.thunk = thunk;
|
@ -3,8 +3,6 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const NAME = "@@service/waitUntil";
|
||||
|
||||
/**
|
||||
* A middleware which acts like a service, because it is stateful
|
||||
* and "long-running" in the background. It provides the ability
|
||||
@ -13,7 +11,7 @@ const NAME = "@@service/waitUntil";
|
||||
* it as a thunk that blocks until the condition is met. Example:
|
||||
*
|
||||
* ```js
|
||||
* const services = { WAIT_UNTIL: require('waitUntilService').name };
|
||||
* const services = { WAIT_UNTIL: require('wait-service').NAME };
|
||||
*
|
||||
* { type: services.WAIT_UNTIL,
|
||||
* predicate: action => action.type === constants.ADD_ITEM,
|
||||
@ -25,6 +23,8 @@ const NAME = "@@service/waitUntil";
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
const NAME = exports.NAME = "@@service/waitUntil";
|
||||
|
||||
function waitUntilService({ dispatch, getState }) {
|
||||
let pending = [];
|
||||
|
||||
@ -62,8 +62,4 @@ function waitUntilService({ dispatch, getState }) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
service: waitUntilService,
|
||||
name: NAME
|
||||
};
|
||||
exports.waitUntilService = waitUntilService;
|
16
browser/devtools/shared/redux/moz.build
Normal file
16
browser/devtools/shared/redux/moz.build
Normal file
@ -0,0 +1,16 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
EXTRA_JS_MODULES.devtools.shared.redux += [
|
||||
'create-store.js',
|
||||
'reducers.js',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES.devtools.shared.redux.middleware += [
|
||||
'middleware/log.js',
|
||||
'middleware/thunk.js',
|
||||
'middleware/wait-service.js',
|
||||
]
|
33
browser/devtools/shared/redux/reducers.js
Normal file
33
browser/devtools/shared/redux/reducers.js
Normal file
@ -0,0 +1,33 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const { combineReducers } = require("devtools/shared/vendor/redux");
|
||||
|
||||
/**
|
||||
* Function that takes a hash of reducers, like `combineReducers`,
|
||||
* and an `emit` function and returns a function to be used as a reducer
|
||||
* for a Redux store. This allows all reducers defined here to receive
|
||||
* a third argument, the `emit` function, for event-based subscriptions
|
||||
* from within reducers.
|
||||
*
|
||||
* @param {Object} reducers
|
||||
* @param {Function} emit
|
||||
* @return {Function}
|
||||
*/
|
||||
function combineEmittingReducers (reducers, emit) {
|
||||
// Wrap each reducer with a wrapper function that calls
|
||||
// the reducer with a third argument, an `emit` function.
|
||||
// Use this rather than a new custom top level reducer that would ultimately
|
||||
// have to replicate redux's `combineReducers` so we only pass in correct state,
|
||||
// the error checking, and other edge cases.
|
||||
function wrapReduce (newReducers, key) {
|
||||
newReducers[key] = (state, action) => reducers[key](state, action, emit);
|
||||
return newReducers;
|
||||
}
|
||||
|
||||
return combineReducers(Object.keys(reducers).reduce(wrapReduce, Object.create(null)));
|
||||
}
|
||||
|
||||
exports.combineEmittingReducers = combineEmittingReducers;
|
21
browser/devtools/shared/vendor/REDUX_LICENSE
vendored
Normal file
21
browser/devtools/shared/vendor/REDUX_LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Dan Abramov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@ -4,12 +4,12 @@
|
||||
# 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/.
|
||||
|
||||
EXTRA_JS_MODULES.devtools.shared.fluxify += [
|
||||
'bindActionCreators.js',
|
||||
'dispatcher.js',
|
||||
'logMiddleware.js',
|
||||
'thunkMiddleware.js',
|
||||
'waitUntilService.js'
|
||||
EXTRA_JS_MODULES.devtools.shared.vendor += [
|
||||
'react.js',
|
||||
'redux.js',
|
||||
]
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
|
||||
if CONFIG['DEBUG_JS_MODULES']:
|
||||
EXTRA_JS_MODULES.devtools.shared.vendor += [
|
||||
'content/react-dev.js'
|
||||
]
|
610
browser/devtools/shared/vendor/redux.js
vendored
Normal file
610
browser/devtools/shared/vendor/redux.js
vendored
Normal file
@ -0,0 +1,610 @@
|
||||
(function webpackUniversalModuleDefinition(root, factory) {
|
||||
if(typeof exports === 'object' && typeof module === 'object')
|
||||
module.exports = factory();
|
||||
else if(typeof define === 'function' && define.amd)
|
||||
define([], factory);
|
||||
else if(typeof exports === 'object')
|
||||
exports["Redux"] = factory();
|
||||
else
|
||||
root["Redux"] = factory();
|
||||
})(this, function() {
|
||||
return /******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId])
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ exports: {},
|
||||
/******/ id: moduleId,
|
||||
/******/ loaded: false
|
||||
/******/ };
|
||||
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.loaded = true;
|
||||
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
|
||||
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "";
|
||||
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(0);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ([
|
||||
/* 0 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
|
||||
'use strict';
|
||||
|
||||
exports.__esModule = true;
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
|
||||
|
||||
var _createStore = __webpack_require__(1);
|
||||
|
||||
var _createStore2 = _interopRequireDefault(_createStore);
|
||||
|
||||
var _utilsCombineReducers = __webpack_require__(7);
|
||||
|
||||
var _utilsCombineReducers2 = _interopRequireDefault(_utilsCombineReducers);
|
||||
|
||||
var _utilsBindActionCreators = __webpack_require__(6);
|
||||
|
||||
var _utilsBindActionCreators2 = _interopRequireDefault(_utilsBindActionCreators);
|
||||
|
||||
var _utilsApplyMiddleware = __webpack_require__(5);
|
||||
|
||||
var _utilsApplyMiddleware2 = _interopRequireDefault(_utilsApplyMiddleware);
|
||||
|
||||
var _utilsCompose = __webpack_require__(2);
|
||||
|
||||
var _utilsCompose2 = _interopRequireDefault(_utilsCompose);
|
||||
|
||||
exports.createStore = _createStore2['default'];
|
||||
exports.combineReducers = _utilsCombineReducers2['default'];
|
||||
exports.bindActionCreators = _utilsBindActionCreators2['default'];
|
||||
exports.applyMiddleware = _utilsApplyMiddleware2['default'];
|
||||
exports.compose = _utilsCompose2['default'];
|
||||
|
||||
/***/ },
|
||||
/* 1 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
|
||||
'use strict';
|
||||
|
||||
exports.__esModule = true;
|
||||
exports['default'] = createStore;
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
|
||||
|
||||
var _utilsIsPlainObject = __webpack_require__(3);
|
||||
|
||||
var _utilsIsPlainObject2 = _interopRequireDefault(_utilsIsPlainObject);
|
||||
|
||||
/**
|
||||
* These are private action types reserved by Redux.
|
||||
* For any unknown actions, you must return the current state.
|
||||
* If the current state is undefined, you must return the initial state.
|
||||
* Do not reference these action types directly in your code.
|
||||
*/
|
||||
var ActionTypes = {
|
||||
INIT: '@@redux/INIT'
|
||||
};
|
||||
|
||||
exports.ActionTypes = ActionTypes;
|
||||
/**
|
||||
* Creates a Redux store that holds the state tree.
|
||||
* The only way to change the data in the store is to call `dispatch()` on it.
|
||||
*
|
||||
* There should only be a single store in your app. To specify how different
|
||||
* parts of the state tree respond to actions, you may combine several reducers
|
||||
* into a single reducer function by using `combineReducers`.
|
||||
*
|
||||
* @param {Function} reducer A function that returns the next state tree, given
|
||||
* the current state tree and the action to handle.
|
||||
*
|
||||
* @param {any} [initialState] The initial state. You may optionally specify it
|
||||
* to hydrate the state from the server in universal apps, or to restore a
|
||||
* previously serialized user session.
|
||||
* If you use `combineReducers` to produce the root reducer function, this must be
|
||||
* an object with the same shape as `combineReducers` keys.
|
||||
*
|
||||
* @returns {Store} A Redux store that lets you read the state, dispatch actions
|
||||
* and subscribe to changes.
|
||||
*/
|
||||
|
||||
function createStore(reducer, initialState) {
|
||||
if (typeof reducer !== 'function') {
|
||||
throw new Error('Expected the reducer to be a function.');
|
||||
}
|
||||
|
||||
var currentReducer = reducer;
|
||||
var currentState = initialState;
|
||||
var listeners = [];
|
||||
var isDispatching = false;
|
||||
|
||||
/**
|
||||
* Reads the state tree managed by the store.
|
||||
*
|
||||
* @returns {any} The current state tree of your application.
|
||||
*/
|
||||
function getState() {
|
||||
return currentState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a change listener. It will be called any time an action is dispatched,
|
||||
* and some part of the state tree may potentially have changed. You may then
|
||||
* call `getState()` to read the current state tree inside the callback.
|
||||
*
|
||||
* @param {Function} listener A callback to be invoked on every dispatch.
|
||||
* @returns {Function} A function to remove this change listener.
|
||||
*/
|
||||
function subscribe(listener) {
|
||||
listeners.push(listener);
|
||||
|
||||
return function unsubscribe() {
|
||||
var index = listeners.indexOf(listener);
|
||||
listeners.splice(index, 1);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an action. It is the only way to trigger a state change.
|
||||
*
|
||||
* The `reducer` function, used to create the store, will be called with the
|
||||
* current state tree and the given `action`. Its return value will
|
||||
* be considered the **next** state of the tree, and the change listeners
|
||||
* will be notified.
|
||||
*
|
||||
* The base implementation only supports plain object actions. If you want to
|
||||
* dispatch a Promise, an Observable, a thunk, or something else, you need to
|
||||
* wrap your store creating function into the corresponding middleware. For
|
||||
* example, see the documentation for the `redux-thunk` package. Even the
|
||||
* middleware will eventually dispatch plain object actions using this method.
|
||||
*
|
||||
* @param {Object} action A plain object representing “what changed”. It is
|
||||
* a good idea to keep actions serializable so you can record and replay user
|
||||
* sessions, or use the time travelling `redux-devtools`.
|
||||
*
|
||||
* @returns {Object} For convenience, the same action object you dispatched.
|
||||
*
|
||||
* Note that, if you use a custom middleware, it may wrap `dispatch()` to
|
||||
* return something else (for example, a Promise you can await).
|
||||
*/
|
||||
function dispatch(action) {
|
||||
if (!_utilsIsPlainObject2['default'](action)) {
|
||||
throw new Error('Actions must be plain objects. Use custom middleware for async actions.');
|
||||
}
|
||||
|
||||
if (isDispatching) {
|
||||
throw new Error('Reducers may not dispatch actions.');
|
||||
}
|
||||
|
||||
try {
|
||||
isDispatching = true;
|
||||
currentState = currentReducer(currentState, action);
|
||||
} finally {
|
||||
isDispatching = false;
|
||||
}
|
||||
|
||||
listeners.slice().forEach(function (listener) {
|
||||
return listener();
|
||||
});
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the reducer currently used by the store to calculate the state.
|
||||
*
|
||||
* You might need this if your app implements code splitting and you want to
|
||||
* load some of the reducers dynamically. You might also need this if you
|
||||
* implement a hot reloading mechanism for Redux.
|
||||
*
|
||||
* @param {Function} nextReducer The reducer for the store to use instead.
|
||||
* @returns {void}
|
||||
*/
|
||||
function replaceReducer(nextReducer) {
|
||||
currentReducer = nextReducer;
|
||||
dispatch({ type: ActionTypes.INIT });
|
||||
}
|
||||
|
||||
// When a store is created, an "INIT" action is dispatched so that every
|
||||
// reducer returns their initial state. This effectively populates
|
||||
// the initial state tree.
|
||||
dispatch({ type: ActionTypes.INIT });
|
||||
|
||||
return {
|
||||
dispatch: dispatch,
|
||||
subscribe: subscribe,
|
||||
getState: getState,
|
||||
replaceReducer: replaceReducer
|
||||
};
|
||||
}
|
||||
|
||||
/***/ },
|
||||
/* 2 */
|
||||
/***/ function(module, exports) {
|
||||
|
||||
/**
|
||||
* Composes single-argument functions from right to left.
|
||||
*
|
||||
* @param {...Function} funcs The functions to compose.
|
||||
* @returns {Function} A function obtained by composing functions from right to
|
||||
* left. For example, compose(f, g, h) is identical to x => h(g(f(x))).
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
exports.__esModule = true;
|
||||
exports["default"] = compose;
|
||||
|
||||
function compose() {
|
||||
for (var _len = arguments.length, funcs = Array(_len), _key = 0; _key < _len; _key++) {
|
||||
funcs[_key] = arguments[_key];
|
||||
}
|
||||
|
||||
return function (arg) {
|
||||
return funcs.reduceRight(function (composed, f) {
|
||||
return f(composed);
|
||||
}, arg);
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = exports["default"];
|
||||
|
||||
/***/ },
|
||||
/* 3 */
|
||||
/***/ function(module, exports) {
|
||||
|
||||
'use strict';
|
||||
|
||||
exports.__esModule = true;
|
||||
exports['default'] = isPlainObject;
|
||||
var fnToString = function fnToString(fn) {
|
||||
return Function.prototype.toString.call(fn);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {any} obj The object to inspect.
|
||||
* @returns {boolean} True if the argument appears to be a plain object.
|
||||
*/
|
||||
|
||||
function isPlainObject(obj) {
|
||||
if (!obj || typeof obj !== 'object') {
|
||||
return false;
|
||||
}
|
||||
|
||||
var proto = typeof obj.constructor === 'function' ? Object.getPrototypeOf(obj) : Object.prototype;
|
||||
|
||||
if (proto === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var constructor = proto.constructor;
|
||||
|
||||
return typeof constructor === 'function' && constructor instanceof constructor && fnToString(constructor) === fnToString(Object);
|
||||
}
|
||||
|
||||
module.exports = exports['default'];
|
||||
|
||||
/***/ },
|
||||
/* 4 */
|
||||
/***/ function(module, exports) {
|
||||
|
||||
/**
|
||||
* Applies a function to every key-value pair inside an object.
|
||||
*
|
||||
* @param {Object} obj The source object.
|
||||
* @param {Function} fn The mapper function that receives the value and the key.
|
||||
* @returns {Object} A new object that contains the mapped values for the keys.
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
exports.__esModule = true;
|
||||
exports["default"] = mapValues;
|
||||
|
||||
function mapValues(obj, fn) {
|
||||
return Object.keys(obj).reduce(function (result, key) {
|
||||
result[key] = fn(obj[key], key);
|
||||
return result;
|
||||
}, {});
|
||||
}
|
||||
|
||||
module.exports = exports["default"];
|
||||
|
||||
/***/ },
|
||||
/* 5 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
|
||||
'use strict';
|
||||
|
||||
exports.__esModule = true;
|
||||
|
||||
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
||||
|
||||
exports['default'] = applyMiddleware;
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
|
||||
|
||||
var _compose = __webpack_require__(2);
|
||||
|
||||
var _compose2 = _interopRequireDefault(_compose);
|
||||
|
||||
/**
|
||||
* Creates a store enhancer that applies middleware to the dispatch method
|
||||
* of the Redux store. This is handy for a variety of tasks, such as expressing
|
||||
* asynchronous actions in a concise manner, or logging every action payload.
|
||||
*
|
||||
* See `redux-thunk` package as an example of the Redux middleware.
|
||||
*
|
||||
* Because middleware is potentially asynchronous, this should be the first
|
||||
* store enhancer in the composition chain.
|
||||
*
|
||||
* Note that each middleware will be given the `dispatch` and `getState` functions
|
||||
* as named arguments.
|
||||
*
|
||||
* @param {...Function} middlewares The middleware chain to be applied.
|
||||
* @returns {Function} A store enhancer applying the middleware.
|
||||
*/
|
||||
|
||||
function applyMiddleware() {
|
||||
for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) {
|
||||
middlewares[_key] = arguments[_key];
|
||||
}
|
||||
|
||||
return function (next) {
|
||||
return function (reducer, initialState) {
|
||||
var store = next(reducer, initialState);
|
||||
var _dispatch = store.dispatch;
|
||||
var chain = [];
|
||||
|
||||
var middlewareAPI = {
|
||||
getState: store.getState,
|
||||
dispatch: function dispatch(action) {
|
||||
return _dispatch(action);
|
||||
}
|
||||
};
|
||||
chain = middlewares.map(function (middleware) {
|
||||
return middleware(middlewareAPI);
|
||||
});
|
||||
_dispatch = _compose2['default'].apply(undefined, chain)(store.dispatch);
|
||||
|
||||
return _extends({}, store, {
|
||||
dispatch: _dispatch
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = exports['default'];
|
||||
|
||||
/***/ },
|
||||
/* 6 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
|
||||
'use strict';
|
||||
|
||||
exports.__esModule = true;
|
||||
exports['default'] = bindActionCreators;
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
|
||||
|
||||
var _utilsMapValues = __webpack_require__(4);
|
||||
|
||||
var _utilsMapValues2 = _interopRequireDefault(_utilsMapValues);
|
||||
|
||||
function bindActionCreator(actionCreator, dispatch) {
|
||||
return function () {
|
||||
return dispatch(actionCreator.apply(undefined, arguments));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns an object whose values are action creators, into an object with the
|
||||
* same keys, but with every function wrapped into a `dispatch` call so they
|
||||
* may be invoked directly. This is just a convenience method, as you can call
|
||||
* `store.dispatch(MyActionCreators.doSomething())` yourself just fine.
|
||||
*
|
||||
* For convenience, you can also pass a single function as the first argument,
|
||||
* and get a function in return.
|
||||
*
|
||||
* @param {Function|Object} actionCreators An object whose values are action
|
||||
* creator functions. One handy way to obtain it is to use ES6 `import * as`
|
||||
* syntax. You may also pass a single function.
|
||||
*
|
||||
* @param {Function} dispatch The `dispatch` function available on your Redux
|
||||
* store.
|
||||
*
|
||||
* @returns {Function|Object} The object mimicking the original object, but with
|
||||
* every action creator wrapped into the `dispatch` call. If you passed a
|
||||
* function as `actionCreators`, the return value will also be a single
|
||||
* function.
|
||||
*/
|
||||
|
||||
function bindActionCreators(actionCreators, dispatch) {
|
||||
if (typeof actionCreators === 'function') {
|
||||
return bindActionCreator(actionCreators, dispatch);
|
||||
}
|
||||
|
||||
if (typeof actionCreators !== 'object' || actionCreators == null) {
|
||||
// eslint-disable-line no-eq-null
|
||||
throw new Error('bindActionCreators expected an object or a function, instead received ' + typeof actionCreators + '. ' + 'Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?');
|
||||
}
|
||||
|
||||
return _utilsMapValues2['default'](actionCreators, function (actionCreator) {
|
||||
return bindActionCreator(actionCreator, dispatch);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = exports['default'];
|
||||
|
||||
/***/ },
|
||||
/* 7 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
|
||||
'use strict';
|
||||
|
||||
exports.__esModule = true;
|
||||
exports['default'] = combineReducers;
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
|
||||
|
||||
var _createStore = __webpack_require__(1);
|
||||
|
||||
var _utilsIsPlainObject = __webpack_require__(3);
|
||||
|
||||
var _utilsIsPlainObject2 = _interopRequireDefault(_utilsIsPlainObject);
|
||||
|
||||
var _utilsMapValues = __webpack_require__(4);
|
||||
|
||||
var _utilsMapValues2 = _interopRequireDefault(_utilsMapValues);
|
||||
|
||||
var _utilsPick = __webpack_require__(8);
|
||||
|
||||
var _utilsPick2 = _interopRequireDefault(_utilsPick);
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
function getErrorMessage(key, action) {
|
||||
var actionType = action && action.type;
|
||||
var actionName = actionType && '"' + actionType.toString() + '"' || 'an action';
|
||||
|
||||
return 'Reducer "' + key + '" returned undefined handling ' + actionName + '. ' + 'To ignore an action, you must explicitly return the previous state.';
|
||||
}
|
||||
|
||||
function verifyStateShape(initialState, currentState) {
|
||||
var reducerKeys = Object.keys(currentState);
|
||||
|
||||
if (reducerKeys.length === 0) {
|
||||
console.error('Store does not have a valid reducer. Make sure the argument passed ' + 'to combineReducers is an object whose values are reducers.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_utilsIsPlainObject2['default'](initialState)) {
|
||||
console.error('initialState has unexpected type of "' + ({}).toString.call(initialState).match(/\s([a-z|A-Z]+)/)[1] + '". Expected initialState to be an object with the following ' + ('keys: "' + reducerKeys.join('", "') + '"'));
|
||||
return;
|
||||
}
|
||||
|
||||
var unexpectedKeys = Object.keys(initialState).filter(function (key) {
|
||||
return reducerKeys.indexOf(key) < 0;
|
||||
});
|
||||
|
||||
if (unexpectedKeys.length > 0) {
|
||||
console.error('Unexpected ' + (unexpectedKeys.length > 1 ? 'keys' : 'key') + ' ' + ('"' + unexpectedKeys.join('", "') + '" in initialState will be ignored. ') + ('Expected to find one of the known reducer keys instead: "' + reducerKeys.join('", "') + '"'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns an object whose values are different reducer functions, into a single
|
||||
* reducer function. It will call every child reducer, and gather their results
|
||||
* into a single state object, whose keys correspond to the keys of the passed
|
||||
* reducer functions.
|
||||
*
|
||||
* @param {Object} reducers An object whose values correspond to different
|
||||
* reducer functions that need to be combined into one. One handy way to obtain
|
||||
* it is to use ES6 `import * as reducers` syntax. The reducers may never return
|
||||
* undefined for any action. Instead, they should return their initial state
|
||||
* if the state passed to them was undefined, and the current state for any
|
||||
* unrecognized action.
|
||||
*
|
||||
* @returns {Function} A reducer function that invokes every reducer inside the
|
||||
* passed object, and builds a state object with the same shape.
|
||||
*/
|
||||
|
||||
function combineReducers(reducers) {
|
||||
var finalReducers = _utilsPick2['default'](reducers, function (val) {
|
||||
return typeof val === 'function';
|
||||
});
|
||||
|
||||
Object.keys(finalReducers).forEach(function (key) {
|
||||
var reducer = finalReducers[key];
|
||||
if (typeof reducer(undefined, { type: _createStore.ActionTypes.INIT }) === 'undefined') {
|
||||
throw new Error('Reducer "' + key + '" returned undefined during initialization. ' + 'If the state passed to the reducer is undefined, you must ' + 'explicitly return the initial state. The initial state may ' + 'not be undefined.');
|
||||
}
|
||||
|
||||
var type = Math.random().toString(36).substring(7).split('').join('.');
|
||||
if (typeof reducer(undefined, { type: type }) === 'undefined') {
|
||||
throw new Error('Reducer "' + key + '" returned undefined when probed with a random type. ' + ('Don\'t try to handle ' + _createStore.ActionTypes.INIT + ' or other actions in "redux/*" ') + 'namespace. They are considered private. Instead, you must return the ' + 'current state for any unknown actions, unless it is undefined, ' + 'in which case you must return the initial state, regardless of the ' + 'action type. The initial state may not be undefined.');
|
||||
}
|
||||
});
|
||||
|
||||
var defaultState = _utilsMapValues2['default'](finalReducers, function () {
|
||||
return undefined;
|
||||
});
|
||||
var stateShapeVerified;
|
||||
|
||||
return function combination(state, action) {
|
||||
if (state === undefined) state = defaultState;
|
||||
|
||||
var finalState = _utilsMapValues2['default'](finalReducers, function (reducer, key) {
|
||||
var newState = reducer(state[key], action);
|
||||
if (typeof newState === 'undefined') {
|
||||
throw new Error(getErrorMessage(key, action));
|
||||
}
|
||||
return newState;
|
||||
});
|
||||
|
||||
if (true) {
|
||||
if (!stateShapeVerified) {
|
||||
verifyStateShape(state, finalState);
|
||||
stateShapeVerified = true;
|
||||
}
|
||||
}
|
||||
|
||||
return finalState;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = exports['default'];
|
||||
|
||||
/***/ },
|
||||
/* 8 */
|
||||
/***/ function(module, exports) {
|
||||
|
||||
/**
|
||||
* Picks key-value pairs from an object where values satisfy a predicate.
|
||||
*
|
||||
* @param {Object} obj The object to pick from.
|
||||
* @param {Function} fn The predicate the values must satisfy to be copied.
|
||||
* @returns {Object} The object with the values that satisfied the predicate.
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
exports.__esModule = true;
|
||||
exports["default"] = pick;
|
||||
|
||||
function pick(obj, fn) {
|
||||
return Object.keys(obj).reduce(function (result, key) {
|
||||
if (fn(obj[key])) {
|
||||
result[key] = obj[key];
|
||||
}
|
||||
return result;
|
||||
}, {});
|
||||
}
|
||||
|
||||
module.exports = exports["default"];
|
||||
|
||||
/***/ }
|
||||
/******/ ])
|
||||
});
|
||||
;
|
@ -18,7 +18,7 @@
|
||||
src="chrome://browser/content/devtools/theme-switching.js"/>
|
||||
|
||||
<script type="application/javascript" src="chrome://browser/content/devtools/d3.js"/>
|
||||
<script type="application/javascript" src="dagre-d3.js"/>
|
||||
<script type="application/javascript" src="chrome://browser/content/devtools/dagre-d3.js"/>
|
||||
<script type="application/javascript" src="webaudioeditor/includes.js"/>
|
||||
<script type="application/javascript" src="webaudioeditor/models.js"/>
|
||||
<script type="application/javascript" src="webaudioeditor/controller.js"/>
|
||||
|
Loading…
Reference in New Issue
Block a user