From 1143552e1690f8b96b3d95381f7f06cbb46ead59 Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Sun, 22 Mar 2015 00:21:38 +0100 Subject: web: add content views --- web/src/js/components/flowview/contentview.js | 158 ++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 web/src/js/components/flowview/contentview.js (limited to 'web/src/js/components/flowview/contentview.js') diff --git a/web/src/js/components/flowview/contentview.js b/web/src/js/components/flowview/contentview.js new file mode 100644 index 00000000..09a64bb2 --- /dev/null +++ b/web/src/js/components/flowview/contentview.js @@ -0,0 +1,158 @@ +var React = require("react"); +var _ = require("lodash"); + +var MessageUtils = require("../../flow/utils.js").MessageUtils; +var utils = require("../../utils.js"); + +var image_regex = /^image\/(png|jpe?g|gif|vnc.microsoft.icon|x-icon)$/i; +var Image = React.createClass({ + statics: { + matches: function (message) { + return image_regex.test(MessageUtils.getContentType(message)); + } + }, + render: function () { + var message_name = this.props.flow.request === this.props.message ? "request" : "response"; + var url = "/flows/" + this.props.flow.id + "/" + message_name + "/content"; + return
+ preview +
; + } +}); + +var Raw = React.createClass({ + statics: { + matches: function (message) { + return true; + } + }, + render: function () { + //FIXME + return
raw
; + } +}); + + +var Auto = React.createClass({ + statics: { + matches: function () { + return false; // don't match itself + }, + findView: function (message) { + for (var i = 0; i < all.length; i++) { + if (all[i].matches(message)) { + return all[i]; + } + } + return all[all.length - 1]; + } + }, + render: function () { + var View = Auto.findView(this.props.message); + return ; + } +}); + +var all = [Auto, Image, Raw]; + + +var ContentEmpty = React.createClass({ + render: function () { + var message_name = this.props.flow.request === this.props.message ? "request" : "response"; + return
No {message_name} content.
; + } +}); + +var ContentMissing = React.createClass({ + render: function () { + var message_name = this.props.flow.request === this.props.message ? "Request" : "Response"; + return
{message_name} content missing.
; + } +}); + +var TooLarge = React.createClass({ + render: function () { + var size = utils.formatSize(this.props.message.contentLength); + return
+ + {size} content size. +
; + } +}); + +var ViewSelector = React.createClass({ + render: function () { + var views = []; + for (var i = 0; i < all.length; i++) { + var view = all[i]; + var className = "btn btn-default"; + if (view === this.props.active) { + className += " active"; + } + var text; + if (view === Auto) { + text = "auto: " + Auto.findView(this.props.message).displayName.toLowerCase(); + } else { + text = view.displayName.toLowerCase(); + } + views.push( + + ); + } + + return
{views}
; + } +}); + +var ContentView = React.createClass({ + getInitialState: function () { + return { + displayLarge: false, + View: Auto + }; + }, + propTypes: { + // It may seem a bit weird at the first glance: + // Every view takes the flow and the message as props, e.g. + // + flow: React.PropTypes.object.isRequired, + message: React.PropTypes.object.isRequired, + }, + selectView: function (view) { + this.setState({ + View: view + }); + }, + displayLarge: function () { + this.setState({displayLarge: true}); + }, + componentWillReceiveProps: function (nextProps) { + if (nextProps.message !== this.props.message) { + this.setState(this.getInitialState()); + } + }, + render: function () { + var message = this.props.message; + if (message.contentLength === 0) { + return ; + } else if (message.contentLength === null) { + return ; + } else if (message.contentLength > 1024 * 1024 * 3 && !this.state.displayLarge) { + return ; + } + + return
+ +
+ +
+
; + } +}); + +module.exports = ContentView; \ No newline at end of file -- cgit v1.2.3 From 941584623281905fec22d8857c5501d196c051f7 Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Sun, 22 Mar 2015 02:25:47 +0100 Subject: web: raw content view --- web/src/js/components/flowview/contentview.js | 43 +++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 5 deletions(-) (limited to 'web/src/js/components/flowview/contentview.js') diff --git a/web/src/js/components/flowview/contentview.js b/web/src/js/components/flowview/contentview.js index 09a64bb2..30a40faa 100644 --- a/web/src/js/components/flowview/contentview.js +++ b/web/src/js/components/flowview/contentview.js @@ -12,23 +12,56 @@ var Image = React.createClass({ } }, render: function () { - var message_name = this.props.flow.request === this.props.message ? "request" : "response"; - var url = "/flows/" + this.props.flow.id + "/" + message_name + "/content"; + var url = MessageUtils.getContentURL(this.props.flow, this.props.message); return
preview
; } }); +var RawMixin = { + getInitialState: function () { + return { + content: undefined + } + }, + requestContent: function (nextProps) { + this.setState({content: undefined}); + var request = MessageUtils.getContent(nextProps.flow, nextProps.message); + request.done(function (data) { + this.setState({content: data}); + }.bind(this)).fail(function (jqXHR, textStatus, errorThrown) { + this.setState({content: "AJAX Error: " + textStatus}); + }.bind(this)); + + }, + componentWillMount: function () { + this.requestContent(this.props); + }, + componentWillReceiveProps: function (nextProps) { + if (nextProps.message !== this.props.message) { + this.requestContent(nextProps); + } + }, + render: function () { + if (!this.state.content) { + return
+ +
; + } + return this.renderContent(); + } +}; + var Raw = React.createClass({ + mixins: [RawMixin], statics: { matches: function (message) { return true; } }, - render: function () { - //FIXME - return
raw
; + renderContent: function () { + return
{this.state.content}
; } }); -- cgit v1.2.3 From 39a8ac7e2a405b84edc55289cb8bdfb2ac1948fa Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Sun, 22 Mar 2015 15:19:35 +0100 Subject: web: improve views --- web/src/js/components/flowview/contentview.js | 66 +++++++++++++++++++++------ 1 file changed, 53 insertions(+), 13 deletions(-) (limited to 'web/src/js/components/flowview/contentview.js') diff --git a/web/src/js/components/flowview/contentview.js b/web/src/js/components/flowview/contentview.js index 30a40faa..e4d11997 100644 --- a/web/src/js/components/flowview/contentview.js +++ b/web/src/js/components/flowview/contentview.js @@ -5,7 +5,7 @@ var MessageUtils = require("../../flow/utils.js").MessageUtils; var utils = require("../../utils.js"); var image_regex = /^image\/(png|jpe?g|gif|vnc.microsoft.icon|x-icon)$/i; -var Image = React.createClass({ +var ViewImage = React.createClass({ statics: { matches: function (message) { return image_regex.test(MessageUtils.getContentType(message)); @@ -22,16 +22,28 @@ var Image = React.createClass({ var RawMixin = { getInitialState: function () { return { - content: undefined + content: undefined, + request: undefined } }, requestContent: function (nextProps) { - this.setState({content: undefined}); + if(this.state.request){ + this.state.request.abort(); + } var request = MessageUtils.getContent(nextProps.flow, nextProps.message); + this.setState({ + content: undefined, + request: request + }); request.done(function (data) { this.setState({content: data}); }.bind(this)).fail(function (jqXHR, textStatus, errorThrown) { - this.setState({content: "AJAX Error: " + textStatus}); + if(textStatus === "abort"){ + return; + } + this.setState({content: "AJAX Error: " + textStatus + "\r\n" + errorThrown}); + }.bind(this)).always(function(){ + this.setState({request: undefined}); }.bind(this)); }, @@ -43,6 +55,11 @@ var RawMixin = { this.requestContent(nextProps); } }, + componentWillUnmount: function(){ + if(this.state.request){ + this.state.request.abort(); + } + }, render: function () { if (!this.state.content) { return
@@ -53,7 +70,7 @@ var RawMixin = { } }; -var Raw = React.createClass({ +var ViewRaw = React.createClass({ mixins: [RawMixin], statics: { matches: function (message) { @@ -65,8 +82,25 @@ var Raw = React.createClass({ } }); +var json_regex = /^application\/json$/i; +var ViewJSON = React.createClass({ + mixins: [RawMixin], + statics: { + matches: function (message) { + return json_regex.test(MessageUtils.getContentType(message)); + } + }, + renderContent: function () { + var json = this.state.content; + try { + json = JSON.stringify(JSON.parse(json), null, 2); + } catch(e) { + } + return
{json}
; + } +}); -var Auto = React.createClass({ +var ViewAuto = React.createClass({ statics: { matches: function () { return false; // don't match itself @@ -81,12 +115,12 @@ var Auto = React.createClass({ } }, render: function () { - var View = Auto.findView(this.props.message); + var View = ViewAuto.findView(this.props.message); return ; } }); -var all = [Auto, Image, Raw]; +var all = [ViewAuto, ViewImage, ViewJSON, ViewRaw]; var ContentEmpty = React.createClass({ @@ -104,6 +138,12 @@ var ContentMissing = React.createClass({ }); var TooLarge = React.createClass({ + statics: { + isTooLarge: function(message){ + var max_mb = ViewImage.matches(message) ? 10 : 0.2; + return message.contentLength > 1024 * 1024 * max_mb; + } + }, render: function () { var size = utils.formatSize(this.props.message.contentLength); return
@@ -123,10 +163,10 @@ var ViewSelector = React.createClass({ className += " active"; } var text; - if (view === Auto) { - text = "auto: " + Auto.findView(this.props.message).displayName.toLowerCase(); + if (view === ViewAuto) { + text = "auto: " + ViewAuto.findView(this.props.message).displayName.toLowerCase().replace("view", ""); } else { - text = view.displayName.toLowerCase(); + text = view.displayName.toLowerCase().replace("view", ""); } views.push(