PageRenderTime 129ms CodeModel.GetById 2ms RepoModel.GetById 1ms app.codeStats 0ms

/public/javascript/pump/model.js

https://gitlab.com/pump.io/pump.io
JavaScript | 982 lines | 809 code | 118 blank | 55 comment | 203 complexity | 900f280cbd6d4a2c0dd2cc5288793032 MD5 | raw file
  1. // pump/model.js
  2. //
  3. // Backbone models for the pump.io client UI
  4. //
  5. // Copyright 2011-2012, E14N https://e14n.com/
  6. //
  7. // @licstart The following is the entire license notice for the
  8. // JavaScript code in this page.
  9. //
  10. // Licensed under the Apache License, Version 2.0 (the "License");
  11. // you may not use this file except in compliance with the License.
  12. // You may obtain a copy of the License at
  13. //
  14. // http://www.apache.org/licenses/LICENSE-2.0
  15. //
  16. // Unless required by applicable law or agreed to in writing, software
  17. // distributed under the License is distributed on an "AS IS" BASIS,
  18. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  19. // See the License for the specific language governing permissions and
  20. // limitations under the License.
  21. //
  22. // @licend The above is the entire license notice
  23. // for the JavaScript code in this page.
  24. (function(_, $, Backbone, Pump) {
  25. "use strict";
  26. // Override backbone sync to use OAuth
  27. Backbone.sync = function(method, model, options) {
  28. var getValue = function(object, prop) {
  29. if (_.isFunction(object[prop])) {
  30. return object[prop]();
  31. } else if (object[prop]) {
  32. return object[prop];
  33. } else if (object.has && object.has(prop)) {
  34. return object.get(prop);
  35. } else {
  36. return null;
  37. }
  38. };
  39. var methodMap = {
  40. "create": "POST",
  41. "update": "PUT",
  42. "delete": "DELETE",
  43. "read": "GET"
  44. };
  45. var type = methodMap[method];
  46. // Default options, unless specified.
  47. options = options || {};
  48. // Default JSON-request options.
  49. var params = {type: type, dataType: "json"};
  50. // Ensure that we have a URL.
  51. if (!options.url) {
  52. if (type == "POST") {
  53. params.url = getValue(model.collection, "url");
  54. } else if (model.proxyURL) {
  55. params.url = model.proxyURL;
  56. } else {
  57. params.url = getValue(model, "url");
  58. }
  59. if (!params.url || !_.isString(params.url)) {
  60. throw new Error("No URL");
  61. }
  62. }
  63. // Ensure that we have the appropriate request data.
  64. if (!options.data && model && (method == "create" || method == "update")) {
  65. params.contentType = "application/json";
  66. params.data = JSON.stringify(model.toJSON());
  67. }
  68. // Don't process data on a non-GET request.
  69. if (params.type !== "GET" && !Backbone.emulateJSON) {
  70. params.processData = false;
  71. }
  72. params = _.extend(params, options);
  73. Pump.ajax(params);
  74. return null;
  75. };
  76. // A little bit of model sugar
  77. // Create Model attributes for our object-y things
  78. Pump.Model = Backbone.Model.extend({
  79. activityObjects: [],
  80. activityObjectBags: [],
  81. activityObjectStreams: [],
  82. activityStreams: [],
  83. peopleStreams: [],
  84. listStreams: [],
  85. people: [],
  86. initialize: function() {
  87. var obj = this,
  88. neverNew = function() { // XXX: neverNude
  89. return false;
  90. },
  91. initer = function(obj, model) {
  92. return function(name) {
  93. var raw = obj.get(name);
  94. if (raw) {
  95. // use unique for cached stuff
  96. if (model.unique) {
  97. obj[name] = model.unique(raw);
  98. } else {
  99. obj[name] = new model(raw);
  100. }
  101. obj[name].isNew = neverNew;
  102. }
  103. obj.on("change:"+name, function(changed) {
  104. var raw = obj.get(name);
  105. if (obj[name] && obj[name].set) {
  106. obj[name].set(raw);
  107. } else if (raw) {
  108. if (model.unique) {
  109. obj[name] = model.unique(raw);
  110. } else {
  111. obj[name] = new model(raw);
  112. }
  113. obj[name].isNew = neverNew;
  114. }
  115. });
  116. };
  117. };
  118. _.each(obj.activityObjects, initer(obj, Pump.ActivityObject));
  119. _.each(obj.activityObjectBags, initer(obj, Pump.ActivityObjectBag));
  120. _.each(obj.activityObjectStreams, initer(obj, Pump.ActivityObjectStream));
  121. _.each(obj.activityStreams, initer(obj, Pump.ActivityStream));
  122. _.each(obj.peopleStreams, initer(obj, Pump.PeopleStream));
  123. _.each(obj.listStreams, initer(obj, Pump.ListStream));
  124. _.each(obj.people, initer(obj, Pump.Person));
  125. },
  126. toJSONRef: function() {
  127. var obj = this;
  128. return {
  129. id: obj.get(obj.idAttribute),
  130. objectType: obj.getObjectType()
  131. };
  132. },
  133. getObjectType: function() {
  134. var obj = this;
  135. return obj.get("objectType");
  136. },
  137. toJSON: function(seen) {
  138. var obj = this,
  139. id = obj.get(obj.idAttribute),
  140. json = _.clone(obj.attributes),
  141. jsoner = function(name) {
  142. if (_.has(obj, name)) {
  143. json[name] = obj[name].toJSON(seenNow);
  144. }
  145. },
  146. seenNow;
  147. if (seen && id && _.includes(seen, id)) {
  148. json = obj.toJSONRef();
  149. } else {
  150. if (seen) {
  151. seenNow = seen.slice(0);
  152. } else {
  153. seenNow = [];
  154. }
  155. if (id) {
  156. seenNow.push(id);
  157. }
  158. _.each(obj.activityObjects, jsoner);
  159. _.each(obj.activityObjectBags, jsoner);
  160. _.each(obj.activityObjectStreams, jsoner);
  161. _.each(obj.activityStreams, jsoner);
  162. _.each(obj.peopleStreams, jsoner);
  163. _.each(obj.listStreams, jsoner);
  164. _.each(obj.people, jsoner);
  165. }
  166. return json;
  167. },
  168. set: function(props) {
  169. var model = this;
  170. if (_.has(props, "items")) {
  171. Pump.debug("Setting property 'items' for model " + model.id);
  172. }
  173. return Backbone.Model.prototype.set.apply(model, arguments);
  174. },
  175. merge: function(props) {
  176. var model = this,
  177. complicated = model.complicated();
  178. Pump.debug("Merging " + model.id + " with " + (props.id || props.url || props.nickname || "unknown"));
  179. _.each(props, function(value, key) {
  180. if (!model.has(key)) {
  181. model.set(key, value);
  182. } else if (_.includes(complicated, key) && model[key] && _.isFunction(model[key].merge)) {
  183. model[key].merge(value);
  184. } else {
  185. // XXX: resolve non-complicated stuff
  186. }
  187. });
  188. },
  189. complicated: function() {
  190. var attrs = ["activityObjects",
  191. "activityObjectBags",
  192. "activityObjectStreams",
  193. "activityStreams",
  194. "peopleStreams",
  195. "listStreams",
  196. "people"],
  197. names = [],
  198. model = this;
  199. _.each(attrs, function(attr) {
  200. if (_.isArray(model[attr])) {
  201. names = names.concat(model[attr]);
  202. }
  203. });
  204. return names;
  205. }
  206. },
  207. {
  208. cache: {},
  209. keyAttr: "id", // works for activities and activityobjects
  210. unique: function(props) {
  211. var inst,
  212. cls = this,
  213. key = props[cls.keyAttr];
  214. if (key && _.has(cls.cache, key)) {
  215. inst = cls.cache[key];
  216. // Check the updated flag
  217. inst.merge(props);
  218. } else {
  219. inst = new cls(props);
  220. if (key) {
  221. cls.cache[key] = inst;
  222. }
  223. inst.on("change:"+cls.keyAttr, function(model, key) {
  224. var oldKey = model.previous(cls.keyAttr);
  225. if (oldKey && _.has(cls.cache, oldKey)) {
  226. delete cls.cache[oldKey];
  227. }
  228. cls.cache[key] = inst;
  229. });
  230. }
  231. return inst;
  232. },
  233. clearCache: function() {
  234. this.cache = {};
  235. }
  236. });
  237. // An array of objects, usually the "items" in a stream
  238. Pump.Items = Backbone.Collection.extend({
  239. constructor: function(models, options) {
  240. var items = this;
  241. // Use unique() to get unique items
  242. models = _.map(models, function(raw) {
  243. return items.model.unique(raw);
  244. });
  245. Backbone.Collection.apply(this, [models, options]);
  246. },
  247. url: function() {
  248. var items = this;
  249. return items.stream.url();
  250. },
  251. toJSON: function(seen) {
  252. var items = this;
  253. return items.map(function(item) {
  254. return item.toJSON(seen);
  255. });
  256. },
  257. merge: function(props) {
  258. var items = this,
  259. unique;
  260. if (_.isArray(props)) {
  261. Pump.debug("Merging items of " + items.url() + " of length " + items.length + " with array of length " + props.length);
  262. unique = props.map(function(item) {
  263. return items.model.unique(item);
  264. });
  265. items.add(unique);
  266. } else {
  267. Pump.debug("Non-array passed to items.merge()");
  268. }
  269. }
  270. });
  271. // A stream of objects. It maps to the ActivityStreams collection
  272. // representation -- some wrap-up data like url and totalItems, plus an array of items.
  273. Pump.Stream = Pump.Model.extend({
  274. people: ["author"],
  275. itemsClass: Pump.Items,
  276. idAttribute: "url",
  277. getObjectType: function() {
  278. var obj = this;
  279. return "collection";
  280. },
  281. initialize: function() {
  282. var str = this,
  283. items = str.get("items");
  284. Pump.Model.prototype.initialize.apply(str);
  285. // We should always have items
  286. if (_.isArray(items)) {
  287. str.items = new str.itemsClass(items);
  288. } else {
  289. str.items = new str.itemsClass([]);
  290. }
  291. str.items.stream = str;
  292. str.on("change:items", function(newStr, items) {
  293. var str = this;
  294. Pump.debug("Resetting items of " + str.url() + " to new array of length " + items.length);
  295. str.items.reset(items);
  296. });
  297. },
  298. url: function() {
  299. var str = this;
  300. if (str.has("pump_io") && _.has(str.get("pump_io"), "proxyURL")) {
  301. return str.get("pump_io").proxyURL;
  302. } else {
  303. return str.get("url");
  304. }
  305. },
  306. nextLink: function(count) {
  307. var str = this,
  308. url,
  309. item;
  310. if (_.isUndefined(count)) {
  311. count = 20;
  312. }
  313. if (str.has("links") && _.has(str.get("links"), "next")) {
  314. url = str.get("links").next.href;
  315. } else if (str.items && str.items.length > 0) {
  316. item = str.items.at(str.items.length-1);
  317. url = str.url() + "?before=" + item.id + "&type=" + item.get("objectType");
  318. } else {
  319. url = null;
  320. }
  321. if (url && count != 20) {
  322. url = url + "&count=" + count;
  323. }
  324. return url;
  325. },
  326. prevLink: function(count) {
  327. var str = this,
  328. url,
  329. item;
  330. if (_.isUndefined(count)) {
  331. count = 20;
  332. }
  333. if (str.has("links") && _.has(str.get("links"), "prev")) {
  334. url = str.get("links").prev.href;
  335. } else if (str.items && str.items.length > 0) {
  336. item = str.items.at(0);
  337. url = str.url() + "?since=" + item.id + "&type=" + item.get("objectType");
  338. } else {
  339. url = null;
  340. }
  341. if (url && count != 20) {
  342. url = url + "&count=" + count;
  343. }
  344. return url;
  345. },
  346. getPrev: function(count, callback) { // Get stuff later than the current group
  347. var stream = this,
  348. prevLink,
  349. options;
  350. if (!callback) {
  351. // This can also be undefined, btw
  352. callback = count;
  353. count = 20;
  354. }
  355. prevLink = stream.prevLink(count);
  356. if (!prevLink) {
  357. if (_.isFunction(callback)) {
  358. callback(new Error("Can't get prevLink for stream " + stream.url()), null);
  359. }
  360. return;
  361. }
  362. options = {
  363. type: "GET",
  364. dataType: "json",
  365. url: prevLink,
  366. success: function(data) {
  367. if (data.items && data.items.length > 0) {
  368. if (stream.items) {
  369. stream.items.add(data.items, {at: 0});
  370. } else {
  371. stream.items = new stream.itemsClass(data.items);
  372. }
  373. }
  374. if (data.links && data.links.prev && data.links.prev.href) {
  375. if (stream.has("links")) {
  376. stream.get("links").prev = data.links.prev;
  377. } else {
  378. stream.set("links", {"prev": {"href": data.links.prev.href}});
  379. }
  380. }
  381. if (_.isFunction(callback)) {
  382. callback(null, data);
  383. }
  384. },
  385. error: function(jqxhr) {
  386. if (_.isFunction(callback)) {
  387. callback(Pump.jqxhrError(jqxhr), null);
  388. }
  389. }
  390. };
  391. Pump.ajax(options);
  392. },
  393. getNext: function(count, callback) { // Get stuff earlier than the current group
  394. var stream = this,
  395. nextLink,
  396. options;
  397. if (!callback) {
  398. // This can also be undefined, btw
  399. callback = count;
  400. count = 20;
  401. }
  402. nextLink = stream.nextLink(count);
  403. if (!nextLink) {
  404. if (_.isFunction(callback)) {
  405. callback(new Error("Can't get nextLink for stream " + stream.url()), null);
  406. }
  407. return;
  408. }
  409. options = {
  410. type: "GET",
  411. dataType: "json",
  412. url: nextLink,
  413. success: function(data) {
  414. if (data.items) {
  415. if (stream.items) {
  416. // Add them at the end
  417. stream.items.add(data.items, {at: stream.items.length});
  418. } else {
  419. stream.items = new stream.itemsClass(data.items);
  420. }
  421. }
  422. if (data.links) {
  423. if (data.links.next && data.links.next.href) {
  424. if (stream.has("links")) {
  425. stream.get("links").next = data.links.next;
  426. } else {
  427. stream.set("links", {"next": {"href": data.links.next.href}});
  428. }
  429. } else {
  430. if (stream.has("links")) {
  431. delete stream.get("links").next;
  432. }
  433. }
  434. }
  435. if (_.isFunction(callback)) {
  436. callback(null, data);
  437. }
  438. },
  439. error: function(jqxhr) {
  440. if (_.isFunction(callback)) {
  441. callback(Pump.jqxhrError(jqxhr), null);
  442. }
  443. }
  444. };
  445. Pump.ajax(options);
  446. },
  447. getAllNext: function(callback) {
  448. var stream = this;
  449. stream.getNext(stream.maxCount(), function(err, data) {
  450. if (err) {
  451. callback(err);
  452. } else if (data.items && data.items.length > 0 && stream.items.length < stream.get("totalItems")) {
  453. // recurse
  454. stream.getAllNext(callback);
  455. } else {
  456. callback(null);
  457. }
  458. });
  459. },
  460. getAllPrev: function(callback) {
  461. var stream = this;
  462. stream.getPrev(stream.maxCount(), function(err, data) {
  463. if (err) {
  464. callback(err);
  465. } else if (data.items && data.items.length > 0 && stream.items.length < stream.get("totalItems")) {
  466. // recurse
  467. stream.getAllPrev(callback);
  468. } else {
  469. callback(null);
  470. }
  471. });
  472. },
  473. getAll: function(callback) { // Get stuff later than the current group
  474. var stream = this,
  475. url = stream.url(),
  476. count,
  477. options,
  478. nl,
  479. pl;
  480. if (!url) {
  481. if (_.isFunction(callback)) {
  482. callback(new Error("No url for stream"), null);
  483. }
  484. return;
  485. }
  486. pl = stream.prevLink();
  487. nl = stream.nextLink();
  488. if (nl || pl) {
  489. var ndone = false,
  490. nerror = false,
  491. pdone = false,
  492. perror = false;
  493. stream.getAllNext(function(err) {
  494. ndone = true;
  495. if (err) {
  496. nerror = true;
  497. if (!perror) {
  498. callback(err);
  499. }
  500. } else {
  501. if (pdone) {
  502. callback(null);
  503. }
  504. }
  505. });
  506. stream.getAllPrev(function(err) {
  507. pdone = true;
  508. if (err) {
  509. perror = true;
  510. if (!nerror) {
  511. callback(err);
  512. }
  513. } else {
  514. if (ndone) {
  515. callback(null);
  516. }
  517. }
  518. });
  519. } else {
  520. count = stream.maxCount();
  521. options = {
  522. type: "GET",
  523. dataType: "json",
  524. url: url + "?count=" + count,
  525. success: function(data) {
  526. if (data.items) {
  527. if (stream.items) {
  528. stream.items.add(data.items);
  529. } else {
  530. stream.items = new stream.itemsClass(data.items);
  531. }
  532. }
  533. if (data.links && data.links.next && data.links.next.href) {
  534. if (stream.has("links")) {
  535. stream.get("links").next = data.links.next;
  536. } else {
  537. stream.set("links", data.links);
  538. }
  539. } else {
  540. // XXX: end-of-collection indicator?
  541. }
  542. stream.trigger("getall");
  543. if (_.isFunction(callback)) {
  544. callback(null, data);
  545. }
  546. },
  547. error: function(jqxhr) {
  548. if (_.isFunction(callback)) {
  549. callback(Pump.jqxhrError(jqxhr), null);
  550. }
  551. }
  552. };
  553. Pump.ajax(options);
  554. }
  555. },
  556. maxCount: function() {
  557. var stream = this,
  558. count,
  559. total = stream.get("totalItems");
  560. if (_.isNumber(total)) {
  561. count = Math.min(total, 200);
  562. } else {
  563. count = 200;
  564. }
  565. return count;
  566. },
  567. toJSONRef: function() {
  568. var str = this;
  569. return {
  570. totalItems: str.get("totalItems"),
  571. url: str.get("url")
  572. };
  573. },
  574. toJSON: function(seen) {
  575. var str = this,
  576. url = str.get("url"),
  577. json,
  578. seenNow;
  579. json = Pump.Model.prototype.toJSON.apply(str, [seen]);
  580. if (!seen || (url && !_.includes(seen, url))) {
  581. if (seen) {
  582. seenNow = seen.slice(0);
  583. } else {
  584. seenNow = [];
  585. }
  586. if (url) {
  587. seenNow.push(url);
  588. }
  589. json.items = str.items.toJSON(seenNow);
  590. }
  591. return json;
  592. },
  593. complicated: function() {
  594. var str = this,
  595. names = Pump.Model.prototype.complicated.apply(str);
  596. names.push("items");
  597. return names;
  598. }
  599. },
  600. {
  601. keyAttr: "url"
  602. });
  603. // A social activity.
  604. Pump.Activity = Pump.Model.extend({
  605. activityObjects: ["actor", "object", "target", "generator", "provider", "location"],
  606. activityObjectBags: ["to", "cc", "bto", "bcc"],
  607. url: function() {
  608. var links = this.get("links"),
  609. pump_io = this.get("pump_io"),
  610. uuid = this.get("uuid");
  611. if (pump_io && pump_io.proxyURL) {
  612. return pump_io.proxyURL;
  613. } else if (links && _.isObject(links) && links.self) {
  614. return links.self;
  615. } else if (uuid) {
  616. return "/api/activity/" + uuid;
  617. } else {
  618. return null;
  619. }
  620. },
  621. pubDate: function() {
  622. return Date.parse(this.published);
  623. },
  624. initialize: function() {
  625. var activity = this;
  626. Pump.Model.prototype.initialize.apply(activity);
  627. // For "post" activities we strip the author
  628. // This adds it back in; important for uniquified stuff
  629. if (activity.verb == "post" &&
  630. activity.object &&
  631. !activity.object.author &&
  632. activity.actor) {
  633. activity.object.author = activity.actor;
  634. }
  635. }
  636. });
  637. Pump.ActivityItems = Pump.Items.extend({
  638. model: Pump.Activity,
  639. add: function(models, options) {
  640. // Usually add at the beginning of the list
  641. if (!options) {
  642. options = {};
  643. }
  644. if (!_.has(options, "at")) {
  645. options.at = 0;
  646. }
  647. Backbone.Collection.prototype.add.apply(this, [models, options]);
  648. // Don't apply changes yet.
  649. // this.applyChanges(models);
  650. },
  651. comparator: function(first, second) {
  652. var d1 = first.pubDate(),
  653. d2 = second.pubDate();
  654. if (d1 > d2) {
  655. return -1;
  656. } else if (d2 > d1) {
  657. return 1;
  658. } else {
  659. return 0;
  660. }
  661. },
  662. applyChanges: function(models) {
  663. var items = this;
  664. if (!_.isArray(models)) {
  665. models = [models];
  666. }
  667. _.each(models, function(act) {
  668. if (!(act instanceof Pump.Activity)) {
  669. act = Pump.Activity.unique(act);
  670. }
  671. switch (act.get("verb")) {
  672. case "post":
  673. case "create":
  674. if (act.object.inReplyTo) {
  675. if (!act.object.author) {
  676. act.object.author = act.actor;
  677. }
  678. if (!act.object.inReplyTo.replies) {
  679. act.object.inReplyTo.replies = new Pump.ActivityObjectStream();
  680. }
  681. if (!act.object.inReplyTo.replies.items) {
  682. act.object.inReplyTo.replies.items = new Pump.ActivityObjectItems();
  683. }
  684. act.object.inReplyTo.replies.items.add(act.object);
  685. }
  686. break;
  687. case "like":
  688. case "favorite":
  689. if (!act.object.likes) {
  690. act.object.likes = new Pump.ActivityObjectStream();
  691. }
  692. if (!act.object.likes.items) {
  693. act.object.likes.items = new Pump.ActivityObjectItems();
  694. }
  695. act.object.likes.items.add(act.actor);
  696. break;
  697. case "unlike":
  698. case "unfavorite":
  699. if (act.object.likes && act.object.likes.items) {
  700. act.object.likes.items.remove(act.actor);
  701. }
  702. break;
  703. case "share":
  704. if (!act.object.shares) {
  705. act.object.shares = new Pump.ActivityObjectStream();
  706. }
  707. if (!act.object.shares.items) {
  708. act.object.shares.items = new Pump.ActivityObjectItems();
  709. }
  710. act.object.shares.items.add(act.actor);
  711. break;
  712. case "unshare":
  713. if (act.object.shares && act.object.shares.items) {
  714. act.object.shares.items.remove(act.actor);
  715. }
  716. break;
  717. }
  718. });
  719. }
  720. });
  721. Pump.ActivityStream = Pump.Stream.extend({
  722. itemsClass: Pump.ActivityItems
  723. });
  724. Pump.ActivityObject = Pump.Model.extend({
  725. activityObjects: ["author", "location", "inReplyTo"],
  726. activityObjectBags: ["attachments", "tags"],
  727. activityObjectStreams: ["likes", "replies", "shares"],
  728. url: function() {
  729. var links = this.get("links"),
  730. pump_io = this.get("pump_io"),
  731. uuid = this.get("uuid"),
  732. objectType = this.get("objectType");
  733. if (pump_io && pump_io.proxyURL) {
  734. return pump_io.proxyURL;
  735. } else if (links &&
  736. _.isObject(links) &&
  737. _.has(links, "self") &&
  738. _.isObject(links.self) &&
  739. _.has(links.self, "href") &&
  740. _.isString(links.self.href)) {
  741. return links.self.href;
  742. } else if (objectType) {
  743. return "/api/"+objectType+"/" + uuid;
  744. } else {
  745. return null;
  746. }
  747. }
  748. });
  749. // XXX: merge with Pump.Stream?
  750. Pump.List = Pump.ActivityObject.extend({
  751. objectType: "collection",
  752. peopleStreams: ["members"],
  753. initialize: function() {
  754. Pump.Model.prototype.initialize.apply(this, arguments);
  755. }
  756. });
  757. Pump.Person = Pump.ActivityObject.extend({
  758. objectType: "person",
  759. activityObjectStreams: ["favorites"],
  760. listStreams: ["lists"],
  761. peopleStreams: ["followers", "following"],
  762. initialize: function() {
  763. Pump.Model.prototype.initialize.apply(this, arguments);
  764. }
  765. });
  766. Pump.ActivityObjectItems = Pump.Items.extend({
  767. model: Pump.ActivityObject
  768. });
  769. Pump.ActivityObjectStream = Pump.Stream.extend({
  770. itemsClass: Pump.ActivityObjectItems
  771. });
  772. Pump.ListItems = Pump.Items.extend({
  773. model: Pump.List
  774. });
  775. Pump.ListStream = Pump.Stream.extend({
  776. itemsClass: Pump.ListItems
  777. });
  778. // Unordered, doesn't have an URL
  779. Pump.ActivityObjectBag = Backbone.Collection.extend({
  780. model: Pump.ActivityObject,
  781. merge: function(models, options) {
  782. var bag = this,
  783. Model = bag.model,
  784. mapped;
  785. mapped = models.map(function(item) {
  786. return Model.unique(item);
  787. });
  788. bag.add(mapped);
  789. }
  790. });
  791. Pump.PeopleItems = Pump.Items.extend({
  792. model: Pump.Person
  793. });
  794. Pump.PeopleStream = Pump.ActivityObjectStream.extend({
  795. itemsClass: Pump.PeopleItems,
  796. nextLink: function() {
  797. var str = this,
  798. url;
  799. url = Pump.ActivityObjectStream.prototype.nextLink.apply(str, arguments);
  800. if (url && url.indexOf("&type=person") == -1) {
  801. url = url + "&type=person";
  802. }
  803. return url;
  804. },
  805. prevLink: function() {
  806. var str = this,
  807. url;
  808. url = Pump.ActivityObjectStream.prototype.prevLink.apply(str, arguments);
  809. if (url && url.indexOf("&type=person") == -1) {
  810. url = url + "&type=person";
  811. }
  812. return url;
  813. }
  814. });
  815. Pump.User = Pump.Model.extend({
  816. idAttribute: "nickname",
  817. people: ["profile"],
  818. initialize: function() {
  819. var user = this,
  820. streamUrl = function(rel) {
  821. return Pump.fullURL("/api/user/" + user.get("nickname") + rel);
  822. },
  823. userStream = function(rel) {
  824. return Pump.ActivityStream.unique({url: streamUrl(rel)});
  825. };
  826. Pump.Model.prototype.initialize.apply(this, arguments);
  827. // XXX: maybe move some of these to Person...?
  828. user.inbox = userStream("/inbox");
  829. user.majorInbox = userStream("/inbox/major");
  830. user.minorInbox = userStream("/inbox/minor");
  831. user.directInbox = userStream("/inbox/direct");
  832. user.majorDirectInbox = userStream("/inbox/direct/major");
  833. user.minorDirectInbox = userStream("/inbox/direct/minor");
  834. user.stream = userStream("/feed");
  835. user.majorStream = userStream("/feed/major");
  836. user.minorStream = userStream("/feed/minor");
  837. user.on("change:nickname", function() {
  838. user.inbox.url = streamUrl("/inbox");
  839. user.majorInbox.url = streamUrl("/inbox/major");
  840. user.minorInbox.url = streamUrl("/inbox/minor");
  841. user.directInbox.url = streamUrl("/inbox/direct");
  842. user.majorDirectInbox.url = streamUrl("/inbox/direct/major");
  843. user.minorDirectInbox.url = streamUrl("/inbox/direct/minor");
  844. user.stream.url = streamUrl("/feed");
  845. user.majorStream.url = streamUrl("/feed/major");
  846. user.minorStream.url = streamUrl("/feed/minor");
  847. });
  848. },
  849. isNew: function() {
  850. // Always PUT
  851. return false;
  852. },
  853. url: function() {
  854. return Pump.fullURL("/api/user/" + this.get("nickname"));
  855. }
  856. },
  857. {
  858. cache: {}, // separate cache
  859. keyAttr: "nickname", // cache by nickname
  860. clearCache: function() {
  861. this.cache = {};
  862. }
  863. });
  864. })(window._, window.$, window.Backbone, window.Pump);