restangular.js 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356
  1. /**
  2. * Restful Resources service for AngularJS apps
  3. * @version v1.4.0 - 2015-04-03 * @link https://github.com/mgonto/restangular
  4. * @author Martin Gontovnikas <martin@gon.to>
  5. * @license MIT License, http://www.opensource.org/licenses/MIT
  6. */(function() {
  7. var restangular = angular.module('restangular', []);
  8. restangular.provider('Restangular', function() {
  9. // Configuration
  10. var Configurer = {};
  11. Configurer.init = function(object, config) {
  12. object.configuration = config;
  13. /**
  14. * Those are HTTP safe methods for which there is no need to pass any data with the request.
  15. */
  16. var safeMethods= ['get', 'head', 'options', 'trace', 'getlist'];
  17. config.isSafe = function(operation) {
  18. return _.contains(safeMethods, operation.toLowerCase());
  19. };
  20. var absolutePattern = /^https?:\/\//i;
  21. config.isAbsoluteUrl = function(string) {
  22. return _.isUndefined(config.absoluteUrl) || _.isNull(config.absoluteUrl) ?
  23. string && absolutePattern.test(string) :
  24. config.absoluteUrl;
  25. };
  26. config.absoluteUrl = _.isUndefined(config.absoluteUrl) ? true : config.absoluteUrl;
  27. object.setSelfLinkAbsoluteUrl = function(value) {
  28. config.absoluteUrl = value;
  29. };
  30. /**
  31. * This is the BaseURL to be used with Restangular
  32. */
  33. config.baseUrl = _.isUndefined(config.baseUrl) ? '' : config.baseUrl;
  34. object.setBaseUrl = function(newBaseUrl) {
  35. config.baseUrl = /\/$/.test(newBaseUrl) ?
  36. newBaseUrl.substring(0, newBaseUrl.length-1) :
  37. newBaseUrl;
  38. return this;
  39. };
  40. /**
  41. * Sets the extra fields to keep from the parents
  42. */
  43. config.extraFields = config.extraFields || [];
  44. object.setExtraFields = function(newExtraFields) {
  45. config.extraFields = newExtraFields;
  46. return this;
  47. };
  48. /**
  49. * Some default $http parameter to be used in EVERY call
  50. **/
  51. config.defaultHttpFields = config.defaultHttpFields || {};
  52. object.setDefaultHttpFields = function(values) {
  53. config.defaultHttpFields = values;
  54. return this;
  55. };
  56. config.withHttpValues = function(httpLocalConfig, obj) {
  57. return _.defaults(obj, httpLocalConfig, config.defaultHttpFields);
  58. };
  59. config.encodeIds = _.isUndefined(config.encodeIds) ? true : config.encodeIds;
  60. object.setEncodeIds = function(encode) {
  61. config.encodeIds = encode;
  62. };
  63. config.defaultRequestParams = config.defaultRequestParams || {
  64. get: {},
  65. post: {},
  66. put: {},
  67. remove: {},
  68. common: {}
  69. };
  70. object.setDefaultRequestParams = function(param1, param2) {
  71. var methods = [],
  72. params = param2 || param1;
  73. if (!_.isUndefined(param2)) {
  74. if (_.isArray(param1)) {
  75. methods = param1;
  76. } else {
  77. methods.push(param1);
  78. }
  79. } else {
  80. methods.push('common');
  81. }
  82. _.each(methods, function (method) {
  83. config.defaultRequestParams[method] = params;
  84. });
  85. return this;
  86. };
  87. object.requestParams = config.defaultRequestParams;
  88. config.defaultHeaders = config.defaultHeaders || {};
  89. object.setDefaultHeaders = function(headers) {
  90. config.defaultHeaders = headers;
  91. object.defaultHeaders = config.defaultHeaders;
  92. return this;
  93. };
  94. object.defaultHeaders = config.defaultHeaders;
  95. /**
  96. * Method overriders will set which methods are sent via POST with an X-HTTP-Method-Override
  97. **/
  98. config.methodOverriders = config.methodOverriders || [];
  99. object.setMethodOverriders = function(values) {
  100. var overriders = _.extend([], values);
  101. if (config.isOverridenMethod('delete', overriders)) {
  102. overriders.push('remove');
  103. }
  104. config.methodOverriders = overriders;
  105. return this;
  106. };
  107. config.jsonp = _.isUndefined(config.jsonp) ? false : config.jsonp;
  108. object.setJsonp = function(active) {
  109. config.jsonp = active;
  110. };
  111. config.isOverridenMethod = function(method, values) {
  112. var search = values || config.methodOverriders;
  113. return !_.isUndefined(_.find(search, function(one) {
  114. return one.toLowerCase() === method.toLowerCase();
  115. }));
  116. };
  117. /**
  118. * Sets the URL creator type. For now, only Path is created. In the future we'll have queryParams
  119. **/
  120. config.urlCreator = config.urlCreator || 'path';
  121. object.setUrlCreator = function(name) {
  122. if (!_.has(config.urlCreatorFactory, name)) {
  123. throw new Error('URL Path selected isn\'t valid');
  124. }
  125. config.urlCreator = name;
  126. return this;
  127. };
  128. /**
  129. * You can set the restangular fields here. The 3 required fields for Restangular are:
  130. *
  131. * id: Id of the element
  132. * route: name of the route of this element
  133. * parentResource: the reference to the parent resource
  134. *
  135. * All of this fields except for id, are handled (and created) by Restangular. By default,
  136. * the field values will be id, route and parentResource respectively
  137. */
  138. config.restangularFields = config.restangularFields || {
  139. id: 'id',
  140. route: 'route',
  141. parentResource: 'parentResource',
  142. restangularCollection: 'restangularCollection',
  143. cannonicalId: '__cannonicalId',
  144. etag: 'restangularEtag',
  145. selfLink: 'href',
  146. get: 'get',
  147. getList: 'getList',
  148. put: 'put',
  149. post: 'post',
  150. remove: 'remove',
  151. head: 'head',
  152. trace: 'trace',
  153. options: 'options',
  154. patch: 'patch',
  155. getRestangularUrl: 'getRestangularUrl',
  156. getRequestedUrl: 'getRequestedUrl',
  157. putElement: 'putElement',
  158. addRestangularMethod: 'addRestangularMethod',
  159. getParentList: 'getParentList',
  160. clone: 'clone',
  161. ids: 'ids',
  162. httpConfig: '_$httpConfig',
  163. reqParams: 'reqParams',
  164. one: 'one',
  165. all: 'all',
  166. several: 'several',
  167. oneUrl: 'oneUrl',
  168. allUrl: 'allUrl',
  169. customPUT: 'customPUT',
  170. customPOST: 'customPOST',
  171. customDELETE: 'customDELETE',
  172. customGET: 'customGET',
  173. customGETLIST: 'customGETLIST',
  174. customOperation: 'customOperation',
  175. doPUT: 'doPUT',
  176. doPOST: 'doPOST',
  177. doDELETE: 'doDELETE',
  178. doGET: 'doGET',
  179. doGETLIST: 'doGETLIST',
  180. fromServer: 'fromServer',
  181. withConfig: 'withConfig',
  182. withHttpConfig: 'withHttpConfig',
  183. singleOne: 'singleOne',
  184. plain: 'plain',
  185. save: 'save',
  186. restangularized: 'restangularized'
  187. };
  188. object.setRestangularFields = function(resFields) {
  189. config.restangularFields =
  190. _.extend(config.restangularFields, resFields);
  191. return this;
  192. };
  193. config.isRestangularized = function(obj) {
  194. return !!obj[config.restangularFields.restangularized];
  195. };
  196. config.setFieldToElem = function(field, elem, value) {
  197. var properties = field.split('.');
  198. var idValue = elem;
  199. _.each(_.initial(properties), function(prop) {
  200. idValue[prop] = {};
  201. idValue = idValue[prop];
  202. });
  203. idValue[_.last(properties)] = value;
  204. return this;
  205. };
  206. config.getFieldFromElem = function(field, elem) {
  207. var properties = field.split('.');
  208. var idValue = elem;
  209. _.each(properties, function(prop) {
  210. if (idValue) {
  211. idValue = idValue[prop];
  212. }
  213. });
  214. return angular.copy(idValue);
  215. };
  216. config.setIdToElem = function(elem, id /*, route */) {
  217. config.setFieldToElem(config.restangularFields.id, elem, id);
  218. return this;
  219. };
  220. config.getIdFromElem = function(elem) {
  221. return config.getFieldFromElem(config.restangularFields.id, elem);
  222. };
  223. config.isValidId = function(elemId) {
  224. return '' !== elemId && !_.isUndefined(elemId) && !_.isNull(elemId);
  225. };
  226. config.setUrlToElem = function(elem, url /*, route */) {
  227. config.setFieldToElem(config.restangularFields.selfLink, elem, url);
  228. return this;
  229. };
  230. config.getUrlFromElem = function(elem) {
  231. return config.getFieldFromElem(config.restangularFields.selfLink, elem);
  232. };
  233. config.useCannonicalId = _.isUndefined(config.useCannonicalId) ? false : config.useCannonicalId;
  234. object.setUseCannonicalId = function(value) {
  235. config.useCannonicalId = value;
  236. return this;
  237. };
  238. config.getCannonicalIdFromElem = function(elem) {
  239. var cannonicalId = elem[config.restangularFields.cannonicalId];
  240. var actualId = config.isValidId(cannonicalId) ? cannonicalId : config.getIdFromElem(elem);
  241. return actualId;
  242. };
  243. /**
  244. * Sets the Response parser. This is used in case your response isn't directly the data.
  245. * For example if you have a response like {meta: {'meta'}, data: {name: 'Gonto'}}
  246. * you can extract this data which is the one that needs wrapping
  247. *
  248. * The ResponseExtractor is a function that receives the response and the method executed.
  249. */
  250. config.responseInterceptors = config.responseInterceptors || [];
  251. config.defaultResponseInterceptor = function(data /*, operation, what, url, response, deferred */) {
  252. return data;
  253. };
  254. config.responseExtractor = function(data, operation, what, url, response, deferred) {
  255. var interceptors = angular.copy(config.responseInterceptors);
  256. interceptors.push(config.defaultResponseInterceptor);
  257. var theData = data;
  258. _.each(interceptors, function(interceptor) {
  259. theData = interceptor(theData, operation,
  260. what, url, response, deferred);
  261. });
  262. return theData;
  263. };
  264. object.addResponseInterceptor = function(extractor) {
  265. config.responseInterceptors.push(extractor);
  266. return this;
  267. };
  268. config.errorInterceptors = config.errorInterceptors || [];
  269. object.addErrorInterceptor = function(interceptor) {
  270. config.errorInterceptors.push(interceptor);
  271. return this;
  272. };
  273. object.setResponseInterceptor = object.addResponseInterceptor;
  274. object.setResponseExtractor = object.addResponseInterceptor;
  275. object.setErrorInterceptor = object.addErrorInterceptor;
  276. /**
  277. * Response interceptor is called just before resolving promises.
  278. */
  279. /**
  280. * Request interceptor is called before sending an object to the server.
  281. */
  282. config.requestInterceptors = config.requestInterceptors || [];
  283. config.defaultInterceptor = function(element, operation, path, url, headers, params, httpConfig) {
  284. return {
  285. element: element,
  286. headers: headers,
  287. params: params,
  288. httpConfig: httpConfig
  289. };
  290. };
  291. config.fullRequestInterceptor = function(element, operation, path, url, headers, params, httpConfig) {
  292. var interceptors = angular.copy(config.requestInterceptors);
  293. var defaultRequest = config.defaultInterceptor(element, operation, path, url, headers, params, httpConfig);
  294. return _.reduce(interceptors, function(request, interceptor) {
  295. return _.extend(request, interceptor(request.element, operation,
  296. path, url, request.headers, request.params, request.httpConfig));
  297. }, defaultRequest);
  298. };
  299. object.addRequestInterceptor = function(interceptor) {
  300. config.requestInterceptors.push(function(elem, operation, path, url, headers, params, httpConfig) {
  301. return {
  302. headers: headers,
  303. params: params,
  304. element: interceptor(elem, operation, path, url),
  305. httpConfig: httpConfig
  306. };
  307. });
  308. return this;
  309. };
  310. object.setRequestInterceptor = object.addRequestInterceptor;
  311. object.addFullRequestInterceptor = function(interceptor) {
  312. config.requestInterceptors.push(interceptor);
  313. return this;
  314. };
  315. object.setFullRequestInterceptor = object.addFullRequestInterceptor;
  316. config.onBeforeElemRestangularized = config.onBeforeElemRestangularized || function(elem) {
  317. return elem;
  318. };
  319. object.setOnBeforeElemRestangularized = function(post) {
  320. config.onBeforeElemRestangularized = post;
  321. return this;
  322. };
  323. object.setRestangularizePromiseInterceptor = function(interceptor) {
  324. config.restangularizePromiseInterceptor = interceptor;
  325. return this;
  326. };
  327. /**
  328. * This method is called after an element has been "Restangularized".
  329. *
  330. * It receives the element, a boolean indicating if it's an element or a collection
  331. * and the name of the model
  332. *
  333. */
  334. config.onElemRestangularized = config.onElemRestangularized || function(elem) {
  335. return elem;
  336. };
  337. object.setOnElemRestangularized = function(post) {
  338. config.onElemRestangularized = post;
  339. return this;
  340. };
  341. config.shouldSaveParent = config.shouldSaveParent || function() {
  342. return true;
  343. };
  344. object.setParentless = function(values) {
  345. if (_.isArray(values)) {
  346. config.shouldSaveParent = function(route) {
  347. return !_.contains(values, route);
  348. };
  349. } else if (_.isBoolean(values)) {
  350. config.shouldSaveParent = function() {
  351. return !values;
  352. };
  353. }
  354. return this;
  355. };
  356. /**
  357. * This lets you set a suffix to every request.
  358. *
  359. * For example, if your api requires that for JSon requests you do /users/123.json, you can set that
  360. * in here.
  361. *
  362. *
  363. * By default, the suffix is null
  364. */
  365. config.suffix = _.isUndefined(config.suffix) ? null : config.suffix;
  366. object.setRequestSuffix = function(newSuffix) {
  367. config.suffix = newSuffix;
  368. return this;
  369. };
  370. /**
  371. * Add element transformers for certain routes.
  372. */
  373. config.transformers = config.transformers || {};
  374. object.addElementTransformer = function(type, secondArg, thirdArg) {
  375. var isCollection = null;
  376. var transformer = null;
  377. if (arguments.length === 2) {
  378. transformer = secondArg;
  379. } else {
  380. transformer = thirdArg;
  381. isCollection = secondArg;
  382. }
  383. var typeTransformers = config.transformers[type];
  384. if (!typeTransformers) {
  385. typeTransformers = config.transformers[type] = [];
  386. }
  387. typeTransformers.push(function(coll, elem) {
  388. if (_.isNull(isCollection) || (coll === isCollection)) {
  389. return transformer(elem);
  390. }
  391. return elem;
  392. });
  393. return object;
  394. };
  395. object.extendCollection = function(route, fn) {
  396. return object.addElementTransformer(route, true, fn);
  397. };
  398. object.extendModel = function(route, fn) {
  399. return object.addElementTransformer(route, false, fn);
  400. };
  401. config.transformElem = function(elem, isCollection, route, Restangular, force) {
  402. if (!force && !config.transformLocalElements && !elem[config.restangularFields.fromServer]) {
  403. return elem;
  404. }
  405. var typeTransformers = config.transformers[route];
  406. var changedElem = elem;
  407. if (typeTransformers) {
  408. _.each(typeTransformers, function(transformer) {
  409. changedElem = transformer(isCollection, changedElem);
  410. });
  411. }
  412. return config.onElemRestangularized(changedElem, isCollection, route, Restangular);
  413. };
  414. config.transformLocalElements = _.isUndefined(config.transformLocalElements) ?
  415. false :
  416. config.transformLocalElements;
  417. object.setTransformOnlyServerElements = function(active) {
  418. config.transformLocalElements = !active;
  419. };
  420. config.fullResponse = _.isUndefined(config.fullResponse) ? false : config.fullResponse;
  421. object.setFullResponse = function(full) {
  422. config.fullResponse = full;
  423. return this;
  424. };
  425. //Internal values and functions
  426. config.urlCreatorFactory = {};
  427. /**
  428. * Base URL Creator. Base prototype for everything related to it
  429. **/
  430. var BaseCreator = function() {
  431. };
  432. BaseCreator.prototype.setConfig = function(config) {
  433. this.config = config;
  434. return this;
  435. };
  436. BaseCreator.prototype.parentsArray = function(current) {
  437. var parents = [];
  438. while(current) {
  439. parents.push(current);
  440. current = current[this.config.restangularFields.parentResource];
  441. }
  442. return parents.reverse();
  443. };
  444. function RestangularResource(config, $http, url, configurer) {
  445. var resource = {};
  446. _.each(_.keys(configurer), function(key) {
  447. var value = configurer[key];
  448. // Add default parameters
  449. value.params = _.extend({}, value.params, config.defaultRequestParams[value.method.toLowerCase()]);
  450. // We don't want the ? if no params are there
  451. if (_.isEmpty(value.params)) {
  452. delete value.params;
  453. }
  454. if (config.isSafe(value.method)) {
  455. resource[key] = function() {
  456. return $http(_.extend(value, {
  457. url: url
  458. }));
  459. };
  460. } else {
  461. resource[key] = function(data) {
  462. return $http(_.extend(value, {
  463. url: url,
  464. data: data
  465. }));
  466. };
  467. }
  468. });
  469. return resource;
  470. }
  471. BaseCreator.prototype.resource = function(current, $http, localHttpConfig, callHeaders, callParams, what, etag,operation) {
  472. var params = _.defaults(callParams || {}, this.config.defaultRequestParams.common);
  473. var headers = _.defaults(callHeaders || {}, this.config.defaultHeaders);
  474. if (etag) {
  475. if (!config.isSafe(operation)) {
  476. headers['If-Match'] = etag;
  477. } else {
  478. headers['If-None-Match'] = etag;
  479. }
  480. }
  481. var url = this.base(current);
  482. if (what) {
  483. var add = '';
  484. if (!/\/$/.test(url)) {
  485. add += '/';
  486. }
  487. add += what;
  488. url += add;
  489. }
  490. if (this.config.suffix &&
  491. url.indexOf(this.config.suffix, url.length - this.config.suffix.length) === -1 &&
  492. !this.config.getUrlFromElem(current)) {
  493. url += this.config.suffix;
  494. }
  495. current[this.config.restangularFields.httpConfig] = undefined;
  496. return RestangularResource(this.config, $http, url, {
  497. getList: this.config.withHttpValues(localHttpConfig,
  498. {method: 'GET',
  499. params: params,
  500. headers: headers}),
  501. get: this.config.withHttpValues(localHttpConfig,
  502. {method: 'GET',
  503. params: params,
  504. headers: headers}),
  505. jsonp: this.config.withHttpValues(localHttpConfig,
  506. {method: 'jsonp',
  507. params: params,
  508. headers: headers}),
  509. put: this.config.withHttpValues(localHttpConfig,
  510. {method: 'PUT',
  511. params: params,
  512. headers: headers}),
  513. post: this.config.withHttpValues(localHttpConfig,
  514. {method: 'POST',
  515. params: params,
  516. headers: headers}),
  517. remove: this.config.withHttpValues(localHttpConfig,
  518. {method: 'DELETE',
  519. params: params,
  520. headers: headers}),
  521. head: this.config.withHttpValues(localHttpConfig,
  522. {method: 'HEAD',
  523. params: params,
  524. headers: headers}),
  525. trace: this.config.withHttpValues(localHttpConfig,
  526. {method: 'TRACE',
  527. params: params,
  528. headers: headers}),
  529. options: this.config.withHttpValues(localHttpConfig,
  530. {method: 'OPTIONS',
  531. params: params,
  532. headers: headers}),
  533. patch: this.config.withHttpValues(localHttpConfig,
  534. {method: 'PATCH',
  535. params: params,
  536. headers: headers})
  537. });
  538. };
  539. /**
  540. * This is the Path URL creator. It uses Path to show Hierarchy in the Rest API.
  541. * This means that if you have an Account that then has a set of Buildings, a URL to a building
  542. * would be /accounts/123/buildings/456
  543. **/
  544. var Path = function() {
  545. };
  546. Path.prototype = new BaseCreator();
  547. Path.prototype.normalizeUrl = function (url){
  548. var parts = /(http[s]?:\/\/)?(.*)?/.exec(url);
  549. parts[2] = parts[2].replace(/[\\\/]+/g, '/');
  550. return (typeof parts[1] !== 'undefined')? parts[1] + parts[2] : parts[2];
  551. };
  552. Path.prototype.base = function(current) {
  553. var __this = this;
  554. return _.reduce(this.parentsArray(current), function(acum, elem) {
  555. var elemUrl;
  556. var elemSelfLink = __this.config.getUrlFromElem(elem);
  557. if (elemSelfLink) {
  558. if (__this.config.isAbsoluteUrl(elemSelfLink)) {
  559. return elemSelfLink;
  560. } else {
  561. elemUrl = elemSelfLink;
  562. }
  563. } else {
  564. elemUrl = elem[__this.config.restangularFields.route];
  565. if (elem[__this.config.restangularFields.restangularCollection]) {
  566. var ids = elem[__this.config.restangularFields.ids];
  567. if (ids) {
  568. elemUrl += '/' + ids.join(',');
  569. }
  570. } else {
  571. var elemId;
  572. if (__this.config.useCannonicalId) {
  573. elemId = __this.config.getCannonicalIdFromElem(elem);
  574. } else {
  575. elemId = __this.config.getIdFromElem(elem);
  576. }
  577. if (config.isValidId(elemId) && !elem.singleOne) {
  578. elemUrl += '/' + (__this.config.encodeIds ? encodeURIComponent(elemId) : elemId);
  579. }
  580. }
  581. }
  582. acum = acum.replace(/\/$/, '') + '/' + elemUrl;
  583. return __this.normalizeUrl(acum);
  584. }, this.config.baseUrl);
  585. };
  586. Path.prototype.fetchUrl = function(current, what) {
  587. var baseUrl = this.base(current);
  588. if (what) {
  589. baseUrl += '/' + what;
  590. }
  591. return baseUrl;
  592. };
  593. Path.prototype.fetchRequestedUrl = function(current, what) {
  594. var url = this.fetchUrl(current, what);
  595. var params = current[config.restangularFields.reqParams];
  596. // From here on and until the end of fetchRequestedUrl,
  597. // the code has been kindly borrowed from angular.js
  598. // The reason for such code bloating is coherence:
  599. // If the user were to use this for cache management, the
  600. // serialization of parameters would need to be identical
  601. // to the one done by angular for cache keys to match.
  602. function sortedKeys(obj) {
  603. var keys = [];
  604. for (var key in obj) {
  605. if (obj.hasOwnProperty(key)) {
  606. keys.push(key);
  607. }
  608. }
  609. return keys.sort();
  610. }
  611. function forEachSorted(obj, iterator, context) {
  612. var keys = sortedKeys(obj);
  613. for ( var i = 0; i < keys.length; i++) {
  614. iterator.call(context, obj[keys[i]], keys[i]);
  615. }
  616. return keys;
  617. }
  618. function encodeUriQuery(val, pctEncodeSpaces) {
  619. return encodeURIComponent(val).
  620. replace(/%40/gi, '@').
  621. replace(/%3A/gi, ':').
  622. replace(/%24/g, '$').
  623. replace(/%2C/gi, ',').
  624. replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
  625. }
  626. if (!params) { return url + (this.config.suffix || ''); }
  627. var parts = [];
  628. forEachSorted(params, function(value, key) {
  629. if (value === null || value === undefined) { return; }
  630. if (!angular.isArray(value)) { value = [value]; }
  631. angular.forEach(value, function(v) {
  632. if (angular.isObject(v)) {
  633. v = angular.toJson(v);
  634. }
  635. parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(v));
  636. });
  637. });
  638. return url + (this.config.suffix || '') + ((url.indexOf('?') === -1) ? '?' : '&') + parts.join('&');
  639. };
  640. config.urlCreatorFactory.path = Path;
  641. };
  642. var globalConfiguration = {};
  643. Configurer.init(this, globalConfiguration);
  644. this.$get = ['$http', '$q', function($http, $q) {
  645. function createServiceForConfiguration(config) {
  646. var service = {};
  647. var urlHandler = new config.urlCreatorFactory[config.urlCreator]();
  648. urlHandler.setConfig(config);
  649. function restangularizeBase(parent, elem, route, reqParams, fromServer) {
  650. elem[config.restangularFields.route] = route;
  651. elem[config.restangularFields.getRestangularUrl] = _.bind(urlHandler.fetchUrl, urlHandler, elem);
  652. elem[config.restangularFields.getRequestedUrl] = _.bind(urlHandler.fetchRequestedUrl, urlHandler, elem);
  653. elem[config.restangularFields.addRestangularMethod] = _.bind(addRestangularMethodFunction, elem);
  654. elem[config.restangularFields.clone] = _.bind(copyRestangularizedElement, elem, elem);
  655. elem[config.restangularFields.reqParams] = _.isEmpty(reqParams) ? null : reqParams;
  656. elem[config.restangularFields.withHttpConfig] = _.bind(withHttpConfig, elem);
  657. elem[config.restangularFields.plain] = _.bind(stripRestangular, elem, elem);
  658. // Tag element as restangularized
  659. elem[config.restangularFields.restangularized] = true;
  660. // RequestLess connection
  661. elem[config.restangularFields.one] = _.bind(one, elem, elem);
  662. elem[config.restangularFields.all] = _.bind(all, elem, elem);
  663. elem[config.restangularFields.several] = _.bind(several, elem, elem);
  664. elem[config.restangularFields.oneUrl] = _.bind(oneUrl, elem, elem);
  665. elem[config.restangularFields.allUrl] = _.bind(allUrl, elem, elem);
  666. elem[config.restangularFields.fromServer] = !!fromServer;
  667. if (parent && config.shouldSaveParent(route)) {
  668. var parentId = config.getIdFromElem(parent);
  669. var parentUrl = config.getUrlFromElem(parent);
  670. var restangularFieldsForParent = _.union(
  671. _.values( _.pick(config.restangularFields, ['route', 'singleOne', 'parentResource']) ),
  672. config.extraFields
  673. );
  674. var parentResource = _.pick(parent, restangularFieldsForParent);
  675. if (config.isValidId(parentId)) {
  676. config.setIdToElem(parentResource, parentId, route);
  677. }
  678. if (config.isValidId(parentUrl)) {
  679. config.setUrlToElem(parentResource, parentUrl, route);
  680. }
  681. elem[config.restangularFields.parentResource] = parentResource;
  682. } else {
  683. elem[config.restangularFields.parentResource] = null;
  684. }
  685. return elem;
  686. }
  687. function one(parent, route, id, singleOne) {
  688. var error;
  689. if (_.isNumber(route) || _.isNumber(parent)) {
  690. error = 'You\'re creating a Restangular entity with the number ';
  691. error += 'instead of the route or the parent. For example, you can\'t call .one(12).';
  692. throw new Error(error);
  693. }
  694. if (_.isUndefined(route)) {
  695. error = 'You\'re creating a Restangular entity either without the path. ';
  696. error += 'For example you can\'t call .one(). Please check if your arguments are valid.';
  697. throw new Error(error);
  698. }
  699. var elem = {};
  700. config.setIdToElem(elem, id, route);
  701. config.setFieldToElem(config.restangularFields.singleOne, elem, singleOne);
  702. return restangularizeElem(parent, elem , route, false);
  703. }
  704. function all(parent, route) {
  705. return restangularizeCollection(parent, [] , route, false);
  706. }
  707. function several(parent, route /*, ids */) {
  708. var collection = [];
  709. collection[config.restangularFields.ids] = Array.prototype.splice.call(arguments, 2);
  710. return restangularizeCollection(parent, collection , route, false);
  711. }
  712. function oneUrl(parent, route, url) {
  713. if (!route) {
  714. throw new Error('Route is mandatory when creating new Restangular objects.');
  715. }
  716. var elem = {};
  717. config.setUrlToElem(elem, url, route);
  718. return restangularizeElem(parent, elem , route, false);
  719. }
  720. function allUrl(parent, route, url) {
  721. if (!route) {
  722. throw new Error('Route is mandatory when creating new Restangular objects.');
  723. }
  724. var elem = {};
  725. config.setUrlToElem(elem, url, route);
  726. return restangularizeCollection(parent, elem , route, false);
  727. }
  728. // Promises
  729. function restangularizePromise(promise, isCollection, valueToFill) {
  730. promise.call = _.bind(promiseCall, promise);
  731. promise.get = _.bind(promiseGet, promise);
  732. promise[config.restangularFields.restangularCollection] = isCollection;
  733. if (isCollection) {
  734. promise.push = _.bind(promiseCall, promise, 'push');
  735. }
  736. promise.$object = valueToFill;
  737. if (config.restangularizePromiseInterceptor) {
  738. config.restangularizePromiseInterceptor(promise);
  739. }
  740. return promise;
  741. }
  742. function promiseCall(method) {
  743. var deferred = $q.defer();
  744. var callArgs = arguments;
  745. var filledValue = {};
  746. this.then(function(val) {
  747. var params = Array.prototype.slice.call(callArgs, 1);
  748. var func = val[method];
  749. func.apply(val, params);
  750. filledValue = val;
  751. deferred.resolve(val);
  752. });
  753. return restangularizePromise(deferred.promise, this[config.restangularFields.restangularCollection], filledValue);
  754. }
  755. function promiseGet(what) {
  756. var deferred = $q.defer();
  757. var filledValue = {};
  758. this.then(function(val) {
  759. filledValue = val[what];
  760. deferred.resolve(filledValue);
  761. });
  762. return restangularizePromise(deferred.promise, this[config.restangularFields.restangularCollection], filledValue);
  763. }
  764. function resolvePromise(deferred, response, data, filledValue) {
  765. _.extend(filledValue, data);
  766. // Trigger the full response interceptor.
  767. if (config.fullResponse) {
  768. return deferred.resolve(_.extend(response, {
  769. data: data
  770. }));
  771. } else {
  772. deferred.resolve(data);
  773. }
  774. }
  775. // Elements
  776. function stripRestangular(elem) {
  777. if (_.isArray(elem)) {
  778. var array = [];
  779. _.each(elem, function(value) {
  780. array.push(config.isRestangularized(value) ? stripRestangular(value) : value);
  781. });
  782. return array;
  783. } else {
  784. return _.omit(elem, _.values(_.omit(config.restangularFields, 'id')));
  785. }
  786. }
  787. function addCustomOperation(elem) {
  788. elem[config.restangularFields.customOperation] = _.bind(customFunction, elem);
  789. _.each(['put', 'post', 'get', 'delete'], function(oper) {
  790. _.each(['do', 'custom'], function(alias) {
  791. var callOperation = oper === 'delete' ? 'remove' : oper;
  792. var name = alias + oper.toUpperCase();
  793. var callFunction;
  794. if (callOperation !== 'put' && callOperation !== 'post') {
  795. callFunction = customFunction;
  796. } else {
  797. callFunction = function(operation, elem, path, params, headers) {
  798. return _.bind(customFunction, this)(operation, path, params, headers, elem);
  799. };
  800. }
  801. elem[name] = _.bind(callFunction, elem, callOperation);
  802. });
  803. });
  804. elem[config.restangularFields.customGETLIST] = _.bind(fetchFunction, elem);
  805. elem[config.restangularFields.doGETLIST] = elem[config.restangularFields.customGETLIST];
  806. }
  807. function copyRestangularizedElement(fromElement, toElement) {
  808. var copiedElement = angular.copy(fromElement, toElement);
  809. return restangularizeElem(copiedElement[config.restangularFields.parentResource],
  810. copiedElement, copiedElement[config.restangularFields.route], true);
  811. }
  812. function restangularizeElem(parent, element, route, fromServer, collection, reqParams) {
  813. var elem = config.onBeforeElemRestangularized(element, false, route);
  814. var localElem = restangularizeBase(parent, elem, route, reqParams, fromServer);
  815. if (config.useCannonicalId) {
  816. localElem[config.restangularFields.cannonicalId] = config.getIdFromElem(localElem);
  817. }
  818. if (collection) {
  819. localElem[config.restangularFields.getParentList] = function() {
  820. return collection;
  821. };
  822. }
  823. localElem[config.restangularFields.restangularCollection] = false;
  824. localElem[config.restangularFields.get] = _.bind(getFunction, localElem);
  825. localElem[config.restangularFields.getList] = _.bind(fetchFunction, localElem);
  826. localElem[config.restangularFields.put] = _.bind(putFunction, localElem);
  827. localElem[config.restangularFields.post] = _.bind(postFunction, localElem);
  828. localElem[config.restangularFields.remove] = _.bind(deleteFunction, localElem);
  829. localElem[config.restangularFields.head] = _.bind(headFunction, localElem);
  830. localElem[config.restangularFields.trace] = _.bind(traceFunction, localElem);
  831. localElem[config.restangularFields.options] = _.bind(optionsFunction, localElem);
  832. localElem[config.restangularFields.patch] = _.bind(patchFunction, localElem);
  833. localElem[config.restangularFields.save] = _.bind(save, localElem);
  834. addCustomOperation(localElem);
  835. return config.transformElem(localElem, false, route, service, true);
  836. }
  837. function restangularizeCollection(parent, element, route, fromServer, reqParams) {
  838. var elem = config.onBeforeElemRestangularized(element, true, route);
  839. var localElem = restangularizeBase(parent, elem, route, reqParams, fromServer);
  840. localElem[config.restangularFields.restangularCollection] = true;
  841. localElem[config.restangularFields.post] = _.bind(postFunction, localElem, null);
  842. localElem[config.restangularFields.remove] = _.bind(deleteFunction, localElem);
  843. localElem[config.restangularFields.head] = _.bind(headFunction, localElem);
  844. localElem[config.restangularFields.trace] = _.bind(traceFunction, localElem);
  845. localElem[config.restangularFields.putElement] = _.bind(putElementFunction, localElem);
  846. localElem[config.restangularFields.options] = _.bind(optionsFunction, localElem);
  847. localElem[config.restangularFields.patch] = _.bind(patchFunction, localElem);
  848. localElem[config.restangularFields.get] = _.bind(getById, localElem);
  849. localElem[config.restangularFields.getList] = _.bind(fetchFunction, localElem, null);
  850. addCustomOperation(localElem);
  851. return config.transformElem(localElem, true, route, service, true);
  852. }
  853. function restangularizeCollectionAndElements(parent, element, route) {
  854. var collection = restangularizeCollection(parent, element, route, false);
  855. _.each(collection, function(elem) {
  856. restangularizeElem(parent, elem, route, false);
  857. });
  858. return collection;
  859. }
  860. function getById(id, reqParams, headers){
  861. return this.customGET(id.toString(), reqParams, headers);
  862. }
  863. function putElementFunction(idx, params, headers) {
  864. var __this = this;
  865. var elemToPut = this[idx];
  866. var deferred = $q.defer();
  867. var filledArray = [];
  868. filledArray = config.transformElem(filledArray, true, elemToPut[config.restangularFields.route], service);
  869. elemToPut.put(params, headers).then(function(serverElem) {
  870. var newArray = copyRestangularizedElement(__this);
  871. newArray[idx] = serverElem;
  872. filledArray = newArray;
  873. deferred.resolve(newArray);
  874. }, function(response) {
  875. deferred.reject(response);
  876. });
  877. return restangularizePromise(deferred.promise, true, filledArray);
  878. }
  879. function parseResponse(resData, operation, route, fetchUrl, response, deferred) {
  880. var data = config.responseExtractor(resData, operation, route, fetchUrl, response, deferred);
  881. var etag = response.headers('ETag');
  882. if (data && etag) {
  883. data[config.restangularFields.etag] = etag;
  884. }
  885. return data;
  886. }
  887. function fetchFunction(what, reqParams, headers) {
  888. var __this = this;
  889. var deferred = $q.defer();
  890. var operation = 'getList';
  891. var url = urlHandler.fetchUrl(this, what);
  892. var whatFetched = what || __this[config.restangularFields.route];
  893. var request = config.fullRequestInterceptor(null, operation,
  894. whatFetched, url, headers || {}, reqParams || {}, this[config.restangularFields.httpConfig] || {});
  895. var filledArray = [];
  896. filledArray = config.transformElem(filledArray, true, whatFetched, service);
  897. var method = 'getList';
  898. if (config.jsonp) {
  899. method = 'jsonp';
  900. }
  901. var okCallback = function(response) {
  902. var resData = response.data;
  903. var fullParams = response.config.params;
  904. var data = parseResponse(resData, operation, whatFetched, url, response, deferred);
  905. // support empty response for getList() calls (some APIs respond with 204 and empty body)
  906. if (_.isUndefined(data) || '' === data) {
  907. data = [];
  908. }
  909. if (!_.isArray(data)) {
  910. throw new Error('Response for getList SHOULD be an array and not an object or something else');
  911. }
  912. var processedData = _.map(data, function(elem) {
  913. if (!__this[config.restangularFields.restangularCollection]) {
  914. return restangularizeElem(__this, elem, what, true, data);
  915. } else {
  916. return restangularizeElem(__this[config.restangularFields.parentResource],
  917. elem, __this[config.restangularFields.route], true, data);
  918. }
  919. });
  920. processedData = _.extend(data, processedData);
  921. if (!__this[config.restangularFields.restangularCollection]) {
  922. resolvePromise(
  923. deferred,
  924. response,
  925. restangularizeCollection(
  926. __this,
  927. processedData,
  928. what,
  929. true,
  930. fullParams
  931. ),
  932. filledArray
  933. );
  934. } else {
  935. resolvePromise(
  936. deferred,
  937. response,
  938. restangularizeCollection(
  939. __this[config.restangularFields.parentResource],
  940. processedData,
  941. __this[config.restangularFields.route],
  942. true,
  943. fullParams
  944. ),
  945. filledArray
  946. );
  947. }
  948. };
  949. urlHandler.resource(this, $http, request.httpConfig, request.headers, request.params, what,
  950. this[config.restangularFields.etag], operation)[method]().then(okCallback, function error(response) {
  951. if (response.status === 304 && __this[config.restangularFields.restangularCollection]) {
  952. resolvePromise(deferred, response, __this, filledArray);
  953. } else if ( _.every(config.errorInterceptors, function(cb) { return cb(response, deferred, okCallback) !== false; }) ) {
  954. // triggered if no callback returns false
  955. deferred.reject(response);
  956. }
  957. });
  958. return restangularizePromise(deferred.promise, true, filledArray);
  959. }
  960. function withHttpConfig(httpConfig) {
  961. this[config.restangularFields.httpConfig] = httpConfig;
  962. return this;
  963. }
  964. function save(params, headers) {
  965. if (this[config.restangularFields.fromServer]) {
  966. return this[config.restangularFields.put](params, headers);
  967. } else {
  968. return _.bind(elemFunction, this)('post', undefined, params, undefined, headers);
  969. }
  970. }
  971. function elemFunction(operation, what, params, obj, headers) {
  972. var __this = this;
  973. var deferred = $q.defer();
  974. var resParams = params || {};
  975. var route = what || this[config.restangularFields.route];
  976. var fetchUrl = urlHandler.fetchUrl(this, what);
  977. var callObj = obj || this;
  978. // fallback to etag on restangular object (since for custom methods we probably don't explicitly specify the etag field)
  979. var etag = callObj[config.restangularFields.etag] || (operation !== 'post' ? this[config.restangularFields.etag] : null);
  980. if (_.isObject(callObj) && config.isRestangularized(callObj)) {
  981. callObj = stripRestangular(callObj);
  982. }
  983. var request = config.fullRequestInterceptor(callObj, operation, route, fetchUrl,
  984. headers || {}, resParams || {}, this[config.restangularFields.httpConfig] || {});
  985. var filledObject = {};
  986. filledObject = config.transformElem(filledObject, false, route, service);
  987. var okCallback = function(response) {
  988. var resData = response.data;
  989. var fullParams = response.config.params;
  990. var elem = parseResponse(resData, operation, route, fetchUrl, response, deferred);
  991. if (elem) {
  992. if (operation === 'post' && !__this[config.restangularFields.restangularCollection]) {
  993. var data = restangularizeElem(
  994. __this[config.restangularFields.parentResource],
  995. elem,
  996. route,
  997. true,
  998. null,
  999. fullParams
  1000. );
  1001. resolvePromise(deferred, response, data, filledObject);
  1002. } else {
  1003. var data = restangularizeElem(
  1004. __this[config.restangularFields.parentResource],
  1005. elem,
  1006. __this[config.restangularFields.route],
  1007. true,
  1008. null,
  1009. fullParams
  1010. );
  1011. data[config.restangularFields.singleOne] = __this[config.restangularFields.singleOne];
  1012. resolvePromise(deferred, response, data, filledObject);
  1013. }
  1014. } else {
  1015. resolvePromise(deferred, response, undefined, filledObject);
  1016. }
  1017. };
  1018. var errorCallback = function(response) {
  1019. if (response.status === 304 && config.isSafe(operation)) {
  1020. resolvePromise(deferred, response, __this, filledObject);
  1021. } else if ( _.every(config.errorInterceptors, function(cb) { return cb(response, deferred, okCallback) !== false; }) ) {
  1022. // triggered if no callback returns false
  1023. deferred.reject(response);
  1024. }
  1025. };
  1026. // Overriding HTTP Method
  1027. var callOperation = operation;
  1028. var callHeaders = _.extend({}, request.headers);
  1029. var isOverrideOperation = config.isOverridenMethod(operation);
  1030. if (isOverrideOperation) {
  1031. callOperation = 'post';
  1032. callHeaders = _.extend(callHeaders, {'X-HTTP-Method-Override': operation === 'remove' ? 'DELETE' : operation.toUpperCase()});
  1033. } else if (config.jsonp && callOperation === 'get') {
  1034. callOperation = 'jsonp';
  1035. }
  1036. if (config.isSafe(operation)) {
  1037. if (isOverrideOperation) {
  1038. urlHandler.resource(this, $http, request.httpConfig, callHeaders, request.params,
  1039. what, etag, callOperation)[callOperation]({}).then(okCallback, errorCallback);
  1040. } else {
  1041. urlHandler.resource(this, $http, request.httpConfig, callHeaders, request.params,
  1042. what, etag, callOperation)[callOperation]().then(okCallback, errorCallback);
  1043. }
  1044. } else {
  1045. urlHandler.resource(this, $http, request.httpConfig, callHeaders, request.params,
  1046. what, etag, callOperation)[callOperation](request.element).then(okCallback, errorCallback);
  1047. }
  1048. return restangularizePromise(deferred.promise, false, filledObject);
  1049. }
  1050. function getFunction(params, headers) {
  1051. return _.bind(elemFunction, this)('get', undefined, params, undefined, headers);
  1052. }
  1053. function deleteFunction(params, headers) {
  1054. return _.bind(elemFunction, this)('remove', undefined, params, undefined, headers);
  1055. }
  1056. function putFunction(params, headers) {
  1057. return _.bind(elemFunction, this)('put', undefined, params, undefined, headers);
  1058. }
  1059. function postFunction(what, elem, params, headers) {
  1060. return _.bind(elemFunction, this)('post', what, params, elem, headers);
  1061. }
  1062. function headFunction(params, headers) {
  1063. return _.bind(elemFunction, this)('head', undefined, params, undefined, headers);
  1064. }
  1065. function traceFunction(params, headers) {
  1066. return _.bind(elemFunction, this)('trace', undefined, params, undefined, headers);
  1067. }
  1068. function optionsFunction(params, headers) {
  1069. return _.bind(elemFunction, this)('options', undefined, params, undefined, headers);
  1070. }
  1071. function patchFunction(elem, params, headers) {
  1072. return _.bind(elemFunction, this)('patch', undefined, params, elem, headers);
  1073. }
  1074. function customFunction(operation, path, params, headers, elem) {
  1075. return _.bind(elemFunction, this)(operation, path, params, elem, headers);
  1076. }
  1077. function addRestangularMethodFunction(name, operation, path, defaultParams, defaultHeaders, defaultElem) {
  1078. var bindedFunction;
  1079. if (operation === 'getList') {
  1080. bindedFunction = _.bind(fetchFunction, this, path);
  1081. } else {
  1082. bindedFunction = _.bind(customFunction, this, operation, path);
  1083. }
  1084. var createdFunction = function(params, headers, elem) {
  1085. var callParams = _.defaults({
  1086. params: params,
  1087. headers: headers,
  1088. elem: elem
  1089. }, {
  1090. params: defaultParams,
  1091. headers: defaultHeaders,
  1092. elem: defaultElem
  1093. });
  1094. return bindedFunction(callParams.params, callParams.headers, callParams.elem);
  1095. };
  1096. if (config.isSafe(operation)) {
  1097. this[name] = createdFunction;
  1098. } else {
  1099. this[name] = function(elem, params, headers) {
  1100. return createdFunction(params, headers, elem);
  1101. };
  1102. }
  1103. }
  1104. function withConfigurationFunction(configurer) {
  1105. var newConfig = angular.copy(_.omit(config, 'configuration'));
  1106. Configurer.init(newConfig, newConfig);
  1107. configurer(newConfig);
  1108. return createServiceForConfiguration(newConfig);
  1109. }
  1110. function toService(route, parent) {
  1111. var knownCollectionMethods = _.values(config.restangularFields);
  1112. var serv = {};
  1113. var collection = (parent || service).all(route);
  1114. serv.one = _.bind(one, (parent || service), parent, route);
  1115. serv.post = _.bind(collection.post, collection);
  1116. serv.getList = _.bind(collection.getList, collection);
  1117. for (var prop in collection) {
  1118. if (collection.hasOwnProperty(prop) && _.isFunction(collection[prop]) && !_.contains(knownCollectionMethods, prop)) {
  1119. serv[prop] = _.bind(collection[prop], collection);
  1120. }
  1121. }
  1122. return serv;
  1123. }
  1124. Configurer.init(service, config);
  1125. service.copy = _.bind(copyRestangularizedElement, service);
  1126. service.service = _.bind(toService, service);
  1127. service.withConfig = _.bind(withConfigurationFunction, service);
  1128. service.one = _.bind(one, service, null);
  1129. service.all = _.bind(all, service, null);
  1130. service.several = _.bind(several, service, null);
  1131. service.oneUrl = _.bind(oneUrl, service, null);
  1132. service.allUrl = _.bind(allUrl, service, null);
  1133. service.stripRestangular = _.bind(stripRestangular, service);
  1134. service.restangularizeElement = _.bind(restangularizeElem, service);
  1135. service.restangularizeCollection = _.bind(restangularizeCollectionAndElements, service);
  1136. return service;
  1137. }
  1138. return createServiceForConfiguration(globalConfiguration);
  1139. }];
  1140. });
  1141. })();