foundation.section.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. /*jslint unparam: true, browser: true, indent: 2 */
  2. ;(function ($, window, document, undefined) {
  3. 'use strict';
  4. Foundation.libs.section = {
  5. name: 'section',
  6. version : '4.2.0',
  7. settings : {
  8. deep_linking: false,
  9. one_up: true,
  10. section_selector : '[data-section]',
  11. region_selector : 'section, .section, [data-section-region]',
  12. title_selector : '.title, [data-section-title]',
  13. active_region_selector : 'section.active, .section.active, .active[data-section-region]',
  14. content_selector : '.content, [data-section-content]',
  15. nav_selector : '[data-section="vertical-nav"], [data-section="horizontal-nav"]',
  16. callback: function (){}
  17. },
  18. init : function (scope, method, options) {
  19. var self = this;
  20. Foundation.inherit(this, 'throttle data_options position_right offset_right');
  21. if (typeof method === 'object') {
  22. $.extend(true, self.settings, method);
  23. }
  24. if (typeof method != 'string') {
  25. this.set_active_from_hash();
  26. this.events();
  27. return true;
  28. } else {
  29. return this[method].call(this, options);
  30. }
  31. },
  32. events : function () {
  33. var self = this;
  34. $(this.scope)
  35. .on('click.fndtn.section', '[data-section] .title, [data-section] [data-section-title]', function (e) {
  36. var $this = $(this),
  37. section = $this.closest(self.settings.region_selector);
  38. if (section.children(self.settings.content_selector).length > 0) {
  39. self.toggle_active.call(this, e, self);
  40. self.reflow();
  41. }
  42. });
  43. $(window)
  44. .on('resize.fndtn.section', self.throttle(function () {
  45. self.resize.call(this);
  46. }, 30))
  47. .on('hashchange', function () {
  48. if (!self.settings.toggled){
  49. self.set_active_from_hash();
  50. $(this).trigger('resize');
  51. }
  52. }).trigger('resize');
  53. $(document)
  54. .on('click.fndtn.section', function (e) {
  55. if ($(e.target).closest(self.settings.title_selector).length < 1) {
  56. $(self.settings.nav_selector)
  57. .children(self.settings.region_selector)
  58. .removeClass('active')
  59. .attr('style', '');
  60. }
  61. });
  62. },
  63. toggle_active : function (e, self) {
  64. var $this = $(this),
  65. self = Foundation.libs.section,
  66. region = $this.closest(self.settings.region_selector),
  67. content = $this.siblings(self.settings.content_selector),
  68. parent = region.parent(),
  69. settings = $.extend({}, self.settings, self.data_options(parent)),
  70. prev_active_section = parent
  71. .children(self.settings.active_region_selector);
  72. self.settings.toggled = true;
  73. if (!settings.deep_linking && content.length > 0) {
  74. e.preventDefault();
  75. }
  76. if (region.hasClass('active')) {
  77. // this is causing the style flash.
  78. if (self.small(parent)
  79. || self.is_vertical_nav(parent)
  80. || self.is_horizontal_nav(parent)
  81. || self.is_accordion(parent)) {
  82. if (prev_active_section[0] !== region[0]
  83. || (prev_active_section[0] === region[0] && !settings.one_up)) {
  84. region
  85. .removeClass('active')
  86. .attr('style', '');
  87. }
  88. }
  89. } else {
  90. var prev_active_section = parent
  91. .children(self.settings.active_region_selector),
  92. title_height = self.outerHeight(region
  93. .children(self.settings.title_selector));
  94. if (self.small(parent) || settings.one_up) {
  95. if (self.small(parent)) {
  96. prev_active_section.attr('style', '');
  97. } else {
  98. prev_active_section.attr('style',
  99. 'visibility: hidden; padding-top: '+title_height+'px;');
  100. }
  101. }
  102. if (self.small(parent)) {
  103. region.attr('style', '');
  104. } else {
  105. region.css('padding-top', title_height);
  106. }
  107. region.addClass('active');
  108. if (prev_active_section.length > 0) {
  109. prev_active_section
  110. .removeClass('active')
  111. .attr('style', '');
  112. }
  113. // Toggle the content display attribute. This is done to
  114. // ensure accurate outerWidth measurements that account for
  115. // the scrollbar.
  116. if (self.is_vertical_tabs(parent)) {
  117. content.css('display', 'block');
  118. if (prev_active_section !== null) {
  119. prev_active_section
  120. .children(self.settings.content_selector)
  121. .css('display', 'none');
  122. }
  123. }
  124. }
  125. setTimeout(function () {
  126. self.settings.toggled = false;
  127. }, 300);
  128. settings.callback();
  129. },
  130. resize : function () {
  131. var self = Foundation.libs.section,
  132. sections = $(self.settings.section_selector);
  133. sections.each(function() {
  134. var $this = $(this),
  135. active_section = $this
  136. .children(self.settings.active_region_selector),
  137. settings = $.extend({}, self.settings, self.data_options($this));
  138. if (active_section.length > 1) {
  139. active_section
  140. .not(':first')
  141. .removeClass('active')
  142. .attr('style', '');
  143. } else if (active_section.length < 1
  144. && !self.is_vertical_nav($this)
  145. && !self.is_horizontal_nav($this)
  146. && !self.is_accordion($this)) {
  147. var first = $this.children(self.settings.region_selector).first();
  148. if (settings.one_up || !self.small($this)) {
  149. first.addClass('active');
  150. }
  151. if (self.small($this)) {
  152. first.attr('style', '');
  153. } else {
  154. first.css('padding-top', self.outerHeight(first
  155. .children(self.settings.title_selector)));
  156. }
  157. }
  158. if (self.small($this)) {
  159. active_section.attr('style', '');
  160. } else {
  161. active_section.css('padding-top', self.outerHeight(active_section
  162. .children(self.settings.title_selector)));
  163. }
  164. self.position_titles($this);
  165. if ( (self.is_horizontal_nav($this) && !self.small($this))
  166. || self.is_vertical_tabs($this) && !self.small($this)) {
  167. self.position_content($this);
  168. } else {
  169. self.position_content($this, false);
  170. }
  171. });
  172. },
  173. is_vertical_nav : function (el) {
  174. return /vertical-nav/i.test(el.data('section'));
  175. },
  176. is_horizontal_nav : function (el) {
  177. return /horizontal-nav/i.test(el.data('section'));
  178. },
  179. is_accordion : function (el) {
  180. return /accordion/i.test(el.data('section'));
  181. },
  182. is_horizontal_tabs : function (el) {
  183. return /^tabs$/i.test(el.data('section'));
  184. },
  185. is_vertical_tabs : function (el) {
  186. return /vertical-tabs/i.test(el.data('section'));
  187. },
  188. set_active_from_hash : function () {
  189. var hash = window.location.hash.substring(1),
  190. sections = $('[data-section]'),
  191. self = this;
  192. sections.each(function () {
  193. var section = $(this),
  194. settings = $.extend({}, self.settings, self.data_options(section));
  195. if (hash.length > 0 && settings.deep_linking) {
  196. var regions = section
  197. .children(self.settings.region_selector)
  198. .attr('style', '')
  199. .removeClass('active');
  200. var hash_regions = regions.map(function () {
  201. var content = $(self.settings.content_selector, this),
  202. content_slug = content.data('slug');
  203. if (new RegExp(content_slug, 'i').test(hash))
  204. return content;
  205. });
  206. var count = hash_regions.length;
  207. for (var i = count - 1; i >= 0; i--) {
  208. $(hash_regions[i]).parent().addClass('active');
  209. }
  210. }
  211. });
  212. },
  213. position_titles : function (section, off) {
  214. var self = this,
  215. titles = section
  216. .children(this.settings.region_selector)
  217. .map(function () {
  218. return $(this).children(self.settings.title_selector);
  219. }),
  220. previous_width = 0,
  221. previous_height = 0,
  222. self = this;
  223. if (typeof off === 'boolean') {
  224. titles.attr('style', '');
  225. } else {
  226. titles.each(function () {
  227. if (self.is_vertical_tabs(section)) {
  228. $(this).css('top', previous_height);
  229. previous_height += self.outerHeight($(this));
  230. } else {
  231. if (!self.rtl) {
  232. $(this).css('left', previous_width);
  233. } else {
  234. $(this).css('right', previous_width);
  235. }
  236. previous_width += self.outerWidth($(this));
  237. }
  238. });
  239. }
  240. },
  241. position_content : function (section, off) {
  242. var self = this,
  243. regions = section.children(self.settings.region_selector),
  244. titles = regions
  245. .map(function () {
  246. return $(this).children(self.settings.title_selector);
  247. }),
  248. content = regions
  249. .map(function () {
  250. return $(this).children(self.settings.content_selector);
  251. });
  252. if (typeof off === 'boolean') {
  253. content.attr('style', '');
  254. section.attr('style', '');
  255. } else {
  256. if (self.is_vertical_tabs(section)
  257. && !self.small(section)) {
  258. var content_min_height = 0,
  259. content_min_width = Number.MAX_VALUE,
  260. title_width = null;
  261. regions.each(function () {
  262. var region = $(this),
  263. title = region.children(self.settings.title_selector),
  264. content = region.children(self.settings.content_selector),
  265. content_width = 0;
  266. title_width = self.outerWidth(title);
  267. content_width = self.outerWidth(section) - title_width;
  268. if (content_width < content_min_width) {
  269. content_min_width = content_width;
  270. }
  271. // Increment the minimum height of the content region
  272. // to align with the height of the titles.
  273. content_min_height += self.outerHeight(title);
  274. // Set all of the inactive tabs to 'display: none'
  275. // The CSS sets all of the tabs as 'display: block'
  276. // in order to account for scrollbars when measuring the width
  277. // of the content regions.
  278. if (!$(this).hasClass('active')) {
  279. content.css('display', 'none');
  280. }
  281. });
  282. regions.each(function () {
  283. var content = $(this).children(self.settings.content_selector);
  284. content.css('minHeight', content_min_height);
  285. // Remove 2 pixels to account for the right-shift in the CSS
  286. content.css('maxWidth', content_min_width - 2);
  287. });
  288. } else {
  289. regions.each(function () {
  290. var region = $(this),
  291. title = region.children(self.settings.title_selector),
  292. content = region.children(self.settings.content_selector);
  293. if (!self.rtl) {
  294. content
  295. .css({left: title.position().left - 1,
  296. top: self.outerHeight(title) - 2});
  297. } else {
  298. content
  299. .css({right: self.position_right(title) + 1,
  300. top: self.outerHeight(title) - 2});
  301. }
  302. });
  303. // temporary work around for Zepto outerheight calculation issues.
  304. if (typeof Zepto === 'function') {
  305. section.height(this.outerHeight($(titles[0])));
  306. } else {
  307. section.height(this.outerHeight($(titles[0])) - 2);
  308. }
  309. }
  310. }
  311. },
  312. position_right : function (el) {
  313. var self = this,
  314. section = el.closest(this.settings.section_selector),
  315. regions = section.children(this.settings.region_selector),
  316. section_width = el.closest(this.settings.section_selector).width(),
  317. offset = regions
  318. .map(function () {
  319. return $(this).children(self.settings.title_selector);
  320. }).length;
  321. return (section_width - el.position().left - el.width() * (el.index() + 1) - offset);
  322. },
  323. reflow : function (scope) {
  324. var scope = scope || document;
  325. $(this.settings.section_selector, scope).trigger('resize');
  326. },
  327. small : function (el) {
  328. var settings = $.extend({}, this.settings, this.data_options(el));
  329. if (this.is_horizontal_tabs(el)) {
  330. return false;
  331. }
  332. if (el && this.is_accordion(el)) {
  333. return true;
  334. }
  335. if ($('html').hasClass('lt-ie9')) {
  336. return true;
  337. }
  338. if ($('html').hasClass('ie8compat')) {
  339. return true;
  340. }
  341. return $(this.scope).width() < 768;
  342. },
  343. off : function () {
  344. $(this.scope).off('.fndtn.section');
  345. $(window).off('.fndtn.section');
  346. $(document).off('.fndtn.section')
  347. }
  348. };
  349. }(Foundation.zj, this, this.document));