RelatedObjectLookups.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. /*global SelectBox, interpolate*/
  2. // Handles related-objects functionality: lookup link for raw_id_fields
  3. // and Add Another links.
  4. (function(django, jet) {
  5. 'use strict';
  6. function html_unescape(text) {
  7. // Unescape a string that was escaped using django.utils.html.escape.
  8. text = text.replace(/&lt;/g, '<');
  9. text = text.replace(/&gt;/g, '>');
  10. text = text.replace(/&quot;/g, '"');
  11. text = text.replace(/&#39;/g, "'");
  12. text = text.replace(/&amp;/g, '&');
  13. return text;
  14. }
  15. // IE doesn't accept periods or dashes in the window name, but the element IDs
  16. // we use to generate popup window names may contain them, therefore we map them
  17. // to allowed characters in a reversible way so that we can locate the correct
  18. // element when the popup window is dismissed.
  19. function id_to_windowname(text) {
  20. text = text.replace(/\./g, '__dot__');
  21. text = text.replace(/\-/g, '__dash__');
  22. return text;
  23. }
  24. function windowname_to_id(text) {
  25. text = text.replace(/__dot__/g, '.');
  26. text = text.replace(/__dash__/g, '-');
  27. return text;
  28. }
  29. function showAdminPopup(triggeringLink, name_regexp, add_popup) {
  30. var name = triggeringLink.id.replace(name_regexp, '');
  31. name = id_to_windowname(name);
  32. var href = triggeringLink.href;
  33. if (add_popup) {
  34. if (href.indexOf('?') === -1) {
  35. href += '?_popup=1';
  36. } else {
  37. href += '&_popup=1';
  38. }
  39. }
  40. // Django JET
  41. showRelatedPopup(name, href);
  42. return false;
  43. }
  44. function showRelatedObjectLookupPopup(triggeringLink) {
  45. return showAdminPopup(triggeringLink, /^lookup_/, true);
  46. }
  47. function dismissRelatedLookupPopup(win, chosenId) {
  48. var name = windowname_to_id(win.name);
  49. var elem = document.getElementById(name);
  50. if (elem.className.indexOf('vManyToManyRawIdAdminField') !== -1 && elem.value) {
  51. elem.value += ',' + chosenId;
  52. } else {
  53. document.getElementById(name).value = chosenId;
  54. }
  55. // Django JET
  56. closeRelatedPopup(win);
  57. }
  58. function showRelatedObjectPopup(triggeringLink) {
  59. return showAdminPopup(triggeringLink, /^(change|add|delete)_/, false);
  60. }
  61. function updateRelatedObjectLinks(triggeringLink) {
  62. var $this = django.jQuery(triggeringLink);
  63. var siblings = $this.nextAll('.change-related, .delete-related');
  64. if (!siblings.length) {
  65. return;
  66. }
  67. var value = $this.val();
  68. if (value) {
  69. siblings.each(function() {
  70. var elm = django.jQuery(this);
  71. elm.attr('href', elm.attr('data-href-template').replace('__fk__', value));
  72. });
  73. } else {
  74. siblings.removeAttr('href');
  75. }
  76. }
  77. function dismissAddRelatedObjectPopup(win, newId, newRepr) {
  78. // newId and newRepr are expected to have previously been escaped by
  79. // django.utils.html.escape.
  80. newId = html_unescape(newId);
  81. newRepr = html_unescape(newRepr);
  82. var name = windowname_to_id(win.name);
  83. var elem = document.getElementById(name);
  84. if (elem) {
  85. var elemName = elem.nodeName.toUpperCase();
  86. if (elemName === 'SELECT') {
  87. elem.options[elem.options.length] = new Option(newRepr, newId, true, true);
  88. } else if (elemName === 'INPUT') {
  89. if (elem.className.indexOf('vManyToManyRawIdAdminField') !== -1 && elem.value) {
  90. elem.value += ',' + newId;
  91. } else {
  92. elem.value = newId;
  93. }
  94. }
  95. // Trigger a change event to update related links if required.
  96. django.jQuery(elem).trigger('change');
  97. } else {
  98. var toId = name + '_to';
  99. var o = new Option(newRepr, newId);
  100. SelectBox.add_to_cache(toId, o);
  101. SelectBox.redisplay(toId);
  102. }
  103. // Django JET
  104. closeRelatedPopup(win);
  105. }
  106. function dismissChangeRelatedObjectPopup(win, objId, newRepr, newId) {
  107. objId = html_unescape(objId);
  108. newRepr = html_unescape(newRepr);
  109. var id = windowname_to_id(win.name).replace(/^edit_/, '');
  110. var selectsSelector = interpolate('#%s, #%s_from, #%s_to', [id, id, id]);
  111. var selects = django.jQuery(selectsSelector);
  112. selects.find('option').each(function() {
  113. if (this.value === objId) {
  114. this.innerHTML = newRepr;
  115. this.value = newId;
  116. }
  117. });
  118. // Django JET
  119. closeRelatedPopup(win);
  120. }
  121. function dismissDeleteRelatedObjectPopup(win, objId) {
  122. objId = html_unescape(objId);
  123. var id = windowname_to_id(win.name).replace(/^delete_/, '');
  124. var selectsSelector = interpolate('#%s, #%s_from, #%s_to', [id, id, id]);
  125. var selects = django.jQuery(selectsSelector);
  126. selects.find('option').each(function() {
  127. if (this.value == objId) {
  128. django.jQuery(this).remove();
  129. }
  130. }).trigger('change');
  131. // Django JET
  132. closeRelatedPopup(win);
  133. }
  134. // Global for testing purposes
  135. window.html_unescape = html_unescape;
  136. window.id_to_windowname = id_to_windowname;
  137. window.windowname_to_id = windowname_to_id;
  138. window.showRelatedObjectLookupPopup = showRelatedObjectLookupPopup;
  139. window.dismissRelatedLookupPopup = dismissRelatedLookupPopup;
  140. window.showRelatedObjectPopup = showRelatedObjectPopup;
  141. window.updateRelatedObjectLinks = updateRelatedObjectLinks;
  142. window.dismissAddRelatedObjectPopup = dismissAddRelatedObjectPopup;
  143. window.dismissChangeRelatedObjectPopup = dismissChangeRelatedObjectPopup;
  144. window.dismissDeleteRelatedObjectPopup = dismissDeleteRelatedObjectPopup;
  145. // Kept for backward compatibility
  146. window.showAddAnotherPopup = showRelatedObjectPopup;
  147. window.dismissAddAnotherPopup = dismissAddRelatedObjectPopup;
  148. // Django JET
  149. opener = parent.window;
  150. function showRelatedPopup(name, href) {
  151. django.jQuery(function($) {
  152. var $container = $('.related-popup-container', parent.document);
  153. var $loading = $container.find('.loading-indicator', parent.document);
  154. var $body = $('body').addClass('non-scrollable', parent.document);
  155. var $popup = $('<iframe>').attr('name', name).attr('src', href).addClass('related-popup').on('load', function() {
  156. $popup.add($('.related-popup-back')).fadeIn(200, 'swing', function() {
  157. $loading.hide();
  158. });
  159. });
  160. $loading.show();
  161. $container.fadeIn(200, 'swing', function() {
  162. $container.append($popup);
  163. });
  164. $body.addClass('non-scrollable', parent.document);
  165. });
  166. }
  167. function closeRelatedPopup(win) {
  168. jet.jQuery('select').trigger('select:init');
  169. jet.jQuery(win.parent).trigger('related-popup:close');
  170. }
  171. django.jQuery(document).ready(function() {
  172. django.jQuery('body').on('click', '.related-widget-wrapper-link', function(e) {
  173. e.preventDefault();
  174. if (this.href) {
  175. var event = django.jQuery.Event('django:show-related', {href: this.href});
  176. django.jQuery(this).trigger(event);
  177. if (!event.isDefaultPrevented()) {
  178. showRelatedObjectPopup(this);
  179. }
  180. }
  181. });
  182. django.jQuery('body').on('change', '.related-widget-wrapper select', function(e) {
  183. var event = django.jQuery.Event('django:update-related');
  184. django.jQuery(this).trigger(event);
  185. if (!event.isDefaultPrevented()) {
  186. updateRelatedObjectLinks(this);
  187. }
  188. });
  189. django.jQuery('.related-widget-wrapper select').trigger('change');
  190. });
  191. }(window.django, window.jet));