API Docs for: 1.0.0
Show:

File: Resources/public/js/alloyeditor/plugins/embed.js

  1. /*
  2. * Copyright (C) eZ Systems AS. All rights reserved.
  3. * For full copyright and license information view LICENSE file distributed with this source code.
  4. */
  5. /* global CKEDITOR */
  6. YUI.add('ez-alloyeditor-plugin-embed', function (Y) {
  7. "use strict";
  8.  
  9. var IMAGE_TYPE_CLASS = 'ez-embed-type-image',
  10. DATA_ALIGNMENT_ATTR = 'ezalign';
  11.  
  12. if (CKEDITOR.plugins.get('ezembed')) {
  13. return;
  14. }
  15.  
  16. /**
  17. * CKEditor plugin to configure the widget plugin so that it recognizes the
  18. * `div[data-ezelement="embed"]` elements as widget.
  19. *
  20. * @class ezembed
  21. * @namespace CKEDITOR.plugins
  22. * @constructor
  23. */
  24. CKEDITOR.plugins.add('ezembed', {
  25. requires: 'widget,ezaddcontent',
  26.  
  27. init: function (editor) {
  28. editor.widgets.add('ezembed', {
  29. defaults: {
  30. href: "ezcontent://57",
  31. content: "home",
  32. view: "embed",
  33. },
  34. draggable: false,
  35. template: '<div data-ezelement="ezembed" data-href="{href}" data-ezview="{view}">{content}</div>',
  36. requiredContent: 'div',
  37.  
  38. upcast: function (element) {
  39. return (
  40. element.name === 'div' &&
  41. element.attributes['data-ezelement'] === 'ezembed'
  42. );
  43. },
  44.  
  45. /**
  46. * Insert an `ezembed` widget in the editor. It overrides the
  47. * default implementation to make sure that in the case where an
  48. * embed widget is focused, a new one is added after it.
  49. *
  50. * @method insert
  51. */
  52. insert: function () {
  53. var element = CKEDITOR.dom.element.createFromHtml(this.template.output(this.defaults)),
  54. wrapper = editor.widgets.wrapElement(element, this.name),
  55. temp = new CKEDITOR.dom.documentFragment(wrapper.getDocument()),
  56. instance;
  57.  
  58. temp.append(wrapper);
  59. editor.widgets.initOn(element, this.name);
  60. editor.eZ.appendElement(wrapper);
  61.  
  62. instance = editor.widgets.getByElement(wrapper);
  63. instance.ready = true;
  64. instance.fire('ready');
  65. instance.focus();
  66. },
  67.  
  68. /**
  69. * It's not possible to *edit* an embed widget in AlloyEditor,
  70. * so `edit` directly calls `insert` instead. This is needed
  71. * because by default, the CKEditor engine calls this method
  72. * when an embed widget has the focus and the `ezembed` command
  73. * is executed. In AlloyEditor, we want to insert a new widget,
  74. * not to `edit` the focused widget as the editing process is
  75. * provided by the style toolbar.
  76. *
  77. * @method edit
  78. */
  79. edit: function () {
  80. this.insert();
  81. },
  82.  
  83. init: function () {
  84. this.on('focus', this._fireEditorInteraction);
  85. this._syncAlignment();
  86. this._getEzConfigElement();
  87. this.setWidgetContent('');
  88. this._cancelEditEvents();
  89. },
  90.  
  91. /**
  92. * Cancels the widget events that trigger the `edit` event as
  93. * an embed widget can not be edited in a *CKEditor way*.
  94. *
  95. * @method _cancelEditEvents
  96. * @private
  97. */
  98. _cancelEditEvents: function () {
  99. var cancel = function (e) {
  100. e.cancel();
  101. };
  102.  
  103. this.on('doubleclick', cancel, null, null, 5);
  104. this.on('key', cancel, null, null, 5);
  105. },
  106.  
  107. /**
  108. * Initializes the alignment on the widget wrapper if the widget
  109. * is aligned.
  110. *
  111. * @method _syncAlignment
  112. * @protected
  113. */
  114. _syncAlignment: function () {
  115. var align = this.element.data(DATA_ALIGNMENT_ATTR);
  116.  
  117. if ( align ) {
  118. this._setAlignment(align);
  119. } else {
  120. this._unsetAlignment();
  121. }
  122. },
  123.  
  124. /**
  125. * Sets the alignment of the embed widget to `type`. The
  126. * alignment is set by adding the `data-ezalign` attribute
  127. * on the widget wrapper and the widget element.
  128. *
  129. * @method _setAlignment
  130. * @protected
  131. * @param {String} type
  132. */
  133. _setAlignment: function (type) {
  134. this.wrapper.data(DATA_ALIGNMENT_ATTR, type);
  135. this.element.data(DATA_ALIGNMENT_ATTR, type);
  136. },
  137.  
  138. /**
  139. * Sets the alignment of the embed widget to `type` and fires
  140. * the corresponding `editorInteraction` event.
  141. *
  142. * @method setAlignment
  143. * @param {String} type
  144. */
  145. setAlignment: function (type, fireEvent) {
  146. this._setAlignment(type);
  147. this._fireEditorInteraction('setAlignment' + type);
  148. },
  149.  
  150. /**
  151. * Removes the alignment of the widget.
  152. *
  153. * @method _unsetAlignment
  154. * @protected
  155. */
  156. _unsetAlignment: function () {
  157. this.wrapper.data(DATA_ALIGNMENT_ATTR, false);
  158. this.element.data(DATA_ALIGNMENT_ATTR, false);
  159. },
  160.  
  161. /**
  162. * Removes the alignment of the widget and fires the
  163. * corresponding `editorInteraction` event.
  164. *
  165. * @method unsetAlignment
  166. */
  167. unsetAlignment: function () {
  168. this._unsetAlignment();
  169. this._fireEditorInteraction('unsetAlignment');
  170. },
  171.  
  172. /**
  173. * Checks whether the embed is aligned with `type` alignment.
  174. *
  175. * @method isAligned
  176. * @param {String} type
  177. * @return {Boolean}
  178. */
  179. isAligned: function (type) {
  180. return (this.wrapper.data(DATA_ALIGNMENT_ATTR) === type);
  181. },
  182.  
  183. /**
  184. * Set the embed as an embed representing an image
  185. *
  186. * @method setImageType
  187. * @return {CKEDITOR.plugins.widget}
  188. */
  189. setImageType: function () {
  190. this.element.addClass(IMAGE_TYPE_CLASS);
  191. return this;
  192. },
  193.  
  194. /**
  195. * Check whether the embed widget represents an image or not.
  196. *
  197. * @method isImage
  198. * @return {Boolean}
  199. */
  200. isImage: function () {
  201. return this.element.hasClass(IMAGE_TYPE_CLASS);
  202. },
  203.  
  204. /**
  205. * Sets the `href` of the embed is URI to the embed content or
  206. * location. (ezcontent://32 for instance).
  207. *
  208. * @method setHref
  209. * @param {String} href
  210. * @return {CKEDITOR.plugins.widget}
  211. */
  212. setHref: function (href) {
  213. this.element.data('href', href);
  214. return this;
  215. },
  216.  
  217. /**
  218. * Returns the `href`of the embed.
  219. *
  220. * @method getHref
  221. * @return {String}
  222. */
  223. getHref: function () {
  224. return this.element.data('href');
  225. },
  226.  
  227. /**
  228. * Sets the widget content. It makes sure the config element is
  229. * not overwritten.
  230. *
  231. * @method setWidgetContent
  232. * @param {String|CKEDITOR.dom.node} content
  233. * @return {CKEDITOR.plugins.widget}
  234. */
  235. setWidgetContent: function (content) {
  236. var element = this.element.getFirst(), next;
  237.  
  238. while ( element ) {
  239. next = element.getNext();
  240. if ( !element.data || !element.data('ezelement') ) {
  241. element.remove();
  242. }
  243. element = next;
  244. }
  245. if ( content instanceof CKEDITOR.dom.node ) {
  246. this.element.append(content);
  247. } else {
  248. this.element.appendText(content);
  249. }
  250. return this;
  251. },
  252.  
  253. /**
  254. * Moves the widget after the given element. It also fires the
  255. * `editorInteraction` event so that the UI can respond to that
  256. * change.
  257. *
  258. * @method moveAfter
  259. * @param {CKEDITOR.dom.element} element
  260. */
  261. moveAfter: function (element) {
  262. this.wrapper.insertAfter(element);
  263. this._fireEditorInteraction('moveAfter');
  264. },
  265.  
  266. /**
  267. * Moves the widget before the given element. It also fires the
  268. * `editorInteraction` event so that the UI can respond to that
  269. * change.
  270. *
  271. * @method moveAfter
  272. * @param {CKEDITOR.dom.element} element
  273. */
  274. moveBefore: function (element) {
  275. this.wrapper.insertBefore(element);
  276. this._fireEditorInteraction('moveBefore');
  277. },
  278.  
  279. /**
  280. * Sets a config value under the `key` for the embed.
  281. *
  282. * @method setConfig
  283. * @param {String} key
  284. * @param {String} value
  285. * @return {CKEDITOR.plugins.widget}
  286. */
  287. setConfig: function (key, value) {
  288. var valueElement = this._getValueElement(key);
  289.  
  290. if ( !valueElement ) {
  291. valueElement = new CKEDITOR.dom.element('span');
  292. valueElement.data('ezelement', 'ezvalue');
  293. valueElement.data('ezvalue-key', key);
  294. this._getEzConfigElement().append(valueElement);
  295. }
  296. valueElement.setText(value);
  297. return this;
  298. },
  299.  
  300. /**
  301. * Returns the config value for the `key` or undefined if the
  302. * config key is not found.
  303. *
  304. * @method getConfig
  305. * @return {String}
  306. */
  307. getConfig: function (key) {
  308. var valueElement = this._getValueElement(key);
  309.  
  310. return valueElement ? valueElement.getText() : undefined;
  311. },
  312.  
  313. /**
  314. * Returns the Element holding the config under `key`
  315. *
  316. * @method _getValueElement
  317. * @param {String} key
  318. * @return {CKEDITOR.dom.element}
  319. */
  320. _getValueElement: function (key) {
  321. return this.element.findOne('[data-ezelement="ezvalue"][data-ezvalue-key="' + key + '"]');
  322. },
  323.  
  324. /**
  325. * Returns the element used as a container the config values. if
  326. * it does not exist, it is created.
  327. *
  328. * @method _getEzConfigElement
  329. * @private
  330. * @return {CKEDITOR.dom.element}
  331. */
  332. _getEzConfigElement: function () {
  333. var config = this.element.findOne('[data-ezelement="ezconfig"]');
  334.  
  335. if ( !config ) {
  336. config = new CKEDITOR.dom.element('span');
  337. config.data('ezelement', 'ezconfig');
  338. this.element.append(config, true);
  339. }
  340. return config;
  341. },
  342.  
  343. /**
  344. * Fires the editorInteraction event so that AlloyEditor editor
  345. * UI remains visible and is updated. This method also computes
  346. * `selectionData.region` and the `pageX` and `pageY` properties
  347. * so that the add toolbar is correctly positioned on the
  348. * widget.
  349. *
  350. * @method _fireEditorInteraction
  351. * @protected
  352. * @param {Object|String} evt this initial event info object or
  353. * the event name for which the `editorInteraction` is fired.
  354. */
  355. _fireEditorInteraction: function (evt) {
  356. var wrapperRegion = this._getWrapperRegion(),
  357. name = evt.name || evt,
  358. e = {
  359. editor: editor,
  360. target: this.element.$,
  361. name: "widget" + name,
  362. pageX: wrapperRegion.left,
  363. pageY: wrapperRegion.top + wrapperRegion.height,
  364. };
  365.  
  366. editor.focus();
  367. this.focus();
  368.  
  369. editor.fire('editorInteraction', {
  370. nativeEvent: e,
  371. selectionData: {
  372. element: this.element,
  373. region: wrapperRegion,
  374. },
  375. });
  376. },
  377.  
  378. /**
  379. * Returns the wrapper element region.
  380. *
  381. * @method _getWrapperRegion
  382. * @private
  383. * @return {Object}
  384. */
  385. _getWrapperRegion: function () {
  386. var scroll = this.wrapper.getWindow().getScrollPosition(),
  387. region = this.wrapper.getClientRect();
  388.  
  389. region.top += scroll.y;
  390. region.bottom += scroll.y;
  391. region.left += scroll.x;
  392. region.right += scroll.x;
  393. region.direction = CKEDITOR.SELECTION_TOP_TO_BOTTOM;
  394. return region;
  395. },
  396. });
  397. },
  398. });
  399. });
  400.