aboutsummaryrefslogtreecommitdiffstats
path: root/web/src/js/store
diff options
context:
space:
mode:
authorMaximilian Hils <git@maximilianhils.com>2016-02-18 12:29:35 +0100
committerMaximilian Hils <git@maximilianhils.com>2016-02-18 12:29:35 +0100
commit18b619e164ced91cf0ac8d3fd3c18be1f07df1cc (patch)
tree071bce256f44f063a2a4b412f43ff2f2b7d3ed5f /web/src/js/store
parent3cbacf4e0bae8cd96752303b42f88e176e638824 (diff)
downloadmitmproxy-18b619e164ced91cf0ac8d3fd3c18be1f07df1cc.tar.gz
mitmproxy-18b619e164ced91cf0ac8d3fd3c18be1f07df1cc.tar.bz2
mitmproxy-18b619e164ced91cf0ac8d3fd3c18be1f07df1cc.zip
move mitmproxy/web to root
Diffstat (limited to 'web/src/js/store')
-rw-r--r--web/src/js/store/store.js181
-rw-r--r--web/src/js/store/view.js115
2 files changed, 296 insertions, 0 deletions
diff --git a/web/src/js/store/store.js b/web/src/js/store/store.js
new file mode 100644
index 00000000..5024049f
--- /dev/null
+++ b/web/src/js/store/store.js
@@ -0,0 +1,181 @@
+
+var _ = require("lodash");
+var $ = require("jquery");
+var EventEmitter = require('events').EventEmitter;
+
+var utils = require("../utils.js");
+var actions = require("../actions.js");
+var dispatcher = require("../dispatcher.js");
+
+
+function ListStore() {
+ EventEmitter.call(this);
+ this.reset();
+}
+_.extend(ListStore.prototype, EventEmitter.prototype, {
+ add: function (elem) {
+ if (elem.id in this._pos_map) {
+ return;
+ }
+ this._pos_map[elem.id] = this.list.length;
+ this.list.push(elem);
+ this.emit("add", elem);
+ },
+ update: function (elem) {
+ if (!(elem.id in this._pos_map)) {
+ return;
+ }
+ this.list[this._pos_map[elem.id]] = elem;
+ this.emit("update", elem);
+ },
+ remove: function (elem_id) {
+ if (!(elem_id in this._pos_map)) {
+ return;
+ }
+ this.list.splice(this._pos_map[elem_id], 1);
+ this._build_map();
+ this.emit("remove", elem_id);
+ },
+ reset: function (elems) {
+ this.list = elems || [];
+ this._build_map();
+ this.emit("recalculate");
+ },
+ _build_map: function () {
+ this._pos_map = {};
+ for (var i = 0; i < this.list.length; i++) {
+ var elem = this.list[i];
+ this._pos_map[elem.id] = i;
+ }
+ },
+ get: function (elem_id) {
+ return this.list[this._pos_map[elem_id]];
+ },
+ index: function (elem_id) {
+ return this._pos_map[elem_id];
+ }
+});
+
+
+function DictStore() {
+ EventEmitter.call(this);
+ this.reset();
+}
+_.extend(DictStore.prototype, EventEmitter.prototype, {
+ update: function (dict) {
+ _.merge(this.dict, dict);
+ this.emit("recalculate");
+ },
+ reset: function (dict) {
+ this.dict = dict || {};
+ this.emit("recalculate");
+ }
+});
+
+function LiveStoreMixin(type) {
+ this.type = type;
+
+ this._updates_before_fetch = undefined;
+ this._fetchxhr = false;
+
+ this.handle = this.handle.bind(this);
+ dispatcher.AppDispatcher.register(this.handle);
+
+ // Avoid double-fetch on startup.
+ if (!(window.ws && window.ws.readyState === WebSocket.CONNECTING)) {
+ this.fetch();
+ }
+}
+_.extend(LiveStoreMixin.prototype, {
+ handle: function (event) {
+ if (event.type === actions.ActionTypes.CONNECTION_OPEN) {
+ return this.fetch();
+ }
+ if (event.type === this.type) {
+ if (event.cmd === actions.StoreCmds.RESET) {
+ this.fetch(event.data);
+ } else if (this._updates_before_fetch) {
+ console.log("defer update", event);
+ this._updates_before_fetch.push(event);
+ } else {
+ this[event.cmd](event.data);
+ }
+ }
+ },
+ close: function () {
+ dispatcher.AppDispatcher.unregister(this.handle);
+ },
+ fetch: function (data) {
+ console.log("fetch " + this.type);
+ if (this._fetchxhr) {
+ this._fetchxhr.abort();
+ }
+ this._updates_before_fetch = []; // (JS: empty array is true)
+ if (data) {
+ this.handle_fetch(data);
+ } else {
+ this._fetchxhr = $.getJSON("/" + this.type)
+ .done(function (message) {
+ this.handle_fetch(message.data);
+ }.bind(this))
+ .fail(function () {
+ EventLogActions.add_event("Could not fetch " + this.type);
+ }.bind(this));
+ }
+ },
+ handle_fetch: function (data) {
+ this._fetchxhr = false;
+ console.log(this.type + " fetched.", this._updates_before_fetch);
+ this.reset(data);
+ var updates = this._updates_before_fetch;
+ this._updates_before_fetch = false;
+ for (var i = 0; i < updates.length; i++) {
+ this.handle(updates[i]);
+ }
+ },
+});
+
+function LiveListStore(type) {
+ ListStore.call(this);
+ LiveStoreMixin.call(this, type);
+}
+_.extend(LiveListStore.prototype, ListStore.prototype, LiveStoreMixin.prototype);
+
+function LiveDictStore(type) {
+ DictStore.call(this);
+ LiveStoreMixin.call(this, type);
+}
+_.extend(LiveDictStore.prototype, DictStore.prototype, LiveStoreMixin.prototype);
+
+
+function FlowStore() {
+ return new LiveListStore(actions.ActionTypes.FLOW_STORE);
+}
+
+function SettingsStore() {
+ return new LiveDictStore(actions.ActionTypes.SETTINGS_STORE);
+}
+
+function EventLogStore() {
+ LiveListStore.call(this, actions.ActionTypes.EVENT_STORE);
+}
+_.extend(EventLogStore.prototype, LiveListStore.prototype, {
+ fetch: function(){
+ LiveListStore.prototype.fetch.apply(this, arguments);
+
+ // Make sure to display updates even if fetching all events failed.
+ // This way, we can send "fetch failed" log messages to the log.
+ if(this._fetchxhr){
+ this._fetchxhr.fail(function(){
+ this.handle_fetch(null);
+ }.bind(this));
+ }
+ }
+});
+
+
+module.exports = {
+ EventLogStore: EventLogStore,
+ SettingsStore: SettingsStore,
+ FlowStore: FlowStore
+}; \ No newline at end of file
diff --git a/web/src/js/store/view.js b/web/src/js/store/view.js
new file mode 100644
index 00000000..d628d46b
--- /dev/null
+++ b/web/src/js/store/view.js
@@ -0,0 +1,115 @@
+var EventEmitter = require('events').EventEmitter;
+var _ = require("lodash");
+
+var utils = require("../utils.js");
+
+function SortByStoreOrder(elem) {
+ return this.store.index(elem.id);
+}
+
+var default_sort = SortByStoreOrder;
+var default_filt = function (elem) {
+ return true;
+};
+
+function StoreView(store, filt, sortfun) {
+ EventEmitter.call(this);
+
+ this.store = store;
+
+ this.add = this.add.bind(this);
+ this.update = this.update.bind(this);
+ this.remove = this.remove.bind(this);
+ this.recalculate = this.recalculate.bind(this);
+ this.store.addListener("add", this.add);
+ this.store.addListener("update", this.update);
+ this.store.addListener("remove", this.remove);
+ this.store.addListener("recalculate", this.recalculate);
+
+ this.recalculate(filt, sortfun);
+}
+
+_.extend(StoreView.prototype, EventEmitter.prototype, {
+ close: function () {
+ this.store.removeListener("add", this.add);
+ this.store.removeListener("update", this.update);
+ this.store.removeListener("remove", this.remove);
+ this.store.removeListener("recalculate", this.recalculate);
+ this.removeAllListeners();
+ },
+ recalculate: function (filt, sortfun) {
+ filt = filt || this.filt || default_filt;
+ sortfun = sortfun || this.sortfun || default_sort;
+ filt = filt.bind(this);
+ sortfun = sortfun.bind(this);
+ this.filt = filt;
+ this.sortfun = sortfun;
+
+ this.list = this.store.list.filter(filt);
+ this.list.sort(function (a, b) {
+ var akey = sortfun(a);
+ var bkey = sortfun(b);
+ if(akey < bkey){
+ return -1;
+ } else if(akey > bkey){
+ return 1;
+ } else {
+ return 0;
+ }
+ });
+ this.emit("recalculate");
+ },
+ index: function (elem) {
+ return _.sortedIndex(this.list, elem, this.sortfun);
+ },
+ add: function (elem) {
+ if (this.filt(elem)) {
+ var idx = this.index(elem);
+ if (idx === this.list.length) { //happens often, .push is way faster.
+ this.list.push(elem);
+ } else {
+ this.list.splice(idx, 0, elem);
+ }
+ this.emit("add", elem, idx);
+ }
+ },
+ update: function (elem) {
+ var idx;
+ var i = this.list.length;
+ // Search from the back, we usually update the latest entries.
+ while (i--) {
+ if (this.list[i].id === elem.id) {
+ idx = i;
+ break;
+ }
+ }
+
+ if (idx === -1) { //not contained in list
+ this.add(elem);
+ } else if (!this.filt(elem)) {
+ this.remove(elem.id);
+ } else {
+ if (this.sortfun(this.list[idx]) !== this.sortfun(elem)) { //sortpos has changed
+ this.remove(this.list[idx]);
+ this.add(elem);
+ } else {
+ this.list[idx] = elem;
+ this.emit("update", elem, idx);
+ }
+ }
+ },
+ remove: function (elem_id) {
+ var idx = this.list.length;
+ while (idx--) {
+ if (this.list[idx].id === elem_id) {
+ this.list.splice(idx, 1);
+ this.emit("remove", elem_id, idx);
+ break;
+ }
+ }
+ }
+});
+
+module.exports = {
+ StoreView: StoreView
+}; \ No newline at end of file