markdown_slider 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md ADDED
@@ -0,0 +1,143 @@
1
+
2
+ Markdown Slider
3
+ ===============
4
+
5
+ Generates HTML5-slides from markdown.
6
+
7
+
8
+ Features
9
+ --------
10
+
11
+ * Generates a single html includes style and script based on [html5slides](http://html5slides.googlecode.com/).
12
+ * Syntax highliting with [redcarpet](https://github.com/tanoku/redcarpet) and [prettify.js](http://google-code-prettify.googlecode.com)
13
+
14
+
15
+ Get Started
16
+ -----------
17
+
18
+ It's available as a Ruby gem.
19
+
20
+ ``` sh
21
+ $ gem install markdown_slider
22
+ ```
23
+
24
+ The source is available on GitHub:
25
+
26
+ ``` sh
27
+ $ git clone git://github.com/massat/markdown_slider.git
28
+ ```
29
+
30
+ ### Write your talk in markdown format.
31
+
32
+ TITLE
33
+ =====
34
+
35
+ section1
36
+ --------
37
+
38
+ It's my awsome code.
39
+
40
+ ```
41
+ STDOUT.puts 'awsome!'
42
+ ```
43
+
44
+ ### And publish it!
45
+
46
+ ``` sh
47
+ $ md-slider example.md > index.html
48
+ ```
49
+
50
+ Usage
51
+ -----
52
+
53
+ #### Dividing rules
54
+
55
+ `md-slider` divides html into slides with `h1` or `h2`.
56
+
57
+ So, following markdown is divided into 3slides.
58
+
59
+ ``` md
60
+ title
61
+ -----
62
+
63
+ subtitle
64
+
65
+ agenda
66
+ ======
67
+
68
+ * foo
69
+ * bar
70
+
71
+ foo
72
+ ===
73
+
74
+ about foo...
75
+
76
+ ```
77
+
78
+ And so you can use `--` (=h2) at positions you want to divide.
79
+
80
+ Syntax highlite
81
+ ---------------
82
+
83
+ writing...
84
+
85
+
86
+ Publish
87
+ -------
88
+
89
+ You can publish slides with `md-slider` command.
90
+ It outputs HTML into STDOUT.
91
+
92
+ ``` sh
93
+ $ md-slider [options] path/to/markdown > path/to/html
94
+ ```
95
+
96
+ #### options
97
+
98
+ You can use following options.
99
+
100
+ ``` nocode
101
+ -h, --help :show help message
102
+
103
+ --title TITLE :specifies the title of slides. (set as title of HTML)
104
+
105
+ --template TEMPLATE :specifies an ERB template.
106
+
107
+ --style STYLE :specifies a css file.
108
+
109
+ --script SCRIPT :specifies a js file
110
+ ```
111
+
112
+ requirements
113
+ ------------
114
+
115
+ * [redcarpet](https://github.com/tanoku/redcarpet)
116
+
117
+ TODO
118
+ ----
119
+
120
+ * Scroll to top when changeing slides.
121
+ * improve syntax highliting.
122
+ * Write tests.
123
+ * Fix default style.
124
+ * Add margin between neighboring headers.
125
+
126
+ License
127
+ -------
128
+
129
+ ``` nocode
130
+ Copyright 2012 Masato Hirai
131
+
132
+ Licensed under the Apache License, Version 2.0 (the "License");
133
+ you may not use this file except in compliance with the License.
134
+ You may obtain a copy of the License at
135
+
136
+ http://www.apache.org/licenses/LICENSE-2.0
137
+
138
+ Unless required by applicable law or agreed to in writing, software
139
+ distributed under the License is distributed on an "AS IS" BASIS,
140
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
141
+ See the License for the specific language governing permissions and
142
+ limitations under the License.
143
+ ```
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ end
6
+
7
+ desc "Run tests"
8
+ task :default => :test
data/bin/md-slider ADDED
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ USAGE = <<'EOS'
4
+
5
+ USAGE:
6
+ md-slider [options] path/to/markdown > path/to/html
7
+
8
+ options:
9
+
10
+ -h, --help :show help message
11
+
12
+ --title TITLE :specifies the title of slides. (set as title of HTML)
13
+
14
+ --template TEMPLATE :specifies an ERB template.
15
+
16
+ --style STYLE :specifies a css file.
17
+
18
+ --script SCRIPT :specifies a js file
19
+
20
+ EOS
21
+
22
+ root = File.expand_path('../../', __FILE__)
23
+ $:.unshift File.expand_path('lib', root)
24
+
25
+ require 'optparse'
26
+ require 'markdown_slider'
27
+
28
+ begin
29
+
30
+ args = ARGV.dup
31
+ options = {}
32
+
33
+ parser = OptionParser.new do |opts|
34
+ opts.on('-h', '--help') {|opt| options[:help] = opt }
35
+ opts.on('--title TITLE') {|opt| options[:title] = opt }
36
+ opts.on('--template TEMPLATE') {|opt| options[:template] = opt }
37
+ opts.on('--style STYLE') {|opt| options[:style] = opt }
38
+ opts.on('--script SCRIPT') {|opt| options[:script] = opt }
39
+ end
40
+
41
+ parser.parse!(args)
42
+
43
+ path_to_markdown = args.length > 0 ? args.pop : nil
44
+
45
+ if path_to_markdown == nil or options.has_key?(:help)
46
+ STDOUT.puts USAGE
47
+ exit
48
+ end
49
+
50
+ STDOUT.puts MarkdownSlider.run(path_to_markdown, options)
51
+
52
+ rescue => e
53
+
54
+ STDERR.puts "ERR: " + e.message
55
+ STDOUT.puts USAGE
56
+
57
+ end
@@ -0,0 +1,2651 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'erb'
4
+ require 'redcarpet'
5
+
6
+ class MarkdownSlider
7
+
8
+ VERSION = '0.0.1'
9
+
10
+ attr_reader :title, :style, :script, :slides
11
+
12
+ def self.run(markdown, options = {})
13
+
14
+ raise ArgumentError, "specified source not found: #{markdown}" unless File.exist?(markdown)
15
+
16
+ input = File::read(markdown)
17
+
18
+ # check options
19
+ if options.has_key?(:template)
20
+ if File.exist?(options[:template])
21
+ options[:template] = File::read(options[:template])
22
+ else
23
+ raise ArgumentError, "specified template not found: #{options[:template]}"
24
+ end
25
+ end
26
+
27
+ if options.has_key?(:style)
28
+ if File.exist?(options[:style])
29
+ options[:style] = File::read(options[:style])
30
+ else
31
+ raise ArgumentError, "specified css not found: #{options[:style]}"
32
+ end
33
+ end
34
+
35
+ if options.has_key?(:script)
36
+ if File.exist?(options[:script])
37
+ options[:script] = File::read(options[:script])
38
+ else
39
+ raise ArgumentError, "specified script not found: #{options[:script]}"
40
+ end
41
+ end
42
+
43
+ MarkdownSlider.new(options).publish(input)
44
+
45
+ end
46
+
47
+
48
+ def initialize(options = {})
49
+
50
+ @title = options[:title] || 'title'
51
+ @template = options[:template] || MarkdownSlider::TEMPLATE
52
+ @style = options[:style] || MarkdownSlider::STYLE
53
+ @script = options[:script] || MarkdownSlider::SCRIPT
54
+
55
+ @parser = Redcarpet::Markdown.new(
56
+ Redcarpet::Render::HTML,
57
+ {
58
+ :tables => true,
59
+ :fenced_code_blocks => true,
60
+ :autolink => true,
61
+ :strikethrough => true,
62
+ :space_after_headers => true,
63
+ }
64
+ )
65
+
66
+ end
67
+
68
+ def publish(input)
69
+
70
+ @slides = split(@parser.render(input))
71
+
72
+ ERB.new(@template).result(binding)
73
+
74
+ end
75
+
76
+ private
77
+
78
+ def split(html)
79
+
80
+ delimiter = '<!--%% DELIMITER %%-->'
81
+ pattern = /<h1>.*?<\/h1>|<h2>.*?<\/h2>/
82
+ text = '';
83
+
84
+ html.lines.each do |line|
85
+ text += delimiter if pattern =~ line and text.length > 0
86
+ text += line
87
+ end
88
+
89
+ text.split(delimiter)
90
+
91
+ end
92
+
93
+ # TEMPLATE
94
+ TEMPLATE =<<-'EOS'
95
+ <!DOCTYPE html>
96
+ <!--
97
+ Based on Google HTML5 slide template
98
+ URL: http://code.google.com/p/html5slides/
99
+ -->
100
+ <html>
101
+ <head>
102
+ <title><%= title %></title>
103
+ <meta charset='utf-8'>
104
+ <script type="text/javascript"'>
105
+ <%= script %>
106
+ </script>
107
+ </head>
108
+ <style>
109
+ <%= style %>
110
+ </style>
111
+ <body style='display: none'>
112
+ <section class='slides layout-regular template-default'>
113
+ <% @slides.each do |slide| %>
114
+ <article>
115
+ <%= slide %>
116
+ </article>
117
+ <% end %>
118
+ </section>
119
+ </body>
120
+ </html>
121
+ EOS
122
+
123
+ # CSS
124
+ STYLE =<<-'EOS'
125
+ /*
126
+ Based on Google HTML5 slides template
127
+ URL: http://code.google.com/p/html5slides/
128
+ */
129
+
130
+ /* Framework */
131
+
132
+ html {
133
+ height: 100%;
134
+ }
135
+
136
+ body {
137
+ margin: 0;
138
+ padding: 0;
139
+
140
+ display: block !important;
141
+
142
+ height: 100%;
143
+ min-height: 740px;
144
+
145
+ overflow-x: hidden;
146
+ overflow-y: auto;
147
+
148
+ background: rgb(215, 215, 215);
149
+ background: -o-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
150
+ background: -moz-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
151
+ background: -webkit-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
152
+ background: -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 500, from(rgb(240, 240, 240)), to(rgb(190, 190, 190)));
153
+
154
+ -webkit-font-smoothing: antialiased;
155
+ }
156
+
157
+ .slides {
158
+ width: 100%;
159
+ height: 100%;
160
+ left: 0;
161
+ top: 0;
162
+
163
+ position: absolute;
164
+
165
+ -webkit-transform: translate3d(0, 0, 0);
166
+ }
167
+
168
+ .slides > article {
169
+ display: block;
170
+
171
+ position: absolute;
172
+ overflow: hidden;
173
+
174
+ width: 900px;
175
+ min-height: 700px;
176
+
177
+ left: 50%;
178
+ top: 50%;
179
+
180
+ margin-left: -450px;
181
+ margin-top: -350px;
182
+
183
+ padding: 40px 60px;
184
+
185
+ box-sizing: border-box;
186
+ -o-box-sizing: border-box;
187
+ -moz-box-sizing: border-box;
188
+ -webkit-box-sizing: border-box;
189
+
190
+ border-radius: 10px;
191
+ -o-border-radius: 10px;
192
+ -moz-border-radius: 10px;
193
+ -webkit-border-radius: 10px;
194
+
195
+ background-color: white;
196
+
197
+ box-shadow: 0 2px 6px rgba(0, 0, 0, .1);
198
+ border: 1px solid rgba(0, 0, 0, .3);
199
+
200
+ transition: transform .3s ease-out;
201
+ -o-transition: -o-transform .3s ease-out;
202
+ -moz-transition: -moz-transform .3s ease-out;
203
+ -webkit-transition: -webkit-transform .3s ease-out;
204
+ }
205
+ .slides.layout-widescreen > article {
206
+ margin-left: -550px;
207
+ width: 1100px;
208
+ }
209
+ .slides.layout-faux-widescreen > article {
210
+ margin-left: -550px;
211
+ width: 1100px;
212
+
213
+ padding: 40px 160px;
214
+ }
215
+
216
+ .slides.template-default > article:not(.nobackground):not(.biglogo) {
217
+ background-color: white;
218
+ }
219
+
220
+ .slides.template-io2011 > article:not(.nobackground):not(.biglogo) {
221
+ background-size: 100%, 225px;
222
+ background-color: white;
223
+ }
224
+ .slides.layout-widescreen > article:not(.nobackground):not(.biglogo),
225
+ .slides.layout-faux-widescreen > article:not(.nobackground):not(.biglogo) {
226
+ background-position-x: 0, 840px;
227
+ }
228
+
229
+ /* Clickable/tappable areas */
230
+
231
+ .slide-area {
232
+ z-index: 1000;
233
+
234
+ position: absolute;
235
+ left: 0;
236
+ top: 0;
237
+ width: 150px;
238
+ min-height: 700px;
239
+
240
+ left: 50%;
241
+ top: 50%;
242
+
243
+ cursor: pointer;
244
+ margin-top: -350px;
245
+
246
+ tap-highlight-color: transparent;
247
+ -o-tap-highlight-color: transparent;
248
+ -moz-tap-highlight-color: transparent;
249
+ -webkit-tap-highlight-color: transparent;
250
+ }
251
+ #prev-slide-area {
252
+ margin-left: -550px;
253
+ }
254
+ #next-slide-area {
255
+ margin-left: 400px;
256
+ }
257
+ .slides.layout-widescreen #prev-slide-area,
258
+ .slides.layout-faux-widescreen #prev-slide-area {
259
+ margin-left: -650px;
260
+ }
261
+ .slides.layout-widescreen #next-slide-area,
262
+ .slides.layout-faux-widescreen #next-slide-area {
263
+ margin-left: 500px;
264
+ }
265
+
266
+ /* Slide styles */
267
+
268
+ .slides.template-default article.biglogo {
269
+ background: white;
270
+ }
271
+
272
+ .slides.template-io2011 article.biglogo {
273
+ background: white;
274
+ background-size: 600px;
275
+ }
276
+
277
+ /* Slides */
278
+
279
+ .slides > article {
280
+ display: none;
281
+ }
282
+ .slides > article.far-past {
283
+ display: block;
284
+ transform: translate(-2040px);
285
+ -o-transform: translate(-2040px);
286
+ -moz-transform: translate(-2040px);
287
+ -webkit-transform: translate3d(-2040px, 0, 0);
288
+ }
289
+ .slides > article.past {
290
+ display: block;
291
+ transform: translate(-1020px);
292
+ -o-transform: translate(-1020px);
293
+ -moz-transform: translate(-1020px);
294
+ -webkit-transform: translate3d(-1020px, 0, 0);
295
+ }
296
+ .slides > article.current {
297
+ display: block;
298
+ transform: translate(0);
299
+ -o-transform: translate(0);
300
+ -moz-transform: translate(0);
301
+ -webkit-transform: translate3d(0, 0, 0);
302
+ }
303
+ .slides > article.next {
304
+ display: block;
305
+ transform: translate(1020px);
306
+ -o-transform: translate(1020px);
307
+ -moz-transform: translate(1020px);
308
+ -webkit-transform: translate3d(1020px, 0, 0);
309
+ }
310
+ .slides > article.far-next {
311
+ display: block;
312
+ transform: translate(2040px);
313
+ -o-transform: translate(2040px);
314
+ -moz-transform: translate(2040px);
315
+ -webkit-transform: translate3d(2040px, 0, 0);
316
+ }
317
+
318
+ .slides.layout-widescreen > article.far-past,
319
+ .slides.layout-faux-widescreen > article.far-past {
320
+ display: block;
321
+ transform: translate(-2260px);
322
+ -o-transform: translate(-2260px);
323
+ -moz-transform: translate(-2260px);
324
+ -webkit-transform: translate3d(-2260px, 0, 0);
325
+ }
326
+ .slides.layout-widescreen > article.past,
327
+ .slides.layout-faux-widescreen > article.past {
328
+ display: block;
329
+ transform: translate(-1130px);
330
+ -o-transform: translate(-1130px);
331
+ -moz-transform: translate(-1130px);
332
+ -webkit-transform: translate3d(-1130px, 0, 0);
333
+ }
334
+ .slides.layout-widescreen > article.current,
335
+ .slides.layout-faux-widescreen > article.current {
336
+ display: block;
337
+ transform: translate(0);
338
+ -o-transform: translate(0);
339
+ -moz-transform: translate(0);
340
+ -webkit-transform: translate3d(0, 0, 0);
341
+ }
342
+ .slides.layout-widescreen > article.next,
343
+ .slides.layout-faux-widescreen > article.next {
344
+ display: block;
345
+ transform: translate(1130px);
346
+ -o-transform: translate(1130px);
347
+ -moz-transform: translate(1130px);
348
+ -webkit-transform: translate3d(1130px, 0, 0);
349
+ }
350
+ .slides.layout-widescreen > article.far-next,
351
+ .slides.layout-faux-widescreen > article.far-next {
352
+ display: block;
353
+ transform: translate(2260px);
354
+ -o-transform: translate(2260px);
355
+ -moz-transform: translate(2260px);
356
+ -webkit-transform: translate3d(2260px, 0, 0);
357
+ }
358
+
359
+ /* Styles for slides */
360
+
361
+ .slides > article {
362
+ font-family: 'Open Sans', Arial, sans-serif;
363
+
364
+ color: rgb(102, 102, 102);
365
+ text-shadow: 0 1px 1px rgba(0, 0, 0, .1);
366
+
367
+ font-size: 30px;
368
+ line-height: 36px;
369
+
370
+ letter-spacing: -1px;
371
+ }
372
+
373
+ b {
374
+ font-weight: 600;
375
+ }
376
+
377
+ .blue {
378
+ color: rgb(0, 102, 204);
379
+ }
380
+ .yellow {
381
+ color: rgb(255, 211, 25);
382
+ }
383
+ .green {
384
+ color: rgb(0, 138, 53);
385
+ }
386
+ .red {
387
+ color: rgb(255, 0, 0);
388
+ }
389
+ .black {
390
+ color: black;
391
+ }
392
+ .white {
393
+ color: white;
394
+ }
395
+
396
+ a {
397
+ color: rgb(0, 102, 204);
398
+ }
399
+ a:visited {
400
+ color: rgba(0, 102, 204, .75);
401
+ }
402
+ a:hover {
403
+ color: black;
404
+ }
405
+
406
+ p {
407
+ margin: 0;
408
+ padding: 0;
409
+
410
+ margin-top: 20px;
411
+ }
412
+ p:first-child {
413
+ margin-top: 0;
414
+ }
415
+
416
+ h1 {
417
+ font-size: 60px;
418
+ line-height: 60px;
419
+
420
+ padding: 0;
421
+ margin: 0;
422
+ margin-top: 200px;
423
+ padding-right: 40px;
424
+
425
+ font-weight: 600;
426
+
427
+ letter-spacing: -3px;
428
+
429
+ color: rgb(51, 51, 51);
430
+ }
431
+
432
+ h2 {
433
+ font-size: 45px;
434
+ line-height: 45px;
435
+
436
+ padding: 0;
437
+ margin: 0;
438
+ padding-right: 40px;
439
+
440
+ font-weight: 600;
441
+
442
+ letter-spacing: -2px;
443
+
444
+ color: rgb(51, 51, 51);
445
+ }
446
+
447
+ h3 {
448
+ font-size: 30px;
449
+ line-height: 36px;
450
+
451
+ padding: 0;
452
+ margin: 0;
453
+ padding-right: 40px;
454
+
455
+ font-weight: 600;
456
+
457
+ letter-spacing: -1px;
458
+
459
+ color: rgb(51, 51, 51);
460
+ }
461
+
462
+ article.fill h3 {
463
+ background: rgba(255, 255, 255, .75);
464
+ padding-top: .2em;
465
+ padding-bottom: .3em;
466
+ margin-top: -.2em;
467
+ margin-left: -60px;
468
+ padding-left: 60px;
469
+ margin-right: -60px;
470
+ padding-right: 60px;
471
+ }
472
+
473
+ ul {
474
+ list-style: none;
475
+ margin: 0;
476
+ padding: 0;
477
+
478
+ margin-top: 40px;
479
+
480
+ margin-left: .75em;
481
+ }
482
+ ul:first-child {
483
+ margin-top: 0;
484
+ }
485
+ ul ul {
486
+ margin-top: .5em;
487
+ }
488
+ li {
489
+ padding: 0;
490
+ margin: 0;
491
+
492
+ margin-bottom: .5em;
493
+ }
494
+ li::before {
495
+ content: '·';
496
+
497
+ width: .75em;
498
+ margin-left: -.75em;
499
+
500
+ position: absolute;
501
+ }
502
+
503
+ pre {
504
+ font-family: 'Droid Sans Mono', 'Courier New', monospace;
505
+
506
+ font-size: 20px;
507
+ line-height: 28px;
508
+ padding: 5px 10px;
509
+
510
+ letter-spacing: -1px;
511
+
512
+ margin-top: 40px;
513
+ margin-bottom: 40px;
514
+
515
+ color: black;
516
+ background: rgb(240, 240, 240);
517
+ border: 1px solid rgb(224, 224, 224);
518
+ box-shadow: inset 0 2px 6px rgba(0, 0, 0, .1);
519
+
520
+ overflow: hidden;
521
+ }
522
+
523
+ code {
524
+ font-size: 95%;
525
+ font-family: 'Droid Sans Mono', 'Courier New', monospace;
526
+
527
+ color: black;
528
+ }
529
+
530
+ iframe {
531
+ width: 100%;
532
+
533
+ height: 620px;
534
+
535
+ background: white;
536
+ border: 1px solid rgb(192, 192, 192);
537
+ margin: -1px;
538
+ /*box-shadow: inset 0 2px 6px rgba(0, 0, 0, .1);*/
539
+ }
540
+
541
+ h3 + iframe {
542
+ margin-top: 40px;
543
+ height: 540px;
544
+ }
545
+
546
+ article.fill iframe {
547
+ position: absolute;
548
+ left: 0;
549
+ top: 0;
550
+ width: 100%;
551
+ height: 100%;
552
+
553
+ border: 0;
554
+ margin: 0;
555
+
556
+ border-radius: 10px;
557
+ -o-border-radius: 10px;
558
+ -moz-border-radius: 10px;
559
+ -webkit-border-radius: 10px;
560
+
561
+ z-index: -1;
562
+ }
563
+
564
+ article.fill img {
565
+ position: absolute;
566
+ left: 0;
567
+ top: 0;
568
+ min-width: 100%;
569
+ min-height: 100%;
570
+
571
+ border-radius: 10px;
572
+ -o-border-radius: 10px;
573
+ -moz-border-radius: 10px;
574
+ -webkit-border-radius: 10px;
575
+
576
+ z-index: -1;
577
+ }
578
+ img.centered {
579
+ margin: 0 auto;
580
+ display: block;
581
+ }
582
+
583
+ table {
584
+ width: 100%;
585
+ border-collapse: collapse;
586
+ margin-top: 40px;
587
+ }
588
+ th {
589
+ font-weight: 600;
590
+ text-align: left;
591
+ }
592
+ td,
593
+ th {
594
+ border: 1px solid rgb(224, 224, 224);
595
+ padding: 5px 10px;
596
+ vertical-align: top;
597
+ }
598
+
599
+ .source {
600
+ position: absolute;
601
+ left: 60px;
602
+ top: 644px;
603
+ padding-right: 175px;
604
+
605
+ font-size: 15px;
606
+ letter-spacing: 0;
607
+ line-height: 18px;
608
+ }
609
+
610
+ q {
611
+ display: block;
612
+ font-size: 60px;
613
+ line-height: 72px;
614
+
615
+ margin-left: 20px;
616
+
617
+ margin-top: 100px;
618
+ margin-right: 150px;
619
+ }
620
+ q::before {
621
+ content: '“';
622
+
623
+ position: absolute;
624
+ display: inline-block;
625
+ margin-left: -2.1em;
626
+ width: 2em;
627
+ text-align: right;
628
+
629
+ font-size: 90px;
630
+ color: rgb(192, 192, 192);
631
+ }
632
+ q::after {
633
+ content: '”';
634
+
635
+ position: absolute;
636
+ margin-left: .1em;
637
+
638
+ font-size: 90px;
639
+ color: rgb(192, 192, 192);
640
+ }
641
+ div.author {
642
+ text-align: right;
643
+ font-size: 40px;
644
+
645
+ margin-top: 20px;
646
+ margin-right: 150px;
647
+ }
648
+ div.author::before {
649
+ content: '—';
650
+ }
651
+
652
+ /* Size variants */
653
+
654
+ article.smaller p,
655
+ article.smaller ul {
656
+ font-size: 20px;
657
+ line-height: 24px;
658
+ letter-spacing: 0;
659
+ }
660
+ article.smaller table {
661
+ font-size: 20px;
662
+ line-height: 24px;
663
+ letter-spacing: 0;
664
+ }
665
+ article.smaller pre {
666
+ font-size: 15px;
667
+ line-height: 20px;
668
+ letter-spacing: 0;
669
+ }
670
+ article.smaller q {
671
+ font-size: 40px;
672
+ line-height: 48px;
673
+ }
674
+ article.smaller q::before,
675
+ article.smaller q::after {
676
+ font-size: 60px;
677
+ }
678
+
679
+ /* Builds */
680
+
681
+ .build > * {
682
+ transition: opacity 0.5s ease-in-out 0.2s;
683
+ -o-transition: opacity 0.5s ease-in-out 0.2s;
684
+ -moz-transition: opacity 0.5s ease-in-out 0.2s;
685
+ -webkit-transition: opacity 0.5s ease-in-out 0.2s;
686
+ }
687
+
688
+ .to-build {
689
+ opacity: 0;
690
+ }
691
+
692
+ /* Pretty print */
693
+
694
+ .prettyprint .str, /* string content */
695
+ .prettyprint .atv { /* a markup attribute value */
696
+ color: rgb(0, 138, 53);
697
+ }
698
+ .prettyprint .kwd, /* a keyword */
699
+ .prettyprint .tag { /* a markup tag name */
700
+ color: rgb(0, 102, 204);
701
+ }
702
+ .prettyprint .com { /* a comment */
703
+ color: rgb(127, 127, 127);
704
+ font-style: italic;
705
+ }
706
+ .prettyprint .lit { /* a literal value */
707
+ color: rgb(127, 0, 0);
708
+ }
709
+ .prettyprint .pun, /* punctuation, lisp open bracket, lisp close bracket */
710
+ .prettyprint .opn,
711
+ .prettyprint .clo {
712
+ color: rgb(127, 127, 127);
713
+ }
714
+ .prettyprint .typ, /* a type name */
715
+ .prettyprint .atn, /* a markup attribute name */
716
+ .prettyprint .dec,
717
+ .prettyprint .var { /* a declaration; a variable name */
718
+ color: rgb(127, 0, 127);
719
+ }
720
+ EOS
721
+
722
+ # SCRIPT
723
+ SCRIPT = <<-'EOS'
724
+ // Copyright (C) 2006 Google Inc.
725
+ //
726
+ // Licensed under the Apache License, Version 2.0 (the "License");
727
+ // you may not use this file except in compliance with the License.
728
+ // You may obtain a copy of the License at
729
+ //
730
+ // http://www.apache.org/licenses/LICENSE-2.0
731
+ //
732
+ // Unless required by applicable law or agreed to in writing, software
733
+ // distributed under the License is distributed on an "AS IS" BASIS,
734
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
735
+ // See the License for the specific language governing permissions and
736
+ // limitations under the License.
737
+
738
+ // JSLint declarations
739
+ /*global console, document, navigator, setTimeout, window */
740
+
741
+ /**
742
+ * Split {@code prettyPrint} into multiple timeouts so as not to interfere with
743
+ * UI events.
744
+ * If set to {@code false}, {@code prettyPrint()} is synchronous.
745
+ */
746
+ window['PR_SHOULD_USE_CONTINUATION'] = true;
747
+
748
+ /** the number of characters between tab columns */
749
+ window['PR_TAB_WIDTH'] = 8;
750
+
751
+ /** Contains functions for creating and registering new language handlers.
752
+ * @type {Object}
753
+ */
754
+ window['PR']
755
+
756
+ /** Pretty print a chunk of code.
757
+ *
758
+ * @param {string} sourceCodeHtml code as html
759
+ * @return {string} code as html, but prettier
760
+ */
761
+ = window['prettyPrintOne']
762
+ /** Find all the {@code <pre>} and {@code <code>} tags in the DOM with
763
+ * {@code class=prettyprint} and prettify them.
764
+ * @param {Function?} opt_whenDone if specified, called when the last entry
765
+ * has been finished.
766
+ */
767
+ = window['prettyPrint'] = void 0;
768
+
769
+
770
+ (function () {
771
+ // Keyword lists for various languages.
772
+ var FLOW_CONTROL_KEYWORDS =
773
+ "break continue do else for if return while ";
774
+ var C_KEYWORDS = FLOW_CONTROL_KEYWORDS + "auto case char const default " +
775
+ "double enum extern float goto int long register short signed sizeof " +
776
+ "static struct switch typedef union unsigned void volatile ";
777
+ var COMMON_KEYWORDS = C_KEYWORDS + "catch class delete false import " +
778
+ "new operator private protected public this throw true try typeof ";
779
+ var CPP_KEYWORDS = COMMON_KEYWORDS + "alignof align_union asm axiom bool " +
780
+ "concept concept_map const_cast constexpr decltype " +
781
+ "dynamic_cast explicit export friend inline late_check " +
782
+ "mutable namespace nullptr reinterpret_cast static_assert static_cast " +
783
+ "template typeid typename using virtual wchar_t where ";
784
+ var JAVA_KEYWORDS = COMMON_KEYWORDS +
785
+ "abstract boolean byte extends final finally implements import " +
786
+ "instanceof null native package strictfp super synchronized throws " +
787
+ "transient ";
788
+ var CSHARP_KEYWORDS = JAVA_KEYWORDS +
789
+ "as base by checked decimal delegate descending dynamic event " +
790
+ "fixed foreach from group implicit in interface internal into is lock " +
791
+ "object out override orderby params partial readonly ref sbyte sealed " +
792
+ "stackalloc string select uint ulong unchecked unsafe ushort var ";
793
+ var COFFEE_KEYWORDS = "all and by catch class else extends false finally " +
794
+ "for if in is isnt loop new no not null of off on or return super then " +
795
+ "true try unless until when while yes ";
796
+ var JSCRIPT_KEYWORDS = COMMON_KEYWORDS +
797
+ "debugger eval export function get null set undefined var with " +
798
+ "Infinity NaN ";
799
+ var PERL_KEYWORDS = "caller delete die do dump elsif eval exit foreach for " +
800
+ "goto if import last local my next no our print package redo require " +
801
+ "sub undef unless until use wantarray while BEGIN END ";
802
+ var PYTHON_KEYWORDS = FLOW_CONTROL_KEYWORDS + "and as assert class def del " +
803
+ "elif except exec finally from global import in is lambda " +
804
+ "nonlocal not or pass print raise try with yield " +
805
+ "False True None ";
806
+ var RUBY_KEYWORDS = FLOW_CONTROL_KEYWORDS + "alias and begin case class def" +
807
+ " defined elsif end ensure false in module next nil not or redo rescue " +
808
+ "retry self super then true undef unless until when yield BEGIN END ";
809
+ var SH_KEYWORDS = FLOW_CONTROL_KEYWORDS + "case done elif esac eval fi " +
810
+ "function in local set then until ";
811
+ var ALL_KEYWORDS = (
812
+ CPP_KEYWORDS + CSHARP_KEYWORDS + JSCRIPT_KEYWORDS + PERL_KEYWORDS +
813
+ PYTHON_KEYWORDS + RUBY_KEYWORDS + SH_KEYWORDS);
814
+
815
+ // token style names. correspond to css classes
816
+ /** token style for a string literal */
817
+ var PR_STRING = 'str';
818
+ /** token style for a keyword */
819
+ var PR_KEYWORD = 'kwd';
820
+ /** token style for a comment */
821
+ var PR_COMMENT = 'com';
822
+ /** token style for a type */
823
+ var PR_TYPE = 'typ';
824
+ /** token style for a literal value. e.g. 1, null, true. */
825
+ var PR_LITERAL = 'lit';
826
+ /** token style for a punctuation string. */
827
+ var PR_PUNCTUATION = 'pun';
828
+ /** token style for a punctuation string. */
829
+ var PR_PLAIN = 'pln';
830
+
831
+ /** token style for an sgml tag. */
832
+ var PR_TAG = 'tag';
833
+ /** token style for a markup declaration such as a DOCTYPE. */
834
+ var PR_DECLARATION = 'dec';
835
+ /** token style for embedded source. */
836
+ var PR_SOURCE = 'src';
837
+ /** token style for an sgml attribute name. */
838
+ var PR_ATTRIB_NAME = 'atn';
839
+ /** token style for an sgml attribute value. */
840
+ var PR_ATTRIB_VALUE = 'atv';
841
+
842
+ /**
843
+ * A class that indicates a section of markup that is not code, e.g. to allow
844
+ * embedding of line numbers within code listings.
845
+ */
846
+ var PR_NOCODE = 'nocode';
847
+
848
+ /** A set of tokens that can precede a regular expression literal in
849
+ * javascript.
850
+ * http://www.mozilla.org/js/language/js20/rationale/syntax.html has the full
851
+ * list, but I've removed ones that might be problematic when seen in
852
+ * languages that don't support regular expression literals.
853
+ *
854
+ * <p>Specifically, I've removed any keywords that can't precede a regexp
855
+ * literal in a syntactically legal javascript program, and I've removed the
856
+ * "in" keyword since it's not a keyword in many languages, and might be used
857
+ * as a count of inches.
858
+ *
859
+ * <p>The link a above does not accurately describe EcmaScript rules since
860
+ * it fails to distinguish between (a=++/b/i) and (a++/b/i) but it works
861
+ * very well in practice.
862
+ *
863
+ * @private
864
+ */
865
+ var REGEXP_PRECEDER_PATTERN = function () {
866
+ var preceders = [
867
+ "!", "!=", "!==", "#", "%", "%=", "&", "&&", "&&=",
868
+ "&=", "(", "*", "*=", /* "+", */ "+=", ",", /* "-", */ "-=",
869
+ "->", /*".", "..", "...", handled below */ "/", "/=", ":", "::", ";",
870
+ "<", "<<", "<<=", "<=", "=", "==", "===", ">",
871
+ ">=", ">>", ">>=", ">>>", ">>>=", "?", "@", "[",
872
+ "^", "^=", "^^", "^^=", "{", "|", "|=", "||",
873
+ "||=", "~" /* handles =~ and !~ */,
874
+ "break", "case", "continue", "delete",
875
+ "do", "else", "finally", "instanceof",
876
+ "return", "throw", "try", "typeof"
877
+ ];
878
+ var pattern = '(?:^^|[+-]';
879
+ for (var i = 0; i < preceders.length; ++i) {
880
+ pattern += '|' + preceders[i].replace(/([^=<>:&a-z])/g, '\\$1');
881
+ }
882
+ pattern += ')\\s*'; // matches at end, and matches empty string
883
+ return pattern;
884
+ // CAVEAT: this does not properly handle the case where a regular
885
+ // expression immediately follows another since a regular expression may
886
+ // have flags for case-sensitivity and the like. Having regexp tokens
887
+ // adjacent is not valid in any language I'm aware of, so I'm punting.
888
+ // TODO: maybe style special characters inside a regexp as punctuation.
889
+ }();
890
+
891
+
892
+ /**
893
+ * Given a group of {@link RegExp}s, returns a {@code RegExp} that globally
894
+ * matches the union of the sets of strings matched by the input RegExp.
895
+ * Since it matches globally, if the input strings have a start-of-input
896
+ * anchor (/^.../), it is ignored for the purposes of unioning.
897
+ * @param {Array.<RegExp>} regexs non multiline, non-global regexs.
898
+ * @return {RegExp} a global regex.
899
+ */
900
+ function combinePrefixPatterns(regexs) {
901
+ var capturedGroupIndex = 0;
902
+
903
+ var needToFoldCase = false;
904
+ var ignoreCase = false;
905
+ for (var i = 0, n = regexs.length; i < n; ++i) {
906
+ var regex = regexs[i];
907
+ if (regex.ignoreCase) {
908
+ ignoreCase = true;
909
+ } else if (/[a-z]/i.test(regex.source.replace(
910
+ /\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi, ''))) {
911
+ needToFoldCase = true;
912
+ ignoreCase = false;
913
+ break;
914
+ }
915
+ }
916
+
917
+ function decodeEscape(charsetPart) {
918
+ if (charsetPart.charAt(0) !== '\\') { return charsetPart.charCodeAt(0); }
919
+ switch (charsetPart.charAt(1)) {
920
+ case 'b': return 8;
921
+ case 't': return 9;
922
+ case 'n': return 0xa;
923
+ case 'v': return 0xb;
924
+ case 'f': return 0xc;
925
+ case 'r': return 0xd;
926
+ case 'u': case 'x':
927
+ return parseInt(charsetPart.substring(2), 16)
928
+ || charsetPart.charCodeAt(1);
929
+ case '0': case '1': case '2': case '3': case '4':
930
+ case '5': case '6': case '7':
931
+ return parseInt(charsetPart.substring(1), 8);
932
+ default: return charsetPart.charCodeAt(1);
933
+ }
934
+ }
935
+
936
+ function encodeEscape(charCode) {
937
+ if (charCode < 0x20) {
938
+ return (charCode < 0x10 ? '\\x0' : '\\x') + charCode.toString(16);
939
+ }
940
+ var ch = String.fromCharCode(charCode);
941
+ if (ch === '\\' || ch === '-' || ch === '[' || ch === ']') {
942
+ ch = '\\' + ch;
943
+ }
944
+ return ch;
945
+ }
946
+
947
+ function caseFoldCharset(charSet) {
948
+ var charsetParts = charSet.substring(1, charSet.length - 1).match(
949
+ new RegExp(
950
+ '\\\\u[0-9A-Fa-f]{4}'
951
+ + '|\\\\x[0-9A-Fa-f]{2}'
952
+ + '|\\\\[0-3][0-7]{0,2}'
953
+ + '|\\\\[0-7]{1,2}'
954
+ + '|\\\\[\\s\\S]'
955
+ + '|-'
956
+ + '|[^-\\\\]',
957
+ 'g'));
958
+ var groups = [];
959
+ var ranges = [];
960
+ var inverse = charsetParts[0] === '^';
961
+ for (var i = inverse ? 1 : 0, n = charsetParts.length; i < n; ++i) {
962
+ var p = charsetParts[i];
963
+ switch (p) {
964
+ case '\\B': case '\\b':
965
+ case '\\D': case '\\d':
966
+ case '\\S': case '\\s':
967
+ case '\\W': case '\\w':
968
+ groups.push(p);
969
+ continue;
970
+ }
971
+ var start = decodeEscape(p);
972
+ var end;
973
+ if (i + 2 < n && '-' === charsetParts[i + 1]) {
974
+ end = decodeEscape(charsetParts[i + 2]);
975
+ i += 2;
976
+ } else {
977
+ end = start;
978
+ }
979
+ ranges.push([start, end]);
980
+ // If the range might intersect letters, then expand it.
981
+ if (!(end < 65 || start > 122)) {
982
+ if (!(end < 65 || start > 90)) {
983
+ ranges.push([Math.max(65, start) | 32, Math.min(end, 90) | 32]);
984
+ }
985
+ if (!(end < 97 || start > 122)) {
986
+ ranges.push([Math.max(97, start) & ~32, Math.min(end, 122) & ~32]);
987
+ }
988
+ }
989
+ }
990
+
991
+ // [[1, 10], [3, 4], [8, 12], [14, 14], [16, 16], [17, 17]]
992
+ // -> [[1, 12], [14, 14], [16, 17]]
993
+ ranges.sort(function (a, b) { return (a[0] - b[0]) || (b[1] - a[1]); });
994
+ var consolidatedRanges = [];
995
+ var lastRange = [NaN, NaN];
996
+ for (var i = 0; i < ranges.length; ++i) {
997
+ var range = ranges[i];
998
+ if (range[0] <= lastRange[1] + 1) {
999
+ lastRange[1] = Math.max(lastRange[1], range[1]);
1000
+ } else {
1001
+ consolidatedRanges.push(lastRange = range);
1002
+ }
1003
+ }
1004
+
1005
+ var out = ['['];
1006
+ if (inverse) { out.push('^'); }
1007
+ out.push.apply(out, groups);
1008
+ for (var i = 0; i < consolidatedRanges.length; ++i) {
1009
+ var range = consolidatedRanges[i];
1010
+ out.push(encodeEscape(range[0]));
1011
+ if (range[1] > range[0]) {
1012
+ if (range[1] + 1 > range[0]) { out.push('-'); }
1013
+ out.push(encodeEscape(range[1]));
1014
+ }
1015
+ }
1016
+ out.push(']');
1017
+ return out.join('');
1018
+ }
1019
+
1020
+ function allowAnywhereFoldCaseAndRenumberGroups(regex) {
1021
+ // Split into character sets, escape sequences, punctuation strings
1022
+ // like ('(', '(?:', ')', '^'), and runs of characters that do not
1023
+ // include any of the above.
1024
+ var parts = regex.source.match(
1025
+ new RegExp(
1026
+ '(?:'
1027
+ + '\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]' // a character set
1028
+ + '|\\\\u[A-Fa-f0-9]{4}' // a unicode escape
1029
+ + '|\\\\x[A-Fa-f0-9]{2}' // a hex escape
1030
+ + '|\\\\[0-9]+' // a back-reference or octal escape
1031
+ + '|\\\\[^ux0-9]' // other escape sequence
1032
+ + '|\\(\\?[:!=]' // start of a non-capturing group
1033
+ + '|[\\(\\)\\^]' // start/emd of a group, or line start
1034
+ + '|[^\\x5B\\x5C\\(\\)\\^]+' // run of other characters
1035
+ + ')',
1036
+ 'g'));
1037
+ var n = parts.length;
1038
+
1039
+ // Maps captured group numbers to the number they will occupy in
1040
+ // the output or to -1 if that has not been determined, or to
1041
+ // undefined if they need not be capturing in the output.
1042
+ var capturedGroups = [];
1043
+
1044
+ // Walk over and identify back references to build the capturedGroups
1045
+ // mapping.
1046
+ for (var i = 0, groupIndex = 0; i < n; ++i) {
1047
+ var p = parts[i];
1048
+ if (p === '(') {
1049
+ // groups are 1-indexed, so max group index is count of '('
1050
+ ++groupIndex;
1051
+ } else if ('\\' === p.charAt(0)) {
1052
+ var decimalValue = +p.substring(1);
1053
+ if (decimalValue && decimalValue <= groupIndex) {
1054
+ capturedGroups[decimalValue] = -1;
1055
+ }
1056
+ }
1057
+ }
1058
+
1059
+ // Renumber groups and reduce capturing groups to non-capturing groups
1060
+ // where possible.
1061
+ for (var i = 1; i < capturedGroups.length; ++i) {
1062
+ if (-1 === capturedGroups[i]) {
1063
+ capturedGroups[i] = ++capturedGroupIndex;
1064
+ }
1065
+ }
1066
+ for (var i = 0, groupIndex = 0; i < n; ++i) {
1067
+ var p = parts[i];
1068
+ if (p === '(') {
1069
+ ++groupIndex;
1070
+ if (capturedGroups[groupIndex] === undefined) {
1071
+ parts[i] = '(?:';
1072
+ }
1073
+ } else if ('\\' === p.charAt(0)) {
1074
+ var decimalValue = +p.substring(1);
1075
+ if (decimalValue && decimalValue <= groupIndex) {
1076
+ parts[i] = '\\' + capturedGroups[groupIndex];
1077
+ }
1078
+ }
1079
+ }
1080
+
1081
+ // Remove any prefix anchors so that the output will match anywhere.
1082
+ // ^^ really does mean an anchored match though.
1083
+ for (var i = 0, groupIndex = 0; i < n; ++i) {
1084
+ if ('^' === parts[i] && '^' !== parts[i + 1]) { parts[i] = ''; }
1085
+ }
1086
+
1087
+ // Expand letters to groups to handle mixing of case-sensitive and
1088
+ // case-insensitive patterns if necessary.
1089
+ if (regex.ignoreCase && needToFoldCase) {
1090
+ for (var i = 0; i < n; ++i) {
1091
+ var p = parts[i];
1092
+ var ch0 = p.charAt(0);
1093
+ if (p.length >= 2 && ch0 === '[') {
1094
+ parts[i] = caseFoldCharset(p);
1095
+ } else if (ch0 !== '\\') {
1096
+ // TODO: handle letters in numeric escapes.
1097
+ parts[i] = p.replace(
1098
+ /[a-zA-Z]/g,
1099
+ function (ch) {
1100
+ var cc = ch.charCodeAt(0);
1101
+ return '[' + String.fromCharCode(cc & ~32, cc | 32) + ']';
1102
+ });
1103
+ }
1104
+ }
1105
+ }
1106
+
1107
+ return parts.join('');
1108
+ }
1109
+
1110
+ var rewritten = [];
1111
+ for (var i = 0, n = regexs.length; i < n; ++i) {
1112
+ var regex = regexs[i];
1113
+ if (regex.global || regex.multiline) { throw new Error('' + regex); }
1114
+ rewritten.push(
1115
+ '(?:' + allowAnywhereFoldCaseAndRenumberGroups(regex) + ')');
1116
+ }
1117
+
1118
+ return new RegExp(rewritten.join('|'), ignoreCase ? 'gi' : 'g');
1119
+ }
1120
+
1121
+
1122
+ /**
1123
+ * Split markup into a string of source code and an array mapping ranges in
1124
+ * that string to the text nodes in which they appear.
1125
+ *
1126
+ * <p>
1127
+ * The HTML DOM structure:</p>
1128
+ * <pre>
1129
+ * (Element "p"
1130
+ * (Element "b"
1131
+ * (Text "print ")) ; #1
1132
+ * (Text "'Hello '") ; #2
1133
+ * (Element "br") ; #3
1134
+ * (Text " + 'World';")) ; #4
1135
+ * </pre>
1136
+ * <p>
1137
+ * corresponds to the HTML
1138
+ * {@code <p><b>print </b>'Hello '<br> + 'World';</p>}.</p>
1139
+ *
1140
+ * <p>
1141
+ * It will produce the output:</p>
1142
+ * <pre>
1143
+ * {
1144
+ * source: "print 'Hello '\n + 'World';",
1145
+ * // 1 2
1146
+ * // 012345678901234 5678901234567
1147
+ * spans: [0, #1, 6, #2, 14, #3, 15, #4]
1148
+ * }
1149
+ * </pre>
1150
+ * <p>
1151
+ * where #1 is a reference to the {@code "print "} text node above, and so
1152
+ * on for the other text nodes.
1153
+ * </p>
1154
+ *
1155
+ * <p>
1156
+ * The {@code} spans array is an array of pairs. Even elements are the start
1157
+ * indices of substrings, and odd elements are the text nodes (or BR elements)
1158
+ * that contain the text for those substrings.
1159
+ * Substrings continue until the next index or the end of the source.
1160
+ * </p>
1161
+ *
1162
+ * @param {Node} node an HTML DOM subtree containing source-code.
1163
+ * @return {Object} source code and the text nodes in which they occur.
1164
+ */
1165
+ function extractSourceSpans(node) {
1166
+ var nocode = /(?:^|\s)nocode(?:\s|$)/;
1167
+
1168
+ var chunks = [];
1169
+ var length = 0;
1170
+ var spans = [];
1171
+ var k = 0;
1172
+
1173
+ var whitespace;
1174
+ if (node.currentStyle) {
1175
+ whitespace = node.currentStyle.whiteSpace;
1176
+ } else if (window.getComputedStyle) {
1177
+ whitespace = document.defaultView.getComputedStyle(node, null)
1178
+ .getPropertyValue('white-space');
1179
+ }
1180
+ var isPreformatted = whitespace && 'pre' === whitespace.substring(0, 3);
1181
+
1182
+ function walk(node) {
1183
+ switch (node.nodeType) {
1184
+ case 1: // Element
1185
+ if (nocode.test(node.className)) { return; }
1186
+ for (var child = node.firstChild; child; child = child.nextSibling) {
1187
+ walk(child);
1188
+ }
1189
+ var nodeName = node.nodeName;
1190
+ if ('BR' === nodeName || 'LI' === nodeName) {
1191
+ chunks[k] = '\n';
1192
+ spans[k << 1] = length++;
1193
+ spans[(k++ << 1) | 1] = node;
1194
+ }
1195
+ break;
1196
+ case 3: case 4: // Text
1197
+ var text = node.nodeValue;
1198
+ if (text.length) {
1199
+ if (!isPreformatted) {
1200
+ text = text.replace(/[ \t\r\n]+/g, ' ');
1201
+ } else {
1202
+ text = text.replace(/\r\n?/g, '\n'); // Normalize newlines.
1203
+ }
1204
+ // TODO: handle tabs here?
1205
+ chunks[k] = text;
1206
+ spans[k << 1] = length;
1207
+ length += text.length;
1208
+ spans[(k++ << 1) | 1] = node;
1209
+ }
1210
+ break;
1211
+ }
1212
+ }
1213
+
1214
+ walk(node);
1215
+
1216
+ return {
1217
+ source: chunks.join('').replace(/\n$/, ''),
1218
+ spans: spans
1219
+ };
1220
+ }
1221
+
1222
+
1223
+ /**
1224
+ * Apply the given language handler to sourceCode and add the resulting
1225
+ * decorations to out.
1226
+ * @param {number} basePos the index of sourceCode within the chunk of source
1227
+ * whose decorations are already present on out.
1228
+ */
1229
+ function appendDecorations(basePos, sourceCode, langHandler, out) {
1230
+ if (!sourceCode) { return; }
1231
+ var job = {
1232
+ source: sourceCode,
1233
+ basePos: basePos
1234
+ };
1235
+ langHandler(job);
1236
+ out.push.apply(out, job.decorations);
1237
+ }
1238
+
1239
+ function createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns) {
1240
+ var shortcuts = {};
1241
+ var tokenizer;
1242
+ (function () {
1243
+ var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns);
1244
+ var allRegexs = [];
1245
+ var regexKeys = {};
1246
+ for (var i = 0, n = allPatterns.length; i < n; ++i) {
1247
+ var patternParts = allPatterns[i];
1248
+ var shortcutChars = patternParts[3];
1249
+ if (shortcutChars) {
1250
+ for (var c = shortcutChars.length; --c >= 0;) {
1251
+ shortcuts[shortcutChars.charAt(c)] = patternParts;
1252
+ }
1253
+ }
1254
+ var regex = patternParts[1];
1255
+ var k = '' + regex;
1256
+ if (!regexKeys.hasOwnProperty(k)) {
1257
+ allRegexs.push(regex);
1258
+ regexKeys[k] = null;
1259
+ }
1260
+ }
1261
+ allRegexs.push(/[\0-\uffff]/);
1262
+ tokenizer = combinePrefixPatterns(allRegexs);
1263
+ })();
1264
+
1265
+ var nPatterns = fallthroughStylePatterns.length;
1266
+ var notWs = /\S/;
1267
+
1268
+ /**
1269
+ * Lexes job.source and produces an output array job.decorations of style
1270
+ * classes preceded by the position at which they start in job.source in
1271
+ * order.
1272
+ *
1273
+ * @param {Object} job an object like {@code
1274
+ * source: {string} sourceText plain text,
1275
+ * basePos: {int} position of job.source in the larger chunk of
1276
+ * sourceCode.
1277
+ * }
1278
+ */
1279
+ var decorate = function (job) {
1280
+ var sourceCode = job.source, basePos = job.basePos;
1281
+ /** Even entries are positions in source in ascending order. Odd enties
1282
+ * are style markers (e.g., PR_COMMENT) that run from that position until
1283
+ * the end.
1284
+ * @type {Array.<number|string>}
1285
+ */
1286
+ var decorations = [basePos, PR_PLAIN];
1287
+ var pos = 0; // index into sourceCode
1288
+ var tokens = sourceCode.match(tokenizer) || [];
1289
+ var styleCache = {};
1290
+
1291
+ for (var ti = 0, nTokens = tokens.length; ti < nTokens; ++ti) {
1292
+ var token = tokens[ti];
1293
+ var style = styleCache[token];
1294
+ var match = void 0;
1295
+
1296
+ var isEmbedded;
1297
+ if (typeof style === 'string') {
1298
+ isEmbedded = false;
1299
+ } else {
1300
+ var patternParts = shortcuts[token.charAt(0)];
1301
+ if (patternParts) {
1302
+ match = token.match(patternParts[1]);
1303
+ style = patternParts[0];
1304
+ } else {
1305
+ for (var i = 0; i < nPatterns; ++i) {
1306
+ patternParts = fallthroughStylePatterns[i];
1307
+ match = token.match(patternParts[1]);
1308
+ if (match) {
1309
+ style = patternParts[0];
1310
+ break;
1311
+ }
1312
+ }
1313
+
1314
+ if (!match) { // make sure that we make progress
1315
+ style = PR_PLAIN;
1316
+ }
1317
+ }
1318
+
1319
+ isEmbedded = style.length >= 5 && 'lang-' === style.substring(0, 5);
1320
+ if (isEmbedded && !(match && typeof match[1] === 'string')) {
1321
+ isEmbedded = false;
1322
+ style = PR_SOURCE;
1323
+ }
1324
+
1325
+ if (!isEmbedded) { styleCache[token] = style; }
1326
+ }
1327
+
1328
+ var tokenStart = pos;
1329
+ pos += token.length;
1330
+
1331
+ if (!isEmbedded) {
1332
+ decorations.push(basePos + tokenStart, style);
1333
+ } else { // Treat group 1 as an embedded block of source code.
1334
+ var embeddedSource = match[1];
1335
+ var embeddedSourceStart = token.indexOf(embeddedSource);
1336
+ var embeddedSourceEnd = embeddedSourceStart + embeddedSource.length;
1337
+ if (match[2]) {
1338
+ // If embeddedSource can be blank, then it would match at the
1339
+ // beginning which would cause us to infinitely recurse on the
1340
+ // entire token, so we catch the right context in match[2].
1341
+ embeddedSourceEnd = token.length - match[2].length;
1342
+ embeddedSourceStart = embeddedSourceEnd - embeddedSource.length;
1343
+ }
1344
+ var lang = style.substring(5);
1345
+ // Decorate the left of the embedded source
1346
+ appendDecorations(
1347
+ basePos + tokenStart,
1348
+ token.substring(0, embeddedSourceStart),
1349
+ decorate, decorations);
1350
+ // Decorate the embedded source
1351
+ appendDecorations(
1352
+ basePos + tokenStart + embeddedSourceStart,
1353
+ embeddedSource,
1354
+ langHandlerForExtension(lang, embeddedSource),
1355
+ decorations);
1356
+ // Decorate the right of the embedded section
1357
+ appendDecorations(
1358
+ basePos + tokenStart + embeddedSourceEnd,
1359
+ token.substring(embeddedSourceEnd),
1360
+ decorate, decorations);
1361
+ }
1362
+ }
1363
+ job.decorations = decorations;
1364
+ };
1365
+ return decorate;
1366
+ }
1367
+
1368
+ /** returns a function that produces a list of decorations from source text.
1369
+ *
1370
+ * This code treats ", ', and ` as string delimiters, and \ as a string
1371
+ * escape. It does not recognize perl's qq() style strings.
1372
+ * It has no special handling for double delimiter escapes as in basic, or
1373
+ * the tripled delimiters used in python, but should work on those regardless
1374
+ * although in those cases a single string literal may be broken up into
1375
+ * multiple adjacent string literals.
1376
+ *
1377
+ * It recognizes C, C++, and shell style comments.
1378
+ *
1379
+ * @param {Object} options a set of optional parameters.
1380
+ * @return {function (Object)} a function that examines the source code
1381
+ * in the input job and builds the decoration list.
1382
+ */
1383
+ function sourceDecorator(options) {
1384
+ var shortcutStylePatterns = [], fallthroughStylePatterns = [];
1385
+ if (options['tripleQuotedStrings']) {
1386
+ // '''multi-line-string''', 'single-line-string', and double-quoted
1387
+ shortcutStylePatterns.push(
1388
+ [PR_STRING, /^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
1389
+ null, '\'"']);
1390
+ } else if (options['multiLineStrings']) {
1391
+ // 'multi-line-string', "multi-line-string"
1392
+ shortcutStylePatterns.push(
1393
+ [PR_STRING, /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,
1394
+ null, '\'"`']);
1395
+ } else {
1396
+ // 'single-line-string', "single-line-string"
1397
+ shortcutStylePatterns.push(
1398
+ [PR_STRING,
1399
+ /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,
1400
+ null, '"\'']);
1401
+ }
1402
+ if (options['verbatimStrings']) {
1403
+ // verbatim-string-literal production from the C# grammar. See issue 93.
1404
+ fallthroughStylePatterns.push(
1405
+ [PR_STRING, /^@\"(?:[^\"]|\"\")*(?:\"|$)/, null]);
1406
+ }
1407
+ var hc = options['hashComments'];
1408
+ if (hc) {
1409
+ if (options['cStyleComments']) {
1410
+ if (hc > 1) { // multiline hash comments
1411
+ shortcutStylePatterns.push(
1412
+ [PR_COMMENT, /^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/, null, '#']);
1413
+ } else {
1414
+ // Stop C preprocessor declarations at an unclosed open comment
1415
+ shortcutStylePatterns.push(
1416
+ [PR_COMMENT, /^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,
1417
+ null, '#']);
1418
+ }
1419
+ fallthroughStylePatterns.push(
1420
+ [PR_STRING,
1421
+ /^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,
1422
+ null]);
1423
+ } else {
1424
+ shortcutStylePatterns.push([PR_COMMENT, /^#[^\r\n]*/, null, '#']);
1425
+ }
1426
+ }
1427
+ if (options['cStyleComments']) {
1428
+ fallthroughStylePatterns.push([PR_COMMENT, /^\/\/[^\r\n]*/, null]);
1429
+ fallthroughStylePatterns.push(
1430
+ [PR_COMMENT, /^\/\*[\s\S]*?(?:\*\/|$)/, null]);
1431
+ }
1432
+ if (options['regexLiterals']) {
1433
+ var REGEX_LITERAL = (
1434
+ // A regular expression literal starts with a slash that is
1435
+ // not followed by * or / so that it is not confused with
1436
+ // comments.
1437
+ '/(?=[^/*])'
1438
+ // and then contains any number of raw characters,
1439
+ + '(?:[^/\\x5B\\x5C]'
1440
+ // escape sequences (\x5C),
1441
+ + '|\\x5C[\\s\\S]'
1442
+ // or non-nesting character sets (\x5B\x5D);
1443
+ + '|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+'
1444
+ // finally closed by a /.
1445
+ + '/');
1446
+ fallthroughStylePatterns.push(
1447
+ ['lang-regex',
1448
+ new RegExp('^' + REGEXP_PRECEDER_PATTERN + '(' + REGEX_LITERAL + ')')
1449
+ ]);
1450
+ }
1451
+
1452
+ var keywords = options['keywords'].replace(/^\s+|\s+$/g, '');
1453
+ if (keywords.length) {
1454
+ fallthroughStylePatterns.push(
1455
+ [PR_KEYWORD,
1456
+ new RegExp('^(?:' + keywords.replace(/\s+/g, '|') + ')\\b'), null]);
1457
+ }
1458
+
1459
+ shortcutStylePatterns.push([PR_PLAIN, /^\s+/, null, ' \r\n\t\xA0']);
1460
+ fallthroughStylePatterns.push(
1461
+ // TODO(mikesamuel): recognize non-latin letters and numerals in idents
1462
+ [PR_LITERAL, /^@[a-z_$][a-z_$@0-9]*/i, null],
1463
+ [PR_TYPE, /^@?[A-Z]+[a-z][A-Za-z_$@0-9]*/, null],
1464
+ [PR_PLAIN, /^[a-z_$][a-z_$@0-9]*/i, null],
1465
+ [PR_LITERAL,
1466
+ new RegExp(
1467
+ '^(?:'
1468
+ // A hex number
1469
+ + '0x[a-f0-9]+'
1470
+ // or an octal or decimal number,
1471
+ + '|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)'
1472
+ // possibly in scientific notation
1473
+ + '(?:e[+\\-]?\\d+)?'
1474
+ + ')'
1475
+ // with an optional modifier like UL for unsigned long
1476
+ + '[a-z]*', 'i'),
1477
+ null, '0123456789'],
1478
+ // Don't treat escaped quotes in bash as starting strings. See issue 144.
1479
+ [PR_PLAIN, /^\\[\s\S]?/, null],
1480
+ [PR_PUNCTUATION, /^.[^\s\w\.$@\'\"\`\/\#\\]*/, null]);
1481
+
1482
+ return createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns);
1483
+ }
1484
+
1485
+ var decorateSource = sourceDecorator({
1486
+ 'keywords': ALL_KEYWORDS,
1487
+ 'hashComments': true,
1488
+ 'cStyleComments': true,
1489
+ 'multiLineStrings': true,
1490
+ 'regexLiterals': true
1491
+ });
1492
+
1493
+ /**
1494
+ * Given a DOM subtree, wraps it in a list, and puts each line into its own
1495
+ * list item.
1496
+ *
1497
+ * @param {Node} node modified in place. Its content is pulled into an
1498
+ * HTMLOListElement, and each line is moved into a separate list item.
1499
+ * This requires cloning elements, so the input might not have unique
1500
+ * IDs after numbering.
1501
+ */
1502
+ function numberLines(node, opt_startLineNum) {
1503
+ var nocode = /(?:^|\s)nocode(?:\s|$)/;
1504
+ var lineBreak = /\r\n?|\n/;
1505
+
1506
+ var document = node.ownerDocument;
1507
+
1508
+ var whitespace;
1509
+ if (node.currentStyle) {
1510
+ whitespace = node.currentStyle.whiteSpace;
1511
+ } else if (window.getComputedStyle) {
1512
+ whitespace = document.defaultView.getComputedStyle(node, null)
1513
+ .getPropertyValue('white-space');
1514
+ }
1515
+ // If it's preformatted, then we need to split lines on line breaks
1516
+ // in addition to <BR>s.
1517
+ var isPreformatted = whitespace && 'pre' === whitespace.substring(0, 3);
1518
+
1519
+ var li = document.createElement('LI');
1520
+ while (node.firstChild) {
1521
+ li.appendChild(node.firstChild);
1522
+ }
1523
+ // An array of lines. We split below, so this is initialized to one
1524
+ // un-split line.
1525
+ var listItems = [li];
1526
+
1527
+ function walk(node) {
1528
+ switch (node.nodeType) {
1529
+ case 1: // Element
1530
+ if (nocode.test(node.className)) { break; }
1531
+ if ('BR' === node.nodeName) {
1532
+ breakAfter(node);
1533
+ // Discard the <BR> since it is now flush against a </LI>.
1534
+ if (node.parentNode) {
1535
+ node.parentNode.removeChild(node);
1536
+ }
1537
+ } else {
1538
+ for (var child = node.firstChild; child; child = child.nextSibling) {
1539
+ walk(child);
1540
+ }
1541
+ }
1542
+ break;
1543
+ case 3: case 4: // Text
1544
+ if (isPreformatted) {
1545
+ var text = node.nodeValue;
1546
+ var match = text.match(lineBreak);
1547
+ if (match) {
1548
+ var firstLine = text.substring(0, match.index);
1549
+ node.nodeValue = firstLine;
1550
+ var tail = text.substring(match.index + match[0].length);
1551
+ if (tail) {
1552
+ var parent = node.parentNode;
1553
+ parent.insertBefore(
1554
+ document.createTextNode(tail), node.nextSibling);
1555
+ }
1556
+ breakAfter(node);
1557
+ if (!firstLine) {
1558
+ // Don't leave blank text nodes in the DOM.
1559
+ node.parentNode.removeChild(node);
1560
+ }
1561
+ }
1562
+ }
1563
+ break;
1564
+ }
1565
+ }
1566
+
1567
+ // Split a line after the given node.
1568
+ function breakAfter(lineEndNode) {
1569
+ // If there's nothing to the right, then we can skip ending the line
1570
+ // here, and move root-wards since splitting just before an end-tag
1571
+ // would require us to create a bunch of empty copies.
1572
+ while (!lineEndNode.nextSibling) {
1573
+ lineEndNode = lineEndNode.parentNode;
1574
+ if (!lineEndNode) { return; }
1575
+ }
1576
+
1577
+ function breakLeftOf(limit, copy) {
1578
+ // Clone shallowly if this node needs to be on both sides of the break.
1579
+ var rightSide = copy ? limit.cloneNode(false) : limit;
1580
+ var parent = limit.parentNode;
1581
+ if (parent) {
1582
+ // We clone the parent chain.
1583
+ // This helps us resurrect important styling elements that cross lines.
1584
+ // E.g. in <i>Foo<br>Bar</i>
1585
+ // should be rewritten to <li><i>Foo</i></li><li><i>Bar</i></li>.
1586
+ var parentClone = breakLeftOf(parent, 1);
1587
+ // Move the clone and everything to the right of the original
1588
+ // onto the cloned parent.
1589
+ var next = limit.nextSibling;
1590
+ parentClone.appendChild(rightSide);
1591
+ for (var sibling = next; sibling; sibling = next) {
1592
+ next = sibling.nextSibling;
1593
+ parentClone.appendChild(sibling);
1594
+ }
1595
+ }
1596
+ return rightSide;
1597
+ }
1598
+
1599
+ var copiedListItem = breakLeftOf(lineEndNode.nextSibling, 0);
1600
+
1601
+ // Walk the parent chain until we reach an unattached LI.
1602
+ for (var parent;
1603
+ // Check nodeType since IE invents document fragments.
1604
+ (parent = copiedListItem.parentNode) && parent.nodeType === 1;) {
1605
+ copiedListItem = parent;
1606
+ }
1607
+ // Put it on the list of lines for later processing.
1608
+ listItems.push(copiedListItem);
1609
+ }
1610
+
1611
+ // Split lines while there are lines left to split.
1612
+ for (var i = 0; // Number of lines that have been split so far.
1613
+ i < listItems.length; // length updated by breakAfter calls.
1614
+ ++i) {
1615
+ walk(listItems[i]);
1616
+ }
1617
+
1618
+ // Make sure numeric indices show correctly.
1619
+ if (opt_startLineNum === (opt_startLineNum|0)) {
1620
+ listItems[0].setAttribute('value', opt_startLineNum);
1621
+ }
1622
+
1623
+ var ol = document.createElement('OL');
1624
+ ol.className = 'linenums';
1625
+ var offset = Math.max(0, ((opt_startLineNum - 1 /* zero index */)) | 0) || 0;
1626
+ for (var i = 0, n = listItems.length; i < n; ++i) {
1627
+ li = listItems[i];
1628
+ // Stick a class on the LIs so that stylesheets can
1629
+ // color odd/even rows, or any other row pattern that
1630
+ // is co-prime with 10.
1631
+ li.className = 'L' + ((i + offset) % 10);
1632
+ if (!li.firstChild) {
1633
+ li.appendChild(document.createTextNode('\xA0'));
1634
+ }
1635
+ ol.appendChild(li);
1636
+ }
1637
+
1638
+ node.appendChild(ol);
1639
+ }
1640
+
1641
+ /**
1642
+ * Breaks {@code job.source} around style boundaries in {@code job.decorations}
1643
+ * and modifies {@code job.sourceNode} in place.
1644
+ * @param {Object} job like <pre>{
1645
+ * source: {string} source as plain text,
1646
+ * spans: {Array.<number|Node>} alternating span start indices into source
1647
+ * and the text node or element (e.g. {@code <BR>}) corresponding to that
1648
+ * span.
1649
+ * decorations: {Array.<number|string} an array of style classes preceded
1650
+ * by the position at which they start in job.source in order
1651
+ * }</pre>
1652
+ * @private
1653
+ */
1654
+ function recombineTagsAndDecorations(job) {
1655
+ var isIE = /\bMSIE\b/.test(navigator.userAgent);
1656
+ var newlineRe = /\n/g;
1657
+
1658
+ var source = job.source;
1659
+ var sourceLength = source.length;
1660
+ // Index into source after the last code-unit recombined.
1661
+ var sourceIndex = 0;
1662
+
1663
+ var spans = job.spans;
1664
+ var nSpans = spans.length;
1665
+ // Index into spans after the last span which ends at or before sourceIndex.
1666
+ var spanIndex = 0;
1667
+
1668
+ var decorations = job.decorations;
1669
+ var nDecorations = decorations.length;
1670
+ // Index into decorations after the last decoration which ends at or before sourceIndex.
1671
+ var decorationIndex = 0;
1672
+
1673
+ // Simplify decorations.
1674
+ var decPos = 0;
1675
+ for (var i = 0; i < nDecorations;) {
1676
+ // Skip over any zero-length decorations.
1677
+ var startPos = decorations[i];
1678
+ var start = i;
1679
+ while (start + 2 < nDecorations && decorations[start + 2] === startPos) {
1680
+ start += 2;
1681
+ }
1682
+ // Conflate all adjacent decorations that use the same style.
1683
+ var startDec = decorations[start + 1];
1684
+ var end = start + 2;
1685
+ while (end + 2 <= nDecorations
1686
+ && (decorations[end + 1] === startDec
1687
+ || decorations[end] === decorations[end + 2])) {
1688
+ end += 2;
1689
+ }
1690
+ decorations[decPos++] = startPos;
1691
+ decorations[decPos++] = startDec;
1692
+ i = end;
1693
+ }
1694
+
1695
+ // Strip any zero-length decoration at the end.
1696
+ if (decPos && decorations[decPos - 2] === sourceLength) { decPos -= 2; }
1697
+ nDecorations = decorations.length = decPos;
1698
+
1699
+ var decoration = null;
1700
+ while (spanIndex < nSpans) {
1701
+ var spanStart = spans[spanIndex];
1702
+ var spanEnd = spans[spanIndex + 2] || sourceLength;
1703
+
1704
+ var decStart = decorations[decorationIndex];
1705
+ var decEnd = decorations[decorationIndex + 2] || sourceLength;
1706
+
1707
+ var end = Math.min(spanEnd, decEnd);
1708
+
1709
+ var textNode = spans[spanIndex + 1];
1710
+ if (textNode.nodeType !== 1) { // Don't muck with <BR>s or <LI>s
1711
+ var styledText = source.substring(sourceIndex, end);
1712
+ // This may seem bizarre, and it is. Emitting LF on IE causes the
1713
+ // code to display with spaces instead of line breaks.
1714
+ // Emitting Windows standard issue linebreaks (CRLF) causes a blank
1715
+ // space to appear at the beginning of every line but the first.
1716
+ // Emitting an old Mac OS 9 line separator makes everything spiffy.
1717
+ if (isIE) { styledText = styledText.replace(newlineRe, '\r'); }
1718
+ textNode.nodeValue = styledText;
1719
+ var document = textNode.ownerDocument;
1720
+ var span = document.createElement('SPAN');
1721
+ span.className = decorations[decorationIndex + 1];
1722
+ var parentNode = textNode.parentNode;
1723
+ parentNode.replaceChild(span, textNode);
1724
+ span.appendChild(textNode);
1725
+ if (sourceIndex < spanEnd) { // Split off a text node.
1726
+ spans[spanIndex + 1] = textNode
1727
+ // TODO: Possibly optimize by using '' if there's no flicker.
1728
+ = document.createTextNode(source.substring(end, spanEnd));
1729
+ parentNode.insertBefore(textNode, span.nextSibling);
1730
+ }
1731
+ }
1732
+
1733
+ sourceIndex = end;
1734
+
1735
+ if (sourceIndex >= spanEnd) {
1736
+ spanIndex += 2;
1737
+ }
1738
+ if (sourceIndex >= decEnd) {
1739
+ decorationIndex += 2;
1740
+ }
1741
+ }
1742
+ }
1743
+
1744
+
1745
+ /** Maps language-specific file extensions to handlers. */
1746
+ var langHandlerRegistry = {};
1747
+ /** Register a language handler for the given file extensions.
1748
+ * @param {function (Object)} handler a function from source code to a list
1749
+ * of decorations. Takes a single argument job which describes the
1750
+ * state of the computation. The single parameter has the form
1751
+ * {@code {
1752
+ * source: {string} as plain text.
1753
+ * decorations: {Array.<number|string>} an array of style classes
1754
+ * preceded by the position at which they start in
1755
+ * job.source in order.
1756
+ * The language handler should assigned this field.
1757
+ * basePos: {int} the position of source in the larger source chunk.
1758
+ * All positions in the output decorations array are relative
1759
+ * to the larger source chunk.
1760
+ * } }
1761
+ * @param {Array.<string>} fileExtensions
1762
+ */
1763
+ function registerLangHandler(handler, fileExtensions) {
1764
+ for (var i = fileExtensions.length; --i >= 0;) {
1765
+ var ext = fileExtensions[i];
1766
+ if (!langHandlerRegistry.hasOwnProperty(ext)) {
1767
+ langHandlerRegistry[ext] = handler;
1768
+ } else if ('console' in window) {
1769
+ console['warn']('cannot override language handler %s', ext);
1770
+ }
1771
+ }
1772
+ }
1773
+ function langHandlerForExtension(extension, source) {
1774
+ if (!(extension && langHandlerRegistry.hasOwnProperty(extension))) {
1775
+ // Treat it as markup if the first non whitespace character is a < and
1776
+ // the last non-whitespace character is a >.
1777
+ extension = /^\s*</.test(source)
1778
+ ? 'default-markup'
1779
+ : 'default-code';
1780
+ }
1781
+ return langHandlerRegistry[extension];
1782
+ }
1783
+ registerLangHandler(decorateSource, ['default-code']);
1784
+ registerLangHandler(
1785
+ createSimpleLexer(
1786
+ [],
1787
+ [
1788
+ [PR_PLAIN, /^[^<?]+/],
1789
+ [PR_DECLARATION, /^<!\w[^>]*(?:>|$)/],
1790
+ [PR_COMMENT, /^<\!--[\s\S]*?(?:-\->|$)/],
1791
+ // Unescaped content in an unknown language
1792
+ ['lang-', /^<\?([\s\S]+?)(?:\?>|$)/],
1793
+ ['lang-', /^<%([\s\S]+?)(?:%>|$)/],
1794
+ [PR_PUNCTUATION, /^(?:<[%?]|[%?]>)/],
1795
+ ['lang-', /^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],
1796
+ // Unescaped content in javascript. (Or possibly vbscript).
1797
+ ['lang-js', /^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],
1798
+ // Contains unescaped stylesheet content
1799
+ ['lang-css', /^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],
1800
+ ['lang-in.tag', /^(<\/?[a-z][^<>]*>)/i]
1801
+ ]),
1802
+ ['default-markup', 'htm', 'html', 'mxml', 'xhtml', 'xml', 'xsl']);
1803
+ registerLangHandler(
1804
+ createSimpleLexer(
1805
+ [
1806
+ [PR_PLAIN, /^[\s]+/, null, ' \t\r\n'],
1807
+ [PR_ATTRIB_VALUE, /^(?:\"[^\"]*\"?|\'[^\']*\'?)/, null, '\"\'']
1808
+ ],
1809
+ [
1810
+ [PR_TAG, /^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],
1811
+ [PR_ATTRIB_NAME, /^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],
1812
+ ['lang-uq.val', /^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],
1813
+ [PR_PUNCTUATION, /^[=<>\/]+/],
1814
+ ['lang-js', /^on\w+\s*=\s*\"([^\"]+)\"/i],
1815
+ ['lang-js', /^on\w+\s*=\s*\'([^\']+)\'/i],
1816
+ ['lang-js', /^on\w+\s*=\s*([^\"\'>\s]+)/i],
1817
+ ['lang-css', /^style\s*=\s*\"([^\"]+)\"/i],
1818
+ ['lang-css', /^style\s*=\s*\'([^\']+)\'/i],
1819
+ ['lang-css', /^style\s*=\s*([^\"\'>\s]+)/i]
1820
+ ]),
1821
+ ['in.tag']);
1822
+ registerLangHandler(
1823
+ createSimpleLexer([], [[PR_ATTRIB_VALUE, /^[\s\S]+/]]), ['uq.val']);
1824
+ registerLangHandler(sourceDecorator({
1825
+ 'keywords': CPP_KEYWORDS,
1826
+ 'hashComments': true,
1827
+ 'cStyleComments': true
1828
+ }), ['c', 'cc', 'cpp', 'cxx', 'cyc', 'm']);
1829
+ registerLangHandler(sourceDecorator({
1830
+ 'keywords': 'null true false'
1831
+ }), ['json']);
1832
+ registerLangHandler(sourceDecorator({
1833
+ 'keywords': CSHARP_KEYWORDS,
1834
+ 'hashComments': true,
1835
+ 'cStyleComments': true,
1836
+ 'verbatimStrings': true
1837
+ }), ['cs']);
1838
+ registerLangHandler(sourceDecorator({
1839
+ 'keywords': JAVA_KEYWORDS,
1840
+ 'cStyleComments': true
1841
+ }), ['java']);
1842
+ registerLangHandler(sourceDecorator({
1843
+ 'keywords': SH_KEYWORDS,
1844
+ 'hashComments': true,
1845
+ 'multiLineStrings': true
1846
+ }), ['bsh', 'csh', 'sh']);
1847
+ registerLangHandler(sourceDecorator({
1848
+ 'keywords': PYTHON_KEYWORDS,
1849
+ 'hashComments': true,
1850
+ 'multiLineStrings': true,
1851
+ 'tripleQuotedStrings': true
1852
+ }), ['cv', 'py']);
1853
+ registerLangHandler(sourceDecorator({
1854
+ 'keywords': PERL_KEYWORDS,
1855
+ 'hashComments': true,
1856
+ 'multiLineStrings': true,
1857
+ 'regexLiterals': true
1858
+ }), ['perl', 'pl', 'pm']);
1859
+ registerLangHandler(sourceDecorator({
1860
+ 'keywords': RUBY_KEYWORDS,
1861
+ 'hashComments': true,
1862
+ 'multiLineStrings': true,
1863
+ 'regexLiterals': true
1864
+ }), ['rb']);
1865
+ registerLangHandler(sourceDecorator({
1866
+ 'keywords': JSCRIPT_KEYWORDS,
1867
+ 'cStyleComments': true,
1868
+ 'regexLiterals': true
1869
+ }), ['js']);
1870
+ registerLangHandler(sourceDecorator({
1871
+ 'keywords': COFFEE_KEYWORDS,
1872
+ 'hashComments': 3, // ### style block comments
1873
+ 'cStyleComments': true,
1874
+ 'multilineStrings': true,
1875
+ 'tripleQuotedStrings': true,
1876
+ 'regexLiterals': true
1877
+ }), ['coffee']);
1878
+ registerLangHandler(createSimpleLexer([], [[PR_STRING, /^[\s\S]+/]]), ['regex']);
1879
+
1880
+ function applyDecorator(job) {
1881
+ var opt_langExtension = job.langExtension;
1882
+
1883
+ try {
1884
+ // Extract tags, and convert the source code to plain text.
1885
+ var sourceAndSpans = extractSourceSpans(job.sourceNode);
1886
+ /** Plain text. @type {string} */
1887
+ var source = sourceAndSpans.source;
1888
+ job.source = source;
1889
+ job.spans = sourceAndSpans.spans;
1890
+ job.basePos = 0;
1891
+
1892
+ // Apply the appropriate language handler
1893
+ langHandlerForExtension(opt_langExtension, source)(job);
1894
+
1895
+ // Integrate the decorations and tags back into the source code,
1896
+ // modifying the sourceNode in place.
1897
+ recombineTagsAndDecorations(job);
1898
+ } catch (e) {
1899
+ if ('console' in window) {
1900
+ console['log'](e && e['stack'] ? e['stack'] : e);
1901
+ }
1902
+ }
1903
+ }
1904
+
1905
+ /**
1906
+ * @param sourceCodeHtml {string} The HTML to pretty print.
1907
+ * @param opt_langExtension {string} The language name to use.
1908
+ * Typically, a filename extension like 'cpp' or 'java'.
1909
+ * @param opt_numberLines {number|boolean} True to number lines,
1910
+ * or the 1-indexed number of the first line in sourceCodeHtml.
1911
+ */
1912
+ function prettyPrintOne(sourceCodeHtml, opt_langExtension, opt_numberLines) {
1913
+ var container = document.createElement('PRE');
1914
+ // This could cause images to load and onload listeners to fire.
1915
+ // E.g. <img onerror="alert(1337)" src="nosuchimage.png">.
1916
+ // We assume that the inner HTML is from a trusted source.
1917
+ container.innerHTML = sourceCodeHtml;
1918
+ if (opt_numberLines) {
1919
+ numberLines(container, opt_numberLines);
1920
+ }
1921
+
1922
+ var job = {
1923
+ langExtension: opt_langExtension,
1924
+ numberLines: opt_numberLines,
1925
+ sourceNode: container
1926
+ };
1927
+ applyDecorator(job);
1928
+ return container.innerHTML;
1929
+ }
1930
+
1931
+ function prettyPrint(opt_whenDone) {
1932
+ function byTagName(tn) { return document.getElementsByTagName(tn); }
1933
+ // fetch a list of nodes to rewrite
1934
+ var codeSegments = [byTagName('pre'), byTagName('code'), byTagName('xmp')];
1935
+ var elements = [];
1936
+ for (var i = 0; i < codeSegments.length; ++i) {
1937
+ for (var j = 0, n = codeSegments[i].length; j < n; ++j) {
1938
+ elements.push(codeSegments[i][j]);
1939
+ }
1940
+ }
1941
+ codeSegments = null;
1942
+
1943
+ var clock = Date;
1944
+ if (!clock['now']) {
1945
+ clock = { 'now': function () { return (new Date).getTime(); } };
1946
+ }
1947
+
1948
+ // The loop is broken into a series of continuations to make sure that we
1949
+ // don't make the browser unresponsive when rewriting a large page.
1950
+ var k = 0;
1951
+ var prettyPrintingJob;
1952
+
1953
+ function doWork() {
1954
+ var endTime = (window['PR_SHOULD_USE_CONTINUATION'] ?
1955
+ clock.now() + 250 /* ms */ :
1956
+ Infinity);
1957
+ for (; k < elements.length && clock.now() < endTime; k++) {
1958
+ var cs = elements[k];
1959
+ if (cs.className && cs.className.indexOf('prettyprint') >= 0) {
1960
+ // If the classes includes a language extensions, use it.
1961
+ // Language extensions can be specified like
1962
+ // <pre class="prettyprint lang-cpp">
1963
+ // the language extension "cpp" is used to find a language handler as
1964
+ // passed to PR.registerLangHandler.
1965
+ var langExtension = cs.className.match(/\blang-(\w+)\b/);
1966
+ if (langExtension) { langExtension = langExtension[1]; }
1967
+
1968
+ // make sure this is not nested in an already prettified element
1969
+ var nested = false;
1970
+ for (var p = cs.parentNode; p; p = p.parentNode) {
1971
+ if ((p.tagName === 'pre' || p.tagName === 'code' ||
1972
+ p.tagName === 'xmp') &&
1973
+ p.className && p.className.indexOf('prettyprint') >= 0) {
1974
+ nested = true;
1975
+ break;
1976
+ }
1977
+ }
1978
+ if (!nested) {
1979
+ // Look for a class like linenums or linenums:<n> where <n> is the
1980
+ // 1-indexed number of the first line.
1981
+ var lineNums = cs.className.match(/\blinenums\b(?::(\d+))?/);
1982
+ lineNums = lineNums
1983
+ ? lineNums[1] && lineNums[1].length ? +lineNums[1] : true
1984
+ : false;
1985
+ if (lineNums) { numberLines(cs, lineNums); }
1986
+
1987
+ // do the pretty printing
1988
+ prettyPrintingJob = {
1989
+ langExtension: langExtension,
1990
+ sourceNode: cs,
1991
+ numberLines: lineNums
1992
+ };
1993
+ applyDecorator(prettyPrintingJob);
1994
+ }
1995
+ }
1996
+ }
1997
+ if (k < elements.length) {
1998
+ // finish up in a continuation
1999
+ setTimeout(doWork, 250);
2000
+ } else if (opt_whenDone) {
2001
+ opt_whenDone();
2002
+ }
2003
+ }
2004
+
2005
+ doWork();
2006
+ }
2007
+
2008
+ window['prettyPrintOne'] = prettyPrintOne;
2009
+ window['prettyPrint'] = prettyPrint;
2010
+ window['PR'] = {
2011
+ 'createSimpleLexer': createSimpleLexer,
2012
+ 'registerLangHandler': registerLangHandler,
2013
+ 'sourceDecorator': sourceDecorator,
2014
+ 'PR_ATTRIB_NAME': PR_ATTRIB_NAME,
2015
+ 'PR_ATTRIB_VALUE': PR_ATTRIB_VALUE,
2016
+ 'PR_COMMENT': PR_COMMENT,
2017
+ 'PR_DECLARATION': PR_DECLARATION,
2018
+ 'PR_KEYWORD': PR_KEYWORD,
2019
+ 'PR_LITERAL': PR_LITERAL,
2020
+ 'PR_NOCODE': PR_NOCODE,
2021
+ 'PR_PLAIN': PR_PLAIN,
2022
+ 'PR_PUNCTUATION': PR_PUNCTUATION,
2023
+ 'PR_SOURCE': PR_SOURCE,
2024
+ 'PR_STRING': PR_STRING,
2025
+ 'PR_TAG': PR_TAG,
2026
+ 'PR_TYPE': PR_TYPE
2027
+ };
2028
+ })();
2029
+
2030
+ /*
2031
+ Based on Google HTML5 slides template
2032
+ URL: http://code.google.com/p/html5slides/
2033
+ */
2034
+
2035
+ var PERMANENT_URL_PREFIX = 'http://html5slides.googlecode.com/svn/trunk/';
2036
+
2037
+ var SLIDE_CLASSES = ['far-past', 'past', 'current', 'next', 'far-next'];
2038
+
2039
+ var PM_TOUCH_SENSITIVITY = 15;
2040
+
2041
+ var curSlide;
2042
+
2043
+ /* ---------------------------------------------------------------------- */
2044
+ /* classList polyfill by Eli Grey
2045
+ * (http://purl.eligrey.com/github/classList.js/blob/master/classList.js) */
2046
+
2047
+ if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) {
2048
+
2049
+ (function (view) {
2050
+
2051
+ var
2052
+ classListProp = "classList"
2053
+ , protoProp = "prototype"
2054
+ , elemCtrProto = (view.HTMLElement || view.Element)[protoProp]
2055
+ , objCtr = Object
2056
+ strTrim = String[protoProp].trim || function () {
2057
+ return this.replace(/^\s+|\s+$/g, "");
2058
+ }
2059
+ , arrIndexOf = Array[protoProp].indexOf || function (item) {
2060
+ for (var i = 0, len = this.length; i < len; i++) {
2061
+ if (i in this && this[i] === item) {
2062
+ return i;
2063
+ }
2064
+ }
2065
+ return -1;
2066
+ }
2067
+ // Vendors: please allow content code to instantiate DOMExceptions
2068
+ , DOMEx = function (type, message) {
2069
+ this.name = type;
2070
+ this.code = DOMException[type];
2071
+ this.message = message;
2072
+ }
2073
+ , checkTokenAndGetIndex = function (classList, token) {
2074
+ if (token === "") {
2075
+ throw new DOMEx(
2076
+ "SYNTAX_ERR"
2077
+ , "An invalid or illegal string was specified"
2078
+ );
2079
+ }
2080
+ if (/\s/.test(token)) {
2081
+ throw new DOMEx(
2082
+ "INVALID_CHARACTER_ERR"
2083
+ , "String contains an invalid character"
2084
+ );
2085
+ }
2086
+ return arrIndexOf.call(classList, token);
2087
+ }
2088
+ , ClassList = function (elem) {
2089
+ var
2090
+ trimmedClasses = strTrim.call(elem.className)
2091
+ , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : []
2092
+ ;
2093
+ for (var i = 0, len = classes.length; i < len; i++) {
2094
+ this.push(classes[i]);
2095
+ }
2096
+ this._updateClassName = function () {
2097
+ elem.className = this.toString();
2098
+ };
2099
+ }
2100
+ , classListProto = ClassList[protoProp] = []
2101
+ , classListGetter = function () {
2102
+ return new ClassList(this);
2103
+ }
2104
+ ;
2105
+ // Most DOMException implementations don't allow calling DOMException's toString()
2106
+ // on non-DOMExceptions. Error's toString() is sufficient here.
2107
+ DOMEx[protoProp] = Error[protoProp];
2108
+ classListProto.item = function (i) {
2109
+ return this[i] || null;
2110
+ };
2111
+ classListProto.contains = function (token) {
2112
+ token += "";
2113
+ return checkTokenAndGetIndex(this, token) !== -1;
2114
+ };
2115
+ classListProto.add = function (token) {
2116
+ token += "";
2117
+ if (checkTokenAndGetIndex(this, token) === -1) {
2118
+ this.push(token);
2119
+ this._updateClassName();
2120
+ }
2121
+ };
2122
+ classListProto.remove = function (token) {
2123
+ token += "";
2124
+ var index = checkTokenAndGetIndex(this, token);
2125
+ if (index !== -1) {
2126
+ this.splice(index, 1);
2127
+ this._updateClassName();
2128
+ }
2129
+ };
2130
+ classListProto.toggle = function (token) {
2131
+ token += "";
2132
+ if (checkTokenAndGetIndex(this, token) === -1) {
2133
+ this.add(token);
2134
+ } else {
2135
+ this.remove(token);
2136
+ }
2137
+ };
2138
+ classListProto.toString = function () {
2139
+ return this.join(" ");
2140
+ };
2141
+
2142
+ if (objCtr.defineProperty) {
2143
+ var classListPropDesc = {
2144
+ get: classListGetter
2145
+ , enumerable: true
2146
+ , configurable: true
2147
+ };
2148
+ try {
2149
+ objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
2150
+ } catch (ex) { // IE 8 doesn't support enumerable:true
2151
+ if (ex.number === -0x7FF5EC54) {
2152
+ classListPropDesc.enumerable = false;
2153
+ objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
2154
+ }
2155
+ }
2156
+ } else if (objCtr[protoProp].__defineGetter__) {
2157
+ elemCtrProto.__defineGetter__(classListProp, classListGetter);
2158
+ }
2159
+
2160
+ }(self));
2161
+
2162
+ }
2163
+ /* ---------------------------------------------------------------------- */
2164
+
2165
+ /* Slide movement */
2166
+
2167
+ function getSlideEl(no) {
2168
+ if ((no < 0) || (no >= slideEls.length)) {
2169
+ return null;
2170
+ } else {
2171
+ return slideEls[no];
2172
+ }
2173
+ };
2174
+
2175
+ function updateSlideClass(slideNo, className) {
2176
+ var el = getSlideEl(slideNo);
2177
+
2178
+ if (!el) {
2179
+ return;
2180
+ }
2181
+
2182
+ if (className) {
2183
+ el.classList.add(className);
2184
+ }
2185
+
2186
+ for (var i in SLIDE_CLASSES) {
2187
+ if (className != SLIDE_CLASSES[i]) {
2188
+ el.classList.remove(SLIDE_CLASSES[i]);
2189
+ }
2190
+ }
2191
+ };
2192
+
2193
+ function updateSlides() {
2194
+ for (var i = 0; i < slideEls.length; i++) {
2195
+ switch (i) {
2196
+ case curSlide - 2:
2197
+ updateSlideClass(i, 'far-past');
2198
+ break;
2199
+ case curSlide - 1:
2200
+ updateSlideClass(i, 'past');
2201
+ break;
2202
+ case curSlide:
2203
+ updateSlideClass(i, 'current');
2204
+ break;
2205
+ case curSlide + 1:
2206
+ updateSlideClass(i, 'next');
2207
+ break;
2208
+ case curSlide + 2:
2209
+ updateSlideClass(i, 'far-next');
2210
+ break;
2211
+ default:
2212
+ updateSlideClass(i);
2213
+ break;
2214
+ }
2215
+ }
2216
+
2217
+ triggerLeaveEvent(curSlide - 1);
2218
+ triggerEnterEvent(curSlide);
2219
+
2220
+ window.setTimeout(function() {
2221
+ // Hide after the slide
2222
+ disableSlideFrames(curSlide - 2);
2223
+ }, 301);
2224
+
2225
+ enableSlideFrames(curSlide - 1);
2226
+ enableSlideFrames(curSlide + 2);
2227
+
2228
+ if (isChromeVoxActive()) {
2229
+ speakAndSyncToNode(slideEls[curSlide]);
2230
+ }
2231
+
2232
+ updateHash();
2233
+ };
2234
+
2235
+ function buildNextItem() {
2236
+ var toBuild = slideEls[curSlide].querySelectorAll('.to-build');
2237
+
2238
+ if (!toBuild.length) {
2239
+ return false;
2240
+ }
2241
+
2242
+ toBuild[0].classList.remove('to-build', '');
2243
+
2244
+ if (isChromeVoxActive()) {
2245
+ speakAndSyncToNode(toBuild[0]);
2246
+ }
2247
+
2248
+ return true;
2249
+ };
2250
+
2251
+ function prevSlide() {
2252
+ if (curSlide > 0) {
2253
+ curSlide--;
2254
+
2255
+ updateSlides();
2256
+ }
2257
+ };
2258
+
2259
+ function nextSlide() {
2260
+ if (buildNextItem()) {
2261
+ return;
2262
+ }
2263
+
2264
+ if (curSlide < slideEls.length - 1) {
2265
+ curSlide++;
2266
+
2267
+ updateSlides();
2268
+ }
2269
+ };
2270
+
2271
+ /* Slide events */
2272
+
2273
+ function triggerEnterEvent(no) {
2274
+ var el = getSlideEl(no);
2275
+ if (!el) {
2276
+ return;
2277
+ }
2278
+
2279
+ var onEnter = el.getAttribute('onslideenter');
2280
+ if (onEnter) {
2281
+ new Function(onEnter).call(el);
2282
+ }
2283
+
2284
+ var evt = document.createEvent('Event');
2285
+ evt.initEvent('slideenter', true, true);
2286
+ evt.slideNumber = no + 1; // Make it readable
2287
+
2288
+ el.dispatchEvent(evt);
2289
+ };
2290
+
2291
+ function triggerLeaveEvent(no) {
2292
+ var el = getSlideEl(no);
2293
+ if (!el) {
2294
+ return;
2295
+ }
2296
+
2297
+ var onLeave = el.getAttribute('onslideleave');
2298
+ if (onLeave) {
2299
+ new Function(onLeave).call(el);
2300
+ }
2301
+
2302
+ var evt = document.createEvent('Event');
2303
+ evt.initEvent('slideleave', true, true);
2304
+ evt.slideNumber = no + 1; // Make it readable
2305
+
2306
+ el.dispatchEvent(evt);
2307
+ };
2308
+
2309
+ /* Touch events */
2310
+
2311
+ function handleTouchStart(event) {
2312
+ if (event.touches.length == 1) {
2313
+ touchDX = 0;
2314
+ touchDY = 0;
2315
+
2316
+ touchStartX = event.touches[0].pageX;
2317
+ touchStartY = event.touches[0].pageY;
2318
+
2319
+ document.body.addEventListener('touchmove', handleTouchMove, true);
2320
+ document.body.addEventListener('touchend', handleTouchEnd, true);
2321
+ }
2322
+ };
2323
+
2324
+ function handleTouchMove(event) {
2325
+ if (event.touches.length > 1) {
2326
+ cancelTouch();
2327
+ } else {
2328
+ touchDX = event.touches[0].pageX - touchStartX;
2329
+ touchDY = event.touches[0].pageY - touchStartY;
2330
+ }
2331
+ };
2332
+
2333
+ function handleTouchEnd(event) {
2334
+ var dx = Math.abs(touchDX);
2335
+ var dy = Math.abs(touchDY);
2336
+
2337
+ if ((dx > PM_TOUCH_SENSITIVITY) && (dy < (dx * 2 / 3))) {
2338
+ if (touchDX > 0) {
2339
+ prevSlide();
2340
+ } else {
2341
+ nextSlide();
2342
+ }
2343
+ }
2344
+
2345
+ cancelTouch();
2346
+ };
2347
+
2348
+ function cancelTouch() {
2349
+ document.body.removeEventListener('touchmove', handleTouchMove, true);
2350
+ document.body.removeEventListener('touchend', handleTouchEnd, true);
2351
+ };
2352
+
2353
+ /* Preloading frames */
2354
+
2355
+ function disableSlideFrames(no) {
2356
+ var el = getSlideEl(no);
2357
+ if (!el) {
2358
+ return;
2359
+ }
2360
+
2361
+ var frames = el.getElementsByTagName('iframe');
2362
+ for (var i = 0, frame; frame = frames[i]; i++) {
2363
+ disableFrame(frame);
2364
+ }
2365
+ };
2366
+
2367
+ function enableSlideFrames(no) {
2368
+ var el = getSlideEl(no);
2369
+ if (!el) {
2370
+ return;
2371
+ }
2372
+
2373
+ var frames = el.getElementsByTagName('iframe');
2374
+ for (var i = 0, frame; frame = frames[i]; i++) {
2375
+ enableFrame(frame);
2376
+ }
2377
+ };
2378
+
2379
+ function disableFrame(frame) {
2380
+ frame.src = 'about:blank';
2381
+ };
2382
+
2383
+ function enableFrame(frame) {
2384
+ var src = frame._src;
2385
+
2386
+ if (frame.src != src && src != 'about:blank') {
2387
+ frame.src = src;
2388
+ }
2389
+ };
2390
+
2391
+ function setupFrames() {
2392
+ var frames = document.querySelectorAll('iframe');
2393
+ for (var i = 0, frame; frame = frames[i]; i++) {
2394
+ frame._src = frame.src;
2395
+ disableFrame(frame);
2396
+ }
2397
+
2398
+ enableSlideFrames(curSlide);
2399
+ enableSlideFrames(curSlide + 1);
2400
+ enableSlideFrames(curSlide + 2);
2401
+ };
2402
+
2403
+ function setupInteraction() {
2404
+ /* Clicking and tapping */
2405
+
2406
+ var el = document.createElement('div');
2407
+ el.className = 'slide-area';
2408
+ el.id = 'prev-slide-area';
2409
+ el.addEventListener('click', prevSlide, false);
2410
+ document.querySelector('section.slides').appendChild(el);
2411
+
2412
+ var el = document.createElement('div');
2413
+ el.className = 'slide-area';
2414
+ el.id = 'next-slide-area';
2415
+ el.addEventListener('click', nextSlide, false);
2416
+ document.querySelector('section.slides').appendChild(el);
2417
+
2418
+ /* Swiping */
2419
+
2420
+ document.body.addEventListener('touchstart', handleTouchStart, false);
2421
+ }
2422
+
2423
+ /* ChromeVox support */
2424
+
2425
+ function isChromeVoxActive() {
2426
+ if (typeof(cvox) == 'undefined') {
2427
+ return false;
2428
+ } else {
2429
+ return true;
2430
+ }
2431
+ };
2432
+
2433
+ function speakAndSyncToNode(node) {
2434
+ if (!isChromeVoxActive()) {
2435
+ return;
2436
+ }
2437
+
2438
+ cvox.ChromeVox.navigationManager.switchToStrategy(
2439
+ cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM, 0, true);
2440
+ cvox.ChromeVox.navigationManager.syncToNode(node);
2441
+ cvox.ChromeVoxUserCommands.finishNavCommand('');
2442
+ var target = node;
2443
+ while (target.firstChild) {
2444
+ target = target.firstChild;
2445
+ }
2446
+ cvox.ChromeVox.navigationManager.syncToNode(target);
2447
+ };
2448
+
2449
+ function speakNextItem() {
2450
+ if (!isChromeVoxActive()) {
2451
+ return;
2452
+ }
2453
+
2454
+ cvox.ChromeVox.navigationManager.switchToStrategy(
2455
+ cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM, 0, true);
2456
+ cvox.ChromeVox.navigationManager.next(true);
2457
+ if (!cvox.DomUtil.isDescendantOfNode(
2458
+ cvox.ChromeVox.navigationManager.getCurrentNode(), slideEls[curSlide])){
2459
+ var target = slideEls[curSlide];
2460
+ while (target.firstChild) {
2461
+ target = target.firstChild;
2462
+ }
2463
+ cvox.ChromeVox.navigationManager.syncToNode(target);
2464
+ cvox.ChromeVox.navigationManager.next(true);
2465
+ }
2466
+ cvox.ChromeVoxUserCommands.finishNavCommand('');
2467
+ };
2468
+
2469
+ function speakPrevItem() {
2470
+ if (!isChromeVoxActive()) {
2471
+ return;
2472
+ }
2473
+
2474
+ cvox.ChromeVox.navigationManager.switchToStrategy(
2475
+ cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM, 0, true);
2476
+ cvox.ChromeVox.navigationManager.previous(true);
2477
+ if (!cvox.DomUtil.isDescendantOfNode(
2478
+ cvox.ChromeVox.navigationManager.getCurrentNode(), slideEls[curSlide])){
2479
+ var target = slideEls[curSlide];
2480
+ while (target.lastChild){
2481
+ target = target.lastChild;
2482
+ }
2483
+ cvox.ChromeVox.navigationManager.syncToNode(target);
2484
+ cvox.ChromeVox.navigationManager.previous(true);
2485
+ }
2486
+ cvox.ChromeVoxUserCommands.finishNavCommand('');
2487
+ };
2488
+
2489
+ /* Hash functions */
2490
+
2491
+ function getCurSlideFromHash() {
2492
+ var slideNo = parseInt(location.hash.substr(1));
2493
+
2494
+ if (slideNo) {
2495
+ curSlide = slideNo - 1;
2496
+ } else {
2497
+ curSlide = 0;
2498
+ }
2499
+ };
2500
+
2501
+ function updateHash() {
2502
+ location.replace('#' + (curSlide + 1));
2503
+ };
2504
+
2505
+ /* Event listeners */
2506
+
2507
+ function handleBodyKeyDown(event) {
2508
+ switch (event.keyCode) {
2509
+ case 39: // right arrow
2510
+ case 13: // Enter
2511
+ case 32: // space
2512
+ case 34: // PgDn
2513
+ nextSlide();
2514
+ event.preventDefault();
2515
+ break;
2516
+
2517
+ case 37: // left arrow
2518
+ case 8: // Backspace
2519
+ case 33: // PgUp
2520
+ prevSlide();
2521
+ event.preventDefault();
2522
+ break;
2523
+
2524
+ case 40: // down arrow
2525
+ if (isChromeVoxActive()) {
2526
+ speakNextItem();
2527
+ } else {
2528
+ nextSlide();
2529
+ }
2530
+ event.preventDefault();
2531
+ break;
2532
+
2533
+ case 38: // up arrow
2534
+ if (isChromeVoxActive()) {
2535
+ speakPrevItem();
2536
+ } else {
2537
+ prevSlide();
2538
+ }
2539
+ event.preventDefault();
2540
+ break;
2541
+ }
2542
+ };
2543
+
2544
+ function addEventListeners() {
2545
+ document.addEventListener('keydown', handleBodyKeyDown, false);
2546
+ };
2547
+
2548
+ /* Initialization */
2549
+
2550
+ function addPrettify() {
2551
+ document.body.onload = function() {
2552
+
2553
+ for (var i = curSlide, slide; slide = slideEls[i]; i++) {
2554
+ var codes = slide.querySelectorAll('code');
2555
+ for (var j = 0, code; code = codes[j]; j++) {
2556
+ if (code.classList) {
2557
+ code.classList.add('prettyprint');
2558
+ }
2559
+ }
2560
+ }
2561
+
2562
+ prettyPrint();
2563
+ }
2564
+ };
2565
+
2566
+ function addFontStyle() {
2567
+ var el = document.createElement('link');
2568
+ el.rel = 'stylesheet';
2569
+ el.type = 'text/css';
2570
+ el.href = 'http://fonts.googleapis.com/css?family=' +
2571
+ 'Open+Sans:regular,semibold,italic,italicsemibold|Droid+Sans+Mono';
2572
+
2573
+ document.body.appendChild(el);
2574
+ };
2575
+
2576
+ function addGeneralStyle() {
2577
+ var el = document.createElement('meta');
2578
+ el.name = 'viewport';
2579
+ el.content = 'width=1100,height=750';
2580
+ document.querySelector('head').appendChild(el);
2581
+
2582
+ var el = document.createElement('meta');
2583
+ el.name = 'apple-mobile-web-app-capable';
2584
+ el.content = 'yes';
2585
+ document.querySelector('head').appendChild(el);
2586
+ };
2587
+
2588
+ function makeBuildLists() {
2589
+ for (var i = curSlide, slide; slide = slideEls[i]; i++) {
2590
+ var items = slide.querySelectorAll('.build > *');
2591
+ for (var j = 0, item; item = items[j]; j++) {
2592
+ if (item.classList) {
2593
+ item.classList.add('to-build');
2594
+ }
2595
+ }
2596
+ }
2597
+ };
2598
+
2599
+ function handleDomLoaded() {
2600
+ slideEls = document.querySelectorAll('section.slides > article');
2601
+
2602
+ setupFrames();
2603
+
2604
+ addFontStyle();
2605
+ addGeneralStyle();
2606
+ addPrettify();
2607
+ addEventListeners();
2608
+
2609
+ updateSlides();
2610
+
2611
+ setupInteraction();
2612
+ makeBuildLists();
2613
+
2614
+ document.body.classList.add('loaded');
2615
+ };
2616
+
2617
+ function initialize() {
2618
+ getCurSlideFromHash();
2619
+
2620
+ if (window['_DEBUG']) {
2621
+ PERMANENT_URL_PREFIX = '../';
2622
+ }
2623
+
2624
+ if (window['_DCL']) {
2625
+ handleDomLoaded();
2626
+ } else {
2627
+ document.addEventListener('DOMContentLoaded', handleDomLoaded, false);
2628
+ }
2629
+ }
2630
+
2631
+ // If ?debug exists then load the script relative instead of absolute
2632
+ if (!window['_DEBUG'] && document.location.href.indexOf('?debug') !== -1) {
2633
+ document.addEventListener('DOMContentLoaded', function() {
2634
+ // Avoid missing the DomContentLoaded event
2635
+ window['_DCL'] = true
2636
+ }, false);
2637
+
2638
+ window['_DEBUG'] = true;
2639
+ var script = document.createElement('script');
2640
+ script.type = 'text/javascript';
2641
+ script.src = '../slides.js';
2642
+ var s = document.getElementsByTagName('script')[0];
2643
+ s.parentNode.insertBefore(script, s);
2644
+
2645
+ // Remove this script
2646
+ s.parentNode.removeChild(s);
2647
+ } else {
2648
+ initialize();
2649
+ }
2650
+ EOS
2651
+ end