Skip to content

Commit

Permalink
Merge pull request #2 from esphen/next
Browse files Browse the repository at this point in the history
0.3.0
  • Loading branch information
Espen H committed Sep 10, 2015
2 parents 6f14332 + d73ec33 commit e4a0e96
Show file tree
Hide file tree
Showing 17 changed files with 168 additions and 83 deletions.
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ A REST client for the rest of us.
_Note: This is the source code, the extension download is [here](https://addons.mozilla.org/en-US/firefox/addon/rested/?src=search)._

RESTED is a new take on rest clients on Firefox.
It is meant to be easy to use to let you work as effective as possible.
It is designed to be easy to use to let you work as effective as possible.
It features all the most commonly used HTTP methods, setting headers,
saving requests to local storage, and more.

Expand All @@ -20,14 +20,17 @@ that they will be merged - requests will be reviewed).
This project was from the very beginning intended to be easy to work with.
It is basically just an angular 1.4 app inside of Firefox, so if you know angular, you're in luck!

Features in 0.2, currently in review:
- Templates in the URLs (example: www.vg.no?authToken={{token}})
- Basic auth
- Fullscreen request panel (minimize collections)
- Other UI goodies
Features in 0.3, currently in review:
- Remove headers from request
- Spinner for slow requests
- Changes undelying implementation $http -> XHR
- Https urls should no longer break
- Responsive adjustments - should now look good on monitors for mice

What features are on the to-do list:
- Improve collections with collapsible collection groups
- Request history
- Export and import collections from file
- Improve pretty-printing of results

In order to work on this project, you're going to need a few things:
Expand Down
11 changes: 11 additions & 0 deletions data/css/common.css
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,22 @@
opacity: 1;
}

.text-ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 100%;
}

.animate {
-webkit-transition: 0.2s ease;
-moz-transition: 0.2s ease;
-o-transition: 0.2s ease;
transition: 0.2s ease;
}

.spinner {
font-size: 3em;
opacity: 0.9;
}

8 changes: 7 additions & 1 deletion data/css/spesific.css
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ body {
font-size: 14px;
}

#spinnerPanel {
padding: 40px 0 40px;
}

/* Rules for allowing sliding ins slidey-button */
slidey-button {
float: right;
Expand Down Expand Up @@ -73,4 +77,6 @@ slidey-button {
/* Makes the animation look a bit more sane */
white-space: nowrap;
}

.remove-header {
margin-top: 10px;
}
2 changes: 1 addition & 1 deletion data/js/app.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
'use strict';

var app = angular.module('RestedApp', []);
angular.module('RestedApp', []);

9 changes: 8 additions & 1 deletion data/js/constants/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,17 @@ module.constant('DB_URL_VARIABLES_STORE_NAME', 'urlVariables');
// application load or on request reset.
module.constant('DEFAULT_REQUEST', {
method: 'GET',
headers: {},
headers: [{
name: '',
value: ''
}],
cache: false
});

// The milliseconds we delay showing spinners
// after a request has been sent.
module.constant('SPINNER_SHOW_DELAY', 300);

// The URLs shown in the URL bar on load.
// Add more if you want!
// The only rule for these: Nothing NSFW or illegal
Expand Down
1 change: 1 addition & 0 deletions data/js/controllers/root.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,4 @@ angular.module('RestedApp')
});
};
});

11 changes: 1 addition & 10 deletions data/js/directives/contentSlider.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,11 @@ angular.module('RestedApp')
link: function(scope, element, attrs, controllers) {
scope.slidden = !scope.startHidden;

// prettyprint handles js and html but not json,
// here we assume that if it starts with [ or {
// it is json, and we parse it as such.
scope.parseJSON = function(body) {
if(body !== null && (typeof body === 'object' || /^\s*[\[\{]/.test(body))) {
return JSON.stringify(body, null, 2);
}
return body;
};

scope.slideToggle = function() {
element.find('.content').slideToggle();
scope.slidden = !scope.slidden;
};
}
};
});

5 changes: 5 additions & 0 deletions data/js/directives/modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ angular.module('RestedApp')

if(newVal) {
element.find('.modal').modal('show');

// Focus OK button after fade-in so user can press Enter
setTimeout(function() {
element.find('#modalAction').focus();
}, 500);
} else {
element.find('.modal').modal('hide');
}
Expand Down
96 changes: 56 additions & 40 deletions data/js/directives/request.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

angular.module('RestedApp')
.directive('request', function(DB, Request, RequestUtils, Base64, Modal) {
.directive('request', function(SPINNER_SHOW_DELAY, DB, Request, RequestUtils, Base64, Modal, $timeout) {
return {
restrict: 'E',
templateUrl: 'views/directives/request.html',
Expand All @@ -14,46 +14,67 @@ angular.module('RestedApp')
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'JSONP']
};

scope.slidden = {};
scope.urlVariables = [];
scope.headers = [];
scope.slidden = {};

var processReturnData = function(response) {
scope.response = response;
scope.response.headers = response.headers();
var spinnerTimeout;

var processReturnData = function() {
var response = this;

// Manually start $digest because angular does not know
// about the async XMLHttpresponse
scope.$apply(function() {
scope.response = response;
scope.response.headers = response.getAllResponseHeaders();

// Format json pretty-like
if (response.getResponseHeader('Content-Type') && response.getResponseHeader('Content-Type').toLowerCase().indexOf('json') > -1) {
scope.response.formattedResponse = JSON.stringify(JSON.parse(response.responseText), null, 2);
} else {
scope.response.formattedResponse = response.responseText;
}

$timeout.cancel(spinnerTimeout);
scope.requestInProgress = false;
});
};

scope.sendRequest = function() {
var request = scope.request;
var headers = {};
var request = angular.copy(scope.request);
var headers = request.headers || [];

// Check for sillyness
// If no URL is provided, assume user wants the placeholder URL.
if (!request.url) {
request.url = RequestUtils.randomURL();
// This is supposed to mutate both request panel and temp request
scope.request.url = request.url = RequestUtils.randomURL();
} else if (request.url === 'chrome://rested/content/rested.html') {
return Modal.set({
title: 'But... Why?',
body: 'You are aware of what you just did, right?'
});
}

// Strip empty headers and re-map headers to
// something we can use in $http.
if (scope.headers) {
request.headers = RequestUtils.reMapHeaders(scope.headers, true);
}

// Add basic auth header
var basicAuth = scope.request.basicAuth;
if (basicAuth && basicAuth.username) {
var password = basicAuth.password ? basicAuth.password : '';
console.log('auth', basicAuth.username, basicAuth.password);
request.headers.Authorization = 'Basic ' + Base64.encode(basicAuth.username + ':' + password);
headers.push({
name: 'Authorization',
value: 'Basic ' + Base64.encode(basicAuth.username + ':' + password)
});
}

Request.run(request, RequestUtils.reMapHeaders(scope.$root.urlVariables, true))
.then(processReturnData, processReturnData);
request.headers = headers;

Request.run(request, RequestUtils.reMapHeaders(scope.$root.urlVariables, true), processReturnData);

// Delay showing spinner for fast connections
spinnerTimeout = $timeout(function() {
scope.requestInProgress = true;
}, SPINNER_SHOW_DELAY);
};

scope.addHeader = function() {
Expand All @@ -62,19 +83,25 @@ angular.module('RestedApp')
value: ''
};

if(!Array.isArray(scope.headers)) {
scope.headers = [];
if(!Array.isArray(scope.request.headers)) {
scope.request.headers = [];
}

var isAlreadyAdded = scope.headers.some(function(item) {
var isAlreadyAdded = scope.request.headers.some(function(item) {
return newHeader.name == item.name;
});

if(isAlreadyAdded) {
return;
}

scope.headers.push(newHeader);
scope.request.headers.push(newHeader);
};

scope.removeHeader = function(header) {
scope.request.headers = scope.request.headers.filter(function(item) {
return item !== header;
});
};

scope.addRequest = function(request) {
Expand All @@ -88,7 +115,6 @@ angular.module('RestedApp')
return;
}

request.headers = RequestUtils.reMapHeaders(scope.headers, true);
scope.addToCollection(request);
};

Expand All @@ -100,14 +126,18 @@ angular.module('RestedApp')
scope.toggleCollections = function() {
// Logic handled in css and ngHide
scope.$root.collectionsMinimized = !scope.$root.collectionsMinimized;
};

scope.$watch('$root.collectionsMinimized', function(isMinimized) {
var isMinimized = scope.$root.collectionsMinimized;
scope.toggleCollectionsConfig = {
title: (isMinimized ? 'Show' : 'Hide') + ' collections',
classes: ['fa', (isMinimized ? 'fa-compress' : 'fa-expand')]
};
});
};

scope.toggleCollectionsConfig = {
title: 'Hide collections',
classes: ['fa', 'fa-expand']
};

scope.addRequestConfig = {
title: 'Add request to collection',
Expand Down Expand Up @@ -150,20 +180,6 @@ angular.module('RestedApp')
});
};

scope.$watch('request', function(newVal, oldVal) {
if(newVal && newVal !== oldVal) {
scope.request = newVal;

// Map headers from {name: value} => [{name: xxx, value: xxx}]
// This is so we can mutate them from the template.
if (scope.request.headers) {
scope.headers = RequestUtils.reMapHeaders(scope.request.headers);
} else {
scope.headers = [];
}
}
});

scope.getRandomURL = RequestUtils.randomURL;
}
};
Expand Down
28 changes: 24 additions & 4 deletions data/js/services/request.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
'use strict';

angular.module('RestedApp')
.factory('Request', function($http) {
.factory('Request', function() {

/**
* Prepend http:// if missing from url
* Make sure to allow https
*/
var prependHttp = function(url) {
if(!(/^http:\/\//.test(url))) {
if(!(/^https?:\/\//.test(url))) {
url = 'http://' + url;
}
return url;
Expand All @@ -29,14 +30,33 @@ angular.module('RestedApp')
}).replace(/([^:]\/)\/+/g, '$1');
};

var createXMLHttpRequest = function(req) {
var request = new XMLHttpRequest(true, true);
request.open(req.method, req.url);

if(Array.isArray(req.headers)) {
req.headers.forEach(function(header) {
if(header.name) {
request.setRequestHeader(header.name, header.value);
}
});
}

return request;
};

return {
run: function(request, parameters) {
run: function(request, parameters, fn) {
var requestCopy = angular.copy(request);
var url = prependHttp(requestCopy.url);
url = mapParameters(url, parameters);

requestCopy.url = url;
return $http(requestCopy);
var req = createXMLHttpRequest(requestCopy);
req.onloadend = fn.bind(req);

req.send(request.data);
}
};
});

1 change: 1 addition & 0 deletions data/js/services/requestUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ angular.module('RestedApp')
* ]
*/
var reMapHeaders = function(headers, asObject) {
// TODO - we no longer use asObject = false
if(!headers && !asObject) {
return [];
} else if(!headers && asObject) {
Expand Down
6 changes: 4 additions & 2 deletions data/views/directives/contentSlider.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ <h4 class="hoverhand" data-ng-click="slideToggle()">
data-ng-class="{'fa-rotate-90': slidden}">
</i>
</h4>
<pre class="content" data-ng-class="{'start-hidden': startHidden}">
{{parseJSON(content)}}
<pre class="content"
data-ng-class="{'start-hidden': startHidden}"
data-ng-bind="content">
</pre>
</div>

Loading

0 comments on commit e4a0e96

Please sign in to comment.