// Generated by CoffeeScript 2.0.0-beta4 (function() { // ------------------------------------------------------------------------ // 變數與常數設置 // ------------------------------------------------------------------------ // 模組名稱。 var Attribute, ClassName, EVENT_NAMESPACE, Error, Event, MODULE_NAMESPACE, Metadata, NAME, Selector, Settings; NAME = 'tab'; // 模組事件鍵名。 EVENT_NAMESPACE = `.${NAME}`; // 模組命名空間。 MODULE_NAMESPACE = `module-${NAME}`; // 模組設定。 Settings = { // 消音所有提示,甚至是錯誤訊息。 silent: false, // 顯示除錯訊息。 debug: true, // 監聽 DOM 結構異動並自動重整快取。 observeChanges: true, // 當分頁第一次開啟時所會呼叫的回呼函式。 onFirstLoad: (tabName) => {}, // 當分頁被開啟時所會呼叫的回呼函式。 onLoad: (tabName) => {}, // 是否要紀錄分頁籤的開關歷程至瀏覽器的上下頁歷程中。 history: true }; // 中繼資料名稱。 Metadata = { LOADED: 'loaded' }; // 事件名稱。 Event = { FIRSTLOAD: `firstload${EVENT_NAMESPACE}`, LOAD: `load${EVENT_NAMESPACE}`, CLICK: `click${EVENT_NAMESPACE}`, HASHCHANGE: `hashchange${EVENT_NAMESPACE}`, POPSTATE: `popstate${EVENT_NAMESPACE}` }; // 標籤名稱。 Attribute = { GROUP: 'data-tab-group', TAB: 'data-tab' }; // 樣式名稱。 ClassName = { ACTIVE: 'active', TAB: 'tab' }; // 選擇器名稱。 Selector = { TAB: (name) => { return `.tab[${Attribute.TAB}='${name}']`; }, ANY_TAB: '.tab[data-tab]', ACTIVE_TAB: '.active.tab[data-tab]', HIDDEN_TAB: '.tab[data-tab]:not(.active)', MENU: '.menu', MENU_ITEM: (name) => { return `.menu .item[${Attribute.TAB}='${name}']`; }, ITEM: '.item' }; // 錯誤訊息。 Error = {}; // ------------------------------------------------------------------------ // 模組註冊 // ------------------------------------------------------------------------ ts.register({NAME, MODULE_NAMESPACE, Error, Settings}, ({$allModules, $this, element, debug, settings, index}) => { var module, separator; // ------------------------------------------------------------------------ // 區域變數 // ------------------------------------------------------------------------ separator = ','; // ------------------------------------------------------------------------ // 模組定義 // ------------------------------------------------------------------------ return module = { change: { tab: (name, recursive = true, update = true) => { var $item, $tab, i, len, path, paths; name = module.decode(name); paths = recursive ? ts(Selector.TAB(name)).tab('get paths') : [name]; for (i = 0, len = paths.length; i < len; i++) { path = paths[i]; $item = ts(Selector.MENU_ITEM(path)); $tab = ts(Selector.TAB(path)); $tab.tab('set active'); $item.tab('set active').tab('hide others'); if ($item.tab('not loaded')) { $item.trigger(Event.FIRSTLOAD, $tab.get(), path).tab('set loaded', true); } $item.trigger(Event.LOAD, $tab.get(), path); } if (update) { module.update.hash(); } return $allModules; } }, hide: { others: () => { var $items; $items = module.get.relative.$items(); $items.tab('set hidden'); return $items.each(function() { return ts(this).tab('get $tab').tab('set hidden'); }); } }, get: { name: () => { return $this.attr(Attribute.TAB); }, paths: () => { var $parent, paths; paths = []; $parent = $this; while ($parent.length !== 0) { paths.push($parent.tab('get name')); $parent = $parent.tab('get parent $tab'); } return paths; }, active: { $tab: () => { return ts(Selector.ACTIVE_TAB); } }, relative: { $items: () => { return $this.closest(Selector.MENU).find(Selector.ITEM).not($this); } }, parent: { $tab: $this.parent().closest(Selector.ANY_TAB) }, tab: () => { return module.get.$tab().get(); }, $tab: () => { return ts(Selector.TAB(module.get.name())); }, path: () => { return module.get.paths().join(separator); }, hash: () => { var hash; hash = window.location.hash; if (hash) { return module.decode(hash.slice(1)); } else { return ''; } } }, decode: (uri) => { return decodeURIComponent(uri); }, has: { hash: () => { return !!window.location.hash; }, parent: { tab: () => { return $this.parent().closest(Selector.ANY_TAB).length !== 0; } }, hidden: { parent: () => { return $this.closest(Selector.HIDDEN_TAB).length !== 0; } }, active: { children: () => { return $this.find(Selector.ACTIVE_TAB).length !== 0; } } }, set: { loaded: (bool) => { return $this.data(Metadata.LOADED, bool); }, hidden: () => { return $this.removeClass(ClassName.ACTIVE); }, active: () => { return $this.addClass(ClassName.ACTIVE); } }, is: { active: () => { return $this.hasClass(ClassName.ACTIVE); }, tab: () => { return $this.hasClass(ClassName.TAB); }, loaded: () => { return $this.data(Metadata.LOADED) === true; } }, not: { loaded: () => { return !module.is.loaded(); } }, apply: { hash: () => { return setTimeout(function() { var hash; if (!module.has.hash()) { return; } hash = module.get.hash(); if (module.same.hash(hash)) { return; } return hash.split(separator).forEach((value) => { return module.change.tab(value, true, false); }); }, 0); } }, update: { hash: () => { var hash; hash = []; module.get.active.$tab().each(function() { var $tab; $tab = ts(this); if ($tab.tab('has hidden parent')) { return; } if ($tab.tab('has active children')) { return; } return hash.push($tab.tab('get name')); }); hash = `#${hash.join(separator)}`; if (settings.history) { return history.pushState(null, null, hash); } else { return history.replaceState(null, null, hash); } } }, same: { hash: (hash) => { var same; same = true; hash.split(separator).forEach((value) => { if (!same) { return; } if (ts(Selector.TAB(value)).tab('has hidden parent')) { return same = false; } }); return same; } }, bind: { events: () => { $this.on(Event.CLICK, () => { if (module.is.active()) { return; } return module.change.tab(module.get.name(), false); }); $this.on(Event.FIRSTLOAD, (event, context, name) => { debug('發生 FIRSTLOAD 事件', context, name); return settings.onFirstLoad.call(context, event, name); }); $this.on(Event.LOAD, (event, context, name) => { debug('發生 LOAD 事件', context, name); return settings.onLoad.call(context, event, name); }); $this.attr('href', 'javascript:void(0)'); if (!settings.history) { return; } return ts(window).off(Event.POPSTATE).on(Event.POPSTATE, () => { debug('發生 POPSTATE 事件', window); return module.apply.hash(); }); } }, // ------------------------------------------------------------------------ // 基礎方法 // ------------------------------------------------------------------------ initialize: () => { debug('初始化分頁籤', element); if (module.is.tab()) { return; } module.bind.events(); return module.apply.hash(); }, instantiate: () => { return debug('實例化分頁籤', element); }, refresh: () => { return $allModules; }, destroy: () => { debug('摧毀分頁籤', element); $this.removeData(MODULE_NAMESPACE).off(EVENT_NAMESPACE); return $allModules; } }; }); }).call(this);