├── .gitignore ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── docs ├── index.html ├── samples │ ├── bach_bwv772.xml │ ├── bach_bwv846p.xml │ ├── beethoven_moonlight_m1.xml │ ├── brahms_quartet1_m2.xml │ ├── chant.xml │ ├── chopin_minute_waltz.xml │ ├── chopin_prelude_4.xml │ ├── joplin_maple_leaf_rag.xml │ └── mozart_quartet3_m1.xml ├── sandbox.html ├── tutorial.html └── viewer_demo.html ├── hack ├── Reunion.mxl ├── Reunion.xml ├── Reunion.zip ├── build.sh ├── index.html ├── lib │ ├── deflate.js │ ├── inflate.js │ └── zip.js ├── vexflow-debug.js └── vexflow-min.js ├── jshintrc ├── push.sh ├── push_demo.sh ├── src ├── accidental.js ├── annotation.js ├── articulation.js ├── barnote.js ├── beam.js ├── bend.js ├── boundingbox.js ├── canvascontext.js ├── clef.js ├── clefnote.js ├── crescendo.js ├── curve.js ├── document.js ├── documentformatter.js ├── dot.js ├── flow.js ├── fonts │ ├── gonville.js │ ├── gonville_all.js │ ├── gonville_original.js │ └── vexflow_font.js ├── formatter.js ├── fraction.js ├── frethandfinger.js ├── ghostnote.js ├── glyph.js ├── glyphs.html ├── gracenote.js ├── gracenotegroup.js ├── header.js ├── keymanager.js ├── keysignature.js ├── measure.js ├── modifier.js ├── modifiercontext.js ├── music.js ├── musicxml.js ├── note.js ├── notehead.js ├── raphaelcontext.js ├── renderer.js ├── stave.js ├── stavebarline.js ├── staveconnector.js ├── stavehairpin.js ├── staveline.js ├── stavemodifier.js ├── stavenote.js ├── staverepetition.js ├── stavesection.js ├── stavetempo.js ├── stavetext.js ├── stavetie.js ├── stavevolta.js ├── stem.js ├── stemmablenote.js ├── stringnumber.js ├── strokes.js ├── tables.js ├── tabnote.js ├── tabslide.js ├── tabstave.js ├── tabtie.js ├── textnote.js ├── tickable.js ├── tickcontext.js ├── timesignature.js ├── timesignote.js ├── transform.html ├── tremolo.js ├── tuning.js ├── tuplet.js ├── vex.js ├── vibrato.js ├── voice.js └── voicegroup.js ├── tests ├── accidental_tests.js ├── annotation_tests.js ├── articulation_tests.js ├── auto_beam_formatting_tests.js ├── beam_tests.js ├── bend_tests.js ├── boundingbox_tests.js ├── clef_tests.js ├── curve_tests.js ├── document_tests.js ├── dot_tests.js ├── flow.html ├── formatter_tests.js ├── gracenote_tests.js ├── key_clef_tests.js ├── keymanager_tests.js ├── keysignature_tests.js ├── mocks.js ├── modifier_tests.js ├── music_tests.js ├── notehead_tests.js ├── percussion_tests.js ├── rests_tests.js ├── rhythm_tests.js ├── stave_tests.js ├── staveconnector_tests.js ├── stavehairpin_tests.js ├── staveline_tests.js ├── stavemodifier_tests.js ├── stavenote_tests.js ├── stavetie_tests.js ├── stringnumber_tests.js ├── strokes_tests.js ├── support │ ├── jquery.js │ ├── qunit.css │ ├── qunit.js │ └── raphael.js ├── table_tests.js ├── tabnote_tests.js ├── tabslide_tests.js ├── tabstave_tests.js ├── tabtie_tests.js ├── textnote_tests.js ├── threevoice_tests.js ├── tickcontext_tests.js ├── timesignature_tests.js ├── tuning_tests.js ├── tuplet_tests.js ├── vexflow_test_helpers.js ├── vibrato_tests.js └── voice_tests.js └── tools ├── find_inconsistent_style.rb └── find_missing_includes.rb /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.lock 3 | ruby_sess.* 4 | .*.swp 5 | .*.swo 6 | .swp 7 | .hi 8 | .[oa] 9 | *.pyc 10 | .svn 11 | .class 12 | # except foo.o which is hand maintained 13 | !foo.o 14 | .DS_Store 15 | .idea/VexFlow.iml 16 | .idea/encodings.xml 17 | .idea/misc.xml 18 | .idea/modules.xml 19 | .idea/scopes/scope_settings.xml 20 | .idea/vcs.xml 21 | .idea/workspace.xml 22 | node_modules 23 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | gem "rake" 3 | gem "therubyracer" 4 | gem "uglifier" 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Vex Flow - A JavaScript library for rendering music notation. 2 | Copyright (c) 2010 Mohit Muthanna Cheppudira 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VexFlow 2 2 | 3 | A JavaScript library for rendering music notation. 4 | Copyright (c) 2010 Mohit Muthanna Cheppudira 5 | 6 | ## What is VexFlow? 7 | 8 | VexFlow is an open-source web-based music notation rendering API. It is written 9 | completely in JavaScript, and runs right in the browser. VexFlow supports HTML5 10 | Canvas and SVG, and runs on all modern browsers. 11 | 12 | Go try out [The VexFlow Tutorial](http://vexflow.com/docs/tutorial.html) to 13 | learn how to use VexFlow. 14 | 15 | If you're not a developer and just want to write and share your music, go to 16 | [My VexFlow](http://my.vexflow.com). 17 | 18 | ## Resources 19 | 20 | To learn and contribute, check out the [VexFlow Wiki](https://github.com/0xfe/vexflow/wiki). 21 | 22 | To build VexFlow from scratch, read the [Build Instructions](https://github.com/0xfe/vexflow/wiki/Build-Instructions). 23 | 24 | ## MIT License 25 | 26 | Copyright (c) Mohit Muthanna Cheppudira 2010
27 | 0xFE http://www.vexflow.com 28 | 29 | Permission is hereby granted, free of charge, to any person obtaining a copy 30 | of this software and associated documentation files (the "Software"), to deal 31 | in the Software without restriction, including without limitation the rights 32 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 33 | copies of the Software, and to permit persons to whom the Software is 34 | furnished to do so, subject to the following conditions: 35 | 36 | The above copyright notice and this permission notice shall be included in 37 | all copies or substantial portions of the Software. 38 | 39 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 40 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 41 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 42 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 43 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 44 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 45 | THE SOFTWARE. 46 | 47 | ## Links 48 | 49 | * [VexFlow Home](http://vexflow.com) 50 | * [My VexFlow](http://my.vexflow.com) 51 | * [Me](http://0xfe.muthanna.com) -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Rakefile for VexFlow 2 | # Copyright 2013 Mohit Cheppudira 3 | 4 | require 'bundler/setup' 5 | require 'fileutils' 6 | require 'rake/testtask' 7 | require 'time' 8 | require 'erb' 9 | require 'uglifier' 10 | 11 | DIR = File.dirname(__FILE__) 12 | TARGET_DIR = "build/vexflow" 13 | TARGET = "#{TARGET_DIR}/vexflow-min.js" 14 | TARGET_RAW = "#{TARGET_DIR}/vexflow-debug.js" 15 | 16 | BUILD_VERSION = "1.2 Custom" 17 | BUILD_PREFIX = "0xFE" 18 | BUILD_DATE = Time.now() 19 | BUILD_COMMIT = `git rev-list --max-count=1 HEAD`.chomp 20 | 21 | JSHINT = "./node_modules/jshint/bin/jshint" 22 | DOCCO = "./node_modules/.bin/docco" 23 | 24 | directory TARGET_DIR 25 | directory 'build/tests' 26 | directory 'build/tests/support' 27 | 28 | # Ordered list of Vexflow source files. If you have dependencies 29 | # between files, order them here. 30 | base_sources = [ 31 | "src/vex.js", 32 | "src/flow.js", 33 | "src/fraction.js", 34 | "src/tables.js", 35 | "src/fonts/vexflow_font.js", 36 | "src/glyph.js", 37 | "src/stave.js", 38 | "src/staveconnector.js", 39 | "src/tabstave.js", 40 | "src/tickcontext.js", 41 | "src/tickable.js", 42 | "src/note.js", 43 | "src/notehead.js", 44 | "src/stem.js", 45 | "src/stemmablenote.js", 46 | "src/stavenote.js", 47 | "src/tabnote.js", 48 | "src/ghostnote.js", 49 | "src/clefnote.js", 50 | "src/timesignote.js", 51 | "src/beam.js", 52 | "src/voice.js", 53 | "src/voicegroup.js", 54 | "src/modifier.js", 55 | "src/modifiercontext.js", 56 | "src/accidental.js", 57 | "src/dot.js", 58 | "src/formatter.js", 59 | "src/stavetie.js", 60 | "src/tabtie.js", 61 | "src/tabslide.js", 62 | "src/bend.js", 63 | "src/vibrato.js", 64 | "src/annotation.js", 65 | "src/articulation.js", 66 | "src/tuning.js", 67 | "src/stavemodifier.js", 68 | "src/keysignature.js", 69 | "src/timesignature.js", 70 | "src/clef.js", 71 | "src/music.js", 72 | "src/keymanager.js", 73 | "src/renderer.js", 74 | "src/raphaelcontext.js", 75 | "src/canvascontext.js", 76 | "src/stavebarline.js", 77 | "src/stavehairpin.js", 78 | "src/stavevolta.js", 79 | "src/staverepetition.js", 80 | "src/stavesection.js", 81 | "src/stavetempo.js", 82 | "src/stavetext.js", 83 | "src/barnote.js", 84 | "src/tremolo.js", 85 | "src/tuplet.js", 86 | "src/boundingbox.js", 87 | "src/textnote.js", 88 | "src/frethandfinger.js", 89 | "src/stringnumber.js", 90 | "src/strokes.js", 91 | "src/curve.js", 92 | "src/staveline.js", 93 | "src/crescendo.js", 94 | 95 | # MusicXML files 96 | "src/measure.js", 97 | "src/musicxml.js", 98 | "src/document.js", 99 | "src/documentformatter.js" 100 | ] 101 | 102 | # Don't minify these files. 103 | reject = [ 104 | "src/header.js", 105 | "src/container.js" 106 | ] 107 | 108 | # Catch other missing JS files 109 | js_files = Dir.glob('src/*.js') 110 | js_files.reject! {|file| base_sources.include?(file)} 111 | js_files.reject! {|file| reject.include?(file)} 112 | vexflow_sources = base_sources + js_files 113 | 114 | # Creates a rake task named "name" to copy files 115 | # from "path_glob" to "dest_dir" 116 | def copy_path(path_glob, dest_dir, name) 117 | FileList[path_glob].each do |source| 118 | target = "#{dest_dir}/#{File.basename(source)}" 119 | file target => [source, dest_dir] do 120 | if not File.directory? source 121 | cp source, target, :verbose => true 122 | end 123 | end 124 | 125 | desc "Copy data in: #{path_glob}" 126 | task name => target 127 | end 128 | end 129 | 130 | # Rake task to compile and minify VexFlow. 131 | file TARGET => vexflow_sources do 132 | # Fill fields in header 133 | header = ERB.new(File.read("src/header.js")).result(binding) 134 | raw_file = File.open(TARGET_RAW, "w") 135 | min_file = File.open(TARGET, "w") 136 | 137 | raw_file.write header 138 | min_file.write header 139 | 140 | vexflow_sources.each do |file| 141 | puts "Minifying: " + file 142 | contents = File.read(file) 143 | min = Uglifier.new( 144 | {:output => 145 | {:comments => :none} 146 | }).compile(contents) 147 | raw_file.write(contents) 148 | min_file.write(min) 149 | end 150 | 151 | raw_file.close() 152 | min_file.close() 153 | puts "Generated: " + TARGET 154 | puts "Unminified: " + TARGET_RAW 155 | end 156 | 157 | copy_path("tests/*", "build/tests", :build_copy) 158 | copy_path("tests/support/*", "build/tests/support", :build_copy) 159 | 160 | task :clean do 161 | sh 'rm -rf build' 162 | end 163 | 164 | task :lint do 165 | # Requires JSHint to be installed 166 | puts "Checking VexFlow sources for lint errors..." 167 | system "#{JSHINT} --show-non-errors --config jshintrc src/*.js" 168 | end 169 | 170 | task :docs do 171 | # Requires JSHint to be installed 172 | puts "Generating documentation..." 173 | system "#{DOCCO} -l linear src/*.js" 174 | end 175 | 176 | task :make => [:build_copy, TARGET_DIR, TARGET] 177 | 178 | task :default => [:make] 179 | -------------------------------------------------------------------------------- /docs/sandbox.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The VexFlow Sandbox 5 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 29 | 30 | 63 | 64 | 65 | 66 |
67 | VexFlow | 68 | My VexFlow | 69 | VexTab | 70 | 0xfe 71 |
72 | 73 |
74 |

The VexFlow Sandbox

75 |
76 | A live editor for experimenting with VexFlow 77 |
pre-pre-pre-alpha by 0xfe. 78 |
79 | 80 |
81 | 82 | 83 |

Have some fun!

84 | 85 |
86 | Try out the VexFlow API in this live editor. 87 |

88 |

89 |

90 |

91 | 129 |

130 | 131 | 132 | 151 | 176 |

177 |

178 | 179 |
180 |

181 | Confused? Go read the tutorial. 182 |

183 |
184 |
185 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /docs/viewer_demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | VexFlow Viewer 4 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 29 | 30 | 80 | 81 | 82 | 83 |
84 | VexFlow | 85 | My VexFlow | 86 | VexTab | 87 | 0xfe 88 |
89 | 90 |
91 |
92 | VexFlow Viewer 93 |
94 |
95 | 96 |
97 |
98 | 99 | 100 | -------------------------------------------------------------------------------- /hack/Reunion.mxl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lasconic/vexflow-musicxml/a4bf68c7b43c8b4dcd8162f777a150ed6e3a15d7/hack/Reunion.mxl -------------------------------------------------------------------------------- /hack/Reunion.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lasconic/vexflow-musicxml/a4bf68c7b43c8b4dcd8162f777a150ed6e3a15d7/hack/Reunion.zip -------------------------------------------------------------------------------- /hack/build.sh: -------------------------------------------------------------------------------- 1 | cd .. 2 | rake 3 | cd hack 4 | cp ../build/vexflow/vexflow-* . -------------------------------------------------------------------------------- /hack/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | VexFlow MusicXML Viewer 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 89 | 95 | 96 | 97 |
98 |

Please enable JavaScript to use the viewer.

99 |
100 | 101 | 102 | -------------------------------------------------------------------------------- /jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "eqnull": true, // allow == and ~= for nulls 3 | "sub": true, // don't enforce dot notation 4 | "trailing": true, // no more trailing spaces 5 | "globals": { 6 | "Vex": false, 7 | "Raphael": false 8 | } // allowed globals 9 | } 10 | -------------------------------------------------------------------------------- /push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Simple deployment script for vexflow.com. 3 | # 4 | # Pushes only JavaScript, tests, and supporting HTML (tutorial, playground) 5 | 6 | TARGET='/home/mohit/www/vexflow' 7 | SSH_TO="mohit@my.vexflow.com source ~/.bash_profile; cd $TARGET;" 8 | SCP_TO="mohit@my.vexflow.com:$TARGET" 9 | 10 | echo Building... 11 | rake clean; rake; rake docs 12 | 13 | ssh $SSH_TO "mkdir -p $TARGET; mkdir -p $TARGET/support" 14 | if [ "$?" != "0" ] 15 | then 16 | echo "Cannot create remote directory." 17 | exit 1 18 | fi 19 | 20 | echo Copying over compiled sources... 21 | scp build/vexflow/vexflow-min.js $SCP_TO/support 22 | scp build/vexflow/vexflow-debug.js $SCP_TO/support 23 | 24 | echo Copying over tests... 25 | rsync -przvl --delete --stats tests $SCP_TO 26 | scp tests/flow.html $SCP_TO/tests/index.html 27 | 28 | echo Copy over docs... 29 | rsync -przvl --delete --stats docs $SCP_TO 30 | scp -r docs/index.html $SCP_TO 31 | ssh $SSH_TO "rm docs/index.html" 32 | 33 | echo Done. 34 | -------------------------------------------------------------------------------- /push_demo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Deploy demo. 3 | # 4 | # Pushes JavaScript, tests, and docs (containing viewer and samples) 5 | 6 | /bin/echo -n "Target directory: " 7 | read TARGET 8 | /bin/echo -n "User@SSH server: " 9 | read SSH_TO 10 | SCP_TO="$SSH_TO:$TARGET" 11 | 12 | echo Building... 13 | scons -c 14 | ./build.sh 15 | 16 | ssh $SSH_TO "mkdir -p $TARGET; mkdir -p $TARGET/support" 17 | if [ "$?" != "0" ] 18 | then 19 | echo "Cannot create remote directory." 20 | exit 1 21 | fi 22 | 23 | echo Copying over compiled sources... 24 | scp build/vexflow/vexflow-min.js $SCP_TO/support 25 | 26 | echo Copying over tests... 27 | ssh $SSH_TO rm -rf tests/ 28 | scp -r build/tests $SCP_TO 29 | scp build/tests/flow.html $SCP_TO/tests/index.html 30 | 31 | echo Copy over docs... 32 | scp -r docs $SCP_TO 33 | 34 | echo Done. 35 | -------------------------------------------------------------------------------- /src/accidental.js: -------------------------------------------------------------------------------- 1 | // [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010. 2 | // 3 | // ## Description 4 | // 5 | // This file implements accidentals as modifiers that can be attached to 6 | // notes. Support is included for both western and microtonal accidentals. 7 | // 8 | // See `tests/accidental_tests.js` for usage examples. 9 | 10 | Vex.Flow.Accidental = (function(){ 11 | function Accidental(type) { 12 | if (arguments.length > 0) this.init(type); 13 | } 14 | 15 | // To enable logging for this class. Set `Vex.Flow.Accidental.DEBUG` to `true`. 16 | function L() { if (Accidental.DEBUG) Vex.L("Vex.Flow.Accidental", arguments); } 17 | 18 | var Modifier = Vex.Flow.Modifier; 19 | 20 | // ## Prototype Methods 21 | // 22 | // An `Accidental` inherits from `Modifier`, and is formatted within a 23 | // `ModifierContext`. 24 | Vex.Inherit(Accidental, Modifier, { 25 | // Create accidental. `type` can be a value from the 26 | // `Vex.Flow.accidentalCodes.accidentals` table in `tables.js`. For 27 | // example: `#`, `##`, `b`, `n`, etc. 28 | init: function(type) { 29 | Accidental.superclass.init.call(this); 30 | L("New accidental: ", type); 31 | 32 | this.note = null; 33 | // The `index` points to a specific note in a chord. 34 | this.index = null; 35 | this.type = type; 36 | this.position = Modifier.Position.LEFT; 37 | 38 | this.render_options = { 39 | // Font size for glyphs 40 | font_scale: 38, 41 | 42 | // Length of stroke across heads above or below the stave. 43 | stroke_px: 3 44 | }; 45 | 46 | this.accidental = Vex.Flow.accidentalCodes(this.type); 47 | if (!this.accidental) throw new Vex.RERR("ArgumentError", "Unknown accidental type: " + type); 48 | 49 | // Cautionary accidentals have parentheses around them 50 | this.cautionary = false; 51 | this.paren_left = null; 52 | this.paren_right = null; 53 | 54 | // Initial width is set from table. 55 | this.setWidth(this.accidental.width); 56 | }, 57 | 58 | // Return the modifier type. Used by the `ModifierContext` to calculate 59 | // layout. 60 | getCategory: function() { return "accidentals"; }, 61 | 62 | // Attach this accidental to `note`, which must be a `StaveNote`. 63 | setNote: function(note){ 64 | if (!note) throw new Vex.RERR("ArgumentError", "Bad note value: " + note); 65 | this.note = note; 66 | 67 | // Accidentals attached to grace notes are rendered smaller. 68 | if (this.note.getCategory() === 'gracenotes') { 69 | this.render_options.font_scale = 25; 70 | this.setWidth(this.accidental.gracenote_width); 71 | } 72 | }, 73 | 74 | // If called, draws parenthesis around accidental. 75 | setAsCautionary: function() { 76 | this.cautionary = true; 77 | this.render_options.font_scale = 28; 78 | this.paren_left = Vex.Flow.accidentalCodes("{"); 79 | this.paren_right = Vex.Flow.accidentalCodes("}"); 80 | var width_adjust = (this.type == "##" || this.type == "bb") ? 6 : 4; 81 | 82 | // Make sure `width` accomodates for parentheses. 83 | this.setWidth(this.paren_left.width + this.accidental.width + this.paren_right.width - width_adjust); 84 | return this; 85 | }, 86 | 87 | // Render accidental onto canvas. 88 | draw: function() { 89 | if (!this.context) throw new Vex.RERR("NoContext", 90 | "Can't draw accidental without a context."); 91 | if (!(this.note && (this.index != null))) throw new Vex.RERR("NoAttachedNote", 92 | "Can't draw accidental without a note and index."); 93 | 94 | // Figure out the start `x` and `y` coordinates for this note and index. 95 | var start = this.note.getModifierStartXY(this.position, this.index); 96 | var acc_x = (start.x + this.x_shift) - this.width; 97 | var acc_y = start.y + this.y_shift; 98 | L("Rendering: ", this.type, acc_x, acc_y); 99 | 100 | if (!this.cautionary) { 101 | // Render the accidental alone. 102 | Vex.Flow.renderGlyph(this.context, acc_x, acc_y, 103 | this.render_options.font_scale, this.accidental.code); 104 | } else { 105 | // Render the accidental in parentheses. 106 | acc_x += 3; 107 | Vex.Flow.renderGlyph(this.context, acc_x, acc_y, 108 | this.render_options.font_scale, this.paren_left.code); 109 | acc_x += 2; 110 | Vex.Flow.renderGlyph(this.context, acc_x, acc_y, 111 | this.render_options.font_scale, this.accidental.code); 112 | acc_x += this.accidental.width - 2; 113 | if (this.type == "##" || this.type == "bb") acc_x -= 2; 114 | Vex.Flow.renderGlyph(this.context, acc_x, acc_y, 115 | this.render_options.font_scale, this.paren_right.code); 116 | } 117 | } 118 | }); 119 | 120 | return Accidental; 121 | }()); -------------------------------------------------------------------------------- /src/annotation.js: -------------------------------------------------------------------------------- 1 | // [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010. 2 | // 3 | // ## Description 4 | // 5 | // This file implements text annotations as modifiers that can be attached to 6 | // notes. 7 | // 8 | // See `tests/annotation_tests.js` for usage examples. 9 | 10 | Vex.Flow.Annotation = (function() { 11 | function Annotation(text) { 12 | if (arguments.length > 0) this.init(text); 13 | } 14 | 15 | // To enable logging for this class. Set `Vex.Flow.Annotation.DEBUG` to `true`. 16 | function L() { if (Annotation.DEBUG) Vex.L("Vex.Flow.Annotation", arguments); } 17 | 18 | // Text annotations can be positioned and justified relative to the note. 19 | Annotation.Justify = { 20 | LEFT: 1, 21 | CENTER: 2, 22 | RIGHT: 3, 23 | CENTER_STEM: 4 24 | }; 25 | 26 | Annotation.VerticalJustify = { 27 | TOP: 1, 28 | CENTER: 2, 29 | BOTTOM: 3, 30 | CENTER_STEM: 4 31 | }; 32 | 33 | // ## Prototype Methods 34 | // 35 | // Annotations inherit from `Modifier` and is positioned correctly when 36 | // in a `ModifierContext`. 37 | var Modifier = Vex.Flow.Modifier; 38 | Vex.Inherit(Annotation, Modifier, { 39 | // Create a new `Annotation` with the string `text`. 40 | init: function(text) { 41 | Annotation.superclass.init.call(this); 42 | 43 | this.note = null; 44 | this.index = null; 45 | this.text_line = 0; 46 | this.text = text; 47 | this.justification = Annotation.Justify.CENTER; 48 | this.vert_justification = Annotation.VerticalJustify.TOP; 49 | this.font = { 50 | family: "Arial", 51 | size: 10, 52 | weight: "" 53 | }; 54 | 55 | // The default width is calculated from the text. 56 | this.setWidth(Vex.Flow.textWidth(text)); 57 | }, 58 | 59 | // Return the modifier type. Used by the `ModifierContext` to calculate 60 | // layout. 61 | getCategory: function() { return "annotations"; }, 62 | 63 | // Set the vertical position of the text relative to the stave. 64 | setTextLine: function(line) { this.text_line = line; return this; }, 65 | 66 | // Set font family, size, and weight. E.g., `Arial`, `10pt`, `Bold`. 67 | setFont: function(family, size, weight) { 68 | this.font = { family: family, size: size, weight: weight }; 69 | return this; 70 | }, 71 | 72 | // Set vertical position of text (above or below stave). `just` must be 73 | // a value in `Annotation.VerticalJustify`. 74 | setVerticalJustification: function(just) { 75 | this.vert_justification = just; 76 | return this; 77 | }, 78 | 79 | // Get and set horizontal justification. `justification` is a value in 80 | // `Annotation.Justify`. 81 | getJustification: function() { return this.justification; }, 82 | setJustification: function(justification) { 83 | this.justification = justification; return this; }, 84 | 85 | // Render text beside the note. 86 | draw: function() { 87 | if (!this.context) throw new Vex.RERR("NoContext", 88 | "Can't draw text annotation without a context."); 89 | if (!this.note) throw new Vex.RERR("NoNoteForAnnotation", 90 | "Can't draw text annotation without an attached note."); 91 | 92 | var start = this.note.getModifierStartXY(Modifier.Position.ABOVE, 93 | this.index); 94 | 95 | // We're changing context parameters. Save current state. 96 | this.context.save(); 97 | this.context.setFont(this.font.family, this.font.size, this.font.weight); 98 | var text_width = this.context.measureText(this.text).width; 99 | 100 | // Estimate text height to be the same as the width of an 'm'. 101 | // 102 | // This is a hack to work around the inability to measure text height 103 | // in HTML5 Canvas (and SVG). 104 | var text_height = this.context.measureText("m").width; 105 | var x, y; 106 | 107 | if (this.justification == Annotation.Justify.LEFT) { 108 | x = start.x; 109 | } else if (this.justification == Annotation.Justify.RIGHT) { 110 | x = start.x - text_width; 111 | } else if (this.justification == Annotation.Justify.CENTER) { 112 | x = start.x - text_width / 2; 113 | } else /* CENTER_STEM */ { 114 | x = this.note.getStemX() - text_width / 2; 115 | } 116 | 117 | var stem_ext, spacing; 118 | var has_stem = this.note.hasStem(); 119 | var stave = this.note.getStave(); 120 | 121 | // The position of the text varies based on whether or not the note 122 | // has a stem. 123 | if (has_stem) { 124 | stem_ext = this.note.getStem().getExtents(); 125 | spacing = stave.getSpacingBetweenLines(); 126 | } 127 | 128 | if (this.vert_justification == Annotation.VerticalJustify.BOTTOM) { 129 | y = stave.getYForBottomText(this.text_line); 130 | if (has_stem) { 131 | var stem_base = (this.note.getStemDirection() === 1 ? stem_ext.baseY : stem_ext.topY); 132 | y = Math.max(y, stem_base + (spacing * (this.text_line + 2))); 133 | } 134 | } else if (this.vert_justification == 135 | Annotation.VerticalJustify.CENTER) { 136 | var yt = this.note.getYForTopText(this.text_line) - 1; 137 | var yb = stave.getYForBottomText(this.text_line); 138 | y = yt + ( yb - yt ) / 2 + text_height / 2; 139 | } else if (this.vert_justification == 140 | Annotation.VerticalJustify.TOP) { 141 | y = Math.min(stave.getYForTopText(this.text_line), this.note.getYs()[0] - 10); 142 | if (has_stem) { 143 | y = Math.min(y, (stem_ext.topY - 5) - (spacing * this.text_line)); 144 | } 145 | } else /* CENTER_STEM */{ 146 | var extents = this.note.getStemExtents(); 147 | y = extents.topY + (extents.baseY - extents.topY) / 2 + 148 | text_height / 2; 149 | } 150 | 151 | L("Rendering annotation: ", this.text, x, y); 152 | this.context.fillText(this.text, x, y); 153 | this.context.restore(); 154 | } 155 | }); 156 | 157 | return Annotation; 158 | }()); -------------------------------------------------------------------------------- /src/barnote.js: -------------------------------------------------------------------------------- 1 | // [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010. 2 | // 3 | // ## Description 4 | // 5 | // A `BarNote` is used to render bar lines (from `barline.js`). `BarNote`s can 6 | // be added to a voice and rendered in the middle of a stave. Since it has no 7 | // duration, it consumes no `tick`s, and is dealt with appropriately by the formatter. 8 | // 9 | // See `tests/barnote_tests.js` for usage examples. 10 | 11 | Vex.Flow.BarNote = (function() { 12 | function BarNote() { this.init(); } 13 | 14 | // To enable logging for this class. Set `Vex.Flow.BarNote.DEBUG` to `true`. 15 | function L() { if (BarNote.DEBUG) Vex.L("Vex.Flow.BarNote", arguments); } 16 | 17 | // ## Prototype Methods 18 | Vex.Inherit(BarNote, Vex.Flow.Note, { 19 | init: function() { 20 | BarNote.superclass.init.call(this, {duration: "b"}); 21 | 22 | var TYPE = Vex.Flow.Barline.type; 23 | this.metrics = { 24 | widths: {} 25 | }; 26 | 27 | // Defined this way to prevent lint errors. 28 | this.metrics.widths[TYPE.SINGLE] = 8; 29 | this.metrics.widths[TYPE.DOUBLE] = 12; 30 | this.metrics.widths[TYPE.END] = 15; 31 | this.metrics.widths[TYPE.REPEAT_BEGIN] = 14; 32 | this.metrics.widths[TYPE.REPEAT_END] = 14; 33 | this.metrics.widths[TYPE.REPEAT_BOTH] = 18; 34 | this.metrics.widths[TYPE.NONE] = 0; 35 | 36 | // Tell the formatter that bar notes have no duration. 37 | this.ignore_ticks = true; 38 | this.type = TYPE.SINGLE; 39 | 40 | // Set width to width of relevant `Barline`. 41 | this.setWidth(this.metrics.widths[this.type]); 42 | }, 43 | 44 | // Get and set the type of Bar note. `type` must be one of `Vex.Flow.Barline.type`. 45 | getType: function() { return this.type; }, 46 | setType: function(type) { 47 | this.type = type; 48 | this.setWidth(this.metrics.widths[this.type]); 49 | return this; 50 | }, 51 | 52 | getBoundingBox: function() { 53 | return new Vex.Flow.BoundingBox(0, 0, 0, 0); 54 | }, 55 | 56 | addToModifierContext: function() { 57 | /* overridden to ignore */ 58 | return this; 59 | }, 60 | 61 | preFormat: function() { 62 | /* overridden to ignore */ 63 | this.setPreFormatted(true); 64 | return this; 65 | }, 66 | 67 | // Render note to stave. 68 | draw: function() { 69 | if (!this.stave) throw new Vex.RERR("NoStave", "Can't draw without a stave."); 70 | L("Rendering bar line at: ", this.getAbsoluteX()); 71 | var barline = new Vex.Flow.Barline(this.type, this.getAbsoluteX()); 72 | barline.draw(this.stave); 73 | } 74 | }); 75 | 76 | return BarNote; 77 | }()); 78 | -------------------------------------------------------------------------------- /src/boundingbox.js: -------------------------------------------------------------------------------- 1 | // Vex Music Notation 2 | // Mohit Muthanna 3 | // 4 | // Copyright Mohit Muthanna 2010 5 | 6 | // Bounding boxes for interactive notation 7 | 8 | /** @constructor */ 9 | Vex.Flow.BoundingBox = (function() { 10 | function BoundingBox(x, y, w, h) { this.init(x, y, w, h); } 11 | BoundingBox.copy = function(that) { 12 | return new BoundingBox(that.x, that.y, that.w, that.h); }; 13 | 14 | BoundingBox.prototype = { 15 | init: function(x, y, w, h) { 16 | this.x = x; 17 | this.y = y; 18 | this.w = w; 19 | this.h = h; 20 | }, 21 | 22 | getX: function() { return this.x; }, 23 | getY: function() { return this.y; }, 24 | getW: function() { return this.w; }, 25 | getH: function() { return this.h; }, 26 | 27 | setX: function(x) { this.x = x; return this; }, 28 | setY: function(y) { this.y = y; return this; }, 29 | setW: function(w) { this.w = w; return this; }, 30 | setH: function(h) { this.h = h; return this; }, 31 | 32 | move: function(x, y) { this.x += x; this.y += y; }, 33 | clone: function() { return BoundingBox.copy(this); }, 34 | 35 | // Merge my box with given box. Creates a bigger bounding box unless 36 | // the given box is contained in this one. 37 | mergeWith: function(boundingBox, ctx) { 38 | var that = boundingBox; 39 | 40 | var new_x = this.x < that.x ? this.x : that.x; 41 | var new_y = this.y < that.y ? this.y : that.y; 42 | var new_w = (this.x + this.w) < (that.x + that.w) ? (that.x + that.w) - this.x : (this.x + this.w) - Vex.Min(this.x, that.x); 43 | var new_h = (this.y + this.h) < (that.y + that.h) ? (that.y + that.h) - this.y : (this.y + this.h) - Vex.Min(this.y, that.y); 44 | 45 | this.x = new_x; 46 | this.y = new_y; 47 | this.w = new_w; 48 | this.h = new_h; 49 | 50 | if (ctx) this.draw(ctx); 51 | return this; 52 | }, 53 | 54 | draw: function(ctx, x, y) { 55 | if (!x) x = 0; 56 | if (!y) y = 0; 57 | ctx.rect(this.x + x, this.y + y, this.w, this.h); 58 | ctx.stroke(); 59 | } 60 | }; 61 | 62 | return BoundingBox; 63 | }()); -------------------------------------------------------------------------------- /src/canvascontext.js: -------------------------------------------------------------------------------- 1 | // Vex Flow 2 | // Mohit Muthanna 3 | // 4 | // A rendering context for the Raphael backend. 5 | // 6 | // Copyright Mohit Cheppudira 2010 7 | 8 | /** @constructor */ 9 | Vex.Flow.CanvasContext = (function() { 10 | function CanvasContext(context) { 11 | if (arguments.length > 0) this.init(context); 12 | } 13 | 14 | CanvasContext.WIDTH = 600; 15 | CanvasContext.HEIGHT = 400; 16 | 17 | CanvasContext.prototype = { 18 | init: function(context) { 19 | // Use a name that is unlikely to clash with a canvas context 20 | // property 21 | this.vexFlowCanvasContext = context; 22 | if (!context.canvas) { 23 | this.canvas = { 24 | width: CanvasContext.WIDTH, 25 | height: CanvasContext.HEIGHT 26 | }; 27 | } else { 28 | this.canvas = this.context.canvas; 29 | } 30 | }, 31 | 32 | clear: function() { 33 | this.vexFlowCanvasContext.clearRect(0, 0, this.canvas.width, this.canvas.height); 34 | }, 35 | 36 | setFont: function(family, size, weight) { 37 | this.vexFlowCanvasContext.font = (weight || "") + " " + size + "pt " + family; 38 | return this; 39 | }, 40 | 41 | setRawFont: function(font) { 42 | this.vexFlowCanvasContext.font = font; 43 | return this; 44 | }, 45 | 46 | setFillStyle: function(style) { 47 | this.vexFlowCanvasContext.fillStyle = style; 48 | return this; 49 | }, 50 | 51 | setBackgroundFillStyle: function(style) { 52 | this.background_fillStyle = style; 53 | return this; 54 | }, 55 | 56 | setStrokeStyle: function(style) { 57 | this.vexFlowCanvasContext.strokeStyle = style; 58 | return this; 59 | }, 60 | 61 | setShadowColor: function(style) { 62 | this.vexFlowCanvasContext.shadowColor = style; 63 | return this; 64 | }, 65 | 66 | setShadowBlur: function(blur) { 67 | this.vexFlowCanvasContext.shadowBlur = blur; 68 | return this; 69 | }, 70 | 71 | setLineWidth: function(width) { 72 | this.vexFlowCanvasContext.lineWidth = width; 73 | return this; 74 | }, 75 | 76 | setLineCap: function(cap_type) { 77 | this.vexFlowCanvasContext.lineCap = cap_type; 78 | return this; 79 | }, 80 | 81 | setLineDash: function(dash) { 82 | this.vexFlowCanvasContext.lineDash = dash; 83 | }, 84 | 85 | scale: function(x, y) { 86 | return this.vexFlowCanvasContext.scale(parseFloat(x), parseFloat(y)); 87 | }, 88 | 89 | resize: function(width, height) { 90 | return this.vexFlowCanvasContext.resize( 91 | parseInt(width, 10), parseInt(height, 10)); 92 | }, 93 | 94 | rect: function(x, y, width, height) { 95 | return this.vexFlowCanvasContext.rect(x, y, width, height); 96 | }, 97 | 98 | fillRect: function(x, y, width, height) { 99 | return this.vexFlowCanvasContext.fillRect(x, y, width, height); 100 | }, 101 | 102 | clearRect: function(x, y, width, height) { 103 | return this.vexFlowCanvasContext.clearRect(x, y, width, height); 104 | }, 105 | 106 | beginPath: function() { 107 | return this.vexFlowCanvasContext.beginPath(); 108 | }, 109 | 110 | moveTo: function(x, y) { 111 | return this.vexFlowCanvasContext.moveTo(x, y); 112 | }, 113 | 114 | lineTo: function(x, y) { 115 | return this.vexFlowCanvasContext.lineTo(x, y); 116 | }, 117 | 118 | bezierCurveTo: function(x1, y1, x2, y2, x, y) { 119 | return this.vexFlowCanvasContext.bezierCurveTo(x1, y1, x2, y2, x, y); 120 | }, 121 | 122 | quadraticCurveTo: function(x1, y1, x, y) { 123 | return this.vexFlowCanvasContext.quadraticCurveTo(x1, y1, x, y); 124 | }, 125 | 126 | // This is an attempt (hack) to simulate the HTML5 canvas 127 | // arc method. 128 | arc: function(x, y, radius, startAngle, endAngle, antiClockwise) { 129 | return this.vexFlowCanvasContext.arc(x, y, radius, startAngle, endAngle, antiClockwise); 130 | }, 131 | 132 | // Adapted from the source for Raphael's Element.glow 133 | glow: function() { 134 | return this.vexFlowCanvasContext.glow(); 135 | }, 136 | 137 | fill: function() { 138 | return this.vexFlowCanvasContext.fill(); 139 | }, 140 | 141 | stroke: function() { 142 | return this.vexFlowCanvasContext.stroke(); 143 | }, 144 | 145 | closePath: function() { 146 | return this.vexFlowCanvasContext.closePath(); 147 | }, 148 | 149 | measureText: function(text) { 150 | return this.vexFlowCanvasContext.measureText(text); 151 | }, 152 | 153 | fillText: function(text, x, y) { 154 | return this.vexFlowCanvasContext.fillText(text, x, y); 155 | }, 156 | 157 | save: function() { 158 | return this.vexFlowCanvasContext.save(); 159 | }, 160 | 161 | restore: function() { 162 | return this.vexFlowCanvasContext.restore(); 163 | } 164 | }; 165 | 166 | return CanvasContext; 167 | }()); 168 | -------------------------------------------------------------------------------- /src/clef.js: -------------------------------------------------------------------------------- 1 | // [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna Cheppudira 2013. 2 | // Co-author: Benjamin W. Bohl 3 | // 4 | // ## Description 5 | // 6 | // This file implements various types of clefs that can be rendered on a stave. 7 | // 8 | // See `tests/clef_tests.js` for usage examples. 9 | 10 | Vex.Flow.Clef = (function() { 11 | function Clef(clef) { 12 | if (arguments.length > 0) this.init(clef); 13 | } 14 | 15 | // To enable logging for this class, set `Vex.Flow.Clef.DEBUG` to `true`. 16 | function L() { if (Vex.Flow.Clef.DEBUG) Vex.L("Vex.Flow.Clef", arguments); } 17 | 18 | // Every clef name is associated with a glyph code from the font file, a 19 | // point size, and a default stave line number. 20 | Clef.types = { 21 | "treble": { 22 | code: "v83", 23 | point: 40, 24 | line: 3 25 | }, 26 | "bass": { 27 | code: "v79", 28 | point: 40, 29 | line: 1 30 | }, 31 | "alto": { 32 | code: "vad", 33 | point: 40, 34 | line: 2 35 | }, 36 | "tenor": { 37 | code: "vad", 38 | point: 40, 39 | line: 1 40 | }, 41 | "percussion": { 42 | code: "v59", 43 | point: 40, 44 | line: 2 45 | }, 46 | "soprano": { 47 | code: "vad", 48 | point: 40, 49 | line: 4 50 | }, 51 | "mezzo-soprano": { 52 | code: "vad", 53 | point: 40, 54 | line: 3 55 | }, 56 | "baritone-c": { 57 | code: "vad", 58 | point: 40, 59 | line: 0 60 | }, 61 | "baritone-f": { 62 | code: "v79", 63 | point: 40, 64 | line: 2 65 | }, 66 | "subbass": { 67 | code: "v79", 68 | point: 40, 69 | line: 0 70 | }, 71 | "french": { 72 | code: "v83", 73 | point: 40, 74 | line: 4 75 | }, 76 | "treble_small": { 77 | code: "v83", 78 | point: 32, 79 | line: 3 80 | }, 81 | "bass_small": { 82 | code: "v79", 83 | point: 32, 84 | line: 1 85 | }, 86 | "alto_small": { 87 | code: "vad", 88 | point: 32, 89 | line: 2 90 | }, 91 | "tenor_small": { 92 | code: "vad", 93 | point: 32, 94 | line: 1 95 | }, 96 | "soprano_small": { 97 | code: "vad", 98 | point: 32, 99 | line: 4 100 | }, 101 | "mezzo-soprano_small": { 102 | code: "vad", 103 | point: 32, 104 | line: 3 105 | }, 106 | "baritone-c_small": { 107 | code: "vad", 108 | point: 32, 109 | line: 0 110 | }, 111 | "baritone-f_small": { 112 | code: "v79", 113 | point: 32, 114 | line: 2 115 | }, 116 | "subbass_small": { 117 | code: "v79", 118 | point: 32, 119 | line: 0 120 | }, 121 | "french_small": { 122 | code: "v83", 123 | point: 32, 124 | line: 4 125 | }, 126 | "percussion_small": { 127 | code: "v59", 128 | point: 32, 129 | line: 2 130 | } 131 | }; 132 | 133 | // ## Prototype Methods 134 | Vex.Inherit(Clef, Vex.Flow.StaveModifier, { 135 | // Create a new clef. The parameter `clef` must be a key from 136 | // `Clef.types`. 137 | init: function(clef) { 138 | var superclass = Vex.Flow.Clef.superclass; 139 | superclass.init.call(this); 140 | 141 | this.clef = Vex.Flow.Clef.types[clef]; 142 | L("Creating clef:", clef); 143 | }, 144 | 145 | // Add this clef to the start of the given `stave`. 146 | addModifier: function(stave) { 147 | var glyph = new Vex.Flow.Glyph(this.clef.code, this.clef.point); 148 | this.placeGlyphOnLine(glyph, stave, this.clef.line); 149 | stave.addGlyph(glyph); 150 | }, 151 | 152 | // Add this clef to the end of the given `stave`. 153 | addEndModifier: function(stave) { 154 | var glyph = new Vex.Flow.Glyph(this.clef.code, this.clef.point); 155 | this.placeGlyphOnLine(glyph, stave, this.clef.line); 156 | stave.addEndGlyph(glyph); 157 | } 158 | }); 159 | 160 | return Clef; 161 | }()); 162 | -------------------------------------------------------------------------------- /src/clefnote.js: -------------------------------------------------------------------------------- 1 | // Vex Flow Notation 2 | // Copyright Mohit Muthanna 2010 3 | // 4 | // Author Taehoon Moon 2014 5 | 6 | /** @constructor */ 7 | Vex.Flow.ClefNote = (function() { 8 | function ClefNote(clef) { this.init(clef); } 9 | 10 | Vex.Inherit(ClefNote, Vex.Flow.Note, { 11 | init: function(clef) { 12 | ClefNote.superclass.init.call(this, {duration: "b"}); 13 | 14 | this.setClef(clef); 15 | 16 | // Note properties 17 | this.ignore_ticks = true; 18 | }, 19 | 20 | setClef: function(clef) { 21 | this.clef = Vex.Flow.Clef.types[clef]; 22 | this.glyph = new Vex.Flow.Glyph(this.clef.code, this.clef.point); 23 | this.setWidth(this.glyph.getMetrics().width); 24 | return this; 25 | }, 26 | 27 | getClef: function() { 28 | return this.clef; 29 | }, 30 | 31 | setStave: function(stave) { 32 | var superclass = Vex.Flow.ClefNote.superclass; 33 | superclass.setStave.call(this, stave); 34 | }, 35 | 36 | getBoundingBox: function() { 37 | return new Vex.Flow.BoundingBox(0, 0, 0, 0); 38 | }, 39 | 40 | addToModifierContext: function() { 41 | /* overridden to ignore */ 42 | return this; 43 | }, 44 | 45 | preFormat: function() { 46 | this.setPreFormatted(true); 47 | return this; 48 | }, 49 | 50 | draw: function() { 51 | if (!this.stave) throw new Vex.RERR("NoStave", "Can't draw without a stave."); 52 | 53 | if (!this.glyph.getContext()) { 54 | this.glyph.setContext(this.context); 55 | } 56 | 57 | this.glyph.setStave(this.stave); 58 | this.glyph.setYShift( 59 | this.stave.getYForLine(this.clef.line) - this.stave.getYForGlyphs()); 60 | this.glyph.renderToStave(this.getAbsoluteX()); 61 | } 62 | }); 63 | 64 | return ClefNote; 65 | }()); 66 | -------------------------------------------------------------------------------- /src/crescendo.js: -------------------------------------------------------------------------------- 1 | // [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010. 2 | // 3 | // ## Description 4 | // 5 | // This file implements the `Crescendo` object which draws crescendos and 6 | // decrescendo dynamics markings. A `Crescendo` is initialized with a 7 | // duration and formatted as part of a `Voice` like any other `Note` 8 | // type in VexFlow. This object would most likely be formatted in a Voice 9 | // with `TextNotes` - which are used to represent other dynamics markings. 10 | Vex.Flow.Crescendo = (function() { 11 | function Crescendo(note_struct) { 12 | if (arguments.length > 0) this.init(note_struct); 13 | } 14 | 15 | // To enable logging for this class. Set `Vex.Flow.Crescendo.DEBUG` to `true`. 16 | function L() { if (Crescendo.DEBUG) Vex.L("Vex.Flow.Crescendo", arguments); } 17 | 18 | // Private helper to draw the hairpin 19 | function renderHairpin(ctx, params) { 20 | var begin_x = params.begin_x; 21 | var end_x = params.end_x; 22 | var y = params.y; 23 | var half_height = params.height / 2; 24 | 25 | ctx.beginPath(); 26 | 27 | if (params.reverse) { 28 | ctx.moveTo(begin_x, y - half_height); 29 | ctx.lineTo(end_x, y); 30 | ctx.lineTo(begin_x, y + half_height); 31 | } else { 32 | ctx.moveTo(end_x, y - half_height); 33 | ctx.lineTo(begin_x, y); 34 | ctx.lineTo(end_x, y + half_height); 35 | } 36 | 37 | ctx.stroke(); 38 | ctx.closePath(); 39 | } 40 | 41 | // ## Prototype Methods 42 | Vex.Inherit(Crescendo, Vex.Flow.Note, { 43 | // Initialize the crescendo's properties 44 | init: function(note_struct) { 45 | Crescendo.superclass.init.call(this, note_struct); 46 | 47 | // Whether the object is a decrescendo 48 | this.decrescendo = false; 49 | 50 | // The staff line to be placed on 51 | this.line = note_struct.line || 0; 52 | 53 | // The height at the open end of the cresc/decresc 54 | this.height = 15; 55 | 56 | Vex.Merge(this.render_options, { 57 | // Extensions to the length of the crescendo on either side 58 | extend_left: 0, 59 | extend_right: 0, 60 | // Vertical shift 61 | y_shift: 0 62 | }); 63 | }, 64 | 65 | // Set the line to center the element on 66 | setLine: function(line) { this.line = line; return this; }, 67 | 68 | // Set the full height at the open end 69 | setHeight: function(height) { this.height = height; return this; }, 70 | 71 | // Set whether the sign should be a descresendo by passing a bool 72 | // to `decresc` 73 | setDecrescendo: function(decresc) { 74 | this.decrescendo = decresc; 75 | return this; 76 | }, 77 | 78 | // Preformat the note 79 | preFormat: function() { this.preFormatted = true; return this; }, 80 | 81 | // Render the Crescendo object onto the canvas 82 | draw: function() { 83 | if (!this.context) throw new Vex.RERR("NoContext", 84 | "Can't draw Hairpin without a context."); 85 | 86 | var tick_context = this.getTickContext(); 87 | var next_context = Vex.Flow.TickContext.getNextContext(tick_context); 88 | 89 | var begin_x = this.getAbsoluteX(); 90 | var end_x; 91 | if (next_context) { 92 | end_x = next_context.getX(); 93 | } else { 94 | end_x = this.stave.x + this.stave.width; 95 | } 96 | 97 | var y = this.stave.getYForLine(this.line + (-3)) + 1; 98 | 99 | L("Drawing ", this.decrescendo ? "decrescendo " : "crescendo ", 100 | this.height, "x", begin_x - end_x); 101 | 102 | renderHairpin(this.context, { 103 | begin_x: begin_x - this.render_options.extend_left, 104 | end_x: end_x + this.render_options.extend_right, 105 | y: y + this.render_options.y_shift, 106 | height: this.height, 107 | reverse: this.decrescendo 108 | }); 109 | } 110 | }); 111 | 112 | return Crescendo; 113 | })(); -------------------------------------------------------------------------------- /src/curve.js: -------------------------------------------------------------------------------- 1 | // VexFlow - Music Engraving for HTML5 2 | // Copyright Mohit Muthanna 2010 3 | // 4 | // This class implements curves (for slurs) 5 | 6 | Vex.Flow.Curve = (function() { 7 | // from: Start note 8 | // to: End note 9 | // options: 10 | // cps: List of control points 11 | // x_shift: pixels to shift 12 | // y_shift: pixels to shift 13 | function Curve(from, to, options) { 14 | if (arguments.length > 0) this.init(from, to, options); 15 | } 16 | 17 | Curve.Position = { 18 | NEAR_HEAD: 1, 19 | NEAR_TOP: 2 20 | }; 21 | 22 | Curve.DEBUG = true; 23 | 24 | Curve.prototype = { 25 | init: function(from, to, options) { 26 | this.render_options = { 27 | spacing: 2, 28 | thickness: 2, 29 | x_shift: 0, 30 | y_shift: 10, 31 | position: Curve.Position.NEAR_HEAD, 32 | invert: false, 33 | cps: [{x: 0, y: 10}, {x: 0, y: 10}] 34 | }; 35 | 36 | Vex.Merge(this.render_options, options); 37 | this.setNotes(from, to); 38 | }, 39 | 40 | setContext: function(context) { this.context = context; return this; }, 41 | setNotes: function(from, to) { 42 | if (!from && !to) 43 | throw new Vex.RuntimeError("BadArguments", 44 | "Curve needs to have either first_note or last_note set."); 45 | 46 | this.from = from; 47 | this.to = to; 48 | return this; 49 | }, 50 | 51 | /** 52 | * @return {boolean} Returns true if this is a partial bar. 53 | */ 54 | isPartial: function() { 55 | return (!this.from || !this.to); 56 | }, 57 | 58 | renderCurve: function(params) { 59 | var ctx = this.context; 60 | var cps = this.render_options.cps; 61 | 62 | var x_shift = this.render_options.x_shift; 63 | var y_shift = this.render_options.y_shift * params.direction; 64 | 65 | var first_x = params.first_x + x_shift; 66 | var first_y = params.first_y + y_shift; 67 | var last_x = params.last_x - x_shift; 68 | var last_y = params.last_y + y_shift; 69 | var thickness = this.render_options.thickness; 70 | 71 | var cp_spacing = (last_x - first_x) / (cps.length + 2); 72 | 73 | ctx.beginPath(); 74 | ctx.moveTo(first_x, first_y); 75 | ctx.bezierCurveTo(first_x + cp_spacing + cps[0].x, 76 | first_y + (cps[0].y * params.direction), 77 | last_x - cp_spacing + cps[1].x, 78 | last_y + (cps[1].y * params.direction), 79 | last_x, last_y); 80 | ctx.bezierCurveTo(last_x - cp_spacing + cps[1].x, 81 | last_y + ((cps[1].y + thickness) * params.direction), 82 | first_x + cp_spacing + cps[0].x, 83 | first_y + ((cps[0].y + thickness) * params.direction), 84 | first_x, first_y); 85 | ctx.stroke(); 86 | ctx.closePath(); 87 | ctx.fill(); 88 | }, 89 | 90 | draw: function() { 91 | if (!this.context) 92 | throw new Vex.RERR("NoContext", "No context to render tie."); 93 | var first_note = this.from; 94 | var last_note = this.to; 95 | var first_x, last_x, first_y, last_y, stem_direction; 96 | 97 | var metric = "baseY"; 98 | var end_metric = "baseY"; 99 | var position = this.render_options.position; 100 | var position_end = this.render_options.position_end; 101 | 102 | if (position === Curve.Position.NEAR_TOP) { 103 | metric = "topY"; 104 | end_metric = "topY"; 105 | } 106 | 107 | if (position_end == Curve.Position.NEAR_HEAD) { 108 | end_metric = "baseY"; 109 | } else if (position_end == Curve.Position.NEAR_TOP) { 110 | end_metric = "topY"; 111 | } 112 | 113 | if (first_note) { 114 | first_x = first_note.getTieRightX(); 115 | stem_direction = first_note.getStemDirection(); 116 | first_y = first_note.getStemExtents()[metric]; 117 | } else { 118 | first_x = last_note.getStave().getTieStartX(); 119 | first_y = last_note.getStemExtents()[metric]; 120 | } 121 | 122 | if (last_note) { 123 | last_x = last_note.getTieLeftX(); 124 | stem_direction = last_note.getStemDirection(); 125 | last_y = last_note.getStemExtents()[end_metric]; 126 | } else { 127 | last_x = first_note.getStave().getTieEndX(); 128 | last_y = first_note.getStemExtents()[end_metric]; 129 | } 130 | 131 | this.renderCurve({ 132 | first_x: first_x, 133 | last_x: last_x, 134 | first_y: first_y, 135 | last_y: last_y, 136 | direction: stem_direction * 137 | (this.render_options.invert === true ? -1 : 1) 138 | }); 139 | return true; 140 | } 141 | }; 142 | 143 | return Curve; 144 | }()); 145 | -------------------------------------------------------------------------------- /src/dot.js: -------------------------------------------------------------------------------- 1 | // VexFlow - Music Engraving for HTML5 2 | // Copyright Mohit Muthanna 2010 3 | // 4 | // This class implements dot modifiers for notes. 5 | 6 | /** 7 | * @constructor 8 | */ 9 | Vex.Flow.Dot = (function() { 10 | function Dot() { 11 | this.init(); 12 | } 13 | 14 | var Modifier = Vex.Flow.Modifier; 15 | Vex.Inherit(Dot, Modifier, { 16 | init: function() { 17 | Dot.superclass.init.call(this); 18 | 19 | this.note = null; 20 | this.index = null; 21 | this.position = Modifier.Position.RIGHT; 22 | 23 | this.radius = 2; 24 | this.setWidth(5); 25 | this.dot_shiftY = 0; 26 | }, 27 | 28 | setNote: function(note){ 29 | this.note = note; 30 | 31 | if (this.note.getCategory() === 'gracenotes') { 32 | this.radius *= 0.50; 33 | this.setWidth(3); 34 | } 35 | }, 36 | 37 | getCategory: function() { return "dots"; }, 38 | 39 | setDotShiftY: function(y) { this.dot_shiftY = y; return this; }, 40 | 41 | draw: function() { 42 | if (!this.context) throw new Vex.RERR("NoContext", 43 | "Can't draw dot without a context."); 44 | if (!(this.note && (this.index != null))) throw new Vex.RERR("NoAttachedNote", 45 | "Can't draw dot without a note and index."); 46 | 47 | var line_space = this.note.stave.options.spacing_between_lines_px; 48 | 49 | var start = this.note.getModifierStartXY(this.position, this.index); 50 | 51 | // Set the starting y coordinate to the base of the stem for TabNotes 52 | if (this.note.getCategory() === 'tabnotes') { 53 | start.y = this.note.getStemExtents().baseY; 54 | } 55 | 56 | var dot_x = (start.x + this.x_shift) + this.width - this.radius; 57 | var dot_y = start.y + this.y_shift + (this.dot_shiftY * line_space); 58 | var ctx = this.context; 59 | 60 | ctx.beginPath(); 61 | ctx.arc(dot_x, dot_y, this.radius, 0, Math.PI * 2, false); 62 | ctx.fill(); 63 | } 64 | }); 65 | 66 | return Dot; 67 | }()); 68 | -------------------------------------------------------------------------------- /src/flow.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Vex Flow - Mohit Muthanna 3 | */ 4 | 5 | /** 6 | * New namespace. 7 | */ 8 | Vex.Flow = { 9 | /** 10 | * The resolution used for all the rhythm timing in this 11 | * library. 12 | * 13 | * @const 14 | * @type {number} 15 | */ 16 | RESOLUTION: 16384, 17 | 18 | /* Kerning (DEPRECATED) */ 19 | IsKerned: true 20 | }; 21 | -------------------------------------------------------------------------------- /src/frethandfinger.js: -------------------------------------------------------------------------------- 1 | // VexFlow - Music Engraving for HTML5 2 | // Copyright Mohit Muthanna 2010 3 | // Author Larry Kuhns 2013 4 | // Class to draws string numbers into the notation. 5 | 6 | /** 7 | * @constructor 8 | */ 9 | Vex.Flow.FretHandFinger = (function() { 10 | function FretHandFinger(number) { 11 | if (arguments.length > 0) this.init(number); 12 | } 13 | 14 | var Modifier = Vex.Flow.Modifier; 15 | Vex.Inherit(FretHandFinger, Modifier, { 16 | init: function(number) { 17 | var superclass = Vex.Flow.FretHandFinger.superclass; 18 | superclass.init.call(this); 19 | 20 | this.note = null; 21 | this.index = null; 22 | this.finger = number; 23 | this.width = 7; 24 | this.position = Modifier.Position.LEFT; // Default position above stem or note head 25 | this.x_shift = 0; 26 | this.y_shift = 0; 27 | this.x_offset = 0; // Horizontal offset from default 28 | this.y_offset = 0; // Vertical offset from default 29 | this.font = { 30 | family: "sans-serif", 31 | size: 9, 32 | weight: "bold" 33 | }; 34 | }, 35 | 36 | getCategory: function() { return "frethandfinger"; }, 37 | getNote: function() { return this.note; }, 38 | setNote: function(note) { this.note = note; return this; }, 39 | getIndex: function() { return this.index; }, 40 | setIndex: function(index) { this.index = index; return this; }, 41 | getPosition: function() { return this.position; }, 42 | setPosition: function(position) { 43 | if (position >= Modifier.Position.LEFT && 44 | position <= Modifier.Position.BELOW) 45 | this.position = position; 46 | return this; 47 | }, 48 | setFretHandFinger: function(number) { this.finger = number; return this; }, 49 | setOffsetX: function(x) { this.x_offset = x; return this; }, 50 | setOffsetY: function(y) { this.y_offset = y; return this; }, 51 | 52 | draw: function() { 53 | if (!this.context) throw new Vex.RERR("NoContext", 54 | "Can't draw string number without a context."); 55 | if (!(this.note && (this.index != null))) throw new Vex.RERR("NoAttachedNote", 56 | "Can't draw string number without a note and index."); 57 | 58 | var ctx = this.context; 59 | var start = this.note.getModifierStartXY(this.position, this.index); 60 | var dot_x = (start.x + this.x_shift + this.x_offset); 61 | var dot_y = start.y + this.y_shift + this.y_offset + 5; 62 | 63 | switch (this.position) { 64 | case Modifier.Position.ABOVE: 65 | dot_x -= 4; 66 | dot_y -= 12; 67 | break; 68 | case Modifier.Position.BELOW: 69 | dot_x -= 2; 70 | dot_y += 10; 71 | break; 72 | case Modifier.Position.LEFT: 73 | dot_x -= this.width; 74 | break; 75 | case Modifier.Position.RIGHT: 76 | dot_x += 1; 77 | break; 78 | } 79 | 80 | ctx.save(); 81 | ctx.setFont(this.font.family, this.font.size, this.font.weight); 82 | ctx.fillText("" + this.finger, dot_x, dot_y); 83 | 84 | ctx.restore(); 85 | } 86 | }); 87 | 88 | return FretHandFinger; 89 | }()); -------------------------------------------------------------------------------- /src/ghostnote.js: -------------------------------------------------------------------------------- 1 | // Vex Flow Notation 2 | // Mohit Muthanna 3 | // 4 | // Copyright Mohit Muthanna 2010 5 | // 6 | // Requires vex.js. 7 | 8 | /** @constructor */ 9 | Vex.Flow.GhostNote = (function() { 10 | function GhostNote(duration) { 11 | if (arguments.length > 0) this.init(duration); 12 | } 13 | 14 | Vex.Inherit(GhostNote, Vex.Flow.StemmableNote, { 15 | init: function(parameter) { 16 | // Sanity check 17 | if (!parameter) { 18 | throw new Vex.RuntimeError("BadArguments", 19 | "Ghost note must have valid initialization data to identify " + 20 | "duration."); 21 | } 22 | 23 | var note_struct; 24 | 25 | // Preserve backwards-compatibility 26 | if (typeof(parameter) === "string") { 27 | note_struct = { duration: parameter }; 28 | } else if (typeof(parameter) === "object") { 29 | note_struct = parameter; 30 | } else { 31 | throw new Vex.RuntimeError("BadArguments", 32 | "Ghost note must have valid initialization data to identify " + 33 | "duration."); 34 | } 35 | 36 | GhostNote.superclass.init.call(this, note_struct); 37 | 38 | // Note properties 39 | this.setWidth(0); 40 | }, 41 | 42 | isRest: function() { return true; }, 43 | 44 | setStave: function(stave) { GhostNote.superclass.setStave.call(this, stave); }, 45 | 46 | addToModifierContext: function() 47 | { /* intentionally overridden */ return this; }, 48 | 49 | preFormat: function() { 50 | this.setPreFormatted(true); 51 | return this; 52 | }, 53 | 54 | draw: function() { 55 | if (!this.stave) throw new Vex.RERR("NoStave", "Can't draw without a stave."); 56 | 57 | // Draw the modifiers 58 | for (var i = 0; i < this.modifiers.length; ++i) { 59 | var modifier = this.modifiers[i]; 60 | modifier.setContext(this.context); 61 | modifier.draw(); 62 | } 63 | } 64 | }); 65 | 66 | return GhostNote; 67 | }()); 68 | -------------------------------------------------------------------------------- /src/glyphs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Vex Glyphs 5 | 7 | 41 | 42 | 43 | 44 | 45 | 46 | 105 | 106 | 107 | 108 |
109 |

Gonville Glyphs

110 |

111 | Cross indicates render coordinates. 112 |

113 | 114 | 115 | HTML5 Canvas not supported on this browser. 116 | 117 | 118 |

119 | For more information visit 0xfe.blogspot.com. 120 |

121 | 122 |
123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /src/gracenote.js: -------------------------------------------------------------------------------- 1 | Vex.Flow.GraceNote = (function() { 2 | var GraceNote = function(note_struct) { 3 | if (arguments.length > 0) this.init(note_struct); 4 | }; 5 | 6 | Vex.Inherit(GraceNote, Vex.Flow.StaveNote, { 7 | init: function(note_struct) { 8 | GraceNote.superclass.init.call(this, note_struct); 9 | 10 | this.render_options.glyph_font_scale = 22; 11 | this.render_options.stem_height = 20; 12 | this.render_options.stroke_px = 2; 13 | this.glyph.head_width = 6; 14 | 15 | this.slash = note_struct.slash; 16 | this.slur = true; 17 | 18 | this.buildNoteHeads(); 19 | 20 | this.width = 3; 21 | }, 22 | 23 | getStemExtension: function(){ 24 | var glyph = this.getGlyph(); 25 | 26 | if (this.stem_extension_override != null) { 27 | return this.stem_extension_override; 28 | } 29 | 30 | if (glyph) { 31 | return this.getStemDirection() === 1 ? glyph.gracenote_stem_up_extension : 32 | glyph.gracenote_stem_down_extension; 33 | } 34 | 35 | return 0; 36 | }, 37 | 38 | getCategory: function() { return 'gracenotes'; }, 39 | 40 | draw: function(){ 41 | GraceNote.superclass.draw.call(this); 42 | var ctx = this.context; 43 | var stem_direction = this.getStemDirection(); 44 | 45 | if (this.slash) { 46 | ctx.beginPath(); 47 | 48 | var x = this.getAbsoluteX(); 49 | var y = this.getYs()[0] - (this.stem.getHeight() / 2.8); 50 | if (stem_direction === 1) { 51 | x += 1; 52 | ctx.lineTo(x, y); 53 | ctx.lineTo(x + 13, y - 9); 54 | } else if (stem_direction === -1) { 55 | x -= 4; 56 | y += 1; 57 | ctx.lineTo(x, y); 58 | ctx.lineTo(x + 13, y + 9); 59 | } 60 | 61 | ctx.closePath(); 62 | ctx.stroke(); 63 | } 64 | } 65 | }); 66 | 67 | return GraceNote; 68 | }()); 69 | -------------------------------------------------------------------------------- /src/gracenotegroup.js: -------------------------------------------------------------------------------- 1 | Vex.Flow.GraceNoteGroup = (function(){ 2 | var GraceNoteGroup = function(grace_notes, config) { 3 | if (arguments.length > 0) this.init(grace_notes, config); 4 | }; 5 | 6 | Vex.Inherit(GraceNoteGroup, Vex.Flow.Modifier, { 7 | init: function(grace_notes, show_slur) { 8 | var superclass = GraceNoteGroup.superclass; 9 | superclass.init.call(this); 10 | 11 | this.note = null; 12 | this.index = null; 13 | this.position = Vex.Flow.Modifier.Position.LEFT; 14 | this.grace_notes = grace_notes; 15 | this.width = 0; 16 | 17 | this.preFormatted = false; 18 | 19 | this.show_slur = show_slur; 20 | this.slur = null; 21 | 22 | this.formatter = new Vex.Flow.Formatter(); 23 | this.voice = new Vex.Flow.Voice({ 24 | num_beats: 4, 25 | beat_value: 4, 26 | resolution: Vex.Flow.RESOLUTION 27 | }).setStrict(false); 28 | 29 | this.voice.addTickables(this.grace_notes); 30 | 31 | return this; 32 | }, 33 | 34 | preFormat: function(){ 35 | if (this.preFormatted) return; 36 | 37 | this.formatter.joinVoices([this.voice]).format([this.voice], 0); 38 | this.setWidth(this.formatter.getMinTotalWidth()); 39 | 40 | this.preFormatted = true; 41 | }, 42 | 43 | beamNotes: function(){ 44 | if (this.grace_notes.length > 1) { 45 | var beam = new Vex.Flow.Beam(this.grace_notes); 46 | 47 | beam.render_options.beam_width = 3; 48 | beam.render_options.partial_beam_length = 4; 49 | 50 | this.beam = beam; 51 | } 52 | 53 | return this; 54 | }, 55 | 56 | setNote: function(note) { 57 | this.note = note; 58 | }, 59 | getCategory: function() { return "gracenotegroups"; }, 60 | setWidth: function(width){ 61 | this.width = width; 62 | }, 63 | getWidth: function(){ 64 | return this.width; 65 | }, 66 | setXShift: function(x_shift) { 67 | this.x_shift = x_shift; 68 | }, 69 | draw: function() { 70 | if (!this.context) { 71 | throw new Vex.RuntimeError("NoContext", 72 | "Can't draw Grace note without a context."); 73 | } 74 | 75 | var note = this.getNote(); 76 | 77 | if (!(note && (this.index !== null))) { 78 | throw new Vex.RuntimeError("NoAttachedNote", 79 | "Can't draw grace note without a parent note and parent note index."); 80 | } 81 | 82 | function alignGraceNotesWithNote(grace_notes, note) { 83 | // Shift over the tick contexts of each note 84 | // So that th aligned with the note 85 | var tickContext = note.getTickContext(); 86 | var extraPx = tickContext.getExtraPx(); 87 | var x = tickContext.getX() - extraPx.left - extraPx.extraLeft; 88 | grace_notes.forEach(function(graceNote) { 89 | var tick_context = graceNote.getTickContext(); 90 | var x_offset = tick_context.getX(); 91 | graceNote.setStave(note.stave); 92 | tick_context.setX(x + x_offset); 93 | }); 94 | } 95 | 96 | alignGraceNotesWithNote(this.grace_notes, note); 97 | 98 | // Draw notes 99 | this.grace_notes.forEach(function(graceNote) { 100 | graceNote.setContext(this.context).draw(); 101 | }, this); 102 | 103 | // Draw beam 104 | if (this.beam) { 105 | this.beam.setContext(this.context).draw(); 106 | } 107 | 108 | if (this.show_slur) { 109 | // Create and draw slur 110 | this.slur = new Vex.Flow.StaveTie({ 111 | last_note: this.grace_notes[0], 112 | first_note: note, 113 | first_indices: [0], 114 | last_indices: [0] 115 | }); 116 | 117 | this.slur.render_options.cp2 = 12; 118 | this.slur.setContext(this.context).draw(); 119 | } 120 | } 121 | }); 122 | 123 | return GraceNoteGroup; 124 | }()); -------------------------------------------------------------------------------- /src/header.js: -------------------------------------------------------------------------------- 1 | /** 2 | * VexFlow Engraver <%= BUILD_VERSION %> 3 | * A library for rendering musical notation and guitar tablature in HTML5. 4 | * 5 | * http://www.vexflow.com 6 | * 7 | * Copyright (c) 2010 Mohit Muthanna Cheppudira 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | * 27 | * This library makes use of Simon Tatham's awesome font - Gonville. 28 | * 29 | * Build ID: <%= BUILD_PREFIX %>@<%= BUILD_COMMIT %> 30 | * Build date: <%= BUILD_DATE %> 31 | */ 32 | -------------------------------------------------------------------------------- /src/keymanager.js: -------------------------------------------------------------------------------- 1 | // VexFlow - Music Engraving for HTML5 2 | // Copyright Mohit Muthanna 2010 3 | // 4 | // This class implements diatonic key management. 5 | // 6 | // requires: vex.js (Vex) 7 | // requires: flow.js (Vex.Flow) 8 | // requires: music.js (Vex.Flow.Music) 9 | 10 | /** 11 | * @constructor 12 | */ 13 | Vex.Flow.KeyManager = (function() { 14 | function KeyManager(key) { 15 | this.init(key); 16 | } 17 | 18 | KeyManager.scales = { 19 | "M": Vex.Flow.Music.scales.major, 20 | "m": Vex.Flow.Music.scales.minor 21 | }; 22 | 23 | KeyManager.prototype = { 24 | init: function(key) { 25 | this.music = new Vex.Flow.Music(); 26 | this.setKey(key); 27 | }, 28 | 29 | setKey: function(key) { 30 | this.key = key; 31 | this.reset(); 32 | return this; 33 | }, 34 | 35 | getKey: function() { return this.key; }, 36 | 37 | reset: function() { 38 | this.keyParts = this.music.getKeyParts(this.key); 39 | 40 | this.keyString = this.keyParts.root; 41 | if (this.keyParts.accidental) this.keyString += this.keyParts.accidental; 42 | 43 | var is_supported_type = KeyManager.scales[this.keyParts.type]; 44 | if (!is_supported_type) 45 | throw new Vex.RERR("BadArguments", "Unsupported key type: " + this.key); 46 | 47 | this.scale = this.music.getScaleTones( 48 | this.music.getNoteValue(this.keyString), 49 | Vex.Flow.KeyManager.scales[this.keyParts.type]); 50 | 51 | this.scaleMap = {}; 52 | this.scaleMapByValue = {}; 53 | this.originalScaleMapByValue = {}; 54 | 55 | var noteLocation = Vex.Flow.Music.root_indices[this.keyParts.root]; 56 | 57 | for (var i = 0; i < Vex.Flow.Music.roots.length; ++i) { 58 | var index = (noteLocation + i) % Vex.Flow.Music.roots.length; 59 | var rootName = Vex.Flow.Music.roots[index]; 60 | 61 | var noteName = this.music.getRelativeNoteName(rootName, this.scale[i]); 62 | this.scaleMap[rootName] = noteName; 63 | this.scaleMapByValue[this.scale[i]] = noteName; 64 | this.originalScaleMapByValue[this.scale[i]] = noteName; 65 | } 66 | 67 | return this; 68 | }, 69 | 70 | getAccidental: function(key) { 71 | var root = this.music.getKeyParts(key).root; 72 | var parts = this.music.getNoteParts(this.scaleMap[root]); 73 | 74 | return { 75 | note: this.scaleMap[root], 76 | accidental: parts.accidental 77 | }; 78 | }, 79 | 80 | selectNote: function(note) { 81 | note = note.toLowerCase(); 82 | var parts = this.music.getNoteParts(note); 83 | 84 | // First look for matching note in our altered scale 85 | var scaleNote = this.scaleMap[parts.root]; 86 | var modparts = this.music.getNoteParts(scaleNote); 87 | 88 | if (scaleNote == note) return { 89 | "note": scaleNote, 90 | "accidental": parts.accidental, 91 | "change": false 92 | }; 93 | 94 | // Then search for a note of equivalent value in our altered scale 95 | var valueNote = this.scaleMapByValue[this.music.getNoteValue(note)]; 96 | if (valueNote != null) { 97 | return { 98 | "note": valueNote, 99 | "accidental": this.music.getNoteParts(valueNote).accidental, 100 | "change": false 101 | }; 102 | } 103 | 104 | // Then search for a note of equivalent value in the original scale 105 | var originalValueNote = this.originalScaleMapByValue[ 106 | this.music.getNoteValue(note)]; 107 | if (originalValueNote != null) { 108 | this.scaleMap[modparts.root] = originalValueNote; 109 | delete this.scaleMapByValue[this.music.getNoteValue(scaleNote)]; 110 | this.scaleMapByValue[this.music.getNoteValue(note)] = originalValueNote; 111 | return { 112 | "note": originalValueNote, 113 | "accidental": this.music.getNoteParts(originalValueNote).accidental, 114 | "change": true 115 | }; 116 | } 117 | 118 | // Then try to unmodify a currently modified note. 119 | if (modparts.root == note) { 120 | delete this.scaleMapByValue[ 121 | this.music.getNoteValue(this.scaleMap[parts.root])]; 122 | this.scaleMapByValue[this.music.getNoteValue(modparts.root)] = 123 | modparts.root; 124 | this.scaleMap[modparts.root] = modparts.root; 125 | return { 126 | "note": modparts.root, 127 | "accidental": null, 128 | "change": true 129 | }; 130 | } 131 | 132 | // Last resort -- shitshoot 133 | delete this.scaleMapByValue[ 134 | this.music.getNoteValue(this.scaleMap[parts.root])]; 135 | this.scaleMapByValue[this.music.getNoteValue(note)] = note; 136 | 137 | delete this.scaleMap[modparts.root]; 138 | this.scaleMap[modparts.root] = note; 139 | 140 | return { 141 | "note": note, 142 | "accidental": parts.accidental, 143 | "change": true 144 | }; 145 | } 146 | }; 147 | 148 | return KeyManager; 149 | }()); 150 | -------------------------------------------------------------------------------- /src/keysignature.js: -------------------------------------------------------------------------------- 1 | // Vex Flow Notation 2 | // Implements key signatures 3 | // 4 | // Requires vex.js. 5 | 6 | /** 7 | * @constructor 8 | */ 9 | Vex.Flow.KeySignature = (function() { 10 | function KeySignature(keySpec) { 11 | if (arguments.length > 0) this.init(keySpec); 12 | } 13 | 14 | Vex.Inherit(KeySignature, Vex.Flow.StaveModifier, { 15 | init: function(key_spec) { 16 | KeySignature.superclass.init(); 17 | 18 | this.glyphFontScale = 38; // TODO(0xFE): Should this match StaveNote? 19 | this.accList = Vex.Flow.keySignature(key_spec); 20 | }, 21 | 22 | addAccToStave: function(stave, acc) { 23 | var glyph = new Vex.Flow.Glyph(acc.glyphCode, this.glyphFontScale); 24 | this.placeGlyphOnLine(glyph, stave, acc.line); 25 | stave.addGlyph(glyph); 26 | }, 27 | 28 | addModifier: function(stave) { 29 | this.convertAccLines(stave.clef, this.accList[0].glyphCode); 30 | for (var i = 0; i < this.accList.length; ++i) { 31 | this.addAccToStave(stave, this.accList[i]); 32 | } 33 | }, 34 | 35 | addToStave: function(stave, firstGlyph) { 36 | if (this.accList.length === 0) 37 | return this; 38 | 39 | if (!firstGlyph) { 40 | stave.addGlyph(this.makeSpacer(this.padding)); 41 | } 42 | 43 | this.addModifier(stave); 44 | return this; 45 | }, 46 | 47 | convertAccLines: function(clef, code) { 48 | var offset = 0.0; // if clef === "treble" 49 | var tenorSharps; 50 | var isTenorSharps = ((clef === "tenor") && (code === "v18")) ? true : false; 51 | 52 | switch (clef) { 53 | case "bass": 54 | offset = 1; 55 | break; 56 | case "alto": 57 | offset = 0.5; 58 | break; 59 | case "tenor": 60 | if (!isTenorSharps) { 61 | offset = -0.5; 62 | } 63 | break; 64 | } 65 | 66 | // Special-case for TenorSharps 67 | var i; 68 | if (isTenorSharps) { 69 | tenorSharps = [3, 1, 2.5, 0.5, 2, 0, 1.5]; 70 | for (i = 0; i < this.accList.length; ++i) { 71 | this.accList[i].line = tenorSharps[i]; 72 | } 73 | } 74 | else { 75 | if (clef != "treble") { 76 | for (i = 0; i < this.accList.length; ++i) { 77 | this.accList[i].line += offset; 78 | } 79 | } 80 | } 81 | } 82 | }); 83 | 84 | return KeySignature; 85 | }()); -------------------------------------------------------------------------------- /src/modifier.js: -------------------------------------------------------------------------------- 1 | // [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010. 2 | // 3 | // ## Description 4 | // 5 | // `Modifier` is an abstract interface for notational elements that modify 6 | // a `Note`. Examples of modifiers are `Accidental`, `Annotation`, `Stroke`, etc. 7 | // 8 | // For a `Modifier` instance to be positioned correctly, it must be part of 9 | // a `ModifierContext`. All modifiers in the same context are rendered relative to 10 | // one another. 11 | // 12 | // Typically, all modifiers to a note are part of the same `ModifierContext` instance. Also, 13 | // in multi-voice staves, all modifiers to notes on the same `tick` are part of the same 14 | // `ModifierContext`. This ensures that multiple voices don't trample all over each other. 15 | 16 | Vex.Flow.Modifier = (function() { 17 | function Modifier() { this.init(); } 18 | // To enable logging for this class. Set `Vex.Flow.Modifier.DEBUG` to `true`. 19 | function L() { if (Modifier.DEBUG) Vex.L("Vex.Flow.Modifier", arguments); } 20 | 21 | // Modifiers can be positioned almost anywhere, relative to a note. 22 | Modifier.Position = { 23 | LEFT: 1, 24 | RIGHT: 2, 25 | ABOVE: 3, 26 | BELOW: 4 27 | }; 28 | 29 | // ## Prototype Methods 30 | Modifier.prototype = { 31 | 32 | // The constructor sets initial widhts and constants. 33 | init: function() { 34 | this.width = 0; 35 | this.context = null; 36 | 37 | // Modifiers are attached to a note and an index. An index is a 38 | // specific head in a chord. 39 | this.note = null; 40 | this.index = null; 41 | 42 | // The `text_line` is reserved space above or below a stave. 43 | this.text_line = 0; 44 | this.position = Modifier.Position.LEFT; 45 | this.modifier_context = null; 46 | this.x_shift = 0; 47 | this.y_shift = 0; 48 | L("Created new modifier"); 49 | }, 50 | 51 | // Every modifier has a category. The `ModifierContext` uses this to determine 52 | // the type and order of the modifiers. 53 | getCategory: function() { return "none"; }, 54 | 55 | // Get and set modifier widths. 56 | getWidth: function() { return this.width; }, 57 | setWidth: function(width) { this.width = width; return this; }, 58 | 59 | // Get and set attached note (`StaveNote`, `TabNote`, etc.) 60 | getNote: function() { return this.note; }, 61 | setNote: function(note) { this.note = note; return this; }, 62 | 63 | // Get and set note index, which is a specific note in a chord. 64 | getIndex: function() { return this.index; }, 65 | setIndex: function(index) { this.index = index; return this; }, 66 | 67 | // Get and set rendering context. 68 | getContext: function() { return this.context; }, 69 | setContext: function(context) { this.context = context; return this; }, 70 | 71 | // Every modifier must be part of a `ModifierContext`. 72 | getModifierContext: function() { return this.modifier_context; }, 73 | setModifierContext: function(c) { this.modifier_context = c; return this; }, 74 | 75 | // Get and set articulation position. 76 | getPosition: function() { return this.position; }, 77 | setPosition: function(position) { this.position = position; return this; }, 78 | 79 | // Set the `text_line` for the modifier. 80 | setTextLine: function(line) { this.text_line = line; return this; }, 81 | 82 | // Shift modifier down `y` pixels. Negative values shift up. 83 | setYShift: function(y) { this.y_shift = y; return this; }, 84 | 85 | // Shift modifier `x` pixels in the direction of the modifier. Negative values 86 | // shift reverse. 87 | setXShift: function(x) { 88 | this.x_shift = 0; 89 | if (this.position == Modifier.Position.LEFT) { 90 | this.x_shift -= x; 91 | } else { 92 | this.x_shift += x; 93 | } 94 | }, 95 | 96 | // Render the modifier onto the canvas. 97 | draw: function() { 98 | if (!this.context) throw new Vex.RERR("NoCanvasContext", 99 | "Can't draw without a canvas context."); 100 | throw new Vex.RERR("MethodNotImplemented", 101 | "Draw() not implemented for this modifier."); 102 | } 103 | }; 104 | 105 | return Modifier; 106 | }()); 107 | -------------------------------------------------------------------------------- /src/renderer.js: -------------------------------------------------------------------------------- 1 | // Vex Flow 2 | // Mohit Muthanna 3 | // 4 | // Support for different rendering contexts: Canvas, Raphael 5 | // 6 | // Copyright Mohit Cheppudira 2010 7 | 8 | /* global document: false */ 9 | 10 | Vex.Flow.Renderer = (function() { 11 | function Renderer(sel, backend) { 12 | if (arguments.length > 0) this.init(sel, backend); 13 | } 14 | 15 | Renderer.Backends = { 16 | CANVAS: 1, 17 | RAPHAEL: 2, 18 | SVG: 3, 19 | VML: 4 20 | }; 21 | 22 | //End of line types 23 | Renderer.LineEndType = { 24 | NONE: 1, // No leg 25 | UP: 2, // Upward leg 26 | DOWN: 3 // Downward leg 27 | }; 28 | 29 | // Set this to true if you're using VexFlow inside a runtime 30 | // that does not allow modifiying canvas objects. There is a small 31 | // performance degradation due to the extra indirection. 32 | Renderer.USE_CANVAS_PROXY = false; 33 | 34 | Renderer.buildContext = function(sel, 35 | backend, width, height, background) { 36 | var renderer = new Renderer(sel, backend); 37 | if (width && height) { renderer.resize(width, height); } 38 | 39 | if (!background) background = "#eed"; 40 | var ctx = renderer.getContext(); 41 | ctx.setBackgroundFillStyle(background); 42 | return ctx; 43 | }; 44 | 45 | Renderer.getCanvasContext = function(sel, width, height, background) { 46 | return Renderer.buildContext(sel, Renderer.Backends.CANVAS, 47 | width, height, background); 48 | }; 49 | 50 | Renderer.getRaphaelContext = function(sel, width, height, background) { 51 | return Renderer.buildContext(sel, Renderer.Backends.RAPHAEL, 52 | width, height, background); 53 | }; 54 | 55 | Renderer.bolsterCanvasContext = function(ctx) { 56 | if (Renderer.USE_CANVAS_PROXY) { 57 | return new Vex.Flow.CanvasContext(ctx); 58 | } 59 | 60 | var methods = ["clear", "setFont", "setRawFont", "setFillStyle", "setBackgroundFillStyle", 61 | "setStrokeStyle", "setShadowColor", "setShadowBlur", "setLineWidth", 62 | "setLineCap", "setLineDash"]; 63 | ctx.vexFlowCanvasContext = ctx; 64 | 65 | for (var i in methods) { 66 | var method = methods[i]; 67 | ctx[method] = Vex.Flow.CanvasContext.prototype[method]; 68 | } 69 | 70 | return ctx; 71 | }; 72 | 73 | //Draw a dashed line (horizontal, vertical or diagonal 74 | //dashPattern = [3,3] draws a 3 pixel dash followed by a three pixel space. 75 | //setting the second number to 0 draws a solid line. 76 | Renderer.drawDashedLine = function(context, fromX, fromY, toX, toY, dashPattern) { 77 | context.beginPath(); 78 | 79 | var dx = toX - fromX; 80 | var dy = toY - fromY; 81 | var angle = Math.atan2(dy, dx); 82 | var x = fromX; 83 | var y = fromY; 84 | context.moveTo(fromX, fromY); 85 | var idx = 0; 86 | var draw = true; 87 | while (!((dx < 0 ? x <= toX : x >= toX) && (dy < 0 ? y <= toY : y >= toY))) { 88 | var dashLength = dashPattern[idx++ % dashPattern.length]; 89 | var nx = x + (Math.cos(angle) * dashLength); 90 | x = dx < 0 ? Math.max(toX, nx) : Math.min(toX, nx); 91 | var ny = y + (Math.sin(angle) * dashLength); 92 | y = dy < 0 ? Math.max(toY, ny) : Math.min(toY, ny); 93 | if (draw) { 94 | context.lineTo(x, y); 95 | } else { 96 | context.moveTo(x, y); 97 | } 98 | draw = !draw; 99 | } 100 | 101 | context.closePath(); 102 | context.stroke(); 103 | }; 104 | 105 | Renderer.prototype = { 106 | init: function(sel, backend) { 107 | // Verify selector 108 | this.sel = sel; 109 | if (!this.sel) throw new Vex.RERR("BadArgument", 110 | "Invalid selector for renderer."); 111 | 112 | // Get element from selector 113 | this.element = document.getElementById(sel); 114 | if (!this.element) this.element = sel; 115 | 116 | // Verify backend and create context 117 | this.ctx = null; 118 | this.paper = null; 119 | this.backend = backend; 120 | if (this.backend == Renderer.Backends.CANVAS) { 121 | // Create context. 122 | if (!this.element.getContext) throw new Vex.RERR("BadElement", 123 | "Can't get canvas context from element: " + sel); 124 | this.ctx = Renderer.bolsterCanvasContext( 125 | this.element.getContext('2d')); 126 | } else if (this.backend == Renderer.Backends.RAPHAEL) { 127 | this.ctx = new Vex.Flow.RaphaelContext(this.element); 128 | } else { 129 | throw new Vex.RERR("InvalidBackend", 130 | "No support for backend: " + this.backend); 131 | } 132 | }, 133 | 134 | resize: function(width, height) { 135 | if (this.backend == Renderer.Backends.CANVAS) { 136 | if (!this.element.getContext) throw new Vex.RERR("BadElement", 137 | "Can't get canvas context from element: " + this.sel); 138 | this.element.width = width; 139 | this.element.height = height; 140 | this.ctx = Renderer.bolsterCanvasContext( 141 | this.element.getContext('2d')); 142 | } else { 143 | this.ctx.resize(width, height); 144 | } 145 | 146 | return this; 147 | }, 148 | 149 | getContext: function() { return this.ctx; } 150 | }; 151 | 152 | return Renderer; 153 | }()); 154 | 155 | 156 | -------------------------------------------------------------------------------- /src/stavebarline.js: -------------------------------------------------------------------------------- 1 | // Vex Flow Notation 2 | // Author Larry Kuhns 2011 3 | // Implements barlines (single, double, repeat, end) 4 | // 5 | // Requires vex.js. 6 | 7 | /** 8 | * @constructor 9 | */ 10 | Vex.Flow.Barline = (function() { 11 | function Barline(type, x) { 12 | if (arguments.length > 0) this.init(type, x); 13 | } 14 | 15 | Barline.type = { 16 | SINGLE: 1, 17 | DOUBLE: 2, 18 | END: 3, 19 | REPEAT_BEGIN: 4, 20 | REPEAT_END: 5, 21 | REPEAT_BOTH: 6, 22 | NONE: 7 23 | }; 24 | 25 | var THICKNESS = Vex.Flow.STAVE_LINE_THICKNESS; 26 | 27 | Vex.Inherit(Barline, Vex.Flow.StaveModifier, { 28 | init: function(type, x) { 29 | Barline.superclass.init.call(this); 30 | this.barline = type; 31 | this.x = x; // Left most x for the stave 32 | }, 33 | 34 | getCategory: function() { return "barlines"; }, 35 | setX: function(x) { this.x = x; return this; }, 36 | 37 | // Draw barlines 38 | draw: function(stave, x_shift) { 39 | x_shift = typeof x_shift !== 'number' ? 0 : x_shift; 40 | 41 | switch (this.barline) { 42 | case Barline.type.SINGLE: 43 | this.drawVerticalBar(stave, this.x, false); 44 | break; 45 | case Barline.type.DOUBLE: 46 | this.drawVerticalBar(stave, this.x, true); 47 | break; 48 | case Barline.type.END: 49 | this.drawVerticalEndBar(stave, this.x); 50 | break; 51 | case Barline.type.REPEAT_BEGIN: 52 | // If the barline is shifted over (in front of clef/time/key) 53 | // Draw vertical bar at the beginning. 54 | if (x_shift > 0) { 55 | this.drawVerticalBar(stave, this.x); 56 | } 57 | this.drawRepeatBar(stave, this.x + x_shift, true); 58 | break; 59 | case Barline.type.REPEAT_END: 60 | this.drawRepeatBar(stave, this.x, false); 61 | break; 62 | case Barline.type.REPEAT_BOTH: 63 | this.drawRepeatBar(stave, this.x, false); 64 | this.drawRepeatBar(stave, this.x, true); 65 | break; 66 | default: 67 | // Default is NONE, so nothing to draw 68 | break; 69 | } 70 | }, 71 | 72 | drawVerticalBar: function(stave, x, double_bar) { 73 | if (!stave.context) throw new Vex.RERR("NoCanvasContext", 74 | "Can't draw stave without canvas context."); 75 | var top_line = stave.getYForLine(0); 76 | var bottom_line = stave.getYForLine(stave.options.num_lines - 1) + (THICKNESS / 2); 77 | if (double_bar) 78 | stave.context.fillRect(x - 3, top_line, 1, bottom_line - top_line + 1); 79 | stave.context.fillRect(x, top_line, 1, bottom_line - top_line + 1); 80 | }, 81 | 82 | drawVerticalEndBar: function(stave, x) { 83 | if (!stave.context) throw new Vex.RERR("NoCanvasContext", 84 | "Can't draw stave without canvas context."); 85 | 86 | var top_line = stave.getYForLine(0); 87 | var bottom_line = stave.getYForLine(stave.options.num_lines - 1) + (THICKNESS / 2); 88 | stave.context.fillRect(x - 5, top_line, 1, bottom_line - top_line + 1); 89 | stave.context.fillRect(x - 2, top_line, 3, bottom_line - top_line + 1); 90 | }, 91 | 92 | drawRepeatBar: function(stave, x, begin) { 93 | if (!stave.context) throw new Vex.RERR("NoCanvasContext", 94 | "Can't draw stave without canvas context."); 95 | 96 | var top_line = stave.getYForLine(0); 97 | var bottom_line = stave.getYForLine(stave.options.num_lines - 1) + (THICKNESS / 2); 98 | var x_shift = 3; 99 | 100 | if (!begin) { 101 | x_shift = -5; 102 | } 103 | 104 | stave.context.fillRect(x + x_shift, top_line, 1, bottom_line - top_line + 1); 105 | stave.context.fillRect(x - 2, top_line, 3, bottom_line - top_line + 1); 106 | 107 | var dot_radius = 2; 108 | 109 | // Shift dots left or right 110 | if (begin) { 111 | x_shift += 4; 112 | } else { 113 | x_shift -= 4; 114 | } 115 | 116 | var dot_x = (x + x_shift) + (dot_radius / 2); 117 | 118 | // calculate the y offset based on number of stave lines 119 | var y_offset = (stave.options.num_lines -1) * 120 | stave.options.spacing_between_lines_px; 121 | y_offset = (y_offset / 2) - 122 | (stave.options.spacing_between_lines_px / 2); 123 | var dot_y = top_line + y_offset + (dot_radius / 2); 124 | 125 | // draw the top repeat dot 126 | stave.context.beginPath(); 127 | stave.context.arc(dot_x, dot_y, dot_radius, 0, Math.PI * 2, false); 128 | stave.context.fill(); 129 | 130 | //draw the bottom repeat dot 131 | dot_y += stave.options.spacing_between_lines_px; 132 | stave.context.beginPath(); 133 | stave.context.arc(dot_x, dot_y, dot_radius, 0, Math.PI * 2, false); 134 | stave.context.fill(); 135 | } 136 | }); 137 | 138 | return Barline; 139 | }()); 140 | -------------------------------------------------------------------------------- /src/stavemodifier.js: -------------------------------------------------------------------------------- 1 | // VexFlow - Music Engraving for HTML5 2 | // 3 | // A base class for stave modifiers (e.g. clefs, key signatures) 4 | // 5 | 6 | 7 | /** 8 | * @constructor 9 | */ 10 | Vex.Flow.StaveModifier = (function() { 11 | function StaveModifier() { 12 | this.init(); 13 | } 14 | 15 | StaveModifier.prototype = { 16 | init: function() { 17 | this.padding = 10; 18 | }, 19 | 20 | getCategory: function() {return "";}, 21 | makeSpacer: function(padding) { 22 | return { 23 | getContext: function() {return true;}, 24 | setStave: function() {}, 25 | renderToStave: function() {}, 26 | getMetrics: function() { 27 | return {width: padding}; 28 | } 29 | }; 30 | }, 31 | 32 | placeGlyphOnLine: function(glyph, stave, line) { 33 | glyph.setYShift(stave.getYForLine(line) - stave.getYForGlyphs()); 34 | }, 35 | 36 | setPadding: function(padding) { 37 | this.padding = padding; 38 | }, 39 | 40 | addToStave: function(stave, firstGlyph) { 41 | if (!firstGlyph) { 42 | stave.addGlyph(this.makeSpacer(this.padding)); 43 | } 44 | 45 | this.addModifier(stave); 46 | return this; 47 | }, 48 | 49 | addToStaveEnd: function(stave, firstGlyph) { 50 | if (!firstGlyph) { 51 | stave.addEndGlyph(this.makeSpacer(this.padding)); 52 | } 53 | else { 54 | stave.addEndGlyph(this.makeSpacer(2)); 55 | } 56 | 57 | this.addEndModifier(stave); 58 | return this; 59 | }, 60 | 61 | addModifier: function() { 62 | throw new Vex.RERR("MethodNotImplemented", 63 | "addModifier() not implemented for this stave modifier."); 64 | }, 65 | 66 | addEndModifier: function() { 67 | throw new Vex.RERR("MethodNotImplemented", 68 | "addEndModifier() not implemented for this stave modifier."); 69 | } 70 | }; 71 | 72 | return StaveModifier; 73 | }()); 74 | 75 | -------------------------------------------------------------------------------- /src/staverepetition.js: -------------------------------------------------------------------------------- 1 | // Vex Flow Notation 2 | // Author Larry Kuhns 2011 3 | // Implements Repetitions (Coda, signo, D.C., etc.) 4 | // 5 | // Requires vex.js. 6 | 7 | Vex.Flow.Repetition = (function() { 8 | function Repetition(type, x, y_shift) { 9 | if (arguments.length > 0) this.init(type, x, y_shift); 10 | } 11 | 12 | Repetition.type = { 13 | NONE: 1, // no coda or segno 14 | CODA_LEFT: 2, // coda at beginning of stave 15 | CODA_RIGHT: 3, // coda at end of stave 16 | SEGNO_LEFT: 4, // segno at beginning of stave 17 | SEGNO_RIGHT: 5, // segno at end of stave 18 | DC: 6, // D.C. at end of stave 19 | DC_AL_CODA: 7, // D.C. al coda at end of stave 20 | DC_AL_FINE: 8, // D.C. al Fine end of stave 21 | DS: 9, // D.S. at end of stave 22 | DS_AL_CODA: 10, // D.S. al coda at end of stave 23 | DS_AL_FINE: 11, // D.S. al Fine at end of stave 24 | FINE: 12 // Fine at end of stave 25 | }; 26 | 27 | Vex.Inherit(Repetition, Vex.Flow.StaveModifier, { 28 | init: function(type, x, y_shift) { 29 | Repetition.superclass.init.call(this); 30 | 31 | this.symbol_type = type; 32 | this.x = x; 33 | this.x_shift = 0; 34 | this.y_shift = y_shift; 35 | this.font = { 36 | family: "times", 37 | size: 12, 38 | weight: "bold italic" 39 | }; 40 | }, 41 | 42 | getCategory: function() { return "repetitions"; }, 43 | setShiftX: function(x) { this.x_shift = x; return this; }, 44 | setShiftY: function(y) { this.y_shift = y; return this; }, 45 | 46 | draw: function(stave, x) { 47 | switch (this.symbol_type) { 48 | case Repetition.type.CODA_RIGHT: 49 | this.drawCodaFixed(stave, x + stave.width); 50 | break; 51 | case Repetition.type.CODA_LEFT: 52 | this.drawSymbolText(stave, x, "Coda", true); 53 | break; 54 | case Repetition.type.SEGNO_LEFT: 55 | this.drawSignoFixed(stave, x); 56 | break; 57 | case Repetition.type.SEGNO_RIGHT: 58 | this.drawSignoFixed(stave, x + stave.width); 59 | break; 60 | case Repetition.type.DC: 61 | this.drawSymbolText(stave, x, "D.C.", false); 62 | break; 63 | case Repetition.type.DC_AL_CODA: 64 | this.drawSymbolText(stave, x, "D.C. al", true); 65 | break; 66 | case Repetition.type.DC_AL_FINE: 67 | this.drawSymbolText(stave, x, "D.C. al Fine", false); 68 | break; 69 | case Repetition.type.DS: 70 | this.drawSymbolText(stave, x, "D.S.", false); 71 | break; 72 | case Repetition.type.DS_AL_CODA: 73 | this.drawSymbolText(stave, x, "D.S. al", true); 74 | break; 75 | case Repetition.type.DS_AL_FINE: 76 | this.drawSymbolText(stave, x, "D.S. al Fine", false); 77 | break; 78 | case Repetition.type.FINE: 79 | this.drawSymbolText(stave, x, "Fine", false); 80 | break; 81 | default: 82 | break; 83 | } 84 | 85 | return this; 86 | }, 87 | 88 | drawCodaFixed: function(stave, x) { 89 | if (!stave.context) throw new Vex.RERR("NoCanvasContext", 90 | "Can't draw stave without canvas context."); 91 | 92 | var y = stave.getYForTopText(stave.options.num_lines) + this.y_shift; 93 | Vex.Flow.renderGlyph(stave.context, this.x + x + this.x_shift, 94 | y + 25, 40, "v4d", true); 95 | return this; 96 | }, 97 | 98 | drawSignoFixed: function(stave, x) { 99 | if (!stave.context) throw new Vex.RERR("NoCanvasContext", 100 | "Can't draw stave without canvas context."); 101 | var y = stave.getYForTopText(stave.options.num_lines) + this.y_shift; 102 | Vex.Flow.renderGlyph(stave.context, this.x + x + this.x_shift, 103 | y + 25, 30, "v8c", true); 104 | return this; 105 | }, 106 | 107 | drawSymbolText: function(stave, x, text, draw_coda) { 108 | if (!stave.context) throw new Vex.RERR("NoCanvasContext", 109 | "Can't draw stave without canvas context."); 110 | 111 | var ctx = stave.context; 112 | ctx.save(); 113 | ctx.setFont(this.font.family, this.font.size, this.font.weight); 114 | // Default to right symbol 115 | var text_x = 0 + this.x_shift; 116 | var symbol_x = x + this.x_shift; 117 | if (this.symbol_type == Vex.Flow.Repetition.type.CODA_LEFT) { 118 | // Offset Coda text to right of stave beginning 119 | text_x = this.x + stave.options.vertical_bar_width; 120 | symbol_x = text_x + ctx.measureText(text).width + 12; 121 | } else { 122 | // Offset Signo text to left stave end 123 | symbol_x = this.x + x + stave.width - 5 + this.x_shift; 124 | text_x = symbol_x - + ctx.measureText(text).width - 12; 125 | } 126 | var y = stave.getYForTopText(stave.options.num_lines) + this.y_shift; 127 | if (draw_coda) { 128 | Vex.Flow.renderGlyph(ctx, symbol_x, y, 40, "v4d", true); 129 | } 130 | 131 | ctx.fillText(text, text_x, y + 5); 132 | ctx.restore(); 133 | 134 | return this; 135 | } 136 | }); 137 | 138 | return Repetition; 139 | }()); -------------------------------------------------------------------------------- /src/stavesection.js: -------------------------------------------------------------------------------- 1 | // VexFlow - Music Engraving for HTML5 2 | // Copyright Mohit Muthanna 2010 3 | // Author Larry Kuhns 2011 4 | // Implements stave section names. 5 | 6 | /** 7 | * @constructor 8 | */ 9 | Vex.Flow.StaveSection = (function() { 10 | function StaveSection(section, x, shift_y) { 11 | if (arguments.length > 0) this.init(section, x, shift_y); 12 | } 13 | 14 | var Modifier = Vex.Flow.Modifier; 15 | Vex.Inherit(StaveSection, Modifier, { 16 | init: function(section, x, shift_y) { 17 | StaveSection.superclass.init.call(this); 18 | 19 | this.setWidth(16); 20 | this.section = section; 21 | this.position = Modifier.Position.ABOVE; 22 | this.x = x; 23 | this.shift_x = 0; 24 | this.shift_y = shift_y; 25 | this.font = { 26 | family: "sans-serif", 27 | size: 12, 28 | weight: "bold" 29 | }; 30 | }, 31 | 32 | getCategory: function() { return "stavesection"; }, 33 | setStaveSection: function(section) { this.section = section; return this; }, 34 | setShiftX: function(x) { this.shift_x = x; return this; }, 35 | setShiftY: function(y) { this.shift_y = y; return this; }, 36 | 37 | draw: function(stave, shift_x) { 38 | if (!stave.context) throw new Vex.RERR("NoContext", 39 | "Can't draw stave section without a context."); 40 | 41 | var ctx = stave.context; 42 | 43 | ctx.save(); 44 | ctx.lineWidth = 2; 45 | ctx.setFont(this.font.family, this.font.size, this.font.weight); 46 | var text_width = ctx.measureText("" + this.section).width; 47 | var width = text_width + 6; // add left & right padding 48 | if (width < 18) width = 18; 49 | var height = 20; 50 | // Seems to be a good default y 51 | var y = stave.getYForTopText(3) + this.shift_y; 52 | var x = this.x + shift_x; 53 | ctx.beginPath(); 54 | ctx.lineWidth = 2; 55 | ctx.rect(x, y, width, height); 56 | ctx.stroke(); 57 | x += (width - text_width) / 2; 58 | ctx.fillText("" + this.section, x, y + 16); 59 | ctx.restore(); 60 | return this; 61 | } 62 | }); 63 | 64 | return StaveSection; 65 | }()); -------------------------------------------------------------------------------- /src/stavetempo.js: -------------------------------------------------------------------------------- 1 | // VexFlow - Music Engraving for HTML5 2 | // Copyright Mohit Muthanna 2010 3 | // Author Radosaw Eichler 2012 4 | // Implements tempo marker. 5 | 6 | /** 7 | * @constructor 8 | * @param {Object} tempo Tempo parameters: { name, duration, dots, bpm } 9 | */ 10 | Vex.Flow.StaveTempo = (function() { 11 | function StaveTempo(tempo, x, shift_y) { 12 | if (arguments.length > 0) this.init(tempo, x, shift_y); 13 | } 14 | 15 | Vex.Inherit(StaveTempo, Vex.Flow.StaveModifier, { 16 | init: function(tempo, x, shift_y) { 17 | StaveTempo.superclass.init.call(this); 18 | 19 | this.tempo = tempo; 20 | this.position = Vex.Flow.Modifier.Position.ABOVE; 21 | this.x = x; 22 | this.shift_x = 10; 23 | this.shift_y = shift_y; 24 | this.font = { 25 | family: "times", 26 | size: 14, 27 | weight: "bold" 28 | }; 29 | this.render_options = { 30 | glyph_font_scale: 30 // font size for note 31 | }; 32 | }, 33 | 34 | getCategory: function() { return "stavetempo"; }, 35 | setTempo: function(tempo) { this.tempo = tempo; return this; }, 36 | setShiftX: function(x) { this.shift_x = x; return this; }, 37 | setShiftY: function(y) { this.shift_y = y; return this; }, 38 | 39 | draw: function(stave, shift_x) { 40 | if (!stave.context) throw new Vex.RERR("NoContext", 41 | "Can't draw stave tempo without a context."); 42 | 43 | var options = this.render_options; 44 | var scale = options.glyph_font_scale / 38; 45 | var name = this.tempo.name; 46 | var duration = this.tempo.duration; 47 | var dots = this.tempo.dots; 48 | var bpm = this.tempo.bpm; 49 | var font = this.font; 50 | var ctx = stave.context; 51 | var x = this.x + this.shift_x + shift_x; 52 | var y = stave.getYForTopText(1) + this.shift_y; 53 | 54 | ctx.save(); 55 | 56 | if (name) { 57 | ctx.setFont(font.family, font.size, font.weight); 58 | ctx.fillText(name, x, y); 59 | x += ctx.measureText(name).width; 60 | } 61 | 62 | if (duration && bpm) { 63 | ctx.setFont(font.family, font.size, 'normal'); 64 | 65 | if (name) { 66 | x += ctx.measureText(" ").width; 67 | ctx.fillText("(", x, y); 68 | x += ctx.measureText("(").width; 69 | } 70 | 71 | var code = Vex.Flow.durationToGlyph(duration); 72 | 73 | x += 3 * scale; 74 | Vex.Flow.renderGlyph(ctx, x, y, options.glyph_font_scale, code.code_head); 75 | x += code.head_width * scale; 76 | 77 | // Draw stem and flags 78 | if (code.stem) { 79 | var stem_height = 30; 80 | 81 | if (code.beam_count) stem_height += 3 * (code.beam_count - 1); 82 | 83 | stem_height *= scale; 84 | 85 | var y_top = y - stem_height; 86 | ctx.fillRect(x, y_top, scale, stem_height); 87 | 88 | if (code.flag) { 89 | Vex.Flow.renderGlyph(ctx, x + scale, y_top, options.glyph_font_scale, 90 | code.code_flag_upstem); 91 | 92 | if (!dots) x += 6 * scale; 93 | } 94 | } 95 | 96 | // Draw dot 97 | for (var i = 0; i < dots; i++) { 98 | x += 6 * scale; 99 | ctx.beginPath(); 100 | ctx.arc(x, y + 2 * scale, 2 * scale, 0, Math.PI * 2, false); 101 | ctx.fill(); 102 | } 103 | 104 | ctx.fillText(" = " + bpm + (name ? ")" : ""), x + 3 * scale, y); 105 | } 106 | 107 | ctx.restore(); 108 | return this; 109 | } 110 | }); 111 | 112 | return StaveTempo; 113 | }()); 114 | -------------------------------------------------------------------------------- /src/stavetext.js: -------------------------------------------------------------------------------- 1 | // VexFlow - Music Engraving for HTML5 2 | // Copyright Mohit Muthanna 2010 3 | // 4 | // Author Taehoon Moon 2014 5 | 6 | /** 7 | * @constructor 8 | */ 9 | Vex.Flow.StaveText = (function() { 10 | function StaveText(text, position, options) { 11 | if (arguments.length > 0) this.init(text, position, options); 12 | } 13 | 14 | var Modifier = Vex.Flow.Modifier; 15 | Vex.Inherit(StaveText, Modifier, { 16 | init: function(text, position, options) { 17 | StaveText.superclass.init.call(this); 18 | 19 | this.setWidth(16); 20 | this.text = text; 21 | this.position = position; 22 | this.options = { 23 | shift_x: 0, 24 | shift_y: 0, 25 | justification: Vex.Flow.TextNote.Justification.CENTER 26 | }; 27 | Vex.Merge(this.options, options); 28 | 29 | this.font = { 30 | family: "times", 31 | size: 16, 32 | weight: "normal" 33 | }; 34 | }, 35 | 36 | getCategory: function() { return "stavetext"; }, 37 | setStaveText: function(text) { this.text = text; return this; }, 38 | setShiftX: function(x) { this.shift_x = x; return this; }, 39 | setShiftY: function(y) { this.shift_y = y; return this; }, 40 | 41 | setFont: function(font) { 42 | Vex.Merge(this.font, font); 43 | }, 44 | 45 | setText: function(text) { 46 | this.text = text; 47 | }, 48 | 49 | draw: function(stave) { 50 | if (!stave.context) throw new Vex.RERR("NoContext", 51 | "Can't draw stave text without a context."); 52 | 53 | var ctx = stave.context; 54 | 55 | ctx.save(); 56 | ctx.lineWidth = 2; 57 | ctx.setFont(this.font.family, this.font.size, this.font.weight); 58 | var text_width = ctx.measureText("" + this.text).width; 59 | 60 | var x, y; 61 | var Modifier = Vex.Flow.Modifier; 62 | switch(this.position) { 63 | case Modifier.Position.LEFT: 64 | case Modifier.Position.RIGHT: 65 | y = (stave.getYForLine(0) + stave.getBottomLineY()) / 2 + this.options.shift_y; 66 | if(this.position == Modifier.Position.LEFT) { 67 | x = stave.getX() - text_width - 24 + this.options.shift_x; 68 | } 69 | else { 70 | x = stave.getX() + stave.getWidth() + 24 + this.options.shift_x; 71 | } 72 | break; 73 | case Modifier.Position.ABOVE: 74 | case Modifier.Position.BELOW: 75 | var Justification = Vex.Flow.TextNote.Justification; 76 | x = stave.getX() + this.options.shift_x; 77 | if(this.options.justification == Justification.CENTER) { 78 | x += stave.getWidth() / 2 - text_width / 2; 79 | } 80 | else if(this.options.justification == Justification.RIGHT) { 81 | x += stave.getWidth() - text_width; 82 | } 83 | 84 | if(this.position == Modifier.Position.ABOVE) { 85 | y = stave.getYForTopText(2) + this.options.shift_y; 86 | } 87 | else { 88 | y = stave.getYForBottomText(2) + this.options.shift_y; 89 | } 90 | break; 91 | default: 92 | throw new Vex.RERR("InvalidPosition", 93 | "Value Must be in Modifier.Position."); 94 | } 95 | 96 | ctx.fillText("" + this.text, x, y + 4); 97 | ctx.restore(); 98 | return this; 99 | } 100 | }); 101 | 102 | return StaveText; 103 | }()); 104 | -------------------------------------------------------------------------------- /src/stavevolta.js: -------------------------------------------------------------------------------- 1 | // Vex Flow Notation 2 | // Author Larry Kuhns 2011 3 | // Implements voltas (repeat brackets) 4 | // 5 | // Requires vex.js. 6 | 7 | Vex.Flow.Volta = (function() { 8 | function Volta(type, number, x, y_shift) { 9 | if (arguments.length > 0) this.init(type, number, x, y_shift); 10 | } 11 | 12 | Volta.type = { 13 | NONE: 1, 14 | BEGIN: 2, 15 | MID: 3, 16 | END: 4, 17 | BEGIN_END: 5 18 | }; 19 | 20 | Vex.Inherit(Volta, Vex.Flow.StaveModifier, { 21 | init: function(type, number, x, y_shift) { 22 | Volta.superclass.init.call(this); 23 | 24 | this.volta = type; 25 | this.x = x; 26 | this.y_shift = y_shift; 27 | this.number = number; 28 | this.font = { 29 | family: "sans-serif", 30 | size: 9, 31 | weight: "bold" 32 | }; 33 | }, 34 | 35 | getCategory: function() { return "voltas"; }, 36 | setShiftY: function(y) { this.y_shift = y; return this; }, 37 | 38 | draw: function(stave, x) { 39 | if (!stave.context) throw new Vex.RERR("NoCanvasContext", 40 | "Can't draw stave without canvas context."); 41 | var ctx = stave.context; 42 | var width = stave.width; 43 | var top_y = stave.getYForTopText(stave.options.num_lines) + this.y_shift; 44 | var vert_height = 1.5 * stave.options.spacing_between_lines_px; 45 | switch(this.volta) { 46 | case Vex.Flow.Volta.type.BEGIN: 47 | ctx.fillRect(this.x + x, top_y, 1, vert_height); 48 | break; 49 | case Vex.Flow.Volta.type.END: 50 | width -= 5; 51 | ctx.fillRect(this.x + x + width, top_y, 1, vert_height); 52 | break; 53 | case Vex.Flow.Volta.type.BEGIN_END: 54 | width -= 3; 55 | ctx.fillRect(this.x + x, top_y, 1, vert_height); 56 | ctx.fillRect(this.x + x + width, top_y, 1, vert_height); 57 | break; 58 | } 59 | // If the beginning of a volta, draw measure number 60 | if (this.volta == Volta.type.BEGIN || 61 | this.volta == Volta.type.BEGIN_END) { 62 | ctx.save(); 63 | ctx.setFont(this.font.family, this.font.size, this.font.weight); 64 | ctx.fillText(this.number, this.x + x + 5, top_y + 15); 65 | ctx.restore(); 66 | } 67 | ctx.fillRect(this.x + x, top_y, width, 1); 68 | return this; 69 | } 70 | }); 71 | 72 | return Volta; 73 | }()); -------------------------------------------------------------------------------- /src/stem.js: -------------------------------------------------------------------------------- 1 | // [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010. 2 | // 3 | // ## Description 4 | // 5 | // This file implements the `Stem` object. Generally this object is handled 6 | // by its parent `StemmableNote`. 7 | // 8 | Vex.Flow.Stem = (function() { 9 | var Stem = function(options) { 10 | if (arguments.length > 0) this.init(options); 11 | }; 12 | 13 | // To enable logging for this class. Set `Vex.Flow.Stem.DEBUG` to `true`. 14 | function L() { if (Stem.DEBUG) Vex.L("Vex.Flow.Stem", arguments); } 15 | 16 | // Stem directions 17 | Stem.UP = 1; 18 | Stem.DOWN = -1; 19 | 20 | // Theme 21 | Stem.WIDTH = Vex.Flow.STEM_WIDTH; 22 | Stem.HEIGHT = Vex.Flow.STEM_HEIGHT; 23 | 24 | // ## Prototype Methods 25 | Stem.prototype = { 26 | init: function(options) { 27 | // Default notehead x bounds 28 | this.x_begin = options.x_begin || 0; 29 | this.x_end = options.x_end || 0; 30 | 31 | // Y bounds for top/bottom most notehead 32 | this.y_top = options.y_top || 0; 33 | this.y_bottom = options.y_bottom || 0; 34 | 35 | // Stem base extension 36 | this.y_extend = options.y_extend || 0; 37 | // Stem top extension 38 | this.stem_extension = options.stem_extension || 0; 39 | 40 | // Direction of the stem 41 | this.stem_direction = options.stem_direction || 0; 42 | 43 | // Flag to override all draw calls 44 | this.hide = false; 45 | }, 46 | 47 | // Set the x bounds for the default notehead 48 | setNoteHeadXBounds: function(x_begin, x_end) { 49 | this.x_begin = x_begin; 50 | this.x_end = x_end; 51 | return this; 52 | }, 53 | 54 | // Set the direction of the stem in relation to the noteheads 55 | setDirection: function(direction){ this.stem_direction = direction; }, 56 | 57 | // Set the extension for the stem, generally for flags or beams 58 | setExtension: function(ext) { this.stem_extension = ext; }, 59 | 60 | // The the y bounds for the top and bottom noteheads 61 | setYBounds: function(y_top, y_bottom) { 62 | this.y_top = y_top; 63 | this.y_bottom = y_bottom; 64 | }, 65 | 66 | // The category of the object 67 | getCategory: function() { return "stem"; }, 68 | 69 | // Set the canvas context to render on 70 | setContext: function(context) { this.context = context; return this;}, 71 | 72 | // Gets the entire height for the stem 73 | getHeight: function() { 74 | return ((this.y_bottom - this.y_top) * this.stem_direction) + 75 | ((Stem.HEIGHT + this.stem_extension) * this.stem_direction); 76 | }, 77 | 78 | getBoundingBox: function() { 79 | throw new Vex.RERR("NotImplemented", "getBoundingBox() not implemented."); 80 | }, 81 | 82 | // Get the y coordinates for the very base of the stem to the top of 83 | // the extension 84 | getExtents: function() { 85 | var ys = [this.y_top, this.y_bottom]; 86 | 87 | var top_pixel = this.y_top; 88 | var base_pixel = this.y_bottom; 89 | var stem_height = Stem.HEIGHT + this.stem_extension; 90 | 91 | for (var i = 0; i < ys.length; ++i) { 92 | var stem_top = ys[i] + (stem_height * -this.stem_direction); 93 | 94 | if (this.stem_direction == Stem.DOWN) { 95 | top_pixel = (top_pixel > stem_top) ? top_pixel : stem_top; 96 | base_pixel = (base_pixel < ys[i]) ? base_pixel : ys[i]; 97 | } else { 98 | top_pixel = (top_pixel < stem_top) ? top_pixel : stem_top; 99 | base_pixel = (base_pixel > ys[i]) ? base_pixel : ys[i]; 100 | } 101 | } 102 | 103 | return { topY: top_pixel, baseY: base_pixel }; 104 | }, 105 | 106 | // Render the stem onto the canvas 107 | draw: function() { 108 | if (!this.context) throw new Vex.RERR("NoCanvasContext", 109 | "Can't draw without a canvas context."); 110 | 111 | if (this.hide) return; 112 | 113 | var ctx = this.context; 114 | var stem_x, stem_y; 115 | var stem_direction = this.stem_direction; 116 | 117 | if (stem_direction == Stem.DOWN) { 118 | // Down stems are rendered to the left of the head. 119 | stem_x = this.x_begin + (Stem.WIDTH / 2); 120 | stem_y = this.y_top + 2; 121 | } else { 122 | // Up stems are rendered to the right of the head. 123 | stem_x = this.x_end + (Stem.WIDTH / 2); 124 | stem_y = this.y_bottom - 2; 125 | } 126 | 127 | stem_y += this.y_extend * stem_direction; 128 | 129 | L("Rendering stem - ", "Top Y: ", this.y_top, "Bottom Y: ", this.y_bottom); 130 | 131 | // Draw the stem 132 | ctx.beginPath(); 133 | ctx.setLineWidth(Stem.WIDTH); 134 | ctx.moveTo(stem_x, stem_y); 135 | ctx.lineTo(stem_x, stem_y - this.getHeight()); 136 | ctx.stroke(); 137 | ctx.setLineWidth(1); 138 | } 139 | }; 140 | 141 | return Stem; 142 | }()); 143 | -------------------------------------------------------------------------------- /src/tabslide.js: -------------------------------------------------------------------------------- 1 | // VexFlow - Music Engraving for HTML5 2 | // Copyright Mohit Muthanna 2010 3 | // 4 | // This class implements varies types of ties between contiguous notes. The 5 | // ties include: regular ties, hammer ons, pull offs, and slides. 6 | 7 | /** 8 | * Create a new tie from the specified notes. The notes must 9 | * be part of the same line, and have the same duration (in ticks). 10 | * 11 | * @constructor 12 | * @param {!Object} context The canvas context. 13 | * @param {!Object} notes The notes to tie up. 14 | * @param {!Object} Options 15 | */ 16 | Vex.Flow.TabSlide = (function() { 17 | function TabSlide(notes, direction) { 18 | if (arguments.length > 0) this.init(notes, direction); 19 | } 20 | 21 | TabSlide.SLIDE_UP = 1; 22 | TabSlide.SLIDE_DOWN = -1; 23 | 24 | TabSlide.createSlideUp = function(notes) { 25 | return new TabSlide(notes, TabSlide.SLIDE_UP); 26 | }; 27 | 28 | TabSlide.createSlideDown = function(notes) { 29 | return new TabSlide(notes, TabSlide.SLIDE_DOWN); 30 | }; 31 | 32 | Vex.Inherit(TabSlide, Vex.Flow.TabTie, { 33 | init: function(notes, direction) { 34 | /** 35 | * Notes is a struct that has: 36 | * 37 | * { 38 | * first_note: Note, 39 | * last_note: Note, 40 | * first_indices: [n1, n2, n3], 41 | * last_indices: [n1, n2, n3] 42 | * } 43 | * 44 | **/ 45 | TabSlide.superclass.init.call(this, notes, "sl."); 46 | if (!direction) { 47 | var first_fret = notes.first_note.getPositions()[0].fret; 48 | var last_fret = notes.last_note.getPositions()[0].fret; 49 | 50 | direction = ((parseInt(first_fret, 10) > parseInt(last_fret, 10)) ? 51 | TabSlide.SLIDE_DOWN : TabSlide.SLIDE_UP); 52 | } 53 | 54 | this.slide_direction = direction; 55 | this.render_options.cp1 = 11; 56 | this.render_options.cp2 = 14; 57 | this.render_options.y_shift = 0.5; 58 | 59 | this.setFont({font: "Times", size: 10, style: "bold italic"}); 60 | this.setNotes(notes); 61 | }, 62 | 63 | renderTie: function(params) { 64 | if (params.first_ys.length === 0 || params.last_ys.length === 0) 65 | throw new Vex.RERR("BadArguments", "No Y-values to render"); 66 | 67 | var ctx = this.context; 68 | var first_x_px = params.first_x_px; 69 | var first_ys = params.first_ys; 70 | var last_x_px = params.last_x_px; 71 | 72 | var direction = this.slide_direction; 73 | if (direction != TabSlide.SLIDE_UP && 74 | direction != TabSlide.SLIDE_DOWN) { 75 | throw new Vex.RERR("BadSlide", "Invalid slide direction"); 76 | } 77 | 78 | for (var i = 0; i < this.first_indices.length; ++i) { 79 | var slide_y = first_ys[this.first_indices[i]] + 80 | this.render_options.y_shift; 81 | 82 | if (isNaN(slide_y)) 83 | throw new Vex.RERR("BadArguments", "Bad indices for slide rendering."); 84 | 85 | ctx.beginPath(); 86 | ctx.moveTo(first_x_px, slide_y + (3 * direction)); 87 | ctx.lineTo(last_x_px, slide_y - (3 * direction)); 88 | ctx.closePath(); 89 | ctx.stroke(); 90 | } 91 | } 92 | }); 93 | 94 | return TabSlide; 95 | }()); -------------------------------------------------------------------------------- /src/tabstave.js: -------------------------------------------------------------------------------- 1 | // Vex Flow 2 | // Mohit Muthanna 3 | // 4 | // Copyright Mohit Cheppudira 2010 5 | 6 | /** @constructor */ 7 | Vex.Flow.TabStave = (function() { 8 | function TabStave(x, y, width, options) { 9 | if (arguments.length > 0) this.init(x, y, width, options); 10 | } 11 | 12 | Vex.Inherit(TabStave, Vex.Flow.Stave, { 13 | init: function(x, y, width, options) { 14 | var tab_options = { 15 | spacing_between_lines_px: 13, 16 | num_lines: 6, 17 | top_text_position: 1 18 | }; 19 | 20 | Vex.Merge(tab_options, options); 21 | TabStave.superclass.init.call(this, x, y, width, tab_options); 22 | }, 23 | 24 | getYForGlyphs: function() { 25 | return this.getYForLine(2.5); 26 | }, 27 | 28 | addTabGlyph: function() { 29 | var glyphScale; 30 | var glyphOffset; 31 | 32 | switch(this.options.num_lines) { 33 | case 8: 34 | glyphScale = 55; 35 | glyphOffset = 14; 36 | break; 37 | case 7: 38 | glyphScale = 47; 39 | glyphOffset = 8; 40 | break; 41 | case 6: 42 | glyphScale = 40; 43 | glyphOffset = 1; 44 | break; 45 | case 5: 46 | glyphScale = 30; 47 | glyphOffset = -6; 48 | break; 49 | case 4: 50 | glyphScale = 23; 51 | glyphOffset = -12; 52 | break; 53 | } 54 | 55 | var tabGlyph = new Vex.Flow.Glyph("v2f", glyphScale); 56 | tabGlyph.y_shift = glyphOffset; 57 | this.addGlyph(tabGlyph); 58 | return this; 59 | } 60 | }); 61 | 62 | return TabStave; 63 | }()); -------------------------------------------------------------------------------- /src/tabtie.js: -------------------------------------------------------------------------------- 1 | // VexFlow - Music Engraving for HTML5 2 | // Copyright Mohit Muthanna 2010 3 | // 4 | // This class implements varies types of ties between contiguous notes. The 5 | // ties include: regular ties, hammer ons, pull offs, and slides. 6 | 7 | /** 8 | * Create a new tie from the specified notes. The notes must 9 | * be part of the same line, and have the same duration (in ticks). 10 | * 11 | * @constructor 12 | * @param {!Object} context The canvas context. 13 | * @param {!Object} notes The notes to tie up. 14 | * @param {!Object} Options 15 | */ 16 | Vex.Flow.TabTie = (function() { 17 | function TabTie(notes, text) { 18 | if (arguments.length > 0) this.init(notes, text); 19 | } 20 | 21 | TabTie.createHammeron = function(notes) { 22 | return new TabTie(notes, "H"); 23 | }; 24 | 25 | TabTie.createPulloff = function(notes) { 26 | return new TabTie(notes, "P"); 27 | }; 28 | 29 | Vex.Inherit(TabTie, Vex.Flow.StaveTie, { 30 | init: function(notes, text) { 31 | /** 32 | * Notes is a struct that has: 33 | * 34 | * { 35 | * first_note: Note, 36 | * last_note: Note, 37 | * first_indices: [n1, n2, n3], 38 | * last_indices: [n1, n2, n3] 39 | * } 40 | * 41 | **/ 42 | TabTie.superclass.init.call(this, notes, text); 43 | this.render_options.cp1 = 9; 44 | this.render_options.cp2 = 11; 45 | this.render_options.y_shift = 3; 46 | 47 | this.setNotes(notes); 48 | }, 49 | 50 | draw: function() { 51 | if (!this.context) 52 | throw new Vex.RERR("NoContext", "No context to render tie."); 53 | var first_note = this.first_note; 54 | var last_note = this.last_note; 55 | var first_x_px, last_x_px, first_ys, last_ys; 56 | 57 | if (first_note) { 58 | first_x_px = first_note.getTieRightX() + this.render_options.tie_spacing; 59 | first_ys = first_note.getYs(); 60 | } else { 61 | first_x_px = last_note.getStave().getTieStartX(); 62 | first_ys = last_note.getYs(); 63 | this.first_indices = this.last_indices; 64 | } 65 | 66 | if (last_note) { 67 | last_x_px = last_note.getTieLeftX() + this.render_options.tie_spacing; 68 | last_ys = last_note.getYs(); 69 | } else { 70 | last_x_px = first_note.getStave().getTieEndX(); 71 | last_ys = first_note.getYs(); 72 | this.last_indices = this.first_indices; 73 | } 74 | 75 | this.renderTie({ 76 | first_x_px: first_x_px, 77 | last_x_px: last_x_px, 78 | first_ys: first_ys, 79 | last_ys: last_ys, 80 | direction: -1 // Tab tie's are always face up. 81 | }); 82 | 83 | this.renderText(first_x_px, last_x_px); 84 | return true; 85 | } 86 | }); 87 | 88 | return TabTie; 89 | }()); 90 | -------------------------------------------------------------------------------- /src/tickable.js: -------------------------------------------------------------------------------- 1 | // Vex Flow 2 | // Copyright Mohit Cheppudira 3 | // 4 | // The tickable interface. Tickables are things that sit on a score and 5 | // have a duration, i.e., they occupy space in the musical rendering dimension. 6 | 7 | /** @constructor */ 8 | Vex.Flow.Tickable = (function() { 9 | function Tickable() { 10 | this.init(); 11 | } 12 | 13 | Tickable.prototype = { 14 | init: function() { 15 | this.intrinsicTicks = 0; 16 | this.tickMultiplier = new Vex.Flow.Fraction(1, 1); 17 | this.ticks = new Vex.Flow.Fraction(0, 1); 18 | this.width = 0; 19 | this.x_shift = 0; // Shift from tick context 20 | this.voice = null; 21 | this.tickContext = null; 22 | this.modifierContext = null; 23 | this.modifiers = []; 24 | this.preFormatted = false; 25 | this.postFormatted = false; 26 | this.tuplet = null; 27 | 28 | // This flag tells the formatter to ignore this tickable during 29 | // formatting and justification. It is set by tickables such as BarNote. 30 | this.ignore_ticks = false; 31 | this.context = null; 32 | }, 33 | 34 | setContext: function(context) { this.context = context; }, 35 | getBoundingBox: function() { return null; }, 36 | getTicks: function() { return this.ticks; }, 37 | shouldIgnoreTicks: function() { return this.ignore_ticks; }, 38 | getWidth: function() { return this.width; }, 39 | setXShift: function(x) { this.x_shift = x; }, 40 | 41 | // Every tickable must be associated with a voice. This allows formatters 42 | // and preFormatter to associate them with the right modifierContexts. 43 | getVoice: function() { 44 | if (!this.voice) throw new Vex.RERR("NoVoice", "Tickable has no voice."); 45 | return this.voice; 46 | }, 47 | setVoice: function(voice) { this.voice = voice; }, 48 | 49 | getTuplet: function() { return this.tuplet; }, 50 | setTuplet: function(tuplet) { 51 | // Detach from previous tuplet 52 | var noteCount, beatsOccupied; 53 | 54 | if (this.tuplet) { 55 | noteCount = this.tuplet.getNoteCount(); 56 | beatsOccupied = this.tuplet.getBeatsOccupied(); 57 | 58 | // Revert old multiplier 59 | this.applyTickMultiplier(noteCount, beatsOccupied); 60 | } 61 | 62 | // Attach to new tuplet 63 | if (tuplet) { 64 | noteCount = tuplet.getNoteCount(); 65 | beatsOccupied = tuplet.getBeatsOccupied(); 66 | 67 | this.applyTickMultiplier(beatsOccupied, noteCount); 68 | } 69 | 70 | this.tuplet = tuplet; 71 | 72 | return this; 73 | }, 74 | 75 | /** optional, if tickable has modifiers **/ 76 | addToModifierContext: function(mc) { 77 | this.modifierContext = mc; 78 | // Add modifiers to modifier context (if any) 79 | this.preFormatted = false; 80 | }, 81 | 82 | /** optional, if tickable has modifiers **/ 83 | addModifier: function(mod) { 84 | this.modifiers.push(mod); 85 | this.preFormatted = false; 86 | return this; 87 | }, 88 | 89 | setTickContext: function(tc) { 90 | this.tickContext = tc; 91 | this.preFormatted = false; 92 | }, 93 | 94 | preFormat: function() { 95 | if (this.preFormatted) return; 96 | 97 | this.width = 0; 98 | if (this.modifierContext) { 99 | this.modifierContext.preFormat(); 100 | this.width += this.modifierContext.getWidth(); 101 | } 102 | }, 103 | 104 | postFormat: function() { 105 | if (this.postFormatted) return; 106 | this.postFormatted = true; 107 | return this; 108 | }, 109 | 110 | getIntrinsicTicks: function() { 111 | return this.intrinsicTicks; 112 | }, 113 | setIntrinsicTicks: function(intrinsicTicks) { 114 | this.intrinsicTicks = intrinsicTicks; 115 | this.ticks = this.tickMultiplier.clone().multiply(this.intrinsicTicks); 116 | }, 117 | 118 | getTickMultiplier: function() { 119 | return this.tickMultiplier; 120 | }, 121 | applyTickMultiplier: function(numerator, denominator) { 122 | this.tickMultiplier.multiply(numerator, denominator); 123 | this.ticks = this.tickMultiplier.clone().multiply(this.intrinsicTicks); 124 | } 125 | }; 126 | 127 | return Tickable; 128 | }()); 129 | -------------------------------------------------------------------------------- /src/tickcontext.js: -------------------------------------------------------------------------------- 1 | // Vex Flow 2 | // Copyright Mohit Cheppudira 3 | // 4 | // A formatter for abstract tickable objects, such as notes, chords, 5 | // tabs, etc. 6 | 7 | /** @constructor */ 8 | Vex.Flow.TickContext = (function() { 9 | function TickContext() { 10 | this.init(); 11 | } 12 | 13 | TickContext.prototype = { 14 | init: function() { 15 | this.currentTick = new Vex.Flow.Fraction(0, 1); 16 | this.maxTicks = new Vex.Flow.Fraction(0, 1); 17 | this.minTicks = null; 18 | this.width = 0; 19 | this.padding = 3; // padding on each side (width += padding * 2) 20 | this.pixelsUsed = 0; 21 | this.x = 0; 22 | this.tickables = []; // Notes, tabs, chords, lyrics. 23 | this.notePx = 0; // width of widest note in this context 24 | this.extraLeftPx = 0; // Extra left pixels for modifers & displace notes 25 | this.extraRightPx = 0; // Extra right pixels for modifers & displace notes 26 | 27 | this.tContexts = []; // Parent array of tick contexts 28 | 29 | // Ignore this tick context for formatting and justification 30 | this.ignore_ticks = true; 31 | this.preFormatted = false; 32 | this.postFormatted = false; 33 | this.context = null; // Rendering context 34 | }, 35 | 36 | setContext: function(context) { this.context = context; return this; }, 37 | getContext: function() { return this.context; }, 38 | shouldIgnoreTicks: function() { return this.ignore_ticks; }, 39 | getWidth: function() { return this.width + (this.padding * 2); }, 40 | getX: function() { return this.x; }, 41 | setX: function(x) { this.x = x; return this; }, 42 | getPixelsUsed: function() { return this.pixelsUsed; }, 43 | setPixelsUsed: function(pixelsUsed) { this.pixelsUsed = pixelsUsed; return this; }, 44 | setPadding: function(padding) { this.padding = padding; return this; }, 45 | getMaxTicks: function() { return this.maxTicks; }, 46 | getMinTicks: function() { return this.minTicks; }, 47 | getTickables: function() { return this.tickables; }, 48 | 49 | // Get widths context, note and left/right modifiers for formatting 50 | getMetrics: function() { 51 | return { width: this.width, notePx: this.notePx, 52 | extraLeftPx: this.extraLeftPx, extraRightPx: this.extraRightPx }; 53 | }, 54 | 55 | getCurrentTick: function() { return this.currentTick; }, 56 | setCurrentTick: function(tick) { 57 | this.currentTick = tick; 58 | this.preFormatted = false; 59 | }, 60 | 61 | // Get left & right pixels used for modifiers 62 | getExtraPx: function() { 63 | var left_shift = 0; 64 | var right_shift = 0; 65 | var extraLeftPx = 0; 66 | var extraRightPx = 0; 67 | for (var i = 0; i < this.tickables.length; i++) { 68 | extraLeftPx = Math.max(this.tickables[i].extraLeftPx, extraLeftPx); 69 | extraRightPx = Math.max(this.tickables[i].extraRightPx, extraRightPx); 70 | var mContext = this.tickables[i].modifierContext; 71 | if (mContext && mContext != null) { 72 | left_shift = Math.max( left_shift, mContext.state.left_shift); 73 | right_shift = Math.max( right_shift, mContext.state.right_shift); 74 | } 75 | } 76 | return { left: left_shift, right: right_shift, 77 | extraLeft: extraLeftPx, extraRight: extraRightPx }; 78 | }, 79 | 80 | addTickable: function(tickable) { 81 | if (!tickable) { 82 | throw new Vex.RERR("BadArgument", "Invalid tickable added."); 83 | } 84 | 85 | if (!tickable.shouldIgnoreTicks()) { 86 | this.ignore_ticks = false; 87 | 88 | var ticks = tickable.getTicks(); 89 | 90 | if (ticks.value() > this.maxTicks.value()) { 91 | this.maxTicks = ticks.clone(); 92 | } 93 | 94 | if (this.minTicks == null) { 95 | this.minTicks = ticks.clone(); 96 | } else if (ticks.value() < this.minTicks.value()) { 97 | this.minTicks = ticks.clone(); 98 | } 99 | } 100 | 101 | tickable.setTickContext(this); 102 | this.tickables.push(tickable); 103 | this.preFormatted = false; 104 | return this; 105 | }, 106 | 107 | preFormat: function() { 108 | if (this.preFormatted) return; 109 | 110 | for (var i = 0; i < this.tickables.length; ++i) { 111 | var tickable = this.tickables[i]; 112 | tickable.preFormat(); 113 | var metrics = tickable.getMetrics(); 114 | 115 | // Maintain max extra pixels from all tickables in the context 116 | this.extraLeftPx = Math.max(this.extraLeftPx, 117 | metrics.extraLeftPx + metrics.modLeftPx); 118 | this.extraRightPx = Math.max(this.extraRightPx, 119 | metrics.extraRightPx + metrics.modRightPx); 120 | 121 | // Maintain the widest note for all tickables in the context 122 | this.notePx = Math.max(this.notePx, metrics.noteWidth); 123 | 124 | // Recalculate the tick context total width 125 | this.width = this.notePx + 126 | this.extraLeftPx + 127 | this.extraRightPx; 128 | } 129 | 130 | return this; 131 | }, 132 | 133 | postFormat: function() { 134 | if (this.postFormatted) return this; 135 | this.postFormatted = true; 136 | return this; 137 | } 138 | }; 139 | 140 | TickContext.getNextContext = function(tContext) { 141 | var contexts = tContext.tContexts; 142 | var index = contexts.indexOf(tContext); 143 | 144 | return contexts[index+1]; 145 | }; 146 | 147 | return TickContext; 148 | }()); 149 | -------------------------------------------------------------------------------- /src/timesignature.js: -------------------------------------------------------------------------------- 1 | // Vex Flow Notation 2 | // Implements time signatures glyphs for staffs 3 | // See tables.js for the internal time signatures 4 | // representation 5 | // 6 | 7 | /** 8 | * @param {string} timeSpec time signature, i.e. "4/4" 9 | * @param {number} [customPadding] custom padding when using multi-stave/multi-instrument setting 10 | * to align key/time signature (in pixels), optional 11 | * @constructor 12 | */ 13 | Vex.Flow.TimeSignature = (function() { 14 | function TimeSignature(timeSpec, customPadding) { 15 | if (arguments.length > 0) this.init(timeSpec, customPadding); 16 | } 17 | 18 | TimeSignature.glyphs = { 19 | "C": { 20 | code: "v41", 21 | point: 40, 22 | line: 2 23 | }, 24 | "C|": { 25 | code: "vb6", 26 | point: 40, 27 | line: 2 28 | } 29 | }; 30 | 31 | Vex.Inherit(TimeSignature, Vex.Flow.StaveModifier, { 32 | init: function(timeSpec, customPadding) { 33 | TimeSignature.superclass.init(); 34 | var padding = customPadding || 15; 35 | 36 | this.setPadding(padding); 37 | this.point = 40; 38 | this.topLine = 2; 39 | this.bottomLine = 4; 40 | this.timeSig = this.parseTimeSpec(timeSpec); 41 | }, 42 | 43 | parseTimeSpec: function(timeSpec) { 44 | if (timeSpec == "C" || timeSpec == "C|") { 45 | var glyphInfo = TimeSignature.glyphs[timeSpec]; 46 | return {num: false, line: glyphInfo.line, 47 | glyph: new Vex.Flow.Glyph(glyphInfo.code, glyphInfo.point)}; 48 | } 49 | 50 | var topNums = []; 51 | var i, c; 52 | for (i = 0; i < timeSpec.length; ++i) { 53 | c = timeSpec.charAt(i); 54 | if (c == "/") { 55 | break; 56 | } 57 | else if (/[0-9]/.test(c)) { 58 | topNums.push(c); 59 | } 60 | else { 61 | throw new Vex.RERR("BadTimeSignature", 62 | "Invalid time spec: " + timeSpec); 63 | } 64 | } 65 | 66 | if (i === 0) { 67 | throw new Vex.RERR("BadTimeSignature", 68 | "Invalid time spec: " + timeSpec); 69 | } 70 | 71 | // skip the "/" 72 | ++i; 73 | 74 | if (i == timeSpec.length) { 75 | throw new Vex.RERR("BadTimeSignature", 76 | "Invalid time spec: " + timeSpec); 77 | } 78 | 79 | 80 | var botNums = []; 81 | for (; i < timeSpec.length; ++i) { 82 | c = timeSpec.charAt(i); 83 | if (/[0-9]/.test(c)) { 84 | botNums.push(c); 85 | } 86 | else { 87 | throw new Vex.RERR("BadTimeSignature", 88 | "Invalid time spec: " + timeSpec); 89 | } 90 | } 91 | 92 | 93 | return {num: true, glyph: this.makeTimeSignatureGlyph(topNums, botNums)}; 94 | }, 95 | 96 | makeTimeSignatureGlyph: function(topNums, botNums) { 97 | var glyph = new Vex.Flow.Glyph("v0", this.point); 98 | glyph["topGlyphs"] = []; 99 | glyph["botGlyphs"] = []; 100 | 101 | var topWidth = 0; 102 | var i, num; 103 | for (i = 0; i < topNums.length; ++i) { 104 | num = topNums[i]; 105 | var topGlyph = new Vex.Flow.Glyph("v" + num, this.point); 106 | 107 | glyph.topGlyphs.push(topGlyph); 108 | topWidth += topGlyph.getMetrics().width; 109 | } 110 | 111 | var botWidth = 0; 112 | for (i = 0; i < botNums.length; ++i) { 113 | num = botNums[i]; 114 | var botGlyph = new Vex.Flow.Glyph("v" + num, this.point); 115 | 116 | glyph.botGlyphs.push(botGlyph); 117 | botWidth += botGlyph.getMetrics().width; 118 | } 119 | 120 | var width = (topWidth > botWidth ? topWidth : botWidth); 121 | var xMin = glyph.getMetrics().x_min; 122 | 123 | glyph.getMetrics = function() { 124 | return { 125 | x_min: xMin, 126 | x_max: xMin + width, 127 | width: width 128 | }; 129 | }; 130 | 131 | var topStartX = (width - topWidth) / 2.0; 132 | var botStartX = (width - botWidth) / 2.0; 133 | 134 | var that = this; 135 | glyph.renderToStave = function(x) { 136 | var start_x = x + topStartX; 137 | var i, g; 138 | for (i = 0; i < this.topGlyphs.length; ++i) { 139 | g = this.topGlyphs[i]; 140 | Vex.Flow.Glyph.renderOutline(this.context, g.metrics.outline, 141 | g.scale, start_x + g.x_shift, this.stave.getYForLine(that.topLine) + 1); 142 | start_x += g.getMetrics().width; 143 | } 144 | 145 | start_x = x + botStartX; 146 | for (i = 0; i < this.botGlyphs.length; ++i) { 147 | g = this.botGlyphs[i]; 148 | that.placeGlyphOnLine(g, this.stave, g.line); 149 | Vex.Flow.Glyph.renderOutline(this.context, g.metrics.outline, 150 | g.scale, start_x + g.x_shift, this.stave.getYForLine(that.bottomLine) + 1); 151 | start_x += g.getMetrics().width; 152 | } 153 | }; 154 | 155 | return glyph; 156 | }, 157 | 158 | getTimeSig: function() { 159 | return this.timeSig; 160 | }, 161 | 162 | addModifier: function(stave) { 163 | if (!this.timeSig.num) { 164 | this.placeGlyphOnLine(this.timeSig.glyph, stave, this.timeSig.line); 165 | } 166 | stave.addGlyph(this.timeSig.glyph); 167 | }, 168 | 169 | addEndModifier: function(stave) { 170 | if (!this.timeSig.num) { 171 | this.placeGlyphOnLine(this.timeSig.glyph, stave, this.timeSig.line); 172 | } 173 | stave.addEndGlyph(this.timeSig.glyph); 174 | } 175 | }); 176 | 177 | return TimeSignature; 178 | }()); 179 | -------------------------------------------------------------------------------- /src/timesignote.js: -------------------------------------------------------------------------------- 1 | // Vex Flow Notation 2 | // Copyright Mohit Muthanna 2010 3 | // 4 | // Author Taehoon Moon 2014 5 | 6 | /** @constructor */ 7 | Vex.Flow.TimeSigNote = (function() { 8 | function TimeSigNote(timeSpec, customPadding) { 9 | if (arguments.length > 0) this.init(timeSpec, customPadding); 10 | } 11 | 12 | Vex.Inherit(TimeSigNote, Vex.Flow.Note, { 13 | init: function(timeSpec, customPadding) { 14 | TimeSigNote.superclass.init.call(this, {duration: "b"}); 15 | 16 | var timeSignature = new Vex.Flow.TimeSignature(timeSpec, customPadding); 17 | this.timeSig = timeSignature.getTimeSig(); 18 | this.setWidth(this.timeSig.glyph.getMetrics().width); 19 | 20 | // Note properties 21 | this.ignore_ticks = true; 22 | }, 23 | 24 | setStave: function(stave) { 25 | var superclass = Vex.Flow.TimeSigNote.superclass; 26 | superclass.setStave.call(this, stave); 27 | }, 28 | 29 | getBoundingBox: function() { 30 | return new Vex.Flow.BoundingBox(0, 0, 0, 0); 31 | }, 32 | 33 | addToModifierContext: function() { 34 | /* overridden to ignore */ 35 | return this; 36 | }, 37 | 38 | preFormat: function() { 39 | this.setPreFormatted(true); 40 | return this; 41 | }, 42 | 43 | draw: function() { 44 | if (!this.stave) throw new Vex.RERR("NoStave", "Can't draw without a stave."); 45 | 46 | if (!this.timeSig.glyph.getContext()) { 47 | this.timeSig.glyph.setContext(this.context); 48 | } 49 | 50 | this.timeSig.glyph.setStave(this.stave); 51 | this.timeSig.glyph.setYShift( 52 | this.stave.getYForLine(this.timeSig.line) - this.stave.getYForGlyphs()); 53 | this.timeSig.glyph.renderToStave(this.getAbsoluteX()); 54 | } 55 | }); 56 | 57 | return TimeSigNote; 58 | }()); 59 | -------------------------------------------------------------------------------- /src/tremolo.js: -------------------------------------------------------------------------------- 1 | // VexFlow - Music Engraving for HTML5 2 | // Author: Mike Corrigan 3 | // 4 | // This class implements tremolo notation. 5 | 6 | /** 7 | * @constructor 8 | */ 9 | Vex.Flow.Tremolo = (function() { 10 | function Tremolo(num) { 11 | if (arguments.length > 0) this.init(num); 12 | } 13 | 14 | var Modifier = Vex.Flow.Modifier; 15 | Vex.Inherit(Tremolo, Modifier, { 16 | init: function(num) { 17 | Tremolo.superclass.init.call(this); 18 | 19 | this.num = num; 20 | this.note = null; 21 | this.index = null; 22 | this.position = Modifier.Position.CENTER; 23 | this.code = "v74"; 24 | this.shift_right = -2; 25 | this.y_spacing = 4; 26 | 27 | this.render_options = { 28 | font_scale: 35, 29 | stroke_px: 3, 30 | stroke_spacing: 10 31 | }; 32 | 33 | this.font = { 34 | family: "Arial", 35 | size: 16, 36 | weight: "" 37 | }; 38 | }, 39 | 40 | getCategory: function() { return "tremolo"; }, 41 | 42 | draw: function() { 43 | if (!this.context) throw new Vex.RERR("NoContext", 44 | "Can't draw Tremolo without a context."); 45 | if (!(this.note && (this.index != null))) throw new Vex.RERR("NoAttachedNote", 46 | "Can't draw Tremolo without a note and index."); 47 | 48 | var start = this.note.getModifierStartXY(this.position, this.index); 49 | var x = start.x; 50 | var y = start.y; 51 | 52 | x += this.shift_right; 53 | for (var i = 0; i < this.num; ++i) { 54 | Vex.Flow.renderGlyph(this.context, x, y, 55 | this.render_options.font_scale, this.code); 56 | y += this.y_spacing; 57 | } 58 | } 59 | }); 60 | 61 | return Tremolo; 62 | }()); 63 | -------------------------------------------------------------------------------- /src/tuning.js: -------------------------------------------------------------------------------- 1 | // VexFlow - Music Engraving for HTML5 2 | // Copyright Mohit Muthanna 2010 3 | // 4 | // This class implements varies types of tunings for tablature. 5 | 6 | /** 7 | * @constructor 8 | */ 9 | Vex.Flow.Tuning = (function() { 10 | function Tuning(tuningString) { 11 | this.init(tuningString); 12 | } 13 | 14 | Tuning.names = { 15 | "standard": "E/5,B/4,G/4,D/4,A/3,E/3", 16 | "dagdad": "D/5,A/4,G/4,D/4,A/3,D/3", 17 | "dropd": "E/5,B/4,G/4,D/4,A/3,D/3", 18 | "eb": "Eb/5,Bb/4,Gb/4,Db/4,Ab/3,Db/3" 19 | }; 20 | 21 | Tuning.prototype = { 22 | init: function(tuningString) { 23 | // Default to standard tuning. 24 | this.setTuning(tuningString || "E/5,B/4,G/4,D/4,A/3,E/3,B/2,E/2"); 25 | }, 26 | 27 | noteToInteger: function(noteString) { 28 | return Vex.Flow.keyProperties(noteString).int_value; 29 | }, 30 | 31 | setTuning: function(noteString) { 32 | if (Vex.Flow.Tuning.names[noteString]) 33 | noteString = Vex.Flow.Tuning.names[noteString]; 34 | 35 | this.tuningString = noteString; 36 | this.tuningValues = []; 37 | this.numStrings = 0; 38 | 39 | var keys = noteString.split(/\s*,\s*/); 40 | if (keys.length === 0) 41 | throw new Vex.RERR("BadArguments", "Invalid tuning string: " + noteString); 42 | 43 | this.numStrings = keys.length; 44 | for (var i = 0; i < this.numStrings; ++i) { 45 | this.tuningValues[i] = this.noteToInteger(keys[i]); 46 | } 47 | }, 48 | 49 | getValueForString: function(stringNum) { 50 | var s = parseInt(stringNum, 10); 51 | if (s < 1 || s > this.numStrings) 52 | throw new Vex.RERR("BadArguments", "String number must be between 1 and " + 53 | this.numStrings + ": " + stringNum); 54 | 55 | return this.tuningValues[s - 1]; 56 | }, 57 | 58 | getValueForFret: function(fretNum, stringNum) { 59 | var stringValue = this.getValueForString(stringNum); 60 | var f = parseInt(fretNum, 10); 61 | 62 | if (f < 0) { 63 | throw new Vex.RERR("BadArguments", "Fret number must be 0 or higher: " + 64 | fretNum); 65 | } 66 | 67 | return stringValue + f; 68 | }, 69 | 70 | getNoteForFret: function(fretNum, stringNum) { 71 | var noteValue = this.getValueForFret(fretNum, stringNum); 72 | 73 | var octave = Math.floor(noteValue / 12); 74 | var value = noteValue % 12; 75 | 76 | return Vex.Flow.integerToNote(value) + "/" + octave; 77 | } 78 | }; 79 | 80 | return Tuning; 81 | }()); 82 | -------------------------------------------------------------------------------- /src/vex.js: -------------------------------------------------------------------------------- 1 | // [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010. 2 | // 3 | // ## Description 4 | // 5 | // This file implements utility methods used by the rest of the VexFlow 6 | // codebase. 7 | // 8 | // ## JSHint Settings 9 | // 10 | /* global window: false */ 11 | /* global document: false */ 12 | 13 | function Vex() {} 14 | 15 | // Default log function sends all arguments to console. 16 | Vex.L = function(block, args) { 17 | if (!args) return; 18 | var line = Array.prototype.slice.call(args).join(" "); 19 | window.console.log(block + ": " + line); 20 | }; 21 | 22 | // Default runtime exception. 23 | Vex.RuntimeError = function(code, message) { 24 | this.code = code; 25 | this.message = message; 26 | }; 27 | Vex.RuntimeError.prototype.toString = function() { 28 | return "RuntimeError: " + this.message; 29 | }; 30 | 31 | // Shortcut method for `RuntimeError`. 32 | Vex.RERR = Vex.RuntimeError; 33 | 34 | // Merge `destination` hash with `source` hash, overwriting like keys 35 | // in `source` if necessary. 36 | Vex.Merge = function(destination, source) { 37 | for (var property in source) 38 | destination[property] = source[property]; 39 | return destination; 40 | }; 41 | 42 | // DEPRECATED. Use `Math.min`. 43 | Vex.Min = function(a, b) { 44 | return (a > b) ? b : a; 45 | }; 46 | 47 | // DEPRECATED. Use `Math.max`. 48 | Vex.Max = function(a, b) { 49 | return (a > b) ? a : b; 50 | }; 51 | 52 | // Round number to nearest fractional value (`.5`, `.25`, etc.) 53 | Vex.RoundN = function(x, n) { 54 | return (x % n) >= (n/2) ? 55 | parseInt(x / n, 10) * n + n : parseInt(x / n, 10) * n; 56 | }; 57 | 58 | // Locate the mid point between stave lines. Returns a fractional line if a space. 59 | Vex.MidLine = function(a, b) { 60 | var mid_line = b + (a - b) / 2; 61 | if (mid_line % 2 > 0) { 62 | mid_line = Vex.RoundN(mid_line * 10, 5) / 10; 63 | } 64 | return mid_line; 65 | }; 66 | 67 | // Take `arr` and return a new list consisting of the sorted, unique, 68 | // contents of arr. Does not modify `arr`. 69 | Vex.SortAndUnique = function(arr, cmp, eq) { 70 | if (arr.length > 1) { 71 | var newArr = []; 72 | var last; 73 | arr.sort(cmp); 74 | 75 | for (var i = 0; i < arr.length; ++i) { 76 | if (i === 0 || !eq(arr[i], last)) { 77 | newArr.push(arr[i]); 78 | } 79 | last = arr[i]; 80 | } 81 | 82 | return newArr; 83 | } else { 84 | return arr; 85 | } 86 | }; 87 | 88 | // Check if array `a` contains `obj`. 89 | Vex.Contains = function(a, obj) { 90 | var i = a.length; 91 | while (i--) { 92 | if (a[i] === obj) { 93 | return true; 94 | } 95 | } 96 | return false; 97 | }; 98 | 99 | // Get the 2D Canvas context from DOM element `canvas_sel`. 100 | Vex.getCanvasContext = function(canvas_sel) { 101 | if (!canvas_sel) 102 | throw new Vex.RERR("BadArgument", "Invalid canvas selector: " + canvas_sel); 103 | 104 | var canvas = document.getElementById(canvas_sel); 105 | if (!(canvas && canvas.getContext)) { 106 | throw new Vex.RERR("UnsupportedBrowserError", 107 | "This browser does not support HTML5 Canvas"); 108 | } 109 | 110 | return canvas.getContext('2d'); 111 | }; 112 | 113 | // Draw a tiny dot marker on the specified canvas. A great debugging aid. 114 | // 115 | // `ctx`: Canvas context. 116 | // `x`, `y`: Dot coordinates. 117 | Vex.drawDot = function(ctx, x, y, color) { 118 | var c = color || "#f55"; 119 | ctx.save(); 120 | ctx.fillStyle = c; 121 | 122 | //draw a circle 123 | ctx.beginPath(); 124 | ctx.arc(x, y, 3, 0, Math.PI*2, true); 125 | ctx.closePath(); 126 | ctx.fill(); 127 | ctx.restore(); 128 | }; 129 | 130 | // Benchmark. Run function `f` once and report time elapsed shifted by `s` milliseconds. 131 | Vex.BM = function(s, f) { 132 | var start_time = new Date().getTime(); 133 | f(); 134 | var elapsed = new Date().getTime() - start_time; 135 | Vex.L(s + elapsed + "ms"); 136 | }; 137 | 138 | // Basic classical inheritance helper. Usage: 139 | // ``` 140 | // // Vex.Inherit(Child, Parent, { 141 | // // getName: function() {return this.name;}, 142 | // // setName: function(name) {this.name = name} 143 | // // }); 144 | // // 145 | // // Returns 'Child'. 146 | // ``` 147 | Vex.Inherit = (function () { 148 | var F = function () {}; 149 | // `C` is Child. `P` is parent. `O` is an object to 150 | // to extend `C` with. 151 | return function (C, P, O) { 152 | F.prototype = P.prototype; 153 | C.prototype = new F(); 154 | C.superclass = P.prototype; 155 | C.prototype.constructor = C; 156 | Vex.Merge(C.prototype, O); 157 | return C; 158 | }; 159 | }()); -------------------------------------------------------------------------------- /src/vibrato.js: -------------------------------------------------------------------------------- 1 | // VexFlow - Music Engraving for HTML5 2 | // Copyright Mohit Muthanna 2010 3 | // 4 | // This class implements vibratos. 5 | 6 | /** 7 | * @constructor 8 | */ 9 | Vex.Flow.Vibrato = (function() { 10 | function Vibrato() { this.init(); } 11 | 12 | var Modifier = Vex.Flow.Modifier; 13 | Vex.Inherit(Vibrato, Modifier, { 14 | init: function() { 15 | var superclass = Vex.Flow.Vibrato.superclass; 16 | superclass.init.call(this); 17 | 18 | this.harsh = false; 19 | this.position = Vex.Flow.Modifier.Position.RIGHT; 20 | this.render_options = { 21 | vibrato_width: 20, 22 | wave_height: 6, 23 | wave_width: 4, 24 | wave_girth: 2 25 | }; 26 | 27 | this.setVibratoWidth(this.render_options.vibrato_width); 28 | }, 29 | 30 | getCategory: function() { return "vibratos"; }, 31 | setHarsh: function(harsh) { this.harsh = harsh; return this; }, 32 | setVibratoWidth: function(width) { 33 | this.vibrato_width = width; 34 | this.setWidth(this.vibrato_width); 35 | return this; 36 | }, 37 | 38 | draw: function() { 39 | if (!this.context) throw new Vex.RERR("NoContext", 40 | "Can't draw vibrato without a context."); 41 | if (!this.note) throw new Vex.RERR("NoNoteForVibrato", 42 | "Can't draw vibrato without an attached note."); 43 | 44 | var start = this.note.getModifierStartXY(Vex.Flow.Modifier.Position.RIGHT, 45 | this.index); 46 | 47 | var ctx = this.context; 48 | var that = this; 49 | var vibrato_width = this.vibrato_width; 50 | 51 | function renderVibrato(x, y) { 52 | var wave_width = that.render_options.wave_width; 53 | var wave_girth = that.render_options.wave_girth; 54 | var wave_height = that.render_options.wave_height; 55 | var num_waves = vibrato_width / wave_width; 56 | 57 | ctx.beginPath(); 58 | 59 | var i; 60 | if (that.harsh) { 61 | ctx.moveTo(x, y + wave_girth + 1); 62 | for (i = 0; i < num_waves / 2; ++i) { 63 | ctx.lineTo(x + wave_width, y - (wave_height / 2)); 64 | x += wave_width; 65 | ctx.lineTo(x + wave_width, y + (wave_height / 2)); 66 | x += wave_width; 67 | } 68 | for (i = 0; i < num_waves / 2; ++i) { 69 | ctx.lineTo(x - wave_width, (y - (wave_height / 2)) + wave_girth + 1); 70 | x -= wave_width; 71 | ctx.lineTo(x - wave_width, (y + (wave_height / 2)) + wave_girth + 1); 72 | x -= wave_width; 73 | } 74 | ctx.fill(); 75 | } else { 76 | ctx.moveTo(x, y + wave_girth); 77 | for (i = 0; i < num_waves / 2; ++i) { 78 | ctx.quadraticCurveTo(x + (wave_width / 2), y - (wave_height / 2), 79 | x + wave_width, y); 80 | x += wave_width; 81 | ctx.quadraticCurveTo(x + (wave_width / 2), y + (wave_height / 2), 82 | x + wave_width, y); 83 | x += wave_width; 84 | } 85 | 86 | for (i = 0; i < num_waves / 2; ++i) { 87 | ctx.quadraticCurveTo( 88 | x - (wave_width / 2), 89 | (y + (wave_height / 2)) + wave_girth, 90 | x - wave_width, y + wave_girth); 91 | x -= wave_width; 92 | ctx.quadraticCurveTo( 93 | x - (wave_width / 2), 94 | (y - (wave_height / 2)) + wave_girth, 95 | x - wave_width, y + wave_girth); 96 | x -= wave_width; 97 | } 98 | ctx.fill(); 99 | } 100 | } 101 | 102 | var vx = start.x + this.x_shift; 103 | var vy = this.note.getYForTopText(this.text_line) + 2; 104 | 105 | renderVibrato(vx, vy); 106 | } 107 | }); 108 | 109 | return Vibrato; 110 | }()); 111 | -------------------------------------------------------------------------------- /src/voicegroup.js: -------------------------------------------------------------------------------- 1 | // Vex Music Notation 2 | // Mohit Muthanna 3 | // 4 | // Copyright Mohit Muthanna 2010 5 | 6 | /** @constructor */ 7 | Vex.Flow.VoiceGroup = (function() { 8 | function VoiceGroup() { 9 | this.init(); 10 | } 11 | 12 | VoiceGroup.prototype = { 13 | init: function() { 14 | this.voices = []; 15 | this.modifierContexts = []; 16 | }, 17 | 18 | // Every tickable must be associated with a voiceGroup. This allows formatters 19 | // and preformatters to associate them with the right modifierContexts. 20 | getVoices: function() { return this.voices; }, 21 | getModifierContexts: function() { return this.modifierContexts; }, 22 | 23 | addVoice: function(voice) { 24 | if (!voice) throw new Vex.RERR("BadArguments", "Voice cannot be null."); 25 | this.voices.push(voice); 26 | voice.setVoiceGroup(this); 27 | } 28 | }; 29 | 30 | return VoiceGroup; 31 | }()); -------------------------------------------------------------------------------- /tests/boundingbox_tests.js: -------------------------------------------------------------------------------- 1 | /** 2 | * VexFlow - Bounding Box Tests 3 | * Copyright Mohit Muthanna 2010 4 | */ 5 | 6 | Vex.Flow.Test.BoundingBox = {} 7 | 8 | Vex.Flow.Test.BoundingBox.Start = function() { 9 | module("BoundingBox"); 10 | test("Initialization Test", Vex.Flow.Test.BoundingBox.initialization); 11 | test("Merging Text", Vex.Flow.Test.BoundingBox.merging); 12 | } 13 | 14 | Vex.Flow.Test.BoundingBox.initialization = function() { 15 | var bb = new Vex.Flow.BoundingBox(4, 5, 6, 7); 16 | equal(bb.getX(), 4, "Bad X"); 17 | equal(bb.getY(), 5, "Bad Y"); 18 | equal(bb.getW(), 6, "Bad W"); 19 | equal(bb.getH(), 7, "Bad H"); 20 | 21 | bb.setX(5) 22 | equal(bb.getX(), 5, "Bad X"); 23 | } 24 | 25 | Vex.Flow.Test.BoundingBox.merging = function() { 26 | var bb1 = new Vex.Flow.BoundingBox(10, 10, 10, 10); 27 | var bb2 = new Vex.Flow.BoundingBox(15, 20, 10, 10); 28 | 29 | equal(bb1.getX(), 10, "Bad X for bb1"); 30 | equal(bb2.getX(), 15, "Bad X for bb2"); 31 | 32 | bb1.mergeWith(bb2); 33 | equal(bb1.getX(), 10); 34 | equal(bb1.getY(), 10); 35 | equal(bb1.getW(), 15); 36 | equal(bb1.getH(), 20); 37 | } 38 | -------------------------------------------------------------------------------- /tests/clef_tests.js: -------------------------------------------------------------------------------- 1 | // VexFlow - Basic Tests 2 | 3 | Vex.Flow.Test.Clef = {} 4 | 5 | Vex.Flow.Test.Clef.Start = function() { 6 | module("Clef"); 7 | Vex.Flow.Test.runTest("Clef Test", Vex.Flow.Test.Clef.draw); 8 | Vex.Flow.Test.runRaphaelTest("Clef Test (Raphael)", 9 | Vex.Flow.Test.Clef.draw); 10 | Vex.Flow.Test.runTest("Small Clef Test", Vex.Flow.Test.Clef.drawSmall); 11 | Vex.Flow.Test.runRaphaelTest("Small Clef Test (Raphael)", 12 | Vex.Flow.Test.Clef.drawSmall); 13 | Vex.Flow.Test.runTest("Clef Change Test", Vex.Flow.Test.Clef.drawClefChange); 14 | Vex.Flow.Test.runRaphaelTest("Clef Change Test (Raphael)", Vex.Flow.Test.Clef.drawClefChange); 15 | } 16 | 17 | Vex.Flow.Test.Clef.draw = function(options, contextBuilder) { 18 | var ctx = new contextBuilder(options.canvas_sel, 800, 120); 19 | var stave = new Vex.Flow.Stave(10, 10, 700); 20 | 21 | stave.addClef("treble"); 22 | stave.addClef("alto"); 23 | stave.addClef("tenor"); 24 | stave.addClef("soprano"); 25 | stave.addClef("bass"); 26 | stave.addClef("mezzo-soprano"); 27 | stave.addClef("baritone-c"); 28 | stave.addClef("baritone-f"); 29 | stave.addClef("subbass"); 30 | stave.addClef("french"); 31 | 32 | stave.addEndClef("treble"); 33 | stave.addEndClef("alto"); 34 | stave.addEndClef("tenor"); 35 | stave.addEndClef("soprano"); 36 | stave.addEndClef("bass"); 37 | stave.addEndClef("mezzo-soprano"); 38 | stave.addEndClef("baritone-c"); 39 | stave.addEndClef("baritone-f"); 40 | stave.addEndClef("subbass"); 41 | stave.addEndClef("french"); 42 | 43 | stave.setContext(ctx); 44 | stave.draw(); 45 | 46 | ok(true, "all pass"); 47 | } 48 | 49 | Vex.Flow.Test.Clef.drawSmall = function(options, contextBuilder) { 50 | var ctx = new contextBuilder(options.canvas_sel, 800, 120); 51 | var stave = new Vex.Flow.Stave(10, 10, 700); 52 | 53 | stave.addClef("treble_small"); 54 | stave.addClef("alto_small"); 55 | stave.addClef("tenor_small"); 56 | stave.addClef("soprano_small"); 57 | stave.addClef("bass_small"); 58 | stave.addClef("mezzo-soprano_small"); 59 | stave.addClef("baritone-c_small"); 60 | stave.addClef("baritone-f_small"); 61 | stave.addClef("subbass_small"); 62 | stave.addClef("french_small"); 63 | 64 | stave.addEndClef("treble_small"); 65 | stave.addEndClef("alto_small"); 66 | stave.addEndClef("tenor_small"); 67 | stave.addEndClef("soprano_small"); 68 | stave.addEndClef("bass_small"); 69 | stave.addEndClef("mezzo-soprano_small"); 70 | stave.addEndClef("baritone-c_small"); 71 | stave.addEndClef("baritone-f_small"); 72 | stave.addEndClef("subbass_small"); 73 | stave.addEndClef("french_small"); 74 | 75 | stave.setContext(ctx); 76 | stave.draw(); 77 | 78 | ok(true, "all pass"); 79 | } 80 | 81 | Vex.Flow.Test.Clef.drawClefChange = function(options, contextBuilder) { 82 | var ctx = new contextBuilder(options.canvas_sel, 800, 120); 83 | var stave = new Vex.Flow.Stave(10, 10, 700); 84 | stave.addClef("treble").setContext(ctx).draw(); 85 | 86 | var notes = [ 87 | new Vex.Flow.StaveNote({ keys: ["c/4"], duration: "q", clef: "treble" }), 88 | new Vex.Flow.ClefNote("alto_small"), 89 | new Vex.Flow.StaveNote({ keys: ["c/4"], duration: "q", clef: "alto" }), 90 | new Vex.Flow.ClefNote("tenor_small"), 91 | new Vex.Flow.StaveNote({ keys: ["c/4"], duration: "q", clef: "tenor" }), 92 | new Vex.Flow.ClefNote("soprano_small"), 93 | new Vex.Flow.StaveNote({ keys: ["c/4"], duration: "q", clef: "soprano" }), 94 | new Vex.Flow.ClefNote("bass_small"), 95 | new Vex.Flow.StaveNote({ keys: ["c/4"], duration: "q", clef: "bass" }), 96 | new Vex.Flow.ClefNote("mezzo-soprano_small"), 97 | new Vex.Flow.StaveNote({ keys: ["c/4"], duration: "q", clef: "mezzo-soprano" }), 98 | new Vex.Flow.ClefNote("baritone-c_small"), 99 | new Vex.Flow.StaveNote({ keys: ["c/4"], duration: "q", clef: "baritone-c" }), 100 | new Vex.Flow.ClefNote("baritone-f_small"), 101 | new Vex.Flow.StaveNote({ keys: ["c/4"], duration: "q", clef: "baritone-f" }), 102 | new Vex.Flow.ClefNote("subbass_small"), 103 | new Vex.Flow.StaveNote({ keys: ["c/4"], duration: "q", clef: "subbass" }), 104 | new Vex.Flow.ClefNote("french_small"), 105 | new Vex.Flow.StaveNote({ keys: ["c/4"], duration: "q", clef: "french" }) 106 | ]; 107 | 108 | var voice = new Vex.Flow.Voice({ 109 | num_beats: 10, 110 | beat_value: 4, 111 | resolution: Vex.Flow.RESOLUTION 112 | }); 113 | 114 | voice.addTickables(notes); 115 | 116 | var formatter = new Vex.Flow.Formatter(). 117 | joinVoices([voice]).format([voice], 500); 118 | 119 | voice.draw(ctx, stave); 120 | ok(true, "all pass"); 121 | } 122 | 123 | -------------------------------------------------------------------------------- /tests/dot_tests.js: -------------------------------------------------------------------------------- 1 | /** 2 | * VexFlow - Dot Tests 3 | * Copyright Mohit Muthanna 2010 4 | */ 5 | 6 | Vex.Flow.Test.Dot = {} 7 | 8 | Vex.Flow.Test.Dot.Start = function() { 9 | module("Dot"); 10 | Vex.Flow.Test.runTest("Basic", Vex.Flow.Test.Dot.basic); 11 | Vex.Flow.Test.runRaphaelTest("Basic (Raphael)", 12 | Vex.Flow.Test.Dot.basic); 13 | Vex.Flow.Test.runTest("Multi Voice", Vex.Flow.Test.Dot.multiVoice); 14 | } 15 | 16 | Vex.Flow.Test.Dot.showNote = function(note, stave, ctx, x) { 17 | var mc = new Vex.Flow.ModifierContext(); 18 | note.addToModifierContext(mc); 19 | 20 | var tickContext = new Vex.Flow.TickContext(); 21 | tickContext.addTickable(note).preFormat().setX(x).setPixelsUsed(65); 22 | 23 | note.setContext(ctx).setStave(stave); 24 | note.draw(); 25 | 26 | ctx.save(); 27 | ctx.font = "10pt Arial"; ctx.strokeStyle = "#579"; ctx.fillStyle = "#345"; 28 | ctx.fillText("w: " + note.getWidth(), note.getAbsoluteX() - 25, 200 / 1.5); 29 | 30 | ctx.beginPath(); 31 | ctx.moveTo(note.getAbsoluteX() - (note.getWidth() / 2), 210/1.5); 32 | ctx.lineTo(note.getAbsoluteX() + (note.getWidth() / 2), 210/1.5); 33 | ctx.stroke(); 34 | ctx.restore(); 35 | return note; 36 | } 37 | 38 | Vex.Flow.Test.Dot.basic = function(options, contextBuilder) { 39 | var ctx = new contextBuilder(options.canvas_sel, 1000, 240); 40 | ctx.scale(1.5, 1.5); ctx.setFillStyle("#221"); ctx.setStrokeStyle("#221"); 41 | var stave = new Vex.Flow.Stave(10, 10, 975); 42 | stave.setContext(ctx); 43 | stave.draw(); 44 | 45 | function newNote(note_struct) { return new Vex.Flow.StaveNote(note_struct); } 46 | function newAcc(type) { return new Vex.Flow.Dot(type); } 47 | 48 | var notes = [ 49 | newNote({ keys: ["c/4", "e/4", "a/4", "b/4"], duration: "w"}). 50 | addDotToAll(), 51 | 52 | newNote({ keys: ["c/5", "b/4", "a/4"], 53 | duration: "q", stem_direction: 1}). 54 | addDotToAll(), 55 | 56 | newNote({ keys: ["b/4", "a/4", "g/4"], 57 | duration: "q", stem_direction: -1}). 58 | addDotToAll(), 59 | 60 | newNote({ keys: ["c/5", "b/4", "f/4", "e/4"], 61 | duration: "q"}). 62 | addDotToAll(), 63 | 64 | newNote({ keys: ["g/5", "e/5", "d/5", "a/4", "g/4"], 65 | duration: "q", stem_direction: -1}). 66 | addDotToAll(), 67 | 68 | newNote({ keys: ["e/5", "d/5", "b/4", "g/4"], 69 | duration: "q", stem_direction: -1}). 70 | addDotToAll(), 71 | 72 | newNote({ keys: ["c/5", "b/4", "g/4", "e/4"], 73 | duration: "q", stem_direction: 1}). 74 | addDotToAll(), 75 | 76 | newNote({ keys: ["d/4", "e/4", "f/4", "a/4", "c/5", "e/5", "g/5"], 77 | duration: "h"}). 78 | addDotToAll(). 79 | addDotToAll(), 80 | 81 | newNote({ keys: ["f/4", "g/4", "a/4", "b/4", "c/5", "e/5", "g/5"], 82 | duration: "16", stem_direction: -1}). 83 | addDotToAll(). 84 | addDotToAll(). 85 | addDotToAll() 86 | ]; 87 | 88 | for (var i = 0; i < notes.length; ++i) { 89 | Vex.Flow.Test.Dot.showNote(notes[i], stave, ctx, 30 + (i * 65)); 90 | var accidentals = notes[i].getDots(); 91 | ok(accidentals.length > 0, "Note " + i + " has accidentals"); 92 | 93 | for (var j = 0; j < accidentals.length; ++j) { 94 | ok(accidentals[j].width > 0, "Dot " + j + " has set width"); 95 | } 96 | } 97 | 98 | ok(true, "Full Dot"); 99 | } 100 | 101 | Vex.Flow.Test.Dot.showNotes = function(note1, note2, stave, ctx, x) { 102 | var mc = new Vex.Flow.ModifierContext(); 103 | note1.addToModifierContext(mc); 104 | note2.addToModifierContext(mc); 105 | 106 | var tickContext = new Vex.Flow.TickContext(); 107 | tickContext.addTickable(note1).addTickable(note2). 108 | preFormat().setX(x).setPixelsUsed(65); 109 | 110 | note1.setContext(ctx).setStave(stave).draw(); 111 | note2.setContext(ctx).setStave(stave).draw(); 112 | 113 | ctx.save(); 114 | ctx.font = "10pt Arial"; ctx.strokeStyle = "#579"; ctx.fillStyle = "#345"; 115 | ctx.fillText("w: " + note2.getWidth(), note2.getAbsoluteX() + 15, 20 / 1.5); 116 | ctx.fillText("w: " + note1.getWidth(), note1.getAbsoluteX() - 25, 220 / 1.5); 117 | 118 | ctx.beginPath(); 119 | ctx.moveTo(note1.getAbsoluteX() - (note1.getWidth() / 2), 230/1.5); 120 | ctx.lineTo(note1.getAbsoluteX() + (note1.getWidth() / 2), 230/1.5); 121 | ctx.stroke(); 122 | ctx.restore(); 123 | } 124 | 125 | Vex.Flow.Test.Dot.multiVoice = function(options, contextBuilder) { 126 | var ctx = new contextBuilder(options.canvas_sel, 400, 150); 127 | 128 | ctx.scale(0.9, 0.9); ctx.fillStyle = "#221"; ctx.strokeStyle = "#221"; 129 | var stave = new Vex.Flow.Stave(10, 10, 420); 130 | stave.setContext(ctx); 131 | stave.draw(); 132 | 133 | function newNote(note_struct) { return new Vex.Flow.StaveNote(note_struct); } 134 | function newAcc(type) { return new Vex.Flow.Dot(type); } 135 | 136 | var note1 = newNote( 137 | { keys: ["c/4", "e/4", "a/4"], duration: "h", stem_direction: -1}). 138 | addDotToAll(). 139 | addDotToAll(); 140 | var note2 = newNote( 141 | { keys: ["d/5", "a/5", "b/5"], duration: "h", stem_direction: 1}). 142 | addDotToAll(); 143 | 144 | Vex.Flow.Test.Dot.showNotes(note1, note2, stave, ctx, 60); 145 | 146 | note1 = newNote( 147 | { keys: ["c/4", "e/4", "c/5"], duration: "h", stem_direction: -1}). 148 | addDot(0). 149 | addDot(0). 150 | addDot(1). 151 | addDot(1). 152 | addDot(2). 153 | addDot(2). 154 | addDot(2); 155 | note2 = newNote( 156 | { keys: ["d/5", "a/5", "b/5"], duration: "q", stem_direction: 1}). 157 | addDotToAll(). 158 | addDotToAll(); 159 | 160 | Vex.Flow.Test.Dot.showNotes(note1, note2, stave, ctx, 150); 161 | 162 | note1 = newNote( 163 | { keys: ["d/4", "c/5", "d/5"], duration: "h", stem_direction: -1}). 164 | addDotToAll(). 165 | addDotToAll(). 166 | addDot(0); 167 | note2 = newNote( 168 | { keys: ["d/5", "a/5", "b/5"], duration: "q", stem_direction: 1}). 169 | addDotToAll(); 170 | 171 | Vex.Flow.Test.Dot.showNotes(note1, note2, stave, ctx, 250); 172 | ok(true, "Full Dot"); 173 | } 174 | -------------------------------------------------------------------------------- /tests/keymanager_tests.js: -------------------------------------------------------------------------------- 1 | /** 2 | * VexFlow - Music Tests 3 | * Copyright Mohit Muthanna 2010 4 | */ 5 | 6 | Vex.Flow.Test.KeyManager = {} 7 | 8 | Vex.Flow.Test.KeyManager.Start = function() { 9 | module("KeyManager"); 10 | test("Valid Notes", Vex.Flow.Test.KeyManager.works); 11 | test("Select Notes", Vex.Flow.Test.KeyManager.selectNotes); 12 | } 13 | 14 | Vex.Flow.Test.KeyManager.works = function() { 15 | // expect(1); 16 | 17 | var manager = new Vex.Flow.KeyManager("g"); 18 | equal(manager.getAccidental("f").accidental, "#"); 19 | 20 | manager.setKey("a"); 21 | equal(manager.getAccidental("c").accidental, "#"); 22 | equal(manager.getAccidental("a").accidental, null); 23 | equal(manager.getAccidental("f").accidental, "#"); 24 | 25 | manager.setKey("A"); 26 | equal(manager.getAccidental("c").accidental, "#"); 27 | equal(manager.getAccidental("a").accidental, null); 28 | equal(manager.getAccidental("f").accidental, "#"); 29 | } 30 | 31 | Vex.Flow.Test.KeyManager.selectNotes = function(options) { 32 | var manager = new Vex.Flow.KeyManager("f"); 33 | equal(manager.selectNote("bb").note, "bb"); 34 | equal(manager.selectNote("bb").accidental, "b"); 35 | equal(manager.selectNote("g").note, "g"); 36 | equal(manager.selectNote("g").accidental, null); 37 | equal(manager.selectNote("b").note, "b"); 38 | equal(manager.selectNote("b").accidental, null); 39 | equal(manager.selectNote("a#").note, "bb"); 40 | equal(manager.selectNote("g#").note, "g#"); 41 | 42 | // Changes have no effect? 43 | equal(manager.selectNote("g#").note, "g#"); 44 | equal(manager.selectNote("bb").note, "bb"); 45 | equal(manager.selectNote("bb").accidental, "b"); 46 | equal(manager.selectNote("g").note, "g"); 47 | equal(manager.selectNote("g").accidental, null); 48 | equal(manager.selectNote("b").note, "b"); 49 | equal(manager.selectNote("b").accidental, null); 50 | equal(manager.selectNote("a#").note, "bb"); 51 | equal(manager.selectNote("g#").note, "g#"); 52 | 53 | // Changes should propagate 54 | manager.reset(); 55 | equal(manager.selectNote("g#").change, true); 56 | equal(manager.selectNote("g#").change, false); 57 | equal(manager.selectNote("g").change, true); 58 | equal(manager.selectNote("g").change, false); 59 | equal(manager.selectNote("g#").change, true); 60 | 61 | manager.reset(); 62 | var note = manager.selectNote("bb"); 63 | equal(note.change, false); 64 | equal(note.accidental, "b"); 65 | note = manager.selectNote("g"); 66 | equal(note.change, false); 67 | equal(note.accidental, null); 68 | note = manager.selectNote("g#"); 69 | equal(note.change, true); 70 | equal(note.accidental, "#"); 71 | note = manager.selectNote("g"); 72 | equal(note.change, true); 73 | equal(note.accidental, null); 74 | note = manager.selectNote("g"); 75 | equal(note.change, false); 76 | equal(note.accidental, null); 77 | note = manager.selectNote("g#"); 78 | equal(note.change, true); 79 | equal(note.accidental, "#"); 80 | } 81 | -------------------------------------------------------------------------------- /tests/keysignature_tests.js: -------------------------------------------------------------------------------- 1 | // VexFlow - Basic Tests 2 | 3 | Vex.Flow.Test.KeySignature = {} 4 | 5 | Vex.Flow.Test.KeySignature.MAJOR_KEYS = [ 6 | "C", 7 | "F", 8 | "Bb", 9 | "Eb", 10 | "Ab", 11 | "Db", 12 | "Gb", 13 | "Cb", 14 | "G", 15 | "D", 16 | "A", 17 | "E", 18 | "B", 19 | "F#", 20 | "C#"]; 21 | 22 | Vex.Flow.Test.KeySignature.MINOR_KEYS = [ 23 | "Am", 24 | "Dm", 25 | "Gm", 26 | "Cm", 27 | "Fm", 28 | "Bbm", 29 | "Ebm", 30 | "Abm", 31 | "Em", 32 | "Bm", 33 | "F#m", 34 | "C#m", 35 | "G#m", 36 | "D#m", 37 | "A#m"]; 38 | 39 | 40 | Vex.Flow.Test.KeySignature.Start = function() { 41 | module("KeySignature"); 42 | test("Key Parser Test", Vex.Flow.Test.KeySignature.parser); 43 | Vex.Flow.Test.runTest("Major Key Test", Vex.Flow.Test.KeySignature.majorKeys); 44 | Vex.Flow.Test.runRaphaelTest("Major Key Test (Raphael)", 45 | Vex.Flow.Test.KeySignature.majorKeys); 46 | Vex.Flow.Test.runTest("Minor Key Test", Vex.Flow.Test.KeySignature.minorKeys); 47 | Vex.Flow.Test.runRaphaelTest("Minor Key Test (Raphael)", 48 | Vex.Flow.Test.KeySignature.minorKeys); 49 | Vex.Flow.Test.runTest("Stave Helper", Vex.Flow.Test.KeySignature.staveHelper); 50 | 51 | } 52 | 53 | Vex.Flow.Test.KeySignature.catchError = function(spec) { 54 | try { 55 | Vex.Flow.keySignature(spec); 56 | } catch (e) { 57 | equal(e.code, "BadKeySignature", e.message); 58 | } 59 | } 60 | 61 | Vex.Flow.Test.KeySignature.parser = function() { 62 | expect(11); 63 | Vex.Flow.Test.KeySignature.catchError("asdf"); 64 | Vex.Flow.Test.KeySignature.catchError("D!"); 65 | Vex.Flow.Test.KeySignature.catchError("E#"); 66 | Vex.Flow.Test.KeySignature.catchError("D#"); 67 | Vex.Flow.Test.KeySignature.catchError("#"); 68 | Vex.Flow.Test.KeySignature.catchError("b"); 69 | Vex.Flow.Test.KeySignature.catchError("Kb"); 70 | Vex.Flow.Test.KeySignature.catchError("Fb"); 71 | Vex.Flow.Test.KeySignature.catchError("Ab"); 72 | Vex.Flow.Test.KeySignature.catchError("Dbm"); 73 | Vex.Flow.Test.KeySignature.catchError("B#m"); 74 | 75 | Vex.Flow.keySignature("B"); 76 | Vex.Flow.keySignature("C"); 77 | Vex.Flow.keySignature("Fm"); 78 | Vex.Flow.keySignature("Ab"); 79 | Vex.Flow.keySignature("Abm"); 80 | Vex.Flow.keySignature("F#"); 81 | Vex.Flow.keySignature("G#m"); 82 | 83 | ok(true, "all pass"); 84 | } 85 | 86 | Vex.Flow.Test.KeySignature.majorKeys = function(options, contextBuilder) { 87 | var ctx = new contextBuilder(options.canvas_sel, 400, 240); 88 | var stave = new Vex.Flow.Stave(10, 10, 350); 89 | var stave2 = new Vex.Flow.Stave(10, 90, 350); 90 | var keys = Vex.Flow.Test.KeySignature.MAJOR_KEYS; 91 | 92 | var keySig = null; 93 | for (var i = 0; i < 8; ++i) { 94 | keySig = new Vex.Flow.KeySignature(keys[i]); 95 | keySig.addToStave(stave); 96 | } 97 | 98 | for (var n = 8; n < keys.length; ++n) { 99 | keySig = new Vex.Flow.KeySignature(keys[n]); 100 | keySig.addToStave(stave2); 101 | } 102 | 103 | 104 | stave.setContext(ctx); 105 | stave.draw(); 106 | stave2.setContext(ctx); 107 | stave2.draw(); 108 | 109 | ok(true, "all pass"); 110 | } 111 | 112 | Vex.Flow.Test.KeySignature.minorKeys = function(options, contextBuilder) { 113 | var ctx = new contextBuilder(options.canvas_sel, 400, 240); 114 | var stave = new Vex.Flow.Stave(10, 10, 350); 115 | var stave2 = new Vex.Flow.Stave(10, 90, 350); 116 | var keys = Vex.Flow.Test.KeySignature.MINOR_KEYS; 117 | 118 | var keySig = null; 119 | for (var i = 0; i < 8; ++i) { 120 | keySig = new Vex.Flow.KeySignature(keys[i]); 121 | keySig.addToStave(stave); 122 | } 123 | 124 | for (var n = 8; n < keys.length; ++n) { 125 | keySig = new Vex.Flow.KeySignature(keys[n]); 126 | keySig.addToStave(stave2); 127 | } 128 | 129 | 130 | stave.setContext(ctx); 131 | stave.draw(); 132 | stave2.setContext(ctx); 133 | stave2.draw(); 134 | 135 | ok(true, "all pass"); 136 | } 137 | 138 | Vex.Flow.Test.KeySignature.staveHelper = function(options, contextBuilder) { 139 | var ctx = new contextBuilder(options.canvas_sel, 400, 240); 140 | var stave = new Vex.Flow.Stave(10, 10, 350); 141 | var stave2 = new Vex.Flow.Stave(10, 90, 350); 142 | var keys = Vex.Flow.Test.KeySignature.MAJOR_KEYS; 143 | 144 | for (var i = 0; i < 8; ++i) { 145 | stave.addKeySignature(keys[i]); 146 | } 147 | 148 | for (var n = 8; n < keys.length; ++n) { 149 | stave2.addKeySignature(keys[n]); 150 | } 151 | 152 | stave.setContext(ctx); 153 | stave.draw(); 154 | stave2.setContext(ctx); 155 | stave2.draw(); 156 | 157 | ok(true, "all pass"); 158 | } 159 | -------------------------------------------------------------------------------- /tests/mocks.js: -------------------------------------------------------------------------------- 1 | /** 2 | * VexFlow - TickContext Tests 3 | * Copyright Mohit Muthanna 2010 4 | */ 5 | 6 | Vex.Flow.Test.TIME4_4 = { 7 | num_beats: 4, 8 | beat_value: 4, 9 | resolution: Vex.Flow.RESOLUTION 10 | }; 11 | 12 | /* Mock Tickable */ 13 | 14 | Vex.Flow.Test.MockTickable = function() { this.ignore_ticks = false; } 15 | Vex.Flow.Test.MockTickable.prototype.getX = function() { 16 | return this.tickContext.getX();} 17 | Vex.Flow.Test.MockTickable.prototype.getIntrinsicTicks = function() {return this.ticks;} 18 | Vex.Flow.Test.MockTickable.prototype.getTicks = function() {return this.ticks;} 19 | Vex.Flow.Test.MockTickable.prototype.setTicks = function(t) { 20 | this.ticks = new Vex.Flow.Fraction(t, 1); return this; }; 21 | Vex.Flow.Test.MockTickable.prototype.getMetrics = function() { 22 | return { noteWidth: this.width, 23 | left_shift: 0, 24 | modLeftPx: 0, modRightPx: 0, 25 | extraLeftPx: 0, extraRightPx: 0 }; 26 | } 27 | Vex.Flow.Test.MockTickable.prototype.getWidth = function() {return this.width;} 28 | Vex.Flow.Test.MockTickable.prototype.setWidth = function(w) { 29 | this.width = w; return this; } 30 | Vex.Flow.Test.MockTickable.prototype.setVoice = function(v) { 31 | this.voice = v; return this; } 32 | Vex.Flow.Test.MockTickable.prototype.setStave = function(stave) { 33 | this.stave = stave; return this; } 34 | Vex.Flow.Test.MockTickable.prototype.setTickContext = function(tc) { 35 | this.tickContext = tc; return this; } 36 | Vex.Flow.Test.MockTickable.prototype.setIgnoreTicks = function(ignore_ticks) { 37 | this.ignore_ticks = ignore_ticks; return this; } 38 | Vex.Flow.Test.MockTickable.prototype.shouldIgnoreTicks = function() { 39 | return this.ignore_ticks; } 40 | Vex.Flow.Test.MockTickable.prototype.preFormat = function() {} 41 | 42 | /* Mock Modifier */ 43 | 44 | Vex.Flow.Test.MockModifier = Vex.Flow.Modifier; 45 | -------------------------------------------------------------------------------- /tests/modifier_tests.js: -------------------------------------------------------------------------------- 1 | /** 2 | * VexFlow - Modifier Tests 3 | * Copyright Mohit Muthanna 2010 4 | */ 5 | 6 | Vex.Flow.Test.ModifierContext = {} 7 | 8 | Vex.Flow.Test.ModifierContext.Start = function() { 9 | module("ModifierContext"); 10 | test("Modifier Width Test", Vex.Flow.Test.ModifierContext.width); 11 | test("Modifier Management", Vex.Flow.Test.ModifierContext.management); 12 | } 13 | 14 | Vex.Flow.Test.ModifierContext.width = function() { 15 | var mc = new Vex.Flow.ModifierContext(); 16 | equal(mc.getWidth(), 0, "New modifier context has no width"); 17 | } 18 | 19 | Vex.Flow.Test.ModifierContext.management = function() { 20 | var mc = new Vex.Flow.ModifierContext(); 21 | var modifier1 = new Vex.Flow.Test.MockModifier; 22 | var modifier2 = new Vex.Flow.Test.MockModifier; 23 | 24 | mc.addModifier(modifier1); 25 | mc.addModifier(modifier2); 26 | 27 | var accidentals = mc.getModifiers("none"); 28 | 29 | equal(accidentals.length, 2, "Added two modifiers"); 30 | } 31 | -------------------------------------------------------------------------------- /tests/notehead_tests.js: -------------------------------------------------------------------------------- 1 | Vex.Flow.Test.NoteHead = {}; 2 | 3 | Vex.Flow.Test.NoteHead.Start = function(){ 4 | module("NoteHead"); 5 | Vex.Flow.Test.runTest("Basic", Vex.Flow.Test.NoteHead.basic); 6 | Vex.Flow.Test.runTest("Bounding Boxes", Vex.Flow.Test.NoteHead.basicBoundingBoxes); 7 | }; 8 | 9 | Vex.Flow.Test.NoteHead.setupContext = function(options, x, y) { 10 | 11 | var ctx = new options.contextBuilder(options.canvas_sel, x || 450, y || 140); 12 | ctx.scale(0.9, 0.9); ctx.fillStyle = "#221"; ctx.strokeStyle = "#221"; 13 | ctx.font = " 10pt Arial"; 14 | var stave = new Vex.Flow.Stave(10, 10, x || 450).addTrebleGlyph(); 15 | 16 | return {context: ctx, stave: stave}; 17 | }; 18 | 19 | Vex.Flow.Test.NoteHead.basic = function(options, contextBuilder){ 20 | options.contextBuilder = contextBuilder; 21 | var c = Vex.Flow.Test.NoteHead.setupContext(options, 450, 250); 22 | 23 | c.stave = new Vex.Flow.Stave(10, 0, 250).addTrebleGlyph(); 24 | 25 | c.context.scale(2.0, 2.0); 26 | c.stave.setContext(c.context).draw(); 27 | 28 | var formatter = new Vex.Flow.Formatter(); 29 | var voice = new Vex.Flow.Voice(Vex.Flow.TIME4_4).setStrict(false); 30 | 31 | var note_head1 = new Vex.Flow.NoteHead({ 32 | duration: "4", 33 | line: 3 34 | }); 35 | 36 | var note_head2 = new Vex.Flow.NoteHead({ 37 | duration: "1", 38 | line: 2.5 39 | }); 40 | 41 | var note_head3 = new Vex.Flow.NoteHead({ 42 | duration: "2", 43 | line: 0 44 | }); 45 | 46 | voice.addTickables([note_head1, note_head2, note_head3]); 47 | formatter.joinVoices([voice]).formatToStave([voice], c.stave); 48 | 49 | voice.draw(c.context, c.stave); 50 | 51 | ok("Basic NoteHead test"); 52 | }; 53 | 54 | Vex.Flow.Test.NoteHead.basicBoundingBoxes = function(options, contextBuilder){ 55 | options.contextBuilder = contextBuilder; 56 | var c = Vex.Flow.Test.NoteHead.setupContext(options, 350, 250); 57 | 58 | c.stave = new Vex.Flow.Stave(10, 0, 250).addTrebleGlyph(); 59 | 60 | c.context.scale(2.0, 2.0); 61 | c.stave.setContext(c.context).draw(); 62 | 63 | var formatter = new Vex.Flow.Formatter(); 64 | var voice = new Vex.Flow.Voice(Vex.Flow.TIME4_4).setStrict(false); 65 | 66 | var note_head1 = new Vex.Flow.NoteHead({ 67 | duration: "4", 68 | line: 3 69 | }); 70 | 71 | var note_head2 = new Vex.Flow.NoteHead({ 72 | duration: "2", 73 | line: 2.5 74 | }); 75 | 76 | var note_head3 = new Vex.Flow.NoteHead({ 77 | duration: "1", 78 | line: 0 79 | }); 80 | 81 | voice.addTickables([note_head1, note_head2, note_head3]); 82 | formatter.joinVoices([voice]).formatToStave([voice], c.stave); 83 | 84 | voice.draw(c.context, c.stave); 85 | 86 | note_head1.getBoundingBox().draw(c.context); 87 | note_head2.getBoundingBox().draw(c.context); 88 | note_head3.getBoundingBox().draw(c.context); 89 | 90 | ok("NoteHead Bounding Boxes"); 91 | }; 92 | 93 | -------------------------------------------------------------------------------- /tests/stavemodifier_tests.js: -------------------------------------------------------------------------------- 1 | // VexFlow - Basic Tests 2 | 3 | Vex.Flow.Test.StaveModifier = {} 4 | 5 | Vex.Flow.Test.StaveModifier.Start = function() { 6 | module("StaveModifier"); 7 | Vex.Flow.Test.runTest("Stave Draw Test (Canvas)", Vex.Flow.Test.Stave.draw); 8 | Vex.Flow.Test.runTest("Vertical Bar Test (Canvas)", 9 | Vex.Flow.Test.Stave.drawVerticalBar); 10 | Vex.Flow.Test.runRaphaelTest("Stave Draw Test (Raphael)", 11 | Vex.Flow.Test.Stave.draw); 12 | Vex.Flow.Test.runRaphaelTest("Vertical Bar Test (Raphael)", 13 | Vex.Flow.Test.Stave.drawVerticalBar); 14 | } 15 | 16 | Vex.Flow.Test.Stave.draw = function(options, contextBuilder) { 17 | var ctx = new contextBuilder(options.canvas_sel, 400, 120); 18 | var stave = new Vex.Flow.Stave(10, 10, 300); 19 | stave.setContext(ctx); 20 | stave.draw(); 21 | 22 | equal(stave.getYForNote(0), 100, "getYForNote(0)"); 23 | equal(stave.getYForLine(5), 100, "getYForLine(5)"); 24 | equal(stave.getYForLine(0), 50, "getYForLine(0) - Top Line"); 25 | equal(stave.getYForLine(4), 90, "getYForLine(4) - Bottom Line"); 26 | 27 | ok(true, "all pass"); 28 | } 29 | 30 | Vex.Flow.Test.Stave.drawVerticalBar = function(options, contextBuilder) { 31 | var ctx = contextBuilder(options.canvas_sel, 400, 120); 32 | var stave = new Vex.Flow.Stave(10, 10, 300); 33 | stave.setContext(ctx); 34 | stave.draw(); 35 | stave.drawVerticalBar(100); 36 | stave.drawVerticalBar(150); 37 | stave.drawVerticalBar(300); 38 | 39 | ok(true, "all pass"); 40 | } 41 | -------------------------------------------------------------------------------- /tests/stavetie_tests.js: -------------------------------------------------------------------------------- 1 | /** 2 | * VexFlow - StaveTie Tests 3 | * Copyright Mohit Muthanna 2010 4 | */ 5 | 6 | Vex.Flow.Test.StaveTie = {} 7 | 8 | Vex.Flow.Test.StaveTie.Start = function() { 9 | module("StaveTie"); 10 | Vex.Flow.Test.runTest("Simple StaveTie", Vex.Flow.Test.StaveTie.simple); 11 | Vex.Flow.Test.runTest("Chord StaveTie", Vex.Flow.Test.StaveTie.chord); 12 | Vex.Flow.Test.runTest("Stem Up StaveTie", Vex.Flow.Test.StaveTie.stemUp); 13 | Vex.Flow.Test.runTest("No End Note", Vex.Flow.Test.StaveTie.noEndNote); 14 | Vex.Flow.Test.runTest("No Start Note", Vex.Flow.Test.StaveTie.noStartNote); 15 | } 16 | 17 | Vex.Flow.Test.StaveTie.tieNotes = function(notes, indices, stave, ctx) { 18 | var voice = new Vex.Flow.Voice(Vex.Flow.Test.TIME4_4); 19 | voice.addTickables(notes); 20 | 21 | var formatter = new Vex.Flow.Formatter().joinVoices([voice]). 22 | format([voice], 250); 23 | voice.draw(ctx, stave); 24 | 25 | var tie = new Vex.Flow.StaveTie({ 26 | first_note: notes[0], 27 | last_note: notes[1], 28 | first_indices: indices, 29 | last_indices: indices, 30 | }); 31 | 32 | tie.setContext(ctx); 33 | tie.draw(); 34 | } 35 | 36 | Vex.Flow.Test.StaveTie.drawTie = function(notes, indices, options) { 37 | var ctx = new options.contextBuilder(options.canvas_sel, 350, 140); 38 | 39 | ctx.scale(0.9, 0.9); ctx.fillStyle = "#221"; ctx.strokeStyle = "#221"; 40 | var stave = new Vex.Flow.Stave(10, 10, 350).setContext(ctx).draw(); 41 | 42 | Vex.Flow.Test.StaveTie.tieNotes(notes, indices, stave, ctx); 43 | } 44 | 45 | Vex.Flow.Test.StaveTie.simple = function(options, contextBuilder) { 46 | options.contextBuilder = contextBuilder; 47 | function newNote(note_struct) { return new Vex.Flow.StaveNote(note_struct); } 48 | function newAcc(type) { return new Vex.Flow.Accidental(type); } 49 | 50 | Vex.Flow.Test.StaveTie.drawTie([ 51 | newNote({ keys: ["c/4", "e/4", "a/4"], stem_direction: -1, duration: "h"}). 52 | addAccidental(0, newAcc("b")). 53 | addAccidental(1, newAcc("#")), 54 | newNote({ keys: ["d/4", "e/4", "f/4"], stem_direction: -1, duration: "h"}) 55 | ], [0, 1], options); 56 | ok(true, "Simple Test"); 57 | } 58 | 59 | Vex.Flow.Test.StaveTie.chord = function(options, contextBuilder) { 60 | options.contextBuilder = contextBuilder; 61 | function newNote(note_struct) { return new Vex.Flow.StaveNote(note_struct); } 62 | function newAcc(type) { return new Vex.Flow.Accidental(type); } 63 | 64 | Vex.Flow.Test.StaveTie.drawTie([ 65 | newNote({ keys: ["d/4", "e/4", "f/4"], stem_direction: -1, duration: "h"}), 66 | newNote({ keys: ["c/4", "f/4", "a/4"], stem_direction: -1, duration: "h"}). 67 | addAccidental(0, newAcc("n")). 68 | addAccidental(1, newAcc("#")), 69 | ], [0, 1, 2], options); 70 | ok(true, "Chord test"); 71 | } 72 | 73 | Vex.Flow.Test.StaveTie.stemUp = function(options, contextBuilder) { 74 | options.contextBuilder = contextBuilder; 75 | function newNote(note_struct) { return new Vex.Flow.StaveNote(note_struct); } 76 | function newAcc(type) { return new Vex.Flow.Accidental(type); } 77 | 78 | Vex.Flow.Test.StaveTie.drawTie([ 79 | newNote({ keys: ["d/4", "e/4", "f/4"], stem_direction: 1, duration: "h"}), 80 | newNote({ keys: ["c/4", "f/4", "a/4"], stem_direction: 1, duration: "h"}). 81 | addAccidental(0, newAcc("n")). 82 | addAccidental(1, newAcc("#")), 83 | ], [0, 1, 2], options); 84 | ok(true, "Stem up test"); 85 | } 86 | 87 | Vex.Flow.Test.StaveTie.noEndNote = function(options, contextBuilder) { 88 | var ctx = contextBuilder(options.canvas_sel, 350, 140); 89 | ctx.scale(0.9, 0.9); ctx.fillStyle = "#221"; ctx.strokeStyle = "#221"; 90 | ctx.font = "10pt Arial"; 91 | var stave = new Vex.Flow.Stave(10, 10, 350).setContext(ctx).draw(); 92 | 93 | function newNote(note_struct) { return new Vex.Flow.StaveNote(note_struct); } 94 | function newAcc(type) { return new Vex.Flow.Accidental(type); } 95 | 96 | notes = [ 97 | newNote({ keys: ["c/4", "e/4", "a/4"], stem_direction: -1, duration: "h"}). 98 | addAccidental(0, newAcc("b")). 99 | addAccidental(1, newAcc("#")), 100 | newNote({ keys: ["d/4", "e/4", "f/4"], stem_direction: -1, duration: "h"}) 101 | ]; 102 | 103 | var voice = new Vex.Flow.Voice(Vex.Flow.Test.TIME4_4); 104 | voice.addTickables(notes); 105 | 106 | var formatter = new Vex.Flow.Formatter().joinVoices([voice]). 107 | format([voice], 250); 108 | voice.draw(ctx, stave); 109 | 110 | var tie = new Vex.Flow.StaveTie({ 111 | first_note: notes[1], 112 | last_note: null, 113 | first_indices: [2], 114 | last_indices: [2] 115 | }, "slow.").setContext(ctx).draw(); 116 | 117 | ok(true, "No end note"); 118 | } 119 | 120 | Vex.Flow.Test.StaveTie.noStartNote = function(options, contextBuilder) { 121 | var ctx = contextBuilder(options.canvas_sel, 350, 140); 122 | ctx.scale(0.9, 0.9); ctx.fillStyle = "#221"; ctx.strokeStyle = "#221"; 123 | ctx.font = "10pt Arial"; 124 | var stave = new Vex.Flow.Stave(10, 10, 350).addTrebleGlyph(). 125 | setContext(ctx).draw(); 126 | 127 | function newNote(note_struct) { return new Vex.Flow.StaveNote(note_struct); } 128 | function newAcc(type) { return new Vex.Flow.Accidental(type); } 129 | 130 | notes = [ 131 | newNote({ keys: ["c/4", "e/4", "a/4"], stem_direction: -1, duration: "h"}). 132 | addAccidental(0, newAcc("b")). 133 | addAccidental(1, newAcc("#")), 134 | newNote({ keys: ["d/4", "e/4", "f/4"], stem_direction: -1, duration: "h"}) 135 | ]; 136 | 137 | var voice = new Vex.Flow.Voice(Vex.Flow.Test.TIME4_4); 138 | voice.addTickables(notes); 139 | 140 | var formatter = new Vex.Flow.Formatter().joinVoices([voice]). 141 | format([voice], 250); 142 | voice.draw(ctx, stave); 143 | 144 | var tie = new Vex.Flow.StaveTie({ 145 | first_note: null, 146 | last_note: notes[0], 147 | first_indices: [2], 148 | last_indices: [2], 149 | }, "H").setContext(ctx).draw(); 150 | 151 | ok(true, "No end note"); 152 | } 153 | -------------------------------------------------------------------------------- /tests/support/qunit.css: -------------------------------------------------------------------------------- 1 | /** 2 | * QUnit v1.10.0pre - A JavaScript Unit Testing Framework 3 | * 4 | * http://qunitjs.com 5 | * 6 | * Copyright 2012 jQuery Foundation and other contributors 7 | * Dual licensed under the MIT or GPL Version 2 licenses. 8 | * http://jquery.org/license 9 | */ 10 | 11 | /** Font Family and Sizes */ 12 | 13 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { 14 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; 15 | } 16 | 17 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } 18 | #qunit-tests { font-size: smaller; } 19 | 20 | 21 | /** Resets */ 22 | 23 | #qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult { 24 | margin: 0; 25 | padding: 0; 26 | } 27 | 28 | 29 | /** Header */ 30 | 31 | #qunit-header { 32 | padding: 0.5em 0 0.5em 1em; 33 | 34 | color: #8699a4; 35 | background-color: #0d3349; 36 | 37 | font-size: 1.5em; 38 | line-height: 1em; 39 | font-weight: normal; 40 | 41 | border-radius: 5px 5px 0 0; 42 | -moz-border-radius: 5px 5px 0 0; 43 | -webkit-border-top-right-radius: 5px; 44 | -webkit-border-top-left-radius: 5px; 45 | } 46 | 47 | #qunit-header a { 48 | text-decoration: none; 49 | color: #c2ccd1; 50 | } 51 | 52 | #qunit-header a:hover, 53 | #qunit-header a:focus { 54 | color: #fff; 55 | } 56 | 57 | #qunit-testrunner-toolbar label { 58 | display: inline-block; 59 | padding: 0 .5em 0 .1em; 60 | } 61 | 62 | #qunit-banner { 63 | height: 5px; 64 | } 65 | 66 | #qunit-testrunner-toolbar { 67 | padding: 0.5em 0 0.5em 2em; 68 | color: #5E740B; 69 | background-color: #eee; 70 | } 71 | 72 | #qunit-userAgent { 73 | padding: 0.5em 0 0.5em 2.5em; 74 | background-color: #2b81af; 75 | color: #fff; 76 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; 77 | } 78 | 79 | 80 | /** Tests: Pass/Fail */ 81 | 82 | #qunit-tests { 83 | list-style-position: inside; 84 | } 85 | 86 | #qunit-tests li { 87 | padding: 0.4em 0.5em 0.4em 2.5em; 88 | border-bottom: 1px solid #fff; 89 | list-style-position: inside; 90 | } 91 | 92 | #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { 93 | display: none; 94 | } 95 | 96 | #qunit-tests li strong { 97 | cursor: pointer; 98 | } 99 | 100 | #qunit-tests li a { 101 | padding: 0.5em; 102 | color: #c2ccd1; 103 | text-decoration: none; 104 | } 105 | #qunit-tests li a:hover, 106 | #qunit-tests li a:focus { 107 | color: #000; 108 | } 109 | 110 | #qunit-tests ol { 111 | margin-top: 0.5em; 112 | padding: 0.5em; 113 | 114 | background-color: #fff; 115 | 116 | border-radius: 5px; 117 | -moz-border-radius: 5px; 118 | -webkit-border-radius: 5px; 119 | } 120 | 121 | #qunit-tests table { 122 | border-collapse: collapse; 123 | margin-top: .2em; 124 | } 125 | 126 | #qunit-tests th { 127 | text-align: right; 128 | vertical-align: top; 129 | padding: 0 .5em 0 0; 130 | } 131 | 132 | #qunit-tests td { 133 | vertical-align: top; 134 | } 135 | 136 | #qunit-tests pre { 137 | margin: 0; 138 | white-space: pre-wrap; 139 | word-wrap: break-word; 140 | } 141 | 142 | #qunit-tests del { 143 | background-color: #e0f2be; 144 | color: #374e0c; 145 | text-decoration: none; 146 | } 147 | 148 | #qunit-tests ins { 149 | background-color: #ffcaca; 150 | color: #500; 151 | text-decoration: none; 152 | } 153 | 154 | /*** Test Counts */ 155 | 156 | #qunit-tests b.counts { color: black; } 157 | #qunit-tests b.passed { color: #5E740B; } 158 | #qunit-tests b.failed { color: #710909; } 159 | 160 | #qunit-tests li li { 161 | padding: 5px; 162 | background-color: #fff; 163 | border-bottom: none; 164 | list-style-position: inside; 165 | } 166 | 167 | /*** Passing Styles */ 168 | 169 | #qunit-tests li li.pass { 170 | color: #3c510c; 171 | background-color: #fff; 172 | border-left: 10px solid #C6E746; 173 | } 174 | 175 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } 176 | #qunit-tests .pass .test-name { color: #366097; } 177 | 178 | #qunit-tests .pass .test-actual, 179 | #qunit-tests .pass .test-expected { color: #999999; } 180 | 181 | #qunit-banner.qunit-pass { background-color: #C6E746; } 182 | 183 | /*** Failing Styles */ 184 | 185 | #qunit-tests li li.fail { 186 | color: #710909; 187 | background-color: #fff; 188 | border-left: 10px solid #EE5757; 189 | white-space: pre; 190 | } 191 | 192 | #qunit-tests > li:last-child { 193 | border-radius: 0 0 5px 5px; 194 | -moz-border-radius: 0 0 5px 5px; 195 | -webkit-border-bottom-right-radius: 5px; 196 | -webkit-border-bottom-left-radius: 5px; 197 | } 198 | 199 | #qunit-tests .fail { color: #000000; background-color: #EE5757; } 200 | #qunit-tests .fail .test-name, 201 | #qunit-tests .fail .module-name { color: #000000; } 202 | 203 | #qunit-tests .fail .test-actual { color: #EE5757; } 204 | #qunit-tests .fail .test-expected { color: green; } 205 | 206 | #qunit-banner.qunit-fail { background-color: #EE5757; } 207 | 208 | 209 | /** Result */ 210 | 211 | #qunit-testresult { 212 | padding: 0.5em 0.5em 0.5em 2.5em; 213 | 214 | color: #2b81af; 215 | background-color: #D2E0E6; 216 | 217 | border-bottom: 1px solid white; 218 | } 219 | #qunit-testresult .module-name { 220 | font-weight: bold; 221 | } 222 | 223 | /** Fixture */ 224 | 225 | #qunit-fixture { 226 | position: absolute; 227 | top: -10000px; 228 | left: -10000px; 229 | width: 1000px; 230 | height: 1000px; 231 | } 232 | -------------------------------------------------------------------------------- /tests/tabslide_tests.js: -------------------------------------------------------------------------------- 1 | /** 2 | * VexFlow - TabSlide Tests 3 | * Copyright Mohit Muthanna 2010 4 | */ 5 | 6 | Vex.Flow.Test.TabSlide = {} 7 | 8 | Vex.Flow.Test.TabSlide.Start = function() { 9 | module("TabSlide"); 10 | Vex.Flow.Test.runTest("Simple TabSlide", Vex.Flow.Test.TabSlide.simple); 11 | Vex.Flow.Test.runTest("Slide Up", Vex.Flow.Test.TabSlide.slideUp); 12 | Vex.Flow.Test.runTest("Slide Down", Vex.Flow.Test.TabSlide.slideDown); 13 | } 14 | 15 | Vex.Flow.Test.TabSlide.tieNotes = function(notes, indices, stave, ctx) { 16 | var voice = new Vex.Flow.Voice(Vex.Flow.Test.TIME4_4); 17 | voice.addTickables(notes); 18 | 19 | var formatter = new Vex.Flow.Formatter().joinVoices([voice]). 20 | format([voice], 100); 21 | voice.draw(ctx, stave); 22 | 23 | var tie = new Vex.Flow.TabSlide({ 24 | first_note: notes[0], 25 | last_note: notes[1], 26 | first_indices: indices, 27 | last_indices: indices, 28 | }, Vex.Flow.TabSlide.SLIDE_UP); 29 | 30 | tie.setContext(ctx); 31 | tie.draw(); 32 | } 33 | 34 | Vex.Flow.Test.TabSlide.setupContext = function(options, x, y) { 35 | var ctx = options.contextBuilder(options.canvas_sel, 350, 140); 36 | ctx.scale(0.9, 0.9); ctx.fillStyle = "#221"; ctx.strokeStyle = "#221"; 37 | ctx.font = "10pt Arial"; 38 | var stave = new Vex.Flow.TabStave(10, 10, x || 350).addTabGlyph(). 39 | setContext(ctx).draw(); 40 | 41 | return {context: ctx, stave: stave}; 42 | } 43 | 44 | 45 | Vex.Flow.Test.TabSlide.simple = function(options, contextBuilder) { 46 | options.contextBuilder = contextBuilder; 47 | var c = Vex.Flow.Test.TabSlide.setupContext(options); 48 | function newNote(tab_struct) { return new Vex.Flow.TabNote(tab_struct); } 49 | 50 | Vex.Flow.Test.TabSlide.tieNotes([ 51 | newNote({ positions: [{str:4, fret:4}], duration: "h"}), 52 | newNote({ positions: [{str:4, fret:6}], duration: "h"}) 53 | ], [0], c.stave, c.context); 54 | ok(true, "Simple Test"); 55 | } 56 | 57 | Vex.Flow.Test.TabSlide.multiTest = function(options, factory) { 58 | var c = Vex.Flow.Test.TabSlide.setupContext(options, 440, 100); 59 | function newNote(tab_struct) { return new Vex.Flow.TabNote(tab_struct); } 60 | 61 | var notes = [ 62 | newNote({ positions: [{str:4, fret:4}], duration: "8"}), 63 | newNote({ positions: [{str:4, fret:4}], duration: "8"}), 64 | newNote({ positions: [{str:4, fret:4}, {str:5, fret:4}], duration: "8"}), 65 | newNote({ positions: [{str:4, fret:6}, {str:5, fret:6}], duration: "8"}), 66 | newNote({ positions: [{str:2, fret:14}], duration: "8"}), 67 | newNote({ positions: [{str:2, fret:16}], duration: "8"}), 68 | newNote({ positions: [{str:2, fret:14}, {str:3, fret:14}], duration: "8"}), 69 | newNote({ positions: [{str:2, fret:16}, {str:3, fret:16}], duration: "8"}) 70 | ]; 71 | 72 | var voice = new Vex.Flow.Voice(Vex.Flow.Test.TIME4_4).addTickables(notes); 73 | var formatter = new Vex.Flow.Formatter().joinVoices([voice]). 74 | format([voice], 300); 75 | voice.draw(c.context, c.stave); 76 | 77 | factory({ 78 | first_note: notes[0], 79 | last_note: notes[1], 80 | first_indices: [0], 81 | last_indices: [0], 82 | }).setContext(c.context).draw(); 83 | 84 | ok(true, "Single note"); 85 | 86 | factory({ 87 | first_note: notes[2], 88 | last_note: notes[3], 89 | first_indices: [0, 1], 90 | last_indices: [0, 1], 91 | }).setContext(c.context).draw(); 92 | 93 | ok(true, "Chord"); 94 | 95 | factory({ 96 | first_note: notes[4], 97 | last_note: notes[5], 98 | first_indices: [0], 99 | last_indices: [0], 100 | }).setContext(c.context).draw(); 101 | 102 | ok(true, "Single note high-fret"); 103 | 104 | factory({ 105 | first_note: notes[6], 106 | last_note: notes[7], 107 | first_indices: [0, 1], 108 | last_indices: [0, 1], 109 | }).setContext(c.context).draw(); 110 | 111 | ok(true, "Chord high-fret"); 112 | } 113 | 114 | Vex.Flow.Test.TabSlide.slideUp = function(options, contextBuilder) { 115 | options.contextBuilder = contextBuilder; 116 | Vex.Flow.Test.TabSlide.multiTest(options, Vex.Flow.TabSlide.createSlideUp); 117 | } 118 | 119 | Vex.Flow.Test.TabSlide.slideDown = function(options, contextBuilder) { 120 | options.contextBuilder = contextBuilder; 121 | Vex.Flow.Test.TabSlide.multiTest(options, Vex.Flow.TabSlide.createSlideDown); 122 | } 123 | -------------------------------------------------------------------------------- /tests/tabstave_tests.js: -------------------------------------------------------------------------------- 1 | /** 2 | * VexFlow - Basic Tests 3 | * Copyright Mohit Muthanna 2010 4 | */ 5 | 6 | Vex.Flow.Test.TabStave = {} 7 | 8 | Vex.Flow.Test.TabStave.Start = function() { 9 | module("TabStave"); 10 | Vex.Flow.Test.runRaphaelTest("TabStave Draw Test (Raphael)", 11 | Vex.Flow.Test.TabStave.draw); 12 | Vex.Flow.Test.runRaphaelTest("Vertical Bar Test (Raphael)", 13 | Vex.Flow.Test.TabStave.drawVerticalBar); 14 | Vex.Flow.Test.runTest("TabStave Draw Test", Vex.Flow.Test.TabStave.draw); 15 | Vex.Flow.Test.runTest("Vertical Bar Test", 16 | Vex.Flow.Test.TabStave.drawVerticalBar); 17 | } 18 | 19 | Vex.Flow.Test.TabStave.draw = function(options, contextBuilder) { 20 | var ctx = new contextBuilder(options.canvas_sel, 21 | 400, 160); 22 | 23 | var stave = new Vex.Flow.TabStave(10, 10, 300); 24 | stave.setNumLines(6); 25 | stave.setContext(ctx); 26 | stave.draw(); 27 | 28 | equal(stave.getYForNote(0), 127, "getYForNote(0)"); 29 | equal(stave.getYForLine(5), 126, "getYForLine(5)"); 30 | equal(stave.getYForLine(0), 61, "getYForLine(0) - Top Line"); 31 | equal(stave.getYForLine(4), 113, "getYForLine(4) - Bottom Line"); 32 | 33 | ok(true, "all pass"); 34 | } 35 | 36 | Vex.Flow.Test.TabStave.drawVerticalBar = function(options, contextBuilder) { 37 | var ctx = new contextBuilder(options.canvas_sel, 38 | 400, 160); 39 | 40 | var stave = new Vex.Flow.TabStave(10, 10, 300); 41 | stave.setNumLines(6); 42 | stave.setContext(ctx); 43 | stave.drawVerticalBar(50, true); 44 | stave.drawVerticalBar(100, true); 45 | stave.drawVerticalBar(150, false); 46 | stave.setEndBarType(Vex.Flow.Barline.type.END); 47 | stave.draw(); 48 | 49 | ok(true, "all pass"); 50 | } 51 | -------------------------------------------------------------------------------- /tests/tickcontext_tests.js: -------------------------------------------------------------------------------- 1 | /** 2 | * VexFlow - TickContext Tests 3 | * Copyright Mohit Muthanna 2010 4 | */ 5 | 6 | Vex.Flow.Test.TickContext = {} 7 | 8 | Vex.Flow.Test.TickContext.Start = function() { 9 | module("TickContext"); 10 | test("Current Tick Test", Vex.Flow.Test.TickContext.currentTick); 11 | test("Tracking Test", Vex.Flow.Test.TickContext.tracking); 12 | } 13 | 14 | Vex.Flow.Test.TickContext.currentTick = function() { 15 | var tc = new Vex.Flow.TickContext(); 16 | equal(tc.getCurrentTick().value(), 0, "New tick context has no ticks"); 17 | } 18 | 19 | Vex.Flow.Test.TickContext.tracking = function() { 20 | function createTickable() { 21 | return new Vex.Flow.Test.MockTickable(Vex.Flow.Test.TIME4_4); 22 | } 23 | 24 | var R = Vex.Flow.RESOLUTION; 25 | var BEAT = 1 * R / 4; 26 | 27 | var tickables = [ 28 | createTickable().setTicks(BEAT).setWidth(10), 29 | createTickable().setTicks(BEAT * 2).setWidth(20), 30 | createTickable().setTicks(BEAT).setWidth(30) 31 | ]; 32 | 33 | var tc = new Vex.Flow.TickContext(); 34 | tc.setPadding(0); 35 | 36 | tc.addTickable(tickables[0]); 37 | equal(tc.getMaxTicks().value(), BEAT); 38 | 39 | tc.addTickable(tickables[1]); 40 | equal(tc.getMaxTicks().value(), BEAT * 2); 41 | 42 | tc.addTickable(tickables[2]); 43 | equal(tc.getMaxTicks().value(), BEAT * 2); 44 | 45 | equal(tc.getWidth(), 0); 46 | tc.preFormat(); 47 | equal(tc.getWidth(), 30); 48 | } 49 | -------------------------------------------------------------------------------- /tests/tuning_tests.js: -------------------------------------------------------------------------------- 1 | /** 2 | * VexFlow - Tuning Tests 3 | * Copyright Mohit Muthanna 2010 4 | */ 5 | 6 | Vex.Flow.Test.Tuning = {} 7 | 8 | Vex.Flow.Test.Tuning.Start = function() { 9 | module("Tuning"); 10 | test("Standard Tuning", Vex.Flow.Test.Tuning.standard); 11 | test("Return note for fret", Vex.Flow.Test.Tuning.noteForFret); 12 | } 13 | 14 | Vex.Flow.Test.Tuning.checkStandard = function(tuning) { 15 | try { 16 | tuning.getValueForString(0); 17 | } catch (e) { 18 | equal(e.code, "BadArguments", "String 0"); 19 | } 20 | 21 | try { 22 | tuning.getValueForString(9); 23 | } catch (e) { 24 | equal(e.code, "BadArguments", "String 7"); 25 | } 26 | 27 | equal(tuning.getValueForString(6), 40, "Low E string"); 28 | equal(tuning.getValueForString(5), 45, "A string"); 29 | equal(tuning.getValueForString(4), 50, "D string"); 30 | equal(tuning.getValueForString(3), 55, "G string"); 31 | equal(tuning.getValueForString(2), 59, "B string"); 32 | equal(tuning.getValueForString(1), 64, "High E string"); 33 | } 34 | 35 | Vex.Flow.Test.Tuning.standard = function() { 36 | expect(16); 37 | 38 | var tuning = new Vex.Flow.Tuning(); 39 | Vex.Flow.Test.Tuning.checkStandard(tuning); 40 | 41 | // Test named tuning 42 | tuning.setTuning("standard"); 43 | Vex.Flow.Test.Tuning.checkStandard(tuning); 44 | } 45 | 46 | Vex.Flow.Test.Tuning.noteForFret = function() { 47 | expect(8); 48 | var tuning = new Vex.Flow.Tuning("E/5,B/4,G/4,D/4,A/3,E/3"); 49 | try { 50 | tuning.getNoteForFret(-1, 1); 51 | } catch(e) { 52 | equal(e.code, "BadArguments", "Fret -1"); 53 | } 54 | 55 | try { 56 | tuning.getNoteForFret(1, -1); 57 | } catch(e) { 58 | equal(e.code, "BadArguments", "String -1"); 59 | } 60 | 61 | equal(tuning.getNoteForFret(0, 1), "E/5", "High E string"); 62 | equal(tuning.getNoteForFret(5, 1), "A/5", "High E string, fret 5"); 63 | equal(tuning.getNoteForFret(0, 2), "B/4", "B string"); 64 | equal(tuning.getNoteForFret(0, 3), "G/4", "G string"); 65 | equal(tuning.getNoteForFret(12, 2), "B/5", "B string, fret 12"); 66 | equal(tuning.getNoteForFret(0, 6), "E/3", "Low E string"); 67 | } 68 | -------------------------------------------------------------------------------- /tests/vexflow_test_helpers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * VexFlow Test Support Library 3 | * Copyright Mohit Muthanna 2010 4 | */ 5 | 6 | Vex.Flow.Test = {} 7 | 8 | Vex.Flow.Test.Font = {size: 10} 9 | 10 | Vex.Flow.Test.genID = function() { 11 | return Vex.Flow.Test.genID.ID++; 12 | } 13 | Vex.Flow.Test.genID.ID = 0; 14 | 15 | Vex.Flow.setupCanvasArray = function () { return ""; } 16 | Vex.Flow.Test.createTestCanvas = function(canvas_sel_name, test_name) { 17 | var sel = Vex.Flow.Test.createTestCanvas.sel; 18 | var test_div = $('
').addClass("testcanvas"); 19 | test_div.append($('
').addClass("name").text(test_name)); 20 | test_div.append($('').addClass("vex-tabdiv"). 21 | attr("id", canvas_sel_name). 22 | addClass("name").text(name)); 23 | $(sel).append(test_div); 24 | } 25 | Vex.Flow.Test.createTestCanvas.sel = "#vexflow_testoutput"; 26 | 27 | Vex.Flow.Test.createTestRaphael = function(canvas_sel_name, test_name) { 28 | var sel = Vex.Flow.Test.createTestCanvas.sel; 29 | var test_div = $('
').addClass("testcanvas"); 30 | test_div.append($('
').addClass("name").text(test_name)); 31 | test_div.append($('
').addClass("vex-tabdiv"). 32 | attr("id", canvas_sel_name)); 33 | $(sel).append(test_div); 34 | } 35 | 36 | Vex.Flow.Test.resizeCanvas = function(sel, width, height) { 37 | $("#" + sel).width(width); 38 | $("#" + sel).attr("width", width); 39 | $("#" + sel).attr("height", height); 40 | } 41 | 42 | Vex.Flow.Test.runTest = function(name, func, params) { 43 | test(name, function() { 44 | test_canvas_sel = "canvas_" + Vex.Flow.Test.genID(); 45 | test_canvas = Vex.Flow.Test.createTestCanvas(test_canvas_sel, name); 46 | func({ canvas_sel: test_canvas_sel, params: params }, 47 | Vex.Flow.Renderer.getCanvasContext); 48 | }); 49 | } 50 | 51 | Vex.Flow.Test.runRaphaelTest = function(name, func, params) { 52 | test(name, function() { 53 | test_canvas_sel = "canvas_" + Vex.Flow.Test.genID(); 54 | test_canvas = Vex.Flow.Test.createTestRaphael(test_canvas_sel, name); 55 | func({ canvas_sel: test_canvas_sel, params: params }, 56 | Vex.Flow.Renderer.getRaphaelContext); 57 | }); 58 | } 59 | -------------------------------------------------------------------------------- /tests/vibrato_tests.js: -------------------------------------------------------------------------------- 1 | /** 2 | * VexFlow - Vibrato Tests 3 | * Copyright Mohit Muthanna 2010 4 | */ 5 | 6 | Vex.Flow.Test.Vibrato = {} 7 | 8 | Vex.Flow.Test.Vibrato.Start = function() { 9 | module("Vibrato"); 10 | Vex.Flow.Test.runTest("Simple Vibrato", Vex.Flow.Test.Vibrato.simple); 11 | Vex.Flow.Test.runTest("Harsh Vibrato", Vex.Flow.Test.Vibrato.harsh); 12 | Vex.Flow.Test.runTest("Vibrato with Bend", Vex.Flow.Test.Vibrato.withBend); 13 | Vex.Flow.Test.runRaphaelTest("Vibrato with Bend (Raphael)", 14 | Vex.Flow.Test.Vibrato.withBend); 15 | } 16 | 17 | Vex.Flow.Test.Vibrato.simple = function(options, contextBuilder) { 18 | var ctx = new contextBuilder(options.canvas_sel, 500, 140); 19 | 20 | ctx.scale(1.5, 1.5); ctx.fillStyle = "#221"; ctx.strokeStyle = "#221"; 21 | ctx.font = "10pt Arial"; 22 | var stave = new Vex.Flow.TabStave(10, 10, 450). 23 | addTabGlyph().setContext(ctx).draw(); 24 | 25 | function newNote(tab_struct) { return new Vex.Flow.TabNote(tab_struct); } 26 | function newVibrato() { return new Vex.Flow.Vibrato(); } 27 | 28 | var notes = [ 29 | newNote({ 30 | positions: [{str: 2, fret: 10}, {str: 4, fret: 9}], duration: "h" }). 31 | addModifier(newVibrato(), 0), 32 | newNote({ 33 | positions: [{str: 2, fret: 10}], duration: "h" }). 34 | addModifier(newVibrato(), 0) 35 | ]; 36 | 37 | Vex.Flow.Formatter.FormatAndDraw(ctx, stave, notes); 38 | ok(true, "Simple Vibrato"); 39 | } 40 | 41 | Vex.Flow.Test.Vibrato.harsh = function(options, contextBuilder) { 42 | var ctx = new contextBuilder(options.canvas_sel, 500, 240); 43 | 44 | ctx.scale(1.5, 1.5); ctx.fillStyle = "#221"; ctx.strokeStyle = "#221"; 45 | ctx.font = "10pt Arial"; 46 | var stave = new Vex.Flow.TabStave(10, 10, 450). 47 | addTabGlyph().setContext(ctx).draw(); 48 | 49 | function newNote(tab_struct) { return new Vex.Flow.TabNote(tab_struct); } 50 | function newVibrato() { return new Vex.Flow.Vibrato(); } 51 | 52 | var notes = [ 53 | newNote({ 54 | positions: [{str: 2, fret: 10}, {str: 4, fret: 9}], duration: "h" }). 55 | addModifier(newVibrato().setHarsh(true), 0), 56 | newNote({ 57 | positions: [{str: 2, fret: 10}], duration: "h" }). 58 | addModifier(newVibrato().setHarsh(true), 0) 59 | ]; 60 | 61 | Vex.Flow.Formatter.FormatAndDraw(ctx, stave, notes); 62 | ok(true, "Harsh Vibrato"); 63 | } 64 | 65 | Vex.Flow.Test.Vibrato.withBend = function(options, contextBuilder) { 66 | var ctx = new contextBuilder(options.canvas_sel, 500, 240); 67 | ctx.scale(1.3, 1.3); ctx.setFillStyle("#221"); ctx.setStrokeStyle("#221"); 68 | ctx.setFont("Arial", Vex.Flow.Test.Font.size, ""); 69 | var stave = new Vex.Flow.TabStave(10, 10, 450). 70 | addTabGlyph().setContext(ctx).draw(); 71 | 72 | function newNote(tab_struct) { return new Vex.Flow.TabNote(tab_struct); } 73 | function newBend(text, release) { return new Vex.Flow.Bend(text, release); } 74 | function newVibrato() { return new Vex.Flow.Vibrato(); } 75 | 76 | var notes = [ 77 | newNote({ 78 | positions: [{str: 2, fret: 9}, {str: 3, fret: 9}], duration: "q" }). 79 | addModifier(newBend("1/2", true), 0). 80 | addModifier(newBend("1/2", true), 1). 81 | addModifier(newVibrato(), 0), 82 | newNote({ 83 | positions: [{str: 2, fret: 10}], duration: "q" }). 84 | addModifier(newBend("Full", false), 0). 85 | addModifier(newVibrato().setVibratoWidth(60), 0), 86 | newNote({ 87 | positions: [{str: 2, fret: 10}], duration: "h" }). 88 | addModifier(newVibrato().setVibratoWidth(120).setHarsh(true), 0) 89 | ]; 90 | 91 | Vex.Flow.Formatter.FormatAndDraw(ctx, stave, notes); 92 | ok(true, "Vibrato with Bend"); 93 | } 94 | -------------------------------------------------------------------------------- /tests/voice_tests.js: -------------------------------------------------------------------------------- 1 | /** 2 | * VexFlow - Voice Tests 3 | * Copyright Mohit Muthanna 2010 4 | */ 5 | 6 | Vex.Flow.Test.Voice = {} 7 | 8 | Vex.Flow.Test.Voice.Start = function() { 9 | module("Voice"); 10 | test("Strict Test", Vex.Flow.Test.Voice.strict); 11 | test("Ignore Test", Vex.Flow.Test.Voice.ignore); 12 | Vex.Flow.Test.runTest("Full Voice Mode Test", Vex.Flow.Test.Voice.full); 13 | } 14 | 15 | Vex.Flow.Test.Voice.strict = function(options) { 16 | expect(7); 17 | function createTickable() { 18 | return new Vex.Flow.Test.MockTickable(Vex.Flow.Test.TIME4_4); 19 | } 20 | 21 | var R = Vex.Flow.RESOLUTION; 22 | var BEAT = 1 * R / 4; 23 | 24 | var tickables = [ 25 | createTickable().setTicks(BEAT), 26 | createTickable().setTicks(BEAT), 27 | createTickable().setTicks(BEAT) 28 | ]; 29 | 30 | var voice = new Vex.Flow.Voice(Vex.Flow.Test.TIME4_4); 31 | equal(voice.totalTicks.value(), BEAT * 4, "4/4 Voice has 4 beats"); 32 | equal(voice.ticksUsed.value(), BEAT * 0, "No beats in voice"); 33 | voice.addTickables(tickables); 34 | equal(voice.ticksUsed.value(), BEAT * 3, "Three beats in voice"); 35 | voice.addTickable(createTickable().setTicks(BEAT)); 36 | equal(voice.ticksUsed.value(), BEAT * 4, "Four beats in voice"); 37 | equal(voice.isComplete(), true, "Voice is complete"); 38 | 39 | try { 40 | voice.addTickable(createTickable().setTicks(BEAT)); 41 | } catch (e) { 42 | equal(e.code, "BadArgument", "Too many ticks exception"); 43 | } 44 | 45 | equal(voice.getSmallestTickCount().value(), BEAT, "Smallest tick count is BEAT"); 46 | } 47 | 48 | Vex.Flow.Test.Voice.ignore = function() { 49 | function createTickable() { 50 | return new Vex.Flow.Test.MockTickable(Vex.Flow.Test.TIME4_4); 51 | } 52 | 53 | var R = Vex.Flow.RESOLUTION; 54 | var BEAT = 1 * R / 4; 55 | 56 | var tickables = [ 57 | createTickable().setTicks(BEAT), 58 | createTickable().setTicks(BEAT), 59 | createTickable().setTicks(BEAT).setIgnoreTicks(true), 60 | createTickable().setTicks(BEAT), 61 | createTickable().setTicks(BEAT).setIgnoreTicks(true), 62 | createTickable().setTicks(BEAT) 63 | ]; 64 | 65 | var voice = new Vex.Flow.Voice(Vex.Flow.Test.TIME4_4); 66 | voice.addTickables(tickables); 67 | ok(true, "all pass"); 68 | } 69 | 70 | Vex.Flow.Test.Voice.full = function(options, contextBuilder) { 71 | var ctx = contextBuilder(options.canvas_sel, 550, 200); 72 | 73 | var stave = new Vex.Flow.Stave(10, 50, 500); 74 | stave.addClef("treble").addTimeSignature("4/4"). 75 | setEndBarType(Vex.Flow.Barline.type.END).setContext(ctx).draw(); 76 | 77 | var notes = [ 78 | new Vex.Flow.StaveNote({ keys: ["c/4"], duration: "q" }), 79 | new Vex.Flow.StaveNote({ keys: ["d/4"], duration: "q" }), 80 | new Vex.Flow.StaveNote({ keys: ["b/4"], duration: "qr" }) 81 | ]; 82 | 83 | var voice = new Vex.Flow.Voice(Vex.Flow.Test.TIME4_4). 84 | setMode(Vex.Flow.Voice.Mode.FULL); 85 | voice.addTickables(notes); 86 | 87 | new Vex.Flow.Formatter().joinVoices([voice]).format([voice], 500); 88 | voice.draw(ctx, stave); 89 | voice.getBoundingBox().draw(ctx); 90 | 91 | try { 92 | voice.addTickable( 93 | new Vex.Flow.StaveNote({ keys: ["c/4"], duration: "h" }) 94 | ); 95 | } catch (e) { 96 | equal(e.code, "BadArgument", "Too many ticks exception"); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /tools/find_inconsistent_style.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | # VexFlow - Copyright Mohit Muthanna Cheppudira 2012 4 | # 5 | # Look for >80 lines and hard tabs. 6 | 7 | SRCDIR = "../src" 8 | DIRS = [ "../src/*.js", "../vextab/*.js" ] 9 | 10 | unless File.exists?(SRCDIR) 11 | puts "This script needs to be run from the tools/ directory." 12 | exit 1 13 | end 14 | 15 | DIRS.each do |dir| 16 | files = Dir.glob(dir) 17 | files.each do |file| 18 | line = 0 19 | File.read(file).each_line do |l| 20 | line += 1 21 | puts "#{file}:#{line} -- #{l.length}" if l.length > 80 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /tools/find_missing_includes.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | # VexFlow - Copyright Mohit Muthanna Cheppudira 2012 4 | # 5 | # This script verifies that the .js files in /src are included in all the 6 | # right places, e.g., SCons rules, HTML files, etc. 7 | 8 | require 'pp' 9 | 10 | SRCDIR = "../src" 11 | HTML_FILES = [ 12 | "../tests/flow.html" 13 | 14 | # Might not need these anymore. 15 | # "../docs/tutorial.html", 16 | # "../docs/sandbox.html" 17 | ] 18 | 19 | unless File.exists?(SRCDIR) 20 | puts "This script needs to be run from the tools/ directory." 21 | exit 1 22 | end 23 | 24 | # Extract all .js files in src/ (non recursive) 25 | files = Dir.glob("#{SRCDIR}/*.js").map { |f| f.gsub(SRCDIR + "/", "")} 26 | 27 | # Work through HTML files and print out missing includes 28 | HTML_FILES.each do |html| 29 | test_includes = File.readlines(html). 30 | grep(/^.*script\ssrc.*\.\.\/src.*\.js.*/). 31 | map { |f| f.chomp.gsub(/^.*"(.+)".*$/, '\1'). 32 | gsub(SRCDIR + "/", "") } 33 | 34 | puts "\nFiles in #{SRCDIR} but not in #{html}:" 35 | pp files - test_includes 36 | end 37 | --------------------------------------------------------------------------------