Files: 6b419928e2e49bc866e8d0b72330f7c9aa29b015 / modules / tabs.js
5216 bytesRaw
1 | var Tabs = require('hypertabs') |
2 | var h = require('hyperscript') |
3 | var pull = require('pull-stream') |
4 | var u = require('../util') |
5 | var keyscroll = require('../keyscroll') |
6 | var open = require('open-external') |
7 | |
8 | function ancestor (el) { |
9 | if(!el) return |
10 | if(el.tagName !== 'A') return ancestor(el.parentElement) |
11 | return el |
12 | } |
13 | |
14 | var plugs = require('../plugs') |
15 | var screen_view = plugs.first(exports._screen_view = []) |
16 | var search_box = plugs.first(exports.search_box = []) |
17 | var menu = plugs.first(exports.menu = []) |
18 | |
19 | exports.message_render = [] |
20 | |
21 | exports.screen_view = function (path) { |
22 | if(path !== 'tabs') |
23 | return |
24 | |
25 | function setSelected (indexes) { |
26 | var ids = indexes.map(function (index) { |
27 | return tabs.get(index).id |
28 | }) |
29 | if(ids.length > 1) |
30 | search.value = 'split('+ids.join(',')+')' |
31 | else |
32 | search.value = ids[0] |
33 | } |
34 | |
35 | var search |
36 | var tabs = Tabs(setSelected) |
37 | |
38 | search = search_box(function (path, change) { |
39 | |
40 | if(tabs.has(path)) { |
41 | tabs.select(path) |
42 | return true |
43 | } |
44 | var el = screen_view(path) |
45 | |
46 | if(el) { |
47 | if(!el.title) el.title = path |
48 | el.scroll = keyscroll(el.querySelector('.scroller__content')) |
49 | tabs.add(el, change) |
50 | // localStorage.openTabs = JSON.stringify(tabs.tabs) |
51 | return change |
52 | } |
53 | }) |
54 | |
55 | //reposition hypertabs menu to inside a container... |
56 | tabs.insertBefore(h('div.header.row', |
57 | h('div.header__tabs.row', tabs.firstChild), //tabs |
58 | h('div.header__search.row.end', h('div', search), menu()) |
59 | ), tabs.firstChild) |
60 | // tabs.insertBefore(search, tabs.firstChild.nextSibling) |
61 | |
62 | var saved = [] |
63 | // try { saved = JSON.parse(localStorage.openTabs) } |
64 | // catch (_) { } |
65 | |
66 | if(!saved || saved.length < 3) |
67 | saved = ['/public', '/private', '/notifications'] |
68 | |
69 | saved.forEach(function (path) { |
70 | var el = screen_view(path) |
71 | el.id = el.id || path |
72 | if (!el) return |
73 | el.scroll = keyscroll(el.querySelector('.scroller__content')) |
74 | if(el) tabs.add(el, false, false) |
75 | }) |
76 | |
77 | tabs.select(0) |
78 | |
79 | //handle link clicks |
80 | window.onclick = function (ev) { |
81 | var link = ancestor(ev.target) |
82 | if(!link) return |
83 | var path = link.hash.substring(1) |
84 | |
85 | ev.preventDefault() |
86 | ev.stopPropagation() |
87 | |
88 | //open external links. |
89 | //this ought to be made into something more runcible |
90 | if(open.isExternal(link.href)) return open(link.href) |
91 | |
92 | if(tabs.has(path)) |
93 | return tabs.select(path, !ev.ctrlKey, !!ev.shiftKey) |
94 | |
95 | var el = screen_view(path) |
96 | if(el) { |
97 | el.id = el.id || path |
98 | el.scroll = keyscroll(el.querySelector('.scroller__content')) |
99 | tabs.add(el, !ev.ctrlKey, !!ev.shiftKey) |
100 | // localStorage.openTabs = JSON.stringify(tabs.tabs) |
101 | } |
102 | |
103 | return false |
104 | } |
105 | |
106 | window.addEventListener('keydown', function (ev) { |
107 | if (ev.target.nodeName === 'INPUT' || ev.target.nodeName === 'TEXTAREA') |
108 | return |
109 | switch(ev.keyCode) { |
110 | |
111 | // scroll through tabs |
112 | case 72: // h |
113 | return tabs.selectRelative(-1) |
114 | case 76: // l |
115 | return tabs.selectRelative(1) |
116 | |
117 | // scroll through messages |
118 | case 74: // j |
119 | return tabs.get(tabs.selected[0]).scroll(1) |
120 | case 75: // k |
121 | return tabs.get(tabs.selected[0]).scroll(-1) |
122 | |
123 | // close a tab |
124 | case 88: // x |
125 | if (tabs.selected) { |
126 | var sel = tabs.selected |
127 | var i = sel.reduce(function (a, b) { return Math.min(a, b) }) |
128 | tabs.remove(sel) |
129 | tabs.select(Math.max(i-1, 0)) |
130 | } |
131 | return |
132 | |
133 | // activate the search field |
134 | case 191: // / |
135 | if (ev.shiftKey) |
136 | search.activate('?', ev) |
137 | else |
138 | search.activate('/', ev) |
139 | return |
140 | |
141 | // navigate to a feed |
142 | case 50: // 2 |
143 | if (ev.shiftKey) |
144 | search.activate('@', ev) |
145 | return |
146 | |
147 | // navigate to a channel |
148 | case 51: // 3 |
149 | if (ev.shiftKey) |
150 | search.activate('#', ev) |
151 | return |
152 | } |
153 | }) |
154 | |
155 | // errors tab |
156 | var errorsContent = h('div.column.scroller__content') |
157 | var errors = h('div.column.scroller', |
158 | {style: {'overflow':'auto'}}, |
159 | h('div.scroller__wrapper', |
160 | errorsContent |
161 | ) |
162 | ) |
163 | |
164 | // remove loader error handler |
165 | if (window.onError) { |
166 | window.removeEventListener('error', window.onError) |
167 | delete window.onError |
168 | } |
169 | |
170 | var errors = h('div.errors', {id: 'errors'}) |
171 | |
172 | // put errors in a tab |
173 | window.addEventListener('error', function (ev) { |
174 | var err = ev.error || ev |
175 | if(!tabs.has('errors')) |
176 | tabs.add(errors, false) |
177 | var el = h('div.message', |
178 | h('strong', err.message), |
179 | h('pre', err.stack)) |
180 | if (errorsContent.firstChild) |
181 | errorsContent.insertBefore(el, errorsContent.firstChild) |
182 | else |
183 | errorsContent.appendChild(el) |
184 | }) |
185 | |
186 | if (process.versions.electron) { |
187 | window.addEventListener('contextmenu', function (ev) { |
188 | ev.preventDefault() |
189 | var remote = require('electron').remote |
190 | var Menu = remote.Menu |
191 | var MenuItem = remote.MenuItem |
192 | var menu = new Menu() |
193 | menu.append(new MenuItem({ |
194 | label: 'Inspect Element', |
195 | click: function () { |
196 | remote.getCurrentWindow().inspectElement(ev.x, ev.y) |
197 | } |
198 | })) |
199 | menu.popup(remote.getCurrentWindow()) |
200 | }) |
201 | } |
202 | |
203 | return tabs |
204 | } |
205 | |
206 | |
207 | |
208 | |
209 | |
210 | |
211 | |
212 | |
213 | |
214 | |
215 |
Built with git-ssb-web