forked from replit-archive/repl.it
-
Notifications
You must be signed in to change notification settings - Fork 1
/
pager.coffee
306 lines (277 loc) · 9.97 KB
/
pager.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# Extension module.
# Responsible for page opening/closing/stacking.
$ = jQuery
ANIMATION_DURATION = 300
KEY_ESCAPE = 27
FIRST_LOAD = true
LANG_TEMPLATE =
language_group: (data) ->
{category, languages} = data
"""
<div class="language-group">
<div class="language-group-header">#{category}</div>
<ul>
#{(@language_entry(language) for language in languages).join('')}
</ul>
</div>
</div>
"""
language_entry: (data) ->
{name, shortcut, system_name, tagline} = data
shortcut_index = name.indexOf(shortcut)
"""
<li data-lang="#{system_name}">
<b>#{name[0...shortcut_index]}<em>#{shortcut}</em>#{name[shortcut_index + 1...]}:</b>
#{tagline}
</li>
"""
render: ->
html = []
categories_order = [
'Classic'
'Practical'
'Esoteric'
'Web'
]
template_data =
Classic:
category: 'Classic'
languages: ['QBasic', 'Forth']
Practical:
category: 'Practical'
languages: ['Ruby', 'Python', 'Lua', 'Scheme']
Esoteric:
category: 'Esoteric'
languages: ['Emoticon', 'Brainfuck', 'LOLCODE', 'Unlambda', 'Bloop']
Web:
category: 'Web'
languages: ['JavaScript', 'Traceur', 'Move', 'Kaffeine', 'CoffeeScript', 'Roy']
for _, category of template_data
for lang_name, index in category.languages
lang = REPLIT.Languages[lang_name]
lang.system_name = lang_name
category.languages[index] = lang
for category in categories_order
html.push @language_group template_data[category]
return html.join ''
PAGES =
workspace:
id: 'content-workspace'
title: '$'
min_width: 500
width: 1000
max_width: 3000
languages:
id: 'content-languages'
title: 'Select a Language'
min_width: 1080
width: 1080
max_width: 1400
examples:
id: 'content-examples'
title: '$ Examples'
min_width: 1000
width: 1000
max_width: 1400
help:
id: 'content-help'
title: 'Help'
min_width: 1000
width: 1000
max_width: 1400
about:
id: 'content-about'
title: 'About Us'
min_width: 600
max_width: 600
width: 600
DEFAULT: 'workspace'
ALLOWED_IN_MODAL = ['help', 'about', 'languages']
$.extend REPLIT,
modal: false
Modal: (@modal)->
LoadExamples: (file, container, callback) ->
$examples_container = $ '#examples-' + container
$('.example-group').remove()
$.get file, (contents) =>
# Parse examples.
raw_examples = contents.split /\*{60,}/
index = 0
total = Math.floor raw_examples.length / 2
while index + 1 < raw_examples.length
name = raw_examples[index].replace /^\s+|\s+$/g, ''
code = raw_examples[index + 1].replace /^\s+|\s+$/g, ''
# Insert an example element and set up its click handler.
example_element = $ """
<div class="example-group example-#{total}">
<div class="example-group-header">#{name}</div>
<code>#{code}</code>
</div>
"""
$examples_container.append example_element
example_element.click -> callback $('code', @).text()
index += 2
# The pages stacking on the screen.
page_stack: []
# Whether we are currently changing a page (to prevent interference).
changing_page: false
# Open a page by its name.
OpenPage: (page_name, callback=$.noop) ->
if @changing_page or (@modal and page_name not in ALLOWED_IN_MODAL) then return
@changing_page = true
page = PAGES[page_name]
current_page = @page_stack[@page_stack.length - 1]
# If the page actually exists and it's not the current one.
if not page or current_page is page_name
@changing_page = false
else
# Calculate and set title.
lang_name = if @current_lang_name
@Languages[@current_lang_name].name
else
''
$title = $ '#title'
new_title = page.title.replace /\$/g, lang_name
if current_page
$title.fadeOut ANIMATION_DURATION, ->
$title.text new_title
$title.fadeIn ANIMATION_DURATION
else
$title.text new_title
# Update widths to those of the new page.
# We can't take into account mobile sizes, so just assign the whole screen
# width. That's Ok, since our mobile layout fits the whole width.
@min_content_width = if @ISMOBILE
document.documentElement.clientWidth - 2 * @RESIZER_WIDTH
else
page.min_width
@max_content_width = page.max_width
# When the workspace is first loaded, don't mess up its default padding.
if FIRST_LOAD and page_name is 'workspace'
FIRST_LOAD = false
page.width = document.documentElement.clientWidth - @DEFAULT_CONTENT_PADDING
@content_padding = document.documentElement.clientWidth - page.width
# Check if the page exists on our stack. If so splice out to be put
# on top.
index = @page_stack.indexOf page_name
if index > -1
@page_stack.splice index, 1
# Put the page on top of the stack.
@page_stack.push page_name
hash_name = if page_name is PAGES.DEFAULT then '' else page_name
REPLIT.setHash 1, hash_name
# Calculate container width.
outerWidth = page.width
# HACK: Workspace doesn't account for resizers for some reason...
if page_name isnt 'workspace' then outerWidth += 2 * @RESIZER_WIDTH
done = =>
@changing_page = false
page.$elem.focus()
callback()
if current_page
# Perform the animation.
PAGES[current_page].width = $('.page:visible').width()
# HACK: Workspace doesn't account for resizers for some reason..
if current_page is 'workspace'
PAGES[current_page].width += 2 * @RESIZER_WIDTH
PAGES[current_page].$elem.fadeOut ANIMATION_DURATION, =>
@$container.animate width: outerWidth, ANIMATION_DURATION, =>
# We need to have the box actually displayed (if invisible) so the
# width calculations inside OnResize() work.
page.$elem.css width: page.width, display: 'block', opacity: 0
@OnResize()
page.$elem.animate opacity: 1, ANIMATION_DURATION, done
else
@$container.css width: outerWidth
page.$elem.css width: page.width, display: 'block'
@OnResize()
done()
# Close the top page and opens the page underneath if exists or just animates
# Back to the original environment width.
CloseLastPage: ->
if @changing_page then return
if @page_stack.length <= 1 then return
closed_page = @page_stack[@page_stack.length - 1]
@OpenPage @page_stack[@page_stack.length - 2], =>
@page_stack.splice @page_stack.indexOf(closed_page), 1
$ ->
# Render language selector.
$('#content-languages').append LANG_TEMPLATE.render()
# Load Examples
REPLIT.$this.bind 'language_loading', (_, system_name) ->
examples = REPLIT.Languages[system_name].examples
if not REPLIT.ISMOBILE
REPLIT.LoadExamples examples.editor, 'editor', (example) ->
REPLIT.editor.getSession().doc.setValue example
REPLIT.OpenPage 'workspace', ->
REPLIT.editor.focus()
REPLIT.LoadExamples examples.console, 'console', (example) ->
REPLIT.jqconsole.SetPromptText example
REPLIT.OpenPage 'workspace', ->
REPLIT.jqconsole.Focus()
# React to hash changes.
REPLIT.$this.bind 'hashchange:1', (_, new_page) ->
if not new_page then new_page = PAGES.DEFAULT
if new_page of PAGES
current_page = @page_stack[@page_stack.length - 1]
if new_page isnt current_page
if REPLIT.changing_page
# Interrupt current page switching animation.
$('.page').stop true, true
@$container.stop true, true
REPLIT.changing_page = false
REPLIT.OpenPage new_page
# Since we will be doing lots of animation and syncing, we better cache the
# jQuery elements.
for name, settings of PAGES
settings.$elem = $("##{settings.id}")
# If we are on a mobile set all default widths to 0 to invoke resizing
# to the minimum which is already set to the width;
if REPLIT.ISMOBILE and name isnt 'workspace' then settings.width = 0
# Assign events.
$body = $ 'body'
$body.delegate '.page-close', 'click', -> REPLIT.CloseLastPage()
$body.delegate '.language-group li', 'click', ->
REPLIT.current_lang_name = $(@).data 'lang'
REPLIT.OpenPage 'workspace', =>
REPLIT.LoadLanguage REPLIT.current_lang_name
# Bind page buttons.
bindPageButtons = ->
$('#button-examples').click ->
# TODO(max99x): Expose state properly from jqconsole.
if REPLIT.current_lang? and REPLIT.jqconsole.state is 2 # STATE_PROMPT
$('#examples-editor').toggle REPLIT.split_ratio != REPLIT.EDITOR_HIDDEN
$('#examples-console').toggle REPLIT.split_ratio != REPLIT.CONSOLE_HIDDEN
REPLIT.OpenPage 'examples'
return false
$('#button-languages').click ->
REPLIT.OpenPage 'languages'
return false
$('#logo').click ->
REPLIT.OpenPage 'languages'
return false
$('#link-about').click ->
REPLIT.OpenPage 'about'
return false
$('#button-help').click ->
REPLIT.OpenPage 'help'
return false
unbindPageButtons = ->
buttons = $('#button-examples, #button-languages, #link-about, #button-help')
buttons.unbind 'click'
bindPageButtons()
# Disable buttons while a language is being loaded.
REPLIT.$this.bind 'language_loading', unbindPageButtons
REPLIT.$this.bind 'language_loaded', bindPageButtons
# Bind page closing to Escape.
$(window).keydown (e) ->
if e.which == KEY_ESCAPE and $('.page:visible') isnt '#content-workspace'
REPLIT.CloseLastPage()
# Bind language selector hotkeys.
$('#content-languages').keypress (e) ->
if e.shiftKey or e.ctrlKey or e.metaKey then return
letter = String.fromCharCode(e.which).toLowerCase()
$('#content-languages li').each ->
if $('em', $ @).text().toLowerCase() == letter
$(@).click()
return false