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