TextEditor.js 76 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470
  1. 'use strict';
  2. /**
  3. * Licensed Materials - Property of IBM
  4. * IBM Cognos Products: BI Dashboard
  5. * (C) Copyright IBM Corp. 2017, 2021
  6. * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  7. */
  8. define(['underscore', '../../lib/@waca/core-client/js/core-client/utils/BrowserUtils', '../../lib/@waca/core-client/js/core-client/ui/core/Class', '../../lib/@waca/core-client/js/core-client/utils/Utils', '../../lib/@waca/textwidget/dist/summernote', '../../lib/@waca/dashboard-common/dist/utils/HtmlXSSUtils', '../widgets/staticwidget/text/ContentEditableSelectionManager', '../../lib/@waca/dashboard-common/dist/ui/dialogs/TextToolbarCompactDialog', '../../lib/@waca/dashboard-common/dist/ui/AuthoringToolbar', '../../app/nls/StringResources', '../widgets/PropertiesUtil', 'jquery', './TextFitUtil', '../../lib/@waca/textwidget/src/js/base/core/agent'], function (_, BrowserUtils, Class, Utils, Summernote, HtmlCleanser, ContentEditableSelectionManager, TextToolbar, Toolbar, resources, PropertiesUtil, $, TextFitUtil, agent) {
  9. var F12 = 123;
  10. var ENTER_KEY = 13;
  11. var WHITELISTED_ELEMENTS = ['<div>', '<p>', '<span>', '<b>', '<u>', '<i>', '<br>', '<font>', '<strong>', '<em>', '<ul>', '<ol>', '<li>'];
  12. // Summernote attributes that are used in Properties Pane
  13. var WHITELISTED_PROPERTIES = ['color', 'font-bold', 'font-family', 'font-italic', 'font-size', 'font-strikethrough', 'font-subscript', 'font-superscript', 'font-underline', 'insertOrderedList', 'insertUnorderedList', 'text-align', 'text-format'];
  14. var cleanContentElements = function cleanContentElements(content) {
  15. return HtmlCleanser.cleanseContentElements(content, WHITELISTED_ELEMENTS, true);
  16. };
  17. var getTextHTML = function getTextHTML(options) {
  18. var initialPlaceholder = options.text ? '<span>' + _.escape(options.text) + '</span>' : '<br>';
  19. return '<span class="textFitted ' + options.style + 'Color ' + options.style + 'FontSize ' + options.style + 'FontFamily"><p>' + initialPlaceholder + '</p></span>';
  20. };
  21. var TextEditor = Class.extend({
  22. SUMMERNOTE_CLASS: '.summernote',
  23. EDITING_AREA: '.note-editable',
  24. TEXT_FITTED: '.textFitted',
  25. savedSel: null,
  26. isResponsive: false,
  27. autoSaveInterval: 2000,
  28. isBold: false,
  29. /**
  30. * Initialize Text editor with the eleemnt where you wanna have text editing capabilities, and a reference to the widget.
  31. *
  32. * @param {Jquery element} options.node The element where we want to initialize summernote
  33. * @param {Jquery element} options.container Element that contains the node.
  34. * @param {Jquery element} options.toolbarNode Element that the toolbar will use to place itself
  35. * @param {Object} options.fonts Object to override the default font properties
  36. * @param {Object} options.fontSizes Object to override the default font sizes
  37. * @param {Object} options.widget A reference to the widget where we want text editing capabilities.
  38. */
  39. init: function init(options) {
  40. this.node = options.node;
  41. this.isResponsive = options.isResponsive;
  42. // TODO: The initSummernote will update the text content and cleanse it again. Should be consolidated with above code in one place.
  43. this.initSummernote();
  44. this.getEditingArea().attr('contenteditable', false);
  45. this.container = options.container;
  46. this.widget = options.widget;
  47. this.colorsService = this.widget.getDashboardApi().getFeature('Colors');
  48. var customFonts = this.widget.getDashboardApi().getFeature('DashboardSettings').get('fonts');
  49. var fonts = Array.isArray(customFonts) ? customFonts.concat(TextEditor.FONTFAMILY_CHOICES) : TextEditor.FONTFAMILY_CHOICES;
  50. this._toolbarDock = this.widget.getDashboardApi().getFeature('ToolbarDock');
  51. this._contentActions = this.widget.getDashboardApi().getFeature('ContentActions');
  52. this._dashboardState = this.widget.getDashboardApi().getFeature('DashboardState');
  53. this._icons = this.widget.getDashboardApi().getFeature('Icons');
  54. this.toolbarNode = options.toolbarNode;
  55. //TODO: options is kept for backward compatibility; it may be removed eventually
  56. this._fontFamilyChoices = options.fonts || fonts;
  57. this._fontSizeChoices = options.fontSizes || TextEditor.FONTSIZE_CHOICES;
  58. this.shouldResizeViz = options.shouldResizeViz;
  59. this.supportsLists = options.supportsLists;
  60. this.numberOfLines = 1;
  61. this.toolbar = new Toolbar({
  62. container: $('body'),
  63. placement: 'top',
  64. noFocus: true
  65. });
  66. var supportsTextWrap = false;
  67. this._ext = options.extension || {};
  68. if (this._ext.isTextWrapOn) {
  69. supportsTextWrap = true;
  70. }
  71. this.textToolbar = new TextToolbar({
  72. onStateChange: this.onToolbarStateChange.bind(this),
  73. textNode: this.getEditingArea(),
  74. properties: this.getToolbarProperties(),
  75. initialState: options.initialState,
  76. supportsLists: options.supportsLists,
  77. supportsTextWrap: supportsTextWrap
  78. });
  79. this.fillText();
  80. var onInit = this._ext.onInit;
  81. onInit && onInit(this);
  82. // Save the content so that we can check if the content has changed.
  83. this.lastSavedData = this.getContent();
  84. this.toolbar.on('toolbar:hide', this.hideTextToolbar, this);
  85. this.widget.eventRouter.on('textToolbar:hide', this.hideTextToolbar, this);
  86. this.widget.eventRouter.on('widget:hideToolbar', this.hideTextToolbar, this);
  87. },
  88. /**
  89. * Initialize summernote in the node given
  90. */
  91. initSummernote: function initSummernote() {
  92. var $summerNote = this.node.children(this.SUMMERNOTE_CLASS);
  93. var preDefinedHtml;
  94. if (!$summerNote.length) {
  95. if (this.node.children().length) {
  96. preDefinedHtml = this.node.html();
  97. this.node.children().remove();
  98. }
  99. $summerNote = this.node.append('<div class="summernote"></div>').children('.summernote');
  100. }
  101. var isAlreadyInitialized = !!$summerNote.data('summernote');
  102. if (!isAlreadyInitialized) {
  103. preDefinedHtml = preDefinedHtml || this.getEditingArea().remove().html();
  104. this.node.find('.note-editor').remove();
  105. $summerNote.summernote({
  106. airMode: true,
  107. popover: {}, //disable the popover
  108. disableDragAndDrop: true,
  109. lineWrapping: true,
  110. keyMap: this._getSummernoteShortcuts()
  111. });
  112. if (preDefinedHtml && HtmlCleanser.isValidHtmlContent(preDefinedHtml)) {
  113. var $editingArea = this.getEditingArea();
  114. $editingArea.html(HtmlCleanser.cleanseContentElements(preDefinedHtml, WHITELISTED_ELEMENTS, true));
  115. // clear old saved jquery textfill font-size value
  116. $editingArea.find('>p').css('font-size', '');
  117. }
  118. }
  119. },
  120. /**
  121. * gets the default font
  122. * @return {Object} font default font properties
  123. * @returns {string} font.id id of the font
  124. */
  125. getDefaultFont: function getDefaultFont() {
  126. var id = '';
  127. this._fontFamilyChoices.forEach(function (font) {
  128. if (font.default === true) {
  129. id += font.value;
  130. }
  131. });
  132. return { id: id };
  133. },
  134. _getSummernoteShortcuts: function _getSummernoteShortcuts() {
  135. return {
  136. pc: {
  137. 'ENTER': 'insertParagraph',
  138. 'CTRL+Z': 'undo',
  139. 'CTRL+Y': 'redo',
  140. 'TAB': '', /* tabbing is handled in onKeyDown */
  141. 'SHIFT+TAB': '',
  142. 'CTRL+B': 'bold',
  143. 'CTRL+I': 'italic',
  144. 'CTRL+U': 'underline',
  145. 'CTRL+SHIFT+L': 'justifyLeft',
  146. 'CTRL+SHIFT+E': 'justifyCenter',
  147. 'CTRL+SHIFT+R': 'justifyRight',
  148. 'CTRL+SHIFT+NUM7': '', /* inserting lists is handled in onKeyDown */
  149. 'CTRL+SHIFT+NUM8': ''
  150. },
  151. mac: {
  152. 'ENTER': 'insertParagraph',
  153. 'CMD+Z': 'undo',
  154. 'CMD+SHIFT+Z': 'redo',
  155. 'TAB': '', /* tabbing is handled in onKeyDown */
  156. 'SHIFT+TAB': '',
  157. 'CMD+B': 'bold',
  158. 'CMD+I': 'italic',
  159. 'CMD+U': 'underline',
  160. 'CMD+SHIFT+L': 'justifyLeft',
  161. 'CMD+SHIFT+E': 'justifyCenter',
  162. 'CMD+SHIFT+R': 'justifyRight',
  163. 'CMD+SHIFT+NUM7': '', /* inserting lists is handled in onKeyDown */
  164. 'CMD+SHIFT+NUM8': ''
  165. }
  166. };
  167. },
  168. destroy: function destroy() {
  169. if (this.toolbar) {
  170. this.toolbar.remove();
  171. this.toolbar = null;
  172. }
  173. if (this.textToolbar) {
  174. this.textToolbar.remove();
  175. this.textToolbar = null;
  176. }
  177. this.selectionManager = null;
  178. this._getSummerNote().summernote('destroy');
  179. },
  180. /**
  181. * Initialize ContentEditableSelectionManager in the element matched by the selector.
  182. *
  183. * @param {String} selector Selector of the element
  184. */
  185. initContentEditable: function initContentEditable(selector) {
  186. this.selectionManager = new ContentEditableSelectionManager(this.node, selector);
  187. },
  188. /**
  189. * insert a text to the editor
  190. * @param {String} text - string text to insert to the editor
  191. */
  192. setText: function setText(text) {
  193. this.getEditingArea().html(HtmlCleanser.cleanseContentElements(text, WHITELISTED_ELEMENTS, true));
  194. this.editingAreaHeight = this._getHeight();
  195. var onSetText = this._ext.onSetText;
  196. onSetText && onSetText(this);
  197. },
  198. getNode: function getNode() {
  199. return this.node;
  200. },
  201. /**
  202. * Defect 230351: Put back focus on text when the focus is stolen by the ODT
  203. */
  204. _putBackFocusOnText: function _putBackFocusOnText() {
  205. this.setFocus();
  206. this.widget.eventRouter.off('widget:hideToolbar:done');
  207. },
  208. setFocus: function setFocus() {
  209. this._getSummerNote().summernote('focus');
  210. },
  211. /*
  212. * Helpers.
  213. */
  214. selectionBindNodeEvents: function selectionBindNodeEvents() {
  215. this.fnSelectionOnKeyDown = this.selectionOnKeyDown.bind(this);
  216. this.node.on('keydown', this.fnSelectionOnKeyDown);
  217. },
  218. selectionUnbindNodeEvents: function selectionUnbindNodeEvents() {
  219. if (this.fnSelectionOnKeyDown) {
  220. this.node.off('keydown', this.fnSelectionOnKeyDown);
  221. this.fnSelectionOnKeyDown = null;
  222. }
  223. },
  224. selectionOnKeyDown: function selectionOnKeyDown(event) {
  225. this.selectionManager.filterKeypress(event);
  226. },
  227. addEditingEvents: function addEditingEvents(options) {
  228. // Detach the enter editing mode gestures.
  229. this.detachEnterEditEvents();
  230. this.bindNodeEvents();
  231. this._startAutoSaver(options);
  232. //SMEditTest
  233. var onEditStart = this._ext.onEditStart;
  234. onEditStart && onEditStart(this);
  235. //SMEditTest
  236. },
  237. removeEditingEvents: function removeEditingEvents(options) {
  238. this.unbindNodeEvents();
  239. this._stopAutoSaver(options);
  240. // Allow the user to enter editing mode again through gestures.
  241. this.attachEnterEditEvents();
  242. var onEditEnd = this._ext.onEditEnd;
  243. onEditEnd && onEditEnd(this);
  244. },
  245. /*
  246. * Event related
  247. */
  248. onInitialTouchDown: function onInitialTouchDown() {
  249. this.hasInitiallyTouchedDown = true;
  250. },
  251. onInitialTouchUp: function onInitialTouchUp() {
  252. if (this.hasInitiallyTouchedDown) {
  253. this.hasInitiallyTouchedDown = false;
  254. //Hide contextual toolbar
  255. this.widget.eventRouter.trigger('widget:hideToolbar');
  256. /**
  257. * Even tough toolbar is hidden, it can sometimes be rendered and focused because of the timer set with the SetTimout
  258. * Defect 230351 fix: Put back focus on text when the focus is stolen by the ODT
  259. */
  260. this.widget.eventRouter.on('widget:hideToolbar:done', this._putBackFocusOnText, this);
  261. this.toggleEditing(true);
  262. } else {
  263. this.widget.eventRouter.off('widget:hideToolbar:done');
  264. }
  265. },
  266. attachEnterEditEvents: function attachEnterEditEvents() {
  267. if (!this.fnTouchDown && this.widget.isAuthoringMode && this.widget.chromeSelected) {
  268. this.fnTouchDown = this.onInitialTouchDown.bind(this);
  269. this.fnTouchUp = this.onInitialTouchUp.bind(this);
  270. this.node.on('touchstart', this.fnTouchDown).on('touchend', this.fnTouchUp).on('mousedown', this.fnTouchDown).on('mouseup', this.fnTouchUp);
  271. }
  272. },
  273. detachEnterEditEvents: function detachEnterEditEvents() {
  274. if (this.fnTouchDown) {
  275. this.node.off('touchstart', this.fnTouchDown).off('touchend', this.fnTouchUp).off('mousedown', this.fnTouchDown).off('mouseup', this.fnTouchUp);
  276. this.fnTouchDown = null;
  277. this.fnTouchUp = null;
  278. }
  279. },
  280. save: function save(options) {
  281. var _this = this;
  282. var data = this.getContent();
  283. var lastSavedData = this.lastSavedData;
  284. if (this.isResponsive) {
  285. // The auto font-size is screen-size specific, and so does not need to be
  286. // persisted. We remove it from the string compare to avoid triggering
  287. // undesired or unnecessary events.
  288. var _clearFontSize = function _clearFontSize(html) {
  289. var $el = $('<div>' + html + '</div>');
  290. $el.find(_this.TEXT_FITTED).css('font-size', '');
  291. return $el.html();
  292. };
  293. data = _clearFontSize(data);
  294. lastSavedData = _clearFontSize(lastSavedData);
  295. }
  296. //truncate style does not need to be persisted
  297. data = this._clearTruncateStyle(data);
  298. lastSavedData = this._clearTruncateStyle(lastSavedData);
  299. // Only update if the value has changed.
  300. if (data !== lastSavedData) {
  301. if (!options) {
  302. var transactionId = _.uniqueId('_text_');
  303. this.widget.onPropertyUpdate({
  304. category: 'saveTitle',
  305. transactionId: transactionId
  306. });
  307. } else {
  308. options.category = 'saveTitle';
  309. this.widget.onPropertyUpdate(options);
  310. }
  311. this.fillText();
  312. }
  313. },
  314. setTruncateOn: function setTruncateOn() {
  315. this.getEditingArea().find(this.TEXT_FITTED).addClass('truncateTextOn');
  316. this.getEditingArea().find('p').addClass('truncateText');
  317. },
  318. setTruncateOff: function setTruncateOff() {
  319. this.getEditingArea().find(this.TEXT_FITTED).removeClass('truncateTextOn');
  320. this.getEditingArea().find('p').removeClass('truncateText');
  321. },
  322. updateLastSavedText: function updateLastSavedText(value) {
  323. if (this.lastSavedData !== value) {
  324. this.lastSavedData = value;
  325. }
  326. },
  327. _clearTruncateStyle: function _clearTruncateStyle(html) {
  328. var $el = $('<div>' + html + '</div>');
  329. $el.find('p').removeClass('truncateText');
  330. return $el.html();
  331. },
  332. updateCssOnParagraphs: function updateCssOnParagraphs(addedClass, removedClass) {
  333. this.getEditingArea().find('p').removeClass(removedClass);
  334. this.getEditingArea().find('p').addClass(addedClass);
  335. },
  336. _startAutoSaver: function _startAutoSaver(options) {
  337. if (!this.autoSaverInterval) {
  338. //Save then start autosaver
  339. this.save(options);
  340. this.autoSaverInterval = setInterval(this.save.bind(this), this.autoSaveInterval);
  341. }
  342. },
  343. _stopAutoSaver: function _stopAutoSaver(options) {
  344. if (this.autoSaverInterval) {
  345. this.save(options);
  346. clearInterval(this.autoSaverInterval);
  347. this.autoSaverInterval = null;
  348. }
  349. },
  350. /**
  351. * Returns whether the event is for the modifier key (cmd on Mac, ctrl on other platforms).
  352. */
  353. _isModifierKey: function _isModifierKey(event) {
  354. return event.ctrlKey || event.metaKey;
  355. },
  356. onKeyDown: function onKeyDown(event) {
  357. var _this2 = this;
  358. // Prevent arrow keys from moving widget around while editing text.
  359. event.stopPropagation();
  360. // TODO backspace will delete the editor, this fix can prevent that, but a better solution needs to be found
  361. if (this.textIsEmpty() && (event.which === 8 || event.which === 46)) {
  362. event.preventDefault();
  363. }
  364. if (event.which === ENTER_KEY) {
  365. this._resizeViz();
  366. }
  367. // handle tabbing and indenting
  368. if (event.which === 9) {
  369. var maxNestingLevelReached = false;
  370. var listItemStartSelected = false;
  371. var listItems = this.getSelectedListItems();
  372. var selRange = this._getRefinedSelectionRange();
  373. listItems.forEach(function (li) {
  374. var $li = $(li);
  375. if (!maxNestingLevelReached && $li.parentsUntil(_this2.EDITING_AREA, 'ul, ol').length > 3) {
  376. maxNestingLevelReached = true;
  377. }
  378. if (!listItemStartSelected && _this2._isNodeStartSelected(li, selRange)) {
  379. listItemStartSelected = true;
  380. }
  381. });
  382. if (listItems.length && listItemStartSelected) {
  383. if (event.shiftKey) {
  384. this._getSummerNote().summernote('outdent');
  385. } else if (!maxNestingLevelReached) {
  386. this._getSummerNote().summernote('indent');
  387. }
  388. } else {
  389. if (event.shiftKey) {
  390. this._focusToolbar();
  391. } else {
  392. this._getSummerNote().summernote('tab');
  393. }
  394. }
  395. event.preventDefault();
  396. this.fillText();
  397. this.save();
  398. }
  399. if (this.supportsLists && (event.which === 55 || event.which === 56) && this._isModifierKey(event) && event.shiftKey) {
  400. this.modifyList(event.which === 55 ? 'insertUnorderedList' : 'insertOrderedList');
  401. this.fillText();
  402. this.save();
  403. this.updatePropertiesIfNecessary();
  404. }
  405. this.selectionOnKeyDown(event);
  406. },
  407. _focusToolbar: function _focusToolbar() {
  408. if (this._toolbarDock && this._toolbarDock.isContentToolbarDocked()) {
  409. this._toolbarDock.focusODT();
  410. } else {
  411. this.toolbar.focus();
  412. }
  413. },
  414. _isToolbarOpened: function _isToolbarOpened() {
  415. return this.toolbar && this.toolbar.isOpened;
  416. },
  417. hideTextToolbar: function hideTextToolbar() {
  418. if (this._isToolbarOpened()) {
  419. this.toolbar.remove();
  420. }
  421. },
  422. show: function show(immediateEditing) {
  423. this.node.show();
  424. this.fillText();
  425. if (immediateEditing) {
  426. this.toggleEditing(true);
  427. }
  428. },
  429. hide: function hide() {
  430. this.toggleEditing(false);
  431. this.node.hide();
  432. },
  433. _getNumberOfLines: function _getNumberOfLines() {
  434. return this.getEditingArea().find('p').length;
  435. },
  436. _getHeight: function _getHeight() {
  437. return this.getEditingArea().height();
  438. },
  439. _resizeVizIfHeightChanged: function _resizeVizIfHeightChanged() {
  440. if (this._getHeight() !== this.editingAreaHeight) {
  441. this._resizeViz();
  442. this.editingAreaHeight = this._getHeight();
  443. }
  444. },
  445. _resizeViz: function _resizeViz() {
  446. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {
  447. hideToolbar: false
  448. };
  449. if (this.shouldResizeViz) {
  450. this.widget.widgetChromeEventRouter.trigger('title:resizeViz', options);
  451. }
  452. },
  453. /*onKeyUp - Checking for the shift F12 event so that we can exit the textwidget.
  454. * - Prevent fn-backspace or delete from deleting the widget while editing text.
  455. */
  456. onKeyUp: function onKeyUp(event) {
  457. if (event.which === 27 && this._isToolbarOpened()) {
  458. this.hideTextToolbar();
  459. this.node.focus();
  460. } else if (event.which === 27 || event.which === F12 && event.shiftKey) {
  461. event.stopPropagation();
  462. this.toggleEditing(false);
  463. this.container.focus();
  464. } else if (event.which === 8 || event.which === 46) {
  465. // delete or fn-backspace
  466. event.stopPropagation();
  467. }
  468. this._resizeVizIfHeightChanged();
  469. this.numberOfLines = this._getNumberOfLines();
  470. this.fillText();
  471. // TODO: The updatePropertiesIfNecessary was added to help offset missing functionality from the
  472. // TextSelectionNormalizer class. The TextSelectionNormalizer class mimics events (selectionchange) that some browsers
  473. // don't handle (like older firefox 45). This line can be removed once TextSelectionNormalizer has been
  474. // restored.
  475. this.updatePropertiesIfNecessary();
  476. },
  477. /**
  478. * prevent moving the widget in authoring mode when we highlight text using the mouse
  479. */
  480. onMouseMove: function onMouseMove(event) {
  481. if (this.hasMouseDownInside) {
  482. event.stopPropagation();
  483. }
  484. },
  485. onMouseUp: function onMouseUp(event) {
  486. if (this.hasMouseDownInside) {
  487. this.hasMouseDownInside = false;
  488. event.stopPropagation();
  489. if (this.fnMouseMove) {
  490. $(document).off('mousemove', this.fnMouseMove);
  491. this.fnMouseMove = null;
  492. }
  493. if (this.fnMouseUp) {
  494. $(document).off('mouseup', this.fnMouseUp);
  495. this.fnMouseUp = null;
  496. }
  497. }
  498. },
  499. onMouseDown: function onMouseDown(event) {
  500. var target = event.target;
  501. if (target.tagName !== 'SELECT' && target.tagName !== 'INPUT' && target.tagName !== 'TEXTAREA' && !target.isContentEditable) {
  502. event.preventDefault();
  503. }
  504. },
  505. onBlur: function onBlur() {
  506. this.selection = this.saveSelection();
  507. },
  508. onMouseDownInside: function onMouseDownInside(event) {
  509. event.stopPropagation();
  510. this.hasMouseDownInside = true;
  511. // Add mouse move and up events. Only start listening for them now so that we aren't continuously
  512. // tracking the mouse movements.
  513. this.fnMouseMove = this.onMouseMove.bind(this);
  514. this.fnMouseUp = this.onMouseUp.bind(this);
  515. $(document).on('mousemove', this.fnMouseMove).on('mouseup', this.fnMouseUp);
  516. },
  517. onTouchStart: function onTouchStart(event) {
  518. // Prevent the move widget handler from taking over.
  519. event.stopPropagation();
  520. },
  521. keepTextSelected: function keepTextSelected() {
  522. if (Utils.isIpad()) {
  523. this.savedSel = this.saveSelection();
  524. }
  525. },
  526. onTextSelected: function onTextSelected() {
  527. var _this3 = this;
  528. if (!this.getEditingArea().is(document.activeElement)) {
  529. // if the selection changes because the user
  530. // clicked elsewhere, we don't care.
  531. return Promise.resolve();
  532. }
  533. // Instantiate this.selection to prepare for new text selection
  534. if (this.selection) {
  535. this.selection = null;
  536. }
  537. var currentSelection = this._getSummerNote().summernote('createRange').toString();
  538. if (!currentSelection || currentSelection !== this.lastSelection) {
  539. return this.showTextToolbar().then(function () {
  540. // Throttle the properties page update
  541. if (_this3.delayedPropertyUpdate) {
  542. clearTimeout(_this3.delayedPropertyUpdate);
  543. }
  544. _this3.delayedPropertyUpdate = setTimeout(function () {
  545. if (!(Utils.isIpad() && $('SELECT:focus').length > 0)) {
  546. this.delayedPropertyUpdate = null;
  547. this.updatePropertiesIfNecessary();
  548. }
  549. }.bind(_this3), 500);
  550. _this3.lastSelection = currentSelection;
  551. if (Utils.isIpad()) {
  552. _this3.savedSel = _this3.saveSelection();
  553. }
  554. });
  555. }
  556. },
  557. bindNodeEvents: function bindNodeEvents() {
  558. this.fnOnKeyDown = this.onKeyDown.bind(this);
  559. this.fnOnKeyUp = this.onKeyUp.bind(this);
  560. this.fnOnPaste = this.onPaste.bind(this);
  561. // Need to keep these events on the div since they won't fire on the inner span
  562. this.node.on('keydown', this.fnOnKeyDown).on('keyup', this.fnOnKeyUp).on('paste', this.fnOnPaste);
  563. this.fnOnMouseDownInside = this.onMouseDownInside.bind(this);
  564. this.fnOnBlur = this.onBlur.bind(this);
  565. this.fnOnTouchStart = this.onTouchStart.bind(this);
  566. this.fnkeepTextSelected = this.keepTextSelected.bind(this);
  567. // Special handlers for when we click/touch on the text to prevent some of the widget default behavior (i.e. widget move)
  568. this.getEditingArea().on('mousedown', this.fnOnMouseDownInside).on('blur', this.fnOnBlur).on('touchstart', this.fnOnTouchStart).on('focusout', this.fnkeepTextSelected);
  569. this.fnMouseDown = this.onMouseDown.bind(this);
  570. this.fnSelectionChange = this.onTextSelected.bind(this);
  571. $(document).on('mousedown', this.fnMouseDown).on('selectionchange', this.fnSelectionChange);
  572. },
  573. unbindNodeEvents: function unbindNodeEvents() {
  574. this.node.off('keydown', this.fnOnKeyDown).off('keyup', this.fnOnKeyUp).off('paste', this.fnOnPaste);
  575. this.getEditingArea().off('mousedown', this.fnOnMouseDownInside).off('touchstart', this.fnOnTouchStart).off('blur', this.fnOnBlur).off('focusout', this.fnkeepTextSelected);
  576. if (this.fnSelectionChange) {
  577. $(document).off('selectionchange', this.fnSelectionChange);
  578. this.fnSelectionChange = null;
  579. }
  580. if (this.fnMouseDown) {
  581. $(document).off('mousedown', this.fnMouseDown);
  582. this.fnMouseDown = null;
  583. }
  584. },
  585. _replaceContent: function _replaceContent(options) {
  586. var currentContent = this.node.find(this.EDITING_AREA);
  587. var prevContent = $(options.value).find(this.EDITING_AREA);
  588. currentContent.html(prevContent.html());
  589. this._resizeVizIfHeightChanged();
  590. },
  591. _didContentChange: function _didContentChange(value) {
  592. if (value) {
  593. var prevContent = $(value).find(this.EDITING_AREA).html();
  594. var currentContent = this.getContent();
  595. return prevContent && currentContent && prevContent !== currentContent;
  596. } else {
  597. return false;
  598. }
  599. },
  600. applyContent: function applyContent(options) {
  601. //Reinitialize summernote if necessary after undo/redo
  602. this.initSummernote();
  603. if (this._didContentChange(options.value)) {
  604. this._replaceContent(options);
  605. if (options.sender === 'TranslationService') {
  606. this.fillText();
  607. }
  608. // We need to update the last saved data here, before replacing content with placeholder. See Defect 254590.
  609. this.lastSavedData = this.getContent();
  610. // update placeholder on undo / redo
  611. if (options.sender === 'UndoRedoController' && this.widget.placeholder) {
  612. this.widget.updatePlaceholder();
  613. }
  614. } else {
  615. this.lastSavedData = this.getContent();
  616. }
  617. },
  618. onPaste: function onPaste(event) {
  619. this._pasteTextExtractedFromHTML(event);
  620. this.fillText();
  621. },
  622. /** put window behind an accessor to make it possible to unit test. */
  623. _getWindow: function _getWindow() {
  624. return window;
  625. },
  626. /** Gets the paste data from the paste event, extracts the text out of whatever else is there,
  627. * and then inserts it where ever the caret is.
  628. */
  629. _pasteTextExtractedFromHTML: function _pasteTextExtractedFromHTML(event) {
  630. var pastedData;
  631. // Stop data actually being pasted into the element. preventDefault() prevents the
  632. // text from being 'pasted twice', once with the formatting and the second time without it (here).
  633. event.preventDefault();
  634. // Get pasted data via clipboard API and insert it where the cursor is.
  635. // This version is apparently more efficient than the alternative (below).
  636. if (event.originalEvent.clipboardData) {
  637. pastedData = event.originalEvent.clipboardData.getData('Text');
  638. this._getWindow().document.execCommand('insertText', false, pastedData);
  639. } else {
  640. // Special case for IE
  641. pastedData = this._getWindow().clipboardData.getData('Text');
  642. var selected = this._getWindow().getSelection().getRangeAt(0);
  643. selected.deleteContents();
  644. selected.insertNode(this._getWindow().document.createTextNode(pastedData));
  645. }
  646. },
  647. isEditing: function isEditing() {
  648. return this.container.hasClass('editing');
  649. },
  650. onToolbarStateChange: function onToolbarStateChange(changedState, options) {
  651. var restoreDefaults = options && options.restoreDefaults;
  652. if (restoreDefaults) {
  653. // 1. The summernote api for bold, italic and underline only offers toggles.
  654. // 2. The current text selection may have mixed styles. (e.g: only some letters of a word are bold)
  655. // ** Therefore, removing all formatting before restoring default styles
  656. this._getSummerNote().summernote('removeFormat');
  657. }
  658. for (var propChanged in changedState) {
  659. if (propChanged === 'underline' || propChanged === 'bold' || propChanged === 'italic') {
  660. var value = changedState[propChanged];
  661. if (!restoreDefaults || value) {
  662. this.modifyTextFormat(propChanged, options);
  663. }
  664. } else if (propChanged === 'fontSize') {
  665. this.onFontSizeUpdate(propChanged, changedState.fontSize);
  666. } else if (propChanged === 'font') {
  667. this.onFontFamilyUpdate('fontFamily', changedState.font);
  668. } else if (propChanged === 'color') {
  669. if (this._isHexColor(changedState.color)) {
  670. this.modifyTextColor(this._findColorIdFromHex(changedState.color));
  671. } else {
  672. // For incoming non-hex colors, we directly set the class on the node, and then update properties to let the toolbar pick up the hexcode for the new color
  673. this.modifyTextColor(changedState.color);
  674. }
  675. } else if (propChanged === 'justifyLeft' || propChanged === 'justifyRight' || propChanged === 'justifyCenter') {
  676. if (changedState[propChanged]) {
  677. this.modifyTextAlignment(propChanged);
  678. }
  679. } else if (propChanged === 'insertUnorderedList' || propChanged === 'insertOrderedList') {
  680. this.modifyList(propChanged, changedState[propChanged]);
  681. } else if (propChanged === 'textWrap') {
  682. this._ext.onTextWrapChanged(changedState[propChanged]);
  683. }
  684. }
  685. this.updatePropertiesIfNecessary();
  686. if (!options || !options.silent) {
  687. this.lastActiveStyles = this.getActiveStyles();
  688. this.fillText();
  689. this.save();
  690. this.widget.refreshPropertiesPane();
  691. }
  692. },
  693. _findColorHexFromId: function _findColorHexFromId(colorId) {
  694. var colorPalette = this._getTextColorChoices();
  695. var found = _.find(colorPalette, function (color) {
  696. return color.id === colorId;
  697. });
  698. if (!found && this.colorsService.isCustomColor(colorId)) {
  699. return this.colorsService.getHexColorFromClassName(colorId);
  700. }
  701. return found ? found.hexValue : found;
  702. },
  703. _findColorIdFromHex: function _findColorIdFromHex(colorValue) {
  704. var colorPalette = this._getTextColorChoices();
  705. var found = _.find(colorPalette, function (color) {
  706. return color.hexValue.toUpperCase() === colorValue.toUpperCase();
  707. });
  708. if (!found) {
  709. return this.colorsService.getColorClassName(colorValue);
  710. }
  711. return found ? found.id : found;
  712. },
  713. convertStyleToToolbarState: function convertStyleToToolbarState(styles) {
  714. var state = {};
  715. if (styles['font-size']) {
  716. state.fontSize = styles['font-size'] + '';
  717. }
  718. if (styles.color) {
  719. state.color = this._findColorHexFromId(styles.color);
  720. }
  721. if (styles['font-family']) {
  722. state.font = this._convertCssToFontId(styles['font-family'])[0];
  723. }
  724. if (styles['text-align']) {
  725. state[styles['text-align']] = true;
  726. }
  727. state.insertUnorderedList = styles.insertUnorderedList;
  728. state.insertOrderedList = styles.insertOrderedList;
  729. //Set the text format applied to true (underline,bold,italic);
  730. state.bold = state.underline = state.italic = false;
  731. _.each(styles['text-format'], function (format) {
  732. state[format] = true;
  733. });
  734. return state;
  735. },
  736. showTextToolbar: function showTextToolbar() {
  737. var _this4 = this;
  738. if (this._toolbarDock && this._toolbarDock.isContentToolbarDocked()) {
  739. if (!this.textToolbar.isRendered) {
  740. var toolbarState = this.convertStyleToToolbarState(this.getActiveStyles());
  741. // refresh the properties options before re-render the text toolbar
  742. this.textToolbar.properties = this.getToolbarProperties();
  743. this._setTextWrapState(toolbarState);
  744. this.textToolbar.setState(toolbarState);
  745. this._toolbarDock.updateODTState([{
  746. type: 'SubView',
  747. responsive: true,
  748. editable: false,
  749. changedAction: null,
  750. subView: this.textToolbar,
  751. removeThenApplyAction: true,
  752. action: function action() {}
  753. }, {
  754. name: 'textToolbarDock',
  755. type: 'Button',
  756. label: resources.get('undockToolbar'),
  757. icon: this._icons.getIcon('undock-icon').id,
  758. disabled: function disabled() {
  759. return _this4._dashboardState.getUiState().focus;
  760. },
  761. actions: {
  762. apply: function () {
  763. this._toolbarDock.disableDockedODT();
  764. this.showTextToolbar();
  765. }.bind(this)
  766. }
  767. }]);
  768. }
  769. return Promise.resolve();
  770. } else {
  771. if (this._isToolbarOpened()) {
  772. return Promise.resolve();
  773. } else {
  774. // unmount react component before clear items
  775. this.textToolbar.remove();
  776. this.toolbar.clearItems();
  777. var _toolbarState = this.convertStyleToToolbarState(this.getActiveStyles());
  778. // refresh the properties options before re-render the text toolbar
  779. this.textToolbar.properties = this.getToolbarProperties();
  780. this._setTextWrapState(_toolbarState);
  781. this.textToolbar.setState(_toolbarState);
  782. var useMoveHandle = this.toolbarNode[0] === this.container[0] || $.contains(this.toolbarNode[0], this.container[0]);
  783. var $moveHandle = this.toolbarNode.find('.moveHandle');
  784. var selectionContext = useMoveHandle && $moveHandle.length ? $moveHandle[0] : this.container[0];
  785. this.toolbar.setSelectionContext([selectionContext]);
  786. return this.toolbar.addItems([{
  787. type: 'SubView',
  788. responsive: true,
  789. editable: false,
  790. changedAction: null,
  791. subView: this.textToolbar, //Here the toolkit toolbar is rendered but not visible. Returns a promise.
  792. removeThenApplyAction: true,
  793. action: function action() {}
  794. }, {
  795. name: 'textToolbarDock',
  796. type: 'Button',
  797. label: resources.get('dockToolbar'),
  798. icon: this._icons.getIcon('dock-icon').id,
  799. actions: {
  800. apply: function () {
  801. this._toolbarDock.enableDockedODT();
  802. this.textToolbar.remove();
  803. this.toolbar.remove();
  804. this.showTextToolbar();
  805. }.bind(this)
  806. }
  807. }]).then(function () {
  808. return _this4.toolbar.show();
  809. });
  810. }
  811. }
  812. },
  813. _setTextWrapState: function _setTextWrapState(toolbarState) {
  814. if (this._ext && this._ext.isTextWrapOn) {
  815. toolbarState.textWrap = this._ext.isTextWrapOn();
  816. }
  817. },
  818. _selectContent: function _selectContent() {
  819. var range = document.createRange();
  820. var isMissingParagraphElement = this.getEditingArea().find('p').length === 0;
  821. // FIXME: The paragraph element shouldn't be getting deleted at all, but our current implementation with IE 11 allows this to happen
  822. var nodeContents = isMissingParagraphElement ? this.getEditingArea().get(0) : this.getEditingArea().find('p').get(0);
  823. range.selectNodeContents(nodeContents);
  824. var sel = window.getSelection();
  825. this._clearSelection(sel);
  826. sel.addRange(range);
  827. },
  828. _focusOnText: function _focusOnText() {
  829. var _this5 = this;
  830. this._selectContent();
  831. this.getEditingArea().focus();
  832. return this.showTextToolbar().then(function () {
  833. _this5._selectContent();
  834. _this5.getEditingArea().focus(); // Required for Firefox, Defect 202868 fix
  835. _this5.widget.refreshPropertiesPane({
  836. 'selectedTab': resources.get('propText')
  837. });
  838. });
  839. },
  840. toggleEditing: function toggleEditing(editing, options) {
  841. var wasEditing = this.isEditing(this.container);
  842. var toggle = editing === undefined ? !wasEditing : editing;
  843. this.container.toggleClass('editing', toggle);
  844. this.getEditingArea().attr('contenteditable', toggle);
  845. if (toggle !== wasEditing) {
  846. this._resizeViz();
  847. }
  848. if (toggle) {
  849. if (!wasEditing) {
  850. this.addEditingEvents(options);
  851. }
  852. this._focusOnText();
  853. } else if (wasEditing && !toggle) {
  854. this._clearSelection(window.getSelection());
  855. this.removeEditingEvents(options);
  856. if (this._toolbarDock && this._toolbarDock.isContentToolbarDocked()) {
  857. var currentSelection = this.widget.getDashboardApi().getCanvas().getSelectedContentList();
  858. var selectedIds = currentSelection.map(function (content) {
  859. return content.getId();
  860. });
  861. this._toolbarDock.updateODTState(this._contentActions.getContentActionList(selectedIds), selectedIds);
  862. } else {
  863. this.hideTextToolbar();
  864. }
  865. //when toggle is off, remove the text details tab from the property panel by refreshing the property panel
  866. this.widget.refreshPropertiesPane();
  867. }
  868. },
  869. _removeClassFromSelection: function _removeClassFromSelection(command) {
  870. var map = {
  871. 'fontSize': /rt_fontSize(\d+|\w+)/g,
  872. 'fontFamily': /rt_fontFamily\w+(-\w+)?/g,
  873. 'textColor': /customColor.+|color\d+|grayShade\d+|responsiveColor/g
  874. };
  875. if (command && map[command]) {
  876. return function (index, cls) {
  877. return (cls.match(map[command]) || []).join(' ');
  878. };
  879. }
  880. },
  881. _addClassToSelection: function _addClassToSelection(command, value) {
  882. var map = {
  883. 'fontSize': 'rt_fontSize',
  884. 'fontFamily': '',
  885. 'textColor': ''
  886. };
  887. return map[command] + value;
  888. },
  889. getToolbarProperties: function getToolbarProperties() {
  890. var colorPalette = this._getTextColorChoices();
  891. var colorValues = [];
  892. _.each(colorPalette, function (color) {
  893. colorValues.push(color.hexValue);
  894. });
  895. return {
  896. 'fonts': this._fontFamilyChoices,
  897. 'fontSizes': this._fontSizeChoices,
  898. 'colors': colorValues
  899. };
  900. },
  901. /**
  902. * livewidget_cleanup: [todo] we might need to move this to a proper place..
  903. * Implementation of {@link PropertiesProviderAPI#getPropertyLayoutList}
  904. */
  905. getPropertyLayoutList: function getPropertyLayoutList() {
  906. if (!this.isEditing()) {
  907. return [];
  908. }
  909. var layoutList = [{
  910. id: 'textDetail',
  911. type: 'Group',
  912. label: resources.get('tabName_textDetails'),
  913. position: 1
  914. }, {
  915. id: 'fontFamily',
  916. type: 'Section',
  917. collapsible: false,
  918. label: resources.get('textFontFamily'),
  919. position: 1
  920. }, {
  921. id: 'fontFamilyLine',
  922. type: 'SingleLineLinks',
  923. items: [{
  924. id: 'left',
  925. align: 'left',
  926. items: []
  927. }, {
  928. id: 'right',
  929. align: 'right',
  930. items: []
  931. }]
  932. }].concat(this.getPropertyLayoutControlList());
  933. return layoutList;
  934. },
  935. getPropertyList: function getPropertyList() {
  936. return [];
  937. },
  938. getPropertyLayoutControlList: function getPropertyLayoutControlList() {
  939. if (!this.isEditing()) {
  940. return [];
  941. }
  942. var layoutControlList = [];
  943. this.lastActiveStyles = this.getActiveStyles();
  944. layoutControlList.push.apply(layoutControlList, this._getFontFamilyProperties());
  945. layoutControlList.push(this._getFontSizeProperty());
  946. layoutControlList.push(this._getTextColorProperty());
  947. layoutControlList.push(this._getFontWeightProperty());
  948. layoutControlList.push(this._getTextAlignmentProperty());
  949. if (this.supportsLists) {
  950. layoutControlList.push(this._getListProperty());
  951. }
  952. return layoutControlList;
  953. },
  954. _getFontFamilyProperties: function _getFontFamilyProperties() {
  955. var activeStyle = this.lastActiveStyles['font-family'];
  956. var selected = activeStyle ? this._convertCssToFontId(activeStyle) : [];
  957. var cssClass = this._findFontFamilyItemFromValue(selected[0], this._fontFamilyChoices);
  958. if (!cssClass) {
  959. selected = [this.getDefaultFont().id];
  960. cssClass = this._findFontFamilyItemFromValue(selected[0], this._fontFamilyChoices);
  961. }
  962. var subCssClass = _.find(cssClass.subCssClasses, function (subCssClass) {
  963. return subCssClass.cssClass === cssClass.cssClass;
  964. });
  965. return [{
  966. id: 'fontFamily',
  967. editor: {
  968. sectionId: 'textDetail.fontFamily.fontFamilyLine.left',
  969. position: 1,
  970. uiControl: {
  971. type: 'DropDown',
  972. ariaDescribedby: resources.get('textFontFamily'),
  973. options: this._fontFamilyChoices,
  974. responsive: false,
  975. style: 'width: 156px;',
  976. value: selected[0],
  977. onChange: this.onFontFamilyUpdate.bind(this)
  978. }
  979. }
  980. }, {
  981. id: 'fontFamilySubClass',
  982. editor: {
  983. sectionId: 'textDetail.fontFamily.fontFamilyLine.left',
  984. position: 2,
  985. hidden: !cssClass || !cssClass.subCssClasses || cssClass.subCssClasses.length === 0,
  986. uiControl: {
  987. type: 'DropDown',
  988. ariaDescribedby: resources.get('textFontFamily'),
  989. options: cssClass && cssClass.subCssClasses,
  990. value: subCssClass && subCssClass.value,
  991. onChange: this.onFontFamilyUpdate.bind(this),
  992. style: 'width:80px;',
  993. responsive: false
  994. }
  995. }
  996. }];
  997. },
  998. _getFontSizeProperty: function _getFontSizeProperty() {
  999. return {
  1000. id: 'fontSize',
  1001. editor: {
  1002. sectionId: 'textDetail',
  1003. position: 2,
  1004. uiControl: {
  1005. type: 'DropDown',
  1006. label: resources.get('textFontSize'),
  1007. options: this._fontSizeChoices,
  1008. onChange: this.onFontSizeUpdate.bind(this),
  1009. value: this.isResponsive ? 'auto' : this.lastActiveStyles['font-size'] + ''
  1010. }
  1011. }
  1012. };
  1013. },
  1014. _getTextColorProperty: function _getTextColorProperty() {
  1015. var _this6 = this;
  1016. var paletteItems = this._getTextColorChoices();
  1017. var selected = _.find(paletteItems, function (palette) {
  1018. return palette.id === _this6.lastActiveStyles.color;
  1019. }) || {
  1020. name: this.lastActiveStyles.color,
  1021. value: ''
  1022. };
  1023. return {
  1024. id: 'textColor',
  1025. editor: {
  1026. sectionId: 'textDetail',
  1027. position: 3,
  1028. uiControl: {
  1029. paletteType: 'DashboardColorSet',
  1030. type: 'ColorPicker',
  1031. showHexValue: true,
  1032. addButton: true,
  1033. label: resources.get('propTextColor'),
  1034. open: false,
  1035. ariaLabel: resources.get('propTextColor'),
  1036. items: paletteItems,
  1037. allowSameSelection: true,
  1038. selectedName: selected ? selected.name : '',
  1039. value: selected ? selected.value : '',
  1040. onChange: this.onTextColorUpdate.bind(this)
  1041. }
  1042. }
  1043. };
  1044. },
  1045. getCustomColorList: function getCustomColorList() {
  1046. var colorList = [];
  1047. var classNames = [];
  1048. var allSpans = this.getEditingArea().find('span');
  1049. allSpans.each(function (index, span) {
  1050. classNames.push(span.className);
  1051. });
  1052. var allClassNames = classNames.join(' ');
  1053. var customColors = this.colorsService.getCustomColorSet();
  1054. customColors.forEach(function (customColor) {
  1055. if (allClassNames.indexOf(customColor.id) !== -1) {
  1056. colorList.push(customColor.hexValue);
  1057. }
  1058. });
  1059. return colorList;
  1060. },
  1061. _getFontWeightProperty: function _getFontWeightProperty() {
  1062. return {
  1063. id: 'textFormat',
  1064. editor: {
  1065. sectionId: 'textDetail',
  1066. position: 4,
  1067. uiControl: {
  1068. type: 'IconPicker',
  1069. label: resources.get('textStyle'),
  1070. useToggling: true,
  1071. allowMultiple: true,
  1072. items: [{
  1073. name: 'bold',
  1074. label: resources.get('propToolbarPickBold'),
  1075. value: 'common-text-bold'
  1076. }, {
  1077. name: 'italic',
  1078. label: resources.get('propToolbarPickItalic'),
  1079. value: 'common-text-italic'
  1080. }, {
  1081. name: 'underline',
  1082. label: resources.get('propToolbarPickUnderline'),
  1083. value: 'common-text-underline'
  1084. }],
  1085. values: this.lastActiveStyles['text-format'],
  1086. onChange: this.onTextFormatUpdate.bind(this)
  1087. }
  1088. }
  1089. };
  1090. },
  1091. _getTextAlignmentProperty: function _getTextAlignmentProperty() {
  1092. return {
  1093. id: 'alignmentPicker',
  1094. editor: {
  1095. sectionId: 'textDetail',
  1096. position: 5,
  1097. uiControl: {
  1098. type: 'IconPicker',
  1099. label: resources.get('propAlignPicker'),
  1100. items: [{
  1101. name: 'justifyLeft',
  1102. label: resources.get('propAlignPickLeft'),
  1103. value: 'common-left-align'
  1104. }, {
  1105. name: 'justifyCenter',
  1106. label: resources.get('propAlignPickCenter'),
  1107. value: 'common-center-align'
  1108. }, {
  1109. name: 'justifyRight',
  1110. label: resources.get('propAlignPickRight'),
  1111. value: 'common-right-align'
  1112. }],
  1113. values: this.lastActiveStyles['text-align'],
  1114. onChange: this.onTextAlignmentUpdate.bind(this)
  1115. }
  1116. }
  1117. };
  1118. },
  1119. _getListProperty: function _getListProperty() {
  1120. var selected = void 0;
  1121. if (this.lastActiveStyles['insertUnorderedList']) {
  1122. selected = 'insertUnorderedList';
  1123. } else if (this.lastActiveStyles['insertOrderedList']) {
  1124. selected = 'insertOrderedList';
  1125. }
  1126. return {
  1127. id: 'listPicker',
  1128. editor: {
  1129. sectionId: 'textDetail',
  1130. position: 6,
  1131. uiControl: {
  1132. type: 'IconPicker',
  1133. label: resources.get('propList'),
  1134. useToggling: true,
  1135. items: [{
  1136. name: 'insertUnorderedList',
  1137. label: resources.get('propUnorderedList'),
  1138. value: this._icons.getIcon('bullet-list').id
  1139. }, {
  1140. name: 'insertOrderedList',
  1141. label: resources.get('propOrderedList'),
  1142. value: this._icons.getIcon('number-list').id
  1143. }],
  1144. values: selected,
  1145. onChange: this.onListUpdate.bind(this)
  1146. }
  1147. }
  1148. };
  1149. },
  1150. updateTextToolbar: function updateTextToolbar(activeStyles) {
  1151. var styles = activeStyles ? activeStyles : this.getActiveStyles();
  1152. var states = this.convertStyleToToolbarState(styles);
  1153. // refresh the properties options before re-render the text toolbar
  1154. this.textToolbar.properties = this.getToolbarProperties();
  1155. this._setTextWrapState(states);
  1156. this.textToolbar.setState(states);
  1157. },
  1158. updatePropertiesIfNecessary: function updatePropertiesIfNecessary() {
  1159. if (this.isEditing()) {
  1160. var activeStyles = this.getActiveStyles();
  1161. var filteredActiveStyles = _.pick(activeStyles, WHITELISTED_PROPERTIES);
  1162. var filteredLastActiveStyles = _.pick(this.lastActiveStyles || {}, WHITELISTED_PROPERTIES);
  1163. if (!_.isEqual(filteredActiveStyles, filteredLastActiveStyles)) {
  1164. this.lastActiveStyles = activeStyles;
  1165. this.updateTextToolbar(activeStyles);
  1166. this.widget.refreshPropertiesPane();
  1167. }
  1168. }
  1169. },
  1170. _convertCssToFontId: function _convertCssToFontId(fontCss) {
  1171. var fontId;
  1172. fontId = fontCss.replace(/["\s]+/g, '');
  1173. fontId = fontId.toLowerCase().split(',');
  1174. fontId = fontId.length ? fontId[0].split('-') : null;
  1175. return fontId;
  1176. },
  1177. getFontFamilySubClassSpec: function getFontFamilySubClassSpec(cssItem, defaultValue) {
  1178. if (!cssItem) {
  1179. return [];
  1180. }
  1181. if (!defaultValue && cssItem.subCssClasses) {
  1182. for (var i = 0; i < cssItem.subCssClasses.length; i++) {
  1183. if (cssItem.subCssClasses[i].cssClass === cssItem.cssClass) {
  1184. defaultValue = cssItem.subCssClasses[i].value;
  1185. break;
  1186. }
  1187. }
  1188. }
  1189. var property = {
  1190. 'type': 'DropDown',
  1191. 'name': 'fontFamilySubClass',
  1192. 'id': 'fontFamilyGroup',
  1193. 'ariaDescribedby': resources.get('textFontFamily'),
  1194. 'defaultValue': defaultValue,
  1195. 'options': cssItem.subCssClasses,
  1196. 'onChange': this.onFontFamilyUpdate.bind(this),
  1197. 'style': 'width:80px;',
  1198. 'responsive': false
  1199. };
  1200. if (!cssItem.subCssClasses || cssItem.subCssClasses.length === 0) {
  1201. property.hidden = true;
  1202. }
  1203. return property;
  1204. },
  1205. modifyTextFormat: function modifyTextFormat(value) {
  1206. if (!this.isEditing()) {
  1207. return;
  1208. }
  1209. if (agent.isFF && (value == 'bold' || value == 'underline')) {
  1210. // [201576] Patch for bold + underline props not showing up in FF
  1211. this.patchUnderline(value);
  1212. } else if (value) {
  1213. this._getSummerNote().summernote(value);
  1214. }
  1215. this.fillText();
  1216. },
  1217. onListUpdate: function onListUpdate(propertyName, propertyValue) {
  1218. if (propertyValue && propertyValue[0] && propertyValue[0].name) {
  1219. this.modifyList(propertyValue[0].name);
  1220. } else {
  1221. var activeList = this.getActiveStyles()['insertUnorderedList'] ? 'insertUnorderedList' : 'insertOrderedList';
  1222. this.modifyList(activeList);
  1223. }
  1224. this.fillText();
  1225. this.save();
  1226. this.updateTextToolbar();
  1227. },
  1228. modifyList: function modifyList(propertyName, propertyValue) {
  1229. if (!this.isEditing() || this.getActiveStyles()[propertyName] === propertyValue) {
  1230. return;
  1231. }
  1232. this._getSummerNote().summernote(propertyName);
  1233. this._styleListItems();
  1234. },
  1235. /**
  1236. * Patch for firefox, applying underline after bold would break
  1237. * Works fine when we have <b><u>, fails spectacularly when we have <u><b>, i.e. underline then bold works, bold then underline fails.
  1238. * @param value {String} bold or underline
  1239. */
  1240. patchUnderline: function patchUnderline(value) {
  1241. if (value == 'bold') {
  1242. this.isBold = !this.isBold;
  1243. this._getSummerNote().summernote(value);
  1244. } else if (value == 'underline') {
  1245. if (this.isBold) {
  1246. // remove bold and reapply after underline so we always have <b><u>, never <u><b> since that breaks in FF
  1247. this._getSummerNote().summernote('bold');
  1248. this._getSummerNote().summernote('underline');
  1249. this._getSummerNote().summernote('bold');
  1250. } else {
  1251. this._getSummerNote().summernote('underline');
  1252. }
  1253. }
  1254. },
  1255. /*
  1256. * Modify the font family: Anton, Roboto, etc.
  1257. */
  1258. modifyFontFamily: function modifyFontFamily(cssClass) {
  1259. if (!this.isEditing()) {
  1260. return;
  1261. }
  1262. var removedClass = this._removeClassFromSelection('fontFamily');
  1263. var addedClass = this._addClassToSelection('fontFamily', cssClass);
  1264. this._getSummerNote().summernote('updateClassToSelection', {
  1265. remove: removedClass,
  1266. add: addedClass
  1267. });
  1268. this._styleListItems();
  1269. this.getEditingArea().focus();
  1270. this.fillText();
  1271. },
  1272. _findFontFamilyItemFromValue: function _findFontFamilyItemFromValue(value, items) {
  1273. var current = void 0;
  1274. for (var i = 0; i < items.length; i++) {
  1275. if (items[i].value === value) {
  1276. return items[i];
  1277. } else if (this.lastActiveStyles && this.lastActiveStyles['font-family'] && items[i].value === this._convertCssToFontId(this.lastActiveStyles['font-family'])[0]) {
  1278. current = items[i];
  1279. }
  1280. }
  1281. if (current) {
  1282. var subCssClassMatch = this._findFontFamilyItemFromValue(value, current.subCssClasses || []);
  1283. if (subCssClassMatch) {
  1284. return subCssClassMatch;
  1285. }
  1286. }
  1287. return null;
  1288. },
  1289. _cleanInlineStyle: function _cleanInlineStyle() {
  1290. this.getEditingArea().find('span, li').css('font-size', '');
  1291. },
  1292. /*
  1293. * Modify the font size.
  1294. */
  1295. modifyFontSize: function modifyFontSize(value) {
  1296. if (!this.isEditing()) {
  1297. return;
  1298. }
  1299. this.isResponsive = value === 'auto';
  1300. var removeClass = this._removeClassFromSelection('fontSize');
  1301. if (!this.isResponsive) {
  1302. this.unFillText();
  1303. this._getSummerNote().summernote('fontSize', parseInt(value));
  1304. this._styleListItems();
  1305. } else {
  1306. this._cleanInlineStyle();
  1307. }
  1308. this._getSummerNote().summernote('updateClassToSelection', {
  1309. remove: removeClass
  1310. });
  1311. this.fillText();
  1312. this.getEditingArea().focus();
  1313. this._resizeViz();
  1314. },
  1315. _isHexColor: function _isHexColor(val) {
  1316. return val.indexOf('#') === 0 && val.length === 7;
  1317. },
  1318. /*
  1319. * Modify the text color.
  1320. */
  1321. modifyTextColor: function modifyTextColor(value) {
  1322. if (!this.isEditing()) {
  1323. return;
  1324. }
  1325. var addedClass = void 0;
  1326. var removedClass = this._removeClassFromSelection('textColor');
  1327. // Check for the transparent color.
  1328. if (value !== 'transparent') {
  1329. addedClass = this._addClassToSelection('textColor', value);
  1330. }
  1331. this._getSummerNote().summernote('updateClassToSelection', {
  1332. remove: removedClass,
  1333. add: addedClass
  1334. });
  1335. this._styleListItems();
  1336. this.getEditingArea().focus();
  1337. },
  1338. onTextFormatUpdate: function onTextFormatUpdate(propertyName, propertyPayload) {
  1339. var formatStyles = ['bold', 'underline', 'italic'];
  1340. var selected = this.getActiveStyles()['text-format'];
  1341. formatStyles.forEach(function (format) {
  1342. var styleApplied = [];
  1343. if (propertyPayload) {
  1344. styleApplied = $.grep(propertyPayload, function (property) {
  1345. return property.name === format;
  1346. });
  1347. }
  1348. if (styleApplied.length > 0 && selected.indexOf(format) === -1 || styleApplied.length === 0 && selected.indexOf(format) > -1) {
  1349. this.modifyTextFormat(format);
  1350. }
  1351. }.bind(this));
  1352. this.fillText();
  1353. this.save();
  1354. this.updateTextToolbar();
  1355. },
  1356. onTextAlignmentUpdate: function onTextAlignmentUpdate(propertyName, propertyPayload) {
  1357. if (propertyPayload && propertyPayload[0] && propertyPayload[0].name) {
  1358. this.modifyTextAlignment(propertyPayload[0].name);
  1359. }
  1360. this.fillText();
  1361. this.save();
  1362. this.updateTextToolbar();
  1363. },
  1364. modifyTextAlignment: function modifyTextAlignment(value) {
  1365. if (!this.isEditing()) {
  1366. return;
  1367. }
  1368. var alignment = value.replace('justify', '');
  1369. var selectedTextNodes = this.getSelectedParagraphs();
  1370. for (var _iterator = selectedTextNodes, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
  1371. var _ref;
  1372. if (_isArray) {
  1373. if (_i >= _iterator.length) break;
  1374. _ref = _iterator[_i++];
  1375. } else {
  1376. _i = _iterator.next();
  1377. if (_i.done) break;
  1378. _ref = _i.value;
  1379. }
  1380. var node = _ref;
  1381. node.style.textAlign = alignment;
  1382. }
  1383. //Changing the text alignment loses focus on the text
  1384. this.getEditingArea().focus();
  1385. },
  1386. onFontSizeUpdate: function onFontSizeUpdate(propertyName, value) {
  1387. this.modifyFontSize(value);
  1388. this.fillText();
  1389. if (Utils.isIpad()) {
  1390. this.restoreSelection();
  1391. }
  1392. this.getEditingArea().focus();
  1393. this.updateTextToolbar();
  1394. },
  1395. onFontFamilyUpdate: function onFontFamilyUpdate(propertyName, value) {
  1396. if (value) {
  1397. var styleItem = this._findFontFamilyItemFromValue(value, this._fontFamilyChoices);
  1398. if (!styleItem) {
  1399. return;
  1400. }
  1401. this.modifyFontFamily(styleItem.cssClass);
  1402. if (propertyName === 'fontFamily') {
  1403. this.widget.triggerExternalEvent('properties:refreshProperty', {
  1404. 'sender': this.widget.model.id,
  1405. 'propertySpec': this.getFontFamilySubClassSpec(styleItem, null),
  1406. 'removeProperty': !styleItem.subCssClasses
  1407. });
  1408. }
  1409. //Force auto-resize of text because some fonts are different default sizes
  1410. this.fillTextAfterDelay(500);
  1411. if (Utils.isIpad()) {
  1412. this.restoreSelection();
  1413. }
  1414. this.getEditingArea().focus();
  1415. this.updateTextToolbar();
  1416. }
  1417. },
  1418. onTextColorUpdate: function onTextColorUpdate(propertyName, propertyValueInfo) {
  1419. if (this.selection) {
  1420. this.restoreSelection(this.selection);
  1421. this.selection = null;
  1422. }
  1423. this.modifyTextColor(propertyValueInfo.name);
  1424. this.fillText();
  1425. this.updateTextToolbar();
  1426. },
  1427. textIsEmpty: function textIsEmpty() {
  1428. return this._getSummerNote().summernote('isEmpty') ||
  1429. /*Firefox*/this.getEditingArea().get(0).textContent.length === 0 && this.numberOfLines == 1;
  1430. },
  1431. getEditingArea: function getEditingArea() {
  1432. return this.node.find(this.EDITING_AREA);
  1433. },
  1434. _getSummerNote: function _getSummerNote() {
  1435. this.initSummernote();
  1436. return this.node.children(this.SUMMERNOTE_CLASS);
  1437. },
  1438. getContent: function getContent() {
  1439. return this.getEditingArea().html();
  1440. },
  1441. getTitleFromHtml: function getTitleFromHtml(html) {
  1442. var titleHtml = html;
  1443. var title = '';
  1444. $(titleHtml).find('p, li').each(function (index, el) {
  1445. title = title + ' ' + el.textContent;
  1446. });
  1447. return title.trim();
  1448. },
  1449. fillText: function fillText() {
  1450. if (!this.node.is(':visible')) {
  1451. return;
  1452. }
  1453. if (this.isResponsive) {
  1454. this.node.addClass('textFillNoScroll');
  1455. this.node.find(this.TEXT_FITTED).css('word-wrap', 'normal');
  1456. var savedLineAlignment = [];
  1457. if (BrowserUtils.isIE()) {
  1458. // Texfit fails in IE and Edge if the text is aligned right (related MS issue 30900154)
  1459. // Scrollwidth erroneously 1px smaller than actual fools the size acceptance test
  1460. this.getEditingArea().find('p, li').each(function (index, el) {
  1461. savedLineAlignment.push(el.style.textAlign);
  1462. el.style.textAlign = 'left';
  1463. });
  1464. }
  1465. var options = {
  1466. maxFontSize: Math.ceil(this.node.height()),
  1467. minFontSize: 1,
  1468. multiLine: true,
  1469. detectMultiLine: false
  1470. };
  1471. TextFitUtil.fillText(this.getEditingArea()[0], options);
  1472. if (savedLineAlignment.length) {
  1473. this.getEditingArea().find('p, li').each(function (index, el) {
  1474. el.style.textAlign = savedLineAlignment[index];
  1475. });
  1476. }
  1477. }
  1478. },
  1479. fillTextAfterDelay: function fillTextAfterDelay(delay) {
  1480. setTimeout(this.fillText.bind(this), delay);
  1481. },
  1482. unFillText: function unFillText() {
  1483. this.node.find(this.TEXT_FITTED).css({
  1484. 'font-size': '',
  1485. 'word-wrap': ''
  1486. });
  1487. this.node.removeClass('textFillNoScroll');
  1488. },
  1489. /**
  1490. * Styling related function
  1491. */
  1492. getActiveStyles: function getActiveStyles() {
  1493. var styles = this._getSummerNote().summernote('currentStyle');
  1494. this._getActiveTextFormat(styles);
  1495. this._getActiveTextAlignment(styles);
  1496. this._getActiveTextColor(styles);
  1497. this._getActiveFontSize(styles);
  1498. this._getActiveLists(styles);
  1499. return styles;
  1500. },
  1501. _getActiveLists: function _getActiveLists(context) {
  1502. context.insertUnorderedList = context['list-style'] === 'unordered';
  1503. context.insertOrderedList = context['list-style'] === 'ordered';
  1504. },
  1505. _getActiveTextFormat: function _getActiveTextFormat(context) {
  1506. var obj = context['text-format'];
  1507. if (!obj) {
  1508. context['text-format'] = obj = [];
  1509. }
  1510. if (context['font-bold'] === 'bold') {
  1511. obj.push('bold');
  1512. }
  1513. if (context['font-underline'] === 'underline') {
  1514. obj.push('underline');
  1515. }
  1516. if (context['font-italic'] === 'italic') {
  1517. obj.push('italic');
  1518. }
  1519. },
  1520. _getActiveTextAlignment: function _getActiveTextAlignment(context) {
  1521. var selectedTextNodes = this.getSelectedParagraphs();
  1522. // If multiple lines have different text alignments
  1523. // will not highlight an alignment in the property panel
  1524. if (selectedTextNodes && selectedTextNodes.length > 0) {
  1525. var style = selectedTextNodes[0].style.textAlign;
  1526. for (var _iterator2 = selectedTextNodes, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) {
  1527. var _ref2;
  1528. if (_isArray2) {
  1529. if (_i2 >= _iterator2.length) break;
  1530. _ref2 = _iterator2[_i2++];
  1531. } else {
  1532. _i2 = _iterator2.next();
  1533. if (_i2.done) break;
  1534. _ref2 = _i2.value;
  1535. }
  1536. var node = _ref2;
  1537. if (node.style.textAlign !== style) {
  1538. context['text-align'] = '';
  1539. return;
  1540. }
  1541. }
  1542. switch (style) {
  1543. case 'center':
  1544. context['text-align'] = 'justifyCenter';
  1545. break;
  1546. case 'right':
  1547. context['text-align'] = 'justifyRight';
  1548. break;
  1549. default:
  1550. context['text-align'] = 'justifyLeft';
  1551. }
  1552. }
  1553. },
  1554. _getActiveTextColor: function _getActiveTextColor(context) {
  1555. if (!context.color) {
  1556. return;
  1557. }
  1558. var match = this._getActiveTextColor_Checker(context.color);
  1559. if (match) {
  1560. context.color = match;
  1561. }
  1562. },
  1563. _getActiveTextColor_Checker: function _getActiveTextColor_Checker(rgbColor) {
  1564. var match = null;
  1565. if (rgbColor) {
  1566. var colors = this._getTextColorChoices();
  1567. var hex = this._rgbToHex(rgbColor);
  1568. for (var _iterator3 = colors, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) {
  1569. var _ref3;
  1570. if (_isArray3) {
  1571. if (_i3 >= _iterator3.length) break;
  1572. _ref3 = _iterator3[_i3++];
  1573. } else {
  1574. _i3 = _iterator3.next();
  1575. if (_i3.done) break;
  1576. _ref3 = _i3.value;
  1577. }
  1578. var color = _ref3;
  1579. // Find key corresponding to the node's color value.
  1580. if (hex && hex === color.hexValue) {
  1581. match = color.id;
  1582. break;
  1583. }
  1584. }
  1585. //If the color is not found then it is a customColor
  1586. if (!match && hex) {
  1587. match = this.colorsService.getColorClassName(hex);
  1588. }
  1589. }
  1590. return match;
  1591. },
  1592. _getActiveFontSize: function _getActiveFontSize(context) {
  1593. if (!context['font-size']) {
  1594. return;
  1595. }
  1596. if (this.isResponsive) {
  1597. context['font-size'] = 'auto';
  1598. }
  1599. },
  1600. _rgbToHex: function _rgbToHex(rgb) {
  1601. rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);
  1602. return rgb && rgb.length === 4 ? '#' + ('0' + parseInt(rgb[1], 10).toString(16)).slice(-2) + ('0' + parseInt(rgb[2], 10).toString(16)).slice(-2) + ('0' + parseInt(rgb[3], 10).toString(16)).slice(-2) : '';
  1603. },
  1604. _getTextColorChoices: function _getTextColorChoices() {
  1605. return this.colorsService.getDashboardColorSet().filter(function (item) {
  1606. return item.id !== 'transparent';
  1607. });
  1608. },
  1609. _copyTextStyles: function _copyTextStyles($from, $to) {
  1610. var hexColor = this._rgbToHex($from.css('color'));
  1611. var fontId = this._convertCssToFontId($from.css('font-family'))[0];
  1612. $to.removeClass();
  1613. var colorClass = this._findColorIdFromHex(hexColor);
  1614. if (colorClass) {
  1615. $to.addClass(colorClass);
  1616. }
  1617. var fontFamilyItem = this._findFontFamilyItemFromValue(fontId, this._fontFamilyChoices);
  1618. if (fontFamilyItem && fontFamilyItem.cssClass) {
  1619. $to.addClass(fontFamilyItem.cssClass);
  1620. }
  1621. if (!this.isResponsive) {
  1622. $to.css({
  1623. 'font-size': $from.css('font-size')
  1624. });
  1625. }
  1626. },
  1627. /**
  1628. * Returns a refined clone of the current selection range.
  1629. * Useful for Range.compareBoundaryPoints calls.
  1630. */
  1631. _getRefinedSelectionRange: function _getRefinedSelectionRange() {
  1632. var selRange = window.getSelection().getRangeAt(0).cloneRange();
  1633. var selStartEl = selRange.startContainer;
  1634. var selStartOffset = selStartEl.firstChild ? 0 : selRange.startOffset;
  1635. while (selStartEl.firstChild) {
  1636. selStartEl = selStartEl.firstChild;
  1637. }
  1638. // Summernote has a patch that inserts bogus chars when styling with no selection
  1639. // Range.compareBoundaryPoints will produce unexpected results if there is more than one of them on a boundary.
  1640. // Logged https://github.com/summernote/summernote/issues/3035, this is a workaround for now.
  1641. var ZERO_WIDTH_SPACE = '\uFEFF';
  1642. if (selStartOffset > 0 && selStartEl.textContent[selStartOffset - 1] === ZERO_WIDTH_SPACE) {
  1643. selStartOffset -= 1;
  1644. if (selStartOffset === 0) {
  1645. var textNodeWalker = document.createTreeWalker($(selStartEl).closest('p, li')[0], window.NodeFilter.SHOW_TEXT);
  1646. while (selStartEl !== textNodeWalker.nextNode()) {}
  1647. var previousTextNode = textNodeWalker.previousNode();
  1648. while (previousTextNode && previousTextNode.textContent[previousTextNode.textContent.length - 1] === ZERO_WIDTH_SPACE) {
  1649. previousTextNode.textContent = previousTextNode.textContent.slice(0, -1);
  1650. if (previousTextNode.parentNode.innerText === '') {
  1651. var toRemove = previousTextNode.parentNode;
  1652. previousTextNode = textNodeWalker.previousNode();
  1653. toRemove.remove();
  1654. }
  1655. }
  1656. }
  1657. }
  1658. selRange.setStart(selStartEl, selStartOffset);
  1659. return selRange;
  1660. },
  1661. _isNodeStartSelected: function _isNodeStartSelected(node, selRange) {
  1662. while (node.firstChild) {
  1663. node = node.firstChild;
  1664. }
  1665. var paragraphRange = document.createRange();
  1666. paragraphRange.setStart(node, 0);
  1667. return selRange.compareBoundaryPoints(window.Range.START_TO_START, paragraphRange) !== 1;
  1668. },
  1669. _styleListItems: function _styleListItems() {
  1670. var _this7 = this;
  1671. var listItems = this.getSelectedListItems();
  1672. if (listItems.length) {
  1673. var selRange = this._getRefinedSelectionRange();
  1674. listItems.forEach(function (node) {
  1675. var $node = $(node);
  1676. if (_this7._isNodeStartSelected(node, selRange)) {
  1677. // The li will be styled; Protect li children against li styles before applying them.
  1678. $node.contents().each(function (index, childNode) {
  1679. var $childNode = $(childNode);
  1680. if (childNode.nodeType === window.Node.TEXT_NODE) {
  1681. var $span = $('<span></span>');
  1682. _this7._copyTextStyles($node, $span);
  1683. $childNode.wrap($span);
  1684. } else {
  1685. _this7._copyTextStyles($childNode, $childNode);
  1686. }
  1687. });
  1688. while (node.firstChild) {
  1689. node = node.firstChild;
  1690. }
  1691. _this7._copyTextStyles($(node.parentNode), $node);
  1692. }
  1693. });
  1694. }
  1695. },
  1696. getSelectedListItems: function getSelectedListItems() {
  1697. return this.getSelectedParagraphs().filter(function (node) {
  1698. return node.nodeName === 'LI';
  1699. });
  1700. },
  1701. getSelectedParagraphs: function getSelectedParagraphs() {
  1702. var selection = window.getSelection();
  1703. if (!selection.rangeCount) {
  1704. return [];
  1705. }
  1706. var range = selection.getRangeAt(0);
  1707. if (range.startContainer === this.getEditingArea()[0]) {
  1708. // Happens on IE if selection was made with ctrl + a (select all)
  1709. return this.getEditingArea().find('p, li').toArray();
  1710. }
  1711. var start = $(range.startContainer).closest('p, li').get(0);
  1712. var end = $(range.endContainer).closest('p, li').get(0);
  1713. if (start === end) {
  1714. return start ? [start] : [];
  1715. }
  1716. var nodes = [];
  1717. var inRange = false;
  1718. $(range.commonAncestorContainer).find('p, li').each(function () {
  1719. if (this === start) {
  1720. inRange = true;
  1721. }
  1722. if (inRange) {
  1723. nodes.push(this);
  1724. }
  1725. if (this === end) {
  1726. inRange = false;
  1727. }
  1728. });
  1729. return nodes;
  1730. },
  1731. saveSelection: function saveSelection() {
  1732. if (window.getSelection) {
  1733. var sel = window.getSelection();
  1734. if (sel.getRangeAt && sel.rangeCount) {
  1735. return sel.getRangeAt(0);
  1736. }
  1737. } else if (document.selection && document.selection.createRange) {
  1738. return document.selection.createRange();
  1739. }
  1740. return null;
  1741. },
  1742. // IE11 throws error 800a025e on removeAllRanges in certain cases
  1743. _clearSelection: function _clearSelection(selection) {
  1744. if (!BrowserUtils.isIE11() || selection.rangeCount > 0 && selection.getRangeAt(0).getClientRects().length > 0) {
  1745. selection.removeAllRanges();
  1746. }
  1747. },
  1748. restoreSelection: function restoreSelection(range) {
  1749. if (range) {
  1750. if (window.getSelection) {
  1751. var sel = window.getSelection();
  1752. this._clearSelection(sel);
  1753. sel.addRange(range);
  1754. } else if (document.selection && range.select) {
  1755. range.select();
  1756. }
  1757. }
  1758. },
  1759. hasContent: function hasContent() {
  1760. var $editingArea = this.getEditingArea();
  1761. return !!$editingArea.text().length || !!$editingArea.find('li').length;
  1762. },
  1763. hasText: function hasText() {
  1764. return !!this.getEditingArea().text().length;
  1765. }
  1766. });
  1767. TextEditor.cleanContentElements = cleanContentElements;
  1768. TextEditor.getTextHTML = getTextHTML;
  1769. //Make sure that the ID here matches css class name.
  1770. TextEditor.FONTFAMILY_CHOICES = [{
  1771. 'value': 'anton',
  1772. 'label': 'Anton',
  1773. 'cssClass': 'rt_fontFamilyAnton'
  1774. }, {
  1775. 'value': 'cherrycreamsoda',
  1776. 'label': 'Cherry Cream Soda',
  1777. 'cssClass': 'rt_fontFamilyCherryCreamSoda'
  1778. }, {
  1779. 'value': 'condiment',
  1780. 'label': 'Condiment',
  1781. 'cssClass': 'rt_fontFamilyCondiment-Regular'
  1782. }, {
  1783. 'value': 'didactgothic',
  1784. 'label': 'Didact Gothic',
  1785. 'cssClass': 'rt_fontFamilyDidactGothic'
  1786. }, {
  1787. 'value': 'euphoriascript',
  1788. 'label': 'Euphoria Script',
  1789. 'cssClass': 'rt_fontFamilyEuphoriaScript-Regular'
  1790. }, {
  1791. 'value': 'helveticaneueforibm',
  1792. 'label': 'Helvetica Neue',
  1793. 'cssClass': 'rt_fontFamilyHelvNeueforIBM-Regular',
  1794. 'subCssClasses': [{
  1795. 'value': 'light',
  1796. 'label': resources.get('textFontFamilySubClassLight'),
  1797. 'cssClass': 'rt_fontFamilyHelvNeueforIBM-Light'
  1798. }, {
  1799. 'value': 'lightitalic',
  1800. 'label': resources.get('textFontFamilySubClassLightItalic'),
  1801. 'cssClass': 'rt_fontFamilyHelvNeueforIBM-LightItalic'
  1802. }, {
  1803. 'value': 'regular',
  1804. 'label': resources.get('textFontFamilySubClassRegular'),
  1805. 'cssClass': 'rt_fontFamilyHelvNeueforIBM-Regular'
  1806. }, {
  1807. 'value': 'italic',
  1808. 'label': resources.get('textFontFamilySubClassItalic'),
  1809. 'cssClass': 'rt_fontFamilyHelvNeueforIBM-Italic'
  1810. }, {
  1811. 'value': 'medium',
  1812. 'label': resources.get('textFontFamilySubClassMedium'),
  1813. 'cssClass': 'rt_fontFamilyHelvNeueforIBM-Medium'
  1814. }, {
  1815. 'value': 'mediumitalic',
  1816. 'label': resources.get('textFontFamilySubClassMediumItalic'),
  1817. 'cssClass': 'rt_fontFamilyHelvNeueforIBM-MediumItalic'
  1818. }, {
  1819. 'value': 'bold',
  1820. 'label': resources.get('textFontFamilySubClassBold'),
  1821. 'cssClass': 'rt_fontFamilyHelvNeueforIBM-Bold'
  1822. }, {
  1823. 'value': 'bolditalic',
  1824. 'label': resources.get('textFontFamilySubClassBoldItalic'),
  1825. 'cssClass': 'rt_fontFamilyHelvNeueforIBM-BoldItalic'
  1826. }]
  1827. }, {
  1828. 'value': 'kaushanscript',
  1829. 'label': 'Kaushan Script',
  1830. 'cssClass': 'rt_fontFamilyKaushanScript-Regular'
  1831. }, {
  1832. 'value': 'lobster',
  1833. 'label': 'Lobster',
  1834. 'cssClass': 'rt_fontFamilyLobster'
  1835. }, {
  1836. 'value': 'merriweather',
  1837. 'label': 'Merriweather',
  1838. 'cssClass': 'rt_fontFamilyMerriweather-Regular',
  1839. 'subCssClasses': [{
  1840. 'value': 'italic',
  1841. 'label': resources.get('textFontFamilySubClassItalic'),
  1842. 'cssClass': 'rt_fontFamilyMerriweather-Italic'
  1843. }, {
  1844. 'value': 'light',
  1845. 'label': resources.get('textFontFamilySubClassLight'),
  1846. 'cssClass': 'rt_fontFamilyMerriweather-Light'
  1847. }, {
  1848. 'value': 'regular',
  1849. 'label': resources.get('textFontFamilySubClassRegular'),
  1850. 'cssClass': 'rt_fontFamilyMerriweather-Regular'
  1851. }, {
  1852. 'value': 'bold',
  1853. 'label': resources.get('textFontFamilySubClassBold'),
  1854. 'cssClass': 'rt_fontFamilyMerriweather-Bold'
  1855. }, {
  1856. 'value': 'bolditalic',
  1857. 'label': resources.get('textFontFamilySubClassBoldItalic'),
  1858. 'cssClass': 'rt_fontFamilyMerriweather-BoldItalic'
  1859. }, {
  1860. 'value': 'black',
  1861. 'label': resources.get('textFontFamilySubClassBlack'),
  1862. 'cssClass': 'rt_fontFamilyMerriweather-Black'
  1863. }, {
  1864. 'value': 'lightitalic',
  1865. 'label': resources.get('textFontFamilySubClassLightItalic'),
  1866. 'cssClass': 'rt_fontFamilyMerriweather-LightItalic'
  1867. }, {
  1868. 'value': 'heavyitalic',
  1869. 'label': resources.get('textFontFamilySubClassHeavyItalic'),
  1870. 'cssClass': 'rt_fontFamilyMerriweather-HeavyItalic'
  1871. }]
  1872. }, {
  1873. 'value': 'merriweathersans',
  1874. 'label': 'Merriweather Sans',
  1875. 'cssClass': 'rt_fontFamilyMerriweatherSans-Regular',
  1876. 'subCssClasses': [{
  1877. 'value': 'italic',
  1878. 'label': resources.get('textFontFamilySubClassItalic'),
  1879. 'cssClass': 'rt_fontFamilyMerriweatherSans-Italic'
  1880. }, {
  1881. 'value': 'light',
  1882. 'label': resources.get('textFontFamilySubClassLight'),
  1883. 'cssClass': 'rt_fontFamilyMerriweatherSans-Light'
  1884. }, {
  1885. 'value': 'lightitalic',
  1886. 'label': resources.get('textFontFamilySubClassLightItalic'),
  1887. 'cssClass': 'rt_fontFamilyMerriweatherSans-LightItalic'
  1888. }, {
  1889. 'value': 'regular',
  1890. 'label': resources.get('textFontFamilySubClassRegular'),
  1891. 'cssClass': 'rt_fontFamilyMerriweatherSans-Regular'
  1892. }, {
  1893. 'value': 'bold',
  1894. 'label': resources.get('textFontFamilySubClassBold'),
  1895. 'cssClass': 'rt_fontFamilyMerriweatherSans-Bold'
  1896. }, {
  1897. 'value': 'bolditalic',
  1898. 'label': resources.get('textFontFamilySubClassBoldItalic'),
  1899. 'cssClass': 'rt_fontFamilyMerriweatherSans-BoldItalic'
  1900. }, {
  1901. 'value': 'extrabold',
  1902. 'label': resources.get('textFontFamilySubClassExtraBold'),
  1903. 'cssClass': 'rt_fontFamilyMerriweatherSans-ExtraBold'
  1904. }, {
  1905. 'value': 'extrabolditalic',
  1906. 'label': resources.get('textFontFamilySubClassExtraBoldItalic'),
  1907. 'cssClass': 'rt_fontFamilyMerriweatherSans-ExtraBoldItalic'
  1908. }]
  1909. }, {
  1910. 'value': 'monoton',
  1911. 'label': 'Monoton',
  1912. 'cssClass': 'rt_fontFamilyMonoton-Regular'
  1913. }, {
  1914. 'value': 'montserrat',
  1915. 'label': 'Montserrat',
  1916. 'cssClass': 'rt_fontFamilyMontserrat-Regular',
  1917. 'subCssClasses': [{
  1918. 'value': 'hairline',
  1919. 'label': resources.get('textFontFamilySubClassHairline'),
  1920. 'cssClass': 'rt_fontFamilyMontserrat-Hairline'
  1921. }, {
  1922. 'value': 'light',
  1923. 'label': resources.get('textFontFamilySubClassLight'),
  1924. 'cssClass': 'rt_fontFamilyMontserrat-Light'
  1925. }, {
  1926. 'value': 'regular',
  1927. 'label': resources.get('textFontFamilySubClassRegular'),
  1928. 'cssClass': 'rt_fontFamilyMontserrat-Regular'
  1929. }, {
  1930. 'value': 'bold',
  1931. 'label': resources.get('textFontFamilySubClassBold'),
  1932. 'cssClass': 'rt_fontFamilyMontserrat-Bold'
  1933. }, {
  1934. 'value': 'black',
  1935. 'label': resources.get('textFontFamilySubClassBlack'),
  1936. 'cssClass': 'rt_fontFamilyMontserrat-Black'
  1937. }]
  1938. }, {
  1939. 'value': 'opensanscondensed',
  1940. 'label': 'Open Sans Condensed',
  1941. 'cssClass': 'rt_fontFamilyOpenSansCondensed-Light',
  1942. 'subCssClasses': [{
  1943. 'value': 'light',
  1944. 'label': resources.get('textFontFamilySubClassLight'),
  1945. 'cssClass': 'rt_fontFamilyOpenSansCondensed-Light'
  1946. }, {
  1947. 'value': 'lightitalic',
  1948. 'label': resources.get('textFontFamilySubClassLightItalic'),
  1949. 'cssClass': 'rt_fontFamilyOpenSansCondensed-LightItalic'
  1950. }, {
  1951. 'value': 'bold',
  1952. 'label': resources.get('textFontFamilySubClassBold'),
  1953. 'cssClass': 'rt_fontFamilyOpenSansCondensed-Bold'
  1954. }]
  1955. }, {
  1956. 'value': 'oswald',
  1957. 'label': 'Oswald',
  1958. 'cssClass': 'rt_fontFamilyOswald-Regular',
  1959. 'subCssClasses': [{
  1960. 'value': 'extralight',
  1961. 'label': resources.get('textFontFamilySubClassExtraLight'),
  1962. 'cssClass': 'rt_fontFamilyOswald-ExtraLight'
  1963. }, {
  1964. 'value': 'extralightitalic',
  1965. 'label': resources.get('textFontFamilySubClassExtraLightItalic'),
  1966. 'cssClass': 'rt_fontFamilyOswald-ExtraLightItalic'
  1967. }, {
  1968. 'value': 'light',
  1969. 'label': resources.get('textFontFamilySubClassLight'),
  1970. 'cssClass': 'rt_fontFamilyOswald-Light'
  1971. }, {
  1972. 'value': 'lightitalic',
  1973. 'label': resources.get('textFontFamilySubClassLightItalic'),
  1974. 'cssClass': 'rt_fontFamilyOswald-LightItalic'
  1975. }, {
  1976. 'value': 'regular',
  1977. 'label': resources.get('textFontFamilySubClassRegular'),
  1978. 'cssClass': 'rt_fontFamilyOswald-Regular'
  1979. }, {
  1980. 'value': 'regularitalic',
  1981. 'label': resources.get('textFontFamilySubClassRegularItalic'),
  1982. 'cssClass': 'rt_fontFamilyOswald-RegularItalic'
  1983. }, {
  1984. 'value': 'medium',
  1985. 'label': resources.get('textFontFamilySubClassMedium'),
  1986. 'cssClass': 'rt_fontFamilyOswald-Medium'
  1987. }, {
  1988. 'value': 'mediumitalic',
  1989. 'label': resources.get('textFontFamilySubClassMediumItalic'),
  1990. 'cssClass': 'rt_fontFamilyOswald-MediumItalic'
  1991. }, {
  1992. 'value': 'demibold',
  1993. 'label': resources.get('textFontFamilySubClassDemiBold'),
  1994. 'cssClass': 'rt_fontFamilyOswald-DemiBold'
  1995. }, {
  1996. 'value': 'demibolditalic',
  1997. 'label': resources.get('textFontFamilySubClassDemiBoldItalic'),
  1998. 'cssClass': 'rt_fontFamilyOswald-DemiBoldItalic'
  1999. }, {
  2000. 'value': 'bold',
  2001. 'label': resources.get('textFontFamilySubClassBold'),
  2002. 'cssClass': 'rt_fontFamilyOswald-Bold'
  2003. }, {
  2004. 'value': 'bolditalic',
  2005. 'label': resources.get('textFontFamilySubClassBoldItalic'),
  2006. 'cssClass': 'rt_fontFamilyOswald-BoldItalic'
  2007. }, {
  2008. 'value': 'heavy',
  2009. 'label': resources.get('textFontFamilySubClassHeavy'),
  2010. 'cssClass': 'rt_fontFamilyOswald-Heavy'
  2011. }, {
  2012. 'value': 'heavyitalic',
  2013. 'label': resources.get('textFontFamilySubClassHeavyItalic'),
  2014. 'cssClass': 'rt_fontFamilyOswald-HeavyItalic'
  2015. }, {
  2016. 'value': 'stencil',
  2017. 'label': resources.get('textFontFamilySubClassStencil'),
  2018. 'cssClass': 'rt_fontFamilyOswald-Stencil'
  2019. }]
  2020. }, {
  2021. 'value': 'playball',
  2022. 'label': 'Playball',
  2023. 'cssClass': 'rt_fontFamilyPlayball'
  2024. }, {
  2025. 'value': 'ibmplexsans',
  2026. 'label': 'IBM Plex',
  2027. default: true,
  2028. 'cssClass': 'rt_fontFamilyPlex-Regular',
  2029. 'subCssClasses': [{
  2030. 'value': 'light',
  2031. 'label': resources.get('textFontFamilySubClassLight'),
  2032. 'cssClass': 'rt_fontFamilyPlex-Light'
  2033. }, {
  2034. 'value': 'lightitalic',
  2035. 'label': resources.get('textFontFamilySubClassLightItalic'),
  2036. 'cssClass': 'rt_fontFamilyPlex-LightItalic'
  2037. }, {
  2038. 'value': 'regular',
  2039. 'label': resources.get('textFontFamilySubClassRegular'),
  2040. 'cssClass': 'rt_fontFamilyPlex-Regular'
  2041. }, {
  2042. 'value': 'italic',
  2043. 'label': resources.get('textFontFamilySubClassItalic'),
  2044. 'cssClass': 'rt_fontFamilyPlex-Italic'
  2045. }, {
  2046. 'value': 'medium',
  2047. 'label': resources.get('textFontFamilySubClassMedium'),
  2048. 'cssClass': 'rt_fontFamilyPlex-Medium'
  2049. }, {
  2050. 'value': 'mediumitalic',
  2051. 'label': resources.get('textFontFamilySubClassMediumItalic'),
  2052. 'cssClass': 'rt_fontFamilyPlex-MediumItalic'
  2053. }, {
  2054. 'value': 'bold',
  2055. 'label': resources.get('textFontFamilySubClassBold'),
  2056. 'cssClass': 'rt_fontFamilyPlex-Bold'
  2057. }, {
  2058. 'value': 'bolditalic',
  2059. 'label': resources.get('textFontFamilySubClassBoldItalic'),
  2060. 'cssClass': 'rt_fontFamilyPlex-BoldItalic'
  2061. }]
  2062. }, {
  2063. 'value': 'roboto',
  2064. 'label': 'Roboto',
  2065. 'cssClass': 'rt_fontFamilyRoboto-Regular',
  2066. 'subCssClasses': [{
  2067. 'value': 'thin',
  2068. 'label': resources.get('textFontFamilySubClassThin'),
  2069. 'cssClass': 'rt_fontFamilyRoboto-Thin'
  2070. }, {
  2071. 'value': 'thinitalic',
  2072. 'label': resources.get('textFontFamilySubClassThinItalic'),
  2073. 'cssClass': 'rt_fontFamilyRoboto-ThinItalic'
  2074. }, {
  2075. 'value': 'light',
  2076. 'label': resources.get('textFontFamilySubClassLight'),
  2077. 'cssClass': 'rt_fontFamilyRoboto-Light'
  2078. }, {
  2079. 'value': 'lightitalic',
  2080. 'label': resources.get('textFontFamilySubClassLightItalic'),
  2081. 'cssClass': 'rt_fontFamilyRoboto-LightItalic'
  2082. }, {
  2083. 'value': 'regular',
  2084. 'label': resources.get('textFontFamilySubClassRegular'),
  2085. 'cssClass': 'rt_fontFamilyRoboto-Regular'
  2086. }, {
  2087. 'value': 'italic',
  2088. 'label': resources.get('textFontFamilySubClassItalic'),
  2089. 'cssClass': 'rt_fontFamilyRoboto-Italic'
  2090. }, {
  2091. 'value': 'medium',
  2092. 'label': resources.get('textFontFamilySubClassMedium'),
  2093. 'cssClass': 'rt_fontFamilyRoboto-Medium'
  2094. }, {
  2095. 'value': 'mediumitalic',
  2096. 'label': resources.get('textFontFamilySubClassMediumItalic'),
  2097. 'cssClass': 'rt_fontFamilyRoboto-MediumItalic'
  2098. }, {
  2099. 'value': 'bold',
  2100. 'label': resources.get('textFontFamilySubClassBold'),
  2101. 'cssClass': 'rt_fontFamilyRoboto-Bold'
  2102. }, {
  2103. 'value': 'bolditalic',
  2104. 'label': resources.get('textFontFamilySubClassBoldItalic'),
  2105. 'cssClass': 'rt_fontFamilyRoboto-BoldItalic'
  2106. }, {
  2107. 'value': 'black',
  2108. 'label': resources.get('textFontFamilySubClassBlack'),
  2109. 'cssClass': 'rt_fontFamilyRoboto-Black'
  2110. }, {
  2111. 'value': 'blackitalic',
  2112. 'label': resources.get('textFontFamilySubClassBlackItalic'),
  2113. 'cssClass': 'rt_fontFamilyRoboto-BlackItalic'
  2114. }]
  2115. }, {
  2116. 'value': 'sortsmillgoudy',
  2117. 'label': 'Sorts Mill Goudy',
  2118. 'cssClass': 'rt_fontFamilySortsMillGoudy-Regular',
  2119. 'subCssClasses': [{
  2120. 'value': 'regular',
  2121. 'label': resources.get('textFontFamilySubClassRegular'),
  2122. 'cssClass': 'rt_fontFamilySortsMillGoudy-Regular'
  2123. }, {
  2124. 'value': 'italic',
  2125. 'label': resources.get('textFontFamilySubClassItalic'),
  2126. 'cssClass': 'rt_fontFamilySortsMillGoudy-Italic'
  2127. }]
  2128. }, {
  2129. 'value': 'specialelite',
  2130. 'label': 'Special Elite',
  2131. 'cssClass': 'rt_fontFamilySpecialElite'
  2132. }];
  2133. TextEditor.INT_FONTSIZE_CHOICES = [{
  2134. 'value': '8',
  2135. 'label': '8'
  2136. }, {
  2137. 'value': '9',
  2138. 'label': '9'
  2139. }, {
  2140. 'value': '10',
  2141. 'label': '10'
  2142. }, {
  2143. 'value': '11',
  2144. 'label': '11'
  2145. }, {
  2146. 'value': '12',
  2147. 'label': '12'
  2148. }, {
  2149. 'value': '14',
  2150. 'label': '14'
  2151. }, {
  2152. 'value': '16',
  2153. 'label': '16'
  2154. }, {
  2155. 'value': '18',
  2156. 'label': '18'
  2157. }, {
  2158. 'value': '20',
  2159. 'label': '20'
  2160. }, {
  2161. 'value': '22',
  2162. 'label': '22'
  2163. }, {
  2164. 'value': '24',
  2165. 'label': '24'
  2166. }, {
  2167. 'value': '26',
  2168. 'label': '26'
  2169. }, {
  2170. 'value': '28',
  2171. 'label': '28'
  2172. }, {
  2173. 'value': '30',
  2174. 'label': '30'
  2175. }, {
  2176. 'value': '36',
  2177. 'label': '36'
  2178. }, {
  2179. 'value': '48',
  2180. 'label': '48'
  2181. }, {
  2182. 'value': '60',
  2183. 'label': '60'
  2184. }, {
  2185. 'value': '72',
  2186. 'label': '72'
  2187. }, {
  2188. 'value': '96',
  2189. 'label': '96'
  2190. }, {
  2191. 'value': '120',
  2192. 'label': '120'
  2193. }, {
  2194. 'value': '170',
  2195. 'label': '170'
  2196. }, {
  2197. 'value': '230',
  2198. 'label': '230'
  2199. }];
  2200. TextEditor.FONTSIZE_CHOICES = [{
  2201. 'value': 'auto',
  2202. 'label': resources.get('textAutoFontSize')
  2203. }].concat(TextEditor.INT_FONTSIZE_CHOICES);
  2204. return TextEditor;
  2205. });
  2206. //# sourceMappingURL=TextEditor.js.map