Skip to content

Commit

Permalink
Merge pull request #50 from TheSharpieOne/master
Browse files Browse the repository at this point in the history
Chore(release): Bump Release 1.1.0
  • Loading branch information
TheSharpieOne committed Feb 15, 2015
2 parents 59cb59c + 2a536da commit 951bd0d
Show file tree
Hide file tree
Showing 4 changed files with 269 additions and 83 deletions.
2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "angular-sails",
"version": "1.0.5",
"version": "1.1.0",
"authors": [
"Jan-Oliver Pantel <info@janpantel.de>"
],
Expand Down
346 changes: 266 additions & 80 deletions dist/angular-sails.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,100 +2,286 @@
'use strict'/*global angular */
angular.module('ngSails', ['ng']);

/*jslint sloppy:true*/
/*global angular, io */
angular.module('ngSails').provider('$sails', function () {
var provider = this,
httpVerbs = ['get', 'post', 'put', 'delete'],
eventNames = ['on', 'once'];

this.url = undefined;
this.interceptors = [];
this.responseHandler = undefined;

this.$get = ['$q', '$timeout', function ($q, $timeout) {
var socket = io.connect(provider.url),
defer = function () {
var deferred = $q.defer(),
promise = deferred.promise;

promise.success = function (fn) {
promise.then(function(response) {
fn(response.data, response.status, response.headers);
});
return promise;
};
(function(angular, io) {
'use strict';
io.sails.autoConnect = false;

promise.error = function (fn) {
promise.then(null, function(response) {
fn(response.data, response.status, response.headers);
});
return promise;
// copied from angular
function parseHeaders(headers) {
var parsed = {},
key, val, i;
if (!headers) return parsed;
angular.forEach(headers.split('\n'), function(line) {
i = line.indexOf(':');
key = lowercase(trim(line.substr(0, i)));
val = trim(line.substr(i + 1));
if (key) {
parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
}
});

return parsed;
}

function trim(value) {
return angular.isString(value) ? value.trim() : value;
}

function isPromiseLike (obj){
return obj && angular.isFunction(obj.then);
}

// copied from angular
function headersGetter(headers) {
var headersObj = angular.isObject(headers) ? headers : undefined;
return function(name) {
if (!headersObj) headersObj = parseHeaders(headers);
if (name) {
var value = headersObj[lowercase(name)];
if (value === void 0) {
value = null;
}
return value;
}
return headersObj;
};
}

angular.module('ngSails').provider('$sails', function() {
var provider = this;

this.httpVerbs = ['get', 'post', 'put', 'delete'];

this.eventNames = ['on', 'off', 'once'];

this.url = undefined;

this.urlPrefix = '';

this.config = {
transports: ['websocket', 'polling'],
useCORSRouteToGetCookie: false
};

this.debug = false;

// like https://docs.angularjs.org/api/ng/service/$http#interceptors
// but with sails.io arguments
var interceptorFactories = this.interceptors = [
/*function($injectables) {
return {
request: function(config) {},
response: function(response) {},
requestError: function(rejection) {},
responseError: function(rejection) {}
};
}*/
];

return deferred;
},
resolveOrReject = this.responseHandler || function (deferred, response) {
var jwr = response;

// backward compatibility with older sails.io (no JWR)
if(!(response instanceof Object && response.constructor.name === "JWR")){
jwr = {
body: response,
headers: response.headers || {},
statusCode: response.statusCode || response.status
};
/*@ngInject*/
this.$get = ["$q", "$injector", "$rootScope", "$log", "$timeout", function($q, $injector, $rootScope, $log, $timeout) {
var socket = (io.sails && io.sails.connect || io.connect)(provider.url, provider.config);

socket.connect = function(opts){
if(!socket.isConnected()){
var _opts = opts||{};
_opts = angular.extend({},provider.config,opts);

// These are the options sails.io.js actually sets when making the connection.
socket.useCORSRouteToGetCookie = _opts.useCORSRouteToGetCookie;
socket.url = _opts.url || provider.url;
socket.multiplex = _opts.multiplex;

socket._connect();
}
return socket;
}

// angular $http returns the 'body' as 'data'.
jwr.data = jwr.body;
// TODO: separate out interceptors into its own file (and provider?).
// build interceptor chain
var reversedInterceptors = [];
angular.forEach(interceptorFactories, function(interceptorFactory) {
reversedInterceptors.unshift(
angular.isString(interceptorFactory) ?
$injector.get(interceptorFactory) : $injector.invoke(interceptorFactory)
);
});

// angular $http returns the 'statusCode' as 'status'.
jwr.status = jwr.statusCode;
// Send the request using the socket
function serverRequest(config) {
var defer = $q.defer();
if (provider.debug) $log.info('$sails ' + config.method + ' ' + config.url, config.data || '');

// TODO: map 'status'/'statusCode' to a 'statusText' to mimic angular $http
if (config.timeout > 0) {
$timeout(timeoutRequest, config.timeout);
} else if (isPromiseLike(config.timeout)) {
config.timeout.then(timeoutRequest);
}

socket['legacy_' + config.method.toLowerCase()](config.url, config.data, serverResponse);

if (jwr.error) {
deferred.reject(jwr);
} else {
deferred.resolve(jwr);
function timeoutRequest(){
serverResponse(null);
}
},
angularify = function (cb, data) {
$timeout(function () {
cb(data);
});
},
promisify = function (methodName) {
socket['legacy_' + methodName] = socket[methodName];
socket[methodName] = function (url, data, cb) {
var deferred = defer();
if (cb === undefined && angular.isFunction(data)) {
cb = data;
data = null;

function serverResponse(result, jwr) {

if (!jwr) {
jwr = {
body: result,
headers: result.headers || {},
statusCode: result.statusCode || result.status || 0,
error: (function() {
if (this.statusCode < 200 || this.statusCode >= 400) {
return this.body || this.statusCode;
}
})()
};
}

jwr.data = jwr.body; // $http compat
jwr.status = jwr.statusCode; // $http compat
jwr.socket = socket;
jwr.url = config.url;
jwr.method = config.method;
jwr.config = config.config;
if (jwr.error) {
if (provider.debug) $log.warn('$sails response ' + jwr.statusCode + ' ' + config.url, jwr);
defer.reject(jwr);
} else {
if (provider.debug) $log.info('$sails response ' + config.url, jwr);
defer.resolve(jwr);
}
deferred.promise.then(cb);
socket['legacy_' + methodName](url, data, function (emulatedHTTPBody, jsonWebSocketResponse) {
resolveOrReject(deferred, jsonWebSocketResponse || emulatedHTTPBody);
}

return defer.promise;
}

function promisify(methodName) {
socket['legacy_' + methodName] = socket[methodName];

socket[methodName] = function(url, data, config) {

var chain = [serverRequest, undefined];

//TODO: more compatible with $http methods and config

var promise = $q.when({
url: provider.urlPrefix + url,
data: data,
socket: socket,
config: config || {},
method: methodName.toUpperCase()
});
return deferred.promise;
};
},
wrapEvent = function (eventName) {
socket['legacy_' + eventName] = socket[eventName];
socket[eventName] = function (event, cb) {
if (cb !== null && angular.isFunction(cb)) {
socket['legacy_' + eventName](event, function (result) {
angularify(cb, result);
});

// apply interceptors
angular.forEach(reversedInterceptors, function(interceptor) {
if (interceptor.request || interceptor.requestError) {
chain.unshift(interceptor.request, interceptor.requestError);
}
if (interceptor.response || interceptor.responseError) {
chain.push(interceptor.response, interceptor.responseError);
}
});

while (chain.length) {
var thenFn = chain.shift();
var rejectFn = chain.shift();

promise = promise.then(thenFn, rejectFn);
}

// be $http compatible
promise.success = function(fn) {
promise.then(function(jwr) {
fn(jwr.body, jwr.statusCode, headersGetter(jwr.headers), jwr);
});
return promise;
};
promise.error = function(fn) {
promise.then(null, function(jwr) {
fn(jwr.body, jwr.statusCode, headersGetter(jwr.headers), jwr);
});
return promise;
};

return promise;
};
};
}

function wrapEvent(eventName) {
if(socket[eventName] || socket._raw[eventName]){
socket['legacy_' + eventName] = socket[eventName] || socket._raw[eventName];
socket[eventName] = function(event, cb) {
if (cb !== null && angular.isFunction(cb)) {
socket['legacy_' + eventName](event, function(result) {
$rootScope.$evalAsync(cb.bind(socket, result));
});
}
};
}
}

angular.forEach(provider.httpVerbs, promisify);
angular.forEach(provider.eventNames, wrapEvent);


/**
* Update a model on sails pushes
* @param {String} name Sails model name
* @param {Array} models Array with model objects
*/
socket.$modelUpdater = function(name, models) {

socket.on(name, function(message) {
var i;

switch (message.verb) {

angular.forEach(httpVerbs, promisify);
angular.forEach(eventNames, wrapEvent);
case "created":
// create new model item
models.push(message.data);
break;

case "updated":
var obj;
for (i = 0; i < models.length; i++) {
if (models[i].id === message.id) {
obj = models[i];
break;
}
}

// cant update if the angular-model does not have the item and the
// sails message does not give us the previous record
if (!obj && !message.previous) return;

if (!obj) {
// sails has given us the previous record, create it in our model
obj = message.previous;
models.push(obj);
}

// update the model item
angular.extend(obj, message.data);
break;

case "destroyed":
for (i = 0; i < models.length; i++) {
if (models[i].id === message.id) {
models.splice(i, 1);
break;
}
}
break;
}
});
};

return socket;
}];
});
return socket;
}];
this.$get.$inject = ["$q", "$injector", "$rootScope", "$log", "$timeout"];
});
}(angular, io));
}(angular, io));
2 changes: 1 addition & 1 deletion dist/angular-sails.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "angular-sails",
"private": true,
"version": "1.0.5",
"version": "1.1.0",
"description": "An angular provider for using the sails socket.io api",
"scripts": {
"build": "gulp build-js",
Expand Down

0 comments on commit 951bd0d

Please sign in to comment.