Drupal investigation

js_sSPHDTODCz8KT3M6AO6pC8BG9E7iv6cz5tYG3PJ6CxI.js 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. /**
  2. * @file
  3. * Dialog API inspired by HTML5 dialog element.
  4. *
  5. * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#the-dialog-element
  6. */
  7. (function ($, Drupal, drupalSettings) {
  8. 'use strict';
  9. /**
  10. * Default dialog options.
  11. *
  12. * @type {object}
  13. *
  14. * @prop {bool} [autoOpen=true]
  15. * @prop {string} [dialogClass='']
  16. * @prop {string} [buttonClass='button']
  17. * @prop {string} [buttonPrimaryClass='button--primary']
  18. * @prop {function} close
  19. */
  20. drupalSettings.dialog = {
  21. autoOpen: true,
  22. dialogClass: '',
  23. // Drupal-specific extensions: see dialog.jquery-ui.js.
  24. buttonClass: 'button',
  25. buttonPrimaryClass: 'button--primary',
  26. // When using this API directly (when generating dialogs on the client
  27. // side), you may want to override this method and do
  28. // `jQuery(event.target).remove()` as well, to remove the dialog on
  29. // closing.
  30. close: function (event) {
  31. Drupal.dialog(event.target).close();
  32. Drupal.detachBehaviors(event.target, null, 'unload');
  33. }
  34. };
  35. /**
  36. * @typedef {object} Drupal.dialog~dialogDefinition
  37. *
  38. * @prop {boolean} open
  39. * Is the dialog open or not.
  40. * @prop {*} returnValue
  41. * Return value of the dialog.
  42. * @prop {function} show
  43. * Method to display the dialog on the page.
  44. * @prop {function} showModal
  45. * Method to display the dialog as a modal on the page.
  46. * @prop {function} close
  47. * Method to hide the dialog from the page.
  48. */
  49. /**
  50. * Polyfill HTML5 dialog element with jQueryUI.
  51. *
  52. * @param {HTMLElement} element
  53. * The element that holds the dialog.
  54. * @param {object} options
  55. * jQuery UI options to be passed to the dialog.
  56. *
  57. * @return {Drupal.dialog~dialogDefinition}
  58. * The dialog instance.
  59. */
  60. Drupal.dialog = function (element, options) {
  61. var undef;
  62. var $element = $(element);
  63. var dialog = {
  64. open: false,
  65. returnValue: undef,
  66. show: function () {
  67. openDialog({modal: false});
  68. },
  69. showModal: function () {
  70. openDialog({modal: true});
  71. },
  72. close: closeDialog
  73. };
  74. function openDialog(settings) {
  75. settings = $.extend({}, drupalSettings.dialog, options, settings);
  76. // Trigger a global event to allow scripts to bind events to the dialog.
  77. $(window).trigger('dialog:beforecreate', [dialog, $element, settings]);
  78. $element.dialog(settings);
  79. dialog.open = true;
  80. $(window).trigger('dialog:aftercreate', [dialog, $element, settings]);
  81. }
  82. function closeDialog(value) {
  83. $(window).trigger('dialog:beforeclose', [dialog, $element]);
  84. $element.dialog('close');
  85. dialog.returnValue = value;
  86. dialog.open = false;
  87. $(window).trigger('dialog:afterclose', [dialog, $element]);
  88. }
  89. return dialog;
  90. };
  91. })(jQuery, Drupal, drupalSettings);
  92. ;
  93. /**
  94. * @file
  95. * Positioning extensions for dialogs.
  96. */
  97. /**
  98. * Triggers when content inside a dialog changes.
  99. *
  100. * @event dialogContentResize
  101. */
  102. (function ($, Drupal, drupalSettings, debounce, displace) {
  103. 'use strict';
  104. // autoResize option will turn off resizable and draggable.
  105. drupalSettings.dialog = $.extend({autoResize: true, maxHeight: '95%'}, drupalSettings.dialog);
  106. /**
  107. * Resets the current options for positioning.
  108. *
  109. * This is used as a window resize and scroll callback to reposition the
  110. * jQuery UI dialog. Although not a built-in jQuery UI option, this can
  111. * be disabled by setting autoResize: false in the options array when creating
  112. * a new {@link Drupal.dialog}.
  113. *
  114. * @function Drupal.dialog~resetSize
  115. *
  116. * @param {jQuery.Event} event
  117. * The event triggered.
  118. *
  119. * @fires event:dialogContentResize
  120. */
  121. function resetSize(event) {
  122. var positionOptions = ['width', 'height', 'minWidth', 'minHeight', 'maxHeight', 'maxWidth', 'position'];
  123. var adjustedOptions = {};
  124. var windowHeight = $(window).height();
  125. var option;
  126. var optionValue;
  127. var adjustedValue;
  128. for (var n = 0; n < positionOptions.length; n++) {
  129. option = positionOptions[n];
  130. optionValue = event.data.settings[option];
  131. if (optionValue) {
  132. // jQuery UI does not support percentages on heights, convert to pixels.
  133. if (typeof optionValue === 'string' && /%$/.test(optionValue) && /height/i.test(option)) {
  134. // Take offsets in account.
  135. windowHeight -= displace.offsets.top + displace.offsets.bottom;
  136. adjustedValue = parseInt(0.01 * parseInt(optionValue, 10) * windowHeight, 10);
  137. // Don't force the dialog to be bigger vertically than needed.
  138. if (option === 'height' && event.data.$element.parent().outerHeight() < adjustedValue) {
  139. adjustedValue = 'auto';
  140. }
  141. adjustedOptions[option] = adjustedValue;
  142. }
  143. }
  144. }
  145. // Offset the dialog center to be at the center of Drupal.displace.offsets.
  146. if (!event.data.settings.modal) {
  147. adjustedOptions = resetPosition(adjustedOptions);
  148. }
  149. event.data.$element
  150. .dialog('option', adjustedOptions)
  151. .trigger('dialogContentResize');
  152. }
  153. /**
  154. * Position the dialog's center at the center of displace.offsets boundaries.
  155. *
  156. * @function Drupal.dialog~resetPosition
  157. *
  158. * @param {object} options
  159. * Options object.
  160. *
  161. * @return {object}
  162. * Altered options object.
  163. */
  164. function resetPosition(options) {
  165. var offsets = displace.offsets;
  166. var left = offsets.left - offsets.right;
  167. var top = offsets.top - offsets.bottom;
  168. var leftString = (left > 0 ? '+' : '-') + Math.abs(Math.round(left / 2)) + 'px';
  169. var topString = (top > 0 ? '+' : '-') + Math.abs(Math.round(top / 2)) + 'px';
  170. options.position = {
  171. my: 'center' + (left !== 0 ? leftString : '') + ' center' + (top !== 0 ? topString : ''),
  172. of: window
  173. };
  174. return options;
  175. }
  176. $(window).on({
  177. 'dialog:aftercreate': function (event, dialog, $element, settings) {
  178. var autoResize = debounce(resetSize, 20);
  179. var eventData = {settings: settings, $element: $element};
  180. if (settings.autoResize === true || settings.autoResize === 'true') {
  181. $element
  182. .dialog('option', {resizable: false, draggable: false})
  183. .dialog('widget').css('position', 'fixed');
  184. $(window)
  185. .on('resize.dialogResize scroll.dialogResize', eventData, autoResize)
  186. .trigger('resize.dialogResize');
  187. $(document).on('drupalViewportOffsetChange.dialogResize', eventData, autoResize);
  188. }
  189. },
  190. 'dialog:beforeclose': function (event, dialog, $element) {
  191. $(window).off('.dialogResize');
  192. $(document).off('.dialogResize');
  193. }
  194. });
  195. })(jQuery, Drupal, drupalSettings, Drupal.debounce, Drupal.displace);
  196. ;
  197. /**
  198. * @file
  199. * Adds default classes to buttons for styling purposes.
  200. */
  201. (function ($) {
  202. 'use strict';
  203. $.widget('ui.dialog', $.ui.dialog, {
  204. options: {
  205. buttonClass: 'button',
  206. buttonPrimaryClass: 'button--primary'
  207. },
  208. _createButtons: function () {
  209. var opts = this.options;
  210. var primaryIndex;
  211. var $buttons;
  212. var index;
  213. var il = opts.buttons.length;
  214. for (index = 0; index < il; index++) {
  215. if (opts.buttons[index].primary && opts.buttons[index].primary === true) {
  216. primaryIndex = index;
  217. delete opts.buttons[index].primary;
  218. break;
  219. }
  220. }
  221. this._super();
  222. $buttons = this.uiButtonSet.children().addClass(opts.buttonClass);
  223. if (typeof primaryIndex !== 'undefined') {
  224. $buttons.eq(index).addClass(opts.buttonPrimaryClass);
  225. }
  226. }
  227. });
  228. })(jQuery);
  229. ;
  230. /**
  231. * @file
  232. * Extends the Drupal AJAX functionality to integrate the dialog API.
  233. */
  234. (function ($, Drupal) {
  235. 'use strict';
  236. /**
  237. * Initialize dialogs for Ajax purposes.
  238. *
  239. * @type {Drupal~behavior}
  240. *
  241. * @prop {Drupal~behaviorAttach} attach
  242. * Attaches the behaviors for dialog ajax functionality.
  243. */
  244. Drupal.behaviors.dialog = {
  245. attach: function (context, settings) {
  246. var $context = $(context);
  247. // Provide a known 'drupal-modal' DOM element for Drupal-based modal
  248. // dialogs. Non-modal dialogs are responsible for creating their own
  249. // elements, since there can be multiple non-modal dialogs at a time.
  250. if (!$('#drupal-modal').length) {
  251. // Add 'ui-front' jQuery UI class so jQuery UI widgets like autocomplete
  252. // sit on top of dialogs. For more information see
  253. // http://api.jqueryui.com/theming/stacking-elements/.
  254. $('<div id="drupal-modal" class="ui-front"/>').hide().appendTo('body');
  255. }
  256. // Special behaviors specific when attaching content within a dialog.
  257. // These behaviors usually fire after a validation error inside a dialog.
  258. var $dialog = $context.closest('.ui-dialog-content');
  259. if ($dialog.length) {
  260. // Remove and replace the dialog buttons with those from the new form.
  261. if ($dialog.dialog('option', 'drupalAutoButtons')) {
  262. // Trigger an event to detect/sync changes to buttons.
  263. $dialog.trigger('dialogButtonsChange');
  264. }
  265. // Force focus on the modal when the behavior is run.
  266. $dialog.dialog('widget').trigger('focus');
  267. }
  268. var originalClose = settings.dialog.close;
  269. // Overwrite the close method to remove the dialog on closing.
  270. settings.dialog.close = function (event) {
  271. originalClose.apply(settings.dialog, arguments);
  272. $(event.target).remove();
  273. };
  274. },
  275. /**
  276. * Scan a dialog for any primary buttons and move them to the button area.
  277. *
  278. * @param {jQuery} $dialog
  279. * An jQuery object containing the element that is the dialog target.
  280. *
  281. * @return {Array}
  282. * An array of buttons that need to be added to the button area.
  283. */
  284. prepareDialogButtons: function ($dialog) {
  285. var buttons = [];
  286. var $buttons = $dialog.find('.form-actions input[type=submit], .form-actions a.button');
  287. $buttons.each(function () {
  288. // Hidden form buttons need special attention. For browser consistency,
  289. // the button needs to be "visible" in order to have the enter key fire
  290. // the form submit event. So instead of a simple "hide" or
  291. // "display: none", we set its dimensions to zero.
  292. // See http://mattsnider.com/how-forms-submit-when-pressing-enter/
  293. var $originalButton = $(this).css({
  294. display: 'block',
  295. width: 0,
  296. height: 0,
  297. padding: 0,
  298. border: 0,
  299. overflow: 'hidden'
  300. });
  301. buttons.push({
  302. text: $originalButton.html() || $originalButton.attr('value'),
  303. class: $originalButton.attr('class'),
  304. click: function (e) {
  305. // If the original button is an anchor tag, triggering the "click"
  306. // event will not simulate a click. Use the click method instead.
  307. if ($originalButton.is('a')) {
  308. $originalButton[0].click();
  309. }
  310. else {
  311. $originalButton.trigger('mousedown').trigger('mouseup').trigger('click');
  312. e.preventDefault();
  313. }
  314. }
  315. });
  316. });
  317. return buttons;
  318. }
  319. };
  320. /**
  321. * Command to open a dialog.
  322. *
  323. * @param {Drupal.Ajax} ajax
  324. * The Drupal Ajax object.
  325. * @param {object} response
  326. * Object holding the server response.
  327. * @param {number} [status]
  328. * The HTTP status code.
  329. *
  330. * @return {bool|undefined}
  331. * Returns false if there was no selector property in the response object.
  332. */
  333. Drupal.AjaxCommands.prototype.openDialog = function (ajax, response, status) {
  334. if (!response.selector) {
  335. return false;
  336. }
  337. var $dialog = $(response.selector);
  338. if (!$dialog.length) {
  339. // Create the element if needed.
  340. $dialog = $('<div id="' + response.selector.replace(/^#/, '') + '" class="ui-front"/>').appendTo('body');
  341. }
  342. // Set up the wrapper, if there isn't one.
  343. if (!ajax.wrapper) {
  344. ajax.wrapper = $dialog.attr('id');
  345. }
  346. // Use the ajax.js insert command to populate the dialog contents.
  347. response.command = 'insert';
  348. response.method = 'html';
  349. ajax.commands.insert(ajax, response, status);
  350. // Move the buttons to the jQuery UI dialog buttons area.
  351. if (!response.dialogOptions.buttons) {
  352. response.dialogOptions.drupalAutoButtons = true;
  353. response.dialogOptions.buttons = Drupal.behaviors.dialog.prepareDialogButtons($dialog);
  354. }
  355. // Bind dialogButtonsChange.
  356. $dialog.on('dialogButtonsChange', function () {
  357. var buttons = Drupal.behaviors.dialog.prepareDialogButtons($dialog);
  358. $dialog.dialog('option', 'buttons', buttons);
  359. });
  360. // Open the dialog itself.
  361. response.dialogOptions = response.dialogOptions || {};
  362. var dialog = Drupal.dialog($dialog.get(0), response.dialogOptions);
  363. if (response.dialogOptions.modal) {
  364. dialog.showModal();
  365. }
  366. else {
  367. dialog.show();
  368. }
  369. // Add the standard Drupal class for buttons for style consistency.
  370. $dialog.parent().find('.ui-dialog-buttonset').addClass('form-actions');
  371. };
  372. /**
  373. * Command to close a dialog.
  374. *
  375. * If no selector is given, it defaults to trying to close the modal.
  376. *
  377. * @param {Drupal.Ajax} [ajax]
  378. * The ajax object.
  379. * @param {object} response
  380. * Object holding the server response.
  381. * @param {string} response.selector
  382. * The selector of the dialog.
  383. * @param {bool} response.persist
  384. * Whether to persist the dialog element or not.
  385. * @param {number} [status]
  386. * The HTTP status code.
  387. */
  388. Drupal.AjaxCommands.prototype.closeDialog = function (ajax, response, status) {
  389. var $dialog = $(response.selector);
  390. if ($dialog.length) {
  391. Drupal.dialog($dialog.get(0)).close();
  392. if (!response.persist) {
  393. $dialog.remove();
  394. }
  395. }
  396. // Unbind dialogButtonsChange.
  397. $dialog.off('dialogButtonsChange');
  398. };
  399. /**
  400. * Command to set a dialog property.
  401. *
  402. * JQuery UI specific way of setting dialog options.
  403. *
  404. * @param {Drupal.Ajax} [ajax]
  405. * The Drupal Ajax object.
  406. * @param {object} response
  407. * Object holding the server response.
  408. * @param {string} response.selector
  409. * Selector for the dialog element.
  410. * @param {string} response.optionsName
  411. * Name of a key to set.
  412. * @param {string} response.optionValue
  413. * Value to set.
  414. * @param {number} [status]
  415. * The HTTP status code.
  416. */
  417. Drupal.AjaxCommands.prototype.setDialogOption = function (ajax, response, status) {
  418. var $dialog = $(response.selector);
  419. if ($dialog.length) {
  420. $dialog.dialog('option', response.optionName, response.optionValue);
  421. }
  422. };
  423. /**
  424. * Binds a listener on dialog creation to handle the cancel link.
  425. *
  426. * @param {jQuery.Event} e
  427. * The event triggered.
  428. * @param {Drupal.dialog~dialogDefinition} dialog
  429. * The dialog instance.
  430. * @param {jQuery} $element
  431. * The jQuery collection of the dialog element.
  432. * @param {object} [settings]
  433. * Dialog settings.
  434. */
  435. $(window).on('dialog:aftercreate', function (e, dialog, $element, settings) {
  436. $element.on('click.dialog', '.dialog-cancel', function (e) {
  437. dialog.close('cancel');
  438. e.preventDefault();
  439. e.stopPropagation();
  440. });
  441. });
  442. /**
  443. * Removes all 'dialog' listeners.
  444. *
  445. * @param {jQuery.Event} e
  446. * The event triggered.
  447. * @param {Drupal.dialog~dialogDefinition} dialog
  448. * The dialog instance.
  449. * @param {jQuery} $element
  450. * jQuery collection of the dialog element.
  451. */
  452. $(window).on('dialog:beforeclose', function (e, dialog, $element) {
  453. $element.off('.dialog');
  454. });
  455. })(jQuery, Drupal);
  456. ;