SpryCollapsiblePanel.js
上传用户:szhf331
上传日期:2022-06-22
资源大小:1032k
文件大小:13k
源码类别:

行业应用

开发平台:

JavaScript

  1. /* SpryCollapsiblePanel.js - Revision: Spry Preview Release 1.4 */
  2. // Copyright (c) 2006. Adobe Systems Incorporated.
  3. // All rights reserved.
  4. //
  5. // Redistribution and use in source and binary forms, with or without
  6. // modification, are permitted provided that the following conditions are met:
  7. //
  8. //   * Redistributions of source code must retain the above copyright notice,
  9. //     this list of conditions and the following disclaimer.
  10. //   * Redistributions in binary form must reproduce the above copyright notice,
  11. //     this list of conditions and the following disclaimer in the documentation
  12. //     and/or other materials provided with the distribution.
  13. //   * Neither the name of Adobe Systems Incorporated nor the names of its
  14. //     contributors may be used to endorse or promote products derived from this
  15. //     software without specific prior written permission.
  16. //
  17. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  18. // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19. // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20. // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  21. // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  22. // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  23. // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  24. // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  25. // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  26. // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  27. // POSSIBILITY OF SUCH DAMAGE.
  28. var Spry;
  29. if (!Spry) Spry = {};
  30. if (!Spry.Widget) Spry.Widget = {};
  31. Spry.Widget.CollapsiblePanel = function(element, opts)
  32. {
  33. this.init(element);
  34. Spry.Widget.CollapsiblePanel.setOptions(this, opts);
  35. this.attachBehaviors();
  36. };
  37. Spry.Widget.CollapsiblePanel.prototype.init = function(element)
  38. {
  39. this.element = this.getElement(element);
  40. this.focusElement = null;
  41. this.hoverClass = "CollapsiblePanelTabHover";
  42. this.openClass = "CollapsiblePanelOpen";
  43. this.closedClass = "CollapsiblePanelClosed";
  44. this.focusedClass = "CollapsiblePanelFocused";
  45. this.enableAnimation = true;
  46. this.enableKeyboardNavigation = true;
  47. this.animator = null;
  48. this.hasFocus = false;
  49. this.contentIsOpen = true;
  50. };
  51. Spry.Widget.CollapsiblePanel.prototype.getElement = function(ele)
  52. {
  53. if (ele && typeof ele == "string")
  54. return document.getElementById(ele);
  55. return ele;
  56. };
  57. Spry.Widget.CollapsiblePanel.prototype.addClassName = function(ele, className)
  58. {
  59. if (!ele || !className || (ele.className && ele.className.search(new RegExp("\b" + className + "\b")) != -1))
  60. return;
  61. ele.className += (ele.className ? " " : "") + className;
  62. };
  63. Spry.Widget.CollapsiblePanel.prototype.removeClassName = function(ele, className)
  64. {
  65. if (!ele || !className || (ele.className && ele.className.search(new RegExp("\b" + className + "\b")) == -1))
  66. return;
  67. ele.className = ele.className.replace(new RegExp("\s*\b" + className + "\b", "g"), "");
  68. };
  69. Spry.Widget.CollapsiblePanel.prototype.hasClassName = function(ele, className)
  70. {
  71. if (!ele || !className || !ele.className || ele.className.search(new RegExp("\b" + className + "\b")) == -1)
  72. return false;
  73. return true;
  74. };
  75. Spry.Widget.CollapsiblePanel.prototype.setDisplay = function(ele, display)
  76. {
  77. if( ele )
  78. ele.style.display = display;
  79. };
  80. Spry.Widget.CollapsiblePanel.setOptions = function(obj, optionsObj, ignoreUndefinedProps)
  81. {
  82. if (!optionsObj)
  83. return;
  84. for (var optionName in optionsObj)
  85. {
  86. if (ignoreUndefinedProps && optionsObj[optionName] == undefined)
  87. continue;
  88. obj[optionName] = optionsObj[optionName];
  89. }
  90. };
  91. Spry.Widget.CollapsiblePanel.prototype.onTabMouseOver = function()
  92. {
  93. this.addClassName(this.getTab(), this.hoverClass);
  94. };
  95. Spry.Widget.CollapsiblePanel.prototype.onTabMouseOut = function()
  96. {
  97. this.removeClassName(this.getTab(), this.hoverClass);
  98. };
  99. Spry.Widget.CollapsiblePanel.prototype.open = function()
  100. {
  101. this.contentIsOpen = true;
  102. if (this.enableAnimation)
  103. {
  104. if (this.animator)
  105. this.animator.stop();
  106. this.animator = new Spry.Widget.CollapsiblePanel.PanelAnimator(this, true);
  107. this.animator.start();
  108. }
  109. else
  110. this.setDisplay(this.getContent(), "block");
  111. this.removeClassName(this.element, this.closedClass);
  112. this.addClassName(this.element, this.openClass);
  113. };
  114. Spry.Widget.CollapsiblePanel.prototype.close = function()
  115. {
  116. this.contentIsOpen = false;
  117. if (this.enableAnimation)
  118. {
  119. if (this.animator)
  120. this.animator.stop();
  121. this.animator = new Spry.Widget.CollapsiblePanel.PanelAnimator(this, false);
  122. this.animator.start();
  123. }
  124. else
  125. this.setDisplay(this.getContent(), "none");
  126. this.removeClassName(this.element, this.openClass);
  127. this.addClassName(this.element, this.closedClass);
  128. };
  129. Spry.Widget.CollapsiblePanel.prototype.onTabClick = function()
  130. {
  131. if (this.isOpen())
  132. this.close();
  133. else
  134. this.open();
  135. this.focus();
  136. };
  137. Spry.Widget.CollapsiblePanel.prototype.onFocus = function(e)
  138. {
  139. this.hasFocus = true;
  140. this.addClassName(this.element, this.focusedClass);
  141. };
  142. Spry.Widget.CollapsiblePanel.prototype.onBlur = function(e)
  143. {
  144. this.hasFocus = false;
  145. this.removeClassName(this.element, this.focusedClass);
  146. };
  147. Spry.Widget.CollapsiblePanel.ENTER_KEY = 13;
  148. Spry.Widget.CollapsiblePanel.SPACE_KEY = 32;
  149. Spry.Widget.CollapsiblePanel.prototype.onKeyDown = function(e)
  150. {
  151. var key = e.keyCode;
  152. if (!this.hasFocus || (key != Spry.Widget.CollapsiblePanel.ENTER_KEY && key != Spry.Widget.CollapsiblePanel.SPACE_KEY))
  153. return true;
  154. if (this.isOpen())
  155. this.close();
  156. else
  157. this.open();
  158. if (e.stopPropagation)
  159. e.stopPropagation();
  160. if (e.preventDefault)
  161. e.preventDefault();
  162. return false;
  163. };
  164. Spry.Widget.CollapsiblePanel.prototype.attachPanelHandlers = function()
  165. {
  166. var tab = this.getTab();
  167. if (!tab)
  168. return;
  169. var self = this;
  170. Spry.Widget.CollapsiblePanel.addEventListener(tab, "click", function(e) { return self.onTabClick(); }, false);
  171. Spry.Widget.CollapsiblePanel.addEventListener(tab, "mouseover", function(e) { return self.onTabMouseOver(); }, false);
  172. Spry.Widget.CollapsiblePanel.addEventListener(tab, "mouseout", function(e) { return self.onTabMouseOut(); }, false);
  173. if (this.enableKeyboardNavigation)
  174. {
  175. // XXX: IE doesn't allow the setting of tabindex dynamically. This means we can't
  176. // rely on adding the tabindex attribute if it is missing to enable keyboard navigation
  177. // by default.
  178. // Find the first element within the tab container that has a tabindex or the first
  179. // anchor tag.
  180. var tabIndexEle = null;
  181. var tabAnchorEle = null;
  182. this.preorderTraversal(tab, function(node) {
  183. if (node.nodeType == 1 /* NODE.ELEMENT_NODE */)
  184. {
  185. var tabIndexAttr = tab.attributes.getNamedItem("tabindex");
  186. if (tabIndexAttr)
  187. {
  188. tabIndexEle = node;
  189. return true;
  190. }
  191. if (!tabAnchorEle && node.nodeName.toLowerCase() == "a")
  192. tabAnchorEle = node;
  193. }
  194. return false;
  195. });
  196. if (tabIndexEle)
  197. this.focusElement = tabIndexEle;
  198. else if (tabAnchorEle)
  199. this.focusElement = tabAnchorEle;
  200. if (this.focusElement)
  201. {
  202. Spry.Widget.CollapsiblePanel.addEventListener(this.focusElement, "focus", function(e) { return self.onFocus(e); }, false);
  203. Spry.Widget.CollapsiblePanel.addEventListener(this.focusElement, "blur", function(e) { return self.onBlur(e); }, false);
  204. Spry.Widget.CollapsiblePanel.addEventListener(this.focusElement, "keydown", function(e) { return self.onKeyDown(e); }, false);
  205. }
  206. }
  207. };
  208. Spry.Widget.CollapsiblePanel.addEventListener = function(element, eventType, handler, capture)
  209. {
  210. try
  211. {
  212. if (element.addEventListener)
  213. element.addEventListener(eventType, handler, capture);
  214. else if (element.attachEvent)
  215. element.attachEvent("on" + eventType, handler);
  216. }
  217. catch (e) {}
  218. };
  219. Spry.Widget.CollapsiblePanel.prototype.preorderTraversal = function(root, func)
  220. {
  221. var stopTraversal = false;
  222. if (root)
  223. {
  224. stopTraversal = func(root);
  225. if (root.hasChildNodes())
  226. {
  227. var child = root.firstChild;
  228. while (!stopTraversal && child)
  229. {
  230. stopTraversal = this.preorderTraversal(child, func);
  231. try { child = child.nextSibling; } catch (e) { child = null; }
  232. }
  233. }
  234. }
  235. return stopTraversal;
  236. };
  237. Spry.Widget.CollapsiblePanel.prototype.attachBehaviors = function()
  238. {
  239. var panel = this.element;
  240. var tab = this.getTab();
  241. var content = this.getContent();
  242. if (this.contentIsOpen || this.hasClassName(panel, this.openClass))
  243. {
  244. this.removeClassName(panel, this.closedClass);
  245. this.setDisplay(content, "block");
  246. this.contentIsOpen = true;
  247. }
  248. else
  249. {
  250. this.removeClassName(panel, this.openClass);
  251. this.addClassName(panel, this.closedClass);
  252. this.setDisplay(content, "none");
  253. this.contentIsOpen = false;
  254. }
  255. this.attachPanelHandlers();
  256. };
  257. Spry.Widget.CollapsiblePanel.prototype.getTab = function()
  258. {
  259. return this.getElementChildren(this.element)[0];
  260. };
  261. Spry.Widget.CollapsiblePanel.prototype.getContent = function()
  262. {
  263. return this.getElementChildren(this.element)[1];
  264. };
  265. Spry.Widget.CollapsiblePanel.prototype.isOpen = function()
  266. {
  267. return this.contentIsOpen;
  268. };
  269. Spry.Widget.CollapsiblePanel.prototype.getElementChildren = function(element)
  270. {
  271. var children = [];
  272. var child = element.firstChild;
  273. while (child)
  274. {
  275. if (child.nodeType == 1 /* Node.ELEMENT_NODE */)
  276. children.push(child);
  277. child = child.nextSibling;
  278. }
  279. return children;
  280. };
  281. Spry.Widget.CollapsiblePanel.prototype.focus = function()
  282. {
  283. if (this.focusElement && this.focusElement.focus)
  284. this.focusElement.focus();
  285. };
  286. /////////////////////////////////////////////////////
  287. Spry.Widget.CollapsiblePanel.PanelAnimator = function(panel, doOpen, opts)
  288. {
  289. this.timer = null;
  290. this.interval = 0;
  291. this.stepCount = 0;
  292. this.fps = 0;
  293. this.steps = 10;
  294. this.duration = 500;
  295. this.onComplete = null;
  296. this.panel = panel;
  297. this.content = panel.getContent();
  298. this.panelData = [];
  299. this.doOpen = doOpen;
  300. Spry.Widget.CollapsiblePanel.setOptions(this, opts);
  301. // If caller specified speed in terms of frames per second,
  302. // convert them into steps.
  303. if (this.fps > 0)
  304. {
  305. this.interval = Math.floor(1000 / this.fps);
  306. this.steps = parseInt((this.duration + (this.interval - 1)) / this.interval);
  307. }
  308. else if (this.steps > 0)
  309. this.interval = this.duration / this.steps;
  310. var c = this.content;
  311. var curHeight = c.offsetHeight ? c.offsetHeight : 0;
  312. if (doOpen && c.style.display == "none")
  313. this.fromHeight = 0;
  314. else
  315. this.fromHeight = curHeight;
  316. if (!doOpen)
  317. this.toHeight = 0;
  318. else
  319. {
  320. if (c.style.display == "none")
  321. {
  322. // The content area is not displayed so in order to calculate the extent
  323. // of the content inside it, we have to set its display to block.
  324. c.style.visibility = "hidden";
  325. c.style.display = "block";
  326. }
  327. // Unfortunately in Mozilla/Firefox, fetching the offsetHeight seems to cause
  328. // the browser to synchronously re-layout and re-display content on the page,
  329. // so we see a brief flash of content that is *after* the panel being positioned
  330. // where it should when the panel is fully expanded. To get around this, we
  331. // temporarily position the content area of the panel absolutely off-screen.
  332. // This has the effect of taking the content out-of-flow, so nothing shifts around.
  333. // var oldPos = c.style.position;
  334. // var oldLeft = c.style.left;
  335. // c.style.position = "absolute";
  336. // c.style.left = "-2000em";
  337. // Clear the height property so we can calculate
  338. // the full height of the content we are going to show.
  339. c.style.height = "";
  340. this.toHeight = c.offsetHeight;
  341. // Now restore the position and offset to what it was!
  342. // c.style.position = oldPos;
  343. // c.style.left = oldLeft;
  344. }
  345. this.increment = (this.toHeight - this.fromHeight) / this.steps;
  346. this.overflow = c.style.overflow;
  347. c.style.height = this.fromHeight + "px";
  348. c.style.visibility = "visible";
  349. c.style.overflow = "hidden";
  350. c.style.display = "block";
  351. };
  352. Spry.Widget.CollapsiblePanel.PanelAnimator.prototype.start = function()
  353. {
  354. var self = this;
  355. this.timer = setTimeout(function() { self.stepAnimation(); }, this.interval);
  356. };
  357. Spry.Widget.CollapsiblePanel.PanelAnimator.prototype.stop = function()
  358. {
  359. if (this.timer)
  360. {
  361. clearTimeout(this.timer);
  362. // If we're killing the timer, restore the overflow
  363. // properties on the panels we were animating!
  364. if (this.stepCount < this.steps)
  365. this.content.style.overflow = this.overflow;
  366. }
  367. this.timer = null;
  368. };
  369. Spry.Widget.CollapsiblePanel.PanelAnimator.prototype.stepAnimation = function()
  370. {
  371. ++this.stepCount;
  372. this.animate();
  373. if (this.stepCount < this.steps)
  374. this.start();
  375. else if (this.onComplete)
  376. this.onComplete();
  377. };
  378. Spry.Widget.CollapsiblePanel.PanelAnimator.prototype.animate = function()
  379. {
  380. if (this.stepCount >= this.steps)
  381. {
  382. if (!this.doOpen)
  383. this.content.style.display = "none";
  384. this.content.style.overflow = this.overflow;
  385. this.content.style.height = this.toHeight + "px";
  386. }
  387. else
  388. {
  389. this.fromHeight += this.increment;
  390. this.content.style.height = this.fromHeight + "px";
  391. }
  392. };