Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 
 
 

715 строки
21 KiB

  1. /**
  2. * Copyright © Magento, Inc. All rights reserved.
  3. * See COPYING.txt for license details.
  4. */
  5. /* global setLocation, Base64, updateElementAtCursor, varienGlobalEvents */
  6. /* eslint-disable strict */
  7. define([
  8. 'jquery',
  9. 'wysiwygAdapter',
  10. 'Magento_Ui/js/modal/alert',
  11. 'jquery/ui',
  12. 'mage/translate',
  13. 'mage/mage',
  14. 'mage/validation',
  15. 'mage/adminhtml/events',
  16. 'prototype',
  17. 'Magento_Ui/js/modal/modal'
  18. ], function (jQuery, wysiwyg, alert) {
  19. var widgetTools = {
  20. /**
  21. * Sets the widget to be active and is the scope of the slide out if the value is set
  22. */
  23. activeSelectedNode: null,
  24. editMode: false,
  25. cursorLocation: 0,
  26. /**
  27. * Set active selected node.
  28. *
  29. * @param {Object} activeSelectedNode
  30. */
  31. setActiveSelectedNode: function (activeSelectedNode) {
  32. this.activeSelectedNode = activeSelectedNode;
  33. },
  34. /**
  35. * Get active selected node.
  36. *
  37. * @returns {null}
  38. */
  39. getActiveSelectedNode: function () {
  40. return this.activeSelectedNode;
  41. },
  42. /**
  43. *
  44. * @param {Boolean} editMode
  45. */
  46. setEditMode: function (editMode) {
  47. this.editMode = editMode;
  48. },
  49. /**
  50. * @param {*} id
  51. * @param {*} html
  52. * @return {String}
  53. */
  54. getDivHtml: function (id, html) {
  55. if (!html) {
  56. html = '';
  57. }
  58. return '<div id="' + id + '">' + html + '</div>';
  59. },
  60. /**
  61. * @param {Object} transport
  62. */
  63. onAjaxSuccess: function (transport) {
  64. var response;
  65. if (transport.responseText.isJSON()) {
  66. response = transport.responseText.evalJSON();
  67. if (response.error) {
  68. throw response;
  69. } else if (response.ajaxExpired && response.ajaxRedirect) {
  70. setLocation(response.ajaxRedirect);
  71. }
  72. }
  73. },
  74. dialogOpened: false,
  75. /**
  76. * @return {Number}
  77. */
  78. getMaxZIndex: function () {
  79. var max = 0,
  80. cn = document.body.childNodes,
  81. i, el, zIndex;
  82. for (i = 0; i < cn.length; i++) {
  83. el = cn[i];
  84. zIndex = el.nodeType == 1 ? parseInt(el.style.zIndex, 10) || 0 : 0; //eslint-disable-line eqeqeq
  85. if (zIndex < 10000) {
  86. max = Math.max(max, zIndex);
  87. }
  88. }
  89. return max + 10;
  90. },
  91. /**
  92. * @param {String} widgetUrl
  93. */
  94. openDialog: function (widgetUrl) {
  95. var oThis = this,
  96. title = 'Insert Widget',
  97. mode = 'new',
  98. dialog;
  99. if (this.editMode) {
  100. title = 'Edit Widget';
  101. mode = 'edit';
  102. }
  103. if (this.dialogOpened) {
  104. return;
  105. }
  106. this.dialogWindow = jQuery('<div/>').modal({
  107. title: jQuery.mage.__(title),
  108. type: 'slide',
  109. buttons: [],
  110. /**
  111. * Opened.
  112. */
  113. opened: function () {
  114. dialog = jQuery(this).addClass('loading magento-message');
  115. widgetUrl += 'mode/' + mode;
  116. new Ajax.Updater($(this), widgetUrl, {
  117. evalScripts: true,
  118. /**
  119. * On complete.
  120. */
  121. onComplete: function () {
  122. dialog.removeClass('loading');
  123. }
  124. });
  125. },
  126. /**
  127. * @param {jQuery.Event} e
  128. * @param {Object} modal
  129. */
  130. closed: function (e, modal) {
  131. modal.modal.remove();
  132. oThis.dialogOpened = false;
  133. }
  134. });
  135. this.dialogOpened = true;
  136. this.dialogWindow.modal('openModal');
  137. }
  138. },
  139. WysiwygWidget = {};
  140. WysiwygWidget.Widget = Class.create();
  141. WysiwygWidget.Widget.prototype = {
  142. /**
  143. * @param {HTMLElement} formEl
  144. * @param {HTMLElement} widgetEl
  145. * @param {*} widgetOptionsEl
  146. * @param {*} optionsSourceUrl
  147. * @param {*} widgetTargetId
  148. */
  149. initialize: function (formEl, widgetEl, widgetOptionsEl, optionsSourceUrl, widgetTargetId) {
  150. $(formEl).insert({
  151. bottom: widgetTools.getDivHtml(widgetOptionsEl)
  152. });
  153. this.formEl = formEl;
  154. this.widgetEl = $(widgetEl);
  155. this.widgetOptionsEl = $(widgetOptionsEl);
  156. this.optionsUrl = optionsSourceUrl;
  157. this.optionValues = new Hash({});
  158. this.widgetTargetId = widgetTargetId;
  159. if (typeof wysiwyg != 'undefined' && wysiwyg.activeEditor()) { //eslint-disable-line eqeqeq
  160. this.bMark = wysiwyg.activeEditor().selection.getBookmark();
  161. }
  162. // disable -- Please Select -- option from being re-selected
  163. this.widgetEl.querySelector('option').setAttribute('disabled', 'disabled');
  164. Event.observe(this.widgetEl, 'change', this.loadOptions.bind(this));
  165. this.initOptionValues();
  166. },
  167. /**
  168. * @return {String}
  169. */
  170. getOptionsContainerId: function () {
  171. return this.widgetOptionsEl.id + '_' + this.widgetEl.value.gsub(/\//, '_');
  172. },
  173. /**
  174. * @param {*} containerId
  175. */
  176. switchOptionsContainer: function (containerId) {
  177. $$('#' + this.widgetOptionsEl.id + ' div[id^=' + this.widgetOptionsEl.id + ']').each(function (e) {
  178. this.disableOptionsContainer(e.id);
  179. }.bind(this));
  180. if (containerId != undefined) { //eslint-disable-line eqeqeq
  181. this.enableOptionsContainer(containerId);
  182. }
  183. this._showWidgetDescription();
  184. },
  185. /**
  186. * @param {*} containerId
  187. */
  188. enableOptionsContainer: function (containerId) {
  189. var container = $(containerId);
  190. container.select('.widget-option').each(function (e) {
  191. e.removeClassName('skip-submit');
  192. if (e.hasClassName('obligatory')) {
  193. e.removeClassName('obligatory');
  194. e.addClassName('required-entry');
  195. }
  196. });
  197. container.removeClassName('no-display');
  198. },
  199. /**
  200. * @param {*} containerId
  201. */
  202. disableOptionsContainer: function (containerId) {
  203. var container = $(containerId);
  204. if (container.hasClassName('no-display')) {
  205. return;
  206. }
  207. container.select('.widget-option').each(function (e) {
  208. // Avoid submitting fields of unactive container
  209. if (!e.hasClassName('skip-submit')) {
  210. e.addClassName('skip-submit');
  211. }
  212. // Form validation workaround for unactive container
  213. if (e.hasClassName('required-entry')) {
  214. e.removeClassName('required-entry');
  215. e.addClassName('obligatory');
  216. }
  217. });
  218. container.addClassName('no-display');
  219. },
  220. /**
  221. * Assign widget options values when existing widget selected in WYSIWYG.
  222. *
  223. * @return {Boolean}
  224. */
  225. initOptionValues: function () {
  226. var e, widgetCode;
  227. if (!this.wysiwygExists()) {
  228. return false;
  229. }
  230. e = this.getWysiwygNode();
  231. if (e.localName === 'span') {
  232. e = e.firstElementChild;
  233. }
  234. if (e != undefined && e.id) { //eslint-disable-line eqeqeq
  235. // attempt to Base64-decode id on selected node; exception is thrown if it is in fact not a widget node
  236. try {
  237. widgetCode = Base64.idDecode(e.id);
  238. } catch (ex) {
  239. return false;
  240. }
  241. if (widgetCode.indexOf('{{widget') !== -1) {
  242. this.optionValues = new Hash({});
  243. widgetCode.gsub(/([a-z0-9\_]+)\s*\=\s*[\"]{1}([^\"]+)[\"]{1}/i, function (match) {
  244. if (match[1] == 'type') { //eslint-disable-line eqeqeq
  245. this.widgetEl.value = match[2];
  246. } else {
  247. this.optionValues.set(match[1], match[2]);
  248. }
  249. }.bind(this));
  250. this.loadOptions();
  251. }
  252. }
  253. },
  254. /**
  255. * Load options.
  256. */
  257. loadOptions: function () {
  258. var optionsContainerId,
  259. params,
  260. msg,
  261. msgTmpl,
  262. $wrapper,
  263. typeName = this.optionValues.get('type_name');
  264. if (!this.widgetEl.value) {
  265. if (typeName) {
  266. msgTmpl = jQuery.mage.__('The widget %1 is no longer available. Select a different widget.');
  267. msg = jQuery.mage.__(msgTmpl).replace('%1', typeName);
  268. jQuery('body').notification('clear').notification('add', {
  269. error: true,
  270. message: msg,
  271. /**
  272. * @param {String} message
  273. */
  274. insertMethod: function (message) {
  275. $wrapper = jQuery('<div/>').html(message);
  276. $wrapper.insertAfter('.modal-slide .page-main-actions');
  277. }
  278. });
  279. }
  280. this.switchOptionsContainer();
  281. return;
  282. }
  283. optionsContainerId = this.getOptionsContainerId();
  284. if ($(optionsContainerId) != undefined) { //eslint-disable-line eqeqeq
  285. this.switchOptionsContainer(optionsContainerId);
  286. return;
  287. }
  288. this._showWidgetDescription();
  289. params = {
  290. 'widget_type': this.widgetEl.value,
  291. values: this.optionValues
  292. };
  293. new Ajax.Request(this.optionsUrl, {
  294. parameters: {
  295. widget: Object.toJSON(params)
  296. },
  297. /**
  298. * On success.
  299. */
  300. onSuccess: function (transport) {
  301. try {
  302. widgetTools.onAjaxSuccess(transport);
  303. this.switchOptionsContainer();
  304. if ($(optionsContainerId) == undefined) { //eslint-disable-line eqeqeq
  305. this.widgetOptionsEl.insert({
  306. bottom: widgetTools.getDivHtml(optionsContainerId, transport.responseText)
  307. });
  308. } else {
  309. this.switchOptionsContainer(optionsContainerId);
  310. }
  311. } catch (e) {
  312. alert({
  313. content: e.message
  314. });
  315. }
  316. }.bind(this)
  317. });
  318. },
  319. /**
  320. * @private
  321. */
  322. _showWidgetDescription: function () {
  323. var noteCnt = this.widgetEl.next().down('small'),
  324. descrCnt = $('widget-description-' + this.widgetEl.selectedIndex),
  325. description;
  326. if (noteCnt != undefined) { //eslint-disable-line eqeqeq
  327. description = descrCnt != undefined ? descrCnt.innerHTML : ''; //eslint-disable-line eqeqeq
  328. noteCnt.update(description);
  329. }
  330. },
  331. /**
  332. * Validate field.
  333. */
  334. validateField: function () {
  335. jQuery(this.widgetEl).valid();
  336. jQuery('#insert_button').removeClass('disabled');
  337. },
  338. /**
  339. * Closes the modal
  340. */
  341. closeModal: function () {
  342. widgetTools.dialogWindow.modal('closeModal');
  343. },
  344. /* eslint-disable max-depth*/
  345. /**
  346. * Insert widget.
  347. */
  348. insertWidget: function () {
  349. var validationResult,
  350. $form = jQuery('#' + this.formEl),
  351. formElements,
  352. i,
  353. params,
  354. editor,
  355. activeNode;
  356. // remove cached validator instance, which caches elements to validate
  357. jQuery.data($form[0], 'validator', null);
  358. $form.validate({
  359. /**
  360. * Ignores elements with .skip-submit, .no-display ancestor elements
  361. */
  362. ignore: function () {
  363. return jQuery(this).closest('.skip-submit, .no-display').length;
  364. },
  365. errorClass: 'mage-error'
  366. });
  367. validationResult = $form.valid();
  368. if (validationResult) {
  369. formElements = [];
  370. i = 0;
  371. Form.getElements($(this.formEl)).each(function (e) {
  372. if (jQuery(e).closest('.skip-submit, .no-display').length === 0) {
  373. formElements[i] = e;
  374. i++;
  375. }
  376. });
  377. // Add as_is flag to parameters if wysiwyg editor doesn't exist
  378. params = Form.serializeElements(formElements);
  379. if (!this.wysiwygExists()) {
  380. params += '&as_is=1';
  381. }
  382. new Ajax.Request($(this.formEl).action, {
  383. parameters: params,
  384. onComplete: function (transport) {
  385. try {
  386. editor = wysiwyg.get(this.widgetTargetId);
  387. widgetTools.onAjaxSuccess(transport);
  388. widgetTools.dialogWindow.modal('closeModal');
  389. if (editor) {
  390. editor.focus();
  391. activeNode = widgetTools.getActiveSelectedNode();
  392. if (activeNode) {
  393. editor.selection.select(activeNode);
  394. editor.selection.setContent(transport.responseText);
  395. editor.fire('Change');
  396. } else if (this.bMark) {
  397. editor.selection.moveToBookmark(this.bMark);
  398. }
  399. }
  400. if (!activeNode) {
  401. this.updateContent(transport.responseText);
  402. }
  403. } catch (e) {
  404. alert({
  405. content: e.message
  406. });
  407. }
  408. }.bind(this)
  409. });
  410. }
  411. },
  412. /**
  413. * @param {Object} content
  414. */
  415. updateContent: function (content) {
  416. var textarea;
  417. if (this.wysiwygExists()) {
  418. wysiwyg.insertContent(content, false);
  419. } else {
  420. textarea = document.getElementById(this.widgetTargetId);
  421. updateElementAtCursor(textarea, content);
  422. varienGlobalEvents.fireEvent('tinymceChange');
  423. jQuery(textarea).trigger('change');
  424. }
  425. },
  426. /**
  427. * @return {Boolean}
  428. */
  429. wysiwygExists: function () {
  430. return typeof wysiwyg != 'undefined' && wysiwyg.get(this.widgetTargetId);
  431. },
  432. /**
  433. * @return {null|wysiwyg.Editor|*}
  434. */
  435. getWysiwyg: function () {
  436. return wysiwyg.get(this.widgetTargetId);
  437. },
  438. /**
  439. * @return {*|Element}
  440. */
  441. getWysiwygNode: function () {
  442. return widgetTools.getActiveSelectedNode() || wysiwyg.activeEditor().selection.getNode();
  443. }
  444. };
  445. WysiwygWidget.chooser = Class.create();
  446. WysiwygWidget.chooser.prototype = {
  447. // HTML element A, on which click event fired when choose a selection
  448. chooserId: null,
  449. // Source URL for Ajax requests
  450. chooserUrl: null,
  451. // Chooser config
  452. config: null,
  453. // Chooser dialog window
  454. dialogWindow: null,
  455. // Chooser content for dialog window
  456. dialogContent: null,
  457. overlayShowEffectOptions: null,
  458. overlayHideEffectOptions: null,
  459. /**
  460. * @param {*} chooserId
  461. * @param {*} chooserUrl
  462. * @param {*} config
  463. */
  464. initialize: function (chooserId, chooserUrl, config) {
  465. this.chooserId = chooserId;
  466. this.chooserUrl = chooserUrl;
  467. this.config = config;
  468. },
  469. /**
  470. * @return {String}
  471. */
  472. getResponseContainerId: function () {
  473. return 'responseCnt' + this.chooserId;
  474. },
  475. /**
  476. * @return {jQuery|*|HTMLElement}
  477. */
  478. getChooserControl: function () {
  479. return $(this.chooserId + 'control');
  480. },
  481. /**
  482. * @return {jQuery|*|HTMLElement}
  483. */
  484. getElement: function () {
  485. return $(this.chooserId + 'value');
  486. },
  487. /**
  488. * @return {jQuery|*|HTMLElement}
  489. */
  490. getElementLabel: function () {
  491. return $(this.chooserId + 'label');
  492. },
  493. /**
  494. * Open.
  495. */
  496. open: function () {
  497. $(this.getResponseContainerId()).show();
  498. },
  499. /**
  500. * Close.
  501. */
  502. close: function () {
  503. $(this.getResponseContainerId()).hide();
  504. this.closeDialogWindow();
  505. },
  506. /**
  507. * Choose.
  508. */
  509. choose: function () {
  510. // Open dialog window with previously loaded dialog content
  511. var responseContainerId;
  512. if (this.dialogContent) {
  513. this.openDialogWindow(this.dialogContent);
  514. return;
  515. }
  516. // Show or hide chooser content if it was already loaded
  517. responseContainerId = this.getResponseContainerId();
  518. // Otherwise load content from server
  519. new Ajax.Request(this.chooserUrl, {
  520. parameters: {
  521. 'element_value': this.getElementValue(),
  522. 'element_label': this.getElementLabelText()
  523. },
  524. /**
  525. * On success.
  526. */
  527. onSuccess: function (transport) {
  528. try {
  529. widgetTools.onAjaxSuccess(transport);
  530. this.dialogContent = widgetTools.getDivHtml(responseContainerId, transport.responseText);
  531. this.openDialogWindow(this.dialogContent);
  532. } catch (e) {
  533. alert({
  534. content: e.message
  535. });
  536. }
  537. }.bind(this)
  538. });
  539. },
  540. /**
  541. * Open dialog winodw.
  542. *
  543. * @param {*} content
  544. */
  545. openDialogWindow: function (content) {
  546. this.dialogWindow = jQuery('<div/>').modal({
  547. title: this.config.buttons.open,
  548. type: 'slide',
  549. buttons: [],
  550. /**
  551. * Opened.
  552. */
  553. opened: function () {
  554. jQuery(this).addClass('magento-message');
  555. },
  556. /**
  557. * @param {jQuery.Event} e
  558. * @param {Object} modal
  559. */
  560. closed: function (e, modal) {
  561. modal.modal.remove();
  562. this.dialogWindow = null;
  563. }
  564. });
  565. this.dialogWindow.modal('openModal').append(content);
  566. },
  567. /**
  568. * Close dialog window.
  569. */
  570. closeDialogWindow: function () {
  571. this.dialogWindow.modal('closeModal').remove();
  572. },
  573. /**
  574. * @return {*|Number}
  575. */
  576. getElementValue: function () {
  577. return this.getElement().value;
  578. },
  579. /**
  580. * @return {String}
  581. */
  582. getElementLabelText: function () {
  583. return this.getElementLabel().innerHTML;
  584. },
  585. /**
  586. * @param {*} value
  587. */
  588. setElementValue: function (value) {
  589. this.getElement().value = value;
  590. },
  591. /**
  592. * @param {*} value
  593. */
  594. setElementLabel: function (value) {
  595. this.getElementLabel().innerHTML = value;
  596. }
  597. };
  598. window.WysiwygWidget = WysiwygWidget;
  599. window.widgetTools = widgetTools;
  600. });