songbooks 0.1.0

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 (87) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/Gemfile +4 -0
  6. data/Guardfile +29 -0
  7. data/LICENSE +22 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +41 -0
  10. data/Rakefile +6 -0
  11. data/app/assets/javascripts/application.coffee +4 -0
  12. data/app/assets/javascripts/components/filters/convertWhitespace.filter.coffee +3 -0
  13. data/app/assets/javascripts/components/song_component/chord.directive.coffee +9 -0
  14. data/app/assets/javascripts/components/song_component/literal.directive.coffee +6 -0
  15. data/app/assets/javascripts/components/song_component/song_component.directive.coffee +6 -0
  16. data/app/assets/javascripts/components/songbook/songbook.controller.coffee +29 -0
  17. data/app/assets/javascripts/components/songs/song.controller.coffee +10 -0
  18. data/app/assets/javascripts/components/songs/song.directive.coffee +7 -0
  19. data/app/assets/javascripts/components/songs/song.service.coffee +98 -0
  20. data/app/assets/javascripts/components/songs/songs.controller.coffee +21 -0
  21. data/app/assets/javascripts/routes.coffee +11 -0
  22. data/app/assets/stylesheets/application.scss +34 -0
  23. data/app/fonts/DejaVuSans-Bold.ttf +0 -0
  24. data/app/fonts/DejaVuSans-BoldOblique.ttf +0 -0
  25. data/app/fonts/DejaVuSans-ExtraLight.ttf +0 -0
  26. data/app/fonts/DejaVuSans-Oblique.ttf +0 -0
  27. data/app/fonts/DejaVuSans.ttf +0 -0
  28. data/app/fonts/DejaVuSansCondensed-Bold.ttf +0 -0
  29. data/app/fonts/DejaVuSansCondensed-BoldOblique.ttf +0 -0
  30. data/app/fonts/DejaVuSansCondensed-Oblique.ttf +0 -0
  31. data/app/fonts/DejaVuSansCondensed.ttf +0 -0
  32. data/app/fonts/DejaVuSansMono-Bold.ttf +0 -0
  33. data/app/fonts/DejaVuSansMono-BoldOblique.ttf +0 -0
  34. data/app/fonts/DejaVuSansMono-Oblique.ttf +0 -0
  35. data/app/fonts/DejaVuSansMono.ttf +0 -0
  36. data/app/fonts/DejaVuSerif-Bold.ttf +0 -0
  37. data/app/fonts/DejaVuSerif-BoldItalic.ttf +0 -0
  38. data/app/fonts/DejaVuSerif-Italic.ttf +0 -0
  39. data/app/fonts/DejaVuSerif.ttf +0 -0
  40. data/app/fonts/DejaVuSerifCondensed-Bold.ttf +0 -0
  41. data/app/fonts/DejaVuSerifCondensed-BoldItalic.ttf +0 -0
  42. data/app/fonts/DejaVuSerifCondensed-Italic.ttf +0 -0
  43. data/app/fonts/DejaVuSerifCondensed.ttf +0 -0
  44. data/app/tex/songbook.tex +12 -0
  45. data/app/views/application.haml +54 -0
  46. data/app/views/layouts/application.haml +40 -0
  47. data/app/views/songbook.prawn +80 -0
  48. data/app/views/songs/component.haml +2 -0
  49. data/app/views/songs/index.jbuilder +5 -0
  50. data/app/views/songs/show.haml +23 -0
  51. data/app/views/songs/show.jbuilder +24 -0
  52. data/app/views/songs/song.haml +4 -0
  53. data/exe/sb_server +6 -0
  54. data/lib/prawn_ext/font/afm.rb +20 -0
  55. data/lib/songbooks.rb +32 -0
  56. data/lib/songbooks/CLI.rb +13 -0
  57. data/lib/songbooks/components/chord.rb +98 -0
  58. data/lib/songbooks/components/literal.rb +57 -0
  59. data/lib/songbooks/components/metadata.rb +69 -0
  60. data/lib/songbooks/components/section.rb +73 -0
  61. data/lib/songbooks/controllers/controller.rb +18 -0
  62. data/lib/songbooks/controllers/songs_controller.rb +30 -0
  63. data/lib/songbooks/folder.rb +32 -0
  64. data/lib/songbooks/server.rb +65 -0
  65. data/lib/songbooks/song.rb +53 -0
  66. data/lib/songbooks/version.rb +3 -0
  67. data/lib/songbooks/view_helper.rb +9 -0
  68. data/public/javascripts/angular-local-storage.js +449 -0
  69. data/public/javascripts/angular-route.js +991 -0
  70. data/public/javascripts/angular.js +28904 -0
  71. data/public/javascripts/application.js +8 -0
  72. data/public/javascripts/components/filters/convertWhitespace.filter.js +8 -0
  73. data/public/javascripts/components/song_component/chord.directive.js +13 -0
  74. data/public/javascripts/components/song_component/literal.directive.js +9 -0
  75. data/public/javascripts/components/song_component/song_component.directive.js +9 -0
  76. data/public/javascripts/components/songbook/songbook.controller.js +33 -0
  77. data/public/javascripts/components/songs/song.controller.js +12 -0
  78. data/public/javascripts/components/songs/song.directive.js +9 -0
  79. data/public/javascripts/components/songs/song.service.js +107 -0
  80. data/public/javascripts/components/songs/songs.controller.js +22 -0
  81. data/public/javascripts/jquery-2.1.4.min.js +4 -0
  82. data/public/javascripts/routes.js +16 -0
  83. data/public/stylesheets/application.css +24 -0
  84. data/public/stylesheets/bootstrap-theme.min.css +5 -0
  85. data/public/stylesheets/bootstrap.min.css +5 -0
  86. data/songbooks.gemspec +51 -0
  87. metadata +369 -0
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,12 @@
1
+ \documentclass[10pt,a4paper,twoside]{article}
2
+ \usepackage[latin1]{inputenc}
3
+ \usepackage{amsmath}
4
+ \usepackage{amsfonts}
5
+ \usepackage{amssymb}
6
+ \usepackage{graphicx}
7
+ \begin{document}
8
+ \begin{tabular}{|r|l|}
9
+ asdf & fdsa \\
10
+ ss & gg \\
11
+ \end{tabular}
12
+ \end{document}
@@ -0,0 +1,54 @@
1
+ !!! 5
2
+ %html{'ng-app' => 'songbooks', 'ng-controller' => 'SongsController as songsCtrl'}
3
+ %head
4
+ %link{:href => '/stylesheets/bootstrap.min.css', :rel => 'stylesheet'}/
5
+ %link{:href => '/stylesheets/bootstrap-theme.min.css', :rel => 'stylesheet'}/
6
+ %link{:href => '/stylesheets/application.css', :rel => 'stylesheet'}/
7
+
8
+ - %w(jquery-2.1.4.min angular angular-route angular-local-storage application routes).each do |file|
9
+ %script{:src => "/javascripts/#{file}.js", :type => 'text/javascript'}
10
+ - Songbooks::ViewHelper.javascripts.each do |file|
11
+ %script{:src => "/javascripts/#{file}", :type => 'text/javascript'}
12
+
13
+ %title
14
+ Songbooks
15
+
16
+ %body{:ng => {:controller => 'SongbookController as songbook'}}
17
+ .container-fluid
18
+ .row
19
+ .col-sm-3.hidden-print
20
+ .panel.panel-default
21
+ .panel-heading
22
+ %h3.panel-title
23
+ Available Songs
24
+ %a.btn.btn-default.btn-sm.pull-right{:ng => {:click => 'songsCtrl.addAllToBook()'}} +
25
+ %br/
26
+ %small= Songbooks.folder.path
27
+ .list-group#songs
28
+ .list-group-item
29
+ %input.form-control{:type => 'text', :ng => {:model => 'songsCtrl.filterQuery'}}
30
+ .list-group-item{:ng => {:click => 'songsCtrl.setActive(song)',
31
+ :repeat => 'song in songsCtrl.songs() | filter: songsCtrl.filterQuery',
32
+ :class => "{ 'active': song.active }"},
33
+ :style => 'cursor: default'}
34
+ %span.pull-right
35
+ %a.btn.btn-sm.btn-default{:ng => {:click => 'songbook.addSong(song)'}} +
36
+ %song
37
+ .col-sm-6
38
+ .panel.panel-default
39
+ .panel-body#main-content{:ng => {:view => true}}
40
+ .col-sm-3.hidden-print
41
+ .panel.panel-default
42
+ .panel-heading
43
+ %h3.panel-title
44
+ {{ songbook.title() }}
45
+ %a.btn.btn-default.btn-sm.pull-right{:ng => {:click => 'songbook.sendToServer()'}} PDF
46
+ %a.btn.btn-default.btn-sm.pull-right{:ng => {:click => 'songbook.clear()'}} Clear
47
+ .clearfix
48
+ .list-group#songs
49
+ .list-group-item
50
+ %input.form-control{:type => 'text', :ng => {:model => 'songbook.filterQuery'}}
51
+ .list-group-item{:ng => {:repeat => 'song in songbook.songs() | filter: songbook.filterQuery'}}
52
+ %span.pull-right
53
+ %a.btn.btn-sm.btn-default{:ng => {:click => 'songbook.removeSong(song)'}} -
54
+ %song
@@ -0,0 +1,40 @@
1
+ !!! 5
2
+
3
+ %html
4
+ %head
5
+ %link{:href => '/stylesheets/bootstrap.min.css', :rel => 'stylesheet'}/
6
+ %link{:href => '/stylesheets/bootstrap-theme.min.css', :rel => 'stylesheet'}/
7
+ %link{:href => '/stylesheets/application.css', :rel => 'stylesheet'}/
8
+ %script{:src => '/javascripts/jquery-2.1.4.min.js', :type => 'text/javascript'}
9
+ %script{:src => '/javascripts/angular.min.js', :type => 'text/javascript'}
10
+
11
+ %body
12
+ .container-fluid
13
+ .row
14
+ .col-sm-3
15
+ .panel.panel-default
16
+ .panel-heading
17
+ %h3.panel-title
18
+ Verfügbare Lieder
19
+ %br/
20
+ %small= Songbooks.chords_directory.path
21
+ .list-group#file-list
22
+ - Songbooks.chords_directory.song_list.songs.each do |song|
23
+ %a.list-group-item{:href => "/songs/#{song.identifier}", :data => {:remote => true}}
24
+ = song.title
25
+ = ' - '
26
+ %i= song.artist
27
+ .col-sm-6
28
+ .panel.panel-default
29
+ .panel-heading
30
+ %h3.panel-title Inhalt
31
+ .panel-body#main-content
32
+ = yield
33
+ .col-sm-3
34
+ .panel.panel-default
35
+ .panel-heading
36
+ %h3.panel-title
37
+ Ausgewählte Lieder
38
+ .panel-body
39
+ ll
40
+
@@ -0,0 +1,80 @@
1
+ #----------------------------------------------------------------
2
+ # Fonts
3
+ #----------------------------------------------------------------
4
+
5
+ pdf.font_families.update('DejaVuSansMono' => {
6
+ :normal => Songbooks.font_file('DejaVuSansMono'),
7
+ :italic => Songbooks.font_file('DejaVuSansMono-Oblique'),
8
+ :bold => Songbooks.font_file('DejaVuSansMono-Bold'),
9
+ :bold_italic => Songbooks.font_file('DejaVuSansMono-BoldOblique')
10
+ })
11
+
12
+ @toc = {}
13
+
14
+ pdf.start_new_page
15
+
16
+ @songs.each do |song|
17
+ pdf.text song.title, :size => 18
18
+ pdf.text song.artist
19
+
20
+ @toc["#{song.title} - #{song.artist}"] = pdf.page_count
21
+
22
+ song_y = pdf.cursor
23
+ text_x = pdf.width_of(song.longest_section_caption) + 10
24
+
25
+ pdf.font 'DejaVuSansMono', :size => 9 do
26
+
27
+ song.sections.each do |section|
28
+ pdf.draw_text section.name, :at => [text_x - pdf.width_of(section.name) - 10, song_y], :style => :bold
29
+
30
+ section.lines.each do |line|
31
+ line_x = text_x
32
+
33
+ line.each do |component|
34
+ case component
35
+ when Songbooks::Components::Literal
36
+ pdf.draw_text component.to_s, :at => [line_x, song_y]
37
+ when Songbooks::Components::Chord
38
+ pdf.fill_color '8B0000'
39
+ pdf.draw_text component.to_s, :at => [line_x, song_y]
40
+ pdf.fill_color '000000'
41
+ end
42
+
43
+ line_x += pdf.width_of(component.to_s)
44
+ end
45
+ song_y -= 12
46
+
47
+ if song_y < 10
48
+ pdf.start_new_page
49
+ song_y = 700
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ pdf.start_new_page unless song == @songs.last
56
+ end
57
+
58
+ pdf.go_to_page 1
59
+
60
+ #----------------------------------------------------------------
61
+ # Table of Contents
62
+ #----------------------------------------------------------------
63
+
64
+ pdf.font 'DejaVuSansMono', :size => 11 do
65
+ @toc.keys.sort.each do |k|
66
+ min_width = pdf.width_of("#{k} #{@toc[k]}")
67
+ dots = ''
68
+ dots << '.' while pdf.width_of(dots) < (530 - min_width)
69
+
70
+ pdf.text "#{k} #{dots} #{@toc[k]}"
71
+ end
72
+ end
73
+
74
+ #----------------------------------------------------------------
75
+ # Page Numbers
76
+ #----------------------------------------------------------------
77
+
78
+ pdf.number_pages '<page>',
79
+ :page_filter => lambda{ |pg| pg != 1 },
80
+ :at => [pdf.bounds.right - 50, 0]
@@ -0,0 +1,2 @@
1
+ %chord{:ng => {:if => 'component.component_type == "chord"'}}<>
2
+ %literal{:ng => {:if => 'component.component_type == "literal"'}}<>
@@ -0,0 +1,5 @@
1
+ json.data do
2
+ json.array!(@songs) do |song|
3
+ json.(song, :identifier, :title, :artist)
4
+ end
5
+ end
@@ -0,0 +1,23 @@
1
+ .pull-right
2
+ %a{:ng => {:click => 'songbook.addSong(songCtrl.song())'}} Add
3
+
4
+ %h3
5
+ %small
6
+ {{ songCtrl.song().artist }}
7
+ %br/
8
+ {{ songCtrl.song().title }}
9
+
10
+ %p
11
+ %span{ng: {repeat: 'i in [-6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6]'}}
12
+ %input{type: 'radio', name: 'transposed_by', ng: {model: 'songCtrl.song().transposedBy'}} {{ i }}
13
+
14
+
15
+ %table
16
+ %tbody
17
+ %tr{:ng => {:repeat => 'section in songCtrl.song().sections'}}
18
+ %td.text-right{:style => 'vertical-align: top; padding: 10px; padding-top: 0'}
19
+ %strong
20
+ {{ section.name }}
21
+ %td
22
+ %div{:ng => {:repeat => 'line in section.lines'}}
23
+ %song-component{:ng => {:repeat => 'component in line'}}
@@ -0,0 +1,24 @@
1
+ json.data do
2
+ json.(@song, :identifier, :title, :artist)
3
+
4
+ json.sections @song.sections do |section|
5
+ json.(section, :name)
6
+
7
+ json.lines section.lines do |line|
8
+ json.array! line do |component|
9
+ case component
10
+ when Songbooks::Components::Literal
11
+ json.component_type 'literal'
12
+ json.content component.to_s
13
+ when Songbooks::Components::Chord
14
+ json.component_type 'chord'
15
+ json.name component.name
16
+ json.transposed_by component.transposed_by.to_s
17
+ else
18
+ json.type 'unknown'
19
+ json.content 'unknown component: '
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,4 @@
1
+ %small
2
+ {{ song.artist }}
3
+ %br/
4
+ {{ song.title }}
data/exe/sb_server ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'songbooks'
5
+ require 'songbooks/CLI'
6
+ Songbooks::CLI.start
@@ -0,0 +1,20 @@
1
+ # class Prawn::Font::AFM
2
+ # # Patch normalize_encoding method to handle conversion more gracefully.
3
+ # #
4
+ # # Any valid utf-8 characters that cannot be encoded to windows-1252 are
5
+ # # replaced with the logic "not" symbol and a warning is issued identifying
6
+ # # the text that cannot be converted.
7
+ # def normalize_encoding(text)
8
+ # text.encode('utf-8')
9
+ # #text.encode 'windows-1252'
10
+ # rescue ::Encoding::UndefinedConversionError
11
+ # warn 'The following text could not be fully converted to the Windows-1252 character set:'
12
+ # warn %(#{text.gsub(/^/, '| ').rstrip})
13
+ # warn ''
14
+ # text.encode 'windows-1252', undef: :replace, invalid: :replace, replace: 'x'
15
+ # rescue ::Encoding::InvalidByteSequenceError
16
+ # raise Prawn::Errors::IncompatibleStringEncoding,
17
+ # %(Your document includes text that's not compatible with the Windows-1252 character set.
18
+ # If you need full UTF-8 support, use TTF fonts instead of PDF's built-in (AFM) fonts\n.)
19
+ # end
20
+ # end
data/lib/songbooks.rb ADDED
@@ -0,0 +1,32 @@
1
+ module Songbooks
2
+ # Gem requires
3
+ require 'haml'
4
+ require 'sinatra'
5
+ require 'active_support/all'
6
+ require 'prawn'
7
+ require 'sinatra/prawn'
8
+ require 'sinatra/jbuilder'
9
+ require 'json'
10
+
11
+ # Ruby requires
12
+ require 'pathname'
13
+
14
+ # Internal requires
15
+ require 'songbooks/folder'
16
+
17
+ def self.root
18
+ Pathname.new(File.dirname(__dir__))
19
+ end
20
+
21
+ def self.font_file(name)
22
+ root.join('app', 'fonts', "#{name}.ttf").to_s
23
+ end
24
+
25
+ def self.initialize_folder(directory_path)
26
+ @folder = Songbooks::Folder.new(directory_path)
27
+ end
28
+
29
+ def self.folder
30
+ @folder
31
+ end
32
+ end
@@ -0,0 +1,13 @@
1
+ require 'thor'
2
+ require 'songbooks/server'
3
+
4
+ module Songbooks
5
+ class CLI < Thor
6
+ desc :start, 'Starts a songbook server'
7
+ def start(directory = '.')
8
+ Songbooks.initialize_folder(File.expand_path(directory))
9
+ puts Songbooks.folder.inspect
10
+ Songbooks::Server.run!
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,98 @@
1
+ module Songbooks
2
+ module Components
3
+ class Chord
4
+
5
+ #----------------------------------------------------------------
6
+ # CONSTANTS
7
+ #----------------------------------------------------------------
8
+
9
+ NOTES = %w(C Db D Eb E F Gb G Ab A Bb B)
10
+
11
+ NOTE_MAPPING = [
12
+ %w(C# Db),
13
+ %w(D# Eb),
14
+ %w(E# F),
15
+ %w(Fb, E),
16
+ %w(F# Gb),
17
+ %w(G# Ab),
18
+ %w(A# Bb),
19
+ %w(B#, C)
20
+ ].each_with_object({}) do |(n1, n2), h|
21
+ h[n1] = n2
22
+ h[n2] = n1
23
+ end
24
+
25
+ CHORD_REGEXP = /[A-G](?:b|#)?(?:(?:m(?:aj)?|M|aug|dim|sus)?(?:[2-7]|9|13)?)?(?:\/[A-G](?:b|#)?)?/
26
+ NOTE_REGEXP = /\A[A-G](b|#)*/
27
+
28
+ # A chord in a line has to appear either right before a space or punctuation symbol
29
+ # or be the only thing left in a string
30
+ CHORD_OCCURRENCE_REGEXP = /\A(#{CHORD_REGEXP})(?:\s+|[\.,\|])|\A(#{CHORD_REGEXP})\z/
31
+
32
+ #----------------------------------------------------------------
33
+ # Parsing
34
+ #----------------------------------------------------------------
35
+
36
+ #
37
+ # Tries to parse the beginning of the given text as a chord
38
+ #
39
+ # If the text starts with a chord, this part of the string is automatically deleted.
40
+ #
41
+ # @return [Chord, NilClass] a chord object if the given text started with a valid chord
42
+ # or +nil+ otherwise.
43
+ #
44
+ def self.munch_chord(text)
45
+ if (match = text.match(CHORD_OCCURRENCE_REGEXP))
46
+ chord_name = match[1..-1].compact.first
47
+ text[0, chord_name.length] = ''
48
+ return Chord.new(chord_name)
49
+ end
50
+ nil
51
+ end
52
+
53
+ def self.starts_with_chord?(text)
54
+ CHORD_OCCURRENCE_REGEXP === text
55
+ end
56
+
57
+ def initialize(chord_name, transposed_by: 0)
58
+ @chord_name = chord_name
59
+ @transposed_by = transposed_by
60
+ end
61
+
62
+ attr_reader :transposed_by
63
+
64
+ def name
65
+ @chord_name
66
+ end
67
+
68
+ def base_note
69
+ name.match(NOTE_REGEXP)[0]
70
+ end
71
+
72
+ def transpose!(count)
73
+ note = base_note
74
+ note = NOTE_MAPPING[base_note] unless NOTES.include?(base_note)
75
+ index = NOTES.index(note)
76
+ transposed_note = NOTES[(index + number) % NOTES.size]
77
+ @transposed_by = count
78
+ @chord_name.sub!(note, transposed_note)
79
+ end
80
+
81
+ def transpose(count)
82
+ dup.transpose!(count)
83
+ end
84
+
85
+ def ==(other_chord)
86
+ name == other_chord.name
87
+ end
88
+
89
+ def to_s
90
+ name
91
+ end
92
+
93
+ def to_str
94
+ name
95
+ end
96
+ end
97
+ end
98
+ end