From 51f10fec59f9f9dd199beff0923e98acb503a5e2 Mon Sep 17 00:00:00 2001 From: Freddy Limpens Date: Thu, 27 Feb 2014 15:25:26 +0100 Subject: [PATCH 1/3] externalized login controller as a service --- index.html | 60 ++++++------ scripts/app.js | 2 +- scripts/src/common/controllers.coffee | 96 +------------------ .../angular-unisson-auth.js | 1 + views/map/map_detail.html | 2 +- views/map/map_new.html | 4 +- 6 files changed, 39 insertions(+), 126 deletions(-) mode change 100644 => 100755 scripts/app.js create mode 120000 scripts/vendor/angular-unisson-auth/angular-unisson-auth.js diff --git a/index.html b/index.html index efea18e..6edc9d7 100644 --- a/index.html +++ b/index.html @@ -17,41 +17,41 @@ -
+
-
+
- -
-
-
-
Entrez votre identifiant pour vous connecter
-
-

- - -

-

- - -

- - Mot de passe oublié ? -
+ +
+
+
+
Entrez votre identifiant pour vous connecter
+
+

+ + +

+

+ + +

+ + Mot de passe oublié ? +
+
+
+
    +
  • +
  • +
  • +
+
+ Fermer +
-
-
    -
  • -
  • -
  • -
-
- Fermer -
-
diff --git a/scripts/app.js b/scripts/app.js old mode 100644 new mode 100755 index 03aeff5..239867a --- a/scripts/app.js +++ b/scripts/app.js @@ -5,7 +5,7 @@ config = { angular.module('map', ['map.controllers', 'map.services', 'map.filters', 'leaflet-directive']); angular.module('common', ['common.filters', 'common.controllers', 'common.services']); -app = angular.module('unisson_map', ['common', 'map', 'ui.router', 'ngAnimate', 'googleOauth']); +app = angular.module('unisson_map', ['common', 'map', 'ui.router', 'ngAnimate', 'googleOauth', 'angular-unisson-auth']); // Config app.constant('moduleTemplateBaseUrl', config.templateBaseUrl + 'map/'); diff --git a/scripts/src/common/controllers.coffee b/scripts/src/common/controllers.coffee index c9b19a8..1fbd78e 100644 --- a/scripts/src/common/controllers.coffee +++ b/scripts/src/common/controllers.coffee @@ -1,95 +1,7 @@ -module = angular.module('common.controllers', ['http-auth-interceptor', 'ngCookies', 'googleOauth']) +module = angular.module('common.controllers', ['angular-unisson-auth']) class LoginCtrl - """ - Login a user - """ - constructor: (@$scope, @$rootScope, @$http, @$state, @Restangular, @$cookies, @authService, @Token) -> - @$scope.isAuthenticated = false - @$scope.username = "" - @$scope.loginrequired = false + constructor: (@$scope, @loginService) -> + @$scope.loginService = @loginService - # On login required - @$scope.$on('event:auth-loginRequired', => - @$scope.loginrequired = true - console.debug("Login required") - ) - - # On login successful - @$scope.$on('event:auth-loginConfirmed', => - console.debug("Login OK") - @$scope.loginrequired = false - @$scope.username = @$cookies.username - @$scope.isAuthenticated = true - ) - - # set authorization header if already logged in - if @$cookies.username and @$cookies.key - console.debug("Already logged in.") - @$http.defaults.headers.common['Authorization'] = "ApiKey #{@$cookies.username}:#{@$cookies.key}" - @authService.loginConfirmed() - - - @$scope.accessToken = @Token.get() - - # Add methods to scope - @$scope.submit = this.submit - @$scope.authenticateGoogle = this.authenticateGoogle - @$scope.forceLogin = this.forceLogin - @$scope.logout = this.logout - - forceLogin: => - @$scope.loginrequired = true - - logout: => - @$scope.isAuthenticated = false - delete @$http.defaults.headers.common['Authorization'] - delete @$cookies['username'] - delete @$cookies['key'] - @$scope.username = "" - - @$state.go('index') - - - submit: => - console.debug('submitting login...') - @Restangular.all('account/user').customPOST("login", {}, {}, - username: @$scope.username - password: @$scope.password - ).then((data) => - @$cookies.username = data.username - @$cookies.key = data.key - @$http.defaults.headers.common['Authorization'] = "ApiKey #{data.username}:#{data.key}" - @authService.loginConfirmed() - , (data) => - console.debug("LoginController submit error: #{data.reason}") - @$scope.errorMsg = data.reason - ) - - authenticateGoogle: => - extraParams = {} - if @$scope.askApproval - extraParams = {approval_prompt: 'force'} - - @Token.getTokenByPopup(extraParams).then((params) => - - # Verify the token before setting it, to avoid the confused deputy problem. - @Restangular.all('account/user/login').customPOST("google", {}, {}, - access_token: params.access_token - ).then((data) => - @$cookies.username = data.username - @$cookies.key = data.key - @$http.defaults.headers.common['Authorization'] = "ApiKey #{data.username}:#{data.key}" - @authService.loginConfirmed() - , (data) => - console.debug("LoginController submit error: #{data.reason}") - @$scope.errorMsg = data.reason - ) - , -> - # Failure getting token from popup. - alert("Failed to get token from popup.") - ) - -LoginCtrl.$inject = ['$scope', '$rootScope', "$http", "$state", "Restangular", "$cookies", "authService", "Token"] - -module.controller("LoginCtrl", LoginCtrl) \ No newline at end of file +module.controller("LoginCtrl", ['$scope','loginService', LoginCtrl]) diff --git a/scripts/vendor/angular-unisson-auth/angular-unisson-auth.js b/scripts/vendor/angular-unisson-auth/angular-unisson-auth.js new file mode 120000 index 0000000..1ae49f4 --- /dev/null +++ b/scripts/vendor/angular-unisson-auth/angular-unisson-auth.js @@ -0,0 +1 @@ +/home/fdy/unisson-env/angular-unisson-auth/angular-unisson-auth.js \ No newline at end of file diff --git a/views/map/map_detail.html b/views/map/map_detail.html index e41641f..8f5de21 100644 --- a/views/map/map_detail.html +++ b/views/map/map_detail.html @@ -59,7 +59,7 @@
- {{ username }} + {{ authVars.username }}  
diff --git a/views/map/map_new.html b/views/map/map_new.html index 7074c4a..b237025 100644 --- a/views/map/map_new.html +++ b/views/map/map_new.html @@ -1,7 +1,7 @@
- - + +
From d10b97b32ba76e41ddb40d1f902c4958cb261584 Mon Sep 17 00:00:00 2001 From: Freddy Limpens Date: Thu, 27 Feb 2014 15:55:30 +0100 Subject: [PATCH 2/3] removed sym link --- scripts/vendor/angular-unisson-auth/angular-unisson-auth.js | 1 - 1 file changed, 1 deletion(-) delete mode 120000 scripts/vendor/angular-unisson-auth/angular-unisson-auth.js diff --git a/scripts/vendor/angular-unisson-auth/angular-unisson-auth.js b/scripts/vendor/angular-unisson-auth/angular-unisson-auth.js deleted file mode 120000 index 1ae49f4..0000000 --- a/scripts/vendor/angular-unisson-auth/angular-unisson-auth.js +++ /dev/null @@ -1 +0,0 @@ -/home/fdy/unisson-env/angular-unisson-auth/angular-unisson-auth.js \ No newline at end of file From 55e8018f192dd4828f21458deb67a9d706d926c1 Mon Sep 17 00:00:00 2001 From: Freddy Limpens Date: Thu, 24 Apr 2014 11:49:28 +0200 Subject: [PATCH 3/3] updated restangular + added config file + fixed var dependencies for unisson login --- index.html | 1 + scripts/app.js | 6 ++---- scripts/config.js.sample | 11 +++++++++++ scripts/vendor/restangular.min.js | 8 +++----- 4 files changed, 17 insertions(+), 9 deletions(-) create mode 100644 scripts/config.js.sample diff --git a/index.html b/index.html index 6edc9d7..c5356b7 100644 --- a/index.html +++ b/index.html @@ -114,6 +114,7 @@ + diff --git a/scripts/app.js b/scripts/app.js index 239867a..89aaab9 100755 --- a/scripts/app.js +++ b/scripts/app.js @@ -1,7 +1,3 @@ -config = { - templateBaseUrl: '/views/', -} - angular.module('map', ['map.controllers', 'map.services', 'map.filters', 'leaflet-directive']); angular.module('common', ['common.filters', 'common.controllers', 'common.services']); @@ -96,6 +92,8 @@ app.config(['$locationProvider', '$stateProvider', '$urlRouterProvider', 'module app.run(['$rootScope', function($rootScope) { $rootScope.MEDIA_URI = 'http://localhost:8000'; $rootScope.CONFIG = config; + $rootScope.loginBaseUrl = config.loginBaseUrl; + $rootScope.homeStateName = config.homeStateName; $rootScope.$on('$stateChangeSuccess', function (event, current, previous) { if ( current.page_title ) diff --git a/scripts/config.js.sample b/scripts/config.js.sample new file mode 100644 index 0000000..39632e4 --- /dev/null +++ b/scripts/config.js.sample @@ -0,0 +1,11 @@ +config = { + templateBaseUrl: '/views/', + bucket_uri: 'http://localhost:8000/bucket/upload/', + useHtml5Mode: false, + media_uri: 'http://localhost:8000', + rest_uri: "http://localhost:8000/api/v0", + rootUrl: "http://localhost:8082/", + applicationAlias : "#/", + homeStateName: 'index', + loginBaseUrl: 'http://localhost:8000/api/v0' +} diff --git a/scripts/vendor/restangular.min.js b/scripts/vendor/restangular.min.js index 5a1bb6c..d43545f 100644 --- a/scripts/vendor/restangular.min.js +++ b/scripts/vendor/restangular.min.js @@ -1,8 +1,6 @@ /** * Restful Resources service for AngularJS apps - * @version v1.0.10 - 2013-07-30 - * @link https://github.com/mgonto/restangular - * @author Martin Gontovnikas + * @version v1.3.1 - 2014-01-29 * @link https://github.com/mgonto/restangular + * @author Martin Gontovnikas * @license MIT License, http://www.opensource.org/licenses/MIT - */ -!function(){var a=angular.module("restangular",[]);a.provider("Restangular",function(){var a={};a.init=function(a,b){function c(a,b,c,d){var e={};return _.each(_.keys(d),function(f){var g=d[f];g.params=_.extend({},g.params,a.defaultRequestParams[g.method.toLowerCase()]),_.isEmpty(g.params)&&delete g.params,e[f]=a.isSafe(g.method)?function(){return b(_.extend(g,{url:c}))}:function(a){return b(_.extend(g,{url:c,data:a}))}}),e}var d=["get","head","options","trace"];b.isSafe=function(a){return _.contains(d,a.toLowerCase())},b.baseUrl=_.isUndefined(b.baseUrl)?"":b.baseUrl,a.setBaseUrl=function(a){b.baseUrl="/"===_.last(a)?_.initial(a).join(""):a},b.extraFields=b.extraFields||[],a.setExtraFields=function(a){b.extraFields=a},b.defaultHttpFields=b.defaultHttpFields||{},a.setDefaultHttpFields=function(a){b.defaultHttpFields=a},b.withHttpDefaults=function(a){return _.defaults(a,b.defaultHttpFields)},b.defaultRequestParams=b.defaultRequestParams||{get:{},post:{},put:{},remove:{},common:{}},a.setDefaultRequestParams=function(a){b.defaultRequestParams.common=a},a.requestParams=b.defaultRequestParams,b.defaultHeaders=b.defaultHeaders||{},a.setDefaultHeaders=function(a){b.defaultHeaders=a},b.methodOverriders=b.methodOverriders||[],a.setMethodOverriders=function(a){var c=_.extend([],a);b.isOverridenMethod("delete",c)&&c.push("remove"),b.methodOverriders=c},b.isOverridenMethod=function(a,c){var d=c||b.methodOverriders;return!_.isUndefined(_.find(d,function(b){return b.toLowerCase()===a.toLowerCase()}))},b.urlCreator=b.urlCreator||"path",a.setUrlCreator=function(a){if(!_.has(b.urlCreatorFactory,a))throw new Error("URL Path selected isn't valid");b.urlCreator=a},b.restangularFields=b.restangularFields||{id:"id",route:"route",parentResource:"parentResource",restangularCollection:"restangularCollection",cannonicalId:"__cannonicalId"},a.setRestangularFields=function(a){b.restangularFields=_.extend(b.restangularFields,a)},b.setIdToElem=function(a,c){var d=b.restangularFields.id.split("."),e=a;_.each(_.initial(d),function(a){e[a]={},e=e[a]}),e[_.last(d)]=c},b.getIdFromElem=function(a){var c=b.restangularFields.id.split("."),d=angular.copy(a);return _.each(c,function(a){d=d[a]}),d},b.useCannonicalId=_.isUndefined(b.useCannonicalId)?!1:b.useCannonicalId,a.setUseCannonicalId=function(a){b.useCannonicalId=a},b.responseExtractor=b.responseExtractor||function(a){return a},a.setResponseExtractor=function(a){b.responseExtractor=a},a.setResponseInterceptor=a.setResponseExtractor,b.fullRequestInterceptor=b.fullRequestInterceptor||function(a,b,c,d,e,f){return{element:a,headers:e,params:f}},a.setRequestInterceptor=function(a){b.fullRequestInterceptor=function(b,c,d,e,f,g){return{headers:f,params:g,element:a(b,c,d,e)}}},a.setFullRequestInterceptor=function(a){b.fullRequestInterceptor=a},b.errorInterceptor=b.errorInterceptor||function(){},a.setErrorInterceptor=function(a){b.errorInterceptor=a},b.onBeforeElemRestangularized=b.onBeforeElemRestangularized||function(a){return a},a.setOnBeforeElemRestangularized=function(a){b.onBeforeElemRestangularized=a},b.onElemRestangularized=b.onElemRestangularized||function(a){return a},a.setOnElemRestangularized=function(a){b.onElemRestangularized=a},a.setListTypeIsArray=function(){},b.shouldSaveParent=b.shouldSaveParent||function(){return!0},a.setParentless=function(a){_.isArray(a)?b.shouldSaveParent=function(b){return!_.contains(a,b)}:_.isBoolean(a)&&(b.shouldSaveParent=function(){return!a})},b.suffix=_.isUndefined(b.suffix)?null:b.suffix,a.setRequestSuffix=function(a){b.suffix=a},b.transformers=b.transformers||{},a.addElementTransformer=function(a,c,d){var e=null,f=null;2===arguments.length?f=c:(f=d,e=c);var g=b.transformers[a];g||(g=b.transformers[a]=[]),g.push(function(a,b){return _.isNull(e)||a==e?f(b):b})},a.extendCollection=function(b,c){return a.addElementTransformer(b,!0,c)},a.extendModel=function(b,c){return a.addElementTransformer(b,!1,c)},b.transformElem=function(a,c,d,e){var f=b.transformers[d],g=a;return f&&_.each(f,function(a){g=a(c,g)}),b.onElemRestangularized(g,c,d,e)},b.fullResponse=_.isUndefined(b.fullResponse)?!1:b.fullResponse,a.setFullResponse=function(a){b.fullResponse=a},b.urlCreatorFactory={};var e=function(){};e.prototype.setConfig=function(a){this.config=a},e.prototype.parentsArray=function(a){for(var b=[];a;)b.push(a),a=a[this.config.restangularFields.parentResource];return b.reverse()},e.prototype.resource=function(a,b,d,e,f){var g=_.defaults(e||{},this.config.defaultRequestParams.common),h=_.defaults(d||{},this.config.defaultHeaders),i=this.base(a);return i+=f?"/"+f:"",i+=this.config.suffix||"",c(this.config,b,i,{getList:this.config.withHttpDefaults({method:"GET",params:g,headers:h}),get:this.config.withHttpDefaults({method:"GET",params:g,headers:h}),put:this.config.withHttpDefaults({method:"PUT",params:g,headers:h}),post:this.config.withHttpDefaults({method:"POST",params:g,headers:h}),remove:this.config.withHttpDefaults({method:"DELETE",params:g,headers:h}),head:this.config.withHttpDefaults({method:"HEAD",params:g,headers:h}),trace:this.config.withHttpDefaults({method:"TRACE",params:g,headers:h}),options:this.config.withHttpDefaults({method:"OPTIONS",params:g,headers:h}),patch:this.config.withHttpDefaults({method:"PATCH",params:g,headers:h})})};var f=function(){};f.prototype=new e,f.prototype.base=function(a){var c=this;return this.config.baseUrl+_.reduce(this.parentsArray(a),function(a,d){var e=a+"/"+d[c.config.restangularFields.route];if(!d[c.config.restangularFields.restangularCollection]){var f;f=b.useCannonicalId?d[b.restangularFields.cannonicalId]:c.config.getIdFromElem(d),""===f||_.isUndefined(f)||_.isNull(f)||(e+="/"+f)}return e},"")},f.prototype.fetchUrl=function(a,b){var c=this.base(a);return b&&(c+="/"+b),c},b.urlCreatorFactory.path=f};var b={};a.init(this,b),this.$get=["$http","$q",function(c,d){function e(f){function g(a,b,c){if(b[f.restangularFields.route]=c,b.getRestangularUrl=_.bind(H.fetchUrl,H,b),b.addRestangularMethod=_.bind(E,b),b.one=_.bind(h,b,b),b.all=_.bind(i,b,b),a&&f.shouldSaveParent(c)){var d=_.union(_.values(_.pick(f.restangularFields,["id","route","parentResource"])),f.extraFields);b[f.restangularFields.parentResource]=_.pick(a,d)}else b[f.restangularFields.parentResource]=null;return b}function h(a,b,c){var d={};return f.setIdToElem(d,c),q(a,d,b)}function i(a,b){return r(a,{},b,!0)}function j(a,b){return a.call=_.bind(k,a),a.get=_.bind(l,a),a[f.restangularFields.restangularCollection]=b,b&&(a.push=_.bind(k,a,"push")),a}function k(a){var b=d.defer(),c=arguments;return this.then(function(d){var e=Array.prototype.slice.call(c,1),f=d[a];f.apply(d,e),b.resolve(d)}),j(b.promise,this[f.restangularFields.restangularCollection])}function l(a){var b=d.defer();return this.then(function(c){b.resolve(c[a])}),j(b.promise,this[f.restangularFields.restangularCollection])}function m(a,b,c){return f.fullResponse?a.resolve(_.extend(b,{data:c})):(a.resolve(c),void 0)}function n(a){return _.omit(a,_.values(_.omit(f.restangularFields,"id")))}function o(a){a.customOperation=_.bind(D,a),_.each(["put","post","get","delete"],function(b){_.each(["do","custom"],function(c){var d="delete"===b?"remove":b,e=c+b.toUpperCase();a[e]=_.bind(D,a,d)})}),a.customGETLIST=_.bind(t,a),a.doGETLIST=a.customGETLIST}function p(a){var b=angular.copy(a);return q(b[f.restangularFields.parentResource],b,b[f.restangularFields.route])}function q(a,b,c){var d=f.onBeforeElemRestangularized(b,!1,c),e=g(a,d,c);return f.useCannonicalId&&(e[f.restangularFields.cannonicalId]=f.getIdFromElem(e)),e[f.restangularFields.restangularCollection]=!1,e.get=_.bind(v,e),e.getList=_.bind(t,e),e.put=_.bind(x,e),e.post=_.bind(y,e),e.remove=_.bind(w,e),e.head=_.bind(z,e),e.trace=_.bind(A,e),e.options=_.bind(B,e),e.patch=_.bind(C,e),o(e),f.transformElem(e,!1,c,G)}function r(a,b,c){var d=f.onBeforeElemRestangularized(b,!0,c),e=g(a,d,c);return e[f.restangularFields.restangularCollection]=!0,e.post=_.bind(y,e,null),e.head=_.bind(z,e),e.trace=_.bind(A,e),e.putElement=_.bind(s,e),e.options=_.bind(B,e),e.patch=_.bind(C,e),e.getList=_.bind(t,e,null),o(e),f.transformElem(e,!0,c,G)}function s(a,b,c){var e=this,f=this[a],g=d.defer();return f.put(b,c).then(function(b){var c=p(e);c[a]=b,g.resolve(c)},function(a){g.reject(a)}),j(g.promise,!0)}function t(a,b,e){var g=this,h=d.defer(),i="getList",k=H.fetchUrl(this,a),l=a||g[f.restangularFields.route],n=f.fullRequestInterceptor(null,i,l,k,e||{},b||{});return H.resource(this,c,n.headers,n.params,a).getList().then(function(b){var c=b.data,d=f.responseExtractor(c,i,l,k),e=_.map(d,function(b){return g[f.restangularFields.restangularCollection]?q(g[f.restangularFields.parentResource],b,g[f.restangularFields.route]):q(g,b,a)});e=_.extend(d,e),g[f.restangularFields.restangularCollection]?m(h,b,r(null,e,g[f.restangularFields.route])):m(h,b,r(g,e,a))},function(a){f.errorInterceptor(a),h.reject(a)}),j(h.promise,!0)}function u(a,b,e,g,h){var i=this,k=d.defer(),l=e||{},o=b||this[f.restangularFields.route],p=H.fetchUrl(this,b),r=g||("remove"===a?void 0:n(this)),s=f.fullRequestInterceptor(r,a,o,p,h||{},l||{}),t=function(c){var d=c.data,e=f.responseExtractor(d,a,o,p);e?"post"!==a||i[f.restangularFields.restangularCollection]?m(k,c,q(i[f.restangularFields.parentResource],e,i[f.restangularFields.route])):m(k,c,q(i,e,b)):m(k,c,void 0)},u=function(a){f.errorInterceptor(a),k.reject(a)},v=a,w=_.extend({},s.headers),x=f.isOverridenMethod(a);return x&&(v="post",w=_.extend(w,{"X-HTTP-Method-Override":a})),f.isSafe(a)?x?H.resource(this,c,w,s.params,b)[v]({}).then(t,u):H.resource(this,c,w,s.params,b)[v]().then(t,u):H.resource(this,c,w,s.params,b)[v](s.element).then(t,u),j(k.promise)}function v(a,b){return _.bind(u,this)("get",void 0,a,void 0,b)}function w(a,b){return _.bind(u,this)("remove",void 0,a,void 0,b)}function x(a,b){return _.bind(u,this)("put",void 0,a,void 0,b)}function y(a,b,c,d){return _.bind(u,this)("post",a,c,b,d)}function z(a,b){return _.bind(u,this)("head",void 0,a,void 0,b)}function A(a,b){return _.bind(u,this)("trace",void 0,a,void 0,b)}function B(a,b){return _.bind(u,this)("options",void 0,a,void 0,b)}function C(a,b,c){return _.bind(u,this)("patch",void 0,b,a,c)}function D(a,b,c,d,e){return _.bind(u,this)(a,b,c,e,d)}function E(a,b,c,d,e,g){var h;h="getList"===b?_.bind(t,this,c):_.bind(D,this,b,c);var i=function(a,b,c){var f=_.defaults({params:a,headers:b,elem:c},{params:d,headers:e,elem:g});return h(f.params,f.headers,f.elem)};this[a]=f.isSafe(b)?i:function(a,b,c){return i(b,c,a)}}function F(c){var d=angular.copy(b);return a.init(d,d),c(d),e(d)}var G={},H=new f.urlCreatorFactory[f.urlCreator];return H.setConfig(f),a.init(G,f),G.copy=_.bind(p,G),G.withConfig=_.bind(F,G),G.one=_.bind(h,G,null),G.all=_.bind(i,G,null),G.restangularizeElement=_.bind(q,G),G.restangularizeCollection=_.bind(r,G),G}return e(b)}]})}(); + */!function(){var a=angular.module("restangular",[]);a.provider("Restangular",function(){var a={};a.init=function(a,b){function c(a,b,c,d){var e={};return _.each(_.keys(d),function(f){var g=d[f];g.params=_.extend({},g.params,a.defaultRequestParams[g.method.toLowerCase()]),_.isEmpty(g.params)&&delete g.params,e[f]=a.isSafe(g.method)?function(){return b(_.extend(g,{url:c}))}:function(a){return b(_.extend(g,{url:c,data:a}))}}),e}a.configuration=b;var d=["get","head","options","trace","getlist"];b.isSafe=function(a){return _.contains(d,a.toLowerCase())};var e=/^https?:\/\//i;b.isAbsoluteUrl=function(a){return _.isUndefined(b.absoluteUrl)||_.isNull(b.absoluteUrl)?a&&e.test(a):b.absoluteUrl},b.absoluteUrl=_.isUndefined(b.absoluteUrl)?!1:!0,a.setSelfLinkAbsoluteUrl=function(a){b.absoluteUrl=a},b.baseUrl=_.isUndefined(b.baseUrl)?"":b.baseUrl,a.setBaseUrl=function(a){return b.baseUrl=/\/$/.test(a)?a.substring(0,a.length-1):a,this},b.extraFields=b.extraFields||[],a.setExtraFields=function(a){return b.extraFields=a,this},b.defaultHttpFields=b.defaultHttpFields||{},a.setDefaultHttpFields=function(a){return b.defaultHttpFields=a,this},b.withHttpValues=function(a,c){return _.defaults(c,a,b.defaultHttpFields)},b.encodeIds=_.isUndefined(b.encodeIds)?!0:b.encodeIds,a.setEncodeIds=function(a){b.encodeIds=a},b.defaultRequestParams=b.defaultRequestParams||{get:{},post:{},put:{},remove:{},common:{}},a.setDefaultRequestParams=function(a,c){var d=[],e=c||a;return _.isUndefined(c)?d.push("common"):_.isArray(a)?d=a:d.push(a),_.each(d,function(a){b.defaultRequestParams[a]=e}),this},a.requestParams=b.defaultRequestParams,b.defaultHeaders=b.defaultHeaders||{},a.setDefaultHeaders=function(c){return b.defaultHeaders=c,a.defaultHeaders=b.defaultHeaders,this},a.defaultHeaders=b.defaultHeaders,b.methodOverriders=b.methodOverriders||[],a.setMethodOverriders=function(a){var c=_.extend([],a);return b.isOverridenMethod("delete",c)&&c.push("remove"),b.methodOverriders=c,this},b.jsonp=_.isUndefined(b.jsonp)?!1:b.jsonp,a.setJsonp=function(a){b.jsonp=a},b.isOverridenMethod=function(a,c){var d=c||b.methodOverriders;return!_.isUndefined(_.find(d,function(b){return b.toLowerCase()===a.toLowerCase()}))},b.urlCreator=b.urlCreator||"path",a.setUrlCreator=function(a){if(!_.has(b.urlCreatorFactory,a))throw new Error("URL Path selected isn't valid");return b.urlCreator=a,this},b.restangularFields=b.restangularFields||{id:"id",route:"route",parentResource:"parentResource",restangularCollection:"restangularCollection",cannonicalId:"__cannonicalId",etag:"restangularEtag",selfLink:"href",get:"get",getList:"getList",put:"put",post:"post",remove:"remove",head:"head",trace:"trace",options:"options",patch:"patch",getRestangularUrl:"getRestangularUrl",getRequestedUrl:"getRequestedUrl",putElement:"putElement",addRestangularMethod:"addRestangularMethod",getParentList:"getParentList",clone:"clone",ids:"ids",httpConfig:"_$httpConfig",reqParams:"reqParams",one:"one",all:"all",several:"several",oneUrl:"oneUrl",allUrl:"allUrl",customPUT:"customPUT",customPOST:"customPOST",customDELETE:"customDELETE",customGET:"customGET",customGETLIST:"customGETLIST",customOperation:"customOperation",doPUT:"doPUT",doPOST:"doPOST",doDELETE:"doDELETE",doGET:"doGET",doGETLIST:"doGETLIST",fromServer:"$fromServer",withConfig:"withConfig",withHttpConfig:"withHttpConfig"},a.setRestangularFields=function(a){return b.restangularFields=_.extend(b.restangularFields,a),this},b.isRestangularized=function(a){return!!a[b.restangularFields.one]||!!a[b.restangularFields.all]},b.setFieldToElem=function(a,b,c){var d=a.split("."),e=b;return _.each(_.initial(d),function(a){e[a]={},e=e[a]}),e[_.last(d)]=c,this},b.getFieldFromElem=function(a,b){var c=a.split("."),d=b;return _.each(c,function(a){d&&(d=d[a])}),angular.copy(d)},b.setIdToElem=function(a,c){return b.setFieldToElem(b.restangularFields.id,a,c),this},b.getIdFromElem=function(a){return b.getFieldFromElem(b.restangularFields.id,a)},b.isValidId=function(a){return""!==a&&!_.isUndefined(a)&&!_.isNull(a)},b.setUrlToElem=function(a,c){return b.setFieldToElem(b.restangularFields.selfLink,a,c),this},b.getUrlFromElem=function(a){return b.getFieldFromElem(b.restangularFields.selfLink,a)},b.useCannonicalId=_.isUndefined(b.useCannonicalId)?!1:b.useCannonicalId,a.setUseCannonicalId=function(a){return b.useCannonicalId=a,this},b.getCannonicalIdFromElem=function(a){var c=a[b.restangularFields.cannonicalId],d=b.isValidId(c)?c:b.getIdFromElem(a);return d},b.responseInterceptors=b.responseInterceptors||[],b.defaultResponseInterceptor=function(a){return a},b.responseExtractor=function(a,c,d,e,f,g){var h=angular.copy(b.responseInterceptors);h.push(b.defaultResponseInterceptor);var i=a;return _.each(h,function(a){i=a(i,c,d,e,f,g)}),i},a.addResponseInterceptor=function(a){return b.responseInterceptors.push(a),this},a.setResponseInterceptor=a.addResponseInterceptor,a.setResponseExtractor=a.addResponseInterceptor,b.requestInterceptors=b.requestInterceptors||[],b.defaultInterceptor=function(a,b,c,d,e,f,g){return{element:a,headers:e,params:f,httpConfig:g}},b.fullRequestInterceptor=function(a,c,d,e,f,g,h){var i=angular.copy(b.requestInterceptors);return i.push(b.defaultInterceptor),_.reduce(i,function(b,i){return _.defaults(b,i(a,c,d,e,f,g,h))},{})},a.addRequestInterceptor=function(a){return b.requestInterceptors.push(function(b,c,d,e,f,g,h){return{headers:f,params:g,element:a(b,c,d,e),httpConfig:h}}),this},a.setRequestInterceptor=a.addRequestInterceptor,a.addFullRequestInterceptor=function(a){return b.requestInterceptors.push(a),this},a.setFullRequestInterceptor=a.addFullRequestInterceptor,b.errorInterceptor=b.errorInterceptor||function(){},a.setErrorInterceptor=function(a){return b.errorInterceptor=a,this},b.onBeforeElemRestangularized=b.onBeforeElemRestangularized||function(a){return a},a.setOnBeforeElemRestangularized=function(a){return b.onBeforeElemRestangularized=a,this},b.onElemRestangularized=b.onElemRestangularized||function(a){return a},a.setOnElemRestangularized=function(a){return b.onElemRestangularized=a,this},b.shouldSaveParent=b.shouldSaveParent||function(){return!0},a.setParentless=function(a){return _.isArray(a)?b.shouldSaveParent=function(b){return!_.contains(a,b)}:_.isBoolean(a)&&(b.shouldSaveParent=function(){return!a}),this},b.suffix=_.isUndefined(b.suffix)?null:b.suffix,a.setRequestSuffix=function(a){return b.suffix=a,this},b.transformers=b.transformers||{},a.addElementTransformer=function(a,c,d){var e=null,f=null;2===arguments.length?f=c:(f=d,e=c);var g=b.transformers[a];g||(g=b.transformers[a]=[]),g.push(function(a,b){return _.isNull(e)||a==e?f(b):b})},a.extendCollection=function(b,c){return a.addElementTransformer(b,!0,c)},a.extendModel=function(b,c){return a.addElementTransformer(b,!1,c)},b.transformElem=function(a,c,d,e){if(!b.transformLocalElements&&!a[b.restangularFields.fromServer])return a;var f=b.transformers[d],g=a;return f&&_.each(f,function(a){g=a(c,g)}),b.onElemRestangularized(g,c,d,e)},b.transformLocalElements=_.isUndefined(b.transformLocalElements)?!0:b.transformLocalElements,a.setTransformOnlyServerElements=function(a){b.transformLocalElements=!a},b.fullResponse=_.isUndefined(b.fullResponse)?!1:b.fullResponse,a.setFullResponse=function(a){return b.fullResponse=a,this},b.urlCreatorFactory={};var f=function(){};f.prototype.setConfig=function(a){return this.config=a,this},f.prototype.parentsArray=function(a){for(var b=[];a;)b.push(a),a=a[this.config.restangularFields.parentResource];return b.reverse()},f.prototype.resource=function(a,d,e,f,g,h,i,j){var k=_.defaults(g||{},this.config.defaultRequestParams.common),l=_.defaults(f||{},this.config.defaultHeaders);i&&(b.isSafe(j)?l["If-None-Match"]=i:l["If-Match"]=i);var m=this.base(a);if(h){var n="";/\/$/.test(m)||(n+="/"),n+=h,m+=n}return this.config.suffix&&-1===m.indexOf(this.config.suffix,m.length-this.config.suffix.length)&&!this.config.getUrlFromElem(a)&&(m+=this.config.suffix),a[this.config.restangularFields.httpConfig]=void 0,c(this.config,d,m,{getList:this.config.withHttpValues(e,{method:"GET",params:k,headers:l}),get:this.config.withHttpValues(e,{method:"GET",params:k,headers:l}),jsonp:this.config.withHttpValues(e,{method:"jsonp",params:k,headers:l}),put:this.config.withHttpValues(e,{method:"PUT",params:k,headers:l}),post:this.config.withHttpValues(e,{method:"POST",params:k,headers:l}),remove:this.config.withHttpValues(e,{method:"DELETE",params:k,headers:l}),head:this.config.withHttpValues(e,{method:"HEAD",params:k,headers:l}),trace:this.config.withHttpValues(e,{method:"TRACE",params:k,headers:l}),options:this.config.withHttpValues(e,{method:"OPTIONS",params:k,headers:l}),patch:this.config.withHttpValues(e,{method:"PATCH",params:k,headers:l})})};var g=function(){};g.prototype=new f,g.prototype.base=function(a){var c=this;return _.reduce(this.parentsArray(a),function(a,d){var e,f=c.config.getUrlFromElem(d);if(f){if(c.config.isAbsoluteUrl(f))return f;e=f}else if(e=d[c.config.restangularFields.route],d[c.config.restangularFields.restangularCollection]){var g=d[c.config.restangularFields.ids];g&&(e+="/"+g.join(","))}else{var h;h=c.config.useCannonicalId?c.config.getCannonicalIdFromElem(d):c.config.getIdFromElem(d),b.isValidId(h)&&(e+="/"+(c.config.encodeIds?encodeURIComponent(h):h))}return a.replace(/\/$/,"")+"/"+e},this.config.baseUrl)},g.prototype.fetchUrl=function(a,b){var c=this.base(a);return b&&(c+="/"+b),c},g.prototype.fetchRequestedUrl=function(a,c){function d(a){var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(c);return b.sort()}function e(a,b,c){for(var e=d(a),f=0;f