PageRenderTime 32ms CodeModel.GetById 10ms RepoModel.GetById 1ms app.codeStats 0ms

/root/static/scripts/relationship-editor/common/viewModel.js

https://bitbucket.org/ianmcorvidae/musicbrainz-server
JavaScript | 303 lines | 233 code | 64 blank | 6 comment | 57 complexity | f8f3d8e7e28832fd4ba4ce0c917642d7 MD5 | raw file
Possible License(s): GPL-2.0, AGPL-1.0, CC-BY-3.0, CC0-1.0
  1. // This file is part of MusicBrainz, the open internet music database.
  2. // Copyright (C) 2014 MetaBrainz Foundation
  3. // Licensed under the GPL version 2, or (at your option) any later version:
  4. // http://www.gnu.org/licenses/gpl-2.0.txt
  5. (function (RE) {
  6. function mapItems(result, item) {
  7. if (item.id) {
  8. result[item.id] = item;
  9. }
  10. if (item.gid) {
  11. result[item.gid] = item;
  12. }
  13. _.transform(item.children, mapItems, result);
  14. }
  15. RE.exportTypeInfo = _.once(function (typeInfo, attrInfo) {
  16. MB.typeInfo = typeInfo;
  17. MB.attrInfo = attrInfo;
  18. MB.typeInfoByID = _(typeInfo).values().flatten().transform(mapItems, {}).value();
  19. MB.attrInfoByID = _(attrInfo).values().transform(mapItems, {}).value();
  20. _.each(MB.typeInfoByID, function (type) {
  21. _.each(type.attributes, function (typeAttr, id) {
  22. typeAttr.attribute = MB.attrInfoByID[id];
  23. });
  24. });
  25. MB.allowedRelations = {};
  26. _(MB.typeInfo).keys().each(function (typeString) {
  27. var types = typeString.split("-");
  28. var type0 = types[0];
  29. var type1 = types[1];
  30. if (!editorMayEditTypes(type0, type1)) {
  31. return;
  32. }
  33. (MB.allowedRelations[type0] = MB.allowedRelations[type0] || []).push(type1);
  34. if (type0 !== type1) {
  35. (MB.allowedRelations[type1] = MB.allowedRelations[type1] || []).push(type0);
  36. }
  37. });
  38. _.each(MB.attrInfoByID, function (attr) {
  39. attr.root = MB.attrInfoByID[attr.rootID];
  40. });
  41. });
  42. RE.ViewModel = aclass({
  43. relationshipClass: RE.fields.Relationship,
  44. activeDialog: ko.observable(),
  45. init: function (options) {
  46. this.source = options.source;
  47. this.uniqueID = _.uniqueId("relationship-editor-");
  48. this.cache = {};
  49. },
  50. getRelationship: function (data, source) {
  51. return MB.getRelationship(data, source);
  52. },
  53. removeRelationship: function (relationship) {
  54. if (relationship.added()) {
  55. relationship.remove();
  56. } else if (relationship.removed()) {
  57. relationship.removed(false);
  58. } else {
  59. if (relationship.edited()) {
  60. relationship.fromJS(relationship.original);
  61. }
  62. relationship.removed(true);
  63. }
  64. },
  65. _sortedRelationships: function (relationships, source) {
  66. return relationships
  67. .sortBy(function (r) { return r.lowerCaseTargetName(source) })
  68. .sortBy("linkOrder");
  69. }
  70. });
  71. }(MB.relationshipEditor = MB.relationshipEditor || {}));
  72. MB.initRelationshipEditors = function (args) {
  73. MB.relationshipEditor.exportTypeInfo(args.typeInfo, args.attrInfo);
  74. var sourceData = args.sourceData;
  75. // XXX used by series edit form
  76. sourceData.gid = sourceData.gid || _.uniqueId("tmp-");
  77. MB.sourceEntityGID = sourceData.gid;
  78. MB.sourceEntity = MB.entity(sourceData);
  79. var source = MB.sourceEntity;
  80. var vmArgs = { source: source, formName: args.formName };
  81. MB.relationshipEditor.GenericEntityViewModel(vmArgs);
  82. var externalLinksEditor = $('#external-links-editor-container')[0];
  83. if (externalLinksEditor) {
  84. MB.sourceExternalLinksEditor = MB.createExternalLinksEditor({
  85. sourceData: sourceData,
  86. mountPoint: externalLinksEditor
  87. });
  88. }
  89. source.parseRelationships(sourceData.relationships);
  90. addPostedRelationships(source);
  91. if (!MB.formWasPosted) {
  92. addRelationshipsFromQueryString(source);
  93. }
  94. var $content = $("#relationship-editor");
  95. ko.applyBindings(MB.sourceRelationshipEditor, $content[0]);
  96. $content.show();
  97. };
  98. MB.getRelationship = function (data, source) {
  99. var target = data.target;
  100. data = _.clone(data);
  101. var backward = source.entityType > target.entityType;
  102. if (source.entityType === target.entityType) {
  103. backward = (data.direction === "backward");
  104. }
  105. data.entities = backward ? [target, source] : [source, target];
  106. var viewModel = getRelationshipEditor(data, source);
  107. if (viewModel) {
  108. if (data.id) {
  109. var cacheKey = _.pluck(data.entities, "entityType").concat(data.id).join("-");
  110. var cached = viewModel.cache[cacheKey];
  111. if (cached) {
  112. return cached;
  113. }
  114. }
  115. var relationship = viewModel.relationshipClass(data, source, viewModel);
  116. return data.id ? (viewModel.cache[cacheKey] = relationship) : relationship;
  117. }
  118. };
  119. function getRelationshipEditor(data, source) {
  120. if (source.entityType === 'url') {
  121. return MB.sourceRelationshipEditor;
  122. }
  123. var target = data.target;
  124. var typeInfo = MB.typeInfoByID[data.linkTypeID];
  125. if ((target && target.entityType === 'url') ||
  126. (typeInfo && (typeInfo.type0 === 'url' || typeInfo.type1 === 'url'))) {
  127. return; // handled by the external links editor
  128. }
  129. if (MB.releaseRelationshipEditor) {
  130. return MB.releaseRelationshipEditor;
  131. }
  132. if (source === MB.sourceRelationshipEditor.source) {
  133. return MB.sourceRelationshipEditor;
  134. }
  135. }
  136. function addSubmittedRelationship(data, source) {
  137. var relationship = MB.getRelationship(data, source);
  138. if (!relationship) {
  139. return;
  140. } else if (relationship.id) {
  141. relationship.fromJS(data);
  142. } else {
  143. relationship.show();
  144. }
  145. }
  146. function addPostedRelationships(source) {
  147. if (MB.hasSessionStorage && sessionStorage.submittedRelationships) {
  148. if (MB.formWasPosted) {
  149. _.each(JSON.parse(sessionStorage.submittedRelationships), function (data) {
  150. addSubmittedRelationship(data, source);
  151. });
  152. }
  153. delete sessionStorage.submittedRelationships;
  154. }
  155. }
  156. function addRelationshipsFromQueryString(source) {
  157. var fields = parseQueryString(window.location.search);
  158. _.each(fields.rels, function (rel) {
  159. var typeInfo = MB.typeInfoByID[rel.type];
  160. var targetIsUUID = uuidRegex.test(rel.target);
  161. if (!typeInfo && !targetIsUUID) {
  162. // We need at least a link type or target gid
  163. return;
  164. }
  165. var target = targetIsUUID ? (MB.entityCache[rel.target] || { gid: rel.target }) : { name: rel.target };
  166. if (typeInfo && !target.entityType) {
  167. target.entityType = source.entityType === typeInfo.type0 ? typeInfo.type1 : typeInfo.type0;
  168. }
  169. var data = {
  170. target: target,
  171. linkTypeID: typeInfo ? typeInfo.id : null,
  172. beginDate: parseDateString(rel.begin_date || ''),
  173. endDate: parseDateString(rel.end_date || ''),
  174. ended: !!Number(rel.ended),
  175. direction: rel.direction,
  176. linkOrder: Number(rel.link_order) || 0
  177. };
  178. if (typeInfo) {
  179. data.attributes = _.transform(rel.attributes, function (accum, attr) {
  180. var attrInfo = MB.attrInfoByID[attr.type];
  181. if (attrInfo && typeInfo.attributes[attrInfo.id]) {
  182. accum.push({
  183. type: { gid: attr.type },
  184. credit: attr.credited_as,
  185. textValue: attr.text_value
  186. });
  187. }
  188. }, []);
  189. }
  190. if (target.entityType) {
  191. addSubmittedRelationship(data, source);
  192. } else {
  193. MB.utility.request({ url: '/ws/js/entity/' + rel.target })
  194. .done(function (targetData) {
  195. data.target = targetData;
  196. addSubmittedRelationship(data, source);
  197. });
  198. }
  199. });
  200. }
  201. var uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[345][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
  202. var dateRegex = /(?:-|([0-9]{4}))(?:-(?:-|(0[1-9]|1[0-2]))(?:-(?:-|([0-2][1-9]|3[0-1])))?)?/;
  203. function parseQueryString(queryString) {
  204. var queryStringRegex = /(?:\\?|&)([A-z0-9\-_.]+)=([^&]+)/g;
  205. var fields = {};
  206. var subField, match, parts;
  207. while (match = queryStringRegex.exec(queryString)) {
  208. subField = fields;
  209. parts = match[1].split('.');
  210. _.each(parts, function (part, index) {
  211. if (index === parts.length - 1) {
  212. subField[part] = decodeURIComponent(match[2])
  213. } else {
  214. subField = subField[part] = subField[part] || {};
  215. }
  216. });
  217. }
  218. return fields;
  219. }
  220. function parseDateString(string) {
  221. var match = string.match(dateRegex);
  222. if (match) {
  223. return {
  224. year: match[1] || null,
  225. month: match[2] || null,
  226. day: match[3] || null
  227. };
  228. }
  229. return null;
  230. }
  231. function editorMayEditTypes(type0, type1) {
  232. var types = [type0, type1].sort().join('-');
  233. if (/^area-area|area-url$/.test(types)) {
  234. return !!MB.userIsLocationEditor;
  235. } else if (/^area-instrument|instrument-instrument|instrument-url$/.test(types)) {
  236. return !!MB.userIsRelationshipEditor;
  237. }
  238. return true;
  239. }