Admin.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  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').each(function () { jQuery(this).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 = jQuery(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. ereg = /width:(auto|(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc)))/i;
  73. select.select2({
  74. width: function() {
  75. // this code is an adaptation of select2 code (initContainerWidth function)
  76. style = this.element.attr('style');
  77. //console.log("main style", style);
  78. if (style !== undefined) {
  79. attrs = style.split(';');
  80. for (i = 0, l = attrs.length; i < l; i = i + 1) {
  81. matches = attrs[i].replace(/\s/g, '').match(ereg);
  82. if (matches !== null && matches.length >= 1)
  83. return matches[1];
  84. }
  85. }
  86. style = this.element.css('width');
  87. if (style.indexOf("%") > 0) {
  88. return style;
  89. }
  90. return '100%';
  91. },
  92. dropdownAutoWidth: true,
  93. minimumResultsForSearch: 10,
  94. allowClear: allowClearEnabled
  95. });
  96. var popover = select.data('popover');
  97. if (undefined !== popover) {
  98. select
  99. .select2('container')
  100. .popover(popover.options)
  101. ;
  102. }
  103. });
  104. }
  105. },
  106. setup_icheck: function(subject) {
  107. if (window.SONATA_CONFIG && window.SONATA_CONFIG.USE_ICHECK) {
  108. jQuery("input[type='checkbox']:not('label.btn>input'), input[type='radio']:not('label.btn>input')", subject).iCheck({
  109. checkboxClass: 'icheckbox_minimal',
  110. radioClass: 'iradio_minimal'
  111. });
  112. }
  113. },
  114. setup_xeditable: function(subject) {
  115. jQuery('.x-editable', subject).editable({
  116. emptyclass: 'editable-empty btn btn-sm',
  117. emptytext: '<i class="glyphicon glyphicon-edit"></i>',
  118. container: 'body',
  119. success: function(response) {
  120. if('KO' === response.status) {
  121. return response.message;
  122. }
  123. var html = jQuery(response.content);
  124. Admin.setup_xeditable(html);
  125. jQuery(this)
  126. .closest('td')
  127. .replaceWith(html)
  128. ;
  129. }
  130. });
  131. },
  132. /**
  133. * render log message
  134. * @param mixed
  135. */
  136. log: function() {
  137. var msg = '[Sonata.Admin] ' + Array.prototype.join.call(arguments,', ');
  138. if (window.console && window.console.log) {
  139. window.console.log(msg);
  140. } else if (window.opera && window.opera.postError) {
  141. window.opera.postError(msg);
  142. }
  143. },
  144. /**
  145. * display related errors messages
  146. *
  147. * @param subject
  148. */
  149. add_pretty_errors: function(subject) {
  150. jQuery('div.sonata-ba-field-error', subject).each(function(index, element) {
  151. var input = jQuery(':input', element);
  152. if (!input.length) {
  153. return;
  154. }
  155. var message = jQuery('div.sonata-ba-field-error-messages', element).html();
  156. jQuery('div.sonata-ba-field-error-messages', element).remove();
  157. if (!message || message.length == 0) {
  158. return;
  159. }
  160. var target = input,
  161. fieldShortDescription = input.closest('.field-container').find('.field-short-description'),
  162. select2 = input.closest('.select2-container')
  163. ;
  164. if (fieldShortDescription.length) {
  165. target = fieldShortDescription;
  166. } else if (select2.length) {
  167. target= select2;
  168. }
  169. target.popover({
  170. content: message,
  171. trigger: 'hover',
  172. html: true,
  173. placement: 'top',
  174. template: '<div class="popover"><div class="arrow"></div><div class="popover-inner"><div class="popover-content alert-error"><p></p></div></div></div>'
  175. });
  176. });
  177. },
  178. stopEvent: function(event) {
  179. // https://github.com/sonata-project/SonataAdminBundle/issues/151
  180. //if it is a standard browser use preventDefault otherwise it is IE then return false
  181. if(event.preventDefault) {
  182. event.preventDefault();
  183. } else {
  184. event.returnValue = false;
  185. }
  186. //if it is a standard browser get target otherwise it is IE then adapt syntax and get target
  187. if (typeof event.target != 'undefined') {
  188. targetElement = event.target;
  189. } else {
  190. targetElement = event.srcElement;
  191. }
  192. return targetElement;
  193. },
  194. add_filters: function(subject) {
  195. jQuery('div.filter_container .sonata-filter-option', subject).hide();
  196. jQuery('h4.filter_legend', subject).click(function(event) {
  197. jQuery('div.filter_container .sonata-filter-option').toggle();
  198. });
  199. },
  200. /**
  201. * Change object field value
  202. * @param subject
  203. */
  204. set_object_field_value: function(subject) {
  205. this.log(jQuery('a.sonata-ba-edit-inline', subject));
  206. jQuery('a.sonata-ba-edit-inline', subject).click(function(event) {
  207. Admin.stopEvent(event);
  208. var subject = jQuery(this);
  209. jQuery.ajax({
  210. url: subject.attr('href'),
  211. type: 'POST',
  212. success: function(json) {
  213. if(json.status === "OK") {
  214. var elm = jQuery(subject).parent();
  215. elm.children().remove();
  216. // fix issue with html comment ...
  217. elm.html(jQuery(json.content.replace(/<!--[\s\S]*?-->/g, "")).html());
  218. elm.effect("highlight", {'color' : '#57A957'}, 2000);
  219. Admin.set_object_field_value(elm);
  220. } else {
  221. jQuery(subject).parent().effect("highlight", {'color' : '#C43C35'}, 2000);
  222. }
  223. }
  224. });
  225. });
  226. },
  227. setup_collection_buttons: function(subject) {
  228. jQuery(subject).on('click', '.sonata-collection-add', function(event) {
  229. Admin.stopEvent(event);
  230. var container = jQuery(this).closest('[data-prototype]');
  231. var proto = container.attr('data-prototype');
  232. var protoName = container.attr('data-prototype-name') || '__name__';
  233. // Set field id
  234. var idRegexp = new RegExp(container.attr('id')+'_'+protoName,'g');
  235. proto = proto.replace(idRegexp, container.attr('id')+'_'+(container.children().length - 1));
  236. // Set field name
  237. var parts = container.attr('id').split('_');
  238. var nameRegexp = new RegExp(parts[parts.length-1]+'\\]\\['+protoName,'g');
  239. proto = proto.replace(nameRegexp, parts[parts.length-1]+']['+(container.children().length - 1));
  240. jQuery(proto)
  241. .insertBefore(jQuery(this).parent())
  242. .trigger('sonata-admin-append-form-element')
  243. ;
  244. jQuery(this).trigger('sonata-collection-item-added');
  245. });
  246. jQuery(subject).on('click', '.sonata-collection-delete', function(event) {
  247. Admin.stopEvent(event);
  248. jQuery(this).trigger('sonata-collection-item-deleted');
  249. jQuery(this).closest('.sonata-collection-row').remove();
  250. });
  251. },
  252. setup_per_page_switcher: function(subject) {
  253. jQuery('select.per-page').change(function(event) {
  254. jQuery('input[type=submit]').hide();
  255. window.top.location.href=this.options[this.selectedIndex].value;
  256. });
  257. },
  258. setup_form_tabs_for_errors: function(subject) {
  259. // Switch to first tab with server side validation errors on page load
  260. jQuery('form', subject).each(function() {
  261. Admin.show_form_first_tab_with_errors(jQuery(this), '.sonata-ba-field-error');
  262. });
  263. // Switch to first tab with HTML5 errors on form submit
  264. jQuery(subject)
  265. .on('click', 'form [type="submit"]', function() {
  266. Admin.show_form_first_tab_with_errors(jQuery(this).closest('form'), ':invalid');
  267. })
  268. .on('keypress', 'form [type="text"]', function(e) {
  269. if (13 === e.which) {
  270. Admin.show_form_first_tab_with_errors(jQuery(this), ':invalid');
  271. }
  272. })
  273. ;
  274. },
  275. show_form_first_tab_with_errors: function(form, errorSelector) {
  276. var tabs = form.find('.nav-tabs a'),
  277. firstTabWithErrors;
  278. tabs.each(function() {
  279. var id = jQuery(this).attr('href'),
  280. tab = jQuery(this),
  281. icon = tab.find('.has-errors');
  282. if (jQuery(id).find(errorSelector).length > 0) {
  283. // Only show first tab with errors
  284. if (!firstTabWithErrors) {
  285. tab.tab('show');
  286. firstTabWithErrors = tab;
  287. }
  288. icon.removeClass('hide');
  289. } else {
  290. icon.addClass('hide');
  291. }
  292. });
  293. },
  294. setup_inline_form_errors: function(subject) {
  295. var deleteCheckboxSelector = '.sonata-ba-field-inline-table [id$="_delete"][type="checkbox"]';
  296. jQuery(deleteCheckboxSelector, subject).each(function() {
  297. Admin.switch_inline_form_errors(jQuery(this));
  298. });
  299. jQuery(subject).on('change', deleteCheckboxSelector, function() {
  300. Admin.switch_inline_form_errors(jQuery(this));
  301. });
  302. },
  303. /**
  304. * Disable inline form errors when the row is marked for deletion
  305. */
  306. switch_inline_form_errors: function(deleteCheckbox) {
  307. var row = deleteCheckbox.closest('.sonata-ba-field-inline-table'),
  308. errors = row.find('.sonata-ba-field-error-messages')
  309. ;
  310. if (deleteCheckbox.is(':checked')) {
  311. row
  312. .find('[required]')
  313. .removeAttr('required')
  314. .attr('data-required', 'required')
  315. ;
  316. errors.hide();
  317. } else {
  318. row
  319. .find('[data-required]')
  320. .attr('required', 'required')
  321. ;
  322. errors.show();
  323. }
  324. }
  325. };