showoff 0.7.0 → 0.9.7

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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/README.rdoc +53 -475
  3. data/Rakefile +17 -18
  4. data/bin/showoff +29 -7
  5. data/lib/commandline_parser.rb +1 -1
  6. data/lib/showoff/version.rb +3 -0
  7. data/lib/showoff.rb +600 -91
  8. data/lib/showoff_utils.rb +110 -4
  9. data/public/css/disconnected-large.png +0 -0
  10. data/public/css/disconnected.png +0 -0
  11. data/public/css/fast.png +0 -0
  12. data/public/css/grippy-close.png +0 -0
  13. data/public/css/grippy.png +0 -0
  14. data/public/css/onepage.css +6 -0
  15. data/public/css/pace.png +0 -0
  16. data/public/css/paceMarker.png +0 -0
  17. data/public/css/presenter.css +333 -43
  18. data/public/css/sh_style.css +15 -0
  19. data/public/css/showoff.css +373 -48
  20. data/public/css/slow.png +0 -0
  21. data/public/css/spinner.gif +0 -0
  22. data/public/css/tipsy.css +26 -0
  23. data/public/favicon.ico +0 -0
  24. data/public/js/jquery.parsequery.min.js +2 -0
  25. data/public/js/jquery.tipsy.js +260 -0
  26. data/public/js/onepage.js +2 -3
  27. data/public/js/presenter.js +384 -33
  28. data/public/js/sh_lang/sh_gherkin.js +112 -0
  29. data/public/js/sh_lang/sh_gherkin.min.js +1 -0
  30. data/public/js/sh_lang/sh_ini.js +87 -0
  31. data/public/js/sh_lang/sh_ini.min.js +87 -0
  32. data/public/js/sh_lang/sh_puppet.js +182 -0
  33. data/public/js/sh_lang/sh_puppet.min.js +182 -0
  34. data/public/js/sh_lang/sh_puppet_output.js +22 -0
  35. data/public/js/sh_lang/sh_puppet_output.min.js +22 -0
  36. data/public/js/sh_lang/sh_shell.min.js +1 -0
  37. data/public/js/showoff.js +423 -51
  38. data/views/404.erb +19 -0
  39. data/views/download.erb +36 -0
  40. data/views/header.erb +35 -25
  41. data/views/header_mini.erb +22 -0
  42. data/views/index.erb +46 -1
  43. data/views/onepage.erb +35 -14
  44. data/views/presenter.erb +63 -21
  45. data/views/stats.erb +73 -0
  46. metadata +170 -131
  47. data/public/css/960.css +0 -653
  48. data/public/css/pdf.css +0 -12
data/public/js/showoff.js CHANGED
@@ -16,10 +16,17 @@ var incrCode = false
16
16
  var debugMode = false
17
17
  var gotoSlidenum = 0
18
18
  var shiftKeyActive = false
19
+ var query
20
+ var slideStartTime = new Date().getTime()
21
+
22
+ var questionPrompt = 'Ask a question...'
23
+ var feedbackPrompt = 'Why?...'
19
24
 
20
25
  var loadSlidesBool
21
26
  var loadSlidesPrefix
22
27
 
28
+ var mode = { track: true, follow: false };
29
+
23
30
  function setupPreso(load_slides, prefix) {
24
31
  if (preso_started)
25
32
  {
@@ -28,6 +35,10 @@ function setupPreso(load_slides, prefix) {
28
35
  }
29
36
  preso_started = true
30
37
 
38
+ // save our query string as an object for later use
39
+ query = $.parseQuery();
40
+
41
+ // Load slides fetches images
31
42
  loadSlidesBool = load_slides
32
43
  loadSlidesPrefix = prefix
33
44
  loadSlides(loadSlidesBool, loadSlidesPrefix)
@@ -36,21 +47,62 @@ function setupPreso(load_slides, prefix) {
36
47
 
37
48
  // bind event handlers
38
49
  document.onkeydown = keyDown
39
- document.onkeyup = keyUp
50
+ document.onkeyup = keyUp
40
51
  /* window.onresize = resized; */
41
52
  /* window.onscroll = scrolled; */
42
53
  /* window.onunload = unloaded; */
43
54
 
44
- $('body').addSwipeEvents().
55
+ $('#preso').addSwipeEvents().
45
56
  bind('tap', swipeLeft). // next
46
57
  bind('swipeleft', swipeLeft). // next
47
58
  bind('swiperight', swipeRight); // prev
59
+
60
+ // give us the ability to disable tracking via url parameter
61
+ if(query.track == 'false') mode.track = false;
62
+
63
+ // Make sure the slides always look right.
64
+ // Better would be dynamic calculations, but this is enough for now.
65
+ $(window).resize(function(){location.reload();});
66
+
67
+ $("#feedbackWrapper").hover(
68
+ function() {
69
+ $('#feedbackSidebar').show();
70
+ document.onkeydown = null;
71
+ document.onkeyup = null;
72
+ },
73
+ function() {
74
+ $('#feedbackSidebar').hide();
75
+ document.onkeydown = keyDown;
76
+ document.onkeyup = keyUp;
77
+ }
78
+ );
79
+
80
+ $("#paceSlower").click(function() { sendPace('slower'); });
81
+ $("#paceFaster").click(function() { sendPace('faster'); });
82
+ $("#askQuestion").click(function() { askQuestion( $("textarea#question").val()) });
83
+ $("#sendFeedback").click(function() {
84
+ sendFeedback($( "input:radio[name=rating]:checked" ).val(), $("textarea#feedback").val())
85
+ });
86
+
87
+ $("textarea#question").val(questionPrompt);
88
+ $("textarea#feedback").val(feedbackPrompt);
89
+ $("textarea#question").focus(function() { clearIf($(this), questionPrompt) });
90
+ $("textarea#feedback").focus(function() { clearIf($(this), feedbackPrompt) });
91
+
92
+ // Open up our control socket
93
+ connectControlChannel();
94
+ /*
95
+ ws = new WebSocket('ws://' + location.host + '/control');
96
+ ws.onopen = function() { connected(); };
97
+ ws.onclose = function() { disconnected(); }
98
+ ws.onmessage = function(m) { parseMessage(m.data); };
99
+ */
48
100
  }
49
101
 
50
102
  function loadSlides(load_slides, prefix) {
51
103
  //load slides offscreen, wait for images and then initialize
52
104
  if (load_slides) {
53
- $("#slides").load("slides", false, function(){
105
+ $("#slides").load(loadSlidesPrefix + "slides", false, function(){
54
106
  $("#slides img").batchImageLoad({
55
107
  loadingCompleteCallback: initializePresentation(prefix)
56
108
  })
@@ -64,7 +116,7 @@ function loadSlides(load_slides, prefix) {
64
116
 
65
117
  function initializePresentation(prefix) {
66
118
  // unhide for height to work in static mode
67
- $("#slides").show();
119
+ $("#slides").show();
68
120
 
69
121
  //center slides offscreen
70
122
  centerSlides($('#slides > .slide'))
@@ -83,6 +135,7 @@ function initializePresentation(prefix) {
83
135
  })
84
136
 
85
137
  setupMenu()
138
+ setupStyleMenu()
86
139
  if (slidesLoaded) {
87
140
  showSlide()
88
141
  } else {
@@ -90,7 +143,12 @@ function initializePresentation(prefix) {
90
143
  slidesLoaded = true
91
144
  }
92
145
  setupSlideParamsCheck();
93
- sh_highlightDocument(prefix+'/js/sh_lang/', '.min.js')
146
+
147
+ try {
148
+ sh_highlightDocument('/js/sh_lang/', '.min.js')
149
+ } catch(e) {
150
+ sh_highlightDocument();
151
+ }
94
152
  $("#preso").trigger("showoff:loaded");
95
153
  }
96
154
 
@@ -101,7 +159,7 @@ function centerSlides(slides) {
101
159
  }
102
160
 
103
161
  function centerSlide(slide) {
104
- var slide_content = $(slide).children(".content").first()
162
+ var slide_content = $(slide).find(".content").first()
105
163
  var height = slide_content.height()
106
164
  var mar_top = (0.5 * parseFloat($(slide).height())) - (0.5 * parseFloat(height))
107
165
  if (mar_top < 0) {
@@ -117,7 +175,7 @@ function setupMenu() {
117
175
  var menu = new ListMenu()
118
176
 
119
177
  slides.each(function(s, elem) {
120
- content = $(elem).children(".content")
178
+ content = $(elem).find(".content")
121
179
  shortTxt = $(content).text().substr(0, 20)
122
180
  path = $(content).attr('ref').split('/')
123
181
  currSlide += 1
@@ -156,11 +214,11 @@ function setupSlideParamsCheck() {
156
214
  setTimeout(check, 100);
157
215
  }
158
216
 
159
- function gotoSlide(slideNum) {
160
- slidenum = parseInt(slideNum)
161
- if (!isNaN(slidenum)) {
162
- showSlide()
163
- }
217
+ function gotoSlide(slideNum, updatepv) {
218
+ slidenum = parseInt(slideNum);
219
+ if (!isNaN(slidenum)) {
220
+ showSlide(false, updatepv);
221
+ }
164
222
  }
165
223
 
166
224
  function showFirstSlide() {
@@ -169,7 +227,9 @@ function showFirstSlide() {
169
227
  showSlide()
170
228
  }
171
229
 
172
- function showSlide(back_step) {
230
+ function showSlide(back_step, updatepv) {
231
+ // allows the master presenter view to disable the update callback
232
+ updatepv = (typeof(updatepv) === 'undefined') ? true : updatepv;
173
233
 
174
234
  if(slidenum < 0) {
175
235
  slidenum = 0
@@ -195,6 +255,8 @@ function showSlide(back_step) {
195
255
  if (fullPage) {
196
256
  $('#preso').css({'width' : '100%', 'overflow' : 'visible'});
197
257
  currentSlide.css({'width' : '100%', 'text-align' : 'center', 'overflow' : 'visible'});
258
+ } else {
259
+ $('#preso').css({'width' : '', 'overflow' : ''});
198
260
  }
199
261
 
200
262
  percent = getSlidePercent()
@@ -215,7 +277,24 @@ function showSlide(back_step) {
215
277
  var currentContent = $(currentSlide).find(".content")
216
278
  currentContent.trigger("showoff:show");
217
279
 
218
- return getCurrentNotes()
280
+ var ret = setCurrentNotes();
281
+
282
+ var fileName = currentSlide.children().first().attr('ref');
283
+ $('#slideFilename').text(fileName);
284
+
285
+ // Update presenter view, if we spawned one
286
+ if (updatepv && 'presenterView' in window) {
287
+ var pv = window.presenterView;
288
+ pv.slidenum = slidenum;
289
+ pv.incrCurr = incrCurr
290
+ pv.incrSteps = incrSteps
291
+ pv.showSlide(true);
292
+ pv.postSlide();
293
+
294
+ pv.update();
295
+ }
296
+
297
+ return ret;
219
298
  }
220
299
 
221
300
  function getSlideProgress()
@@ -225,9 +304,21 @@ function getSlideProgress()
225
304
 
226
305
  function getCurrentNotes()
227
306
  {
228
- var notes = currentSlide.find("p.notes").text()
229
- $('#notesInfo').text(notes)
230
- return notes
307
+ var notes = currentSlide.find("div.notes");
308
+ return notes;
309
+ }
310
+
311
+ function getCurrentNotesText()
312
+ {
313
+ var notes = getCurrentNotes();
314
+ return notes.text();
315
+ }
316
+
317
+ function setCurrentNotes()
318
+ {
319
+ var notes = getCurrentNotesText();
320
+ $('#notesInfo').text(notes);
321
+ return notes;
231
322
  }
232
323
 
233
324
  function getSlidePercent()
@@ -252,20 +343,138 @@ function determineIncremental()
252
343
  })
253
344
  }
254
345
 
255
- function prevStep()
346
+ function showIncremental(incr)
256
347
  {
348
+ elem = incrElem.eq(incrCurr)
349
+ if (incrCode && elem.hasClass('command')) {
350
+ incrElem.eq(incrCurr).css('visibility', 'visible').jTypeWriter({duration:1.0})
351
+ } else {
352
+ incrElem.eq(incrCurr).css('visibility', 'visible')
353
+ }
354
+ }
355
+
356
+ function clearIf(elem, val) {
357
+ console.log(elem.val());
358
+ console.log(val);
359
+ if(elem.val() == val ) { elem.val(''); }
360
+ }
361
+
362
+ function connectControlChannel() {
363
+ ws = new WebSocket('ws://' + location.host + '/control');
364
+ ws.onopen = function() { connected(); };
365
+ ws.onclose = function() { disconnected(); }
366
+ ws.onmessage = function(m) { parseMessage(m.data); };
367
+ }
368
+
369
+ // This exists as an intermediary simply so the presenter view can override it
370
+ function reconnectControlChannel() {
371
+ connectControlChannel();
372
+ }
373
+
374
+ function connected() {
375
+ console.log('Control socket opened');
376
+ $("#feedbackSidebar button").attr("disabled", false);
377
+ $("img#disconnected").hide();
257
378
 
379
+ try {
380
+ // If we are a presenter, then remind the server where we are
381
+ update();
382
+ register();
383
+ }
384
+ catch (e) {}
385
+ }
386
+
387
+ function disconnected() {
388
+ console.log('Control socket closed');
389
+ $("#feedbackSidebar button").attr("disabled", true);
390
+ $("img#disconnected").show();
391
+
392
+ setTimeout(function() { reconnectControlChannel() } , 5000);
393
+ }
394
+
395
+ function parseMessage(data) {
396
+ var command = JSON.parse(data);
397
+
398
+ if ("current" in command) { follow(command["current"]); }
399
+
400
+ // Presenter messages only, so catch errors if method doesn't exist
401
+ try {
402
+ if ("pace" in command) { paceFeedback(command["pace"]); }
403
+ if ("question" in command) { askQuestion(command["question"]); }
404
+ }
405
+ catch(e) {
406
+ console.log("Not a presenter!");
407
+ }
408
+
409
+ }
410
+
411
+ function sendPace(pace) {
412
+ ws.send(JSON.stringify({ message: 'pace', pace: pace}));
413
+ feedbackActivity();
414
+ }
415
+
416
+ function askQuestion(question) {
417
+ ws.send(JSON.stringify({ message: 'question', question: question}));
418
+ $("textarea#question").val(questionPrompt);
419
+ feedbackActivity();
420
+ }
421
+
422
+ function sendFeedback(rating, feedback) {
423
+ var slide = $("#slideFilename").text();
424
+ ws.send(JSON.stringify({ message: 'feedback', rating: rating, feedback: feedback, slide: slide}));
425
+ $("textarea#feedback").val(feedbackPrompt);
426
+ $("input:radio[name=rating]:checked").attr('checked', false);
427
+ feedbackActivity();
428
+ }
429
+
430
+ function feedbackActivity() {
431
+ $("img#feedbackActivity").show();
432
+ setTimeout(function() { $("img#feedbackActivity").hide() }, 1000);
433
+ }
434
+
435
+ function track() {
436
+ if (mode.track) {
437
+ var slideName = $("#slideFilename").text();
438
+ var slideEndTime = new Date().getTime();
439
+ var elapsedTime = slideEndTime - slideStartTime;
440
+
441
+ // reset the timer
442
+ slideStartTime = slideEndTime;
443
+
444
+ if (elapsedTime > 1000) {
445
+ elapsedTime /= 1000;
446
+ ws.send(JSON.stringify({ message: 'track', slide: slideName, time: elapsedTime}));
447
+ }
448
+ }
449
+ }
450
+
451
+ function follow(slide) {
452
+ if (mode.follow) {
453
+ console.log("New slide: " + slide);
454
+ gotoSlide(slide);
455
+ }
456
+ }
457
+
458
+ function getPosition() {
459
+ // get the current position from the server
460
+ ws.send(JSON.stringify({ message: 'position' }));
461
+ }
462
+
463
+ function prevStep(updatepv)
464
+ {
258
465
  var event = jQuery.Event("showoff:prev");
259
466
  $(currentSlide).find(".content").trigger(event);
260
467
  if (event.isDefaultPrevented()) {
261
468
  return;
262
469
  }
263
470
 
471
+ track();
472
+
264
473
  slidenum--
265
- return showSlide(true) // We show the slide fully loaded
474
+ return showSlide(true, updatepv) // We show the slide fully loaded
266
475
  }
267
476
 
268
- function nextStep()
477
+ function nextStep(updatepv)
269
478
  {
270
479
  var event = jQuery.Event("showoff:next");
271
480
  $(currentSlide).find(".content").trigger(event);
@@ -273,30 +482,44 @@ function nextStep()
273
482
  return;
274
483
  }
275
484
 
485
+ track();
486
+
276
487
  if (incrCurr >= incrSteps) {
277
488
  slidenum++
278
- return showSlide()
489
+ return showSlide(false, updatepv)
279
490
  } else {
280
- elem = incrElem.eq(incrCurr)
281
- if (incrCode && elem.hasClass('command')) {
282
- incrElem.eq(incrCurr).css('visibility', 'visible').jTypeWriter({duration:1.0})
283
- } else {
284
- incrElem.eq(incrCurr).css('visibility', 'visible')
285
- }
286
- incrCurr++
491
+ showIncremental(incrCurr);
492
+ var incrEvent = jQuery.Event("showoff:incr");
493
+ incrEvent.slidenum = slidenum;
494
+ incrEvent.incr = incrCurr;
495
+ $(currentSlide).find(".content").trigger(incrEvent);
496
+ incrCurr++;
287
497
  }
288
498
  }
289
499
 
290
500
  function doDebugStuff()
291
501
  {
292
502
  if (debugMode) {
293
- $('#debugInfo').show()
294
- debug('debug mode on')
503
+ $('#debugInfo').show();
504
+ $('#slideFilename').show();
295
505
  } else {
296
- $('#debugInfo').hide()
506
+ $('#debugInfo').hide();
507
+ $('#slideFilename').hide();
297
508
  }
298
509
  }
299
510
 
511
+ function blankScreen()
512
+ {
513
+ if ($('#screenblanker').length) { // if #screenblanker exists
514
+ $('#screenblanker').slideUp('normal', function() {
515
+ $('#screenblanker').remove();
516
+ });
517
+ } else {
518
+ $('body').prepend('<div id="screenblanker"></div>');
519
+ $('#screenblanker').slideDown();
520
+ }
521
+ }
522
+
300
523
  var notesMode = false
301
524
  function toggleNotes()
302
525
  {
@@ -309,6 +532,18 @@ function toggleNotes()
309
532
  }
310
533
  }
311
534
 
535
+ function toggleFollow()
536
+ {
537
+ mode.follow = ! mode.follow;
538
+
539
+ if(mode.follow) {
540
+ $("#followMode").show().text('Follow Mode:');
541
+ getPosition();
542
+ } else {
543
+ $("#followMode").hide();
544
+ }
545
+ }
546
+
312
547
  function executeAnyCode()
313
548
  {
314
549
  var $jsCode = $('.execute .sh_javascript code:visible')
@@ -322,7 +557,7 @@ function executeAnyCode()
322
557
  var $coffeeCode = $('.execute .sh_coffeescript code:visible')
323
558
  if ($coffeeCode.length > 0) {
324
559
  executeCoffee.call($coffeeCode);
325
- }
560
+ }
326
561
  }
327
562
 
328
563
  function debug(data)
@@ -396,14 +631,30 @@ function keyDown(event)
396
631
  {
397
632
  $('#navmenu').toggle().trigger('click')
398
633
  }
634
+ else if (key == 83) // 's' for style
635
+ {
636
+ $('#stylemenu').toggle().trigger('click')
637
+ }
399
638
  else if (key == 90 || key == 191) // z or ? for help
400
639
  {
401
640
  $('#help').toggle()
402
641
  }
403
- else if (key == 66 || key == 70) // f for footer (also "b" which is what kensington remote "stop" button sends
642
+ else if (key == 66) // b for blank, also what kensington remote "stop" button sends
643
+ {
644
+ blankScreen()
645
+ }
646
+ else if (key == 70) // f for footer
404
647
  {
405
648
  toggleFooter()
406
649
  }
650
+ else if (key == 71) // g for follow mode
651
+ {
652
+ toggleFollow()
653
+ }
654
+ else if (key == 76) // l for leader mode
655
+ {
656
+ toggleLeader()
657
+ }
407
658
  else if (key == 78) // 'n' for notes
408
659
  {
409
660
  toggleNotes()
@@ -412,9 +663,14 @@ function keyDown(event)
412
663
  {
413
664
  removeResults();
414
665
  }
415
- else if (key == 80) // 'p' for preshow
666
+ else if (key == 80) // 'p' for preshow, 'P' for pause
416
667
  {
417
- togglePreShow();
668
+ if (shiftKeyActive) {
669
+ togglePause();
670
+ }
671
+ else {
672
+ togglePreShow();
673
+ }
418
674
  }
419
675
  return true
420
676
  }
@@ -548,22 +804,24 @@ function togglePreShow() {
548
804
  stopPreShow()
549
805
  } else {
550
806
  var minutes = prompt("Minutes from now to start")
551
- preshow_secondsLeft = parseFloat(minutes) * 60
552
- toggleFooter()
553
- $.getJSON("preshow_files", false, function(data) {
554
- $('#preso').after("<div id='preshow'></div><div id='tips'></div><div id='preshow_timer'></div>")
555
- $.each(data, function(i, n) {
556
- if(n == "preshow.json") {
557
- // has a descriptions file
558
- $.getJSON("/file/_preshow/preshow.json", false, function(data) {
559
- preshow_des = data
560
- })
561
- } else {
562
- $('#preshow').append('<img ref="' + n + '" src="/file/_preshow/' + n + '"/>')
563
- }
807
+
808
+ if (preshow_secondsLeft = parseFloat(minutes) * 60) {
809
+ toggleFooter()
810
+ $.getJSON("preshow_files", false, function(data) {
811
+ $('#preso').after("<div id='preshow'></div><div id='tips'></div><div id='preshow_timer'></div>")
812
+ $.each(data, function(i, n) {
813
+ if(n == "preshow.json") {
814
+ // has a descriptions file
815
+ $.getJSON("/file/_preshow/preshow.json", false, function(data) {
816
+ preshow_des = data
817
+ })
818
+ } else {
819
+ $('#preshow').append('<img ref="' + n + '" src="/file/_preshow/' + n + '"/>')
820
+ }
821
+ })
822
+ startPreShow()
564
823
  })
565
- startPreShow()
566
- })
824
+ }
567
825
  }
568
826
  }
569
827
 
@@ -596,7 +854,7 @@ function startPreShow() {
596
854
 
597
855
  function addPreShowTips() {
598
856
  time = secondsToTime(preshow_secondsLeft)
599
- $('#preshow_timer').text(time + ' to go-time')
857
+ $('#preshow_timer').text('Resuming in: ' + time)
600
858
  var des = preshow_des && preshow_des[tmpImg.attr("ref")]
601
859
  if(des) {
602
860
  $('#tips').show()
@@ -641,3 +899,117 @@ function nextPreShowImage() {
641
899
  /********************
642
900
  End PreShow Code
643
901
  ********************/
902
+
903
+ function togglePause() {
904
+ $("#pauseScreen").toggle();
905
+ }
906
+
907
+ /********************
908
+ Style-Picker Code
909
+ ********************/
910
+
911
+ function styleChoiceTags() {
912
+ return $('link[rel*="stylesheet"][href*="file/"]');
913
+ }
914
+
915
+ function styleChoices() {
916
+ return $.map(styleChoiceTags(), function(el) { return styleChoiceString(el.href); });
917
+ }
918
+
919
+ function styleChoiceString(href) {
920
+ var parts = href.split('/');
921
+ var file = parts[parts.length - 1];
922
+ var choice = file.replace(/\.css$/, '');
923
+
924
+ return choice;
925
+ }
926
+
927
+ function getCurrentStyle()
928
+ {
929
+ var current = '';
930
+
931
+ styleChoiceTags().each(function (i, el) {
932
+ if (el.rel == 'stylesheet') {
933
+ current = el.href;
934
+ }
935
+ });
936
+
937
+ return styleChoiceString(current);
938
+ }
939
+
940
+ function setCurrentStyle(style, prop)
941
+ {
942
+ styleChoiceTags().each(function (i, el) {
943
+ el.rel = 'alternate stylesheet';
944
+
945
+ if (styleChoiceString(el.href) == style) {
946
+ el.rel = 'stylesheet';
947
+ }
948
+ });
949
+
950
+ if (prop) {
951
+ if ('presenterView' in window) {
952
+ var pv = window.presenterView;
953
+ pv.setCurrentStyle(style, false);
954
+ }
955
+ }
956
+ }
957
+
958
+ function setupStyleMenu() {
959
+ $('#stylemenu').hide();
960
+
961
+ var menu = new StyleListMenu();
962
+ styleChoices().each(function(s) {
963
+ menu.addItem(s)
964
+ })
965
+
966
+ $('#stylepicker').html(menu.getList())
967
+ $('#stylemenu').menu({
968
+ content: $('#stylepicker').html(),
969
+ flyOut: true
970
+ });
971
+ }
972
+
973
+ function StyleListMenu()
974
+ {
975
+ this.typeName = 'StyleListMenu'
976
+ this.items = new Array();
977
+ this.addItem = function (key) {
978
+ this.items[key] = new StyleListMenuItem(key)
979
+ }
980
+ this.getList = function() {
981
+ var newMenu = $("<ul>")
982
+ for(var i in this.items) {
983
+ var item = this.items[i]
984
+ var domItem = $("<li>")
985
+ if (item.textName != undefined) {
986
+ choice = $("<a onclick=\"setCurrentStyle('" + item.textName + "', true); $('#stylemenu').hide();\" href=\"#\">" + item.textName + "</a>")
987
+ domItem.append(choice)
988
+ newMenu.append(domItem)
989
+ }
990
+ }
991
+ return newMenu
992
+ }
993
+ }
994
+
995
+ function StyleListMenuItem(t)
996
+ {
997
+ this.typeName = "StyleListMenuItem"
998
+ this.textName = t
999
+ }
1000
+ /********************
1001
+ End Style-Picker Code
1002
+ ********************/
1003
+
1004
+
1005
+ /********************
1006
+ Stats page
1007
+ ********************/
1008
+
1009
+ function setupStats()
1010
+ {
1011
+ $("#stats div#all div.detail").hide();
1012
+ $("#stats div#all div.row").click(function() {
1013
+ $(this).find("div.detail").slideToggle("fast");
1014
+ });
1015
+ }
data/views/404.erb ADDED
@@ -0,0 +1,19 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3
+
4
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
5
+ <head>
6
+ <%= erb :header %>
7
+ </head>
8
+
9
+ <body id="download">
10
+
11
+ <div id="preso">
12
+ <h1>File Not Found!</h1>
13
+ <h2><%= @env['REQUEST_PATH'] %>
14
+ </div>
15
+ <div id="footer">
16
+ <span id="debugInfo"></span>
17
+ </div>
18
+ </body>
19
+ </html>