Dashboard sipadu mbip
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

widget-stack.js 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. /*
  2. Copyright (c) 2010, Yahoo! Inc. All rights reserved.
  3. Code licensed under the BSD License:
  4. http://developer.yahoo.com/yui/license.html
  5. version: 3.4.0
  6. build: nightly
  7. */
  8. YUI.add('widget-stack', function(Y) {
  9. /**
  10. * Provides stackable (z-index) support for Widgets through an extension.
  11. *
  12. * @module widget-stack
  13. */
  14. var L = Y.Lang,
  15. UA = Y.UA,
  16. Node = Y.Node,
  17. Widget = Y.Widget,
  18. ZINDEX = "zIndex",
  19. SHIM = "shim",
  20. VISIBLE = "visible",
  21. BOUNDING_BOX = "boundingBox",
  22. RENDER_UI = "renderUI",
  23. BIND_UI = "bindUI",
  24. SYNC_UI = "syncUI",
  25. OFFSET_WIDTH = "offsetWidth",
  26. OFFSET_HEIGHT = "offsetHeight",
  27. PARENT_NODE = "parentNode",
  28. FIRST_CHILD = "firstChild",
  29. OWNER_DOCUMENT = "ownerDocument",
  30. WIDTH = "width",
  31. HEIGHT = "height",
  32. PX = "px",
  33. // HANDLE KEYS
  34. SHIM_DEFERRED = "shimdeferred",
  35. SHIM_RESIZE = "shimresize",
  36. // Events
  37. VisibleChange = "visibleChange",
  38. WidthChange = "widthChange",
  39. HeightChange = "heightChange",
  40. ShimChange = "shimChange",
  41. ZIndexChange = "zIndexChange",
  42. ContentUpdate = "contentUpdate",
  43. // CSS
  44. STACKED = "stacked";
  45. /**
  46. * Widget extension, which can be used to add stackable (z-index) support to the
  47. * base Widget class along with a shimming solution, through the
  48. * <a href="Base.html#method_build">Base.build</a> method.
  49. *
  50. * @class WidgetStack
  51. * @param {Object} User configuration object
  52. */
  53. function Stack(config) {
  54. this._stackNode = this.get(BOUNDING_BOX);
  55. this._stackHandles = {};
  56. // WIDGET METHOD OVERLAP
  57. Y.after(this._renderUIStack, this, RENDER_UI);
  58. Y.after(this._syncUIStack, this, SYNC_UI);
  59. Y.after(this._bindUIStack, this, BIND_UI);
  60. }
  61. // Static Properties
  62. /**
  63. * Static property used to define the default attribute
  64. * configuration introduced by WidgetStack.
  65. *
  66. * @property ATTRS
  67. * @type Object
  68. * @static
  69. */
  70. Stack.ATTRS = {
  71. /**
  72. * @attribute shim
  73. * @type boolean
  74. * @default false, for all browsers other than IE6, for which a shim is enabled by default.
  75. *
  76. * @description Boolean flag to indicate whether or not a shim should be added to the Widgets
  77. * boundingBox, to protect it from select box bleedthrough.
  78. */
  79. shim: {
  80. value: (UA.ie == 6)
  81. },
  82. /**
  83. * @attribute zIndex
  84. * @type number
  85. * @default 0
  86. * @description The z-index to apply to the Widgets boundingBox. Non-numerical values for
  87. * zIndex will be converted to 0
  88. */
  89. zIndex: {
  90. value : 0,
  91. setter: '_setZIndex'
  92. }
  93. };
  94. /**
  95. * The HTML parsing rules for the WidgetStack class.
  96. *
  97. * @property HTML_PARSER
  98. * @static
  99. * @type Object
  100. */
  101. Stack.HTML_PARSER = {
  102. zIndex: function (srcNode) {
  103. return this._parseZIndex(srcNode);
  104. }
  105. };
  106. /**
  107. * Default class used to mark the shim element
  108. *
  109. * @property SHIM_CLASS_NAME
  110. * @type String
  111. * @static
  112. * @default "yui3-widget-shim"
  113. */
  114. Stack.SHIM_CLASS_NAME = Widget.getClassName(SHIM);
  115. /**
  116. * Default class used to mark the boundingBox of a stacked widget.
  117. *
  118. * @property STACKED_CLASS_NAME
  119. * @type String
  120. * @static
  121. * @default "yui3-widget-stacked"
  122. */
  123. Stack.STACKED_CLASS_NAME = Widget.getClassName(STACKED);
  124. /**
  125. * Default markup template used to generate the shim element.
  126. *
  127. * @property SHIM_TEMPLATE
  128. * @type String
  129. * @static
  130. */
  131. Stack.SHIM_TEMPLATE = '<iframe class="' + Stack.SHIM_CLASS_NAME + '" frameborder="0" title="Widget Stacking Shim" src="javascript:false" tabindex="-1" role="presentation"></iframe>';
  132. Stack.prototype = {
  133. /**
  134. * Synchronizes the UI to match the Widgets stack state. This method in
  135. * invoked after syncUI is invoked for the Widget class using YUI's aop infrastructure.
  136. *
  137. * @method _syncUIStack
  138. * @protected
  139. */
  140. _syncUIStack: function() {
  141. this._uiSetShim(this.get(SHIM));
  142. this._uiSetZIndex(this.get(ZINDEX));
  143. },
  144. /**
  145. * Binds event listeners responsible for updating the UI state in response to
  146. * Widget stack related state changes.
  147. * <p>
  148. * This method is invoked after bindUI is invoked for the Widget class
  149. * using YUI's aop infrastructure.
  150. * </p>
  151. * @method _bindUIStack
  152. * @protected
  153. */
  154. _bindUIStack: function() {
  155. this.after(ShimChange, this._afterShimChange);
  156. this.after(ZIndexChange, this._afterZIndexChange);
  157. },
  158. /**
  159. * Creates/Initializes the DOM to support stackability.
  160. * <p>
  161. * This method in invoked after renderUI is invoked for the Widget class
  162. * using YUI's aop infrastructure.
  163. * </p>
  164. * @method _renderUIStack
  165. * @protected
  166. */
  167. _renderUIStack: function() {
  168. this._stackNode.addClass(Stack.STACKED_CLASS_NAME);
  169. },
  170. /**
  171. Parses a `zIndex` attribute value from this widget's `srcNode`.
  172. @method _parseZIndex
  173. @param {Node} srcNode The node to parse a `zIndex` value from.
  174. @return {Mixed} The parsed `zIndex` value.
  175. @protected
  176. **/
  177. _parseZIndex: function (srcNode) {
  178. var zIndex;
  179. // Prefers how WebKit handles `z-index` which better matches the
  180. // spec:
  181. //
  182. // * http://www.w3.org/TR/CSS2/visuren.html#z-index
  183. // * https://bugs.webkit.org/show_bug.cgi?id=15562
  184. //
  185. // When a node isn't rendered in the document, and/or when a
  186. // node is not positioned, then it doesn't have a context to derive
  187. // a valid `z-index` value from.
  188. if (!srcNode.inDoc() || srcNode.getStyle('position') === 'static') {
  189. zIndex = 'auto';
  190. } else {
  191. // Uses `getComputedStyle()` because it has greater accuracy in
  192. // more browsers than `getStyle()` does for `z-index`.
  193. zIndex = srcNode.getComputedStyle('zIndex');
  194. }
  195. // This extension adds a stacking context to widgets, therefore a
  196. // `srcNode` witout a stacking context (i.e. "auto") will return
  197. // `null` from this DOM parser. This way the widget's default or
  198. // user provided value for `zIndex` will be used.
  199. return zIndex === 'auto' ? null : zIndex;
  200. },
  201. /**
  202. * Default setter for zIndex attribute changes. Normalizes zIndex values to
  203. * numbers, converting non-numerical values to 0.
  204. *
  205. * @method _setZIndex
  206. * @protected
  207. * @param {String | Number} zIndex
  208. * @return {Number} Normalized zIndex
  209. */
  210. _setZIndex: function(zIndex) {
  211. if (L.isString(zIndex)) {
  212. zIndex = parseInt(zIndex, 10);
  213. }
  214. if (!L.isNumber(zIndex)) {
  215. zIndex = 0;
  216. }
  217. return zIndex;
  218. },
  219. /**
  220. * Default attribute change listener for the shim attribute, responsible
  221. * for updating the UI, in response to attribute changes.
  222. *
  223. * @method _afterShimChange
  224. * @protected
  225. * @param {EventFacade} e The event facade for the attribute change
  226. */
  227. _afterShimChange : function(e) {
  228. this._uiSetShim(e.newVal);
  229. },
  230. /**
  231. * Default attribute change listener for the zIndex attribute, responsible
  232. * for updating the UI, in response to attribute changes.
  233. *
  234. * @method _afterZIndexChange
  235. * @protected
  236. * @param {EventFacade} e The event facade for the attribute change
  237. */
  238. _afterZIndexChange : function(e) {
  239. this._uiSetZIndex(e.newVal);
  240. },
  241. /**
  242. * Updates the UI to reflect the zIndex value passed in.
  243. *
  244. * @method _uiSetZIndex
  245. * @protected
  246. * @param {number} zIndex The zindex to be reflected in the UI
  247. */
  248. _uiSetZIndex: function (zIndex) {
  249. this._stackNode.setStyle(ZINDEX, zIndex);
  250. },
  251. /**
  252. * Updates the UI to enable/disable the shim. If the widget is not currently visible,
  253. * creation of the shim is deferred until it is made visible, for performance reasons.
  254. *
  255. * @method _uiSetShim
  256. * @protected
  257. * @param {boolean} enable If true, creates/renders the shim, if false, removes it.
  258. */
  259. _uiSetShim: function (enable) {
  260. if (enable) {
  261. // Lazy creation
  262. if (this.get(VISIBLE)) {
  263. this._renderShim();
  264. } else {
  265. this._renderShimDeferred();
  266. }
  267. // Eagerly attach resize handlers
  268. //
  269. // Required because of Event stack behavior, commit ref: cd8dddc
  270. // Should be revisted after Ticket #2531067 is resolved.
  271. if (UA.ie == 6) {
  272. this._addShimResizeHandlers();
  273. }
  274. } else {
  275. this._destroyShim();
  276. }
  277. },
  278. /**
  279. * Sets up change handlers for the visible attribute, to defer shim creation/rendering
  280. * until the Widget is made visible.
  281. *
  282. * @method _renderShimDeferred
  283. * @private
  284. */
  285. _renderShimDeferred : function() {
  286. this._stackHandles[SHIM_DEFERRED] = this._stackHandles[SHIM_DEFERRED] || [];
  287. var handles = this._stackHandles[SHIM_DEFERRED],
  288. createBeforeVisible = function(e) {
  289. if (e.newVal) {
  290. this._renderShim();
  291. }
  292. };
  293. handles.push(this.on(VisibleChange, createBeforeVisible));
  294. // Depending how how Ticket #2531067 is resolved, a reversal of
  295. // commit ref: cd8dddc could lead to a more elagent solution, with
  296. // the addition of this line here:
  297. //
  298. // handles.push(this.after(VisibleChange, this.sizeShim));
  299. },
  300. /**
  301. * Sets up event listeners to resize the shim when the size of the Widget changes.
  302. * <p>
  303. * NOTE: This method is only used for IE6 currently, since IE6 doesn't support a way to
  304. * resize the shim purely through CSS, when the Widget does not have an explicit width/height
  305. * set.
  306. * </p>
  307. * @method _addShimResizeHandlers
  308. * @private
  309. */
  310. _addShimResizeHandlers : function() {
  311. this._stackHandles[SHIM_RESIZE] = this._stackHandles[SHIM_RESIZE] || [];
  312. var sizeShim = this.sizeShim,
  313. handles = this._stackHandles[SHIM_RESIZE];
  314. handles.push(this.after(VisibleChange, sizeShim));
  315. handles.push(this.after(WidthChange, sizeShim));
  316. handles.push(this.after(HeightChange, sizeShim));
  317. handles.push(this.after(ContentUpdate, sizeShim));
  318. },
  319. /**
  320. * Detaches any handles stored for the provided key
  321. *
  322. * @method _detachStackHandles
  323. * @param String handleKey The key defining the group of handles which should be detached
  324. * @private
  325. */
  326. _detachStackHandles : function(handleKey) {
  327. var handles = this._stackHandles[handleKey],
  328. handle;
  329. if (handles && handles.length > 0) {
  330. while((handle = handles.pop())) {
  331. handle.detach();
  332. }
  333. }
  334. },
  335. /**
  336. * Creates the shim element and adds it to the DOM
  337. *
  338. * @method _renderShim
  339. * @private
  340. */
  341. _renderShim : function() {
  342. var shimEl = this._shimNode,
  343. stackEl = this._stackNode;
  344. if (!shimEl) {
  345. shimEl = this._shimNode = this._getShimTemplate();
  346. stackEl.insertBefore(shimEl, stackEl.get(FIRST_CHILD));
  347. this._detachStackHandles(SHIM_DEFERRED);
  348. this.sizeShim();
  349. }
  350. },
  351. /**
  352. * Removes the shim from the DOM, and detaches any related event
  353. * listeners.
  354. *
  355. * @method _destroyShim
  356. * @private
  357. */
  358. _destroyShim : function() {
  359. if (this._shimNode) {
  360. this._shimNode.get(PARENT_NODE).removeChild(this._shimNode);
  361. this._shimNode = null;
  362. this._detachStackHandles(SHIM_DEFERRED);
  363. this._detachStackHandles(SHIM_RESIZE);
  364. }
  365. },
  366. /**
  367. * For IE6, synchronizes the size and position of iframe shim to that of
  368. * Widget bounding box which it is protecting. For all other browsers,
  369. * this method does not do anything.
  370. *
  371. * @method sizeShim
  372. */
  373. sizeShim: function () {
  374. var shim = this._shimNode,
  375. node = this._stackNode;
  376. if (shim && UA.ie === 6 && this.get(VISIBLE)) {
  377. shim.setStyle(WIDTH, node.get(OFFSET_WIDTH) + PX);
  378. shim.setStyle(HEIGHT, node.get(OFFSET_HEIGHT) + PX);
  379. }
  380. },
  381. /**
  382. * Creates a cloned shim node, using the SHIM_TEMPLATE html template, for use on a new instance.
  383. *
  384. * @method _getShimTemplate
  385. * @private
  386. * @return {Node} node A new shim Node instance.
  387. */
  388. _getShimTemplate : function() {
  389. return Node.create(Stack.SHIM_TEMPLATE, this._stackNode.get(OWNER_DOCUMENT));
  390. }
  391. };
  392. Y.WidgetStack = Stack;
  393. }, '3.4.0' ,{requires:['base-build', 'widget']});