HtmlEditor2.html 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5. <title>The source code</title>
  6. <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  7. <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  8. <style type="text/css">
  9. .highlight { display: block; background-color: #ddd; }
  10. </style>
  11. <script type="text/javascript">
  12. function highlight() {
  13. document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
  14. }
  15. </script>
  16. </head>
  17. <body onload="prettyPrint(); highlight();">
  18. <pre class="prettyprint lang-js"><span id='Ext-form-field-HtmlEditor'>/**
  19. </span> * Provides a lightweight HTML Editor component. Some toolbar features are not supported by Safari and will be
  20. * automatically hidden when needed. These are noted in the config options where appropriate.
  21. *
  22. * The editor's toolbar buttons have tooltips defined in the {@link #buttonTips} property, but they are not
  23. * enabled by default unless the global {@link Ext.tip.QuickTipManager} singleton is
  24. * {@link Ext.tip.QuickTipManager#init initialized}.
  25. *
  26. * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an
  27. * Editor within any element that has display set to 'none' can cause problems in Safari and Firefox due to their
  28. * default iframe reloading bugs.
  29. *
  30. * # Example usage
  31. *
  32. * Simple example rendered with default options:
  33. *
  34. * @example
  35. * Ext.tip.QuickTipManager.init(); // enable tooltips
  36. * Ext.create('Ext.form.HtmlEditor', {
  37. * width: 580,
  38. * height: 250,
  39. * renderTo: Ext.getBody()
  40. * });
  41. *
  42. * Passed via xtype into a container and with custom options:
  43. *
  44. * @example
  45. * Ext.tip.QuickTipManager.init(); // enable tooltips
  46. * new Ext.panel.Panel({
  47. * title: 'HTML Editor',
  48. * renderTo: Ext.getBody(),
  49. * width: 550,
  50. * height: 250,
  51. * frame: true,
  52. * layout: 'fit',
  53. * items: {
  54. * xtype: 'htmleditor',
  55. * enableColors: false,
  56. * enableAlignments: false
  57. * }
  58. * });
  59. *
  60. * # Reflow issues
  61. *
  62. * In some browsers, a layout reflow will cause the underlying editor iframe to be reset. This
  63. * is most commonly seen when using the editor in collapsed panels with animation. In these cases
  64. * it is best to avoid animation. More information can be found here: https://bugzilla.mozilla.org/show_bug.cgi?id=90268
  65. */
  66. Ext.define('Ext.form.field.HtmlEditor', {
  67. extend:'Ext.Component',
  68. mixins: {
  69. labelable: 'Ext.form.Labelable',
  70. field: 'Ext.form.field.Field'
  71. },
  72. alias: 'widget.htmleditor',
  73. alternateClassName: 'Ext.form.HtmlEditor',
  74. requires: [
  75. 'Ext.tip.QuickTipManager',
  76. 'Ext.picker.Color',
  77. 'Ext.toolbar.Item',
  78. 'Ext.toolbar.Toolbar',
  79. 'Ext.util.Format',
  80. 'Ext.layout.component.field.HtmlEditor'
  81. ],
  82. childEls: [
  83. 'iframeEl', 'textareaEl'
  84. ],
  85. fieldSubTpl: [
  86. '{beforeTextAreaTpl}',
  87. '&lt;textarea id=&quot;{cmpId}-textareaEl&quot; name=&quot;{name}&quot; tabIndex=&quot;-1&quot; {inputAttrTpl}',
  88. ' class=&quot;{textareaCls}&quot; style=&quot;{size}&quot; autocomplete=&quot;off&quot;&gt;',
  89. '{[Ext.util.Format.htmlEncode(values.value)]}',
  90. '&lt;/textarea&gt;',
  91. '{afterTextAreaTpl}',
  92. '{beforeIFrameTpl}',
  93. '&lt;iframe id=&quot;{cmpId}-iframeEl&quot; name=&quot;{iframeName}&quot; frameBorder=&quot;0&quot; {iframeAttrTpl}',
  94. ' style=&quot;overflow:auto;{size}&quot; src=&quot;{iframeSrc}&quot;&gt;&lt;/iframe&gt;',
  95. '{afterIFrameTpl}',
  96. {
  97. disableFormats: true
  98. }
  99. ],
  100. subTplInsertions: [
  101. <span id='Ext-form-field-HtmlEditor-cfg-beforeTextAreaTpl'> /**
  102. </span> * @cfg {String/Array/Ext.XTemplate} beforeTextAreaTpl
  103. * An optional string or `XTemplate` configuration to insert in the field markup
  104. * before the textarea element. If an `XTemplate` is used, the component's
  105. * {@link Ext.form.field.Base#getSubTplData subTpl data} serves as the context.
  106. */
  107. 'beforeTextAreaTpl',
  108. <span id='Ext-form-field-HtmlEditor-cfg-afterTextAreaTpl'> /**
  109. </span> * @cfg {String/Array/Ext.XTemplate} afterTextAreaTpl
  110. * An optional string or `XTemplate` configuration to insert in the field markup
  111. * after the textarea element. If an `XTemplate` is used, the component's
  112. * {@link Ext.form.field.Base#getSubTplData subTpl data} serves as the context.
  113. */
  114. 'afterTextAreaTpl',
  115. <span id='Ext-form-field-HtmlEditor-cfg-beforeIFrameTpl'> /**
  116. </span> * @cfg {String/Array/Ext.XTemplate} beforeIFrameTpl
  117. * An optional string or `XTemplate` configuration to insert in the field markup
  118. * before the iframe element. If an `XTemplate` is used, the component's
  119. * {@link Ext.form.field.Base#getSubTplData subTpl data} serves as the context.
  120. */
  121. 'beforeIFrameTpl',
  122. <span id='Ext-form-field-HtmlEditor-cfg-afterIFrameTpl'> /**
  123. </span> * @cfg {String/Array/Ext.XTemplate} afterIFrameTpl
  124. * An optional string or `XTemplate` configuration to insert in the field markup
  125. * after the iframe element. If an `XTemplate` is used, the component's
  126. * {@link Ext.form.field.Base#getSubTplData subTpl data} serves as the context.
  127. */
  128. 'afterIFrameTpl',
  129. <span id='Ext-form-field-HtmlEditor-cfg-iframeAttrTpl'> /**
  130. </span> * @cfg {String/Array/Ext.XTemplate} iframeAttrTpl
  131. * An optional string or `XTemplate` configuration to insert in the field markup
  132. * inside the iframe element (as attributes). If an `XTemplate` is used, the component's
  133. * {@link Ext.form.field.Base#getSubTplData subTpl data} serves as the context.
  134. */
  135. 'iframeAttrTpl',
  136. // inherited
  137. 'inputAttrTpl'
  138. ],
  139. <span id='Ext-form-field-HtmlEditor-cfg-enableFormat'> /**
  140. </span> * @cfg {Boolean} enableFormat
  141. * Enable the bold, italic and underline buttons
  142. */
  143. enableFormat : true,
  144. <span id='Ext-form-field-HtmlEditor-cfg-enableFontSize'> /**
  145. </span> * @cfg {Boolean} enableFontSize
  146. * Enable the increase/decrease font size buttons
  147. */
  148. enableFontSize : true,
  149. <span id='Ext-form-field-HtmlEditor-cfg-enableColors'> /**
  150. </span> * @cfg {Boolean} enableColors
  151. * Enable the fore/highlight color buttons
  152. */
  153. enableColors : true,
  154. <span id='Ext-form-field-HtmlEditor-cfg-enableAlignments'> /**
  155. </span> * @cfg {Boolean} enableAlignments
  156. * Enable the left, center, right alignment buttons
  157. */
  158. enableAlignments : true,
  159. <span id='Ext-form-field-HtmlEditor-cfg-enableLists'> /**
  160. </span> * @cfg {Boolean} enableLists
  161. * Enable the bullet and numbered list buttons. Not available in Safari.
  162. */
  163. enableLists : true,
  164. <span id='Ext-form-field-HtmlEditor-cfg-enableSourceEdit'> /**
  165. </span> * @cfg {Boolean} enableSourceEdit
  166. * Enable the switch to source edit button. Not available in Safari.
  167. */
  168. enableSourceEdit : true,
  169. <span id='Ext-form-field-HtmlEditor-cfg-enableLinks'> /**
  170. </span> * @cfg {Boolean} enableLinks
  171. * Enable the create link button. Not available in Safari.
  172. */
  173. enableLinks : true,
  174. <span id='Ext-form-field-HtmlEditor-cfg-enableFont'> /**
  175. </span> * @cfg {Boolean} enableFont
  176. * Enable font selection. Not available in Safari.
  177. */
  178. enableFont : true,
  179. //&lt;locale&gt;
  180. <span id='Ext-form-field-HtmlEditor-cfg-createLinkText'> /**
  181. </span> * @cfg {String} createLinkText
  182. * The default text for the create link prompt
  183. */
  184. createLinkText : 'Please enter the URL for the link:',
  185. //&lt;/locale&gt;
  186. <span id='Ext-form-field-HtmlEditor-cfg-defaultLinkValue'> /**
  187. </span> * @cfg {String} [defaultLinkValue='http://']
  188. * The default value for the create link prompt
  189. */
  190. defaultLinkValue : 'http:/'+'/',
  191. <span id='Ext-form-field-HtmlEditor-cfg-fontFamilies'> /**
  192. </span> * @cfg {String[]} fontFamilies
  193. * An array of available font families
  194. */
  195. fontFamilies : [
  196. 'Arial',
  197. 'Courier New',
  198. 'Tahoma',
  199. 'Times New Roman',
  200. 'Verdana'
  201. ],
  202. defaultFont: 'tahoma',
  203. <span id='Ext-form-field-HtmlEditor-cfg-defaultValue'> /**
  204. </span> * @cfg {String} defaultValue
  205. * A default value to be put into the editor to resolve focus issues.
  206. *
  207. * Defaults to (Non-breaking space) in Opera and IE6,
  208. * (Zero-width space) in all other browsers.
  209. */
  210. defaultValue: (Ext.isOpera || Ext.isIE6) ? '&amp;#160;' : '&amp;#8203;',
  211. editorWrapCls: Ext.baseCSSPrefix + 'html-editor-wrap',
  212. componentLayout: 'htmleditor',
  213. // private properties
  214. initialized : false,
  215. activated : false,
  216. sourceEditMode : false,
  217. iframePad:3,
  218. hideMode:'offsets',
  219. afterBodyEl: '&lt;/div&gt;',
  220. maskOnDisable: true,
  221. // private
  222. initComponent : function(){
  223. var me = this;
  224. me.addEvents(
  225. <span id='Ext-form-field-HtmlEditor-event-initialize'> /**
  226. </span> * @event initialize
  227. * Fires when the editor is fully initialized (including the iframe)
  228. * @param {Ext.form.field.HtmlEditor} this
  229. */
  230. 'initialize',
  231. <span id='Ext-form-field-HtmlEditor-event-activate'> /**
  232. </span> * @event activate
  233. * Fires when the editor is first receives the focus. Any insertion must wait until after this event.
  234. * @param {Ext.form.field.HtmlEditor} this
  235. */
  236. 'activate',
  237. <span id='Ext-form-field-HtmlEditor-event-beforesync'> /**
  238. </span> * @event beforesync
  239. * Fires before the textarea is updated with content from the editor iframe. Return false to cancel the
  240. * sync.
  241. * @param {Ext.form.field.HtmlEditor} this
  242. * @param {String} html
  243. */
  244. 'beforesync',
  245. <span id='Ext-form-field-HtmlEditor-event-beforepush'> /**
  246. </span> * @event beforepush
  247. * Fires before the iframe editor is updated with content from the textarea. Return false to cancel the
  248. * push.
  249. * @param {Ext.form.field.HtmlEditor} this
  250. * @param {String} html
  251. */
  252. 'beforepush',
  253. <span id='Ext-form-field-HtmlEditor-event-sync'> /**
  254. </span> * @event sync
  255. * Fires when the textarea is updated with content from the editor iframe.
  256. * @param {Ext.form.field.HtmlEditor} this
  257. * @param {String} html
  258. */
  259. 'sync',
  260. <span id='Ext-form-field-HtmlEditor-event-push'> /**
  261. </span> * @event push
  262. * Fires when the iframe editor is updated with content from the textarea.
  263. * @param {Ext.form.field.HtmlEditor} this
  264. * @param {String} html
  265. */
  266. 'push',
  267. <span id='Ext-form-field-HtmlEditor-event-editmodechange'> /**
  268. </span> * @event editmodechange
  269. * Fires when the editor switches edit modes
  270. * @param {Ext.form.field.HtmlEditor} this
  271. * @param {Boolean} sourceEdit True if source edit, false if standard editing.
  272. */
  273. 'editmodechange'
  274. );
  275. me.callParent(arguments);
  276. me.createToolbar(me);
  277. // Init mixins
  278. me.initLabelable();
  279. me.initField();
  280. },
  281. <span id='Ext-form-field-HtmlEditor-method-getRefItems'> /**
  282. </span> * @private
  283. * Must define this function to allow the Layout base class to collect all descendant layouts to be run.
  284. */
  285. getRefItems: function() {
  286. return [ this.toolbar ];
  287. },
  288. /*
  289. * Called when the editor creates its toolbar. Override this method if you need to
  290. * add custom toolbar buttons.
  291. * @param {Ext.form.field.HtmlEditor} editor
  292. * @protected
  293. */
  294. createToolbar : function(editor){
  295. var me = this,
  296. items = [], i,
  297. tipsEnabled = Ext.tip.QuickTipManager &amp;&amp; Ext.tip.QuickTipManager.isEnabled(),
  298. baseCSSPrefix = Ext.baseCSSPrefix,
  299. fontSelectItem, toolbar, undef;
  300. function btn(id, toggle, handler){
  301. return {
  302. itemId : id,
  303. cls : baseCSSPrefix + 'btn-icon',
  304. iconCls: baseCSSPrefix + 'edit-'+id,
  305. enableToggle:toggle !== false,
  306. scope: editor,
  307. handler:handler||editor.relayBtnCmd,
  308. clickEvent: 'mousedown',
  309. tooltip: tipsEnabled ? editor.buttonTips[id] || undef : undef,
  310. overflowText: editor.buttonTips[id].title || undef,
  311. tabIndex: -1
  312. };
  313. }
  314. if (me.enableFont &amp;&amp; !Ext.isSafari2) {
  315. fontSelectItem = Ext.widget('component', {
  316. renderTpl: [
  317. '&lt;select id=&quot;{id}-selectEl&quot; class=&quot;{cls}&quot;&gt;',
  318. '&lt;tpl for=&quot;fonts&quot;&gt;',
  319. '&lt;option value=&quot;{[values.toLowerCase()]}&quot; style=&quot;font-family:{.}&quot;&lt;tpl if=&quot;values.toLowerCase()==parent.defaultFont&quot;&gt; selected&lt;/tpl&gt;&gt;{.}&lt;/option&gt;',
  320. '&lt;/tpl&gt;',
  321. '&lt;/select&gt;'
  322. ],
  323. renderData: {
  324. cls: baseCSSPrefix + 'font-select',
  325. fonts: me.fontFamilies,
  326. defaultFont: me.defaultFont
  327. },
  328. childEls: ['selectEl'],
  329. afterRender: function() {
  330. me.fontSelect = this.selectEl;
  331. Ext.Component.prototype.afterRender.apply(this, arguments);
  332. },
  333. onDisable: function() {
  334. var selectEl = this.selectEl;
  335. if (selectEl) {
  336. selectEl.dom.disabled = true;
  337. }
  338. Ext.Component.prototype.onDisable.apply(this, arguments);
  339. },
  340. onEnable: function() {
  341. var selectEl = this.selectEl;
  342. if (selectEl) {
  343. selectEl.dom.disabled = false;
  344. }
  345. Ext.Component.prototype.onEnable.apply(this, arguments);
  346. },
  347. listeners: {
  348. change: function() {
  349. me.relayCmd('fontname', me.fontSelect.dom.value);
  350. me.deferFocus();
  351. },
  352. element: 'selectEl'
  353. }
  354. });
  355. items.push(
  356. fontSelectItem,
  357. '-'
  358. );
  359. }
  360. if (me.enableFormat) {
  361. items.push(
  362. btn('bold'),
  363. btn('italic'),
  364. btn('underline')
  365. );
  366. }
  367. if (me.enableFontSize) {
  368. items.push(
  369. '-',
  370. btn('increasefontsize', false, me.adjustFont),
  371. btn('decreasefontsize', false, me.adjustFont)
  372. );
  373. }
  374. if (me.enableColors) {
  375. items.push(
  376. '-', {
  377. itemId: 'forecolor',
  378. cls: baseCSSPrefix + 'btn-icon',
  379. iconCls: baseCSSPrefix + 'edit-forecolor',
  380. overflowText: editor.buttonTips.forecolor.title,
  381. tooltip: tipsEnabled ? editor.buttonTips.forecolor || undef : undef,
  382. tabIndex:-1,
  383. menu : Ext.widget('menu', {
  384. plain: true,
  385. items: [{
  386. xtype: 'colorpicker',
  387. allowReselect: true,
  388. focus: Ext.emptyFn,
  389. value: '000000',
  390. plain: true,
  391. clickEvent: 'mousedown',
  392. handler: function(cp, color) {
  393. me.execCmd('forecolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
  394. me.deferFocus();
  395. this.up('menu').hide();
  396. }
  397. }]
  398. })
  399. }, {
  400. itemId: 'backcolor',
  401. cls: baseCSSPrefix + 'btn-icon',
  402. iconCls: baseCSSPrefix + 'edit-backcolor',
  403. overflowText: editor.buttonTips.backcolor.title,
  404. tooltip: tipsEnabled ? editor.buttonTips.backcolor || undef : undef,
  405. tabIndex:-1,
  406. menu : Ext.widget('menu', {
  407. plain: true,
  408. items: [{
  409. xtype: 'colorpicker',
  410. focus: Ext.emptyFn,
  411. value: 'FFFFFF',
  412. plain: true,
  413. allowReselect: true,
  414. clickEvent: 'mousedown',
  415. handler: function(cp, color) {
  416. if (Ext.isGecko) {
  417. me.execCmd('useCSS', false);
  418. me.execCmd('hilitecolor', color);
  419. me.execCmd('useCSS', true);
  420. me.deferFocus();
  421. } else {
  422. me.execCmd(Ext.isOpera ? 'hilitecolor' : 'backcolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
  423. me.deferFocus();
  424. }
  425. this.up('menu').hide();
  426. }
  427. }]
  428. })
  429. }
  430. );
  431. }
  432. if (me.enableAlignments) {
  433. items.push(
  434. '-',
  435. btn('justifyleft'),
  436. btn('justifycenter'),
  437. btn('justifyright')
  438. );
  439. }
  440. if (!Ext.isSafari2) {
  441. if (me.enableLinks) {
  442. items.push(
  443. '-',
  444. btn('createlink', false, me.createLink)
  445. );
  446. }
  447. if (me.enableLists) {
  448. items.push(
  449. '-',
  450. btn('insertorderedlist'),
  451. btn('insertunorderedlist')
  452. );
  453. }
  454. if (me.enableSourceEdit) {
  455. items.push(
  456. '-',
  457. btn('sourceedit', true, function(btn){
  458. me.toggleSourceEdit(!me.sourceEditMode);
  459. })
  460. );
  461. }
  462. }
  463. // Everything starts disabled.
  464. for (i = 0; i &lt; items.length; i++) {
  465. if (items[i].itemId !== 'sourceedit') {
  466. items[i].disabled = true;
  467. }
  468. }
  469. // build the toolbar
  470. // Automatically rendered in AbstractComponent.afterRender's renderChildren call
  471. toolbar = Ext.widget('toolbar', {
  472. id: me.id + '-toolbar',
  473. ownerCt: me,
  474. cls: Ext.baseCSSPrefix + 'html-editor-tb',
  475. enableOverflow: true,
  476. items: items,
  477. ownerLayout: me.getComponentLayout(),
  478. // stop form submits
  479. listeners: {
  480. click: function(e){
  481. e.preventDefault();
  482. },
  483. element: 'el'
  484. }
  485. });
  486. me.toolbar = toolbar;
  487. },
  488. getMaskTarget: function(){
  489. return this.bodyEl;
  490. },
  491. <span id='Ext-form-field-HtmlEditor-method-setReadOnly'> /**
  492. </span> * Sets the read only state of this field.
  493. * @param {Boolean} readOnly Whether the field should be read only.
  494. */
  495. setReadOnly: function(readOnly) {
  496. var me = this,
  497. textareaEl = me.textareaEl,
  498. iframeEl = me.iframeEl,
  499. body;
  500. me.readOnly = readOnly;
  501. if (textareaEl) {
  502. textareaEl.dom.readOnly = readOnly;
  503. }
  504. if (me.initialized) {
  505. body = me.getEditorBody();
  506. if (Ext.isIE) {
  507. // Hide the iframe while setting contentEditable so it doesn't grab focus
  508. iframeEl.setDisplayed(false);
  509. body.contentEditable = !readOnly;
  510. iframeEl.setDisplayed(true);
  511. } else {
  512. me.setDesignMode(!readOnly);
  513. }
  514. if (body) {
  515. body.style.cursor = readOnly ? 'default' : 'text';
  516. }
  517. me.disableItems(readOnly);
  518. }
  519. },
  520. <span id='Ext-form-field-HtmlEditor-method-getDocMarkup'> /**
  521. </span> * Called when the editor initializes the iframe with HTML contents. Override this method if you
  522. * want to change the initialization markup of the iframe (e.g. to add stylesheets).
  523. *
  524. * **Note:** IE8-Standards has unwanted scroller behavior, so the default meta tag forces IE7 compatibility.
  525. * Also note that forcing IE7 mode works when the page is loaded normally, but if you are using IE's Web
  526. * Developer Tools to manually set the document mode, that will take precedence and override what this
  527. * code sets by default. This can be confusing when developing, but is not a user-facing issue.
  528. * @protected
  529. */
  530. getDocMarkup: function() {
  531. var me = this,
  532. h = me.iframeEl.getHeight() - me.iframePad * 2;
  533. return Ext.String.format('&lt;html&gt;&lt;head&gt;&lt;style type=&quot;text/css&quot;&gt;body{border:0;margin:0;padding:{0}px;height:{1}px;box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box;cursor:text}&lt;/style&gt;&lt;/head&gt;&lt;body&gt;&lt;/body&gt;&lt;/html&gt;', me.iframePad, h);
  534. },
  535. // private
  536. getEditorBody: function() {
  537. var doc = this.getDoc();
  538. return doc.body || doc.documentElement;
  539. },
  540. // private
  541. getDoc: function() {
  542. return (!Ext.isIE &amp;&amp; this.iframeEl.dom.contentDocument) || this.getWin().document;
  543. },
  544. // private
  545. getWin: function() {
  546. return Ext.isIE ? this.iframeEl.dom.contentWindow : window.frames[this.iframeEl.dom.name];
  547. },
  548. // Do the job of a container layout at this point even though we are not a Container.
  549. // TODO: Refactor as a Container.
  550. finishRenderChildren: function () {
  551. this.callParent();
  552. this.toolbar.finishRender();
  553. },
  554. // private
  555. onRender: function() {
  556. var me = this;
  557. me.callParent(arguments);
  558. // The input element is interrogated by the layout to extract height when labelAlign is 'top'
  559. // It must be set, and then switched between the iframe and the textarea
  560. me.inputEl = me.iframeEl;
  561. // Start polling for when the iframe document is ready to be manipulated
  562. me.monitorTask = Ext.TaskManager.start({
  563. run: me.checkDesignMode,
  564. scope: me,
  565. interval: 100
  566. });
  567. },
  568. initRenderTpl: function() {
  569. var me = this;
  570. if (!me.hasOwnProperty('renderTpl')) {
  571. me.renderTpl = me.getTpl('labelableRenderTpl');
  572. }
  573. return me.callParent();
  574. },
  575. initRenderData: function() {
  576. this.beforeSubTpl = '&lt;div class=&quot;' + this.editorWrapCls + '&quot;&gt;' + Ext.DomHelper.markup(this.toolbar.getRenderTree());
  577. return Ext.applyIf(this.callParent(), this.getLabelableRenderData());
  578. },
  579. getSubTplData: function() {
  580. return {
  581. $comp : this,
  582. cmpId : this.id,
  583. id : this.getInputId(),
  584. textareaCls : Ext.baseCSSPrefix + 'hidden',
  585. value : this.value,
  586. iframeName : Ext.id(),
  587. iframeSrc : Ext.SSL_SECURE_URL,
  588. size : 'height:100px;width:100%'
  589. };
  590. },
  591. getSubTplMarkup: function() {
  592. return this.getTpl('fieldSubTpl').apply(this.getSubTplData());
  593. },
  594. initFrameDoc: function() {
  595. var me = this,
  596. doc, task;
  597. Ext.TaskManager.stop(me.monitorTask);
  598. doc = me.getDoc();
  599. me.win = me.getWin();
  600. doc.open();
  601. doc.write(me.getDocMarkup());
  602. doc.close();
  603. task = { // must defer to wait for browser to be ready
  604. run: function() {
  605. var doc = me.getDoc();
  606. if (doc.body || doc.readyState === 'complete') {
  607. Ext.TaskManager.stop(task);
  608. me.setDesignMode(true);
  609. Ext.defer(me.initEditor, 10, me);
  610. }
  611. },
  612. interval : 10,
  613. duration:10000,
  614. scope: me
  615. };
  616. Ext.TaskManager.start(task);
  617. },
  618. checkDesignMode: function() {
  619. var me = this,
  620. doc = me.getDoc();
  621. if (doc &amp;&amp; (!doc.editorInitialized || me.getDesignMode() !== 'on')) {
  622. me.initFrameDoc();
  623. }
  624. },
  625. <span id='Ext-form-field-HtmlEditor-method-setDesignMode'> /**
  626. </span> * @private
  627. * Sets current design mode. To enable, mode can be true or 'on', off otherwise
  628. */
  629. setDesignMode: function(mode) {
  630. var me = this,
  631. doc = me.getDoc();
  632. if (doc) {
  633. if (me.readOnly) {
  634. mode = false;
  635. }
  636. doc.designMode = (/on|true/i).test(String(mode).toLowerCase()) ?'on':'off';
  637. }
  638. },
  639. // private
  640. getDesignMode: function() {
  641. var doc = this.getDoc();
  642. return !doc ? '' : String(doc.designMode).toLowerCase();
  643. },
  644. disableItems: function(disabled) {
  645. var items = this.getToolbar().items.items,
  646. i,
  647. iLen = items.length,
  648. item;
  649. for (i = 0; i &lt; iLen; i++) {
  650. item = items[i];
  651. if (item.getItemId() !== 'sourceedit') {
  652. item.setDisabled(disabled);
  653. }
  654. }
  655. },
  656. <span id='Ext-form-field-HtmlEditor-method-toggleSourceEdit'> /**
  657. </span> * Toggles the editor between standard and source edit mode.
  658. * @param {Boolean} [sourceEditMode] True for source edit, false for standard
  659. */
  660. toggleSourceEdit: function(sourceEditMode) {
  661. var me = this,
  662. iframe = me.iframeEl,
  663. textarea = me.textareaEl,
  664. hiddenCls = Ext.baseCSSPrefix + 'hidden',
  665. btn = me.getToolbar().getComponent('sourceedit');
  666. if (!Ext.isBoolean(sourceEditMode)) {
  667. sourceEditMode = !me.sourceEditMode;
  668. }
  669. me.sourceEditMode = sourceEditMode;
  670. if (btn.pressed !== sourceEditMode) {
  671. btn.toggle(sourceEditMode);
  672. }
  673. if (sourceEditMode) {
  674. me.disableItems(true);
  675. me.syncValue();
  676. iframe.addCls(hiddenCls);
  677. textarea.removeCls(hiddenCls);
  678. textarea.dom.removeAttribute('tabIndex');
  679. textarea.focus();
  680. me.inputEl = textarea;
  681. }
  682. else {
  683. if (me.initialized) {
  684. me.disableItems(me.readOnly);
  685. }
  686. me.pushValue();
  687. iframe.removeCls(hiddenCls);
  688. textarea.addCls(hiddenCls);
  689. textarea.dom.setAttribute('tabIndex', -1);
  690. me.deferFocus();
  691. me.inputEl = iframe;
  692. }
  693. me.fireEvent('editmodechange', me, sourceEditMode);
  694. me.updateLayout();
  695. },
  696. // private used internally
  697. createLink : function() {
  698. var url = prompt(this.createLinkText, this.defaultLinkValue);
  699. if (url &amp;&amp; url !== 'http:/'+'/') {
  700. this.relayCmd('createlink', url);
  701. }
  702. },
  703. clearInvalid: Ext.emptyFn,
  704. // docs inherit from Field
  705. setValue: function(value) {
  706. var me = this,
  707. textarea = me.textareaEl;
  708. me.mixins.field.setValue.call(me, value);
  709. if (value === null || value === undefined) {
  710. value = '';
  711. }
  712. if (textarea) {
  713. textarea.dom.value = value;
  714. }
  715. me.pushValue();
  716. return this;
  717. },
  718. <span id='Ext-form-field-HtmlEditor-method-cleanHtml'> /**
  719. </span> * If you need/want custom HTML cleanup, this is the method you should override.
  720. * @param {String} html The HTML to be cleaned
  721. * @return {String} The cleaned HTML
  722. * @protected
  723. */
  724. cleanHtml: function(html) {
  725. html = String(html);
  726. if (Ext.isWebKit) { // strip safari nonsense
  727. html = html.replace(/\sclass=&quot;(?:Apple-style-span|khtml-block-placeholder)&quot;/gi, '');
  728. }
  729. /*
  730. * Neat little hack. Strips out all the non-digit characters from the default
  731. * value and compares it to the character code of the first character in the string
  732. * because it can cause encoding issues when posted to the server. We need the
  733. * parseInt here because charCodeAt will return a number.
  734. */
  735. if (html.charCodeAt(0) === parseInt(this.defaultValue.replace(/\D/g, ''), 10)) {
  736. html = html.substring(1);
  737. }
  738. return html;
  739. },
  740. <span id='Ext-form-field-HtmlEditor-method-syncValue'> /**
  741. </span> * Syncs the contents of the editor iframe with the textarea.
  742. * @protected
  743. */
  744. syncValue : function(){
  745. var me = this,
  746. body, changed, html, bodyStyle, match;
  747. if (me.initialized) {
  748. body = me.getEditorBody();
  749. html = body.innerHTML;
  750. if (Ext.isWebKit) {
  751. bodyStyle = body.getAttribute('style'); // Safari puts text-align styles on the body element!
  752. match = bodyStyle.match(/text-align:(.*?);/i);
  753. if (match &amp;&amp; match[1]) {
  754. html = '&lt;div style=&quot;' + match[0] + '&quot;&gt;' + html + '&lt;/div&gt;';
  755. }
  756. }
  757. html = me.cleanHtml(html);
  758. if (me.fireEvent('beforesync', me, html) !== false) {
  759. if (me.textareaEl.dom.value != html) {
  760. me.textareaEl.dom.value = html;
  761. changed = true;
  762. }
  763. me.fireEvent('sync', me, html);
  764. if (changed) {
  765. // we have to guard this to avoid infinite recursion because getValue
  766. // calls this method...
  767. me.checkChange();
  768. }
  769. }
  770. }
  771. },
  772. //docs inherit from Field
  773. getValue : function() {
  774. var me = this,
  775. value;
  776. if (!me.sourceEditMode) {
  777. me.syncValue();
  778. }
  779. value = me.rendered ? me.textareaEl.dom.value : me.value;
  780. me.value = value;
  781. return value;
  782. },
  783. <span id='Ext-form-field-HtmlEditor-method-pushValue'> /**
  784. </span> * Pushes the value of the textarea into the iframe editor.
  785. * @protected
  786. */
  787. pushValue: function() {
  788. var me = this,
  789. v;
  790. if(me.initialized){
  791. v = me.textareaEl.dom.value || '';
  792. if (!me.activated &amp;&amp; v.length &lt; 1) {
  793. v = me.defaultValue;
  794. }
  795. if (me.fireEvent('beforepush', me, v) !== false) {
  796. me.getEditorBody().innerHTML = v;
  797. if (Ext.isGecko) {
  798. // Gecko hack, see: https://bugzilla.mozilla.org/show_bug.cgi?id=232791#c8
  799. me.setDesignMode(false); //toggle off first
  800. me.setDesignMode(true);
  801. }
  802. me.fireEvent('push', me, v);
  803. }
  804. }
  805. },
  806. // private
  807. deferFocus : function(){
  808. this.focus(false, true);
  809. },
  810. getFocusEl: function() {
  811. var me = this,
  812. win = me.win;
  813. return win &amp;&amp; !me.sourceEditMode ? win : me.textareaEl;
  814. },
  815. // private
  816. initEditor : function(){
  817. //Destroying the component during/before initEditor can cause issues.
  818. try {
  819. var me = this,
  820. dbody = me.getEditorBody(),
  821. ss = me.textareaEl.getStyles('font-size', 'font-family', 'background-image', 'background-repeat', 'background-color', 'color'),
  822. doc,
  823. fn;
  824. ss['background-attachment'] = 'fixed'; // w3c
  825. dbody.bgProperties = 'fixed'; // ie
  826. Ext.DomHelper.applyStyles(dbody, ss);
  827. doc = me.getDoc();
  828. if (doc) {
  829. try {
  830. Ext.EventManager.removeAll(doc);
  831. } catch(e) {}
  832. }
  833. /*
  834. * We need to use createDelegate here, because when using buffer, the delayed task is added
  835. * as a property to the function. When the listener is removed, the task is deleted from the function.
  836. * Since onEditorEvent is shared on the prototype, if we have multiple html editors, the first time one of the editors
  837. * is destroyed, it causes the fn to be deleted from the prototype, which causes errors. Essentially, we're just anonymizing the function.
  838. */
  839. fn = Ext.Function.bind(me.onEditorEvent, me);
  840. Ext.EventManager.on(doc, {
  841. mousedown: fn,
  842. dblclick: fn,
  843. click: fn,
  844. keyup: fn,
  845. buffer:100
  846. });
  847. // These events need to be relayed from the inner document (where they stop
  848. // bubbling) up to the outer document. This has to be done at the DOM level so
  849. // the event reaches listeners on elements like the document body. The effected
  850. // mechanisms that depend on this bubbling behavior are listed to the right
  851. // of the event.
  852. fn = me.onRelayedEvent;
  853. Ext.EventManager.on(doc, {
  854. mousedown: fn, // menu dismisal (MenuManager) and Window onMouseDown (toFront)
  855. mousemove: fn, // window resize drag detection
  856. mouseup: fn, // window resize termination
  857. click: fn, // not sure, but just to be safe
  858. dblclick: fn, // not sure again
  859. scope: me
  860. });
  861. if (Ext.isGecko) {
  862. Ext.EventManager.on(doc, 'keypress', me.applyCommand, me);
  863. }
  864. if (me.fixKeys) {
  865. Ext.EventManager.on(doc, 'keydown', me.fixKeys, me);
  866. }
  867. // We need to be sure we remove all our events from the iframe on unload or we're going to LEAK!
  868. Ext.EventManager.on(window, 'unload', me.beforeDestroy, me);
  869. doc.editorInitialized = true;
  870. me.initialized = true;
  871. me.pushValue();
  872. me.setReadOnly(me.readOnly);
  873. me.fireEvent('initialize', me);
  874. } catch(ex) {
  875. // ignore (why?)
  876. }
  877. },
  878. // private
  879. beforeDestroy : function(){
  880. var me = this,
  881. monitorTask = me.monitorTask,
  882. doc, prop;
  883. if (monitorTask) {
  884. Ext.TaskManager.stop(monitorTask);
  885. }
  886. if (me.rendered) {
  887. try {
  888. doc = me.getDoc();
  889. if (doc) {
  890. // removeAll() doesn't currently know how to handle iframe document,
  891. // so for now we have to wrap it in an Ext.Element using Ext.fly,
  892. // or else IE6/7 will leak big time when the page is refreshed.
  893. // TODO: this may not be needed once we find a more permanent fix.
  894. // see EXTJSIV-5891.
  895. Ext.EventManager.removeAll(Ext.fly(doc));
  896. for (prop in doc) {
  897. if (doc.hasOwnProperty &amp;&amp; doc.hasOwnProperty(prop)) {
  898. delete doc[prop];
  899. }
  900. }
  901. }
  902. } catch(e) {
  903. // ignore (why?)
  904. }
  905. Ext.destroyMembers(me, 'toolbar', 'iframeEl', 'textareaEl');
  906. }
  907. me.callParent();
  908. },
  909. // private
  910. onRelayedEvent: function (event) {
  911. // relay event from the iframe's document to the document that owns the iframe...
  912. var iframeEl = this.iframeEl,
  913. iframeXY = iframeEl.getXY(),
  914. eventXY = event.getXY();
  915. // the event from the inner document has XY relative to that document's origin,
  916. // so adjust it to use the origin of the iframe in the outer document:
  917. event.xy = [iframeXY[0] + eventXY[0], iframeXY[1] + eventXY[1]];
  918. event.injectEvent(iframeEl); // blame the iframe for the event...
  919. event.xy = eventXY; // restore the original XY (just for safety)
  920. },
  921. // private
  922. onFirstFocus : function(){
  923. var me = this,
  924. selection, range;
  925. me.activated = true;
  926. me.disableItems(me.readOnly);
  927. if (Ext.isGecko) { // prevent silly gecko errors
  928. me.win.focus();
  929. selection = me.win.getSelection();
  930. if (!selection.focusNode || selection.focusNode.nodeType !== 3) {
  931. range = selection.getRangeAt(0);
  932. range.selectNodeContents(me.getEditorBody());
  933. range.collapse(true);
  934. me.deferFocus();
  935. }
  936. try {
  937. me.execCmd('useCSS', true);
  938. me.execCmd('styleWithCSS', false);
  939. } catch(e) {
  940. // ignore (why?)
  941. }
  942. }
  943. me.fireEvent('activate', me);
  944. },
  945. // private
  946. adjustFont: function(btn) {
  947. var adjust = btn.getItemId() === 'increasefontsize' ? 1 : -1,
  948. size = this.getDoc().queryCommandValue('FontSize') || '2',
  949. isPxSize = Ext.isString(size) &amp;&amp; size.indexOf('px') !== -1,
  950. isSafari;
  951. size = parseInt(size, 10);
  952. if (isPxSize) {
  953. // Safari 3 values
  954. // 1 = 10px, 2 = 13px, 3 = 16px, 4 = 18px, 5 = 24px, 6 = 32px
  955. if (size &lt;= 10) {
  956. size = 1 + adjust;
  957. }
  958. else if (size &lt;= 13) {
  959. size = 2 + adjust;
  960. }
  961. else if (size &lt;= 16) {
  962. size = 3 + adjust;
  963. }
  964. else if (size &lt;= 18) {
  965. size = 4 + adjust;
  966. }
  967. else if (size &lt;= 24) {
  968. size = 5 + adjust;
  969. }
  970. else {
  971. size = 6 + adjust;
  972. }
  973. size = Ext.Number.constrain(size, 1, 6);
  974. } else {
  975. isSafari = Ext.isSafari;
  976. if (isSafari) { // safari
  977. adjust *= 2;
  978. }
  979. size = Math.max(1, size + adjust) + (isSafari ? 'px' : 0);
  980. }
  981. this.execCmd('FontSize', size);
  982. },
  983. // private
  984. onEditorEvent: function(e) {
  985. this.updateToolbar();
  986. },
  987. <span id='Ext-form-field-HtmlEditor-method-updateToolbar'> /**
  988. </span> * Triggers a toolbar update by reading the markup state of the current selection in the editor.
  989. * @protected
  990. */
  991. updateToolbar: function() {
  992. var me = this,
  993. btns, doc, name, fontSelect;
  994. if (me.readOnly) {
  995. return;
  996. }
  997. if (!me.activated) {
  998. me.onFirstFocus();
  999. return;
  1000. }
  1001. btns = me.getToolbar().items.map;
  1002. doc = me.getDoc();
  1003. if (me.enableFont &amp;&amp; !Ext.isSafari2) {
  1004. name = (doc.queryCommandValue('FontName') || me.defaultFont).toLowerCase();
  1005. fontSelect = me.fontSelect.dom;
  1006. if (name !== fontSelect.value) {
  1007. fontSelect.value = name;
  1008. }
  1009. }
  1010. function updateButtons() {
  1011. for (var i = 0, l = arguments.length, name; i &lt; l; i++) {
  1012. name = arguments[i];
  1013. btns[name].toggle(doc.queryCommandState(name));
  1014. }
  1015. }
  1016. if(me.enableFormat){
  1017. updateButtons('bold', 'italic', 'underline');
  1018. }
  1019. if(me.enableAlignments){
  1020. updateButtons('justifyleft', 'justifycenter', 'justifyright');
  1021. }
  1022. if(!Ext.isSafari2 &amp;&amp; me.enableLists){
  1023. updateButtons('insertorderedlist', 'insertunorderedlist');
  1024. }
  1025. Ext.menu.Manager.hideAll();
  1026. me.syncValue();
  1027. },
  1028. // private
  1029. relayBtnCmd: function(btn) {
  1030. this.relayCmd(btn.getItemId());
  1031. },
  1032. <span id='Ext-form-field-HtmlEditor-method-relayCmd'> /**
  1033. </span> * Executes a Midas editor command on the editor document and performs necessary focus and toolbar updates.
  1034. * **This should only be called after the editor is initialized.**
  1035. * @param {String} cmd The Midas command
  1036. * @param {String/Boolean} [value=null] The value to pass to the command
  1037. */
  1038. relayCmd: function(cmd, value) {
  1039. Ext.defer(function() {
  1040. var me = this;
  1041. me.focus();
  1042. me.execCmd(cmd, value);
  1043. me.updateToolbar();
  1044. }, 10, this);
  1045. },
  1046. <span id='Ext-form-field-HtmlEditor-method-execCmd'> /**
  1047. </span> * Executes a Midas editor command directly on the editor document. For visual commands, you should use
  1048. * {@link #relayCmd} instead. **This should only be called after the editor is initialized.**
  1049. * @param {String} cmd The Midas command
  1050. * @param {String/Boolean} [value=null] The value to pass to the command
  1051. */
  1052. execCmd : function(cmd, value){
  1053. var me = this,
  1054. doc = me.getDoc(),
  1055. undef;
  1056. doc.execCommand(cmd, false, value === undef ? null : value);
  1057. me.syncValue();
  1058. },
  1059. // private
  1060. applyCommand : function(e){
  1061. if (e.ctrlKey) {
  1062. var me = this,
  1063. c = e.getCharCode(), cmd;
  1064. if (c &gt; 0) {
  1065. c = String.fromCharCode(c);
  1066. switch (c) {
  1067. case 'b':
  1068. cmd = 'bold';
  1069. break;
  1070. case 'i':
  1071. cmd = 'italic';
  1072. break;
  1073. case 'u':
  1074. cmd = 'underline';
  1075. break;
  1076. }
  1077. if (cmd) {
  1078. me.win.focus();
  1079. me.execCmd(cmd);
  1080. me.deferFocus();
  1081. e.preventDefault();
  1082. }
  1083. }
  1084. }
  1085. },
  1086. <span id='Ext-form-field-HtmlEditor-method-insertAtCursor'> /**
  1087. </span> * Inserts the passed text at the current cursor position.
  1088. * Note: the editor must be initialized and activated to insert text.
  1089. * @param {String} text
  1090. */
  1091. insertAtCursor : function(text){
  1092. var me = this,
  1093. range;
  1094. if (me.activated) {
  1095. me.win.focus();
  1096. if (Ext.isIE) {
  1097. range = me.getDoc().selection.createRange();
  1098. if (range) {
  1099. range.pasteHTML(text);
  1100. me.syncValue();
  1101. me.deferFocus();
  1102. }
  1103. }else{
  1104. me.execCmd('InsertHTML', text);
  1105. me.deferFocus();
  1106. }
  1107. }
  1108. },
  1109. // private
  1110. fixKeys: (function() { // load time branching for fastest keydown performance
  1111. if (Ext.isIE) {
  1112. return function(e){
  1113. var me = this,
  1114. k = e.getKey(),
  1115. doc = me.getDoc(),
  1116. readOnly = me.readOnly,
  1117. range, target;
  1118. if (k === e.TAB) {
  1119. e.stopEvent();
  1120. if (!readOnly) {
  1121. range = doc.selection.createRange();
  1122. if(range){
  1123. range.collapse(true);
  1124. range.pasteHTML('&amp;#160;&amp;#160;&amp;#160;&amp;#160;');
  1125. me.deferFocus();
  1126. }
  1127. }
  1128. }
  1129. else if (k === e.ENTER) {
  1130. if (!readOnly) {
  1131. range = doc.selection.createRange();
  1132. if (range) {
  1133. target = range.parentElement();
  1134. if(!target || target.tagName.toLowerCase() !== 'li'){
  1135. e.stopEvent();
  1136. range.pasteHTML('&lt;br /&gt;');
  1137. range.collapse(false);
  1138. range.select();
  1139. }
  1140. }
  1141. }
  1142. }
  1143. };
  1144. }
  1145. if (Ext.isOpera) {
  1146. return function(e){
  1147. var me = this;
  1148. if (e.getKey() === e.TAB) {
  1149. e.stopEvent();
  1150. if (!me.readOnly) {
  1151. me.win.focus();
  1152. me.execCmd('InsertHTML','&amp;#160;&amp;#160;&amp;#160;&amp;#160;');
  1153. me.deferFocus();
  1154. }
  1155. }
  1156. };
  1157. }
  1158. if (Ext.isWebKit) {
  1159. return function(e){
  1160. var me = this,
  1161. k = e.getKey(),
  1162. readOnly = me.readOnly;
  1163. if (k === e.TAB) {
  1164. e.stopEvent();
  1165. if (!readOnly) {
  1166. me.execCmd('InsertText','\t');
  1167. me.deferFocus();
  1168. }
  1169. }
  1170. else if (k === e.ENTER) {
  1171. e.stopEvent();
  1172. if (!readOnly) {
  1173. me.execCmd('InsertHtml','&lt;br /&gt;&lt;br /&gt;');
  1174. me.deferFocus();
  1175. }
  1176. }
  1177. };
  1178. }
  1179. return null; // not needed, so null
  1180. }()),
  1181. <span id='Ext-form-field-HtmlEditor-method-getToolbar'> /**
  1182. </span> * Returns the editor's toolbar. **This is only available after the editor has been rendered.**
  1183. * @return {Ext.toolbar.Toolbar}
  1184. */
  1185. getToolbar : function(){
  1186. return this.toolbar;
  1187. },
  1188. //&lt;locale&gt;
  1189. <span id='Ext-form-field-HtmlEditor-property-buttonTips'> /**
  1190. </span> * @property {Object} buttonTips
  1191. * Object collection of toolbar tooltips for the buttons in the editor. The key is the command id associated with
  1192. * that button and the value is a valid QuickTips object. For example:
  1193. *
  1194. * {
  1195. * bold : {
  1196. * title: 'Bold (Ctrl+B)',
  1197. * text: 'Make the selected text bold.',
  1198. * cls: 'x-html-editor-tip'
  1199. * },
  1200. * italic : {
  1201. * title: 'Italic (Ctrl+I)',
  1202. * text: 'Make the selected text italic.',
  1203. * cls: 'x-html-editor-tip'
  1204. * },
  1205. * ...
  1206. */
  1207. buttonTips : {
  1208. bold : {
  1209. title: 'Bold (Ctrl+B)',
  1210. text: 'Make the selected text bold.',
  1211. cls: Ext.baseCSSPrefix + 'html-editor-tip'
  1212. },
  1213. italic : {
  1214. title: 'Italic (Ctrl+I)',
  1215. text: 'Make the selected text italic.',
  1216. cls: Ext.baseCSSPrefix + 'html-editor-tip'
  1217. },
  1218. underline : {
  1219. title: 'Underline (Ctrl+U)',
  1220. text: 'Underline the selected text.',
  1221. cls: Ext.baseCSSPrefix + 'html-editor-tip'
  1222. },
  1223. increasefontsize : {
  1224. title: 'Grow Text',
  1225. text: 'Increase the font size.',
  1226. cls: Ext.baseCSSPrefix + 'html-editor-tip'
  1227. },
  1228. decreasefontsize : {
  1229. title: 'Shrink Text',
  1230. text: 'Decrease the font size.',
  1231. cls: Ext.baseCSSPrefix + 'html-editor-tip'
  1232. },
  1233. backcolor : {
  1234. title: 'Text Highlight Color',
  1235. text: 'Change the background color of the selected text.',
  1236. cls: Ext.baseCSSPrefix + 'html-editor-tip'
  1237. },
  1238. forecolor : {
  1239. title: 'Font Color',
  1240. text: 'Change the color of the selected text.',
  1241. cls: Ext.baseCSSPrefix + 'html-editor-tip'
  1242. },
  1243. justifyleft : {
  1244. title: 'Align Text Left',
  1245. text: 'Align text to the left.',
  1246. cls: Ext.baseCSSPrefix + 'html-editor-tip'
  1247. },
  1248. justifycenter : {
  1249. title: 'Center Text',
  1250. text: 'Center text in the editor.',
  1251. cls: Ext.baseCSSPrefix + 'html-editor-tip'
  1252. },
  1253. justifyright : {
  1254. title: 'Align Text Right',
  1255. text: 'Align text to the right.',
  1256. cls: Ext.baseCSSPrefix + 'html-editor-tip'
  1257. },
  1258. insertunorderedlist : {
  1259. title: 'Bullet List',
  1260. text: 'Start a bulleted list.',
  1261. cls: Ext.baseCSSPrefix + 'html-editor-tip'
  1262. },
  1263. insertorderedlist : {
  1264. title: 'Numbered List',
  1265. text: 'Start a numbered list.',
  1266. cls: Ext.baseCSSPrefix + 'html-editor-tip'
  1267. },
  1268. createlink : {
  1269. title: 'Hyperlink',
  1270. text: 'Make the selected text a hyperlink.',
  1271. cls: Ext.baseCSSPrefix + 'html-editor-tip'
  1272. },
  1273. sourceedit : {
  1274. title: 'Source Edit',
  1275. text: 'Switch to source editing mode.',
  1276. cls: Ext.baseCSSPrefix + 'html-editor-tip'
  1277. }
  1278. }
  1279. //&lt;/locale&gt;
  1280. // hide stuff that is not compatible
  1281. <span id='Ext-form-field-HtmlEditor-event-blur'> /**
  1282. </span> * @event blur
  1283. * @private
  1284. */
  1285. <span id='Ext-form-field-HtmlEditor-event-focus'> /**
  1286. </span> * @event focus
  1287. * @private
  1288. */
  1289. <span id='Ext-form-field-HtmlEditor-event-specialkey'> /**
  1290. </span> * @event specialkey
  1291. * @private
  1292. */
  1293. <span id='Ext-form-field-HtmlEditor-cfg-fieldCls'> /**
  1294. </span> * @cfg {String} fieldCls
  1295. * @private
  1296. */
  1297. <span id='Ext-form-field-HtmlEditor-cfg-focusCls'> /**
  1298. </span> * @cfg {String} focusCls
  1299. * @private
  1300. */
  1301. <span id='Ext-form-field-HtmlEditor-cfg-autoCreate'> /**
  1302. </span> * @cfg {String} autoCreate
  1303. * @private
  1304. */
  1305. <span id='Ext-form-field-HtmlEditor-cfg-inputType'> /**
  1306. </span> * @cfg {String} inputType
  1307. * @private
  1308. */
  1309. <span id='Ext-form-field-HtmlEditor-cfg-invalidCls'> /**
  1310. </span> * @cfg {String} invalidCls
  1311. * @private
  1312. */
  1313. <span id='Ext-form-field-HtmlEditor-cfg-invalidText'> /**
  1314. </span> * @cfg {String} invalidText
  1315. * @private
  1316. */
  1317. <span id='Ext-form-field-HtmlEditor-cfg-msgFx'> /**
  1318. </span> * @cfg {String} msgFx
  1319. * @private
  1320. */
  1321. <span id='Ext-form-field-HtmlEditor-cfg-allowDomMove'> /**
  1322. </span> * @cfg {Boolean} allowDomMove
  1323. * @private
  1324. */
  1325. <span id='Ext-form-field-HtmlEditor-cfg-applyTo'> /**
  1326. </span> * @cfg {String} applyTo
  1327. * @private
  1328. */
  1329. <span id='Ext-form-field-HtmlEditor-cfg-readOnly'> /**
  1330. </span> * @cfg {String} readOnly
  1331. * @private
  1332. */
  1333. <span id='Ext-form-field-HtmlEditor-cfg-tabIndex'> /**
  1334. </span> * @cfg {String} tabIndex
  1335. * @private
  1336. */
  1337. <span id='Ext-form-field-HtmlEditor-method-validate'> /**
  1338. </span> * @method validate
  1339. * @private
  1340. */
  1341. });
  1342. </pre>
  1343. </body>
  1344. </html>