Admin.js 12 KB

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