PageRenderTime 43ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/app/scripts/vendor/backbone-sharepoint.odata.js

https://github.com/TheWebShop/gallery-walking
JavaScript | 231 lines | 140 code | 51 blank | 40 comment | 29 complexity | c8565ca7776e5be4cc081d6811749c1e MD5 | raw file
  1. /******************************************************************
  2. * Backbone.SharePoint OData proxy
  3. *
  4. * Author: Luc Stakenborg
  5. * Date: Mar 2, 2012
  6. *
  7. * Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
  8. * Copyright (c) 2012, Luc Stakenborg, Oxida B.V.
  9. ******************************************************************
  10. */
  11. (function(Backbone, _, $) {
  12. // SharePoint ListData service
  13. var LISTDATA_SERVICE = '_vti_bin/ListData.svc',
  14. url;
  15. // calculate url based on site, list and (optional) id
  16. url = function(options) {
  17. var site = options.site,
  18. list = options.list,
  19. id = options.id,
  20. // remove leading and trailing forward slashes from the site path
  21. path = site.replace(/^\/+|\/+$/g, ''),
  22. url = (path ? '/' + path : '') + '/' + LISTDATA_SERVICE + '/' + list + (id ? '(' + encodeURIComponent(id) + ')' : '');
  23. return url;
  24. };
  25. Backbone.SP = {};
  26. Backbone.SP.Item = Backbone.Model.extend({
  27. // the id attribute of a SharePoint item. Please note capital I
  28. idAttribute: 'Id',
  29. // the SharePoint site on the current server. By default: root
  30. site: '',
  31. initialize: function() {
  32. this._changeSet = {};
  33. this.bind('change', this._updateChangeSet);
  34. },
  35. _updateChangeSet: function() {
  36. var changedAttributes = this.changedAttributes();
  37. // if attributes are set due to server response, the response will contain __metadata
  38. if (changedAttributes.__metadata) {
  39. this._changeSet = {};
  40. } else {
  41. _.extend(this._changeSet, this.changedAttributes());
  42. }
  43. },
  44. url: function() {
  45. var options = {
  46. site: this.site,
  47. list: this.list
  48. };
  49. if (!this.isNew()) {
  50. options.id = this.id;
  51. }
  52. return url(options);
  53. },
  54. unset: function(attr, options) {
  55. var result = Backbone.Model.prototype.unset.call(this, attr, options);
  56. if (result) {
  57. delete this._changeSet[attr];
  58. }
  59. return result;
  60. },
  61. clear: function(attr, options) {
  62. var result = Backbone.Model.prototype.clear.call(this, attr, options);
  63. if (result) {
  64. this._changeSet = {};
  65. }
  66. return result;
  67. },
  68. sync: function(method, model, options) {
  69. var metadata = model.get("__metadata"),
  70. methodMap = {
  71. 'create': 'POST',
  72. // OData requires MERGE for partial updates
  73. // We will use Method tunneling throug POST because
  74. // MERGE isn't supported by IE7 + IE8
  75. 'update': 'POST',
  76. 'delete': 'DELETE',
  77. 'read': 'GET'
  78. },
  79. type = methodMap[method],
  80. // Default JSON-request options.
  81. params = _.extend({
  82. type: type,
  83. dataType: 'json',
  84. processData: (type === 'GET')
  85. }, options);
  86. // Ensure that we have a URL.
  87. if (!params.url) {
  88. params.url = model.url();
  89. }
  90. // Ensure that we have the appropriate request data.
  91. if (!params.data && model && (method === 'create' || method === 'update')) {
  92. params.contentType = 'application/json';
  93. if (method === 'create') {
  94. params.data = JSON.stringify(model.toJSON());
  95. }
  96. if (method === 'update') {
  97. params.data = JSON.stringify(model._changeSet || {});
  98. params.headers = {
  99. // header required for Method tunneling
  100. 'X-HTTP-Method': 'MERGE',
  101. // header required for concurrency control
  102. 'If-Match': metadata ? metadata.etag : '*'
  103. };
  104. }
  105. }
  106. // transfer special url parameters like select and
  107. // orderby to the params.data hash
  108. if (method === 'read') {
  109. params.data = params.data || {};
  110. _(['filter', 'select', 'orderby', 'top', 'skip', 'expand', 'inlinecount'])
  111. .each(function(keyword) {
  112. if (options[keyword]) {
  113. params.data['$' + keyword] = options[keyword];
  114. }
  115. });
  116. }
  117. // Create a success handler to:
  118. // (1) set etag
  119. // (2) normalize the response, so a model.fetch() does not require a parse()
  120. var success = options.success;
  121. params.success = function(resp, status, xhr) {
  122. // OData responds with an updated Etag
  123. var etag = xhr.getResponseHeader('Etag');
  124. // always clear changeSet after a server response
  125. model._changeSet = {};
  126. // Instead of passing resp, we'll pass resp.d
  127. // make sure we cover 204 response (resp is empty) on Delete and Update
  128. // This way we don't need to override the model.parse() method
  129. if (success) {
  130. if (Backbone.VERSION === '0.9.9' || Backbone.VERSION === '0.9.10') {
  131. success(model, resp && resp.d, options);
  132. } else {
  133. success(resp && resp.d, status, xhr);
  134. }
  135. }
  136. if (etag) {
  137. // Backbone doesn't support setting/getting nested attributes
  138. // Updating etag attribute directly instead
  139. model.attributes.__metadata.etag = etag;
  140. }
  141. };
  142. var error = options.error;
  143. params.error = function(xhr, status, errorText) {
  144. if (error) {
  145. // Include the error text in xhr so it's available to the callback
  146. if (errorText) {
  147. xhr.errorText = errorText;
  148. }
  149. error(model, xhr, options);
  150. }
  151. };
  152. // Make the request.
  153. return $.ajax(params);
  154. }
  155. });
  156. Backbone.SP.List = Backbone.Collection.extend({
  157. url: function() {
  158. // use the Model's url method, if available
  159. if (this.model && this.model.list) {
  160. return this.model.prototype.url();
  161. }
  162. // otherwise use site and list settings of this collection
  163. return url({
  164. site: this.site,
  165. list: this.list
  166. });
  167. },
  168. sync: function(method, model, options) {
  169. return this.model.prototype.sync(method, model, options);
  170. },
  171. parse: function(response) {
  172. if (response.__count) {
  173. this._count = parseInt(response.__count, 10);
  174. } else {
  175. delete this._count;
  176. }
  177. return response.results;
  178. }
  179. });
  180. }(Backbone, _, $));