showoff 0.7.0 → 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
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>