Renderable.js 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127
  1. /**
  2. * Given a component hierarchy of this:
  3. *
  4. * {
  5. * xtype: 'panel',
  6. * id: 'ContainerA',
  7. * layout: 'hbox',
  8. * renderTo: Ext.getBody(),
  9. * items: [
  10. * {
  11. * id: 'ContainerB',
  12. * xtype: 'container',
  13. * items: [
  14. * { id: 'ComponentA' }
  15. * ]
  16. * }
  17. * ]
  18. * }
  19. *
  20. * The rendering of the above proceeds roughly like this:
  21. *
  22. * - ContainerA's initComponent calls #render passing the `renderTo` property as the
  23. * container argument.
  24. * - `render` calls the `getRenderTree` method to get a complete {@link Ext.DomHelper} spec.
  25. * - `getRenderTree` fires the "beforerender" event and calls the #beforeRender
  26. * method. Its result is obtained by calling #getElConfig.
  27. * - The #getElConfig method uses the `renderTpl` and its render data as the content
  28. * of the `autoEl` described element.
  29. * - The result of `getRenderTree` is passed to {@link Ext.DomHelper#append}.
  30. * - The `renderTpl` contains calls to render things like docked items, container items
  31. * and raw markup (such as the `html` or `tpl` config properties). These calls are to
  32. * methods added to the {@link Ext.XTemplate} instance by #setupRenderTpl.
  33. * - The #setupRenderTpl method adds methods such as `renderItems`, `renderContent`, etc.
  34. * to the template. These are directed to "doRenderItems", "doRenderContent" etc..
  35. * - The #setupRenderTpl calls traverse from components to their {@link Ext.layout.Layout}
  36. * object.
  37. * - When a container is rendered, it also has a `renderTpl`. This is processed when the
  38. * `renderContainer` method is called in the component's `renderTpl`. This call goes to
  39. * Ext.layout.container.Container#doRenderContainer. This method repeats this
  40. * process for all components in the container.
  41. * - After the top-most component's markup is generated and placed in to the DOM, the next
  42. * step is to link elements to their components and finish calling the component methods
  43. * `onRender` and `afterRender` as well as fire the corresponding events.
  44. * - The first step in this is to call #finishRender. This method descends the
  45. * component hierarchy and calls `onRender` and fires the `render` event. These calls
  46. * are delivered top-down to approximate the timing of these calls/events from previous
  47. * versions.
  48. * - During the pass, the component's `el` is set. Likewise, the `renderSelectors` and
  49. * `childEls` are applied to capture references to the component's elements.
  50. * - These calls are also made on the {@link Ext.layout.container.Container} layout to
  51. * capture its elements. Both of these classes use {@link Ext.util.ElementContainer} to
  52. * handle `childEls` processing.
  53. * - Once this is complete, a similar pass is made by calling #finishAfterRender.
  54. * This call also descends the component hierarchy, but this time the calls are made in
  55. * a bottom-up order to `afterRender`.
  56. *
  57. * @private
  58. */
  59. Ext.define('Ext.util.Renderable', {
  60. requires: [
  61. 'Ext.dom.Element'
  62. ],
  63. frameCls: Ext.baseCSSPrefix + 'frame',
  64. frameIdRegex: /[\-]frame\d+[TMB][LCR]$/,
  65. frameElementCls: {
  66. tl: [],
  67. tc: [],
  68. tr: [],
  69. ml: [],
  70. mc: [],
  71. mr: [],
  72. bl: [],
  73. bc: [],
  74. br: []
  75. },
  76. frameElNames: ['TL','TC','TR','ML','MC','MR','BL','BC','BR'],
  77. frameTpl: [
  78. '{%this.renderDockedItems(out,values,0);%}',
  79. '<tpl if="top">',
  80. '<tpl if="left"><div id="{fgid}TL" class="{frameCls}-tl {baseCls}-tl {baseCls}-{ui}-tl<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tl</tpl>" style="background-position: {tl}; padding-left: {frameWidth}px" role="presentation"></tpl>',
  81. '<tpl if="right"><div id="{fgid}TR" class="{frameCls}-tr {baseCls}-tr {baseCls}-{ui}-tr<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tr</tpl>" style="background-position: {tr}; padding-right: {frameWidth}px" role="presentation"></tpl>',
  82. '<div id="{fgid}TC" class="{frameCls}-tc {baseCls}-tc {baseCls}-{ui}-tc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tc</tpl>" style="background-position: {tc}; height: {frameWidth}px" role="presentation"></div>',
  83. '<tpl if="right"></div></tpl>',
  84. '<tpl if="left"></div></tpl>',
  85. '</tpl>',
  86. '<tpl if="left"><div id="{fgid}ML" class="{frameCls}-ml {baseCls}-ml {baseCls}-{ui}-ml<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-ml</tpl>" style="background-position: {ml}; padding-left: {frameWidth}px" role="presentation"></tpl>',
  87. '<tpl if="right"><div id="{fgid}MR" class="{frameCls}-mr {baseCls}-mr {baseCls}-{ui}-mr<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mr</tpl>" style="background-position: {mr}; padding-right: {frameWidth}px" role="presentation"></tpl>',
  88. '<div id="{fgid}MC" class="{frameCls}-mc {baseCls}-mc {baseCls}-{ui}-mc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mc</tpl>" role="presentation">',
  89. '{%this.applyRenderTpl(out, values)%}',
  90. '</div>',
  91. '<tpl if="right"></div></tpl>',
  92. '<tpl if="left"></div></tpl>',
  93. '<tpl if="bottom">',
  94. '<tpl if="left"><div id="{fgid}BL" class="{frameCls}-bl {baseCls}-bl {baseCls}-{ui}-bl<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bl</tpl>" style="background-position: {bl}; padding-left: {frameWidth}px" role="presentation"></tpl>',
  95. '<tpl if="right"><div id="{fgid}BR" class="{frameCls}-br {baseCls}-br {baseCls}-{ui}-br<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-br</tpl>" style="background-position: {br}; padding-right: {frameWidth}px" role="presentation"></tpl>',
  96. '<div id="{fgid}BC" class="{frameCls}-bc {baseCls}-bc {baseCls}-{ui}-bc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bc</tpl>" style="background-position: {bc}; height: {frameWidth}px" role="presentation"></div>',
  97. '<tpl if="right"></div></tpl>',
  98. '<tpl if="left"></div></tpl>',
  99. '</tpl>',
  100. '{%this.renderDockedItems(out,values,1);%}'
  101. ],
  102. frameTableTpl: [
  103. '{%this.renderDockedItems(out,values,0);%}',
  104. '<table><tbody>',
  105. '<tpl if="top">',
  106. '<tr>',
  107. '<tpl if="left"><td id="{fgid}TL" class="{frameCls}-tl {baseCls}-tl {baseCls}-{ui}-tl<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tl</tpl>" style="background-position: {tl}; padding-left:{frameWidth}px" role="presentation"></td></tpl>',
  108. '<td id="{fgid}TC" class="{frameCls}-tc {baseCls}-tc {baseCls}-{ui}-tc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tc</tpl>" style="background-position: {tc}; height: {frameWidth}px" role="presentation"></td>',
  109. '<tpl if="right"><td id="{fgid}TR" class="{frameCls}-tr {baseCls}-tr {baseCls}-{ui}-tr<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tr</tpl>" style="background-position: {tr}; padding-left: {frameWidth}px" role="presentation"></td></tpl>',
  110. '</tr>',
  111. '</tpl>',
  112. '<tr>',
  113. '<tpl if="left"><td id="{fgid}ML" class="{frameCls}-ml {baseCls}-ml {baseCls}-{ui}-ml<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-ml</tpl>" style="background-position: {ml}; padding-left: {frameWidth}px" role="presentation"></td></tpl>',
  114. '<td id="{fgid}MC" class="{frameCls}-mc {baseCls}-mc {baseCls}-{ui}-mc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mc</tpl>" style="background-position: 0 0;" role="presentation">',
  115. '{%this.applyRenderTpl(out, values)%}',
  116. '</td>',
  117. '<tpl if="right"><td id="{fgid}MR" class="{frameCls}-mr {baseCls}-mr {baseCls}-{ui}-mr<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mr</tpl>" style="background-position: {mr}; padding-left: {frameWidth}px" role="presentation"></td></tpl>',
  118. '</tr>',
  119. '<tpl if="bottom">',
  120. '<tr>',
  121. '<tpl if="left"><td id="{fgid}BL" class="{frameCls}-bl {baseCls}-bl {baseCls}-{ui}-bl<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bl</tpl>" style="background-position: {bl}; padding-left: {frameWidth}px" role="presentation"></td></tpl>',
  122. '<td id="{fgid}BC" class="{frameCls}-bc {baseCls}-bc {baseCls}-{ui}-bc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bc</tpl>" style="background-position: {bc}; height: {frameWidth}px" role="presentation"></td>',
  123. '<tpl if="right"><td id="{fgid}BR" class="{frameCls}-br {baseCls}-br {baseCls}-{ui}-br<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-br</tpl>" style="background-position: {br}; padding-left: {frameWidth}px" role="presentation"></td></tpl>',
  124. '</tr>',
  125. '</tpl>',
  126. '</tbody></table>',
  127. '{%this.renderDockedItems(out,values,1);%}'
  128. ],
  129. /**
  130. * Allows addition of behavior after rendering is complete. At this stage the Component’s Element
  131. * will have been styled according to the configuration, will have had any configured CSS class
  132. * names added, and will be in the configured visibility and the configured enable state.
  133. *
  134. * @template
  135. * @protected
  136. */
  137. afterRender : function() {
  138. var me = this,
  139. data = {},
  140. protoEl = me.protoEl,
  141. target = me.getTargetEl(),
  142. item;
  143. me.finishRenderChildren();
  144. if (me.styleHtmlContent) {
  145. target.addCls(me.styleHtmlCls);
  146. }
  147. protoEl.writeTo(data);
  148. // Here we apply any styles that were set on the protoEl during the rendering phase
  149. // A majority of times this will not happen, but we still need to handle it
  150. item = data.removed;
  151. if (item) {
  152. target.removeCls(item);
  153. }
  154. item = data.cls;
  155. if (item.length) {
  156. target.addCls(item);
  157. }
  158. item = data.style;
  159. if (data.style) {
  160. target.setStyle(item);
  161. }
  162. me.protoEl = null;
  163. // If this is the outermost Container, lay it out as soon as it is rendered.
  164. if (!me.ownerCt) {
  165. me.updateLayout();
  166. }
  167. },
  168. afterFirstLayout : function(width, height) {
  169. var me = this,
  170. hasX = Ext.isDefined(me.x),
  171. hasY = Ext.isDefined(me.y),
  172. pos, xy;
  173. // For floaters, calculate x and y if they aren't defined by aligning
  174. // the sized element to the center of either the container or the ownerCt
  175. if (me.floating && (!hasX || !hasY)) {
  176. if (me.floatParent) {
  177. pos = me.floatParent.getTargetEl().getViewRegion();
  178. xy = me.el.getAlignToXY(me.floatParent.getTargetEl(), 'c-c');
  179. pos.left = xy[0] - pos.left;
  180. pos.top = xy[1] - pos.top;
  181. } else {
  182. xy = me.el.getAlignToXY(me.container, 'c-c');
  183. pos = me.container.translatePoints(xy[0], xy[1]);
  184. }
  185. me.x = hasX ? me.x : pos.left;
  186. me.y = hasY ? me.y : pos.top;
  187. hasX = hasY = true;
  188. }
  189. if (hasX || hasY) {
  190. me.setPosition(me.x, me.y);
  191. }
  192. me.onBoxReady(width, height);
  193. if (me.hasListeners.boxready) {
  194. me.fireEvent('boxready', me, width, height);
  195. }
  196. },
  197. onBoxReady: Ext.emptyFn,
  198. /**
  199. * Sets references to elements inside the component. This applies {@link Ext.AbstractComponent#cfg-renderSelectors renderSelectors}
  200. * as well as {@link Ext.AbstractComponent#cfg-childEls childEls}.
  201. * @private
  202. */
  203. applyRenderSelectors: function() {
  204. var me = this,
  205. selectors = me.renderSelectors,
  206. el = me.el,
  207. dom = el.dom,
  208. selector;
  209. me.applyChildEls(el);
  210. // We still support renderSelectors. There are a few places in the framework that
  211. // need them and they are a documented part of the API. In fact, we support mixing
  212. // childEls and renderSelectors (no reason not to).
  213. if (selectors) {
  214. for (selector in selectors) {
  215. if (selectors.hasOwnProperty(selector) && selectors[selector]) {
  216. me[selector] = Ext.get(Ext.DomQuery.selectNode(selectors[selector], dom));
  217. }
  218. }
  219. }
  220. },
  221. beforeRender: function () {
  222. var me = this,
  223. target = me.getTargetEl(),
  224. layout = me.getComponentLayout();
  225. // Just before rendering, set the frame flag if we are an always-framed component like Window or Tip.
  226. me.frame = me.frame || me.alwaysFramed;
  227. if (!layout.initialized) {
  228. layout.initLayout();
  229. }
  230. // Attempt to set overflow style prior to render if the targetEl can be accessed.
  231. // If the targetEl does not exist yet, this will take place in finishRender
  232. if (target) {
  233. target.setStyle(me.getOverflowStyle());
  234. me.overflowStyleSet = true;
  235. }
  236. me.setUI(me.ui);
  237. if (me.disabled) {
  238. // pass silent so the event doesn't fire the first time.
  239. me.disable(true);
  240. }
  241. },
  242. /**
  243. * @private
  244. * Called from the selected frame generation template to insert this Component's inner structure inside the framing structure.
  245. *
  246. * When framing is used, a selected frame generation template is used as the primary template of the #getElConfig instead
  247. * of the configured {@link Ext.AbstractComponent#renderTpl renderTpl}. The renderTpl is invoked by this method which is injected into the framing template.
  248. */
  249. doApplyRenderTpl: function(out, values) {
  250. // Careful! This method is bolted on to the frameTpl so all we get for context is
  251. // the renderData! The "this" pointer is the frameTpl instance!
  252. var me = values.$comp,
  253. tpl;
  254. // Don't do this if the component is already rendered:
  255. if (!me.rendered) {
  256. tpl = me.initRenderTpl();
  257. tpl.applyOut(values.renderData, out);
  258. }
  259. },
  260. /**
  261. * Handles autoRender.
  262. * Floating Components may have an ownerCt. If they are asking to be constrained, constrain them within that
  263. * ownerCt, and have their z-index managed locally. Floating Components are always rendered to document.body
  264. */
  265. doAutoRender: function() {
  266. var me = this;
  267. if (!me.rendered) {
  268. if (me.floating) {
  269. me.render(document.body);
  270. } else {
  271. me.render(Ext.isBoolean(me.autoRender) ? Ext.getBody() : me.autoRender);
  272. }
  273. }
  274. },
  275. doRenderContent: function (out, renderData) {
  276. // Careful! This method is bolted on to the renderTpl so all we get for context is
  277. // the renderData! The "this" pointer is the renderTpl instance!
  278. var me = renderData.$comp;
  279. if (me.html) {
  280. Ext.DomHelper.generateMarkup(me.html, out);
  281. delete me.html;
  282. }
  283. if (me.tpl) {
  284. // Make sure this.tpl is an instantiated XTemplate
  285. if (!me.tpl.isTemplate) {
  286. me.tpl = new Ext.XTemplate(me.tpl);
  287. }
  288. if (me.data) {
  289. //me.tpl[me.tplWriteMode](target, me.data);
  290. me.tpl.applyOut(me.data, out);
  291. delete me.data;
  292. }
  293. }
  294. },
  295. doRenderFramingDockedItems: function (out, renderData, after) {
  296. // Careful! This method is bolted on to the frameTpl so all we get for context is
  297. // the renderData! The "this" pointer is the frameTpl instance!
  298. var me = renderData.$comp;
  299. // Most components don't have dockedItems, so check for doRenderDockedItems on the
  300. // component (also, don't do this if the component is already rendered):
  301. if (!me.rendered && me.doRenderDockedItems) {
  302. // The "renderData" property is placed in scope for the renderTpl, but we don't
  303. // want to render docked items at that level in addition to the framing level:
  304. renderData.renderData.$skipDockedItems = true;
  305. // doRenderDockedItems requires the $comp property on renderData, but this is
  306. // set on the frameTpl's renderData as well:
  307. me.doRenderDockedItems.call(this, out, renderData, after);
  308. }
  309. },
  310. /**
  311. * This method visits the rendered component tree in a "top-down" order. That is, this
  312. * code runs on a parent component before running on a child. This method calls the
  313. * {@link #onRender} method of each component.
  314. * @param {Number} containerIdx The index into the Container items of this Component.
  315. *
  316. * @private
  317. */
  318. finishRender: function(containerIdx) {
  319. var me = this,
  320. tpl, data, contentEl, el, pre, hide;
  321. // We are typically called w/me.el==null as a child of some ownerCt that is being
  322. // rendered. We are also called by render for a normal component (w/o a configured
  323. // me.el). In this case, render sets me.el and me.rendering (indirectly). Lastly
  324. // we are also called on a component (like a Viewport) that has a configured me.el
  325. // (body for a Viewport) when render is called. In this case, it is not flagged as
  326. // "me.rendering" yet becasue it does not produce a renderTree. We use this to know
  327. // not to regen the renderTpl.
  328. if (!me.el || me.$pid) {
  329. if (me.container) {
  330. el = me.container.getById(me.id, true);
  331. } else {
  332. el = Ext.getDom(me.id);
  333. }
  334. if (!me.el) {
  335. // Typical case: we produced the el during render
  336. me.wrapPrimaryEl(el);
  337. } else {
  338. // We were configured with an el and created a proxy, so now we can swap
  339. // the proxy for me.el:
  340. delete me.$pid;
  341. if (!me.el.dom) {
  342. // make sure me.el is an Element
  343. me.wrapPrimaryEl(me.el);
  344. }
  345. el.parentNode.insertBefore(me.el.dom, el);
  346. Ext.removeNode(el); // remove placeholder el
  347. // TODO - what about class/style?
  348. }
  349. } else if (!me.rendering) {
  350. // We were configured with an el and then told to render (e.g., Viewport). We
  351. // need to generate the proper DOM. Insert first because the layout system
  352. // insists that child Component elements indices match the Component indices.
  353. tpl = me.initRenderTpl();
  354. if (tpl) {
  355. data = me.initRenderData();
  356. tpl.insertFirst(me.getTargetEl(), data);
  357. }
  358. }
  359. // else we are rendering
  360. if (!me.container) {
  361. // top-level rendered components will already have me.container set up
  362. me.container = Ext.get(me.el.dom.parentNode);
  363. }
  364. if (me.ctCls) {
  365. me.container.addCls(me.ctCls);
  366. }
  367. // Sets the rendered flag and clears the redering flag
  368. me.onRender(me.container, containerIdx);
  369. // If we could not access a target protoEl in bewforeRender, we have to set the overflow styles here.
  370. if (!me.overflowStyleSet) {
  371. me.getTargetEl().setStyle(me.getOverflowStyle());
  372. }
  373. // Tell the encapsulating element to hide itself in the way the Component is configured to hide
  374. // This means DISPLAY, VISIBILITY or OFFSETS.
  375. me.el.setVisibilityMode(Ext.Element[me.hideMode.toUpperCase()]);
  376. if (me.overCls) {
  377. me.el.hover(me.addOverCls, me.removeOverCls, me);
  378. }
  379. if (me.hasListeners.render) {
  380. me.fireEvent('render', me);
  381. }
  382. if (me.contentEl) {
  383. pre = Ext.baseCSSPrefix;
  384. hide = pre + 'hide-';
  385. contentEl = Ext.get(me.contentEl);
  386. contentEl.removeCls([pre+'hidden', hide+'display', hide+'offsets', hide+'nosize']);
  387. me.getTargetEl().appendChild(contentEl.dom);
  388. }
  389. me.afterRender(); // this can cause a layout
  390. if (me.hasListeners.afterrender) {
  391. me.fireEvent('afterrender', me);
  392. }
  393. me.initEvents();
  394. if (me.hidden) {
  395. // Hiding during the render process should not perform any ancillary
  396. // actions that the full hide process does; It is not hiding, it begins in a hidden state.'
  397. // So just make the element hidden according to the configured hideMode
  398. me.el.hide();
  399. }
  400. },
  401. finishRenderChildren: function () {
  402. var layout = this.getComponentLayout();
  403. layout.finishRender();
  404. },
  405. getElConfig : function() {
  406. var me = this,
  407. autoEl = me.autoEl,
  408. frameInfo = me.getFrameInfo(),
  409. config = {
  410. tag: 'div',
  411. tpl: frameInfo ? me.initFramingTpl(frameInfo.table) : me.initRenderTpl()
  412. },
  413. i, frameElNames, len, suffix, frameGenId;
  414. me.initStyles(me.protoEl);
  415. me.protoEl.writeTo(config);
  416. me.protoEl.flush();
  417. if (Ext.isString(autoEl)) {
  418. config.tag = autoEl;
  419. } else {
  420. Ext.apply(config, autoEl); // harmless if !autoEl
  421. }
  422. // It's important to assign the id here as an autoEl.id could have been (wrongly) applied and this would get things out of sync
  423. config.id = me.id;
  424. if (config.tpl) {
  425. // Use the framingTpl as the main content creating template. It will call out to this.applyRenderTpl(out, values)
  426. if (frameInfo) {
  427. frameElNames = me.frameElNames;
  428. len = frameElNames.length;
  429. frameGenId = me.id + '-frame1';
  430. me.frameGenId = 1;
  431. config.tplData = Ext.apply({}, {
  432. $comp: me,
  433. fgid: frameGenId,
  434. ui: me.ui,
  435. uiCls: me.uiCls,
  436. frameCls: me.frameCls,
  437. baseCls: me.baseCls,
  438. frameWidth: frameInfo.maxWidth,
  439. top: !!frameInfo.top,
  440. left: !!frameInfo.left,
  441. right: !!frameInfo.right,
  442. bottom: !!frameInfo.bottom,
  443. renderData: me.initRenderData()
  444. }, me.getFramePositions(frameInfo));
  445. // Add the childEls for each of the frame elements
  446. for (i = 0; i < len; i++) {
  447. suffix = frameElNames[i];
  448. me.addChildEls({ name: 'frame' + suffix, id: frameGenId + suffix });
  449. }
  450. // Panel must have a frameBody
  451. me.addChildEls({
  452. name: 'frameBody',
  453. id: frameGenId + 'MC'
  454. });
  455. } else {
  456. config.tplData = me.initRenderData();
  457. }
  458. }
  459. return config;
  460. },
  461. // Create the framingTpl from the string.
  462. // Poke in a reference to applyRenderTpl(frameInfo, out)
  463. initFramingTpl: function(table) {
  464. var tpl = table ? this.getTpl('frameTableTpl') : this.getTpl('frameTpl');
  465. if (tpl && !tpl.applyRenderTpl) {
  466. this.setupFramingTpl(tpl);
  467. }
  468. return tpl;
  469. },
  470. /**
  471. * @private
  472. * Inject a reference to the function which applies the render template into the framing template. The framing template
  473. * wraps the content.
  474. */
  475. setupFramingTpl: function(frameTpl) {
  476. frameTpl.applyRenderTpl = this.doApplyRenderTpl;
  477. frameTpl.renderDockedItems = this.doRenderFramingDockedItems;
  478. },
  479. /**
  480. * This function takes the position argument passed to onRender and returns a
  481. * DOM element that you can use in the insertBefore.
  482. * @param {String/Number/Ext.dom.Element/HTMLElement} position Index, element id or element you want
  483. * to put this component before.
  484. * @return {HTMLElement} DOM element that you can use in the insertBefore
  485. */
  486. getInsertPosition: function(position) {
  487. // Convert the position to an element to insert before
  488. if (position !== undefined) {
  489. if (Ext.isNumber(position)) {
  490. position = this.container.dom.childNodes[position];
  491. }
  492. else {
  493. position = Ext.getDom(position);
  494. }
  495. }
  496. return position;
  497. },
  498. getRenderTree: function() {
  499. var me = this;
  500. if (!me.hasListeners.beforerender || me.fireEvent('beforerender', me) !== false) {
  501. me.beforeRender();
  502. // Flag to let the layout's finishRenderItems and afterFinishRenderItems
  503. // know which items to process
  504. me.rendering = true;
  505. if (me.el) {
  506. // Since we are producing a render tree, we produce a "proxy el" that will
  507. // sit in the rendered DOM precisely where me.el belongs. We replace the
  508. // proxy el in the finishRender phase.
  509. return {
  510. tag: 'div',
  511. id: (me.$pid = Ext.id())
  512. };
  513. }
  514. return me.getElConfig();
  515. }
  516. return null;
  517. },
  518. initContainer: function(container) {
  519. var me = this;
  520. // If you render a component specifying the el, we get the container
  521. // of the el, and make sure we dont move the el around in the dom
  522. // during the render
  523. if (!container && me.el) {
  524. container = me.el.dom.parentNode;
  525. me.allowDomMove = false;
  526. }
  527. me.container = container.dom ? container : Ext.get(container);
  528. return me.container;
  529. },
  530. /**
  531. * Initialized the renderData to be used when rendering the renderTpl.
  532. * @return {Object} Object with keys and values that are going to be applied to the renderTpl
  533. * @private
  534. */
  535. initRenderData: function() {
  536. var me = this;
  537. return Ext.apply({
  538. $comp: me,
  539. id: me.id,
  540. ui: me.ui,
  541. uiCls: me.uiCls,
  542. baseCls: me.baseCls,
  543. componentCls: me.componentCls,
  544. frame: me.frame
  545. }, me.renderData);
  546. },
  547. /**
  548. * Initializes the renderTpl.
  549. * @return {Ext.XTemplate} The renderTpl XTemplate instance.
  550. * @private
  551. */
  552. initRenderTpl: function() {
  553. var tpl = this.getTpl('renderTpl');
  554. if (tpl && !tpl.renderContent) {
  555. this.setupRenderTpl(tpl);
  556. }
  557. return tpl;
  558. },
  559. /**
  560. * Template method called when this Component's DOM structure is created.
  561. *
  562. * At this point, this Component's (and all descendants') DOM structure *exists* but it has not
  563. * been layed out (positioned and sized).
  564. *
  565. * Subclasses which override this to gain access to the structure at render time should
  566. * call the parent class's method before attempting to access any child elements of the Component.
  567. *
  568. * @param {Ext.core.Element} parentNode The parent Element in which this Component's encapsulating element is contained.
  569. * @param {Number} containerIdx The index within the parent Container's child collection of this Component.
  570. *
  571. * @template
  572. * @protected
  573. */
  574. onRender: function(parentNode, containerIdx) {
  575. var me = this,
  576. x = me.x,
  577. y = me.y,
  578. lastBox, width, height,
  579. el = me.el,
  580. body = Ext.getBody().dom;
  581. // Wrap this Component in a reset wraper if necessary
  582. if (Ext.scopeResetCSS && !me.ownerCt) {
  583. // If this component's el is the body element, we add the reset class to the html tag
  584. if (el.dom === body) {
  585. el.parent().addCls(Ext.resetCls);
  586. }
  587. // Otherwise, we ensure that there is a wrapper which has the reset class
  588. else {
  589. // Floaters rendered into the body can all be bumped into the common reset element
  590. if (me.floating && me.el.dom.parentNode === body) {
  591. Ext.resetElement.appendChild(me.el);
  592. }
  593. // Else we wrap this element in an element that adds the reset class.
  594. else {
  595. // Wrap this Component's DOM with a reset structure as determined in EventManager's initExtCss closure.
  596. me.resetEl = el.wrap(Ext.resetElementSpec, false, Ext.supports.CSS3LinearGradient ? undefined : '*');
  597. }
  598. }
  599. }
  600. me.applyRenderSelectors();
  601. // Flag set on getRenderTree to flag to the layout's postprocessing routine that
  602. // the Component is in the process of being rendered and needs postprocessing.
  603. delete me.rendering;
  604. me.rendered = true;
  605. // We need to remember these to avoid writing them during the initial layout:
  606. lastBox = null;
  607. if (x !== undefined) {
  608. lastBox = lastBox || {};
  609. lastBox.x = x;
  610. }
  611. if (y !== undefined) {
  612. lastBox = lastBox || {};
  613. lastBox.y = y;
  614. }
  615. // Framed components need their width/height to apply to the frame, which is
  616. // best handled in layout at present.
  617. // If we're using the content box model, we also cannot assign initial sizes since we do not know the border widths to subtract
  618. if (!me.getFrameInfo() && Ext.isBorderBox) {
  619. width = me.width;
  620. height = me.height;
  621. if (typeof width == 'number') {
  622. lastBox = lastBox || {};
  623. lastBox.width = width;
  624. }
  625. if (typeof height == 'number') {
  626. lastBox = lastBox || {};
  627. lastBox.height = height;
  628. }
  629. }
  630. me.lastBox = me.el.lastBox = lastBox;
  631. },
  632. /**
  633. * Renders the Component into the passed HTML element.
  634. *
  635. * **If you are using a {@link Ext.container.Container Container} object to house this
  636. * Component, then do not use the render method.**
  637. *
  638. * A Container's child Components are rendered by that Container's
  639. * {@link Ext.container.Container#layout layout} manager when the Container is first rendered.
  640. *
  641. * If the Container is already rendered when a new child Component is added, you may need to call
  642. * the Container's {@link Ext.container.Container#doLayout doLayout} to refresh the view which
  643. * causes any unrendered child Components to be rendered. This is required so that you can add
  644. * multiple child components if needed while only refreshing the layout once.
  645. *
  646. * When creating complex UIs, it is important to remember that sizing and positioning
  647. * of child items is the responsibility of the Container's {@link Ext.container.Container#layout layout}
  648. * manager. If you expect child items to be sized in response to user interactions, you must
  649. * configure the Container with a layout manager which creates and manages the type of layout you
  650. * have in mind.
  651. *
  652. * **Omitting the Container's {@link Ext.Container#layout layout} config means that a basic
  653. * layout manager is used which does nothing but render child components sequentially into the
  654. * Container. No sizing or positioning will be performed in this situation.**
  655. *
  656. * @param {Ext.Element/HTMLElement/String} [container] The element this Component should be
  657. * rendered into. If it is being created from existing markup, this should be omitted.
  658. * @param {String/Number} [position] The element ID or DOM node index within the container **before**
  659. * which this component will be inserted (defaults to appending to the end of the container)
  660. */
  661. render: function(container, position) {
  662. var me = this,
  663. el = me.el && (me.el = Ext.get(me.el)), // ensure me.el is wrapped
  664. vetoed,
  665. tree,
  666. nextSibling;
  667. Ext.suspendLayouts();
  668. container = me.initContainer(container);
  669. nextSibling = me.getInsertPosition(position);
  670. if (!el) {
  671. tree = me.getRenderTree();
  672. if (me.ownerLayout && me.ownerLayout.transformItemRenderTree) {
  673. tree = me.ownerLayout.transformItemRenderTree(tree);
  674. }
  675. // tree will be null if a beforerender listener returns false
  676. if (tree) {
  677. if (nextSibling) {
  678. el = Ext.DomHelper.insertBefore(nextSibling, tree);
  679. } else {
  680. el = Ext.DomHelper.append(container, tree);
  681. }
  682. me.wrapPrimaryEl(el);
  683. }
  684. } else {
  685. if (!me.hasListeners.beforerender || me.fireEvent('beforerender', me) !== false) {
  686. // Set configured styles on pre-rendered Component's element
  687. me.initStyles(el);
  688. if (me.allowDomMove !== false) {
  689. //debugger; // TODO
  690. if (nextSibling) {
  691. container.dom.insertBefore(el.dom, nextSibling);
  692. } else {
  693. container.dom.appendChild(el.dom);
  694. }
  695. }
  696. } else {
  697. vetoed = true;
  698. }
  699. }
  700. if (el && !vetoed) {
  701. me.finishRender(position);
  702. }
  703. Ext.resumeLayouts(!container.isDetachedBody);
  704. },
  705. /**
  706. * Ensures that this component is attached to `document.body`. If the component was
  707. * rendered to {@link Ext#getDetachedBody}, then it will be appended to `document.body`.
  708. * Any configured position is also restored.
  709. * @param {Boolean} [runLayout=false] True to run the component's layout.
  710. */
  711. ensureAttachedToBody: function (runLayout) {
  712. var comp = this,
  713. body;
  714. while (comp.ownerCt) {
  715. comp = comp.ownerCt;
  716. }
  717. if (comp.container.isDetachedBody) {
  718. comp.container = body = Ext.resetElement;
  719. body.appendChild(comp.el.dom);
  720. if (runLayout) {
  721. comp.updateLayout();
  722. }
  723. if (typeof comp.x == 'number' || typeof comp.y == 'number') {
  724. comp.setPosition(comp.x, comp.y);
  725. }
  726. }
  727. },
  728. setupRenderTpl: function (renderTpl) {
  729. renderTpl.renderBody = renderTpl.renderContent = this.doRenderContent;
  730. },
  731. wrapPrimaryEl: function (dom) {
  732. this.el = Ext.get(dom, true);
  733. },
  734. /**
  735. * @private
  736. */
  737. initFrame : function() {
  738. if (Ext.supports.CSS3BorderRadius || !this.frame) {
  739. return;
  740. }
  741. var me = this,
  742. frameInfo = me.getFrameInfo(),
  743. frameWidth, frameTpl, frameGenId,
  744. i,
  745. frameElNames = me.frameElNames,
  746. len = frameElNames.length,
  747. suffix;
  748. if (frameInfo) {
  749. frameWidth = frameInfo.maxWidth;
  750. frameTpl = me.getFrameTpl(frameInfo.table);
  751. // since we render id's into the markup and id's NEED to be unique, we have a
  752. // simple strategy for numbering their generations.
  753. me.frameGenId = frameGenId = (me.frameGenId || 0) + 1;
  754. frameGenId = me.id + '-frame' + frameGenId;
  755. // Here we render the frameTpl to this component. This inserts the 9point div or the table framing.
  756. frameTpl.insertFirst(me.el, Ext.apply({
  757. $comp: me,
  758. fgid: frameGenId,
  759. ui: me.ui,
  760. uiCls: me.uiCls,
  761. frameCls: me.frameCls,
  762. baseCls: me.baseCls,
  763. frameWidth: frameWidth,
  764. top: !!frameInfo.top,
  765. left: !!frameInfo.left,
  766. right: !!frameInfo.right,
  767. bottom: !!frameInfo.bottom
  768. }, me.getFramePositions(frameInfo)));
  769. // The frameBody is returned in getTargetEl, so that layouts render items to the correct target.
  770. me.frameBody = me.el.down('.' + me.frameCls + '-mc');
  771. // Clean out the childEls for the old frame elements (the majority of the els)
  772. me.removeChildEls(function (c) {
  773. return c.id && me.frameIdRegex.test(c.id);
  774. });
  775. // Grab references to the childEls for each of the new frame elements
  776. for (i = 0; i < len; i++) {
  777. suffix = frameElNames[i];
  778. me['frame' + suffix] = me.el.getById(frameGenId + suffix);
  779. }
  780. }
  781. },
  782. updateFrame: function() {
  783. if (Ext.supports.CSS3BorderRadius || !this.frame) {
  784. return;
  785. }
  786. var me = this,
  787. wasTable = this.frameSize && this.frameSize.table,
  788. oldFrameTL = this.frameTL,
  789. oldFrameBL = this.frameBL,
  790. oldFrameML = this.frameML,
  791. oldFrameMC = this.frameMC,
  792. newMCClassName;
  793. this.initFrame();
  794. if (oldFrameMC) {
  795. if (me.frame) {
  796. // Store the class names set on the new MC
  797. newMCClassName = this.frameMC.dom.className;
  798. // Framing elements have been selected in initFrame, no need to run applyRenderSelectors
  799. // Replace the new mc with the old mc
  800. oldFrameMC.insertAfter(this.frameMC);
  801. this.frameMC.remove();
  802. // Restore the reference to the old frame mc as the framebody
  803. this.frameBody = this.frameMC = oldFrameMC;
  804. // Apply the new mc classes to the old mc element
  805. oldFrameMC.dom.className = newMCClassName;
  806. // Remove the old framing
  807. if (wasTable) {
  808. me.el.query('> table')[1].remove();
  809. }
  810. else {
  811. if (oldFrameTL) {
  812. oldFrameTL.remove();
  813. }
  814. if (oldFrameBL) {
  815. oldFrameBL.remove();
  816. }
  817. if (oldFrameML) {
  818. oldFrameML.remove();
  819. }
  820. }
  821. }
  822. }
  823. else if (me.frame) {
  824. this.applyRenderSelectors();
  825. }
  826. },
  827. /**
  828. * @private
  829. * On render, reads an encoded style attribute, "background-position" from the style of this Component's element.
  830. * This information is memoized based upon the CSS class name of this Component's element.
  831. * Because child Components are rendered as textual HTML as part of the topmost Container, a dummy div is inserted
  832. * into the document to receive the document element's CSS class name, and therefore style attributes.
  833. */
  834. getFrameInfo: function() {
  835. // If native framing can be used, or this component is not going to be framed, then do not attempt to read CSS framing info.
  836. if (Ext.supports.CSS3BorderRadius || !this.frame) {
  837. return false;
  838. }
  839. var me = this,
  840. frameInfoCache = me.frameInfoCache,
  841. el = me.el || me.protoEl,
  842. cls = el.dom ? el.dom.className : el.classList.join(' '),
  843. frameInfo = frameInfoCache[cls],
  844. styleEl, left, top, info;
  845. if (frameInfo == null) {
  846. // Get the singleton frame style proxy with our el class name stamped into it.
  847. styleEl = Ext.fly(me.getStyleProxy(cls), 'frame-style-el');
  848. left = styleEl.getStyle('background-position-x');
  849. top = styleEl.getStyle('background-position-y');
  850. // Some browsers don't support background-position-x and y, so for those
  851. // browsers let's split background-position into two parts.
  852. if (!left && !top) {
  853. info = styleEl.getStyle('background-position').split(' ');
  854. left = info[0];
  855. top = info[1];
  856. }
  857. frameInfo = me.calculateFrame(left, top);
  858. if (frameInfo) {
  859. // Just to be sure we set the background image of the el to none.
  860. el.setStyle('background-image', 'none');
  861. }
  862. //<debug error>
  863. // This happens when you set frame: true explicitly without using the x-frame mixin in sass.
  864. // This way IE can't figure out what sizes to use and thus framing can't work.
  865. if (me.frame === true && !frameInfo) {
  866. Ext.log.error('You have set frame: true explicity on this component (' + me.getXType() + ') and it ' +
  867. 'does not have any framing defined in the CSS template. In this case IE cannot figure out ' +
  868. 'what sizes to use and thus framing on this component will be disabled.');
  869. }
  870. //</debug>
  871. frameInfoCache[cls] = frameInfo;
  872. }
  873. me.frame = !!frameInfo;
  874. me.frameSize = frameInfo;
  875. return frameInfo;
  876. },
  877. calculateFrame: function(left, top){
  878. // We actually pass a string in the form of '[type][tl][tr]px [direction][br][bl]px' as
  879. // the background position of this.el from the CSS to indicate to IE that this component needs
  880. // framing. We parse it here.
  881. if (!(parseInt(left, 10) >= 1000000 && parseInt(top, 10) >= 1000000)) {
  882. return false;
  883. }
  884. var max = Math.max,
  885. tl = parseInt(left.substr(3, 2), 10),
  886. tr = parseInt(left.substr(5, 2), 10),
  887. br = parseInt(top.substr(3, 2), 10),
  888. bl = parseInt(top.substr(5, 2), 10),
  889. frameInfo = {
  890. // Table markup starts with 110, div markup with 100.
  891. table: left.substr(0, 3) == '110',
  892. // Determine if we are dealing with a horizontal or vertical component
  893. vertical: top.substr(0, 3) == '110',
  894. // Get and parse the different border radius sizes
  895. top: max(tl, tr),
  896. right: max(tr, br),
  897. bottom: max(bl, br),
  898. left: max(tl, bl)
  899. };
  900. frameInfo.maxWidth = max(frameInfo.top, frameInfo.right, frameInfo.bottom, frameInfo.left);
  901. frameInfo.width = frameInfo.left + frameInfo.right;
  902. frameInfo.height = frameInfo.top + frameInfo.bottom;
  903. return frameInfo;
  904. },
  905. /**
  906. * @private
  907. * Returns an offscreen div with the same class name as the element this is being rendered.
  908. * This is because child item rendering takes place in a detached div which, being not part of the document, has no styling.
  909. */
  910. getStyleProxy: function(cls) {
  911. var result = this.styleProxyEl || (Ext.AbstractComponent.prototype.styleProxyEl = Ext.resetElement.createChild({
  912. style: {
  913. position: 'absolute',
  914. top: '-10000px'
  915. }
  916. }, null, true));
  917. result.className = cls;
  918. return result;
  919. },
  920. getFramePositions: function(frameInfo) {
  921. var me = this,
  922. frameWidth = frameInfo.maxWidth,
  923. dock = me.dock,
  924. positions, tc, bc, ml, mr;
  925. if (frameInfo.vertical) {
  926. tc = '0 -' + (frameWidth * 0) + 'px';
  927. bc = '0 -' + (frameWidth * 1) + 'px';
  928. if (dock && dock == "right") {
  929. tc = 'right -' + (frameWidth * 0) + 'px';
  930. bc = 'right -' + (frameWidth * 1) + 'px';
  931. }
  932. positions = {
  933. tl: '0 -' + (frameWidth * 0) + 'px',
  934. tr: '0 -' + (frameWidth * 1) + 'px',
  935. bl: '0 -' + (frameWidth * 2) + 'px',
  936. br: '0 -' + (frameWidth * 3) + 'px',
  937. ml: '-' + (frameWidth * 1) + 'px 0',
  938. mr: 'right 0',
  939. tc: tc,
  940. bc: bc
  941. };
  942. } else {
  943. ml = '-' + (frameWidth * 0) + 'px 0';
  944. mr = 'right 0';
  945. if (dock && dock == "bottom") {
  946. ml = 'left bottom';
  947. mr = 'right bottom';
  948. }
  949. positions = {
  950. tl: '0 -' + (frameWidth * 2) + 'px',
  951. tr: 'right -' + (frameWidth * 3) + 'px',
  952. bl: '0 -' + (frameWidth * 4) + 'px',
  953. br: 'right -' + (frameWidth * 5) + 'px',
  954. ml: ml,
  955. mr: mr,
  956. tc: '0 -' + (frameWidth * 0) + 'px',
  957. bc: '0 -' + (frameWidth * 1) + 'px'
  958. };
  959. }
  960. return positions;
  961. },
  962. /**
  963. * @private
  964. */
  965. getFrameTpl : function(table) {
  966. return this.getTpl(table ? 'frameTableTpl' : 'frameTpl');
  967. },
  968. // Cache the frame information object so as not to cause style recalculations
  969. frameInfoCache: {}
  970. });