Admin.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. /*
  2. This file is part of the Sonata package.
  3. (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
  4. For the full copyright and license information, please view the LICENSE
  5. file that was distributed with this source code.
  6. */
  7. jQuery(document).ready(function() {
  8. jQuery('html').removeClass('no-js');
  9. if (window.SONATA_CONFIG && window.SONATA_CONFIG.CONFIRM_EXIT) {
  10. jQuery('.sonata-ba-form form').confirmExit();
  11. }
  12. Admin.setup_per_page_switcher(document);
  13. Admin.shared_setup(document);
  14. });
  15. jQuery(document).on('sonata-admin-append-form-element', function(e) {
  16. Admin.setup_select2(e.target);
  17. });
  18. var Admin = {
  19. /**
  20. * This function must called when a ajax call is done, to ensure
  21. * retrieve html is properly setup
  22. *
  23. * @param subject
  24. */
  25. shared_setup: function(subject) {
  26. Admin.log("[Admin] apply shared_setup");
  27. Admin.setup_collection_buttons(subject);
  28. Admin.set_object_field_value(subject);
  29. Admin.setup_select2(subject);
  30. Admin.add_filters(subject);
  31. Admin.setup_xeditable(subject);
  32. Admin.add_pretty_errors(subject);
  33. Admin.setup_form_tabs_for_errors(subject);
  34. Admin.setup_inline_form_errors(subject);
  35. // Admin.setup_list_modal(subject);
  36. },
  37. setup_list_modal: function(modal) {
  38. // this will force relation modal to open list of entity in a wider modal
  39. // to improve readability
  40. jQuery('div.modal-dialog', modal).css({
  41. width: '90%', //choose your width
  42. height: '85%',
  43. padding: 0
  44. });
  45. jQuery('div.modal-content', modal).css({
  46. 'border-radius':'0',
  47. height: '100%',
  48. padding: 0
  49. });
  50. jQuery('.modal-body', modal).css({
  51. width: 'auto',
  52. height: '90%',
  53. padding: 5,
  54. overflow: 'scroll'
  55. });
  56. },
  57. setup_select2: function(subject) {
  58. if (window.SONATA_CONFIG && window.SONATA_CONFIG.USE_SELECT2 && window.Select2) {
  59. jQuery("input[type='checkbox'], input[type='radio']", subject).iCheck({
  60. checkboxClass: 'icheckbox_minimal',
  61. radioClass: 'iradio_minimal'
  62. });
  63. jQuery('select:not([data-sonata-select2="false"])', subject).each(function() {
  64. var select = $(this);
  65. var allowClearEnabled = false;
  66. if (select.find('option[value=""]').length) {
  67. allowClearEnabled = true;
  68. }
  69. if (select.attr('data-sonata-select2-allow-clear')==='true') {
  70. allowClearEnabled = true;
  71. } else if (select.attr('data-sonata-select2-allow-clear')==='false') {
  72. allowClearEnabled = false;
  73. }
  74. select.select2({
  75. width: 'resolve',
  76. minimumResultsForSearch: 10,
  77. allowClear: allowClearEnabled
  78. });
  79. var popover = select.data('popover');
  80. if (undefined !== popover) {
  81. select
  82. .select2('container')
  83. .popover(popover.options)
  84. ;
  85. }
  86. });
  87. }
  88. },
  89. setup_xeditable: function(subject) {
  90. jQuery('.x-editable', subject).editable({
  91. emptyclass: 'editable-empty btn btn-sm',
  92. emptytext: '<i class="glyphicon glyphicon-edit"></i>',
  93. success: function(response) {
  94. if('KO' === response.status) {
  95. return response.message;
  96. }
  97. var html = jQuery(response.content);
  98. Admin.setup_xeditable(html);
  99. jQuery(this)
  100. .closest('td')
  101. .replaceWith(html)
  102. ;
  103. }
  104. });
  105. },
  106. /**
  107. * render log message
  108. * @param mixed
  109. */
  110. log: function() {
  111. var msg = '[Sonata.Admin] ' + Array.prototype.join.call(arguments,', ');
  112. if (window.console && window.console.log) {
  113. window.console.log(msg);
  114. } else if (window.opera && window.opera.postError) {
  115. window.opera.postError(msg);
  116. }
  117. },
  118. /**
  119. * display related errors messages
  120. *
  121. * @param subject
  122. */
  123. add_pretty_errors: function(subject) {
  124. jQuery('div.sonata-ba-field-error', subject).each(function(index, element) {
  125. var input = jQuery(':input', element);
  126. if (!input.length) {
  127. return;
  128. }
  129. var message = jQuery('div.sonata-ba-field-error-messages', element).html();
  130. jQuery('div.sonata-ba-field-error-messages', element).remove();
  131. if (!message || message.length == 0) {
  132. return;
  133. }
  134. var target = input,
  135. fieldShortDescription = input.closest('.field-container').find('.field-short-description'),
  136. select2 = input.closest('.select2-container')
  137. ;
  138. if (fieldShortDescription.length) {
  139. target = fieldShortDescription;
  140. } else if (select2.length) {
  141. target= select2;
  142. }
  143. target.popover({
  144. content: message,
  145. trigger: 'hover',
  146. html: true,
  147. placement: 'top',
  148. template: '<div class="popover"><div class="arrow"></div><div class="popover-inner"><div class="popover-content alert-error"><p></p></div></div></div>'
  149. });
  150. });
  151. },
  152. stopEvent: function(event) {
  153. // https://github.com/sonata-project/SonataAdminBundle/issues/151
  154. //if it is a standard browser use preventDefault otherwise it is IE then return false
  155. if(event.preventDefault) {
  156. event.preventDefault();
  157. } else {
  158. event.returnValue = false;
  159. }
  160. //if it is a standard browser get target otherwise it is IE then adapt syntax and get target
  161. if (typeof event.target != 'undefined') {
  162. targetElement = event.target;
  163. } else {
  164. targetElement = event.srcElement;
  165. }
  166. return targetElement;
  167. },
  168. add_filters: function(subject) {
  169. jQuery('div.filter_container .sonata-filter-option', subject).hide();
  170. jQuery('h4.filter_legend', subject).click(function(event) {
  171. jQuery('div.filter_container .sonata-filter-option').toggle();
  172. });
  173. },
  174. /**
  175. * Change object field value
  176. * @param subject
  177. */
  178. set_object_field_value: function(subject) {
  179. this.log(jQuery('a.sonata-ba-edit-inline', subject));
  180. jQuery('a.sonata-ba-edit-inline', subject).click(function(event) {
  181. Admin.stopEvent(event);
  182. var subject = jQuery(this);
  183. jQuery.ajax({
  184. url: subject.attr('href'),
  185. type: 'POST',
  186. success: function(json) {
  187. if(json.status === "OK") {
  188. var elm = jQuery(subject).parent();
  189. elm.children().remove();
  190. // fix issue with html comment ...
  191. elm.html(jQuery(json.content.replace(/<!--[\s\S]*?-->/g, "")).html());
  192. elm.effect("highlight", {'color' : '#57A957'}, 2000);
  193. Admin.set_object_field_value(elm);
  194. } else {
  195. jQuery(subject).parent().effect("highlight", {'color' : '#C43C35'}, 2000);
  196. }
  197. }
  198. });
  199. });
  200. },
  201. setup_collection_buttons: function(subject) {
  202. jQuery(subject).on('click', '.sonata-collection-add', function(event) {
  203. Admin.stopEvent(event);
  204. var container = jQuery(this).closest('[data-prototype]');
  205. var proto = container.attr('data-prototype');
  206. var protoName = container.attr('data-prototype-name') || '__name__';
  207. // Set field id
  208. var idRegexp = new RegExp(container.attr('id')+'_'+protoName,'g');
  209. proto = proto.replace(idRegexp, container.attr('id')+'_'+(container.children().length - 1));
  210. // Set field name
  211. var parts = container.attr('id').split('_');
  212. var nameRegexp = new RegExp(parts[parts.length-1]+'\\]\\['+protoName,'g');
  213. proto = proto.replace(nameRegexp, parts[parts.length-1]+']['+(container.children().length - 1));
  214. jQuery(proto)
  215. .insertBefore(jQuery(this).parent())
  216. .trigger('sonata-admin-append-form-element')
  217. ;
  218. jQuery(this).trigger('sonata-collection-item-added');
  219. });
  220. jQuery(subject).on('click', '.sonata-collection-delete', function(event) {
  221. Admin.stopEvent(event);
  222. jQuery(this).trigger('sonata-collection-item-deleted');
  223. jQuery(this).closest('.sonata-collection-row').remove();
  224. });
  225. },
  226. setup_per_page_switcher: function(subject) {
  227. jQuery('select.per-page').change(function(event) {
  228. jQuery('input[type=submit]').hide();
  229. window.top.location.href=this.options[this.selectedIndex].value;
  230. });
  231. },
  232. setup_form_tabs_for_errors: function(subject) {
  233. // Switch to first tab with server side validation errors on page load
  234. jQuery('form', subject).each(function() {
  235. Admin.show_form_first_tab_with_errors(jQuery(this), '.sonata-ba-field-error');
  236. });
  237. // Switch to first tab with HTML5 errors on form submit
  238. jQuery(subject)
  239. .on('click', 'form [type="submit"]', function() {
  240. Admin.show_form_first_tab_with_errors(jQuery(this).closest('form'), ':invalid');
  241. })
  242. .on('keypress', 'form [type="text"]', function(e) {
  243. if (13 === e.which) {
  244. Admin.show_form_first_tab_with_errors(jQuery(this), ':invalid');
  245. }
  246. })
  247. ;
  248. },
  249. show_form_first_tab_with_errors: function(form, errorSelector) {
  250. var tabs = form.find('.nav-tabs a'),
  251. firstTabWithErrors;
  252. tabs.each(function() {
  253. var id = jQuery(this).attr('href'),
  254. tab = jQuery(this),
  255. icon = tab.find('.has-errors');
  256. if (jQuery(id).find(errorSelector).length > 0) {
  257. // Only show first tab with errors
  258. if (!firstTabWithErrors) {
  259. tab.tab('show');
  260. firstTabWithErrors = tab;
  261. }
  262. icon.removeClass('hide');
  263. } else {
  264. icon.addClass('hide');
  265. }
  266. });
  267. },
  268. setup_inline_form_errors: function(subject) {
  269. var deleteCheckboxSelector = '.sonata-ba-field-inline-table [id$="_delete"][type="checkbox"]';
  270. jQuery(deleteCheckboxSelector, subject).each(function() {
  271. Admin.switch_inline_form_errors(jQuery(this));
  272. });
  273. $(subject).on('change', deleteCheckboxSelector, function() {
  274. Admin.switch_inline_form_errors(jQuery(this));
  275. });
  276. },
  277. /**
  278. * Disable inline form errors when the row is marked for deletion
  279. */
  280. switch_inline_form_errors: function(deleteCheckbox) {
  281. var row = deleteCheckbox.closest('.sonata-ba-field-inline-table'),
  282. errors = row.find('.sonata-ba-field-error-messages')
  283. ;
  284. if (deleteCheckbox.is(':checked')) {
  285. row
  286. .find('[required]')
  287. .removeAttr('required')
  288. .attr('data-required', 'required')
  289. ;
  290. errors.hide();
  291. } else {
  292. row
  293. .find('[data-required]')
  294. .attr('required', 'required')
  295. ;
  296. errors.show();
  297. }
  298. }
  299. };