From acb76032b768bc7bebc330714b2f38f805928f1e Mon Sep 17 00:00:00 2001 From: manuroe Date: Sun, 19 Oct 2014 08:47:17 +0200 Subject: [PATCH] Added MIDI.js and VexFlow libs --- webclient/inc/Base64.js | 67 + webclient/inc/SoundManager2/license.txt | 29 + .../script/soundmanager2-jsmin.js | 104 + .../script/soundmanager2-nodebug-jsmin.js | 77 + .../script/soundmanager2-nodebug.js | 2377 +++ .../inc/SoundManager2/script/soundmanager2.js | 5019 ++++++ .../inc/SoundManager2/swf/soundmanager2.swf | Bin 0 -> 2850 bytes .../SoundManager2/swf/soundmanager2_debug.swf | Bin 0 -> 3280 bytes .../swf/soundmanager2_flash9.swf | Bin 0 -> 8682 bytes .../swf/soundmanager2_flash9_debug.swf | Bin 0 -> 16806 bytes .../swf/soundmanager2_flash_xdomain.zip | Bin 0 -> 32404 bytes webclient/inc/WebMIDIAPI.js | 421 + webclient/inc/base64binary.js | 80 + webclient/inc/jasmid/LICENSE | 24 + webclient/inc/jasmid/midifile.js | 238 + webclient/inc/jasmid/replayer.js | 96 + webclient/inc/jasmid/stream.js | 69 + webclient/js/MIDI.js | 1280 ++ webclient/js/vextab/tabdiv-debug.js | 2850 ++++ webclient/js/vextab/underscore-min.js | 1 + webclient/js/vextab/vexflow-debug.js | 12803 ++++++++++++++++ webclient/js/vextab/vextab.js | 325 + webclient/js/vextab/vextab_parser.js | 981 ++ 23 files changed, 26841 insertions(+) create mode 100755 webclient/inc/Base64.js create mode 100755 webclient/inc/SoundManager2/license.txt create mode 100755 webclient/inc/SoundManager2/script/soundmanager2-jsmin.js create mode 100755 webclient/inc/SoundManager2/script/soundmanager2-nodebug-jsmin.js create mode 100755 webclient/inc/SoundManager2/script/soundmanager2-nodebug.js create mode 100755 webclient/inc/SoundManager2/script/soundmanager2.js create mode 100755 webclient/inc/SoundManager2/swf/soundmanager2.swf create mode 100755 webclient/inc/SoundManager2/swf/soundmanager2_debug.swf create mode 100755 webclient/inc/SoundManager2/swf/soundmanager2_flash9.swf create mode 100755 webclient/inc/SoundManager2/swf/soundmanager2_flash9_debug.swf create mode 100755 webclient/inc/SoundManager2/swf/soundmanager2_flash_xdomain.zip create mode 100755 webclient/inc/WebMIDIAPI.js create mode 100755 webclient/inc/base64binary.js create mode 100755 webclient/inc/jasmid/LICENSE create mode 100755 webclient/inc/jasmid/midifile.js create mode 100755 webclient/inc/jasmid/replayer.js create mode 100755 webclient/inc/jasmid/stream.js create mode 100644 webclient/js/MIDI.js create mode 100644 webclient/js/vextab/tabdiv-debug.js create mode 100644 webclient/js/vextab/underscore-min.js create mode 100644 webclient/js/vextab/vexflow-debug.js create mode 100644 webclient/js/vextab/vextab.js create mode 100644 webclient/js/vextab/vextab_parser.js diff --git a/webclient/inc/Base64.js b/webclient/inc/Base64.js new file mode 100755 index 0000000000..7c1ef079b9 --- /dev/null +++ b/webclient/inc/Base64.js @@ -0,0 +1,67 @@ +// http://ntt.cc/2008/01/19/base64-encoder-decoder-with-javascript.html + +// window.atob and window.btoa + +(function (window) { + + var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + + window.btoa || (window.btoa = function encode64(input) { + input = escape(input); + var output = ""; + var chr1, chr2, chr3 = ""; + var enc1, enc2, enc3, enc4 = ""; + var i = 0; + do { + chr1 = input.charCodeAt(i++); + chr2 = input.charCodeAt(i++); + chr3 = input.charCodeAt(i++); + enc1 = chr1 >> 2; + enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); + enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); + enc4 = chr3 & 63; + if (isNaN(chr2)) { + enc3 = enc4 = 64; + } else if (isNaN(chr3)) { + enc4 = 64; + } + output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4); + chr1 = chr2 = chr3 = ""; + enc1 = enc2 = enc3 = enc4 = ""; + } while (i < input.length); + return output; + }); + + window.atob || (window.atob = function(input) { + var output = ""; + var chr1, chr2, chr3 = ""; + var enc1, enc2, enc3, enc4 = ""; + var i = 0; + // remove all characters that are not A-Z, a-z, 0-9, +, /, or = + var base64test = /[^A-Za-z0-9\+\/\=]/g; + if (base64test.exec(input)) { + alert("There were invalid base64 characters in the input text.\n" + "Valid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\n" + "Expect errors in decoding."); + } + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + do { + enc1 = keyStr.indexOf(input.charAt(i++)); + enc2 = keyStr.indexOf(input.charAt(i++)); + enc3 = keyStr.indexOf(input.charAt(i++)); + enc4 = keyStr.indexOf(input.charAt(i++)); + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; + output = output + String.fromCharCode(chr1); + if (enc3 != 64) { + output = output + String.fromCharCode(chr2); + } + if (enc4 != 64) { + output = output + String.fromCharCode(chr3); + } + chr1 = chr2 = chr3 = ""; + enc1 = enc2 = enc3 = enc4 = ""; + } while (i < input.length); + return unescape(output); + }); + +}(this)); \ No newline at end of file diff --git a/webclient/inc/SoundManager2/license.txt b/webclient/inc/SoundManager2/license.txt new file mode 100755 index 0000000000..1a17182fcf --- /dev/null +++ b/webclient/inc/SoundManager2/license.txt @@ -0,0 +1,29 @@ +Software License Agreement (BSD License) + +Copyright (c) 2007, Scott Schiller (schillmania.com) +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + +* Neither the name of schillmania.com nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission from schillmania.com. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/webclient/inc/SoundManager2/script/soundmanager2-jsmin.js b/webclient/inc/SoundManager2/script/soundmanager2-jsmin.js new file mode 100755 index 0000000000..e8b14ae168 --- /dev/null +++ b/webclient/inc/SoundManager2/script/soundmanager2-jsmin.js @@ -0,0 +1,104 @@ +/** @license + + + SoundManager 2: JavaScript Sound for the Web + ---------------------------------------------- + http://schillmania.com/projects/soundmanager2/ + + Copyright (c) 2007, Scott Schiller. All rights reserved. + Code provided under the BSD License: + http://schillmania.com/projects/soundmanager2/license.txt + + V2.97a.20111220 +*/ +(function(G){function W(W,la){function l(b){return function(a){var d=this._t;return!d||!d._a?(d&&d.sID?c._wD(k+"ignoring "+a.type+": "+d.sID):c._wD(k+"ignoring "+a.type),null):b.call(this,a)}}this.flashVersion=8;this.debugMode=!0;this.debugFlash=!1;this.consoleOnly=this.useConsole=!0;this.waitForWindowLoad=!1;this.bgColor="#ffffff";this.useHighPerformance=!1;this.html5PollingInterval=this.flashPollingInterval=null;this.flashLoadTimeout=1E3;this.wmode=null;this.allowScriptAccess="always";this.useFlashBlock= +!1;this.useHTML5Audio=!0;this.html5Test=/^(probably|maybe)$/i;this.preferFlash=!0;this.noSWFCache=!1;this.audioFormats={mp3:{type:['audio/mpeg; codecs="mp3"',"audio/mpeg","audio/mp3","audio/MPA","audio/mpa-robust"],required:!0},mp4:{related:["aac","m4a"],type:['audio/mp4; codecs="mp4a.40.2"',"audio/aac","audio/x-m4a","audio/MP4A-LATM","audio/mpeg4-generic"],required:!1},ogg:{type:["audio/ogg; codecs=vorbis"],required:!1},wav:{type:['audio/wav; codecs="1"',"audio/wav","audio/wave","audio/x-wav"],required:!1}}; +this.defaultOptions={autoLoad:!1,autoPlay:!1,from:null,loops:1,onid3:null,onload:null,whileloading:null,onplay:null,onpause:null,onresume:null,whileplaying:null,onposition:null,onstop:null,onfailure:null,onfinish:null,multiShot:!0,multiShotEvents:!1,position:null,pan:0,stream:!0,to:null,type:null,usePolicyFile:!1,volume:100};this.flash9Options={isMovieStar:null,usePeakData:!1,useWaveformData:!1,useEQData:!1,onbufferchange:null,ondataerror:null};this.movieStarOptions={bufferTime:3,serverURL:null,onconnect:null, +duration:null};this.movieID="sm2-container";this.id=la||"sm2movie";this.debugID="soundmanager-debug";this.debugURLParam=/([#?&])debug=1/i;this.versionNumber="V2.97a.20111220";this.movieURL=this.version=null;this.url=W||null;this.altURL=null;this.enabled=this.swfLoaded=!1;this.oMC=null;this.sounds={};this.soundIDs=[];this.didFlashBlock=this.muted=!1;this.filePattern=null;this.filePatterns={flash8:/\.mp3(\?.*)?$/i,flash9:/\.mp3(\?.*)?$/i};this.features={buffering:!1,peakData:!1,waveformData:!1,eqData:!1, +movieStar:!1};this.sandbox={type:null,types:{remote:"remote (domain-based) rules",localWithFile:"local with file access (no internet access)",localWithNetwork:"local with network (internet access only, no local access)",localTrusted:"local, trusted (local+internet access)"},description:null,noRemote:null,noLocal:null};var ma;try{ma="undefined"!==typeof Audio&&"undefined"!==typeof(new Audio).canPlayType}catch(fb){ma=!1}this.hasHTML5=ma;this.html5={usingFlash:null};this.flash={};this.ignoreFlash=this.html5Only= +!1;var Ea,c=this,i=null,k="HTML5::",u,p=navigator.userAgent,j=G,O=j.location.href.toString(),h=document,na,X,m,B=[],oa=!0,w,P=!1,Q=!1,n=!1,y=!1,Y=!1,o,Za=0,R,v,pa,H,I,Z,Fa,qa,E,$,aa,J,ra,sa,ba,ca,K,Ga,ta,$a=["log","info","warn","error"],Ha,da,Ia,S=null,ua=null,q,va,L,Ja,ea,fa,wa,s,ga=!1,xa=!1,Ka,La,Ma,ha=0,T=null,ia,z=null,Na,ja,U,C,ya,za,Oa,r,Pa=Array.prototype.slice,F=!1,t,ka,Qa,A,Ra,Aa=p.match(/(ipad|iphone|ipod)/i),ab=p.match(/firefox/i),bb=p.match(/droid/i),D=p.match(/msie/i),cb=p.match(/webkit/i), +V=p.match(/safari/i)&&!p.match(/chrome/i),db=p.match(/opera/i),Ba=p.match(/(mobile|pre\/|xoom)/i)||Aa,Ca=!O.match(/usehtml5audio/i)&&!O.match(/sm2\-ignorebadua/i)&&V&&!p.match(/silk/i)&&p.match(/OS X 10_6_([3-7])/i),Sa="undefined"!==typeof console&&"undefined"!==typeof console.log,Da="undefined"!==typeof h.hasFocus?h.hasFocus():null,M=V&&"undefined"===typeof h.hasFocus,Ta=!M,Ua=/(mp3|mp4|mpa)/i,N=h.location?h.location.protocol.match(/http/i):null,Va=!N?"http://":"",Wa=/^\s*audio\/(?:x-)?(?:mpeg4|aac|flv|mov|mp4||m4v|m4a|mp4v|3gp|3g2)\s*(?:$|;)/i, +Xa="mpeg4,aac,flv,mov,mp4,m4v,f4v,m4a,mp4v,3gp,3g2".split(","),eb=RegExp("\\.("+Xa.join("|")+")(\\?.*)?$","i");this.mimePattern=/^\s*audio\/(?:x-)?(?:mp(?:eg|3))\s*(?:$|;)/i;this.useAltURL=!N;this._global_a=null;if(Ba&&(c.useHTML5Audio=!0,c.preferFlash=!1,Aa))F=c.ignoreFlash=!0;this.supported=this.ok=function(){return z?n&&!y:c.useHTML5Audio&&c.hasHTML5};this.getMovie=function(c){return u(c)||h[c]||j[c]};this.createSound=function(b){function a(){f=ea(f);c.sounds[e.id]=new Ea(e);c.soundIDs.push(e.id); +return c.sounds[e.id]}var d,f=null,e=d=null;d="soundManager.createSound(): "+q(!n?"notReady":"notOK");if(!n||!c.ok())return wa(d),!1;2===arguments.length&&(b={id:arguments[0],url:arguments[1]});f=v(b);f.url=ia(f.url);e=f;e.id.toString().charAt(0).match(/^[0-9]$/)&&c._wD("soundManager.createSound(): "+q("badID",e.id),2);c._wD("soundManager.createSound(): "+e.id+" ("+e.url+")",1);if(s(e.id,!0))return c._wD("soundManager.createSound(): "+e.id+" exists",1),c.sounds[e.id];if(ja(e))d=a(),c._wD("Loading sound "+ +e.id+" via HTML5"),d._setup_html5(e);else{if(8=b)return!1;for(;b--;)if(c=x[b],!c.fired&&a.position>=c.position)c.fired=!0,l++,c.method.apply(c.scope,[c.position]);return!0};this._resetOnPosition=function(a){var b,c;b=x.length;if(!b)return!1;for(;b--;)if(c=x[b],c.fired&&a<=c.position)c.fired=!1,l--;return!0};r=function(){var b=a._iO,d=b.from,e=b.to,f,g;g=function(){c._wD(a.sID+': "to" time of '+e+" reached.");a.clearOnPosition(e,g);a.stop()};f=function(){c._wD(a.sID+ +': playing "from" '+d);if(null!==e&&!isNaN(e))a.onPosition(e,g)};if(null!==d&&!isNaN(d))b.position=d,b.multiShot=!1,f();return b};Ya=function(){var b=a._iO.onposition;if(b)for(var c in b)if(b.hasOwnProperty(c))a.onPosition(parseInt(c,10),b[c])};n=function(){var b=a._iO.onposition;if(b)for(var c in b)b.hasOwnProperty(c)&&a.clearOnPosition(parseInt(c,10))};g=function(){a.isHTML5&&Ka(a)};h=function(){a.isHTML5&&La(a)};d=function(){x=[];l=0;j=!1;a._hasTimer=null;a._a=null;a._html5_canplay=!1;a.bytesLoaded= +null;a.bytesTotal=null;a.duration=a._iO&&a._iO.duration?a._iO.duration:null;a.durationEstimate=null;a.eqData=[];a.eqData.left=[];a.eqData.right=[];a.failures=0;a.isBuffering=!1;a.instanceOptions={};a.instanceCount=0;a.loaded=!1;a.metadata={};a.readyState=0;a.muted=!1;a.paused=!1;a.peakData={left:0,right:0};a.waveformData={left:[],right:[]};a.playState=0;a.position=null};d();this._onTimer=function(b){var c,d=!1,e={};if(a._hasTimer||b){if(a._a&&(b||(0f.duration?a.duration:f.duration:parseInt(a.bytesTotal/a.bytesLoaded*a.duration,10),void 0===a.durationEstimate)a.durationEstimate=a.duration;3!==a.readyState&&f.whileloading&&f.whileloading.apply(a)};this._whileplaying=function(b,c,d,e,f){var g=a._iO;if(isNaN(b)|| +null===b)return!1;a.position=b;a._processOnPosition();if(!a.isHTML5&&8m)c._wD(q("needfl9")),c.flashVersion=m=9;c.version=c.versionNumber+(c.html5Only?" (HTML5-only mode)":9===m?" (AS3/Flash 9)":" (AS2/Flash 8)");8'}if(P&&Q)return!1;if(c.html5Only)return qa(),d(),c.oMC=u(c.movieID),X(),Q=P=!0,!1;var e=a||c.url,i=c.altURL||e,g;g=ba();var j,m,k=L(),l,n=null,n=(n=h.getElementsByTagName("html")[0])&&n.dir&&n.dir.match(/rtl/i),b="undefined"===typeof b?c.id:b;qa();c.url=Ia(N?e:i);a=c.url;c.wmode=!c.wmode&&c.useHighPerformance?"transparent":c.wmode;if(null!==c.wmode&&(p.match(/msie 8/i)||!D&&!c.useHighPerformance)&&navigator.platform.match(/win32|win64/i))o("spcWmode"), +c.wmode=null;g={name:b,id:b,src:a,width:"auto",height:"auto",quality:"high",allowScriptAccess:c.allowScriptAccess,bgcolor:c.bgColor,pluginspage:Va+"www.macromedia.com/go/getflashplayer",title:"JS/Flash audio component (SoundManager 2)",type:"application/x-shockwave-flash",wmode:c.wmode,hasPriority:"true"};if(c.debugFlash)g.FlashVars="debug=1";c.wmode||delete g.wmode;if(D)e=h.createElement("div"),m=['',f("movie",a),f("AllowScriptAccess",c.allowScriptAccess),f("quality",g.quality),c.wmode?f("wmode",c.wmode):"",f("bgcolor",c.bgColor),f("hasPriority","true"),c.debugFlash?f("FlashVars",g.FlashVars):"",""].join("");else for(j in e=h.createElement("embed"),g)g.hasOwnProperty(j)&&e.setAttribute(j,g[j]);ta();k=L();if(g=ba())if(c.oMC=u(c.movieID)||h.createElement("div"), +c.oMC.id){l=c.oMC.className;c.oMC.className=(l?l+" ":"movieContainer")+(k?" "+k:"");c.oMC.appendChild(e);if(D)j=c.oMC.appendChild(h.createElement("div")),j.className="sm2-object-box",j.innerHTML=m;Q=!0}else{c.oMC.id=c.movieID;c.oMC.className="movieContainer "+k;j=k=null;if(!c.useFlashBlock)if(c.useHighPerformance)k={position:"fixed",width:"8px",height:"8px",bottom:"0px",left:"0px",overflow:"hidden"};else if(k={position:"absolute",width:"6px",height:"6px",top:"-9999px",left:"-9999px"},n)k.left=Math.abs(parseInt(k.left, +10))+"px";if(cb)c.oMC.style.zIndex=1E4;if(!c.debugFlash)for(l in k)k.hasOwnProperty(l)&&(c.oMC.style[l]=k[l]);try{D||c.oMC.appendChild(e);g.appendChild(c.oMC);if(D)j=c.oMC.appendChild(h.createElement("div")),j.className="sm2-object-box",j.innerHTML=m;Q=!0}catch(r){throw Error(q("domError")+" \n"+r.toString());}}P=!0;d();c._wD("soundManager::createMovie(): Trying to load "+a+(!N&&c.altURL?" (alternate URL)":""),1);return!0};aa=function(){if(c.html5Only)return ca(),!1;if(i)return!1;i=c.getMovie(c.id); +if(!i)S?(D?c.oMC.innerHTML=ua:c.oMC.appendChild(S),S=null,P=!0):ca(c.id,c.url),i=c.getMovie(c.id);i&&o("waitEI");c.oninitmovie instanceof Function&&setTimeout(c.oninitmovie,1);return!0};Z=function(){setTimeout(Fa,1E3)};Fa=function(){if(ga)return!1;ga=!0;r.remove(j,"load",Z);if(M&&!Da)return o("waitFocus"),!1;var b;n||(b=c.getMoviePercent(),c._wD(q("waitImpatient",100===b?" (SWF loaded)":0=b)return!1;for(;b--;)if(c=t[b],!c.fired&&a.position>=c.position)c.fired=!0,l++,c.method.apply(c.scope,[c.position]);return!0};this._resetOnPosition=function(a){var b,c;b=t.length;if(!b)return!1;for(;b--;)if(c=t[b],c.fired&&a<=c.position)c.fired=!1,l--;return!0};p=function(){var b=a._iO,c=b.from,d=b.to,e,f;f=function(){a.clearOnPosition(d,f);a.stop()};e= +function(){if(null!==d&&!isNaN(d))a.onPosition(d,f)};if(null!==c&&!isNaN(c))b.position=c,b.multiShot=!1,e();return b};j=function(){var b=a._iO.onposition;if(b)for(var c in b)if(b.hasOwnProperty(c))a.onPosition(parseInt(c,10),b[c])};n=function(){var b=a._iO.onposition;if(b)for(var c in b)b.hasOwnProperty(c)&&a.clearOnPosition(parseInt(c,10))};z=function(){a.isHTML5&&Fa(a)};g=function(){a.isHTML5&&Ga(a)};e=function(){t=[];l=0;k=!1;a._hasTimer=null;a._a=null;a._html5_canplay=!1;a.bytesLoaded=null;a.bytesTotal= +null;a.duration=a._iO&&a._iO.duration?a._iO.duration:null;a.durationEstimate=null;a.eqData=[];a.eqData.left=[];a.eqData.right=[];a.failures=0;a.isBuffering=!1;a.instanceOptions={};a.instanceCount=0;a.loaded=!1;a.metadata={};a.readyState=0;a.muted=!1;a.paused=!1;a.peakData={left:0,right:0};a.waveformData={left:[],right:[]};a.playState=0;a.position=null};e();this._onTimer=function(b){var c,d=!1,e={};if(a._hasTimer||b){if(a._a&&(b||(0f.duration?a.duration:f.duration:parseInt(a.bytesTotal/a.bytesLoaded*a.duration,10),void 0===a.durationEstimate)a.durationEstimate=a.duration;3!==a.readyState&&f.whileloading&&f.whileloading.apply(a)};this._whileplaying=function(b,c,d,e,f){var g=a._iO;if(isNaN(b)||null===b)return!1;a.position=b;a._processOnPosition();if(!a.isHTML5&&8i)c.flashVersion=i=9;c.version=c.versionNumber+(c.html5Only?" (HTML5-only mode)":9===i?" (AS3/Flash 9)":" (AS2/Flash 8)");8'}if(K&&L)return!1;if(c.html5Only)return ka(),c.oMC=S(c.movieID),T(),L=K=!0,!1;var f=a||c.url,d=c.altURL||f,g;g=na();var h,j,i=H(),l,m=null,m=(m=k.getElementsByTagName("html")[0])&&m.dir&&m.dir.match(/rtl/i),b="undefined"===typeof b?c.id:b;ka();c.url=Ea(za?f:d);a=c.url;c.wmode=!c.wmode&&c.useHighPerformance?"transparent": +c.wmode;if(null!==c.wmode&&(p.match(/msie 8/i)||!y&&!c.useHighPerformance)&&navigator.platform.match(/win32|win64/i))c.wmode=null;g={name:b,id:b,src:a,width:"auto",height:"auto",quality:"high",allowScriptAccess:c.allowScriptAccess,bgcolor:c.bgColor,pluginspage:Oa+"www.macromedia.com/go/getflashplayer",title:"JS/Flash audio component (SoundManager 2)",type:"application/x-shockwave-flash",wmode:c.wmode,hasPriority:"true"};if(c.debugFlash)g.FlashVars="debug=1";c.wmode||delete g.wmode;if(y)f=k.createElement("div"), +j=['',e("movie",a),e("AllowScriptAccess",c.allowScriptAccess),e("quality",g.quality),c.wmode?e("wmode",c.wmode):"",e("bgcolor",c.bgColor),e("hasPriority","true"),c.debugFlash?e("FlashVars",g.FlashVars):"",""].join("");else for(h in f= +k.createElement("embed"),g)g.hasOwnProperty(h)&&f.setAttribute(h,g[h]);oa();i=H();if(g=na())if(c.oMC=S(c.movieID)||k.createElement("div"),c.oMC.id){l=c.oMC.className;c.oMC.className=(l?l+" ":"movieContainer")+(i?" "+i:"");c.oMC.appendChild(f);if(y)h=c.oMC.appendChild(k.createElement("div")),h.className="sm2-object-box",h.innerHTML=j;L=!0}else{c.oMC.id=c.movieID;c.oMC.className="movieContainer "+i;h=i=null;if(!c.useFlashBlock)if(c.useHighPerformance)i={position:"fixed",width:"8px",height:"8px",bottom:"0px", +left:"0px",overflow:"hidden"};else if(i={position:"absolute",width:"6px",height:"6px",top:"-9999px",left:"-9999px"},m)i.left=Math.abs(parseInt(i.left,10))+"px";if(Ua)c.oMC.style.zIndex=1E4;if(!c.debugFlash)for(l in i)i.hasOwnProperty(l)&&(c.oMC.style[l]=i[l]);try{y||c.oMC.appendChild(f);g.appendChild(c.oMC);if(y)h=c.oMC.appendChild(k.createElement("div")),h.className="sm2-object-box",h.innerHTML=j;L=!0}catch(n){throw Error(G("domError")+" \n"+n.toString());}}return K=!0};V=function(){if(c.html5Only)return W(), +!1;if(h)return!1;h=c.getMovie(c.id);if(!h)N?(y?c.oMC.innerHTML=pa:c.oMC.appendChild(N),N=null,K=!0):W(c.id,c.url),h=c.getMovie(c.id);c.oninitmovie instanceof Function&&setTimeout(c.oninitmovie,1);return!0};U=function(){setTimeout(Ba,1E3)};Ba=function(){if($)return!1;$=!0;n.remove(j,"load",U);if(I&&!ya)return!1;var b;m||(b=c.getMoviePercent());setTimeout(function(){b=c.getMoviePercent();!m&&Ma&&(null===b?c.useFlashBlock||0===c.flashLoadTimeout?c.useFlashBlock&&qa():X(!0):0!==c.flashLoadTimeout&&X(!0))}, +c.flashLoadTimeout)};A=function(){function b(){n.remove(j,"focus",A);n.remove(j,"load",A)}if(ya||!I)return b(),!0;ya=Ma=!0;Q&&I&&n.remove(j,"mousemove",A);$=!1;b();return!0};La=function(){var b,a=[];if(c.useHTML5Audio&&c.hasHTML5)for(b in c.audioFormats)c.audioFormats.hasOwnProperty(b)&&a.push(b+": "+c.html5[b]+(!c.html5[b]&&r&&c.flash[b]?" (using flash)":c.preferFlash&&c.flash[b]&&r?" (preferring flash)":!c.html5[b]?" ("+(c.audioFormats[b].required?"required, ":"")+"and no flash support)":""))}; +M=function(b){if(m)return!1;if(c.html5Only)return m=!0,D(),!0;var a;if(!c.useFlashBlock||!c.flashLoadTimeout||c.getMoviePercent())m=!0,w&&(a={type:!r&&s?"NO_FLASH":"INIT_TIMEOUT"});if(w||b){if(c.useFlashBlock&&c.oMC)c.oMC.className=H()+" "+(null===c.getMoviePercent()?"swf_timedout":"swf_error");C({type:"ontimeout",error:a});F(a);return!1}if(c.waitForWindowLoad&&!ia)return n.add(j,"load",D),!1;D();return!0};T=function(){if(m)return!1;if(c.html5Only){if(!m)n.remove(j,"load",c.beginDelayedInit),c.enabled= +!0,M();return!0}V();try{h._externalInterfaceTest(!1),Ca(!0,c.flashPollingInterval||(c.useHighPerformance?10:50)),c.debugMode||h._disableDebug(),c.enabled=!0,c.html5Only||n.add(j,"unload",ha)}catch(b){return F({type:"JS_TO_FLASH_EXCEPTION",fatal:!0}),X(!0),M(),!1}M();n.remove(j,"load",c.beginDelayedInit);return!0};E=function(){if(ma)return!1;ma=!0;oa();if(!r&&c.hasHTML5)c.useHTML5Audio=!0,c.preferFlash=!1;Ja();c.html5.usingFlash=Ia();s=c.html5.usingFlash;La();if(!r&&s)c.flashLoadTimeout=1;k.removeEventListener&& +k.removeEventListener("DOMContentLoaded",E,!1);V();return!0};ua=function(){"complete"===k.readyState&&(E(),k.detachEvent("onreadystatechange",ua));return!0};la=function(){ia=!0;n.remove(j,"load",la)};da();n.add(j,"focus",A);n.add(j,"load",A);n.add(j,"load",U);n.add(j,"load",la);Q&&I&&n.add(j,"mousemove",A);k.addEventListener?k.addEventListener("DOMContentLoaded",E,!1):k.attachEvent?k.attachEvent("onreadystatechange",ua):F({type:"NO_DOM2_EVENTS",fatal:!0});"complete"===k.readyState&&setTimeout(E,100)} +var ea=null;if("undefined"===typeof SM2_DEFER||!SM2_DEFER)ea=new R;J.SoundManager=R;J.soundManager=ea})(window); \ No newline at end of file diff --git a/webclient/inc/SoundManager2/script/soundmanager2-nodebug.js b/webclient/inc/SoundManager2/script/soundmanager2-nodebug.js new file mode 100755 index 0000000000..ba81243f73 --- /dev/null +++ b/webclient/inc/SoundManager2/script/soundmanager2-nodebug.js @@ -0,0 +1,2377 @@ +/** @license + * + * SoundManager 2: JavaScript Sound for the Web + * ---------------------------------------------- + * http://schillmania.com/projects/soundmanager2/ + * + * Copyright (c) 2007, Scott Schiller. All rights reserved. + * Code provided under the BSD License: + * http://schillmania.com/projects/soundmanager2/license.txt + * + * V2.97a.20111220 + */ + +/*global window, SM2_DEFER, sm2Debugger, console, document, navigator, setTimeout, setInterval, clearInterval, Audio */ +/* jslint regexp: true, sloppy: true, white: true, nomen: true, plusplus: true */ + +(function(window) { +var soundManager = null; +function SoundManager(smURL, smID) { + this.flashVersion = 8; + this.debugMode = false; + this.debugFlash = false; + this.useConsole = true; + this.consoleOnly = true; + this.waitForWindowLoad = false; + this.bgColor = '#ffffff'; + this.useHighPerformance = false; + this.flashPollingInterval = null; + this.html5PollingInterval = null; + this.flashLoadTimeout = 1000; + this.wmode = null; + this.allowScriptAccess = 'always'; + this.useFlashBlock = false; + this.useHTML5Audio = true; + this.html5Test = /^(probably|maybe)$/i; + this.preferFlash = true; + this.noSWFCache = false; + this.audioFormats = { + 'mp3': { + 'type': ['audio/mpeg; codecs="mp3"', 'audio/mpeg', 'audio/mp3', 'audio/MPA', 'audio/mpa-robust'], + 'required': true + }, + 'mp4': { + 'related': ['aac','m4a'], + 'type': ['audio/mp4; codecs="mp4a.40.2"', 'audio/aac', 'audio/x-m4a', 'audio/MP4A-LATM', 'audio/mpeg4-generic'], + 'required': false + }, + 'ogg': { + 'type': ['audio/ogg; codecs=vorbis'], + 'required': false + }, + 'wav': { + 'type': ['audio/wav; codecs="1"', 'audio/wav', 'audio/wave', 'audio/x-wav'], + 'required': false + } + }; + this.defaultOptions = { + 'autoLoad': false, + 'autoPlay': false, + 'from': null, + 'loops': 1, + 'onid3': null, + 'onload': null, + 'whileloading': null, + 'onplay': null, + 'onpause': null, + 'onresume': null, + 'whileplaying': null, + 'onposition': null, + 'onstop': null, + 'onfailure': null, + 'onfinish': null, + 'multiShot': true, + 'multiShotEvents': false, + 'position': null, + 'pan': 0, + 'stream': true, + 'to': null, + 'type': null, + 'usePolicyFile': false, + 'volume': 100 + }; + this.flash9Options = { + 'isMovieStar': null, + 'usePeakData': false, + 'useWaveformData': false, + 'useEQData': false, + 'onbufferchange': null, + 'ondataerror': null + }; + this.movieStarOptions = { + 'bufferTime': 3, + 'serverURL': null, + 'onconnect': null, + 'duration': null + }; + this.movieID = 'sm2-container'; + this.id = (smID || 'sm2movie'); + this.debugID = 'soundmanager-debug'; + this.debugURLParam = /([#?&])debug=1/i; + this.versionNumber = 'V2.97a.20111220'; + this.version = null; + this.movieURL = null; + this.url = (smURL || null); + this.altURL = null; + this.swfLoaded = false; + this.enabled = false; + this.oMC = null; + this.sounds = {}; + this.soundIDs = []; + this.muted = false; + this.didFlashBlock = false; + this.filePattern = null; + this.filePatterns = { + 'flash8': /\.mp3(\?.*)?$/i, + 'flash9': /\.mp3(\?.*)?$/i + }; + this.features = { + 'buffering': false, + 'peakData': false, + 'waveformData': false, + 'eqData': false, + 'movieStar': false + }; + this.sandbox = { + }; + this.hasHTML5 = (function() { + try { + return (typeof Audio !== 'undefined' && typeof new Audio().canPlayType !== 'undefined'); + } catch(e) { + return false; + } + }()); + this.html5 = { + 'usingFlash': null + }; + this.flash = {}; + this.html5Only = false; + this.ignoreFlash = false; + var SMSound, + _s = this, _flash = null, _sm = 'soundManager', _smc = _sm+'::', _h5 = 'HTML5::', _id, _ua = navigator.userAgent, _win = window, _wl = _win.location.href.toString(), _doc = document, _doNothing, _init, _fV, _on_queue = [], _debugOpen = true, _debugTS, _didAppend = false, _appendSuccess = false, _didInit = false, _disabled = false, _windowLoaded = false, _wDS, _wdCount = 0, _initComplete, _mixin, _addOnEvent, _processOnEvents, _initUserOnload, _delayWaitForEI, _waitForEI, _setVersionInfo, _handleFocus, _strings, _initMovie, _domContentLoaded, _winOnLoad, _didDCLoaded, _getDocument, _createMovie, _catchError, _setPolling, _initDebug, _debugLevels = ['log', 'info', 'warn', 'error'], _defaultFlashVersion = 8, _disableObject, _failSafely, _normalizeMovieURL, _oRemoved = null, _oRemovedHTML = null, _str, _flashBlockHandler, _getSWFCSS, _swfCSS, _toggleDebug, _loopFix, _policyFix, _complain, _idCheck, _waitingForEI = false, _initPending = false, _smTimer, _onTimer, _startTimer, _stopTimer, _timerExecute, _h5TimerCount = 0, _h5IntervalTimer = null, _parseURL, + _needsFlash = null, _featureCheck, _html5OK, _html5CanPlay, _html5Ext, _html5Unload, _domContentLoadedIE, _testHTML5, _event, _slice = Array.prototype.slice, _useGlobalHTML5Audio = false, _hasFlash, _detectFlash, _badSafariFix, _html5_events, _showSupport, + _is_iDevice = _ua.match(/(ipad|iphone|ipod)/i), _is_firefox = _ua.match(/firefox/i), _is_android = _ua.match(/droid/i), _isIE = _ua.match(/msie/i), _isWebkit = _ua.match(/webkit/i), _isSafari = (_ua.match(/safari/i) && !_ua.match(/chrome/i)), _isOpera = (_ua.match(/opera/i)), + _likesHTML5 = (_ua.match(/(mobile|pre\/|xoom)/i) || _is_iDevice), + _isBadSafari = (!_wl.match(/usehtml5audio/i) && !_wl.match(/sm2\-ignorebadua/i) && _isSafari && !_ua.match(/silk/i) && _ua.match(/OS X 10_6_([3-7])/i)), + _hasConsole = (typeof console !== 'undefined' && typeof console.log !== 'undefined'), _isFocused = (typeof _doc.hasFocus !== 'undefined'?_doc.hasFocus():null), _tryInitOnFocus = (_isSafari && typeof _doc.hasFocus === 'undefined'), _okToDisable = !_tryInitOnFocus, _flashMIME = /(mp3|mp4|mpa)/i, + _emptyURL = 'about:blank', + _overHTTP = (_doc.location?_doc.location.protocol.match(/http/i):null), + _http = (!_overHTTP ? 'http:/'+'/' : ''), + _netStreamMimeTypes = /^\s*audio\/(?:x-)?(?:mpeg4|aac|flv|mov|mp4||m4v|m4a|mp4v|3gp|3g2)\s*(?:$|;)/i, + _netStreamTypes = ['mpeg4', 'aac', 'flv', 'mov', 'mp4', 'm4v', 'f4v', 'm4a', 'mp4v', '3gp', '3g2'], + _netStreamPattern = new RegExp('\\.(' + _netStreamTypes.join('|') + ')(\\?.*)?$', 'i'); + this.mimePattern = /^\s*audio\/(?:x-)?(?:mp(?:eg|3))\s*(?:$|;)/i; + this.useAltURL = !_overHTTP; + this._global_a = null; + _swfCSS = { + 'swfBox': 'sm2-object-box', + 'swfDefault': 'movieContainer', + 'swfError': 'swf_error', + 'swfTimedout': 'swf_timedout', + 'swfLoaded': 'swf_loaded', + 'swfUnblocked': 'swf_unblocked', + 'sm2Debug': 'sm2_debug', + 'highPerf': 'high_performance', + 'flashDebug': 'flash_debug' + }; + if (_likesHTML5) { + _s.useHTML5Audio = true; + _s.preferFlash = false; + if (_is_iDevice) { + _s.ignoreFlash = true; + _useGlobalHTML5Audio = true; + } + } + this.ok = function() { + return (_needsFlash?(_didInit && !_disabled):(_s.useHTML5Audio && _s.hasHTML5)); + }; + this.supported = this.ok; + this.getMovie = function(smID) { + return _id(smID) || _doc[smID] || _win[smID]; + }; + this.createSound = function(oOptions) { + var _cs, _cs_string, + thisOptions = null, oSound = null, _tO = null; + if (!_didInit || !_s.ok()) { + _complain(_cs_string); + return false; + } + if (arguments.length === 2) { + oOptions = { + 'id': arguments[0], + 'url': arguments[1] + }; + } + thisOptions = _mixin(oOptions); + thisOptions.url = _parseURL(thisOptions.url); + _tO = thisOptions; + if (_idCheck(_tO.id, true)) { + return _s.sounds[_tO.id]; + } + function make() { + thisOptions = _loopFix(thisOptions); + _s.sounds[_tO.id] = new SMSound(_tO); + _s.soundIDs.push(_tO.id); + return _s.sounds[_tO.id]; + } + if (_html5OK(_tO)) { + oSound = make(); + oSound._setup_html5(_tO); + } else { + if (_fV > 8) { + if (_tO.isMovieStar === null) { + _tO.isMovieStar = (_tO.serverURL || (_tO.type ? _tO.type.match(_netStreamMimeTypes) : false) || _tO.url.match(_netStreamPattern)); + } + if (_tO.isMovieStar) { + if (_tO.usePeakData) { + _tO.usePeakData = false; + } + } + } + _tO = _policyFix(_tO, _cs); + oSound = make(); + if (_fV === 8) { + _flash._createSound(_tO.id, _tO.loops||1, _tO.usePolicyFile); + } else { + _flash._createSound(_tO.id, _tO.url, _tO.usePeakData, _tO.useWaveformData, _tO.useEQData, _tO.isMovieStar, (_tO.isMovieStar?_tO.bufferTime:false), _tO.loops||1, _tO.serverURL, _tO.duration||null, _tO.autoPlay, true, _tO.autoLoad, _tO.usePolicyFile); + if (!_tO.serverURL) { + oSound.connected = true; + if (_tO.onconnect) { + _tO.onconnect.apply(oSound); + } + } + } + if (!_tO.serverURL && (_tO.autoLoad || _tO.autoPlay)) { + oSound.load(_tO); + } + } + if (!_tO.serverURL && _tO.autoPlay) { + oSound.play(); + } + return oSound; + }; + this.destroySound = function(sID, _bFromSound) { + if (!_idCheck(sID)) { + return false; + } + var oS = _s.sounds[sID], i; + oS._iO = {}; + oS.stop(); + oS.unload(); + for (i = 0; i < _s.soundIDs.length; i++) { + if (_s.soundIDs[i] === sID) { + _s.soundIDs.splice(i, 1); + break; + } + } + if (!_bFromSound) { + oS.destruct(true); + } + oS = null; + delete _s.sounds[sID]; + return true; + }; + this.load = function(sID, oOptions) { + if (!_idCheck(sID)) { + return false; + } + return _s.sounds[sID].load(oOptions); + }; + this.unload = function(sID) { + if (!_idCheck(sID)) { + return false; + } + return _s.sounds[sID].unload(); + }; + this.onPosition = function(sID, nPosition, oMethod, oScope) { + if (!_idCheck(sID)) { + return false; + } + return _s.sounds[sID].onposition(nPosition, oMethod, oScope); + }; + this.onposition = this.onPosition; + this.clearOnPosition = function(sID, nPosition, oMethod) { + if (!_idCheck(sID)) { + return false; + } + return _s.sounds[sID].clearOnPosition(nPosition, oMethod); + }; + this.play = function(sID, oOptions) { + if (!_didInit || !_s.ok()) { + _complain(_sm+'.play(): ' + _str(!_didInit?'notReady':'notOK')); + return false; + } + if (!_idCheck(sID)) { + if (!(oOptions instanceof Object)) { + oOptions = { + url: oOptions + }; + } + if (oOptions && oOptions.url) { + oOptions.id = sID; + return _s.createSound(oOptions).play(); + } else { + return false; + } + } + return _s.sounds[sID].play(oOptions); + }; + this.start = this.play; + this.setPosition = function(sID, nMsecOffset) { + if (!_idCheck(sID)) { + return false; + } + return _s.sounds[sID].setPosition(nMsecOffset); + }; + this.stop = function(sID) { + if (!_idCheck(sID)) { + return false; + } + return _s.sounds[sID].stop(); + }; + this.stopAll = function() { + var oSound; + for (oSound in _s.sounds) { + if (_s.sounds.hasOwnProperty(oSound)) { + _s.sounds[oSound].stop(); + } + } + }; + this.pause = function(sID) { + if (!_idCheck(sID)) { + return false; + } + return _s.sounds[sID].pause(); + }; + this.pauseAll = function() { + var i; + for (i = _s.soundIDs.length; i--;) { + _s.sounds[_s.soundIDs[i]].pause(); + } + }; + this.resume = function(sID) { + if (!_idCheck(sID)) { + return false; + } + return _s.sounds[sID].resume(); + }; + this.resumeAll = function() { + var i; + for (i = _s.soundIDs.length; i--;) { + _s.sounds[_s.soundIDs[i]].resume(); + } + }; + this.togglePause = function(sID) { + if (!_idCheck(sID)) { + return false; + } + return _s.sounds[sID].togglePause(); + }; + this.setPan = function(sID, nPan) { + if (!_idCheck(sID)) { + return false; + } + return _s.sounds[sID].setPan(nPan); + }; + this.setVolume = function(sID, nVol) { + if (!_idCheck(sID)) { + return false; + } + return _s.sounds[sID].setVolume(nVol); + }; + this.mute = function(sID) { + var i = 0; + if (typeof sID !== 'string') { + sID = null; + } + if (!sID) { + for (i = _s.soundIDs.length; i--;) { + _s.sounds[_s.soundIDs[i]].mute(); + } + _s.muted = true; + } else { + if (!_idCheck(sID)) { + return false; + } + return _s.sounds[sID].mute(); + } + return true; + }; + this.muteAll = function() { + _s.mute(); + }; + this.unmute = function(sID) { + var i; + if (typeof sID !== 'string') { + sID = null; + } + if (!sID) { + for (i = _s.soundIDs.length; i--;) { + _s.sounds[_s.soundIDs[i]].unmute(); + } + _s.muted = false; + } else { + if (!_idCheck(sID)) { + return false; + } + return _s.sounds[sID].unmute(); + } + return true; + }; + this.unmuteAll = function() { + _s.unmute(); + }; + this.toggleMute = function(sID) { + if (!_idCheck(sID)) { + return false; + } + return _s.sounds[sID].toggleMute(); + }; + this.getMemoryUse = function() { + var ram = 0; + if (_flash && _fV !== 8) { + ram = parseInt(_flash._getMemoryUse(), 10); + } + return ram; + }; + this.disable = function(bNoDisable) { + var i; + if (typeof bNoDisable === 'undefined') { + bNoDisable = false; + } + if (_disabled) { + return false; + } + _disabled = true; + for (i = _s.soundIDs.length; i--;) { + _disableObject(_s.sounds[_s.soundIDs[i]]); + } + _initComplete(bNoDisable); + _event.remove(_win, 'load', _initUserOnload); + return true; + }; + this.canPlayMIME = function(sMIME) { + var result; + if (_s.hasHTML5) { + result = _html5CanPlay({type:sMIME}); + } + if (!_needsFlash || result) { + return result; + } else { + return (sMIME ? !!((_fV > 8 ? sMIME.match(_netStreamMimeTypes) : null) || sMIME.match(_s.mimePattern)) : null); + } + }; + this.canPlayURL = function(sURL) { + var result; + if (_s.hasHTML5) { + result = _html5CanPlay({url: sURL}); + } + if (!_needsFlash || result) { + return result; + } else { + return (sURL ? !!(sURL.match(_s.filePattern)) : null); + } + }; + this.canPlayLink = function(oLink) { + if (typeof oLink.type !== 'undefined' && oLink.type) { + if (_s.canPlayMIME(oLink.type)) { + return true; + } + } + return _s.canPlayURL(oLink.href); + }; + this.getSoundById = function(sID, _suppressDebug) { + if (!sID) { + throw new Error(_sm+'.getSoundById(): sID is null/undefined'); + } + var result = _s.sounds[sID]; + return result; + }; + this.onready = function(oMethod, oScope) { + var sType = 'onready'; + if (oMethod && oMethod instanceof Function) { + if (!oScope) { + oScope = _win; + } + _addOnEvent(sType, oMethod, oScope); + _processOnEvents(); + return true; + } else { + throw _str('needFunction', sType); + } + }; + this.ontimeout = function(oMethod, oScope) { + var sType = 'ontimeout'; + if (oMethod && oMethod instanceof Function) { + if (!oScope) { + oScope = _win; + } + _addOnEvent(sType, oMethod, oScope); + _processOnEvents({type:sType}); + return true; + } else { + throw _str('needFunction', sType); + } + }; + this._writeDebug = function(sText, sType, _bTimestamp) { + return true; + }; + this._wD = this._writeDebug; + this._debug = function() { + }; + this.reboot = function() { + var i, j; + for (i = _s.soundIDs.length; i--;) { + _s.sounds[_s.soundIDs[i]].destruct(); + } + try { + if (_isIE) { + _oRemovedHTML = _flash.innerHTML; + } + _oRemoved = _flash.parentNode.removeChild(_flash); + } catch(e) { + } + _oRemovedHTML = _oRemoved = _needsFlash = null; + _s.enabled = _didDCLoaded = _didInit = _waitingForEI = _initPending = _didAppend = _appendSuccess = _disabled = _s.swfLoaded = false; + _s.soundIDs = _s.sounds = []; + _flash = null; + for (i in _on_queue) { + if (_on_queue.hasOwnProperty(i)) { + for (j = _on_queue[i].length; j--;) { + _on_queue[i][j].fired = false; + } + } + } + _win.setTimeout(_s.beginDelayedInit, 20); + }; + this.getMoviePercent = function() { + return (_flash && typeof _flash.PercentLoaded !== 'undefined' ? _flash.PercentLoaded() : null); + }; + this.beginDelayedInit = function() { + _windowLoaded = true; + _domContentLoaded(); + setTimeout(function() { + if (_initPending) { + return false; + } + _createMovie(); + _initMovie(); + _initPending = true; + return true; + }, 20); + _delayWaitForEI(); + }; + this.destruct = function() { + _s.disable(true); + }; + SMSound = function(oOptions) { + var _t = this, _resetProperties, _add_html5_events, _remove_html5_events, _stop_html5_timer, _start_html5_timer, _attachOnPosition, _onplay_called = false, _onPositionItems = [], _onPositionFired = 0, _detachOnPosition, _applyFromTo, _lastURL = null; + var _lastHTML5State = { + duration: null, + time: null + }; + this.sID = oOptions.id; + this.url = oOptions.url; + this.options = _mixin(oOptions); + this.instanceOptions = this.options; + this._iO = this.instanceOptions; + this.pan = this.options.pan; + this.volume = this.options.volume; + this.isHTML5 = false; + this._a = null; + this.id3 = {}; + this._debug = function() { + }; + this.load = function(oOptions) { + var oS = null, _iO; + if (typeof oOptions !== 'undefined') { + _t._iO = _mixin(oOptions, _t.options); + _t.instanceOptions = _t._iO; + } else { + oOptions = _t.options; + _t._iO = oOptions; + _t.instanceOptions = _t._iO; + if (_lastURL && _lastURL !== _t.url) { + _t._iO.url = _t.url; + _t.url = null; + } + } + if (!_t._iO.url) { + _t._iO.url = _t.url; + } + _t._iO.url = _parseURL(_t._iO.url); + if (_t._iO.url === _t.url && _t.readyState !== 0 && _t.readyState !== 2) { + if (_t.readyState === 3 && _t._iO.onload) { + _t._iO.onload.apply(_t, [(!!_t.duration)]); + } + return _t; + } + _iO = _t._iO; + _lastURL = _t.url; + _t.loaded = false; + _t.readyState = 1; + _t.playState = 0; + if (_html5OK(_iO)) { + oS = _t._setup_html5(_iO); + if (!oS._called_load) { + _t._html5_canplay = false; + _t._a.autobuffer = 'auto'; + _t._a.preload = 'auto'; + oS.load(); + oS._called_load = true; + if (_iO.autoPlay) { + _t.play(); + } + } else { + } + } else { + try { + _t.isHTML5 = false; + _t._iO = _policyFix(_loopFix(_iO)); + _iO = _t._iO; + if (_fV === 8) { + _flash._load(_t.sID, _iO.url, _iO.stream, _iO.autoPlay, (_iO.whileloading?1:0), _iO.loops||1, _iO.usePolicyFile); + } else { + _flash._load(_t.sID, _iO.url, !!(_iO.stream), !!(_iO.autoPlay), _iO.loops||1, !!(_iO.autoLoad), _iO.usePolicyFile); + } + } catch(e) { + _catchError({type:'SMSOUND_LOAD_JS_EXCEPTION', fatal:true}); + } + } + return _t; + }; + this.unload = function() { + if (_t.readyState !== 0) { + if (!_t.isHTML5) { + if (_fV === 8) { + _flash._unload(_t.sID, _emptyURL); + } else { + _flash._unload(_t.sID); + } + } else { + _stop_html5_timer(); + if (_t._a) { + _t._a.pause(); + _html5Unload(_t._a); + } + } + _resetProperties(); + } + return _t; + }; + this.destruct = function(_bFromSM) { + if (!_t.isHTML5) { + _t._iO.onfailure = null; + _flash._destroySound(_t.sID); + } else { + _stop_html5_timer(); + if (_t._a) { + _t._a.pause(); + _html5Unload(_t._a); + if (!_useGlobalHTML5Audio) { + _remove_html5_events(); + } + _t._a._t = null; + _t._a = null; + } + } + if (!_bFromSM) { + _s.destroySound(_t.sID, true); + } + }; + this.play = function(oOptions, _updatePlayState) { + var fN, allowMulti, a, onready; + _updatePlayState = _updatePlayState === undefined ? true : _updatePlayState; + if (!oOptions) { + oOptions = {}; + } + _t._iO = _mixin(oOptions, _t._iO); + _t._iO = _mixin(_t._iO, _t.options); + _t._iO.url = _parseURL(_t._iO.url); + _t.instanceOptions = _t._iO; + if (_t._iO.serverURL && !_t.connected) { + if (!_t.getAutoPlay()) { + _t.setAutoPlay(true); + } + return _t; + } + if (_html5OK(_t._iO)) { + _t._setup_html5(_t._iO); + _start_html5_timer(); + } + if (_t.playState === 1 && !_t.paused) { + allowMulti = _t._iO.multiShot; + if (!allowMulti) { + return _t; + } else { + } + } + if (!_t.loaded) { + if (_t.readyState === 0) { + if (!_t.isHTML5) { + _t._iO.autoPlay = true; + } + _t.load(_t._iO); + } else if (_t.readyState === 2) { + return _t; + } else { + } + } else { + } + if (!_t.isHTML5 && _fV === 9 && _t.position > 0 && _t.position === _t.duration) { + oOptions.position = 0; + } + if (_t.paused && _t.position && _t.position > 0) { + _t.resume(); + } else { + _t._iO = _mixin(oOptions, _t._iO); + if (_t._iO.from !== null && _t._iO.to !== null && _t.instanceCount === 0 && _t.playState === 0 && !_t._iO.serverURL) { + onready = function() { + _t._iO = _mixin(oOptions, _t._iO); + _t.play(_t._iO); + }; + if (_t.isHTML5 && !_t._html5_canplay) { + _t.load({ + _oncanplay: onready + }); + return false; + } else if (!_t.isHTML5 && !_t.loaded && (!_t.readyState || _t.readyState !== 2)) { + _t.load({ + onload: onready + }); + return false; + } + _t._iO = _applyFromTo(); + } + if (!_t.instanceCount || _t._iO.multiShotEvents || (!_t.isHTML5 && _fV > 8 && !_t.getAutoPlay())) { + _t.instanceCount++; + } + if (_t.playState === 0 && _t._iO.onposition) { + _attachOnPosition(_t); + } + _t.playState = 1; + _t.paused = false; + _t.position = (typeof _t._iO.position !== 'undefined' && !isNaN(_t._iO.position) ? _t._iO.position : 0); + if (!_t.isHTML5) { + _t._iO = _policyFix(_loopFix(_t._iO)); + } + if (_t._iO.onplay && _updatePlayState) { + _t._iO.onplay.apply(_t); + _onplay_called = true; + } + _t.setVolume(_t._iO.volume, true); + _t.setPan(_t._iO.pan, true); + if (!_t.isHTML5) { + _flash._start(_t.sID, _t._iO.loops || 1, (_fV === 9?_t._iO.position:_t._iO.position / 1000)); + } else { + _start_html5_timer(); + a = _t._setup_html5(); + _t.setPosition(_t._iO.position); + a.play(); + } + } + return _t; + }; + this.start = this.play; + this.stop = function(bAll) { + var _iO = _t._iO, _oP; + if (_t.playState === 1) { + _t._onbufferchange(0); + _t._resetOnPosition(0); + _t.paused = false; + if (!_t.isHTML5) { + _t.playState = 0; + } + _detachOnPosition(); + if (_iO.to) { + _t.clearOnPosition(_iO.to); + } + if (!_t.isHTML5) { + _flash._stop(_t.sID, bAll); + if (_iO.serverURL) { + _t.unload(); + } + } else { + if (_t._a) { + _oP = _t.position; + _t.setPosition(0); + _t.position = _oP; + _t._a.pause(); + _t.playState = 0; + _t._onTimer(); + _stop_html5_timer(); + } + } + _t.instanceCount = 0; + _t._iO = {}; + if (_iO.onstop) { + _iO.onstop.apply(_t); + } + } + return _t; + }; + this.setAutoPlay = function(autoPlay) { + _t._iO.autoPlay = autoPlay; + if (!_t.isHTML5) { + _flash._setAutoPlay(_t.sID, autoPlay); + if (autoPlay) { + if (!_t.instanceCount && _t.readyState === 1) { + _t.instanceCount++; + } + } + } + }; + this.getAutoPlay = function() { + return _t._iO.autoPlay; + }; + this.setPosition = function(nMsecOffset) { + if (nMsecOffset === undefined) { + nMsecOffset = 0; + } + var original_pos, + position, position1K, + offset = (_t.isHTML5 ? Math.max(nMsecOffset,0) : Math.min(_t.duration || _t._iO.duration, Math.max(nMsecOffset, 0))); + original_pos = _t.position; + _t.position = offset; + position1K = _t.position/1000; + _t._resetOnPosition(_t.position); + _t._iO.position = offset; + if (!_t.isHTML5) { + position = (_fV === 9 ? _t.position : position1K); + if (_t.readyState && _t.readyState !== 2) { + _flash._setPosition(_t.sID, position, (_t.paused || !_t.playState)); + } + } else if (_t._a) { + if (_t._html5_canplay) { + if (_t._a.currentTime !== position1K) { + try { + _t._a.currentTime = position1K; + if (_t.playState === 0 || _t.paused) { + _t._a.pause(); + } + } catch(e) { + } + } + } else { + } + } + if (_t.isHTML5) { + if (_t.paused) { + _t._onTimer(true); + } + } + return _t; + }; + this.pause = function(_bCallFlash) { + if (_t.paused || (_t.playState === 0 && _t.readyState !== 1)) { + return _t; + } + _t.paused = true; + if (!_t.isHTML5) { + if (_bCallFlash || _bCallFlash === undefined) { + _flash._pause(_t.sID); + } + } else { + _t._setup_html5().pause(); + _stop_html5_timer(); + } + if (_t._iO.onpause) { + _t._iO.onpause.apply(_t); + } + return _t; + }; + this.resume = function() { + var _iO = _t._iO; + if (!_t.paused) { + return _t; + } + _t.paused = false; + _t.playState = 1; + if (!_t.isHTML5) { + if (_iO.isMovieStar && !_iO.serverURL) { + _t.setPosition(_t.position); + } + _flash._pause(_t.sID); + } else { + _t._setup_html5().play(); + _start_html5_timer(); + } + if (_onplay_called && _iO.onplay) { + _iO.onplay.apply(_t); + _onplay_called = true; + } else if (_iO.onresume) { + _iO.onresume.apply(_t); + } + return _t; + }; + this.togglePause = function() { + if (_t.playState === 0) { + _t.play({ + position: (_fV === 9 && !_t.isHTML5 ? _t.position : _t.position / 1000) + }); + return _t; + } + if (_t.paused) { + _t.resume(); + } else { + _t.pause(); + } + return _t; + }; + this.setPan = function(nPan, bInstanceOnly) { + if (typeof nPan === 'undefined') { + nPan = 0; + } + if (typeof bInstanceOnly === 'undefined') { + bInstanceOnly = false; + } + if (!_t.isHTML5) { + _flash._setPan(_t.sID, nPan); + } + _t._iO.pan = nPan; + if (!bInstanceOnly) { + _t.pan = nPan; + _t.options.pan = nPan; + } + return _t; + }; + this.setVolume = function(nVol, _bInstanceOnly) { + if (typeof nVol === 'undefined') { + nVol = 100; + } + if (typeof _bInstanceOnly === 'undefined') { + _bInstanceOnly = false; + } + if (!_t.isHTML5) { + _flash._setVolume(_t.sID, (_s.muted && !_t.muted) || _t.muted?0:nVol); + } else if (_t._a) { + _t._a.volume = Math.max(0, Math.min(1, nVol/100)); + } + _t._iO.volume = nVol; + if (!_bInstanceOnly) { + _t.volume = nVol; + _t.options.volume = nVol; + } + return _t; + }; + this.mute = function() { + _t.muted = true; + if (!_t.isHTML5) { + _flash._setVolume(_t.sID, 0); + } else if (_t._a) { + _t._a.muted = true; + } + return _t; + }; + this.unmute = function() { + _t.muted = false; + var hasIO = typeof _t._iO.volume !== 'undefined'; + if (!_t.isHTML5) { + _flash._setVolume(_t.sID, hasIO?_t._iO.volume:_t.options.volume); + } else if (_t._a) { + _t._a.muted = false; + } + return _t; + }; + this.toggleMute = function() { + return (_t.muted?_t.unmute():_t.mute()); + }; + this.onPosition = function(nPosition, oMethod, oScope) { + _onPositionItems.push({ + position: nPosition, + method: oMethod, + scope: (typeof oScope !== 'undefined' ? oScope : _t), + fired: false + }); + return _t; + }; + this.onposition = this.onPosition; + this.clearOnPosition = function(nPosition, oMethod) { + var i; + nPosition = parseInt(nPosition, 10); + if (isNaN(nPosition)) { + return false; + } + for (i=0; i < _onPositionItems.length; i++) { + if (nPosition === _onPositionItems[i].position) { + if (!oMethod || (oMethod === _onPositionItems[i].method)) { + if (_onPositionItems[i].fired) { + _onPositionFired--; + } + _onPositionItems.splice(i, 1); + } + } + } + }; + this._processOnPosition = function() { + var i, item, j = _onPositionItems.length; + if (!j || !_t.playState || _onPositionFired >= j) { + return false; + } + for (i=j; i--;) { + item = _onPositionItems[i]; + if (!item.fired && _t.position >= item.position) { + item.fired = true; + _onPositionFired++; + item.method.apply(item.scope, [item.position]); + } + } + return true; + }; + this._resetOnPosition = function(nPosition) { + var i, item, j = _onPositionItems.length; + if (!j) { + return false; + } + for (i=j; i--;) { + item = _onPositionItems[i]; + if (item.fired && nPosition <= item.position) { + item.fired = false; + _onPositionFired--; + } + } + return true; + }; + _applyFromTo = function() { + var _iO = _t._iO, + f = _iO.from, + t = _iO.to, + start, end; + end = function() { + _t.clearOnPosition(t, end); + _t.stop(); + }; + start = function() { + if (t !== null && !isNaN(t)) { + _t.onPosition(t, end); + } + }; + if (f !== null && !isNaN(f)) { + _iO.position = f; + _iO.multiShot = false; + start(); + } + return _iO; + }; + _attachOnPosition = function() { + var op = _t._iO.onposition; + if (op) { + var item; + for (item in op) { + if (op.hasOwnProperty(item)) { + _t.onPosition(parseInt(item, 10), op[item]); + } + } + } + }; + _detachOnPosition = function() { + var op = _t._iO.onposition; + if (op) { + var item; + for (item in op) { + if (op.hasOwnProperty(item)) { + _t.clearOnPosition(parseInt(item, 10)); + } + } + } + }; + _start_html5_timer = function() { + if (_t.isHTML5) { + _startTimer(_t); + } + }; + _stop_html5_timer = function() { + if (_t.isHTML5) { + _stopTimer(_t); + } + }; + _resetProperties = function() { + _onPositionItems = []; + _onPositionFired = 0; + _onplay_called = false; + _t._hasTimer = null; + _t._a = null; + _t._html5_canplay = false; + _t.bytesLoaded = null; + _t.bytesTotal = null; + _t.duration = (_t._iO && _t._iO.duration ? _t._iO.duration : null); + _t.durationEstimate = null; + _t.eqData = []; + _t.eqData.left = []; + _t.eqData.right = []; + _t.failures = 0; + _t.isBuffering = false; + _t.instanceOptions = {}; + _t.instanceCount = 0; + _t.loaded = false; + _t.metadata = {}; + _t.readyState = 0; + _t.muted = false; + _t.paused = false; + _t.peakData = { + left: 0, + right: 0 + }; + _t.waveformData = { + left: [], + right: [] + }; + _t.playState = 0; + _t.position = null; + }; + _resetProperties(); + this._onTimer = function(bForce) { + var duration, isNew = false, time, x = {}; + if (_t._hasTimer || bForce) { + if (_t._a && (bForce || ((_t.playState > 0 || _t.readyState === 1) && !_t.paused))) { + duration = _t._get_html5_duration(); + if (duration !== _lastHTML5State.duration) { + _lastHTML5State.duration = duration; + _t.duration = duration; + isNew = true; + } + _t.durationEstimate = _t.duration; + time = (_t._a.currentTime * 1000 || 0); + if (time !== _lastHTML5State.time) { + _lastHTML5State.time = time; + isNew = true; + } + if (isNew || bForce) { + _t._whileplaying(time,x,x,x,x); + } + return isNew; + } else { + return false; + } + } + }; + this._get_html5_duration = function() { + var _iO = _t._iO, + d = (_t._a ? _t._a.duration*1000 : (_iO ? _iO.duration : undefined)), + result = (d && !isNaN(d) && d !== Infinity ? d : (_iO ? _iO.duration : null)); + return result; + }; + this._setup_html5 = function(oOptions) { + var _iO = _mixin(_t._iO, oOptions), d = decodeURI, + _a = _useGlobalHTML5Audio ? _s._global_a : _t._a, + _dURL = d(_iO.url), + _oldIO = (_a && _a._t ? _a._t.instanceOptions : null); + if (_a) { + if (_a._t) { + if (!_useGlobalHTML5Audio && _dURL === d(_lastURL)) { + return _a; + } else if (_useGlobalHTML5Audio && _oldIO.url === _iO.url && (!_lastURL || (_lastURL === _oldIO.url))) { + return _a; + } + } + if (_useGlobalHTML5Audio && _a._t && _a._t.playState && _iO.url !== _oldIO.url) { + _a._t.stop(); + } + _resetProperties(); + _a.src = _iO.url; + _t.url = _iO.url; + _lastURL = _iO.url; + _a._called_load = false; + } else { + _a = new Audio(_iO.url); + _a._called_load = false; + if (_is_android) { + _a._called_load = true; + } + if (_useGlobalHTML5Audio) { + _s._global_a = _a; + } + } + _t.isHTML5 = true; + _t._a = _a; + _a._t = _t; + _add_html5_events(); + _a.loop = (_iO.loops>1?'loop':''); + if (_iO.autoLoad || _iO.autoPlay) { + _t.load(); + } else { + _a.autobuffer = false; + _a.preload = 'none'; + } + _a.loop = (_iO.loops > 1 ? 'loop' : ''); + return _a; + }; + _add_html5_events = function() { + if (_t._a._added_events) { + return false; + } + var f; + function add(oEvt, oFn, bCapture) { + return _t._a ? _t._a.addEventListener(oEvt, oFn, bCapture||false) : null; + } + _t._a._added_events = true; + for (f in _html5_events) { + if (_html5_events.hasOwnProperty(f)) { + add(f, _html5_events[f]); + } + } + return true; + }; + _remove_html5_events = function() { + var f; + function remove(oEvt, oFn, bCapture) { + return (_t._a ? _t._a.removeEventListener(oEvt, oFn, bCapture||false) : null); + } + _t._a._added_events = false; + for (f in _html5_events) { + if (_html5_events.hasOwnProperty(f)) { + remove(f, _html5_events[f]); + } + } + }; + this._onload = function(nSuccess) { + var fN, loadOK = !!(nSuccess); + _t.loaded = loadOK; + _t.readyState = loadOK?3:2; + _t._onbufferchange(0); + if (_t._iO.onload) { + _t._iO.onload.apply(_t, [loadOK]); + } + return true; + }; + this._onbufferchange = function(nIsBuffering) { + if (_t.playState === 0) { + return false; + } + if ((nIsBuffering && _t.isBuffering) || (!nIsBuffering && !_t.isBuffering)) { + return false; + } + _t.isBuffering = (nIsBuffering === 1); + if (_t._iO.onbufferchange) { + _t._iO.onbufferchange.apply(_t); + } + return true; + }; + this._onsuspend = function() { + if (_t._iO.onsuspend) { + _t._iO.onsuspend.apply(_t); + } + return true; + }; + this._onfailure = function(msg, level, code) { + _t.failures++; + if (_t._iO.onfailure && _t.failures === 1) { + _t._iO.onfailure(_t, msg, level, code); + } else { + } + }; + this._onfinish = function() { + var _io_onfinish = _t._iO.onfinish; + _t._onbufferchange(0); + _t._resetOnPosition(0); + if (_t.instanceCount) { + _t.instanceCount--; + if (!_t.instanceCount) { + _detachOnPosition(); + _t.playState = 0; + _t.paused = false; + _t.instanceCount = 0; + _t.instanceOptions = {}; + _t._iO = {}; + _stop_html5_timer(); + } + if (!_t.instanceCount || _t._iO.multiShotEvents) { + if (_io_onfinish) { + _io_onfinish.apply(_t); + } + } + } + }; + this._whileloading = function(nBytesLoaded, nBytesTotal, nDuration, nBufferLength) { + var _iO = _t._iO; + _t.bytesLoaded = nBytesLoaded; + _t.bytesTotal = nBytesTotal; + _t.duration = Math.floor(nDuration); + _t.bufferLength = nBufferLength; + if (!_iO.isMovieStar) { + if (_iO.duration) { + _t.durationEstimate = (_t.duration > _iO.duration) ? _t.duration : _iO.duration; + } else { + _t.durationEstimate = parseInt((_t.bytesTotal / _t.bytesLoaded) * _t.duration, 10); + } + if (_t.durationEstimate === undefined) { + _t.durationEstimate = _t.duration; + } + if (_t.readyState !== 3 && _iO.whileloading) { + _iO.whileloading.apply(_t); + } + } else { + _t.durationEstimate = _t.duration; + if (_t.readyState !== 3 && _iO.whileloading) { + _iO.whileloading.apply(_t); + } + } + }; + this._whileplaying = function(nPosition, oPeakData, oWaveformDataLeft, oWaveformDataRight, oEQData) { + var _iO = _t._iO; + if (isNaN(nPosition) || nPosition === null) { + return false; + } + _t.position = nPosition; + _t._processOnPosition(); + if (!_t.isHTML5 && _fV > 8) { + if (_iO.usePeakData && typeof oPeakData !== 'undefined' && oPeakData) { + _t.peakData = { + left: oPeakData.leftPeak, + right: oPeakData.rightPeak + }; + } + if (_iO.useWaveformData && typeof oWaveformDataLeft !== 'undefined' && oWaveformDataLeft) { + _t.waveformData = { + left: oWaveformDataLeft.split(','), + right: oWaveformDataRight.split(',') + }; + } + if (_iO.useEQData) { + if (typeof oEQData !== 'undefined' && oEQData && oEQData.leftEQ) { + var eqLeft = oEQData.leftEQ.split(','); + _t.eqData = eqLeft; + _t.eqData.left = eqLeft; + if (typeof oEQData.rightEQ !== 'undefined' && oEQData.rightEQ) { + _t.eqData.right = oEQData.rightEQ.split(','); + } + } + } + } + if (_t.playState === 1) { + if (!_t.isHTML5 && _fV === 8 && !_t.position && _t.isBuffering) { + _t._onbufferchange(0); + } + if (_iO.whileplaying) { + _iO.whileplaying.apply(_t); + } + } + return true; + }; + this._onmetadata = function(oMDProps, oMDData) { + var oData = {}, i, j; + for (i = 0, j = oMDProps.length; i < j; i++) { + oData[oMDProps[i]] = oMDData[i]; + } + _t.metadata = oData; + if (_t._iO.onmetadata) { + _t._iO.onmetadata.apply(_t); + } + }; + this._onid3 = function(oID3Props, oID3Data) { + var oData = [], i, j; + for (i = 0, j = oID3Props.length; i < j; i++) { + oData[oID3Props[i]] = oID3Data[i]; + } + _t.id3 = _mixin(_t.id3, oData); + if (_t._iO.onid3) { + _t._iO.onid3.apply(_t); + } + }; + this._onconnect = function(bSuccess) { + bSuccess = (bSuccess === 1); + _t.connected = bSuccess; + if (bSuccess) { + _t.failures = 0; + if (_idCheck(_t.sID)) { + if (_t.getAutoPlay()) { + _t.play(undefined, _t.getAutoPlay()); + } else if (_t._iO.autoLoad) { + _t.load(); + } + } + if (_t._iO.onconnect) { + _t._iO.onconnect.apply(_t, [bSuccess]); + } + } + }; + this._ondataerror = function(sError) { + if (_t.playState > 0) { + if (_t._iO.ondataerror) { + _t._iO.ondataerror.apply(_t); + } + } + }; + }; + _getDocument = function() { + return (_doc.body || _doc._docElement || _doc.getElementsByTagName('div')[0]); + }; + _id = function(sID) { + return _doc.getElementById(sID); + }; + _mixin = function(oMain, oAdd) { + var o1 = {}, i, o2, o; + for (i in oMain) { + if (oMain.hasOwnProperty(i)) { + o1[i] = oMain[i]; + } + } + o2 = (typeof oAdd === 'undefined'?_s.defaultOptions:oAdd); + for (o in o2) { + if (o2.hasOwnProperty(o) && typeof o1[o] === 'undefined') { + o1[o] = o2[o]; + } + } + return o1; + }; + _event = (function() { + var old = (_win.attachEvent), + evt = { + add: (old?'attachEvent':'addEventListener'), + remove: (old?'detachEvent':'removeEventListener') + }; + function getArgs(oArgs) { + var args = _slice.call(oArgs), len = args.length; + if (old) { + args[1] = 'on' + args[1]; + if (len > 3) { + args.pop(); + } + } else if (len === 3) { + args.push(false); + } + return args; + } + function apply(args, sType) { + var element = args.shift(), + method = [evt[sType]]; + if (old) { + element[method](args[0], args[1]); + } else { + element[method].apply(element, args); + } + } + function add() { + apply(getArgs(arguments), 'add'); + } + function remove() { + apply(getArgs(arguments), 'remove'); + } + return { + 'add': add, + 'remove': remove + }; + }()); + function _html5_event(oFn) { + return function(e) { + var t = this._t; + if (!t || !t._a) { + return null; + } else { + return oFn.call(this, e); + } + }; + } + _html5_events = { + abort: _html5_event(function(e) { + }), + canplay: _html5_event(function(e) { + var t = this._t; + if (t._html5_canplay) { + return true; + } + t._html5_canplay = true; + t._onbufferchange(0); + var position1K = (!isNaN(t.position)?t.position/1000:null); + if (t.position && this.currentTime !== position1K) { + try { + this.currentTime = position1K; + } catch(ee) { + } + } + if (t._iO._oncanplay) { + t._iO._oncanplay(); + } + }), + load: _html5_event(function(e) { + var t = this._t; + if (!t.loaded) { + t._onbufferchange(0); + t._whileloading(t.bytesTotal, t.bytesTotal, t._get_html5_duration()); + t._onload(true); + } + }), + emptied: _html5_event(function(e) { + }), + ended: _html5_event(function(e) { + var t = this._t; + t._onfinish(); + }), + error: _html5_event(function(e) { + this._t._onload(false); + }), + loadeddata: _html5_event(function(e) { + var t = this._t, + bytesTotal = t.bytesTotal || 1; + if (!t._loaded && !_isSafari) { + t.duration = t._get_html5_duration(); + t._whileloading(bytesTotal, bytesTotal, t._get_html5_duration()); + t._onload(true); + } + }), + loadedmetadata: _html5_event(function(e) { + }), + loadstart: _html5_event(function(e) { + this._t._onbufferchange(1); + }), + play: _html5_event(function(e) { + this._t._onbufferchange(0); + }), + playing: _html5_event(function(e) { + this._t._onbufferchange(0); + }), + progress: _html5_event(function(e) { + var t = this._t; + if (t.loaded) { + return false; + } + var i, j, str, buffered = 0, + isProgress = (e.type === 'progress'), + ranges = e.target.buffered, + loaded = (e.loaded||0), + total = (e.total||1); + if (ranges && ranges.length) { + for (i=ranges.length; i--;) { + buffered = (ranges.end(i) - ranges.start(i)); + } + loaded = buffered/e.target.duration; + } + if (!isNaN(loaded)) { + t._onbufferchange(0); + t._whileloading(loaded, total, t._get_html5_duration()); + if (loaded && total && loaded === total) { + _html5_events.load.call(this, e); + } + } + }), + ratechange: _html5_event(function(e) { + }), + suspend: _html5_event(function(e) { + var t = this._t; + _html5_events.progress.call(this, e); + t._onsuspend(); + }), + stalled: _html5_event(function(e) { + }), + timeupdate: _html5_event(function(e) { + this._t._onTimer(); + }), + waiting: _html5_event(function(e) { + var t = this._t; + t._onbufferchange(1); + }) + }; + _html5OK = function(iO) { + return (!iO.serverURL && (iO.type?_html5CanPlay({type:iO.type}):_html5CanPlay({url:iO.url})||_s.html5Only)); + }; + _html5Unload = function(oAudio) { + if (oAudio) { + oAudio.src = (_is_firefox ? '' : _emptyURL); + } + }; + _html5CanPlay = function(o) { + if (!_s.useHTML5Audio || !_s.hasHTML5) { + return false; + } + var url = (o.url || null), + mime = (o.type || null), + aF = _s.audioFormats, + result, + offset, + fileExt, + item; + function preferFlashCheck(kind) { + return (_s.preferFlash && _hasFlash && !_s.ignoreFlash && (typeof _s.flash[kind] !== 'undefined' && _s.flash[kind])); + } + if (mime && _s.html5[mime] !== 'undefined') { + return (_s.html5[mime] && !preferFlashCheck(mime)); + } + if (!_html5Ext) { + _html5Ext = []; + for (item in aF) { + if (aF.hasOwnProperty(item)) { + _html5Ext.push(item); + if (aF[item].related) { + _html5Ext = _html5Ext.concat(aF[item].related); + } + } + } + _html5Ext = new RegExp('\\.('+_html5Ext.join('|')+')(\\?.*)?$','i'); + } + fileExt = (url ? url.toLowerCase().match(_html5Ext) : null); + if (!fileExt || !fileExt.length) { + if (!mime) { + return false; + } else { + offset = mime.indexOf(';'); + fileExt = (offset !== -1?mime.substr(0,offset):mime).substr(6); + } + } else { + fileExt = fileExt[1]; + } + if (fileExt && typeof _s.html5[fileExt] !== 'undefined') { + return (_s.html5[fileExt] && !preferFlashCheck(fileExt)); + } else { + mime = 'audio/'+fileExt; + result = _s.html5.canPlayType({type:mime}); + _s.html5[fileExt] = result; + return (result && _s.html5[mime] && !preferFlashCheck(mime)); + } + }; + _testHTML5 = function() { + if (!_s.useHTML5Audio || typeof Audio === 'undefined') { + return false; + } + var a = (typeof Audio !== 'undefined' ? (_isOpera ? new Audio(null) : new Audio()) : null), + item, support = {}, aF, i; + function _cp(m) { + var canPlay, i, j, isOK = false; + if (!a || typeof a.canPlayType !== 'function') { + return false; + } + if (m instanceof Array) { + for (i=0, j=m.length; i 1 && sOpt.stream) { + sOpt.stream = false; + } + return sOpt; + }; + _policyFix = function(sOpt, sPre) { + if (sOpt && !sOpt.usePolicyFile && (sOpt.onid3 || sOpt.usePeakData || sOpt.useWaveformData || sOpt.useEQData)) { + sOpt.usePolicyFile = true; + } + return sOpt; + }; + _complain = function(sMsg) { + }; + _doNothing = function() { + return false; + }; + _disableObject = function(o) { + var oProp; + for (oProp in o) { + if (o.hasOwnProperty(oProp) && typeof o[oProp] === 'function') { + o[oProp] = _doNothing; + } + } + oProp = null; + }; + _failSafely = function(bNoDisable) { + if (typeof bNoDisable === 'undefined') { + bNoDisable = false; + } + if (_disabled || bNoDisable) { + _s.disable(bNoDisable); + } + }; + _normalizeMovieURL = function(smURL) { + var urlParams = null, url; + if (smURL) { + if (smURL.match(/\.swf(\?.*)?$/i)) { + urlParams = smURL.substr(smURL.toLowerCase().lastIndexOf('.swf?') + 4); + if (urlParams) { + return smURL; + } + } else if (smURL.lastIndexOf('/') !== smURL.length - 1) { + smURL += '/'; + } + } + url = (smURL && smURL.lastIndexOf('/') !== - 1 ? smURL.substr(0, smURL.lastIndexOf('/') + 1) : './') + _s.movieURL; + if (_s.noSWFCache) { + url += ('?ts=' + new Date().getTime()); + } + return url; + }; + _setVersionInfo = function() { + _fV = parseInt(_s.flashVersion, 10); + if (_fV !== 8 && _fV !== 9) { + _s.flashVersion = _fV = _defaultFlashVersion; + } + var isDebug = (_s.debugMode || _s.debugFlash?'_debug.swf':'.swf'); + if (_s.useHTML5Audio && !_s.html5Only && _s.audioFormats.mp4.required && _fV < 9) { + _s.flashVersion = _fV = 9; + } + _s.version = _s.versionNumber + (_s.html5Only?' (HTML5-only mode)':(_fV === 9?' (AS3/Flash 9)':' (AS2/Flash 8)')); + if (_fV > 8) { + _s.defaultOptions = _mixin(_s.defaultOptions, _s.flash9Options); + _s.features.buffering = true; + _s.defaultOptions = _mixin(_s.defaultOptions, _s.movieStarOptions); + _s.filePatterns.flash9 = new RegExp('\\.(mp3|' + _netStreamTypes.join('|') + ')(\\?.*)?$', 'i'); + _s.features.movieStar = true; + } else { + _s.features.movieStar = false; + } + _s.filePattern = _s.filePatterns[(_fV !== 8?'flash9':'flash8')]; + _s.movieURL = (_fV === 8?'soundmanager2.swf':'soundmanager2_flash9.swf').replace('.swf', isDebug); + _s.features.peakData = _s.features.waveformData = _s.features.eqData = (_fV > 8); + }; + _setPolling = function(bPolling, bHighPerformance) { + if (!_flash) { + return false; + } + _flash._setPolling(bPolling, bHighPerformance); + }; + _initDebug = function() { + if (_s.debugURLParam.test(_wl)) { + _s.debugMode = true; + } + }; + _idCheck = this.getSoundById; + _getSWFCSS = function() { + var css = []; + if (_s.debugMode) { + css.push(_swfCSS.sm2Debug); + } + if (_s.debugFlash) { + css.push(_swfCSS.flashDebug); + } + if (_s.useHighPerformance) { + css.push(_swfCSS.highPerf); + } + return css.join(' '); + }; + _flashBlockHandler = function() { + var name = _str('fbHandler'), + p = _s.getMoviePercent(), + css = _swfCSS, + error = {type:'FLASHBLOCK'}; + if (_s.html5Only) { + return false; + } + if (!_s.ok()) { + if (_needsFlash) { + _s.oMC.className = _getSWFCSS() + ' ' + css.swfDefault + ' ' + (p === null?css.swfTimedout:css.swfError); + } + _s.didFlashBlock = true; + _processOnEvents({type:'ontimeout', ignoreInit:true, error:error}); + _catchError(error); + } else { + if (_s.oMC) { + _s.oMC.className = [_getSWFCSS(), css.swfDefault, css.swfLoaded + (_s.didFlashBlock?' '+css.swfUnblocked:'')].join(' '); + } + } + }; + _addOnEvent = function(sType, oMethod, oScope) { + if (typeof _on_queue[sType] === 'undefined') { + _on_queue[sType] = []; + } + _on_queue[sType].push({ + 'method': oMethod, + 'scope': (oScope || null), + 'fired': false + }); + }; + _processOnEvents = function(oOptions) { + if (!oOptions) { + oOptions = { + type: 'onready' + }; + } + if (!_didInit && oOptions && !oOptions.ignoreInit) { + return false; + } + if (oOptions.type === 'ontimeout' && _s.ok()) { + return false; + } + var status = { + success: (oOptions && oOptions.ignoreInit?_s.ok():!_disabled) + }, + srcQueue = (oOptions && oOptions.type?_on_queue[oOptions.type]||[]:[]), + queue = [], i, j, + args = [status], + canRetry = (_needsFlash && _s.useFlashBlock && !_s.ok()); + if (oOptions.error) { + args[0].error = oOptions.error; + } + for (i = 0, j = srcQueue.length; i < j; i++) { + if (srcQueue[i].fired !== true) { + queue.push(srcQueue[i]); + } + } + if (queue.length) { + for (i = 0, j = queue.length; i < j; i++) { + if (queue[i].scope) { + queue[i].method.apply(queue[i].scope, args); + } else { + queue[i].method.apply(this, args); + } + if (!canRetry) { + queue[i].fired = true; + } + } + } + return true; + }; + _initUserOnload = function() { + _win.setTimeout(function() { + if (_s.useFlashBlock) { + _flashBlockHandler(); + } + _processOnEvents(); + if (_s.onload instanceof Function) { + _s.onload.apply(_win); + } + if (_s.waitForWindowLoad) { + _event.add(_win, 'load', _initUserOnload); + } + },1); + }; + _detectFlash = function() { + if (_hasFlash !== undefined) { + return _hasFlash; + } + var hasPlugin = false, n = navigator, nP = n.plugins, obj, type, types, AX = _win.ActiveXObject; + if (nP && nP.length) { + type = 'application/x-shockwave-flash'; + types = n.mimeTypes; + if (types && types[type] && types[type].enabledPlugin && types[type].enabledPlugin.description) { + hasPlugin = true; + } + } else if (typeof AX !== 'undefined') { + try { + obj = new AX('ShockwaveFlash.ShockwaveFlash'); + } catch(e) { + } + hasPlugin = (!!obj); + } + _hasFlash = hasPlugin; + return hasPlugin; + }; + _featureCheck = function() { + var needsFlash, item, + isSpecial = (_is_iDevice && !!(_ua.match(/os (1|2|3_0|3_1)/i))); + if (isSpecial) { + _s.hasHTML5 = false; + _s.html5Only = true; + if (_s.oMC) { + _s.oMC.style.display = 'none'; + } + return false; + } + if (_s.useHTML5Audio) { + if (!_s.html5 || !_s.html5.canPlayType) { + _s.hasHTML5 = false; + return true; + } else { + _s.hasHTML5 = true; + } + if (_isBadSafari) { + if (_detectFlash()) { + return true; + } + } + } else { + return true; + } + for (item in _s.audioFormats) { + if (_s.audioFormats.hasOwnProperty(item)) { + if ((_s.audioFormats[item].required && !_s.html5.canPlayType(_s.audioFormats[item].type)) || _s.flash[item] || _s.flash[_s.audioFormats[item].type]) { + needsFlash = true; + } + } + } + if (_s.ignoreFlash) { + needsFlash = false; + } + _s.html5Only = (_s.hasHTML5 && _s.useHTML5Audio && !needsFlash); + return (!_s.html5Only); + }; + _parseURL = function(url) { + var i, j, result = 0; + if (url instanceof Array) { + for (i=0, j=url.length; i'; + } + _setVersionInfo(); + _s.url = _normalizeMovieURL(_overHTTP?remoteURL:localURL); + smURL = _s.url; + _s.wmode = (!_s.wmode && _s.useHighPerformance ? 'transparent' : _s.wmode); + if (_s.wmode !== null && (_ua.match(/msie 8/i) || (!_isIE && !_s.useHighPerformance)) && navigator.platform.match(/win32|win64/i)) { + _s.wmode = null; + } + oEmbed = { + 'name': smID, + 'id': smID, + 'src': smURL, + 'width': side, + 'height': side, + 'quality': 'high', + 'allowScriptAccess': _s.allowScriptAccess, + 'bgcolor': _s.bgColor, + 'pluginspage': _http+'www.macromedia.com/go/getflashplayer', + 'title': swfTitle, + 'type': 'application/x-shockwave-flash', + 'wmode': _s.wmode, + 'hasPriority': 'true' + }; + if (_s.debugFlash) { + oEmbed.FlashVars = 'debug=1'; + } + if (!_s.wmode) { + delete oEmbed.wmode; + } + if (_isIE) { + oMovie = _doc.createElement('div'); + movieHTML = [ + '', + param('movie', smURL), + param('AllowScriptAccess', _s.allowScriptAccess), + param('quality', oEmbed.quality), + (_s.wmode? param('wmode', _s.wmode): ''), + param('bgcolor', _s.bgColor), + param('hasPriority', 'true'), + (_s.debugFlash ? param('FlashVars', oEmbed.FlashVars) : ''), + '' + ].join(''); + } else { + oMovie = _doc.createElement('embed'); + for (tmp in oEmbed) { + if (oEmbed.hasOwnProperty(tmp)) { + oMovie.setAttribute(tmp, oEmbed[tmp]); + } + } + } + _initDebug(); + extraClass = _getSWFCSS(); + oTarget = _getDocument(); + if (oTarget) { + _s.oMC = (_id(_s.movieID) || _doc.createElement('div')); + if (!_s.oMC.id) { + _s.oMC.id = _s.movieID; + _s.oMC.className = _swfCSS.swfDefault + ' ' + extraClass; + s = null; + oEl = null; + if (!_s.useFlashBlock) { + if (_s.useHighPerformance) { + s = { + 'position': 'fixed', + 'width': '8px', + 'height': '8px', + 'bottom': '0px', + 'left': '0px', + 'overflow': 'hidden' + }; + } else { + s = { + 'position': 'absolute', + 'width': '6px', + 'height': '6px', + 'top': '-9999px', + 'left': '-9999px' + }; + if (isRTL) { + s.left = Math.abs(parseInt(s.left,10))+'px'; + } + } + } + if (_isWebkit) { + _s.oMC.style.zIndex = 10000; + } + if (!_s.debugFlash) { + for (x in s) { + if (s.hasOwnProperty(x)) { + _s.oMC.style[x] = s[x]; + } + } + } + try { + if (!_isIE) { + _s.oMC.appendChild(oMovie); + } + oTarget.appendChild(_s.oMC); + if (_isIE) { + oEl = _s.oMC.appendChild(_doc.createElement('div')); + oEl.className = _swfCSS.swfBox; + oEl.innerHTML = movieHTML; + } + _appendSuccess = true; + } catch(e) { + throw new Error(_str('domError')+' \n'+e.toString()); + } + } else { + sClass = _s.oMC.className; + _s.oMC.className = (sClass?sClass+' ':_swfCSS.swfDefault) + (extraClass?' '+extraClass:''); + _s.oMC.appendChild(oMovie); + if (_isIE) { + oEl = _s.oMC.appendChild(_doc.createElement('div')); + oEl.className = _swfCSS.swfBox; + oEl.innerHTML = movieHTML; + } + _appendSuccess = true; + } + } + _didAppend = true; + _initMsg(); + return true; + }; + _initMovie = function() { + if (_s.html5Only) { + _createMovie(); + return false; + } + if (_flash) { + return false; + } + _flash = _s.getMovie(_s.id); + if (!_flash) { + if (!_oRemoved) { + _createMovie(_s.id, _s.url); + } else { + if (!_isIE) { + _s.oMC.appendChild(_oRemoved); + } else { + _s.oMC.innerHTML = _oRemovedHTML; + } + _oRemoved = null; + _didAppend = true; + } + _flash = _s.getMovie(_s.id); + } + if (_s.oninitmovie instanceof Function) { + setTimeout(_s.oninitmovie, 1); + } + return true; + }; + _delayWaitForEI = function() { + setTimeout(_waitForEI, 1000); + }; + _waitForEI = function() { + if (_waitingForEI) { + return false; + } + _waitingForEI = true; + _event.remove(_win, 'load', _delayWaitForEI); + if (_tryInitOnFocus && !_isFocused) { + return false; + } + var p; + if (!_didInit) { + p = _s.getMoviePercent(); + } + setTimeout(function() { + p = _s.getMoviePercent(); + if (!_didInit && _okToDisable) { + if (p === null) { + if (_s.useFlashBlock || _s.flashLoadTimeout === 0) { + if (_s.useFlashBlock) { + _flashBlockHandler(); + } + } else { + _failSafely(true); + } + } else { + if (_s.flashLoadTimeout === 0) { + } else { + _failSafely(true); + } + } + } + }, _s.flashLoadTimeout); + }; + _handleFocus = function() { + function cleanup() { + _event.remove(_win, 'focus', _handleFocus); + _event.remove(_win, 'load', _handleFocus); + } + if (_isFocused || !_tryInitOnFocus) { + cleanup(); + return true; + } + _okToDisable = true; + _isFocused = true; + if (_isSafari && _tryInitOnFocus) { + _event.remove(_win, 'mousemove', _handleFocus); + } + _waitingForEI = false; + cleanup(); + return true; + }; + _showSupport = function() { + var item, tests = []; + if (_s.useHTML5Audio && _s.hasHTML5) { + for (item in _s.audioFormats) { + if (_s.audioFormats.hasOwnProperty(item)) { + tests.push(item + ': ' + _s.html5[item] + (!_s.html5[item] && _hasFlash && _s.flash[item] ? ' (using flash)' : (_s.preferFlash && _s.flash[item] && _hasFlash ? ' (preferring flash)': (!_s.html5[item] ? ' (' + (_s.audioFormats[item].required ? 'required, ':'') + 'and no flash support)' : '')))); + } + } + } + }; + _initComplete = function(bNoDisable) { + if (_didInit) { + return false; + } + if (_s.html5Only) { + _didInit = true; + _initUserOnload(); + return true; + } + var wasTimeout = (_s.useFlashBlock && _s.flashLoadTimeout && !_s.getMoviePercent()), + error; + if (!wasTimeout) { + _didInit = true; + if (_disabled) { + error = {type: (!_hasFlash && _needsFlash ? 'NO_FLASH' : 'INIT_TIMEOUT')}; + } + } + if (_disabled || bNoDisable) { + if (_s.useFlashBlock && _s.oMC) { + _s.oMC.className = _getSWFCSS() + ' ' + (_s.getMoviePercent() === null?_swfCSS.swfTimedout:_swfCSS.swfError); + } + _processOnEvents({type:'ontimeout', error:error}); + _catchError(error); + return false; + } else { + } + if (_s.waitForWindowLoad && !_windowLoaded) { + _event.add(_win, 'load', _initUserOnload); + return false; + } else { + _initUserOnload(); + } + return true; + }; + _init = function() { + if (_didInit) { + return false; + } + function _cleanup() { + _event.remove(_win, 'load', _s.beginDelayedInit); + } + if (_s.html5Only) { + if (!_didInit) { + _cleanup(); + _s.enabled = true; + _initComplete(); + } + return true; + } + _initMovie(); + try { + _flash._externalInterfaceTest(false); + _setPolling(true, (_s.flashPollingInterval || (_s.useHighPerformance ? 10 : 50))); + if (!_s.debugMode) { + _flash._disableDebug(); + } + _s.enabled = true; + if (!_s.html5Only) { + _event.add(_win, 'unload', _doNothing); + } + } catch(e) { + _catchError({type:'JS_TO_FLASH_EXCEPTION', fatal:true}); + _failSafely(true); + _initComplete(); + return false; + } + _initComplete(); + _cleanup(); + return true; + }; + _domContentLoaded = function() { + if (_didDCLoaded) { + return false; + } + _didDCLoaded = true; + _initDebug(); + if (!_hasFlash && _s.hasHTML5) { + _s.useHTML5Audio = true; + _s.preferFlash = false; + } + _testHTML5(); + _s.html5.usingFlash = _featureCheck(); + _needsFlash = _s.html5.usingFlash; + _showSupport(); + if (!_hasFlash && _needsFlash) { + _s.flashLoadTimeout = 1; + } + if (_doc.removeEventListener) { + _doc.removeEventListener('DOMContentLoaded', _domContentLoaded, false); + } + _initMovie(); + return true; + }; + _domContentLoadedIE = function() { + if (_doc.readyState === 'complete') { + _domContentLoaded(); + _doc.detachEvent('onreadystatechange', _domContentLoadedIE); + } + return true; + }; + _winOnLoad = function() { + _windowLoaded = true; + _event.remove(_win, 'load', _winOnLoad); + }; + _detectFlash(); + _event.add(_win, 'focus', _handleFocus); + _event.add(_win, 'load', _handleFocus); + _event.add(_win, 'load', _delayWaitForEI); + _event.add(_win, 'load', _winOnLoad); + if (_isSafari && _tryInitOnFocus) { + _event.add(_win, 'mousemove', _handleFocus); + } + if (_doc.addEventListener) { + _doc.addEventListener('DOMContentLoaded', _domContentLoaded, false); + } else if (_doc.attachEvent) { + _doc.attachEvent('onreadystatechange', _domContentLoadedIE); + } else { + _catchError({type:'NO_DOM2_EVENTS', fatal:true}); + } + if (_doc.readyState === 'complete') { + setTimeout(_domContentLoaded,100); + } +} +// SM2_DEFER details: http://www.schillmania.com/projects/soundmanager2/doc/getstarted/#lazy-loading +if (typeof SM2_DEFER === 'undefined' || !SM2_DEFER) { + soundManager = new SoundManager(); +} +window.SoundManager = SoundManager; +window.soundManager = soundManager; +}(window)); \ No newline at end of file diff --git a/webclient/inc/SoundManager2/script/soundmanager2.js b/webclient/inc/SoundManager2/script/soundmanager2.js new file mode 100755 index 0000000000..4b115c3159 --- /dev/null +++ b/webclient/inc/SoundManager2/script/soundmanager2.js @@ -0,0 +1,5019 @@ +/** @license + * + * SoundManager 2: JavaScript Sound for the Web + * ---------------------------------------------- + * http://schillmania.com/projects/soundmanager2/ + * + * Copyright (c) 2007, Scott Schiller. All rights reserved. + * Code provided under the BSD License: + * http://schillmania.com/projects/soundmanager2/license.txt + * + * V2.97a.20111220 + */ + +/*global window, SM2_DEFER, sm2Debugger, console, document, navigator, setTimeout, setInterval, clearInterval, Audio */ +/* jslint regexp: true, sloppy: true, white: true, nomen: true, plusplus: true */ + +/** + * About this file + * --------------- + * This is the fully-commented source version of the SoundManager 2 API, + * recommended for use during development and testing. + * + * See soundmanager2-nodebug-jsmin.js for an optimized build (~10KB with gzip.) + * http://schillmania.com/projects/soundmanager2/doc/getstarted/#basic-inclusion + * Alternately, serve this file with gzip for 75% compression savings (~30KB over HTTP.) + * + * You may notice and comments in this source; these are delimiters for + * debug blocks which are removed in the -nodebug builds, further optimizing code size. + * + * Also, as you may note: Whoa, reliable cross-platform/device audio support is hard! ;) + */ + +(function(window) { + +var soundManager = null; + +/** + * The SoundManager constructor. + * + * @constructor + * @param {string} smURL Optional: Path to SWF files + * @param {string} smID Optional: The ID to use for the SWF container element + * @this {SoundManager} + * @return {SoundManager} The new SoundManager instance + */ + +function SoundManager(smURL, smID) { + // Top-level configuration options + + this.flashVersion = 8; // flash build to use (8 or 9.) Some API features require 9. + this.debugMode = true; // enable debugging output (console.log() with HTML fallback) + this.debugFlash = false; // enable debugging output inside SWF, troubleshoot Flash/browser issues + this.useConsole = true; // use console.log() if available (otherwise, writes to #soundmanager-debug element) + this.consoleOnly = true; // if console is being used, do not create/write to #soundmanager-debug + this.waitForWindowLoad = false; // force SM2 to wait for window.onload() before trying to call soundManager.onload() + this.bgColor = '#ffffff'; // SWF background color. N/A when wmode = 'transparent' + this.useHighPerformance = false; // position:fixed flash movie can help increase js/flash speed, minimize lag + this.flashPollingInterval = null; // msec affecting whileplaying/loading callback frequency. If null, default of 50 msec is used. + this.html5PollingInterval = null; // msec affecting whileplaying() for HTML5 audio, excluding mobile devices. If null, native HTML5 update events are used. + this.flashLoadTimeout = 1000; // msec to wait for flash movie to load before failing (0 = infinity) + this.wmode = null; // flash rendering mode - null, 'transparent', or 'opaque' (last two allow z-index to work) + this.allowScriptAccess = 'always'; // for scripting the SWF (object/embed property), 'always' or 'sameDomain' + this.useFlashBlock = false; // *requires flashblock.css, see demos* - allow recovery from flash blockers. Wait indefinitely and apply timeout CSS to SWF, if applicable. + this.useHTML5Audio = true; // use HTML5 Audio() where API is supported (most Safari, Chrome versions), Firefox (no MP3/MP4.) Ideally, transparent vs. Flash API where possible. + this.html5Test = /^(probably|maybe)$/i; // HTML5 Audio() format support test. Use /^probably$/i; if you want to be more conservative. + this.preferFlash = true; // overrides useHTML5audio. if true and flash support present, will try to use flash for MP3/MP4 as needed since HTML5 audio support is still quirky in browsers. + this.noSWFCache = false; // if true, appends ?ts={date} to break aggressive SWF caching. + + this.audioFormats = { + + /** + * determines HTML5 support + flash requirements. + * if no support (via flash and/or HTML5) for a "required" format, SM2 will fail to start. + * flash fallback is used for MP3 or MP4 if HTML5 can't play it (or if preferFlash = true) + * multiple MIME types may be tested while trying to get a positive canPlayType() response. + */ + + 'mp3': { + 'type': ['audio/mpeg; codecs="mp3"', 'audio/mpeg', 'audio/mp3', 'audio/MPA', 'audio/mpa-robust'], + 'required': true + }, + + 'mp4': { + 'related': ['aac','m4a'], // additional formats under the MP4 container + 'type': ['audio/mp4; codecs="mp4a.40.2"', 'audio/aac', 'audio/x-m4a', 'audio/MP4A-LATM', 'audio/mpeg4-generic'], + 'required': false + }, + + 'ogg': { + 'type': ['audio/ogg; codecs=vorbis'], + 'required': false + }, + + 'wav': { + 'type': ['audio/wav; codecs="1"', 'audio/wav', 'audio/wave', 'audio/x-wav'], + 'required': false + } + + }; + + this.defaultOptions = { + + /** + * the default configuration for sound objects made with createSound() and related methods + * eg., volume, auto-load behaviour and so forth + */ + + 'autoLoad': false, // enable automatic loading (otherwise .load() will be called on demand with .play(), the latter being nicer on bandwidth - if you want to .load yourself, you also can) + 'autoPlay': false, // enable playing of file as soon as possible (much faster if "stream" is true) + 'from': null, // position to start playback within a sound (msec), default = beginning + 'loops': 1, // how many times to repeat the sound (position will wrap around to 0, setPosition() will break out of loop when >0) + 'onid3': null, // callback function for "ID3 data is added/available" + 'onload': null, // callback function for "load finished" + 'whileloading': null, // callback function for "download progress update" (X of Y bytes received) + 'onplay': null, // callback for "play" start + 'onpause': null, // callback for "pause" + 'onresume': null, // callback for "resume" (pause toggle) + 'whileplaying': null, // callback during play (position update) + 'onposition': null, // object containing times and function callbacks for positions of interest + 'onstop': null, // callback for "user stop" + 'onfailure': null, // callback function for when playing fails + 'onfinish': null, // callback function for "sound finished playing" + 'multiShot': true, // let sounds "restart" or layer on top of each other when played multiple times, rather than one-shot/one at a time + 'multiShotEvents': false, // fire multiple sound events (currently onfinish() only) when multiShot is enabled + 'position': null, // offset (milliseconds) to seek to within loaded sound data. + 'pan': 0, // "pan" settings, left-to-right, -100 to 100 + 'stream': true, // allows playing before entire file has loaded (recommended) + 'to': null, // position to end playback within a sound (msec), default = end + 'type': null, // MIME-like hint for file pattern / canPlay() tests, eg. audio/mp3 + 'usePolicyFile': false, // enable crossdomain.xml request for audio on remote domains (for ID3/waveform access) + 'volume': 100 // self-explanatory. 0-100, the latter being the max. + + }; + + this.flash9Options = { + + /** + * flash 9-only options, + * merged into defaultOptions if flash 9 is being used + */ + + 'isMovieStar': null, // "MovieStar" MPEG4 audio mode. Null (default) = auto detect MP4, AAC etc. based on URL. true = force on, ignore URL + 'usePeakData': false, // enable left/right channel peak (level) data + 'useWaveformData': false, // enable sound spectrum (raw waveform data) - NOTE: May increase CPU load. + 'useEQData': false, // enable sound EQ (frequency spectrum data) - NOTE: May increase CPU load. + 'onbufferchange': null, // callback for "isBuffering" property change + 'ondataerror': null // callback for waveform/eq data access error (flash playing audio in other tabs/domains) + + }; + + this.movieStarOptions = { + + /** + * flash 9.0r115+ MPEG4 audio options, + * merged into defaultOptions if flash 9+movieStar mode is enabled + */ + + 'bufferTime': 3, // seconds of data to buffer before playback begins (null = flash default of 0.1 seconds - if AAC playback is gappy, try increasing.) + 'serverURL': null, // rtmp: FMS or FMIS server to connect to, required when requesting media via RTMP or one of its variants + 'onconnect': null, // rtmp: callback for connection to flash media server + 'duration': null // rtmp: song duration (msec) + + }; + + // HTML attributes (id + class names) for the SWF container + + this.movieID = 'sm2-container'; + this.id = (smID || 'sm2movie'); + + this.debugID = 'soundmanager-debug'; + this.debugURLParam = /([#?&])debug=1/i; + + // dynamic attributes + + this.versionNumber = 'V2.97a.20111220'; + this.version = null; + this.movieURL = null; + this.url = (smURL || null); + this.altURL = null; + this.swfLoaded = false; + this.enabled = false; + this.oMC = null; + this.sounds = {}; + this.soundIDs = []; + this.muted = false; + this.didFlashBlock = false; + this.filePattern = null; + + this.filePatterns = { + + 'flash8': /\.mp3(\?.*)?$/i, + 'flash9': /\.mp3(\?.*)?$/i + + }; + + // support indicators, set at init + + this.features = { + + 'buffering': false, + 'peakData': false, + 'waveformData': false, + 'eqData': false, + 'movieStar': false + + }; + + // flash sandbox info, used primarily in troubleshooting + + this.sandbox = { + + // + 'type': null, + 'types': { + 'remote': 'remote (domain-based) rules', + 'localWithFile': 'local with file access (no internet access)', + 'localWithNetwork': 'local with network (internet access only, no local access)', + 'localTrusted': 'local, trusted (local+internet access)' + }, + 'description': null, + 'noRemote': null, + 'noLocal': null + // + + }; + + /** + * basic HTML5 Audio() support test + * try...catch because of IE 9 "not implemented" nonsense + * https://github.com/Modernizr/Modernizr/issues/224 + */ + + this.hasHTML5 = (function() { + try { + return (typeof Audio !== 'undefined' && typeof new Audio().canPlayType !== 'undefined'); + } catch(e) { + return false; + } + }()); + + /** + * format support (html5/flash) + * stores canPlayType() results based on audioFormats. + * eg. { mp3: boolean, mp4: boolean } + * treat as read-only. + */ + + this.html5 = { + 'usingFlash': null // set if/when flash fallback is needed + }; + + this.flash = {}; // file type support hash + + this.html5Only = false; // determined at init time + this.ignoreFlash = false; // used for special cases (eg. iPad/iPhone/palm OS?) + + /** + * a few private internals (OK, a lot. :D) + */ + + var SMSound, + _s = this, _flash = null, _sm = 'soundManager', _smc = _sm+'::', _h5 = 'HTML5::', _id, _ua = navigator.userAgent, _win = window, _wl = _win.location.href.toString(), _doc = document, _doNothing, _init, _fV, _on_queue = [], _debugOpen = true, _debugTS, _didAppend = false, _appendSuccess = false, _didInit = false, _disabled = false, _windowLoaded = false, _wDS, _wdCount = 0, _initComplete, _mixin, _addOnEvent, _processOnEvents, _initUserOnload, _delayWaitForEI, _waitForEI, _setVersionInfo, _handleFocus, _strings, _initMovie, _domContentLoaded, _winOnLoad, _didDCLoaded, _getDocument, _createMovie, _catchError, _setPolling, _initDebug, _debugLevels = ['log', 'info', 'warn', 'error'], _defaultFlashVersion = 8, _disableObject, _failSafely, _normalizeMovieURL, _oRemoved = null, _oRemovedHTML = null, _str, _flashBlockHandler, _getSWFCSS, _swfCSS, _toggleDebug, _loopFix, _policyFix, _complain, _idCheck, _waitingForEI = false, _initPending = false, _smTimer, _onTimer, _startTimer, _stopTimer, _timerExecute, _h5TimerCount = 0, _h5IntervalTimer = null, _parseURL, + _needsFlash = null, _featureCheck, _html5OK, _html5CanPlay, _html5Ext, _html5Unload, _domContentLoadedIE, _testHTML5, _event, _slice = Array.prototype.slice, _useGlobalHTML5Audio = false, _hasFlash, _detectFlash, _badSafariFix, _html5_events, _showSupport, + _is_iDevice = _ua.match(/(ipad|iphone|ipod)/i), _is_firefox = _ua.match(/firefox/i), _is_android = _ua.match(/droid/i), _isIE = _ua.match(/msie/i), _isWebkit = _ua.match(/webkit/i), _isSafari = (_ua.match(/safari/i) && !_ua.match(/chrome/i)), _isOpera = (_ua.match(/opera/i)), + _likesHTML5 = (_ua.match(/(mobile|pre\/|xoom)/i) || _is_iDevice), + _isBadSafari = (!_wl.match(/usehtml5audio/i) && !_wl.match(/sm2\-ignorebadua/i) && _isSafari && !_ua.match(/silk/i) && _ua.match(/OS X 10_6_([3-7])/i)), // Safari 4 and 5 (excluding Kindle Fire, "Silk") occasionally fail to load/play HTML5 audio on Snow Leopard 10.6.3 through 10.6.7 due to bug(s) in QuickTime X and/or other underlying frameworks. :/ Confirmed bug. https://bugs.webkit.org/show_bug.cgi?id=32159 + _hasConsole = (typeof console !== 'undefined' && typeof console.log !== 'undefined'), _isFocused = (typeof _doc.hasFocus !== 'undefined'?_doc.hasFocus():null), _tryInitOnFocus = (_isSafari && typeof _doc.hasFocus === 'undefined'), _okToDisable = !_tryInitOnFocus, _flashMIME = /(mp3|mp4|mpa)/i, + _emptyURL = 'about:blank', // safe URL to unload, or load nothing from (flash 8 + most HTML5 UAs) + _overHTTP = (_doc.location?_doc.location.protocol.match(/http/i):null), + _http = (!_overHTTP ? 'http:/'+'/' : ''), + // mp3, mp4, aac etc. + _netStreamMimeTypes = /^\s*audio\/(?:x-)?(?:mpeg4|aac|flv|mov|mp4||m4v|m4a|mp4v|3gp|3g2)\s*(?:$|;)/i, + // Flash v9.0r115+ "moviestar" formats + _netStreamTypes = ['mpeg4', 'aac', 'flv', 'mov', 'mp4', 'm4v', 'f4v', 'm4a', 'mp4v', '3gp', '3g2'], + _netStreamPattern = new RegExp('\\.(' + _netStreamTypes.join('|') + ')(\\?.*)?$', 'i'); + + this.mimePattern = /^\s*audio\/(?:x-)?(?:mp(?:eg|3))\s*(?:$|;)/i; // default mp3 set + + // use altURL if not "online" + this.useAltURL = !_overHTTP; + this._global_a = null; + + _swfCSS = { + + 'swfBox': 'sm2-object-box', + 'swfDefault': 'movieContainer', + 'swfError': 'swf_error', // SWF loaded, but SM2 couldn't start (other error) + 'swfTimedout': 'swf_timedout', + 'swfLoaded': 'swf_loaded', + 'swfUnblocked': 'swf_unblocked', // or loaded OK + 'sm2Debug': 'sm2_debug', + 'highPerf': 'high_performance', + 'flashDebug': 'flash_debug' + + }; + + if (_likesHTML5) { + + // prefer HTML5 for mobile + tablet-like devices, probably more reliable vs. flash at this point. + _s.useHTML5Audio = true; + _s.preferFlash = false; + + if (_is_iDevice) { + // by default, use global feature. iOS onfinish() -> next may fail otherwise. + _s.ignoreFlash = true; + _useGlobalHTML5Audio = true; + } + + } + + /** + * Public SoundManager API + * ----------------------- + */ + + this.ok = function() { + + return (_needsFlash?(_didInit && !_disabled):(_s.useHTML5Audio && _s.hasHTML5)); + + }; + + this.supported = this.ok; // legacy + + this.getMovie = function(smID) { + + // safety net: some old browsers differ on SWF references, possibly related to ExternalInterface / flash version + return _id(smID) || _doc[smID] || _win[smID]; + + }; + + /** + * Creates a SMSound sound object instance. + * + * @param {object} oOptions Sound options (at minimum, id and url are required.) + * @return {object} SMSound The new SMSound object. + */ + + this.createSound = function(oOptions) { + + var _cs, _cs_string, + thisOptions = null, oSound = null, _tO = null; + + // + _cs = _sm+'.createSound(): '; + _cs_string = _cs + _str(!_didInit?'notReady':'notOK'); + // + + if (!_didInit || !_s.ok()) { + _complain(_cs_string); + return false; + } + + if (arguments.length === 2) { + // function overloading in JS! :) ..assume simple createSound(id,url) use case + oOptions = { + 'id': arguments[0], + 'url': arguments[1] + }; + } + + // inherit from defaultOptions + thisOptions = _mixin(oOptions); + + thisOptions.url = _parseURL(thisOptions.url); + + // local shortcut + _tO = thisOptions; + + // + if (_tO.id.toString().charAt(0).match(/^[0-9]$/)) { + _s._wD(_cs + _str('badID', _tO.id), 2); + } + + _s._wD(_cs + _tO.id + ' (' + _tO.url + ')', 1); + // + + if (_idCheck(_tO.id, true)) { + _s._wD(_cs + _tO.id + ' exists', 1); + return _s.sounds[_tO.id]; + } + + function make() { + + thisOptions = _loopFix(thisOptions); + _s.sounds[_tO.id] = new SMSound(_tO); + _s.soundIDs.push(_tO.id); + return _s.sounds[_tO.id]; + + } + + if (_html5OK(_tO)) { + + oSound = make(); + _s._wD('Loading sound '+_tO.id+' via HTML5'); + oSound._setup_html5(_tO); + + } else { + + if (_fV > 8) { + if (_tO.isMovieStar === null) { + // attempt to detect MPEG-4 formats + _tO.isMovieStar = (_tO.serverURL || (_tO.type ? _tO.type.match(_netStreamMimeTypes) : false) || _tO.url.match(_netStreamPattern)); + } + // + if (_tO.isMovieStar) { + _s._wD(_cs + 'using MovieStar handling'); + } + // + if (_tO.isMovieStar) { + if (_tO.usePeakData) { + _wDS('noPeak'); + _tO.usePeakData = false; + } + // + if (_tO.loops > 1) { + _wDS('noNSLoop'); + } + // + } + } + + _tO = _policyFix(_tO, _cs); + oSound = make(); + + if (_fV === 8) { + _flash._createSound(_tO.id, _tO.loops||1, _tO.usePolicyFile); + } else { + _flash._createSound(_tO.id, _tO.url, _tO.usePeakData, _tO.useWaveformData, _tO.useEQData, _tO.isMovieStar, (_tO.isMovieStar?_tO.bufferTime:false), _tO.loops||1, _tO.serverURL, _tO.duration||null, _tO.autoPlay, true, _tO.autoLoad, _tO.usePolicyFile); + if (!_tO.serverURL) { + // We are connected immediately + oSound.connected = true; + if (_tO.onconnect) { + _tO.onconnect.apply(oSound); + } + } + } + + if (!_tO.serverURL && (_tO.autoLoad || _tO.autoPlay)) { + // call load for non-rtmp streams + oSound.load(_tO); + } + + } + + // rtmp will play in onconnect + if (!_tO.serverURL && _tO.autoPlay) { + oSound.play(); + } + + return oSound; + + }; + + /** + * Destroys a SMSound sound object instance. + * + * @param {string} sID The ID of the sound to destroy + */ + + this.destroySound = function(sID, _bFromSound) { + + // explicitly destroy a sound before normal page unload, etc. + + if (!_idCheck(sID)) { + return false; + } + + var oS = _s.sounds[sID], i; + + // Disable all callbacks while the sound is being destroyed + oS._iO = {}; + + oS.stop(); + oS.unload(); + + for (i = 0; i < _s.soundIDs.length; i++) { + if (_s.soundIDs[i] === sID) { + _s.soundIDs.splice(i, 1); + break; + } + } + + if (!_bFromSound) { + // ignore if being called from SMSound instance + oS.destruct(true); + } + + oS = null; + delete _s.sounds[sID]; + + return true; + + }; + + /** + * Calls the load() method of a SMSound object by ID. + * + * @param {string} sID The ID of the sound + * @param {object} oOptions Optional: Sound options + */ + + this.load = function(sID, oOptions) { + + if (!_idCheck(sID)) { + return false; + } + return _s.sounds[sID].load(oOptions); + + }; + + /** + * Calls the unload() method of a SMSound object by ID. + * + * @param {string} sID The ID of the sound + */ + + this.unload = function(sID) { + + if (!_idCheck(sID)) { + return false; + } + return _s.sounds[sID].unload(); + + }; + + /** + * Calls the onPosition() method of a SMSound object by ID. + * + * @param {string} sID The ID of the sound + * @param {number} nPosition The position to watch for + * @param {function} oMethod The relevant callback to fire + * @param {object} oScope Optional: The scope to apply the callback to + * @return {SMSound} The SMSound object + */ + + this.onPosition = function(sID, nPosition, oMethod, oScope) { + + if (!_idCheck(sID)) { + return false; + } + return _s.sounds[sID].onposition(nPosition, oMethod, oScope); + + }; + + // legacy/backwards-compability: lower-case method name + this.onposition = this.onPosition; + + /** + * Calls the clearOnPosition() method of a SMSound object by ID. + * + * @param {string} sID The ID of the sound + * @param {number} nPosition The position to watch for + * @param {function} oMethod Optional: The relevant callback to fire + * @return {SMSound} The SMSound object + */ + + this.clearOnPosition = function(sID, nPosition, oMethod) { + + if (!_idCheck(sID)) { + return false; + } + return _s.sounds[sID].clearOnPosition(nPosition, oMethod); + + }; + + /** + * Calls the play() method of a SMSound object by ID. + * + * @param {string} sID The ID of the sound + * @param {object} oOptions Optional: Sound options + * @return {SMSound} The SMSound object + */ + + this.play = function(sID, oOptions) { + + if (!_didInit || !_s.ok()) { + _complain(_sm+'.play(): ' + _str(!_didInit?'notReady':'notOK')); + return false; + } + + if (!_idCheck(sID)) { + if (!(oOptions instanceof Object)) { + // overloading use case: play('mySound','/path/to/some.mp3'); + oOptions = { + url: oOptions + }; + } + if (oOptions && oOptions.url) { + // overloading use case, create+play: .play('someID',{url:'/path/to.mp3'}); + _s._wD(_sm+'.play(): attempting to create "' + sID + '"', 1); + oOptions.id = sID; + return _s.createSound(oOptions).play(); + } else { + return false; + } + } + + return _s.sounds[sID].play(oOptions); + + }; + + this.start = this.play; // just for convenience + + /** + * Calls the setPosition() method of a SMSound object by ID. + * + * @param {string} sID The ID of the sound + * @param {number} nMsecOffset Position (milliseconds) + * @return {SMSound} The SMSound object + */ + + this.setPosition = function(sID, nMsecOffset) { + + if (!_idCheck(sID)) { + return false; + } + return _s.sounds[sID].setPosition(nMsecOffset); + + }; + + /** + * Calls the stop() method of a SMSound object by ID. + * + * @param {string} sID The ID of the sound + * @return {SMSound} The SMSound object + */ + + this.stop = function(sID) { + + if (!_idCheck(sID)) { + return false; + } + + _s._wD(_sm+'.stop(' + sID + ')', 1); + return _s.sounds[sID].stop(); + + }; + + /** + * Stops all currently-playing sounds. + */ + + this.stopAll = function() { + + var oSound; + _s._wD(_sm+'.stopAll()', 1); + + for (oSound in _s.sounds) { + if (_s.sounds.hasOwnProperty(oSound)) { + // apply only to sound objects + _s.sounds[oSound].stop(); + } + } + + }; + + /** + * Calls the pause() method of a SMSound object by ID. + * + * @param {string} sID The ID of the sound + * @return {SMSound} The SMSound object + */ + + this.pause = function(sID) { + + if (!_idCheck(sID)) { + return false; + } + return _s.sounds[sID].pause(); + + }; + + /** + * Pauses all currently-playing sounds. + */ + + this.pauseAll = function() { + + var i; + for (i = _s.soundIDs.length; i--;) { + _s.sounds[_s.soundIDs[i]].pause(); + } + + }; + + /** + * Calls the resume() method of a SMSound object by ID. + * + * @param {string} sID The ID of the sound + * @return {SMSound} The SMSound object + */ + + this.resume = function(sID) { + + if (!_idCheck(sID)) { + return false; + } + return _s.sounds[sID].resume(); + + }; + + /** + * Resumes all currently-paused sounds. + */ + + this.resumeAll = function() { + + var i; + for (i = _s.soundIDs.length; i--;) { + _s.sounds[_s.soundIDs[i]].resume(); + } + + }; + + /** + * Calls the togglePause() method of a SMSound object by ID. + * + * @param {string} sID The ID of the sound + * @return {SMSound} The SMSound object + */ + + this.togglePause = function(sID) { + + if (!_idCheck(sID)) { + return false; + } + return _s.sounds[sID].togglePause(); + + }; + + /** + * Calls the setPan() method of a SMSound object by ID. + * + * @param {string} sID The ID of the sound + * @param {number} nPan The pan value (-100 to 100) + * @return {SMSound} The SMSound object + */ + + this.setPan = function(sID, nPan) { + + if (!_idCheck(sID)) { + return false; + } + return _s.sounds[sID].setPan(nPan); + + }; + + /** + * Calls the setVolume() method of a SMSound object by ID. + * + * @param {string} sID The ID of the sound + * @param {number} nVol The volume value (0 to 100) + * @return {SMSound} The SMSound object + */ + + this.setVolume = function(sID, nVol) { + + if (!_idCheck(sID)) { + return false; + } + return _s.sounds[sID].setVolume(nVol); + + }; + + /** + * Calls the mute() method of either a single SMSound object by ID, or all sound objects. + * + * @param {string} sID Optional: The ID of the sound (if omitted, all sounds will be used.) + */ + + this.mute = function(sID) { + + var i = 0; + + if (typeof sID !== 'string') { + sID = null; + } + + if (!sID) { + _s._wD(_sm+'.mute(): Muting all sounds'); + for (i = _s.soundIDs.length; i--;) { + _s.sounds[_s.soundIDs[i]].mute(); + } + _s.muted = true; + } else { + if (!_idCheck(sID)) { + return false; + } + _s._wD(_sm+'.mute(): Muting "' + sID + '"'); + return _s.sounds[sID].mute(); + } + + return true; + + }; + + /** + * Mutes all sounds. + */ + + this.muteAll = function() { + + _s.mute(); + + }; + + /** + * Calls the unmute() method of either a single SMSound object by ID, or all sound objects. + * + * @param {string} sID Optional: The ID of the sound (if omitted, all sounds will be used.) + */ + + this.unmute = function(sID) { + + var i; + + if (typeof sID !== 'string') { + sID = null; + } + + if (!sID) { + + _s._wD(_sm+'.unmute(): Unmuting all sounds'); + for (i = _s.soundIDs.length; i--;) { + _s.sounds[_s.soundIDs[i]].unmute(); + } + _s.muted = false; + + } else { + + if (!_idCheck(sID)) { + return false; + } + _s._wD(_sm+'.unmute(): Unmuting "' + sID + '"'); + return _s.sounds[sID].unmute(); + + } + + return true; + + }; + + /** + * Unmutes all sounds. + */ + + this.unmuteAll = function() { + + _s.unmute(); + + }; + + /** + * Calls the toggleMute() method of a SMSound object by ID. + * + * @param {string} sID The ID of the sound + * @return {SMSound} The SMSound object + */ + + this.toggleMute = function(sID) { + + if (!_idCheck(sID)) { + return false; + } + return _s.sounds[sID].toggleMute(); + + }; + + /** + * Retrieves the memory used by the flash plugin. + * + * @return {number} The amount of memory in use + */ + + this.getMemoryUse = function() { + + // flash-only + var ram = 0; + + if (_flash && _fV !== 8) { + ram = parseInt(_flash._getMemoryUse(), 10); + } + + return ram; + + }; + + /** + * Undocumented: NOPs soundManager and all SMSound objects. + */ + + this.disable = function(bNoDisable) { + + // destroy all functions + var i; + + if (typeof bNoDisable === 'undefined') { + bNoDisable = false; + } + + if (_disabled) { + return false; + } + + _disabled = true; + _wDS('shutdown', 1); + + for (i = _s.soundIDs.length; i--;) { + _disableObject(_s.sounds[_s.soundIDs[i]]); + } + + // fire "complete", despite fail + _initComplete(bNoDisable); + _event.remove(_win, 'load', _initUserOnload); + + return true; + + }; + + /** + * Determines playability of a MIME type, eg. 'audio/mp3'. + */ + + this.canPlayMIME = function(sMIME) { + + var result; + + if (_s.hasHTML5) { + result = _html5CanPlay({type:sMIME}); + } + + if (!_needsFlash || result) { + // no flash, or OK + return result; + } else { + // if flash 9, test netStream (movieStar) types as well. + return (sMIME ? !!((_fV > 8 ? sMIME.match(_netStreamMimeTypes) : null) || sMIME.match(_s.mimePattern)) : null); + } + + }; + + /** + * Determines playability of a URL based on audio support. + * + * @param {string} sURL The URL to test + * @return {boolean} URL playability + */ + + this.canPlayURL = function(sURL) { + + var result; + + if (_s.hasHTML5) { + result = _html5CanPlay({url: sURL}); + } + + if (!_needsFlash || result) { + // no flash, or OK + return result; + } else { + return (sURL ? !!(sURL.match(_s.filePattern)) : null); + } + + }; + + /** + * Determines playability of an HTML DOM <a> object (or similar object literal) based on audio support. + * + * @param {object} oLink an HTML DOM <a> object or object literal including href and/or type attributes + * @return {boolean} URL playability + */ + + this.canPlayLink = function(oLink) { + + if (typeof oLink.type !== 'undefined' && oLink.type) { + if (_s.canPlayMIME(oLink.type)) { + return true; + } + } + + return _s.canPlayURL(oLink.href); + + }; + + /** + * Retrieves a SMSound object by ID. + * + * @param {string} sID The ID of the sound + * @return {SMSound} The SMSound object + */ + + this.getSoundById = function(sID, _suppressDebug) { + + if (!sID) { + throw new Error(_sm+'.getSoundById(): sID is null/undefined'); + } + + var result = _s.sounds[sID]; + + // + if (!result && !_suppressDebug) { + _s._wD('"' + sID + '" is an invalid sound ID.', 2); + } + // + + return result; + + }; + + /** + * Queues a callback for execution when SoundManager has successfully initialized. + * + * @param {function} oMethod The callback method to fire + * @param {object} oScope Optional: The scope to apply to the callback + */ + + this.onready = function(oMethod, oScope) { + + var sType = 'onready'; + + if (oMethod && oMethod instanceof Function) { + + // + if (_didInit) { + _s._wD(_str('queue', sType)); + } + // + + if (!oScope) { + oScope = _win; + } + + _addOnEvent(sType, oMethod, oScope); + _processOnEvents(); + + return true; + + } else { + + throw _str('needFunction', sType); + + } + + }; + + /** + * Queues a callback for execution when SoundManager has failed to initialize. + * + * @param {function} oMethod The callback method to fire + * @param {object} oScope Optional: The scope to apply to the callback + */ + + this.ontimeout = function(oMethod, oScope) { + + var sType = 'ontimeout'; + + if (oMethod && oMethod instanceof Function) { + + // + if (_didInit) { + _s._wD(_str('queue', sType)); + } + // + + if (!oScope) { + oScope = _win; + } + + _addOnEvent(sType, oMethod, oScope); + _processOnEvents({type:sType}); + + return true; + + } else { + + throw _str('needFunction', sType); + + } + + }; + + /** + * Writes console.log()-style debug output to a console or in-browser element. + * Applies when SoundManager.debugMode = true + * + * @param {string} sText The console message + * @param {string} sType Optional: Log type of 'info', 'warn' or 'error' + * @param {object} Optional: The scope to apply to the callback + */ + + this._writeDebug = function(sText, sType, _bTimestamp) { + + // pseudo-private console.log()-style output + // + + var sDID = 'soundmanager-debug', o, oItem, sMethod; + + if (!_s.debugMode) { + return false; + } + + if (typeof _bTimestamp !== 'undefined' && _bTimestamp) { + sText = sText + ' | ' + new Date().getTime(); + } + + if (_hasConsole && _s.useConsole) { + sMethod = _debugLevels[sType]; + if (typeof console[sMethod] !== 'undefined') { + console[sMethod](sText); + } else { + console.log(sText); + } + if (_s.consoleOnly) { + return true; + } + } + + try { + + o = _id(sDID); + + if (!o) { + return false; + } + + oItem = _doc.createElement('div'); + + if (++_wdCount % 2 === 0) { + oItem.className = 'sm2-alt'; + } + + if (typeof sType === 'undefined') { + sType = 0; + } else { + sType = parseInt(sType, 10); + } + + oItem.appendChild(_doc.createTextNode(sText)); + + if (sType) { + if (sType >= 2) { + oItem.style.fontWeight = 'bold'; + } + if (sType === 3) { + oItem.style.color = '#ff3333'; + } + } + + // top-to-bottom + // o.appendChild(oItem); + + // bottom-to-top + o.insertBefore(oItem, o.firstChild); + + } catch(e) { + // oh well + } + + o = null; + // + + return true; + + }; + + // alias + this._wD = this._writeDebug; + + /** + * Provides debug / state information on all SMSound objects. + */ + + this._debug = function() { + + // + var i, j; + _wDS('currentObj', 1); + + for (i = 0, j = _s.soundIDs.length; i < j; i++) { + _s.sounds[_s.soundIDs[i]]._debug(); + } + // + + }; + + /** + * Restarts and re-initializes the SoundManager instance. + */ + + this.reboot = function() { + + // attempt to reset and init SM2 + _s._wD(_sm+'.reboot()'); + + // + if (_s.soundIDs.length) { + _s._wD('Destroying ' + _s.soundIDs.length + ' SMSound objects...'); + } + // + + var i, j; + + for (i = _s.soundIDs.length; i--;) { + _s.sounds[_s.soundIDs[i]].destruct(); + } + + // trash ze flash + + try { + if (_isIE) { + _oRemovedHTML = _flash.innerHTML; + } + _oRemoved = _flash.parentNode.removeChild(_flash); + _s._wD('Flash movie removed.'); + } catch(e) { + // uh-oh. + _wDS('badRemove', 2); + } + + // actually, force recreate of movie. + _oRemovedHTML = _oRemoved = _needsFlash = null; + + _s.enabled = _didDCLoaded = _didInit = _waitingForEI = _initPending = _didAppend = _appendSuccess = _disabled = _s.swfLoaded = false; + _s.soundIDs = _s.sounds = []; + _flash = null; + + for (i in _on_queue) { + if (_on_queue.hasOwnProperty(i)) { + for (j = _on_queue[i].length; j--;) { + _on_queue[i][j].fired = false; + } + } + } + + _s._wD(_sm + ': Rebooting...'); + _win.setTimeout(_s.beginDelayedInit, 20); + + }; + + /** + * Undocumented: Determines the SM2 flash movie's load progress. + * + * @return {number or null} Percent loaded, or if invalid/unsupported, null. + */ + + this.getMoviePercent = function() { + + return (_flash && typeof _flash.PercentLoaded !== 'undefined' ? _flash.PercentLoaded() : null); + + }; + + /** + * Additional helper for manually invoking SM2's init process after DOM Ready / window.onload(). + */ + + this.beginDelayedInit = function() { + + _windowLoaded = true; + _domContentLoaded(); + + setTimeout(function() { + + if (_initPending) { + return false; + } + + _createMovie(); + _initMovie(); + _initPending = true; + + return true; + + }, 20); + + _delayWaitForEI(); + + }; + + /** + * Destroys the SoundManager instance and all SMSound instances. + */ + + this.destruct = function() { + + _s._wD(_sm+'.destruct()'); + _s.disable(true); + + }; + + /** + * SMSound() (sound object) constructor + * ------------------------------------ + * + * @param {object} oOptions Sound options (id and url are required attributes) + * @return {SMSound} The new SMSound object + */ + + SMSound = function(oOptions) { + + var _t = this, _resetProperties, _add_html5_events, _remove_html5_events, _stop_html5_timer, _start_html5_timer, _attachOnPosition, _onplay_called = false, _onPositionItems = [], _onPositionFired = 0, _detachOnPosition, _applyFromTo, _lastURL = null; + + var _lastHTML5State = { + // tracks duration + position (time) + duration: null, + time: null + }; + + this.sID = oOptions.id; + this.url = oOptions.url; + this.options = _mixin(oOptions); + + // per-play-instance-specific options + this.instanceOptions = this.options; + + // short alias + this._iO = this.instanceOptions; + + // assign property defaults + this.pan = this.options.pan; + this.volume = this.options.volume; + this.isHTML5 = false; + this._a = null; + + /** + * SMSound() public methods + * ------------------------ + */ + + this.id3 = {}; + + /** + * Writes SMSound object parameters to debug console + */ + + this._debug = function() { + + // + // pseudo-private console.log()-style output + + if (_s.debugMode) { + + var stuff = null, msg = [], sF, sfBracket, maxLength = 64; + + for (stuff in _t.options) { + if (_t.options[stuff] !== null) { + if (_t.options[stuff] instanceof Function) { + // handle functions specially + sF = _t.options[stuff].toString(); + // normalize spaces + sF = sF.replace(/\s\s+/g, ' '); + sfBracket = sF.indexOf('{'); + msg.push(' ' + stuff + ': {' + sF.substr(sfBracket + 1, (Math.min(Math.max(sF.indexOf('\n') - 1, maxLength), maxLength))).replace(/\n/g, '') + '... }'); + } else { + msg.push(' ' + stuff + ': ' + _t.options[stuff]); + } + } + } + + _s._wD('SMSound() merged options: {\n' + msg.join(', \n') + '\n}'); + + } + // + + }; + + // + this._debug(); + // + + /** + * Begins loading a sound per its *url*. + * + * @param {object} oOptions Optional: Sound options + * @return {SMSound} The SMSound object + */ + + this.load = function(oOptions) { + + var oS = null, _iO; + + if (typeof oOptions !== 'undefined') { + _t._iO = _mixin(oOptions, _t.options); + _t.instanceOptions = _t._iO; + } else { + oOptions = _t.options; + _t._iO = oOptions; + _t.instanceOptions = _t._iO; + if (_lastURL && _lastURL !== _t.url) { + _wDS('manURL'); + _t._iO.url = _t.url; + _t.url = null; + } + } + + if (!_t._iO.url) { + _t._iO.url = _t.url; + } + + _t._iO.url = _parseURL(_t._iO.url); + + _s._wD('SMSound.load(): ' + _t._iO.url, 1); + + if (_t._iO.url === _t.url && _t.readyState !== 0 && _t.readyState !== 2) { + _wDS('onURL', 1); + // if loaded and an onload() exists, fire immediately. + if (_t.readyState === 3 && _t._iO.onload) { + // assume success based on truthy duration. + _t._iO.onload.apply(_t, [(!!_t.duration)]); + } + return _t; + } + + // local shortcut + _iO = _t._iO; + + _lastURL = _t.url; + _t.loaded = false; + _t.readyState = 1; + _t.playState = 0; + + // TODO: If switching from HTML5 -> flash (or vice versa), stop currently-playing audio. + + if (_html5OK(_iO)) { + + oS = _t._setup_html5(_iO); + + if (!oS._called_load) { + + _s._wD(_h5+'load: '+_t.sID); + _t._html5_canplay = false; + + // given explicit load call, try to get whole file. + // early HTML5 implementation (non-standard) + _t._a.autobuffer = 'auto'; + // standard + _t._a.preload = 'auto'; + + oS.load(); + oS._called_load = true; + + if (_iO.autoPlay) { + _t.play(); + } + + } else { + _s._wD(_h5+'ignoring request to load again: '+_t.sID); + } + + } else { + + try { + _t.isHTML5 = false; + _t._iO = _policyFix(_loopFix(_iO)); + // re-assign local shortcut + _iO = _t._iO; + if (_fV === 8) { + _flash._load(_t.sID, _iO.url, _iO.stream, _iO.autoPlay, (_iO.whileloading?1:0), _iO.loops||1, _iO.usePolicyFile); + } else { + _flash._load(_t.sID, _iO.url, !!(_iO.stream), !!(_iO.autoPlay), _iO.loops||1, !!(_iO.autoLoad), _iO.usePolicyFile); + } + } catch(e) { + _wDS('smError', 2); + _debugTS('onload', false); + _catchError({type:'SMSOUND_LOAD_JS_EXCEPTION', fatal:true}); + + } + + } + + return _t; + + }; + + /** + * Unloads a sound, canceling any open HTTP requests. + * + * @return {SMSound} The SMSound object + */ + + this.unload = function() { + + // Flash 8/AS2 can't "close" a stream - fake it by loading an empty URL + // Flash 9/AS3: Close stream, preventing further load + // HTML5: Most UAs will use empty URL + + if (_t.readyState !== 0) { + + _s._wD('SMSound.unload(): "' + _t.sID + '"'); + + if (!_t.isHTML5) { + if (_fV === 8) { + _flash._unload(_t.sID, _emptyURL); + } else { + _flash._unload(_t.sID); + } + } else { + _stop_html5_timer(); + if (_t._a) { + _t._a.pause(); + _html5Unload(_t._a); + } + } + + // reset load/status flags + _resetProperties(); + + } + + return _t; + + }; + + /** + * Unloads and destroys a sound. + */ + + this.destruct = function(_bFromSM) { + + _s._wD('SMSound.destruct(): "' + _t.sID + '"'); + + if (!_t.isHTML5) { + + // kill sound within Flash + // Disable the onfailure handler + _t._iO.onfailure = null; + _flash._destroySound(_t.sID); + + } else { + + _stop_html5_timer(); + + if (_t._a) { + _t._a.pause(); + _html5Unload(_t._a); + if (!_useGlobalHTML5Audio) { + _remove_html5_events(); + } + // break obvious circular reference + _t._a._t = null; + _t._a = null; + } + + } + + if (!_bFromSM) { + // ensure deletion from controller + _s.destroySound(_t.sID, true); + + } + + }; + + /** + * Begins playing a sound. + * + * @param {object} oOptions Optional: Sound options + * @return {SMSound} The SMSound object + */ + + this.play = function(oOptions, _updatePlayState) { + + var fN, allowMulti, a, onready; + + // + fN = 'SMSound.play(): '; + // + + _updatePlayState = _updatePlayState === undefined ? true : _updatePlayState; // default to true + + if (!oOptions) { + oOptions = {}; + } + + _t._iO = _mixin(oOptions, _t._iO); + _t._iO = _mixin(_t._iO, _t.options); + _t._iO.url = _parseURL(_t._iO.url); + _t.instanceOptions = _t._iO; + + // RTMP-only + if (_t._iO.serverURL && !_t.connected) { + if (!_t.getAutoPlay()) { + _s._wD(fN+' Netstream not connected yet - setting autoPlay'); + _t.setAutoPlay(true); + } + // play will be called in _onconnect() + return _t; + } + + if (_html5OK(_t._iO)) { + _t._setup_html5(_t._iO); + _start_html5_timer(); + } + + if (_t.playState === 1 && !_t.paused) { + allowMulti = _t._iO.multiShot; + if (!allowMulti) { + _s._wD(fN + '"' + _t.sID + '" already playing (one-shot)', 1); + return _t; + } else { + _s._wD(fN + '"' + _t.sID + '" already playing (multi-shot)', 1); + } + } + + if (!_t.loaded) { + + if (_t.readyState === 0) { + + _s._wD(fN + 'Attempting to load "' + _t.sID + '"', 1); + + // try to get this sound playing ASAP + if (!_t.isHTML5) { + // assign directly because setAutoPlay() increments the instanceCount + _t._iO.autoPlay = true; + } + + _t.load(_t._iO); + + } else if (_t.readyState === 2) { + + _s._wD(fN + 'Could not load "' + _t.sID + '" - exiting', 2); + return _t; + + } else { + + _s._wD(fN + '"' + _t.sID + '" is loading - attempting to play..', 1); + + } + + } else { + + _s._wD(fN + '"' + _t.sID + '"'); + + } + + if (!_t.isHTML5 && _fV === 9 && _t.position > 0 && _t.position === _t.duration) { + // flash 9 needs a position reset if play() is called while at the end of a sound. + _s._wD(fN + '"' + _t.sID + '": Sound at end, resetting to position:0'); + oOptions.position = 0; + } + + /** + * Streams will pause when their buffer is full if they are being loaded. + * In this case paused is true, but the song hasn't started playing yet. + * If we just call resume() the onplay() callback will never be called. + * So only call resume() if the position is > 0. + * Another reason is because options like volume won't have been applied yet. + */ + + if (_t.paused && _t.position && _t.position > 0) { + + // https://gist.github.com/37b17df75cc4d7a90bf6 + _s._wD(fN + '"' + _t.sID + '" is resuming from paused state',1); + _t.resume(); + + } else { + + _t._iO = _mixin(oOptions, _t._iO); + + // apply from/to parameters, if they exist (and not using RTMP) + if (_t._iO.from !== null && _t._iO.to !== null && _t.instanceCount === 0 && _t.playState === 0 && !_t._iO.serverURL) { + + onready = function() { + // sound "canplay" or onload() + // re-apply from/to to instance options, and start playback + _t._iO = _mixin(oOptions, _t._iO); + _t.play(_t._iO); + }; + + // HTML5 needs to at least have "canplay" fired before seeking. + if (_t.isHTML5 && !_t._html5_canplay) { + + // this hasn't been loaded yet. load it first, and then do this again. + _s._wD(fN+'Beginning load of "'+ _t.sID+'" for from/to case'); + + _t.load({ + _oncanplay: onready + }); + + return false; + + } else if (!_t.isHTML5 && !_t.loaded && (!_t.readyState || _t.readyState !== 2)) { + + // to be safe, preload the whole thing in Flash. + + _s._wD(fN+'Preloading "'+ _t.sID+'" for from/to case'); + + _t.load({ + onload: onready + }); + + return false; + + } + + // otherwise, we're ready to go. re-apply local options, and continue + + _t._iO = _applyFromTo(); + + } + + _s._wD(fN+'"'+ _t.sID+'" is starting to play'); + + if (!_t.instanceCount || _t._iO.multiShotEvents || (!_t.isHTML5 && _fV > 8 && !_t.getAutoPlay())) { + _t.instanceCount++; + } + + // if first play and onposition parameters exist, apply them now + if (_t.playState === 0 && _t._iO.onposition) { + _attachOnPosition(_t); + } + + _t.playState = 1; + _t.paused = false; + + _t.position = (typeof _t._iO.position !== 'undefined' && !isNaN(_t._iO.position) ? _t._iO.position : 0); + + if (!_t.isHTML5) { + _t._iO = _policyFix(_loopFix(_t._iO)); + } + + if (_t._iO.onplay && _updatePlayState) { + _t._iO.onplay.apply(_t); + _onplay_called = true; + } + + _t.setVolume(_t._iO.volume, true); + _t.setPan(_t._iO.pan, true); + + if (!_t.isHTML5) { + + _flash._start(_t.sID, _t._iO.loops || 1, (_fV === 9?_t._iO.position:_t._iO.position / 1000)); + + } else { + + _start_html5_timer(); + a = _t._setup_html5(); + _t.setPosition(_t._iO.position); + a.play(); + + } + + } + + return _t; + + }; + + // just for convenience + this.start = this.play; + + /** + * Stops playing a sound (and optionally, all sounds) + * + * @param {boolean} bAll Optional: Whether to stop all sounds + * @return {SMSound} The SMSound object + */ + + this.stop = function(bAll) { + + var _iO = _t._iO, _oP; + + if (_t.playState === 1) { + + _t._onbufferchange(0); + _t._resetOnPosition(0); + _t.paused = false; + + if (!_t.isHTML5) { + _t.playState = 0; + } + + // remove onPosition listeners, if any + _detachOnPosition(); + + // and "to" position, if set + if (_iO.to) { + _t.clearOnPosition(_iO.to); + } + + if (!_t.isHTML5) { + + _flash._stop(_t.sID, bAll); + + // hack for netStream: just unload + if (_iO.serverURL) { + _t.unload(); + } + + } else { + + if (_t._a) { + + _oP = _t.position; + + // act like Flash, though + _t.setPosition(0); + + // hack: reflect old position for onstop() (also like Flash) + _t.position = _oP; + + // html5 has no stop() + // NOTE: pausing means iOS requires interaction to resume. + _t._a.pause(); + + _t.playState = 0; + + // and update UI + _t._onTimer(); + + _stop_html5_timer(); + + } + + } + + _t.instanceCount = 0; + _t._iO = {}; + + if (_iO.onstop) { + _iO.onstop.apply(_t); + } + + } + + return _t; + + }; + + /** + * Undocumented/internal: Sets autoPlay for RTMP. + * + * @param {boolean} autoPlay state + */ + + this.setAutoPlay = function(autoPlay) { + + _s._wD('sound '+_t.sID+' turned autoplay ' + (autoPlay ? 'on' : 'off')); + _t._iO.autoPlay = autoPlay; + + if (!_t.isHTML5) { + _flash._setAutoPlay(_t.sID, autoPlay); + if (autoPlay) { + // only increment the instanceCount if the sound isn't loaded (TODO: verify RTMP) + if (!_t.instanceCount && _t.readyState === 1) { + _t.instanceCount++; + _s._wD('sound '+_t.sID+' incremented instance count to '+_t.instanceCount); + } + } + } + + }; + + /** + * Undocumented/internal: Returns the autoPlay boolean. + * + * @return {boolean} The current autoPlay value + */ + + this.getAutoPlay = function() { + + return _t._iO.autoPlay; + + }; + + /** + * Sets the position of a sound. + * + * @param {number} nMsecOffset Position (milliseconds) + * @return {SMSound} The SMSound object + */ + + this.setPosition = function(nMsecOffset) { + + if (nMsecOffset === undefined) { + nMsecOffset = 0; + } + + var original_pos, + position, position1K, + // Use the duration from the instance options, if we don't have a track duration yet. + // position >= 0 and <= current available (loaded) duration + offset = (_t.isHTML5 ? Math.max(nMsecOffset,0) : Math.min(_t.duration || _t._iO.duration, Math.max(nMsecOffset, 0))); + + original_pos = _t.position; + _t.position = offset; + position1K = _t.position/1000; + _t._resetOnPosition(_t.position); + _t._iO.position = offset; + + if (!_t.isHTML5) { + + position = (_fV === 9 ? _t.position : position1K); + if (_t.readyState && _t.readyState !== 2) { + // if paused or not playing, will not resume (by playing) + _flash._setPosition(_t.sID, position, (_t.paused || !_t.playState)); + } + + } else if (_t._a) { + + // Set the position in the canplay handler if the sound is not ready yet + if (_t._html5_canplay) { + if (_t._a.currentTime !== position1K) { + /** + * DOM/JS errors/exceptions to watch out for: + * if seek is beyond (loaded?) position, "DOM exception 11" + * "INDEX_SIZE_ERR": DOM exception 1 + */ + _s._wD('setPosition('+position1K+'): setting position'); + try { + _t._a.currentTime = position1K; + if (_t.playState === 0 || _t.paused) { + // allow seek without auto-play/resume + _t._a.pause(); + } + } catch(e) { + _s._wD('setPosition('+position1K+'): setting position failed: '+e.message, 2); + } + } + } else { + _s._wD('setPosition('+position1K+'): delaying, sound not ready'); + } + + } + + if (_t.isHTML5) { + if (_t.paused) { + // if paused, refresh UI right away + // force update + _t._onTimer(true); + } + } + + return _t; + + }; + + /** + * Pauses sound playback. + * + * @return {SMSound} The SMSound object + */ + + this.pause = function(_bCallFlash) { + + if (_t.paused || (_t.playState === 0 && _t.readyState !== 1)) { + return _t; + } + + _s._wD('SMSound.pause()'); + _t.paused = true; + + if (!_t.isHTML5) { + if (_bCallFlash || _bCallFlash === undefined) { + _flash._pause(_t.sID); + } + } else { + _t._setup_html5().pause(); + _stop_html5_timer(); + } + + if (_t._iO.onpause) { + _t._iO.onpause.apply(_t); + } + + return _t; + + }; + + /** + * Resumes sound playback. + * + * @return {SMSound} The SMSound object + */ + + /** + * When auto-loaded streams pause on buffer full they have a playState of 0. + * We need to make sure that the playState is set to 1 when these streams "resume". + * When a paused stream is resumed, we need to trigger the onplay() callback if it + * hasn't been called already. In this case since the sound is being played for the + * first time, I think it's more appropriate to call onplay() rather than onresume(). + */ + + this.resume = function() { + + var _iO = _t._iO; + + if (!_t.paused) { + return _t; + } + + _s._wD('SMSound.resume()'); + _t.paused = false; + _t.playState = 1; + + if (!_t.isHTML5) { + if (_iO.isMovieStar && !_iO.serverURL) { + // Bizarre Webkit bug (Chrome reported via 8tracks.com dudes): AAC content paused for 30+ seconds(?) will not resume without a reposition. + _t.setPosition(_t.position); + } + // flash method is toggle-based (pause/resume) + _flash._pause(_t.sID); + } else { + _t._setup_html5().play(); + _start_html5_timer(); + } + + if (_onplay_called && _iO.onplay) { + _iO.onplay.apply(_t); + _onplay_called = true; + } else if (_iO.onresume) { + _iO.onresume.apply(_t); + } + + return _t; + + }; + + /** + * Toggles sound playback. + * + * @return {SMSound} The SMSound object + */ + + this.togglePause = function() { + + _s._wD('SMSound.togglePause()'); + + if (_t.playState === 0) { + _t.play({ + position: (_fV === 9 && !_t.isHTML5 ? _t.position : _t.position / 1000) + }); + return _t; + } + + if (_t.paused) { + _t.resume(); + } else { + _t.pause(); + } + + return _t; + + }; + + /** + * Sets the panning (L-R) effect. + * + * @param {number} nPan The pan value (-100 to 100) + * @return {SMSound} The SMSound object + */ + + this.setPan = function(nPan, bInstanceOnly) { + + if (typeof nPan === 'undefined') { + nPan = 0; + } + + if (typeof bInstanceOnly === 'undefined') { + bInstanceOnly = false; + } + + if (!_t.isHTML5) { + _flash._setPan(_t.sID, nPan); + } // else { no HTML5 pan? } + + _t._iO.pan = nPan; + + if (!bInstanceOnly) { + _t.pan = nPan; + _t.options.pan = nPan; + } + + return _t; + + }; + + /** + * Sets the volume. + * + * @param {number} nVol The volume value (0 to 100) + * @return {SMSound} The SMSound object + */ + + this.setVolume = function(nVol, _bInstanceOnly) { + + /** + * Note: Setting volume has no effect on iOS "special snowflake" devices. + * Hardware volume control overrides software, and volume + * will always return 1 per Apple docs. (iOS 4 + 5.) + * http://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/HTML-canvas-guide/AddingSoundtoCanvasAnimations/AddingSoundtoCanvasAnimations.html + */ + + if (typeof nVol === 'undefined') { + nVol = 100; + } + + if (typeof _bInstanceOnly === 'undefined') { + _bInstanceOnly = false; + } + + if (!_t.isHTML5) { + _flash._setVolume(_t.sID, (_s.muted && !_t.muted) || _t.muted?0:nVol); + } else if (_t._a) { + // valid range: 0-1 + _t._a.volume = Math.max(0, Math.min(1, nVol/100)); + } + + _t._iO.volume = nVol; + + if (!_bInstanceOnly) { + _t.volume = nVol; + _t.options.volume = nVol; + } + + return _t; + + }; + + /** + * Mutes the sound. + * + * @return {SMSound} The SMSound object + */ + + this.mute = function() { + + _t.muted = true; + + if (!_t.isHTML5) { + _flash._setVolume(_t.sID, 0); + } else if (_t._a) { + _t._a.muted = true; + } + + return _t; + + }; + + /** + * Unmutes the sound. + * + * @return {SMSound} The SMSound object + */ + + this.unmute = function() { + + _t.muted = false; + var hasIO = typeof _t._iO.volume !== 'undefined'; + + if (!_t.isHTML5) { + _flash._setVolume(_t.sID, hasIO?_t._iO.volume:_t.options.volume); + } else if (_t._a) { + _t._a.muted = false; + } + + return _t; + + }; + + /** + * Toggles the muted state of a sound. + * + * @return {SMSound} The SMSound object + */ + + this.toggleMute = function() { + + return (_t.muted?_t.unmute():_t.mute()); + + }; + + /** + * Registers a callback to be fired when a sound reaches a given position during playback. + * + * @param {number} nPosition The position to watch for + * @param {function} oMethod The relevant callback to fire + * @param {object} oScope Optional: The scope to apply the callback to + * @return {SMSound} The SMSound object + */ + + this.onPosition = function(nPosition, oMethod, oScope) { + + // TODO: basic dupe checking? + + _onPositionItems.push({ + position: nPosition, + method: oMethod, + scope: (typeof oScope !== 'undefined' ? oScope : _t), + fired: false + }); + + return _t; + + }; + + // legacy/backwards-compability: lower-case method name + this.onposition = this.onPosition; + + /** + * Removes registered callback(s) from a sound, by position and/or callback. + * + * @param {number} nPosition The position to clear callback(s) for + * @param {function} oMethod Optional: Identify one callback to be removed when multiple listeners exist for one position + * @return {SMSound} The SMSound object + */ + + this.clearOnPosition = function(nPosition, oMethod) { + + var i; + + nPosition = parseInt(nPosition, 10); + + if (isNaN(nPosition)) { + // safety check + return false; + } + + for (i=0; i < _onPositionItems.length; i++) { + + if (nPosition === _onPositionItems[i].position) { + // remove this item if no method was specified, or, if the method matches + if (!oMethod || (oMethod === _onPositionItems[i].method)) { + if (_onPositionItems[i].fired) { + // decrement "fired" counter, too + _onPositionFired--; + } + _onPositionItems.splice(i, 1); + } + } + + } + + }; + + this._processOnPosition = function() { + + var i, item, j = _onPositionItems.length; + + if (!j || !_t.playState || _onPositionFired >= j) { + return false; + } + + for (i=j; i--;) { + item = _onPositionItems[i]; + if (!item.fired && _t.position >= item.position) { + item.fired = true; + _onPositionFired++; + item.method.apply(item.scope, [item.position]); + } + } + + return true; + + }; + + this._resetOnPosition = function(nPosition) { + + // reset "fired" for items interested in this position + var i, item, j = _onPositionItems.length; + + if (!j) { + return false; + } + + for (i=j; i--;) { + item = _onPositionItems[i]; + if (item.fired && nPosition <= item.position) { + item.fired = false; + _onPositionFired--; + } + } + + return true; + + }; + + /** + * SMSound() private internals + * -------------------------------- + */ + + _applyFromTo = function() { + + var _iO = _t._iO, + f = _iO.from, + t = _iO.to, + start, end; + + end = function() { + + // end has been reached. + _s._wD(_t.sID + ': "to" time of ' + t + ' reached.'); + + // detach listener + _t.clearOnPosition(t, end); + + // stop should clear this, too + _t.stop(); + + }; + + start = function() { + + _s._wD(_t.sID + ': playing "from" ' + f); + + // add listener for end + if (t !== null && !isNaN(t)) { + _t.onPosition(t, end); + } + + }; + + if (f !== null && !isNaN(f)) { + + // apply to instance options, guaranteeing correct start position. + _iO.position = f; + + // multiShot timing can't be tracked, so prevent that. + _iO.multiShot = false; + + start(); + + } + + // return updated instanceOptions including starting position + return _iO; + + }; + + _attachOnPosition = function() { + + var op = _t._iO.onposition; + + // attach onposition things, if any, now. + + if (op) { + + var item; + + for (item in op) { + if (op.hasOwnProperty(item)) { + _t.onPosition(parseInt(item, 10), op[item]); + } + } + + } + + }; + + _detachOnPosition = function() { + + var op = _t._iO.onposition; + + // detach any onposition()-style listeners. + + if (op) { + + var item; + + for (item in op) { + if (op.hasOwnProperty(item)) { + _t.clearOnPosition(parseInt(item, 10)); + } + } + + } + + }; + + _start_html5_timer = function() { + + if (_t.isHTML5) { + _startTimer(_t); + } + + }; + + _stop_html5_timer = function() { + + if (_t.isHTML5) { + _stopTimer(_t); + } + + }; + + _resetProperties = function() { + + _onPositionItems = []; + _onPositionFired = 0; + _onplay_called = false; + + _t._hasTimer = null; + _t._a = null; + _t._html5_canplay = false; + _t.bytesLoaded = null; + _t.bytesTotal = null; + _t.duration = (_t._iO && _t._iO.duration ? _t._iO.duration : null); + _t.durationEstimate = null; + + // legacy: 1D array + _t.eqData = []; + + _t.eqData.left = []; + _t.eqData.right = []; + + _t.failures = 0; + _t.isBuffering = false; + _t.instanceOptions = {}; + _t.instanceCount = 0; + _t.loaded = false; + _t.metadata = {}; + + // 0 = uninitialised, 1 = loading, 2 = failed/error, 3 = loaded/success + _t.readyState = 0; + + _t.muted = false; + _t.paused = false; + + _t.peakData = { + left: 0, + right: 0 + }; + + _t.waveformData = { + left: [], + right: [] + }; + + _t.playState = 0; + _t.position = null; + + }; + + _resetProperties(); + + /** + * Pseudo-private SMSound internals + * -------------------------------- + */ + + this._onTimer = function(bForce) { + + /** + * HTML5-only _whileplaying() etc. + * called from both HTML5 native events, and polling/interval-based timers + * mimics flash and fires only when time/duration change, so as to be polling-friendly + */ + + var duration, isNew = false, time, x = {}; + + if (_t._hasTimer || bForce) { + + // TODO: May not need to track readyState (1 = loading) + + if (_t._a && (bForce || ((_t.playState > 0 || _t.readyState === 1) && !_t.paused))) { + + duration = _t._get_html5_duration(); + + if (duration !== _lastHTML5State.duration) { + + _lastHTML5State.duration = duration; + _t.duration = duration; + isNew = true; + + } + + // TODO: investigate why this goes wack if not set/re-set each time. + _t.durationEstimate = _t.duration; + + time = (_t._a.currentTime * 1000 || 0); + + if (time !== _lastHTML5State.time) { + + _lastHTML5State.time = time; + isNew = true; + + } + + if (isNew || bForce) { + + _t._whileplaying(time,x,x,x,x); + + } + + return isNew; + + } else { + + // _s._wD('_onTimer: Warn for "'+_t.sID+'": '+(!_t._a?'Could not find element. ':'')+(_t.playState === 0?'playState bad, 0?':'playState = '+_t.playState+', OK')); + + return false; + + } + + } + + }; + + this._get_html5_duration = function() { + + var _iO = _t._iO, + d = (_t._a ? _t._a.duration*1000 : (_iO ? _iO.duration : undefined)), + result = (d && !isNaN(d) && d !== Infinity ? d : (_iO ? _iO.duration : null)); + + return result; + + }; + + this._setup_html5 = function(oOptions) { + + var _iO = _mixin(_t._iO, oOptions), d = decodeURI, + _a = _useGlobalHTML5Audio ? _s._global_a : _t._a, + _dURL = d(_iO.url), + _oldIO = (_a && _a._t ? _a._t.instanceOptions : null); + + if (_a) { + + if (_a._t) { + + if (!_useGlobalHTML5Audio && _dURL === d(_lastURL)) { + // same url, ignore request + return _a; + } else if (_useGlobalHTML5Audio && _oldIO.url === _iO.url && (!_lastURL || (_lastURL === _oldIO.url))) { + // iOS-type reuse case + return _a; + } + + } + + _s._wD('setting new URL on existing object: ' + _dURL + (_lastURL ? ', old URL: ' + _lastURL : '')); + + /** + * "First things first, I, Poppa.." (reset the previous state of the old sound, if playing) + * Fixes case with devices that can only play one sound at a time + * Otherwise, other sounds in mid-play will be terminated without warning and in a stuck state + */ + + if (_useGlobalHTML5Audio && _a._t && _a._t.playState && _iO.url !== _oldIO.url) { + _a._t.stop(); + } + + // new URL, so reset load/playstate and so on + _resetProperties(); + + _a.src = _iO.url; + _t.url = _iO.url; + _lastURL = _iO.url; + _a._called_load = false; + + } else { + + _s._wD('creating HTML5 Audio() element with URL: '+_dURL); + _a = new Audio(_iO.url); + + _a._called_load = false; + + // android (seen in 2.3/Honeycomb) sometimes fails first .load() -> .play(), results in playback failure and ended() events? + if (_is_android) { + _a._called_load = true; + } + + if (_useGlobalHTML5Audio) { + _s._global_a = _a; + } + + } + + _t.isHTML5 = true; + + // store a ref on the track + _t._a = _a; + + // store a ref on the audio + _a._t = _t; + + _add_html5_events(); + _a.loop = (_iO.loops>1?'loop':''); + + if (_iO.autoLoad || _iO.autoPlay) { + + _t.load(); + + } else { + + // early HTML5 implementation (non-standard) + _a.autobuffer = false; + + // standard + _a.preload = 'none'; + + } + + // boolean instead of "loop", for webkit? - spec says string. http://www.w3.org/TR/html-markup/audio.html#audio.attrs.loop + _a.loop = (_iO.loops > 1 ? 'loop' : ''); + + return _a; + + }; + + _add_html5_events = function() { + + if (_t._a._added_events) { + return false; + } + + var f; + + function add(oEvt, oFn, bCapture) { + return _t._a ? _t._a.addEventListener(oEvt, oFn, bCapture||false) : null; + } + + _s._wD(_h5+'adding event listeners: '+_t.sID); + _t._a._added_events = true; + + for (f in _html5_events) { + if (_html5_events.hasOwnProperty(f)) { + add(f, _html5_events[f]); + } + } + + return true; + + }; + + _remove_html5_events = function() { + + // Remove event listeners + + var f; + + function remove(oEvt, oFn, bCapture) { + return (_t._a ? _t._a.removeEventListener(oEvt, oFn, bCapture||false) : null); + } + + _s._wD(_h5+'removing event listeners: '+_t.sID); + _t._a._added_events = false; + + for (f in _html5_events) { + if (_html5_events.hasOwnProperty(f)) { + remove(f, _html5_events[f]); + } + } + + }; + + /** + * Pseudo-private event internals + * ------------------------------ + */ + + this._onload = function(nSuccess) { + + + var fN, loadOK = !!(nSuccess); + _s._wD(fN + '"' + _t.sID + '"' + (loadOK?' loaded.':' failed to load? - ' + _t.url), (loadOK?1:2)); + + // + fN = 'SMSound._onload(): '; + if (!loadOK && !_t.isHTML5) { + if (_s.sandbox.noRemote === true) { + _s._wD(fN + _str('noNet'), 1); + } + if (_s.sandbox.noLocal === true) { + _s._wD(fN + _str('noLocal'), 1); + } + } + // + + _t.loaded = loadOK; + _t.readyState = loadOK?3:2; + _t._onbufferchange(0); + + if (_t._iO.onload) { + _t._iO.onload.apply(_t, [loadOK]); + } + + return true; + + }; + + this._onbufferchange = function(nIsBuffering) { + + if (_t.playState === 0) { + // ignore if not playing + return false; + } + + if ((nIsBuffering && _t.isBuffering) || (!nIsBuffering && !_t.isBuffering)) { + return false; + } + + _t.isBuffering = (nIsBuffering === 1); + if (_t._iO.onbufferchange) { + _s._wD('SMSound._onbufferchange(): ' + nIsBuffering); + _t._iO.onbufferchange.apply(_t); + } + + return true; + + }; + + /** + * Notify Mobile Safari that user action is required + * to continue playing / loading the audio file. + */ + + this._onsuspend = function() { + + if (_t._iO.onsuspend) { + _s._wD('SMSound._onsuspend()'); + _t._iO.onsuspend.apply(_t); + } + + return true; + + }; + + /** + * flash 9/movieStar + RTMP-only method, should fire only once at most + * at this point we just recreate failed sounds rather than trying to reconnect + */ + + this._onfailure = function(msg, level, code) { + + _t.failures++; + _s._wD('SMSound._onfailure(): "'+_t.sID+'" count '+_t.failures); + + if (_t._iO.onfailure && _t.failures === 1) { + _t._iO.onfailure(_t, msg, level, code); + } else { + _s._wD('SMSound._onfailure(): ignoring'); + } + + }; + + this._onfinish = function() { + + // store local copy before it gets trashed.. + var _io_onfinish = _t._iO.onfinish; + + _t._onbufferchange(0); + _t._resetOnPosition(0); + + // reset some state items + if (_t.instanceCount) { + + _t.instanceCount--; + + if (!_t.instanceCount) { + + // remove onPosition listeners, if any + _detachOnPosition(); + + // reset instance options + _t.playState = 0; + _t.paused = false; + _t.instanceCount = 0; + _t.instanceOptions = {}; + _t._iO = {}; + _stop_html5_timer(); + + } + + if (!_t.instanceCount || _t._iO.multiShotEvents) { + // fire onfinish for last, or every instance + if (_io_onfinish) { + _s._wD('SMSound._onfinish(): "' + _t.sID + '"'); + _io_onfinish.apply(_t); + } + } + + } + + }; + + this._whileloading = function(nBytesLoaded, nBytesTotal, nDuration, nBufferLength) { + + var _iO = _t._iO; + + _t.bytesLoaded = nBytesLoaded; + _t.bytesTotal = nBytesTotal; + _t.duration = Math.floor(nDuration); + _t.bufferLength = nBufferLength; + + if (!_iO.isMovieStar) { + + if (_iO.duration) { + // use options, if specified and larger + _t.durationEstimate = (_t.duration > _iO.duration) ? _t.duration : _iO.duration; + } else { + _t.durationEstimate = parseInt((_t.bytesTotal / _t.bytesLoaded) * _t.duration, 10); + + } + + if (_t.durationEstimate === undefined) { + _t.durationEstimate = _t.duration; + } + + if (_t.readyState !== 3 && _iO.whileloading) { + _iO.whileloading.apply(_t); + } + + } else { + + _t.durationEstimate = _t.duration; + if (_t.readyState !== 3 && _iO.whileloading) { + _iO.whileloading.apply(_t); + } + + } + + }; + + this._whileplaying = function(nPosition, oPeakData, oWaveformDataLeft, oWaveformDataRight, oEQData) { + + var _iO = _t._iO; + + if (isNaN(nPosition) || nPosition === null) { + // flash safety net + return false; + } + + _t.position = nPosition; + _t._processOnPosition(); + + if (!_t.isHTML5 && _fV > 8) { + + if (_iO.usePeakData && typeof oPeakData !== 'undefined' && oPeakData) { + _t.peakData = { + left: oPeakData.leftPeak, + right: oPeakData.rightPeak + }; + } + + if (_iO.useWaveformData && typeof oWaveformDataLeft !== 'undefined' && oWaveformDataLeft) { + _t.waveformData = { + left: oWaveformDataLeft.split(','), + right: oWaveformDataRight.split(',') + }; + } + + if (_iO.useEQData) { + if (typeof oEQData !== 'undefined' && oEQData && oEQData.leftEQ) { + var eqLeft = oEQData.leftEQ.split(','); + _t.eqData = eqLeft; + _t.eqData.left = eqLeft; + if (typeof oEQData.rightEQ !== 'undefined' && oEQData.rightEQ) { + _t.eqData.right = oEQData.rightEQ.split(','); + } + } + } + + } + + if (_t.playState === 1) { + + // special case/hack: ensure buffering is false if loading from cache (and not yet started) + if (!_t.isHTML5 && _fV === 8 && !_t.position && _t.isBuffering) { + _t._onbufferchange(0); + } + + if (_iO.whileplaying) { + // flash may call after actual finish + _iO.whileplaying.apply(_t); + } + + } + + return true; + + }; + + this._onmetadata = function(oMDProps, oMDData) { + + /** + * internal: flash 9 + NetStream (MovieStar/RTMP-only) feature + * RTMP may include song title, MovieStar content may include encoding info + * + * @param {array} oMDProps (names) + * @param {array} oMDData (values) + */ + + _s._wD('SMSound._onmetadata(): "' + this.sID + '" metadata received.'); + + var oData = {}, i, j; + + for (i = 0, j = oMDProps.length; i < j; i++) { + oData[oMDProps[i]] = oMDData[i]; + } + _t.metadata = oData; + + if (_t._iO.onmetadata) { + _t._iO.onmetadata.apply(_t); + } + + }; + + this._onid3 = function(oID3Props, oID3Data) { + + /** + * internal: flash 8 + flash 9 ID3 feature + * may include artist, song title etc. + * + * @param {array} oID3Props (names) + * @param {array} oID3Data (values) + */ + + _s._wD('SMSound._onid3(): "' + this.sID + '" ID3 data received.'); + + var oData = [], i, j; + + for (i = 0, j = oID3Props.length; i < j; i++) { + oData[oID3Props[i]] = oID3Data[i]; + } + _t.id3 = _mixin(_t.id3, oData); + + if (_t._iO.onid3) { + _t._iO.onid3.apply(_t); + } + + }; + + // flash/RTMP-only + + this._onconnect = function(bSuccess) { + + bSuccess = (bSuccess === 1); + _s._wD('SMSound._onconnect(): "'+_t.sID+'"'+(bSuccess?' connected.':' failed to connect? - '+_t.url), (bSuccess?1:2)); + _t.connected = bSuccess; + + if (bSuccess) { + + _t.failures = 0; + + if (_idCheck(_t.sID)) { + if (_t.getAutoPlay()) { + // only update the play state if auto playing + _t.play(undefined, _t.getAutoPlay()); + } else if (_t._iO.autoLoad) { + _t.load(); + } + } + + if (_t._iO.onconnect) { + _t._iO.onconnect.apply(_t, [bSuccess]); + } + + } + + }; + + this._ondataerror = function(sError) { + + // flash 9 wave/eq data handler + // hack: called at start, and end from flash at/after onfinish() + if (_t.playState > 0) { + _s._wD('SMSound._ondataerror(): ' + sError); + if (_t._iO.ondataerror) { + _t._iO.ondataerror.apply(_t); + } + } + + }; + + }; // SMSound() + + /** + * Private SoundManager internals + * ------------------------------ + */ + + _getDocument = function() { + + return (_doc.body || _doc._docElement || _doc.getElementsByTagName('div')[0]); + + }; + + _id = function(sID) { + + return _doc.getElementById(sID); + + }; + + _mixin = function(oMain, oAdd) { + + // non-destructive merge + var o1 = {}, i, o2, o; + + // clone c1 + for (i in oMain) { + if (oMain.hasOwnProperty(i)) { + o1[i] = oMain[i]; + } + } + + o2 = (typeof oAdd === 'undefined'?_s.defaultOptions:oAdd); + for (o in o2) { + if (o2.hasOwnProperty(o) && typeof o1[o] === 'undefined') { + o1[o] = o2[o]; + } + } + return o1; + + }; + + _event = (function() { + + var old = (_win.attachEvent), + evt = { + add: (old?'attachEvent':'addEventListener'), + remove: (old?'detachEvent':'removeEventListener') + }; + + function getArgs(oArgs) { + + var args = _slice.call(oArgs), len = args.length; + + if (old) { + // prefix + args[1] = 'on' + args[1]; + if (len > 3) { + // no capture + args.pop(); + } + } else if (len === 3) { + args.push(false); + } + + return args; + + } + + function apply(args, sType) { + + var element = args.shift(), + method = [evt[sType]]; + + if (old) { + element[method](args[0], args[1]); + } else { + element[method].apply(element, args); + } + + } + + function add() { + + apply(getArgs(arguments), 'add'); + + } + + function remove() { + + apply(getArgs(arguments), 'remove'); + + } + + return { + 'add': add, + 'remove': remove + }; + + }()); + + /** + * Internal HTML5 event handling + * ----------------------------- + */ + + function _html5_event(oFn) { + + // wrap html5 event handlers so we don't call them on destroyed sounds + + return function(e) { + + var t = this._t; + + if (!t || !t._a) { + // + if (t && t.sID) { + _s._wD(_h5+'ignoring '+e.type+': '+t.sID); + } else { + _s._wD(_h5+'ignoring '+e.type); + } + // + return null; + } else { + return oFn.call(this, e); + } + + }; + + } + + _html5_events = { + + // HTML5 event-name-to-handler map + + abort: _html5_event(function(e) { + + _s._wD(_h5+'abort: '+this._t.sID); + + }), + + // enough has loaded to play + + canplay: _html5_event(function(e) { + + var t = this._t; + + if (t._html5_canplay) { + // this event has already fired. ignore. + return true; + } + + t._html5_canplay = true; + _s._wD(_h5+'canplay: '+t.sID+', '+t.url); + t._onbufferchange(0); + var position1K = (!isNaN(t.position)?t.position/1000:null); + + // set the position if position was set before the sound loaded + if (t.position && this.currentTime !== position1K) { + _s._wD(_h5+'canplay: setting position to '+position1K); + try { + this.currentTime = position1K; + } catch(ee) { + _s._wD(_h5+'setting position failed: '+ee.message, 2); + } + } + + // hack for HTML5 from/to case + if (t._iO._oncanplay) { + t._iO._oncanplay(); + } + + }), + + load: _html5_event(function(e) { + + var t = this._t; + + if (!t.loaded) { + t._onbufferchange(0); + // should be 1, and the same + t._whileloading(t.bytesTotal, t.bytesTotal, t._get_html5_duration()); + t._onload(true); + } + + }), + + emptied: _html5_event(function(e) { + + _s._wD(_h5+'emptied: '+this._t.sID); + + }), + + ended: _html5_event(function(e) { + + var t = this._t; + + _s._wD(_h5+'ended: '+t.sID); + t._onfinish(); + + }), + + error: _html5_event(function(e) { + + _s._wD(_h5+'error: '+this.error.code); + // call load with error state? + this._t._onload(false); + + }), + + loadeddata: _html5_event(function(e) { + + var t = this._t, + // at least 1 byte, so math works + bytesTotal = t.bytesTotal || 1; + + _s._wD(_h5+'loadeddata: '+this._t.sID); + + // safari seems to nicely report progress events, eventually totalling 100% + if (!t._loaded && !_isSafari) { + t.duration = t._get_html5_duration(); + // fire whileloading() with 100% values + t._whileloading(bytesTotal, bytesTotal, t._get_html5_duration()); + t._onload(true); + } + + }), + + loadedmetadata: _html5_event(function(e) { + + _s._wD(_h5+'loadedmetadata: '+this._t.sID); + + }), + + loadstart: _html5_event(function(e) { + + _s._wD(_h5+'loadstart: '+this._t.sID); + // assume buffering at first + this._t._onbufferchange(1); + + }), + + play: _html5_event(function(e) { + + _s._wD(_h5+'play: '+this._t.sID+', '+this._t.url); + // once play starts, no buffering + this._t._onbufferchange(0); + + }), + + playing: _html5_event(function(e) { + + _s._wD(_h5+'playing: '+this._t.sID); + + // once play starts, no buffering + this._t._onbufferchange(0); + + }), + + progress: _html5_event(function(e) { + + var t = this._t; + + if (t.loaded) { + return false; + } + + var i, j, str, buffered = 0, + isProgress = (e.type === 'progress'), + ranges = e.target.buffered, + + // firefox 3.6 implements e.loaded/total (bytes) + loaded = (e.loaded||0), + + total = (e.total||1); + + if (ranges && ranges.length) { + + // if loaded is 0, try TimeRanges implementation as % of load + // https://developer.mozilla.org/en/DOM/TimeRanges + + for (i=ranges.length; i--;) { + buffered = (ranges.end(i) - ranges.start(i)); + } + + // linear case, buffer sum; does not account for seeking and HTTP partials / byte ranges + loaded = buffered/e.target.duration; + + // + if (isProgress && ranges.length > 1) { + str = []; + j = ranges.length; + for (i=0; i + + } + + if (!isNaN(loaded)) { + + // if progress, likely not buffering + t._onbufferchange(0); + t._whileloading(loaded, total, t._get_html5_duration()); + if (loaded && total && loaded === total) { + // in case "onload" doesn't fire (eg. gecko 1.9.2) + _html5_events.load.call(this, e); + } + + } + + }), + + ratechange: _html5_event(function(e) { + + _s._wD(_h5+'ratechange: '+this._t.sID); + + }), + + suspend: _html5_event(function(e) { + + // download paused/stopped, may have finished (eg. onload) + var t = this._t; + + _s._wD(_h5+'suspend: '+t.sID); + _html5_events.progress.call(this, e); + t._onsuspend(); + + }), + + stalled: _html5_event(function(e) { + + _s._wD(_h5+'stalled: '+this._t.sID); + + }), + + timeupdate: _html5_event(function(e) { + + this._t._onTimer(); + + }), + + waiting: _html5_event(function(e) { + + var t = this._t; + + // see also: seeking + _s._wD(_h5+'waiting: '+t.sID); + + // playback faster than download rate, etc. + t._onbufferchange(1); + + }) + + }; + + _html5OK = function(iO) { + + // Use type, if specified. If HTML5-only mode, no other options, so just give 'er + return (!iO.serverURL && (iO.type?_html5CanPlay({type:iO.type}):_html5CanPlay({url:iO.url})||_s.html5Only)); + + }; + + _html5Unload = function(oAudio) { + + /** + * Internal method: Unload media, and cancel any current/pending network requests. + * Firefox can load an empty URL, which allegedly destroys the decoder and stops the download. + * https://developer.mozilla.org/En/Using_audio_and_video_in_Firefox#Stopping_the_download_of_media + * Other UA behaviour is unclear, so everyone else gets an about:blank-style URL. + */ + + if (oAudio) { + // Firefox likes '' for unload, most other UAs don't and fail to unload. + oAudio.src = (_is_firefox ? '' : _emptyURL); + } + + }; + + _html5CanPlay = function(o) { + + /** + * Try to find MIME, test and return truthiness + * o = { + * url: '/path/to/an.mp3', + * type: 'audio/mp3' + * } + */ + + if (!_s.useHTML5Audio || !_s.hasHTML5) { + return false; + } + + var url = (o.url || null), + mime = (o.type || null), + aF = _s.audioFormats, + result, + offset, + fileExt, + item; + + function preferFlashCheck(kind) { + + // whether flash should play a given type + return (_s.preferFlash && _hasFlash && !_s.ignoreFlash && (typeof _s.flash[kind] !== 'undefined' && _s.flash[kind])); + + } + + // account for known cases like audio/mp3 + + if (mime && _s.html5[mime] !== 'undefined') { + return (_s.html5[mime] && !preferFlashCheck(mime)); + } + + if (!_html5Ext) { + _html5Ext = []; + for (item in aF) { + if (aF.hasOwnProperty(item)) { + _html5Ext.push(item); + if (aF[item].related) { + _html5Ext = _html5Ext.concat(aF[item].related); + } + } + } + _html5Ext = new RegExp('\\.('+_html5Ext.join('|')+')(\\?.*)?$','i'); + } + + // TODO: Strip URL queries, etc. + fileExt = (url ? url.toLowerCase().match(_html5Ext) : null); + + if (!fileExt || !fileExt.length) { + if (!mime) { + return false; + } else { + // audio/mp3 -> mp3, result should be known + offset = mime.indexOf(';'); + // strip "audio/X; codecs.." + fileExt = (offset !== -1?mime.substr(0,offset):mime).substr(6); + } + } else { + // match the raw extension name - "mp3", for example + fileExt = fileExt[1]; + } + + if (fileExt && typeof _s.html5[fileExt] !== 'undefined') { + // result known + return (_s.html5[fileExt] && !preferFlashCheck(fileExt)); + } else { + mime = 'audio/'+fileExt; + result = _s.html5.canPlayType({type:mime}); + _s.html5[fileExt] = result; + // _s._wD('canPlayType, found result: '+result); + return (result && _s.html5[mime] && !preferFlashCheck(mime)); + } + + }; + + _testHTML5 = function() { + + if (!_s.useHTML5Audio || typeof Audio === 'undefined') { + return false; + } + + // double-whammy: Opera 9.64 throws WRONG_ARGUMENTS_ERR if no parameter passed to Audio(), and Webkit + iOS happily tries to load "null" as a URL. :/ + var a = (typeof Audio !== 'undefined' ? (_isOpera ? new Audio(null) : new Audio()) : null), + item, support = {}, aF, i; + + function _cp(m) { + + var canPlay, i, j, isOK = false; + + if (!a || typeof a.canPlayType !== 'function') { + return false; + } + + if (m instanceof Array) { + // iterate through all mime types, return any successes + for (i=0, j=m.length; i + notReady: 'Not loaded yet - wait for soundManager.onload()/onready()', + notOK: 'Audio support is not available.', + domError: _smc + 'createMovie(): appendChild/innerHTML call failed. DOM not ready or other error.', + spcWmode: _smc + 'createMovie(): Removing wmode, preventing known SWF loading issue(s)', + swf404: _sm + ': Verify that %s is a valid path.', + tryDebug: 'Try ' + _sm + '.debugFlash = true for more security details (output goes to SWF.)', + checkSWF: 'See SWF output for more debug info.', + localFail: _sm + ': Non-HTTP page (' + _doc.location.protocol + ' URL?) Review Flash player security settings for this special case:\nhttp://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html\nMay need to add/allow path, eg. c:/sm2/ or /users/me/sm2/', + waitFocus: _sm + ': Special case: Waiting for focus-related event..', + waitImpatient: _sm + ': Getting impatient, still waiting for Flash%s...', + waitForever: _sm + ': Waiting indefinitely for Flash (will recover if unblocked)...', + needFunction: _sm + ': Function object expected for %s', + badID: 'Warning: Sound ID "%s" should be a string, starting with a non-numeric character', + currentObj: '--- ' + _sm + '._debug(): Current sound objects ---', + waitEI: _smc + 'initMovie(): Waiting for ExternalInterface call from Flash..', + waitOnload: _sm + ': Waiting for window.onload()', + docLoaded: _sm + ': Document already loaded', + onload: _smc + 'initComplete(): calling soundManager.onload()', + onloadOK: _sm + '.onload() complete', + init: _smc + 'init()', + didInit: _smc + 'init(): Already called?', + flashJS: _sm + ': Attempting to call Flash from JS..', + secNote: 'Flash security note: Network/internet URLs will not load due to security restrictions. Access can be configured via Flash Player Global Security Settings Page: http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html', + badRemove: 'Warning: Failed to remove flash movie.', + noPeak: 'Warning: peakData features unsupported for movieStar formats', + shutdown: _sm + '.disable(): Shutting down', + queue: _sm + ': Queueing %s handler', + smFail: _sm + ': Failed to initialise.', + smError: 'SMSound.load(): Exception: JS-Flash communication failed, or JS error.', + fbTimeout: 'No flash response, applying .'+_swfCSS.swfTimedout+' CSS..', + fbLoaded: 'Flash loaded', + fbHandler: _smc+'flashBlockHandler()', + manURL: 'SMSound.load(): Using manually-assigned URL', + onURL: _sm + '.load(): current URL already assigned.', + badFV: _sm + '.flashVersion must be 8 or 9. "%s" is invalid. Reverting to %s.', + as2loop: 'Note: Setting stream:false so looping can work (flash 8 limitation)', + noNSLoop: 'Note: Looping not implemented for MovieStar formats', + needfl9: 'Note: Switching to flash 9, required for MP4 formats.', + mfTimeout: 'Setting flashLoadTimeout = 0 (infinite) for off-screen, mobile flash case', + mfOn: 'mobileFlash::enabling on-screen flash repositioning', + policy: 'Enabling usePolicyFile for data access' + // + + }; + + _str = function() { + + // internal string replace helper. + // arguments: o [,items to replace] + // + + // real array, please + var args = _slice.call(arguments), + + // first arg + o = args.shift(), + + str = (_strings && _strings[o]?_strings[o]:''), i, j; + if (str && args && args.length) { + for (i = 0, j = args.length; i < j; i++) { + str = str.replace('%s', args[i]); + } + } + + return str; + // + + }; + + _loopFix = function(sOpt) { + + // flash 8 requires stream = false for looping to work + if (_fV === 8 && sOpt.loops > 1 && sOpt.stream) { + _wDS('as2loop'); + sOpt.stream = false; + } + + return sOpt; + + }; + + _policyFix = function(sOpt, sPre) { + + if (sOpt && !sOpt.usePolicyFile && (sOpt.onid3 || sOpt.usePeakData || sOpt.useWaveformData || sOpt.useEQData)) { + _s._wD((sPre || '') + _str('policy')); + sOpt.usePolicyFile = true; + } + + return sOpt; + + }; + + _complain = function(sMsg) { + + // + if (typeof console !== 'undefined' && typeof console.warn !== 'undefined') { + console.warn(sMsg); + } else { + _s._wD(sMsg); + } + // + + }; + + _doNothing = function() { + + return false; + + }; + + _disableObject = function(o) { + + var oProp; + + for (oProp in o) { + if (o.hasOwnProperty(oProp) && typeof o[oProp] === 'function') { + o[oProp] = _doNothing; + } + } + + oProp = null; + + }; + + _failSafely = function(bNoDisable) { + + // general failure exception handler + + if (typeof bNoDisable === 'undefined') { + bNoDisable = false; + } + + if (_disabled || bNoDisable) { + _wDS('smFail', 2); + _s.disable(bNoDisable); + } + + }; + + _normalizeMovieURL = function(smURL) { + + var urlParams = null, url; + + if (smURL) { + if (smURL.match(/\.swf(\?.*)?$/i)) { + urlParams = smURL.substr(smURL.toLowerCase().lastIndexOf('.swf?') + 4); + if (urlParams) { + // assume user knows what they're doing + return smURL; + } + } else if (smURL.lastIndexOf('/') !== smURL.length - 1) { + // append trailing slash, if needed + smURL += '/'; + } + } + + url = (smURL && smURL.lastIndexOf('/') !== - 1 ? smURL.substr(0, smURL.lastIndexOf('/') + 1) : './') + _s.movieURL; + + if (_s.noSWFCache) { + url += ('?ts=' + new Date().getTime()); + } + + return url; + + }; + + _setVersionInfo = function() { + + // short-hand for internal use + + _fV = parseInt(_s.flashVersion, 10); + + if (_fV !== 8 && _fV !== 9) { + _s._wD(_str('badFV', _fV, _defaultFlashVersion)); + _s.flashVersion = _fV = _defaultFlashVersion; + } + + // debug flash movie, if applicable + + var isDebug = (_s.debugMode || _s.debugFlash?'_debug.swf':'.swf'); + + if (_s.useHTML5Audio && !_s.html5Only && _s.audioFormats.mp4.required && _fV < 9) { + _s._wD(_str('needfl9')); + _s.flashVersion = _fV = 9; + } + + _s.version = _s.versionNumber + (_s.html5Only?' (HTML5-only mode)':(_fV === 9?' (AS3/Flash 9)':' (AS2/Flash 8)')); + + // set up default options + if (_fV > 8) { + // +flash 9 base options + _s.defaultOptions = _mixin(_s.defaultOptions, _s.flash9Options); + _s.features.buffering = true; + // +moviestar support + _s.defaultOptions = _mixin(_s.defaultOptions, _s.movieStarOptions); + _s.filePatterns.flash9 = new RegExp('\\.(mp3|' + _netStreamTypes.join('|') + ')(\\?.*)?$', 'i'); + _s.features.movieStar = true; + } else { + _s.features.movieStar = false; + } + + // regExp for flash canPlay(), etc. + _s.filePattern = _s.filePatterns[(_fV !== 8?'flash9':'flash8')]; + + // if applicable, use _debug versions of SWFs + _s.movieURL = (_fV === 8?'soundmanager2.swf':'soundmanager2_flash9.swf').replace('.swf', isDebug); + + _s.features.peakData = _s.features.waveformData = _s.features.eqData = (_fV > 8); + + }; + + _setPolling = function(bPolling, bHighPerformance) { + + if (!_flash) { + return false; + } + + _flash._setPolling(bPolling, bHighPerformance); + + }; + + _initDebug = function() { + + // starts debug mode, creating output
for UAs without console object + + // allow force of debug mode via URL + if (_s.debugURLParam.test(_wl)) { + _s.debugMode = true; + } + + // + if (_id(_s.debugID)) { + return false; + } + + var oD, oDebug, oTarget, oToggle, tmp; + + if (_s.debugMode && !_id(_s.debugID) && (!_hasConsole || !_s.useConsole || !_s.consoleOnly)) { + + oD = _doc.createElement('div'); + oD.id = _s.debugID + '-toggle'; + + oToggle = { + 'position': 'fixed', + 'bottom': '0px', + 'right': '0px', + 'width': '1.2em', + 'height': '1.2em', + 'lineHeight': '1.2em', + 'margin': '2px', + 'textAlign': 'center', + 'border': '1px solid #999', + 'cursor': 'pointer', + 'background': '#fff', + 'color': '#333', + 'zIndex': 10001 + }; + + oD.appendChild(_doc.createTextNode('-')); + oD.onclick = _toggleDebug; + oD.title = 'Toggle SM2 debug console'; + + if (_ua.match(/msie 6/i)) { + oD.style.position = 'absolute'; + oD.style.cursor = 'hand'; + } + + for (tmp in oToggle) { + if (oToggle.hasOwnProperty(tmp)) { + oD.style[tmp] = oToggle[tmp]; + } + } + + oDebug = _doc.createElement('div'); + oDebug.id = _s.debugID; + oDebug.style.display = (_s.debugMode?'block':'none'); + + if (_s.debugMode && !_id(oD.id)) { + try { + oTarget = _getDocument(); + oTarget.appendChild(oD); + } catch(e2) { + throw new Error(_str('domError')+' \n'+e2.toString()); + } + oTarget.appendChild(oDebug); + } + + } + + oTarget = null; + // + + }; + + _idCheck = this.getSoundById; + + // + _wDS = function(o, errorLevel) { + + if (!o) { + return ''; + } else { + return _s._wD(_str(o), errorLevel); + } + + }; + + // last-resort debugging option + + if (_wl.indexOf('sm2-debug=alert') + 1 && _s.debugMode) { + _s._wD = function(sText) {window.alert(sText);}; + } + + _toggleDebug = function() { + + var o = _id(_s.debugID), + oT = _id(_s.debugID + '-toggle'); + + if (!o) { + return false; + } + + if (_debugOpen) { + // minimize + oT.innerHTML = '+'; + o.style.display = 'none'; + } else { + oT.innerHTML = '-'; + o.style.display = 'block'; + } + + _debugOpen = !_debugOpen; + + }; + + _debugTS = function(sEventType, bSuccess, sMessage) { + + // troubleshooter debug hooks + + if (typeof sm2Debugger !== 'undefined') { + try { + sm2Debugger.handleEvent(sEventType, bSuccess, sMessage); + } catch(e) { + // oh well + } + } + + return true; + + }; + // + + _getSWFCSS = function() { + + var css = []; + + if (_s.debugMode) { + css.push(_swfCSS.sm2Debug); + } + + if (_s.debugFlash) { + css.push(_swfCSS.flashDebug); + } + + if (_s.useHighPerformance) { + css.push(_swfCSS.highPerf); + } + + return css.join(' '); + + }; + + _flashBlockHandler = function() { + + // *possible* flash block situation. + + var name = _str('fbHandler'), + p = _s.getMoviePercent(), + css = _swfCSS, + error = {type:'FLASHBLOCK'}; + + if (_s.html5Only) { + return false; + } + + if (!_s.ok()) { + + if (_needsFlash) { + // make the movie more visible, so user can fix + _s.oMC.className = _getSWFCSS() + ' ' + css.swfDefault + ' ' + (p === null?css.swfTimedout:css.swfError); + _s._wD(name+': '+_str('fbTimeout')+(p?' ('+_str('fbLoaded')+')':'')); + } + + _s.didFlashBlock = true; + + // fire onready(), complain lightly + _processOnEvents({type:'ontimeout', ignoreInit:true, error:error}); + _catchError(error); + + } else { + + // SM2 loaded OK (or recovered) + + // + if (_s.didFlashBlock) { + _s._wD(name+': Unblocked'); + } + // + + if (_s.oMC) { + _s.oMC.className = [_getSWFCSS(), css.swfDefault, css.swfLoaded + (_s.didFlashBlock?' '+css.swfUnblocked:'')].join(' '); + } + + } + + }; + + _addOnEvent = function(sType, oMethod, oScope) { + + if (typeof _on_queue[sType] === 'undefined') { + _on_queue[sType] = []; + } + + _on_queue[sType].push({ + 'method': oMethod, + 'scope': (oScope || null), + 'fired': false + }); + + }; + + _processOnEvents = function(oOptions) { + + // assume onready, if unspecified + + if (!oOptions) { + oOptions = { + type: 'onready' + }; + } + + if (!_didInit && oOptions && !oOptions.ignoreInit) { + // not ready yet. + return false; + } + + if (oOptions.type === 'ontimeout' && _s.ok()) { + // invalid case + return false; + } + + var status = { + success: (oOptions && oOptions.ignoreInit?_s.ok():!_disabled) + }, + + // queue specified by type, or none + srcQueue = (oOptions && oOptions.type?_on_queue[oOptions.type]||[]:[]), + + queue = [], i, j, + args = [status], + canRetry = (_needsFlash && _s.useFlashBlock && !_s.ok()); + + if (oOptions.error) { + args[0].error = oOptions.error; + } + + for (i = 0, j = srcQueue.length; i < j; i++) { + if (srcQueue[i].fired !== true) { + queue.push(srcQueue[i]); + } + } + + if (queue.length) { + _s._wD(_sm + ': Firing ' + queue.length + ' '+oOptions.type+'() item' + (queue.length === 1?'':'s')); + for (i = 0, j = queue.length; i < j; i++) { + if (queue[i].scope) { + queue[i].method.apply(queue[i].scope, args); + } else { + queue[i].method.apply(this, args); + } + if (!canRetry) { + // flashblock case doesn't count here + queue[i].fired = true; + } + } + } + + return true; + + }; + + _initUserOnload = function() { + + _win.setTimeout(function() { + + if (_s.useFlashBlock) { + _flashBlockHandler(); + } + + _processOnEvents(); + + // call user-defined "onload", scoped to window + + if (_s.onload instanceof Function) { + _wDS('onload', 1); + _s.onload.apply(_win); + _wDS('onloadOK', 1); + } + + if (_s.waitForWindowLoad) { + _event.add(_win, 'load', _initUserOnload); + } + + },1); + + }; + + _detectFlash = function() { + + // hat tip: Flash Detect library (BSD, (C) 2007) by Carl "DocYes" S. Yestrau - http://featureblend.com/javascript-flash-detection-library.html / http://featureblend.com/license.txt + + if (_hasFlash !== undefined) { + // this work has already been done. + return _hasFlash; + } + + var hasPlugin = false, n = navigator, nP = n.plugins, obj, type, types, AX = _win.ActiveXObject; + + if (nP && nP.length) { + type = 'application/x-shockwave-flash'; + types = n.mimeTypes; + if (types && types[type] && types[type].enabledPlugin && types[type].enabledPlugin.description) { + hasPlugin = true; + } + } else if (typeof AX !== 'undefined') { + try { + obj = new AX('ShockwaveFlash.ShockwaveFlash'); + } catch(e) { + // oh well + } + hasPlugin = (!!obj); + } + + _hasFlash = hasPlugin; + + return hasPlugin; + + }; + + _featureCheck = function() { + + var needsFlash, item, + + // iPhone <= 3.1 has broken HTML5 audio(), but firmware 3.2 (iPad) + iOS4 works. + isSpecial = (_is_iDevice && !!(_ua.match(/os (1|2|3_0|3_1)/i))); + + if (isSpecial) { + + // has Audio(), but is broken; let it load links directly. + _s.hasHTML5 = false; + + // ignore flash case, however + _s.html5Only = true; + + if (_s.oMC) { + _s.oMC.style.display = 'none'; + } + + return false; + + } + + if (_s.useHTML5Audio) { + + if (!_s.html5 || !_s.html5.canPlayType) { + _s._wD('SoundManager: No HTML5 Audio() support detected.'); + _s.hasHTML5 = false; + return true; + } else { + _s.hasHTML5 = true; + } + if (_isBadSafari) { + _s._wD(_smc+'Note: Buggy HTML5 Audio in Safari on this OS X release, see https://bugs.webkit.org/show_bug.cgi?id=32159 - '+(!_hasFlash?' would use flash fallback for MP3/MP4, but none detected.':'will use flash fallback for MP3/MP4, if available'),1); + if (_detectFlash()) { + return true; + } + } + } else { + + // flash needed (or, HTML5 needs enabling.) + return true; + + } + + for (item in _s.audioFormats) { + if (_s.audioFormats.hasOwnProperty(item)) { + if ((_s.audioFormats[item].required && !_s.html5.canPlayType(_s.audioFormats[item].type)) || _s.flash[item] || _s.flash[_s.audioFormats[item].type]) { + // flash may be required, or preferred for this format + needsFlash = true; + } + } + } + + // sanity check.. + if (_s.ignoreFlash) { + needsFlash = false; + } + + _s.html5Only = (_s.hasHTML5 && _s.useHTML5Audio && !needsFlash); + + return (!_s.html5Only); + + }; + + _parseURL = function(url) { + + /** + * Internal: Finds and returns the first playable URL (or failing that, the first URL.) + * @param {string or array} url A single URL string, OR, an array of URL strings or {url:'/path/to/resource', type:'audio/mp3'} objects. + */ + + var i, j, result = 0; + + if (url instanceof Array) { + + // find the first good one + for (i=0, j=url.length; i + var sb = _s.sandbox; + + sb.type = sandboxType; + sb.description = sb.types[(typeof sb.types[sandboxType] !== 'undefined'?sandboxType:'unknown')]; + + _s._wD('Flash security sandbox type: ' + sb.type); + + if (sb.type === 'localWithFile') { + + sb.noRemote = true; + sb.noLocal = false; + _wDS('secNote', 2); + + } else if (sb.type === 'localWithNetwork') { + + sb.noRemote = false; + sb.noLocal = true; + + } else if (sb.type === 'localTrusted') { + + sb.noRemote = false; + sb.noLocal = false; + + } + // + + }; + + this._externalInterfaceOK = function(flashDate, swfVersion) { + + // flash callback confirming flash loaded, EI working etc. + // flashDate = approx. timing/delay info for JS/flash bridge + // swfVersion: SWF build string + + if (_s.swfLoaded) { + return false; + } + + var e, eiTime = new Date().getTime(); + + _s._wD(_smc+'externalInterfaceOK()' + (flashDate?' (~' + (eiTime - flashDate) + ' ms)':'')); + _debugTS('swf', true); + _debugTS('flashtojs', true); + _s.swfLoaded = true; + _tryInitOnFocus = false; + + if (_isBadSafari) { + _badSafariFix(); + } + + // complain if JS + SWF build/version strings don't match, excluding +DEV builds + // + if (!swfVersion || swfVersion.replace(/\+dev/i,'') !== _s.versionNumber.replace(/\+dev/i, '')) { + + e = _sm + ': Fatal: JavaScript file build "' + _s.versionNumber + '" does not match Flash SWF build "' + swfVersion + '" at ' + _s.url + '. Ensure both are up-to-date.'; + + // escape flash -> JS stack so this error fires in window. + setTimeout(function versionMismatch() { + throw new Error(e); + }, 0); + + // exit, init will fail with timeout + return false; + + } + // + + if (_isIE) { + // IE needs a timeout OR delay until window.onload - may need TODO: investigating + setTimeout(_init, 100); + } else { + _init(); + } + + }; + + /** + * Private initialization helpers + * ------------------------------ + */ + + _createMovie = function(smID, smURL) { + + if (_didAppend && _appendSuccess) { + // ignore if already succeeded + return false; + } + + function _initMsg() { + _s._wD('-- SoundManager 2 ' + _s.version + (!_s.html5Only && _s.useHTML5Audio?(_s.hasHTML5?' + HTML5 audio':', no HTML5 audio support'):'') + (!_s.html5Only ? (_s.useHighPerformance?', high performance mode, ':', ') + (( _s.flashPollingInterval ? 'custom (' + _s.flashPollingInterval + 'ms)' : 'normal') + ' polling') + (_s.wmode?', wmode: ' + _s.wmode:'') + (_s.debugFlash?', flash debug mode':'') + (_s.useFlashBlock?', flashBlock mode':'') : '') + ' --', 1); + } + + if (_s.html5Only) { + + // 100% HTML5 mode + _setVersionInfo(); + + _initMsg(); + _s.oMC = _id(_s.movieID); + _init(); + + // prevent multiple init attempts + _didAppend = true; + + _appendSuccess = true; + + return false; + + } + + // flash path + var remoteURL = (smURL || _s.url), + localURL = (_s.altURL || remoteURL), + swfTitle = 'JS/Flash audio component (SoundManager 2)', + oEmbed, oMovie, oTarget = _getDocument(), tmp, movieHTML, oEl, extraClass = _getSWFCSS(), + s, x, sClass, side = 'auto', isRTL = null, + html = _doc.getElementsByTagName('html')[0]; + + isRTL = (html && html.dir && html.dir.match(/rtl/i)); + smID = (typeof smID === 'undefined'?_s.id:smID); + + function param(name, value) { + return ''; + } + + // safety check for legacy (change to Flash 9 URL) + _setVersionInfo(); + _s.url = _normalizeMovieURL("inc/SoundManager2/swf/"); + smURL = _s.url; + + _s.wmode = (!_s.wmode && _s.useHighPerformance ? 'transparent' : _s.wmode); + + if (_s.wmode !== null && (_ua.match(/msie 8/i) || (!_isIE && !_s.useHighPerformance)) && navigator.platform.match(/win32|win64/i)) { + /** + * extra-special case: movie doesn't load until scrolled into view when using wmode = anything but 'window' here + * does not apply when using high performance (position:fixed means on-screen), OR infinite flash load timeout + * wmode breaks IE 8 on Vista + Win7 too in some cases, as of January 2011 (?) + */ + _wDS('spcWmode'); + _s.wmode = null; + } + + oEmbed = { + 'name': smID, + 'id': smID, + 'src': smURL, + 'width': side, + 'height': side, + 'quality': 'high', + 'allowScriptAccess': _s.allowScriptAccess, + 'bgcolor': _s.bgColor, + 'pluginspage': _http+'www.macromedia.com/go/getflashplayer', + 'title': swfTitle, + 'type': 'application/x-shockwave-flash', + 'wmode': _s.wmode, + // http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html + 'hasPriority': 'true' + }; + + if (_s.debugFlash) { + oEmbed.FlashVars = 'debug=1'; + } + + if (!_s.wmode) { + // don't write empty attribute + delete oEmbed.wmode; + } + + if (_isIE) { + + // IE is "special". + oMovie = _doc.createElement('div'); + movieHTML = [ + '', + param('movie', smURL), + param('AllowScriptAccess', _s.allowScriptAccess), + param('quality', oEmbed.quality), + (_s.wmode? param('wmode', _s.wmode): ''), + param('bgcolor', _s.bgColor), + param('hasPriority', 'true'), + (_s.debugFlash ? param('FlashVars', oEmbed.FlashVars) : ''), + '' + ].join(''); + + } else { + + oMovie = _doc.createElement('embed'); + for (tmp in oEmbed) { + if (oEmbed.hasOwnProperty(tmp)) { + oMovie.setAttribute(tmp, oEmbed[tmp]); + } + } + + } + + _initDebug(); + extraClass = _getSWFCSS(); + oTarget = _getDocument(); + + if (oTarget) { + + _s.oMC = (_id(_s.movieID) || _doc.createElement('div')); + + if (!_s.oMC.id) { + + _s.oMC.id = _s.movieID; + _s.oMC.className = _swfCSS.swfDefault + ' ' + extraClass; + s = null; + oEl = null; + + if (!_s.useFlashBlock) { + if (_s.useHighPerformance) { + // on-screen at all times + s = { + 'position': 'fixed', + 'width': '8px', + 'height': '8px', + // >= 6px for flash to run fast, >= 8px to start up under Firefox/win32 in some cases. odd? yes. + 'bottom': '0px', + 'left': '0px', + 'overflow': 'hidden' + }; + } else { + // hide off-screen, lower priority + s = { + 'position': 'absolute', + 'width': '6px', + 'height': '6px', + 'top': '-9999px', + 'left': '-9999px' + }; + if (isRTL) { + s.left = Math.abs(parseInt(s.left,10))+'px'; + } + } + } + + if (_isWebkit) { + // soundcloud-reported render/crash fix, safari 5 + _s.oMC.style.zIndex = 10000; + } + + if (!_s.debugFlash) { + for (x in s) { + if (s.hasOwnProperty(x)) { + _s.oMC.style[x] = s[x]; + } + } + } + + try { + if (!_isIE) { + _s.oMC.appendChild(oMovie); + } + oTarget.appendChild(_s.oMC); + if (_isIE) { + oEl = _s.oMC.appendChild(_doc.createElement('div')); + oEl.className = _swfCSS.swfBox; + oEl.innerHTML = movieHTML; + } + _appendSuccess = true; + } catch(e) { + throw new Error(_str('domError')+' \n'+e.toString()); + } + + } else { + + // SM2 container is already in the document (eg. flashblock use case) + sClass = _s.oMC.className; + _s.oMC.className = (sClass?sClass+' ':_swfCSS.swfDefault) + (extraClass?' '+extraClass:''); + _s.oMC.appendChild(oMovie); + if (_isIE) { + oEl = _s.oMC.appendChild(_doc.createElement('div')); + oEl.className = _swfCSS.swfBox; + oEl.innerHTML = movieHTML; + } + _appendSuccess = true; + + } + + } + + _didAppend = true; + _initMsg(); + _s._wD(_smc+'createMovie(): Trying to load ' + smURL + (!_overHTTP && _s.altURL?' (alternate URL)':''), 1); + + return true; + + }; + + _initMovie = function() { + + if (_s.html5Only) { + _createMovie(); + return false; + } + + // attempt to get, or create, movie + // may already exist + if (_flash) { + return false; + } + + // inline markup case + _flash = _s.getMovie(_s.id); + + if (!_flash) { + if (!_oRemoved) { + // try to create + _createMovie(_s.id, _s.url); + } else { + // try to re-append removed movie after reboot() + if (!_isIE) { + _s.oMC.appendChild(_oRemoved); + } else { + _s.oMC.innerHTML = _oRemovedHTML; + } + _oRemoved = null; + _didAppend = true; + } + _flash = _s.getMovie(_s.id); + } + + // + if (_flash) { + _wDS('waitEI'); + } + // + + if (_s.oninitmovie instanceof Function) { + setTimeout(_s.oninitmovie, 1); + } + + return true; + + }; + + _delayWaitForEI = function() { + + setTimeout(_waitForEI, 1000); + + }; + + _waitForEI = function() { + + if (_waitingForEI) { + return false; + } + + _waitingForEI = true; + _event.remove(_win, 'load', _delayWaitForEI); + + if (_tryInitOnFocus && !_isFocused) { + // giant Safari 3.1 hack - assume mousemove = focus given lack of focus event + _wDS('waitFocus'); + return false; + } + + var p; + if (!_didInit) { + p = _s.getMoviePercent(); + _s._wD(_str('waitImpatient', (p === 100?' (SWF loaded)':(p > 0?' (SWF ' + p + '% loaded)':'')))); + } + + setTimeout(function() { + + p = _s.getMoviePercent(); + + // + if (!_didInit) { + _s._wD(_sm + ': No Flash response within expected time.\nLikely causes: ' + (p === 0?'Loading ' + _s.movieURL + ' may have failed (and/or Flash ' + _fV + '+ not present?), ':'') + 'Flash blocked or JS-Flash security error.' + (_s.debugFlash?' ' + _str('checkSWF'):''), 2); + if (!_overHTTP && p) { + _wDS('localFail', 2); + if (!_s.debugFlash) { + _wDS('tryDebug', 2); + } + } + if (p === 0) { + // if 0 (not null), probably a 404. + _s._wD(_str('swf404', _s.url)); + } + _debugTS('flashtojs', false, ': Timed out' + _overHTTP?' (Check flash security or flash blockers)':' (No plugin/missing SWF?)'); + } + // + + // give up / time-out, depending + + if (!_didInit && _okToDisable) { + if (p === null) { + // SWF failed. Maybe blocked. + if (_s.useFlashBlock || _s.flashLoadTimeout === 0) { + if (_s.useFlashBlock) { + _flashBlockHandler(); + } + _wDS('waitForever'); + } else { + // old SM2 behaviour, simply fail + _failSafely(true); + } + } else { + // flash loaded? Shouldn't be a blocking issue, then. + if (_s.flashLoadTimeout === 0) { + _wDS('waitForever'); + } else { + _failSafely(true); + } + } + } + + }, _s.flashLoadTimeout); + + }; + + _handleFocus = function() { + + function cleanup() { + _event.remove(_win, 'focus', _handleFocus); + _event.remove(_win, 'load', _handleFocus); + } + + if (_isFocused || !_tryInitOnFocus) { + cleanup(); + return true; + } + + _okToDisable = true; + _isFocused = true; + _s._wD(_smc+'handleFocus()'); + + if (_isSafari && _tryInitOnFocus) { + _event.remove(_win, 'mousemove', _handleFocus); + } + + // allow init to restart + _waitingForEI = false; + + cleanup(); + return true; + + }; + + _showSupport = function() { + + var item, tests = []; + + if (_s.useHTML5Audio && _s.hasHTML5) { + for (item in _s.audioFormats) { + if (_s.audioFormats.hasOwnProperty(item)) { + tests.push(item + ': ' + _s.html5[item] + (!_s.html5[item] && _hasFlash && _s.flash[item] ? ' (using flash)' : (_s.preferFlash && _s.flash[item] && _hasFlash ? ' (preferring flash)': (!_s.html5[item] ? ' (' + (_s.audioFormats[item].required ? 'required, ':'') + 'and no flash support)' : '')))); + } + } + _s._wD('-- SoundManager 2: HTML5 support tests ('+_s.html5Test+'): '+tests.join(', ')+' --',1); + } + + }; + + _initComplete = function(bNoDisable) { + + if (_didInit) { + return false; + } + + if (_s.html5Only) { + // all good. + _s._wD('-- SoundManager 2: loaded --'); + _didInit = true; + _initUserOnload(); + _debugTS('onload', true); + return true; + } + + var wasTimeout = (_s.useFlashBlock && _s.flashLoadTimeout && !_s.getMoviePercent()), + error; + + if (!wasTimeout) { + _didInit = true; + if (_disabled) { + error = {type: (!_hasFlash && _needsFlash ? 'NO_FLASH' : 'INIT_TIMEOUT')}; + } + } + + _s._wD('-- SoundManager 2 ' + (_disabled?'failed to load':'loaded') + ' (' + (_disabled?'security/load error':'OK') + ') --', 1); + + if (_disabled || bNoDisable) { + if (_s.useFlashBlock && _s.oMC) { + _s.oMC.className = _getSWFCSS() + ' ' + (_s.getMoviePercent() === null?_swfCSS.swfTimedout:_swfCSS.swfError); + } + _processOnEvents({type:'ontimeout', error:error}); + _debugTS('onload', false); + _catchError(error); + return false; + } else { + _debugTS('onload', true); + } + + if (_s.waitForWindowLoad && !_windowLoaded) { + _wDS('waitOnload'); + _event.add(_win, 'load', _initUserOnload); + return false; + } else { + // + if (_s.waitForWindowLoad && _windowLoaded) { + _wDS('docLoaded'); + } + // + _initUserOnload(); + } + + return true; + + }; + + _init = function() { + + _wDS('init'); + + // called after onload() + + if (_didInit) { + _wDS('didInit'); + return false; + } + + function _cleanup() { + _event.remove(_win, 'load', _s.beginDelayedInit); + } + + if (_s.html5Only) { + if (!_didInit) { + // we don't need no steenking flash! + _cleanup(); + _s.enabled = true; + _initComplete(); + } + return true; + } + + // flash path + _initMovie(); + + try { + + _wDS('flashJS'); + + // attempt to talk to Flash + _flash._externalInterfaceTest(false); + + // apply user-specified polling interval, OR, if "high performance" set, faster vs. default polling + // (determines frequency of whileloading/whileplaying callbacks, effectively driving UI framerates) + _setPolling(true, (_s.flashPollingInterval || (_s.useHighPerformance ? 10 : 50))); + + if (!_s.debugMode) { + // stop the SWF from making debug output calls to JS + _flash._disableDebug(); + } + + _s.enabled = true; + _debugTS('jstoflash', true); + + if (!_s.html5Only) { + // prevent browser from showing cached page state (or rather, restoring "suspended" page state) via back button, because flash may be dead + // http://www.webkit.org/blog/516/webkit-page-cache-ii-the-unload-event/ + _event.add(_win, 'unload', _doNothing); + } + + } catch(e) { + + _s._wD('js/flash exception: ' + e.toString()); + _debugTS('jstoflash', false); + _catchError({type:'JS_TO_FLASH_EXCEPTION', fatal:true}); + // don't disable, for reboot() + _failSafely(true); + _initComplete(); + + return false; + + } + + _initComplete(); + + // disconnect events + _cleanup(); + + return true; + + }; + + _domContentLoaded = function() { + + if (_didDCLoaded) { + return false; + } + + _didDCLoaded = true; + _initDebug(); + + /** + * Temporary feature: allow force of HTML5 via URL params: sm2-usehtml5audio=0 or 1 + * Ditto for sm2-preferFlash, too. + */ + // + (function(){ + + var a = 'sm2-usehtml5audio=', l = _wl.toLowerCase(), b = null, + a2 = 'sm2-preferflash=', b2 = null, hasCon = (typeof console !== 'undefined' && typeof console.log !== 'undefined'); + + if (l.indexOf(a) !== -1) { + b = (l.charAt(l.indexOf(a)+a.length) === '1'); + if (hasCon) { + console.log((b?'Enabling ':'Disabling ')+'useHTML5Audio via URL parameter'); + } + _s.useHTML5Audio = b; + } + + if (l.indexOf(a2) !== -1) { + b2 = (l.charAt(l.indexOf(a2)+a2.length) === '1'); + if (hasCon) { + console.log((b2?'Enabling ':'Disabling ')+'preferFlash via URL parameter'); + } + _s.preferFlash = b2; + } + + }()); + // + + if (!_hasFlash && _s.hasHTML5) { + _s._wD('SoundManager: No Flash detected'+(!_s.useHTML5Audio?', enabling HTML5.':'. Trying HTML5-only mode.')); + _s.useHTML5Audio = true; + // make sure we aren't preferring flash, either + // TODO: preferFlash should not matter if flash is not installed. Currently, stuff breaks without the below tweak. + _s.preferFlash = false; + } + + _testHTML5(); + _s.html5.usingFlash = _featureCheck(); + _needsFlash = _s.html5.usingFlash; + _showSupport(); + + if (!_hasFlash && _needsFlash) { + _s._wD('SoundManager: Fatal error: Flash is needed to play some required formats, but is not available.'); + // TODO: Fatal here vs. timeout approach, etc. + // hack: fail sooner. + _s.flashLoadTimeout = 1; + } + + if (_doc.removeEventListener) { + _doc.removeEventListener('DOMContentLoaded', _domContentLoaded, false); + } + + _initMovie(); + return true; + + }; + + _domContentLoadedIE = function() { + + if (_doc.readyState === 'complete') { + _domContentLoaded(); + _doc.detachEvent('onreadystatechange', _domContentLoadedIE); + } + + return true; + + }; + + _winOnLoad = function() { + // catch edge case of _initComplete() firing after window.load() + _windowLoaded = true; + _event.remove(_win, 'load', _winOnLoad); + }; + + // sniff up-front + _detectFlash(); + + // focus and window load, init (primarily flash-driven) + _event.add(_win, 'focus', _handleFocus); + _event.add(_win, 'load', _handleFocus); + _event.add(_win, 'load', _delayWaitForEI); + _event.add(_win, 'load', _winOnLoad); + + + if (_isSafari && _tryInitOnFocus) { + // massive Safari 3.1 focus detection hack + _event.add(_win, 'mousemove', _handleFocus); + } + + if (_doc.addEventListener) { + + _doc.addEventListener('DOMContentLoaded', _domContentLoaded, false); + + } else if (_doc.attachEvent) { + + _doc.attachEvent('onreadystatechange', _domContentLoadedIE); + + } else { + + // no add/attachevent support - safe to assume no JS -> Flash either + _debugTS('onload', false); + _catchError({type:'NO_DOM2_EVENTS', fatal:true}); + + } + + if (_doc.readyState === 'complete') { + // DOMReady has already happened. + setTimeout(_domContentLoaded,100); + } + +} // SoundManager() + +// SM2_DEFER details: http://www.schillmania.com/projects/soundmanager2/doc/getstarted/#lazy-loading + +if (typeof SM2_DEFER === 'undefined' || !SM2_DEFER) { + soundManager = new SoundManager(); +} + +/** + * SoundManager public interfaces + * ------------------------------ + */ + +window.SoundManager = SoundManager; // constructor +window.soundManager = soundManager; // public API, flash callbacks etc. + +}(window)); \ No newline at end of file diff --git a/webclient/inc/SoundManager2/swf/soundmanager2.swf b/webclient/inc/SoundManager2/swf/soundmanager2.swf new file mode 100755 index 0000000000000000000000000000000000000000..14850f2158003034fdca9fbada7c2b70438324ba GIT binary patch literal 2850 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 AdjJ3c literal 0 HcmV?d00001 diff --git a/webclient/inc/SoundManager2/swf/soundmanager2_debug.swf b/webclient/inc/SoundManager2/swf/soundmanager2_debug.swf new file mode 100755 index 0000000000000000000000000000000000000000..aa4750a6e277d1d70b561a6051d8bac20aab3fce GIT binary patch literal 3280 zcmV;>3@`ITS5pY09RL7$+O%2Qa~tIqKU%G<4Y@%Qq5yKbm`h?B%ZY&kj%|F49TQtJ zmXnkKQCHee(k822vAeS4P%Z^p=mk^S8PD+0nZEQ7Xotu4tuviY`zLg!uYK%aKzh#k z_9i>QywnfUcRAnr&gFM5pL_@4ECBo-;B*G&lzaGhU;r0jHG?0o9vQ?>9Ie)Bt48Ct z(Gq^C>h>IS#c*)qjlsPm-xP`bv%hJ-h5oHt%XaIA4I9GqE!Tl-W2IMKGD>6TFI>1V zHg-N*uNgj^ovDtEF4%_O)?Xfm=b*af2ciSMX!JZQ*aE}0-OYKoV^|L8BYMMgeSgH{ zxh@=|ZVMCYhA&>L&bdzDxwb7l@Ke%+yOsJa(FmZ#TRx<}b-cu4acmzcUFY&<9*ODdaZV_U<9atts@uxc!YOB&2!yAGKOkEXZ(g? zixt-t;J8&9quN%>f%Rn|t;C&RML4}aBkQ22_m;?Lkv$qcKX5y=rH6b_c~BClyQNIlz5*#Xv(cER;JMgYEbS3nbG zf*H@k!<&{Fw4p65vz>gCTQmbOdVyOd zjA_{yFq%N(A-6F0FA7znFS>Ngkpat}(E>Sk}Y%92^zg8`kO5ll(8wjvn z)N0*BtD*5G>fY2ueTp48u2(}G>LEj2c%JKx>x*un55dqmUEJv+c!a4BNoGU3>*+&j zjzcA4Ib)hufS5OIJ;Ju$6<){kDH43$5y&HO^}5hoo`JfUe5@*jK4Iu>Pc$co+Ck7A zA06G?+$?nrjD(IbEu++MJEMND+jYHQ)N~s?bar6SRinZgWlwc&V@r6WZDDsu(RySI zzt)jBI{!k63_LW&l!a^ zZV8@ADS^Y45xw4(*vWO&)hC$ zfM(BW$RH$$*9EPGmNIp-#~_l{wylmO0a^lQnYsCa;bCG4P|6yMZcDpK+q#Iltp!w- zmsw6uVI!ov&Q%O)YdxuTO{POhCO=X9Yp#uf0bK**3298sTb{cWZg=qlfm-tYQkEK1 zO3e*UjPzAYI7ypuJp-;-q;Oc^&S3@V__{>^-9U#U=~6VFXPcymp34OWPXtMcOd@<@k;YnxbVV z=v&ot6S$v)Pcxtth;jf&SsJOzGI1FIo@8k}3_AyKn5XVa%QIuGOrHnV3X+@3iw`Xh z()6Hqh%Y&;9pvRBixWF}(2nLwlvy4>9F4P}J)$u6u^jOC@mz+#PlWSF!|#*yjYkWq zqo?H26WU{Z(c|Tv9I%v2gEW7lNV`RCu#(J9Yu~F#qbS4pfm*8tWXnu7eH!Pv!p;Dw zs`jL&6c_2>5PzyTK{|!#o+7#drW??n&Ov48AddKEHK#qpwt04q?fMwsdPdE$3}?3w zQfrzW2E~UaQ3z-E{iQ|;)^iK*+ZC+kII?0zl zT0F)f`~*;RqUSCx$%Tiag@q+H@({=X%W&8(s#!+eWyV{~FapNXbnH^4h?nvzhRJJd z2~@A}(d+}E8cIiZA7R0l<9iShw^*E&`-UBpot-3HYUM;YoK=!N)=f&Xx=+yHbqj0_ z?RAy&id6D-rd?a>vjDD~ltODDGf`$xM$K1>^hGW+#U8E3ndKy_x1N>_GaC#t*;Pq) zU>N?Dp@;yPh%gZ+lWcmTrY9IDNGlUivYKZ(kFxyN2rnAxc#x5jUzG%CpS-GFCQTX) zvWJWDAvIqvOP-Ny2ej*JQmSO-hig(al_qbLmyy!}*d;0N-H0PPtS5R{Pk3@1ICm;Y z_$GVgt+GU91>|vDMT5PiDwQ1#L8fL^MLW#GzP%_ZZk8DyHTJXmZHQBI^ECNU7zE+n zx%loI;oVpI?!G}8Tcvb<-+PVy@%70!?CkpXu_)!&tGz~py4Zg}jJT^3w;8l%Z08Kx zIW@V?5tbGs^PSa?Sce{J#aS3+7S0VfH7O90Rg<#|*U~IktMG{2E61A(ZK4Wa-;-F%-e3IIcgRT(7o#I(YAZ}d zCg}F|VR9Wc3iMhqAc>iqMLuuE0YBI8(zG#|oWE`*x$M5O$ZKzJKLwEGT29wy9el}G znoaC-LACr<*<$}W5k^(QU}LxYmpE3ENyQBMo~z(LYc6e-#zLUb?bHjaqwB8fattIAV(% zgv3DpZpU+Rz(S0um4mucksYi@UCVLl8reW8vSyQOk_wLUp!m5=B)-bx_wBPdo7>Oy z;ZnX-)!mQCKP8cHXFBfdB7xZ)Cm^Nm(R5oXBT=E`dj#?($sBgoTWd0JYd2M?UrxK5 zgK*@2o*#*~}Obl}}?%tK*9+~@(eTJr^IfzPcHhdiQwb1D* z7}s8PHLi+kT%NfEUxcLis%5@~5qTO&b=F#yX;&WmF~cTDS^E$7(sD#Hj!QSaw_PCA zsF9m6pxo{ZHkM_GUv!1UN3xHWm^_y(M|y{wX;VC--qslknV$h=x&E}xA&p@Fr@o%AG(i1x{vmu`#7ZgcptjikZyJ#x@t&Q-G^>7 zq}$ww?%j~?-EW~o>1>;vOZIttF6o|m4W{Eu3M+_0!!%C+ O2LJ&7{{sO3qBe>|LR~Ka literal 0 HcmV?d00001 diff --git a/webclient/inc/SoundManager2/swf/soundmanager2_flash9.swf b/webclient/inc/SoundManager2/swf/soundmanager2_flash9.swf new file mode 100755 index 0000000000000000000000000000000000000000..2705986ac107a092aad7fa094d19c15c38278460 GIT binary patch literal 8682 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 I0++K_Y73U(2><{9 literal 0 HcmV?d00001 diff --git a/webclient/inc/SoundManager2/swf/soundmanager2_flash9_debug.swf b/webclient/inc/SoundManager2/swf/soundmanager2_flash9_debug.swf new file mode 100755 index 0000000000000000000000000000000000000000..243d6495b488ace9e5f2f076edf88f561650731b GIT binary patch literal 16806 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 ZjVD?oaIiY)Vkm6eEAtWi{{cd~Z1|p1w|W2o literal 0 HcmV?d00001 diff --git a/webclient/inc/SoundManager2/swf/soundmanager2_flash_xdomain.zip b/webclient/inc/SoundManager2/swf/soundmanager2_flash_xdomain.zip new file mode 100755 index 0000000000000000000000000000000000000000..29737aaf13e7166d6ee83816b332d1acae2b88ba GIT binary patch literal 32404 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

)s`;JPK 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

').addClass("vex-canvas"); + $(sel).append(this.canvas); + this.renderer = new Vex.Flow.Renderer(this.canvas[0], + Vex.Flow.Renderer.Backends.RAPHAEL); + } + + this.ctx_sel = $(sel).find(".vex-canvas"); + this.renderer.resize(this.width, this.height); + this.ctx = this.renderer.getContext(); + this.ctx.setBackgroundFillStyle(this.ctx_sel.css("background-color")); + this.ctx.scale(this.scale, this.scale); + + // Grab editor properties + this.editor = $(sel).attr("editor") || ""; + this.show_errors = $(sel).attr("show-errors") || ""; + this.editor_width= $(sel).attr("editor_width") || this.width; + this.editor_height= $(sel).attr("editor_height") || 200; + + var that = this; + if (this.editor == "true") { + this.text_area = $('').addClass("editor"). + val(this.code); + this.editor_error = $('
').addClass("editor-error"); + $(sel).append($('

')).append(this.editor_error); + $(sel).append($('

')).append(this.text_area); + this.text_area.width(this.editor_width); + this.text_area.height(this.editor_height); + this.text_area.keyup(function() { + if (that.timeoutID) window.clearTimeout(that.timeoutID); + that.timeoutID = + window.setTimeout(function() { + // Draw only if code changed + if (that.code != that.text_area.val()) { + that.code = that.text_area.val(); + that.redraw() + } + }, 250); + }); + } if (this.show_errors == "true") { + this.editor_error = $('

').addClass("editor-error"); + $(sel).append($('

')).append(this.editor_error); + } + + // Initialize parser. + this.artist = new Vex.Flow.Artist(10, 0, this.width, {scale: this.scale}); + this.parser = new Vex.Flow.VexTab(this.artist); + + if (Vex.Flow.Player) { + opts = {}; + if (options) opts.soundfont_url = options.soundfont_url; + this.player = new Vex.Flow.Player(this.artist, opts); + } + + this.redraw(); +} + +Vex.Flow.TabDiv.prototype.redraw = function() { + var that = this; + Vex.BM("Total render time: ", function() { + that.parse(); that.draw();}); + + return this; +} + +Vex.Flow.TabDiv.prototype.drawInternal = function() { + if (!this.parser.isValid()) return this; + return this.artist.draw(this.renderer); +} + +Vex.Flow.TabDiv.prototype.parseInternal = function() { + try { + this.artist.reset(); + this.parser.reset(); + this.parser.parse(this.code); + this.editor_error.empty(); + } catch (e) { + if (this.editor_error) { + this.editor_error.empty(); + this.editor_error.append( + $('

').addClass("text").html( + "Sucky VexTab: " + e.message)); + } + } + return this; +} + +Vex.Flow.TabDiv.prototype.parse = function() { + var that = this; + Vex.BM("Parse time: ", function() { that.parseInternal(); }); + return this; +} + +Vex.Flow.TabDiv.prototype.draw = function() { + var that = this; + Vex.BM("Draw time: ", function() { that.drawInternal(); }); + return this; +} + +// Automatic initialization. +Vex.Flow.TabDiv.start = function() { + $(Vex.Flow.TabDiv.SEL).each(function(index) { + new Vex.Flow.TabDiv(this); + }); +} + +$(function() {if (Vex.Flow.TabDiv.SEL) { Vex.Flow.TabDiv.start() }}); +// Generated by CoffeeScript 1.8.0 +(function() { + var __slice = [].slice, + __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + + Vex.Flow.Artist = (function() { + var L, formatAndRender, getFingering, getScoreArticulationParts, getStrokeParts, makeBend, makeDuration, parseBool; + + Artist.DEBUG = false; + + L = function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + if (Vex.Flow.Artist.DEBUG) { + return typeof console !== "undefined" && console !== null ? console.log.apply(console, ["(Vex.Flow.Artist)"].concat(__slice.call(args))) : void 0; + } + }; + + Artist.NOLOGO = false; + + function Artist(x, y, width, options) { + this.x = x; + this.y = y; + this.width = width; + this.options = { + font_face: "Arial", + font_size: 10, + font_style: null, + bottom_spacing: 20 + (Vex.Flow.Artist.NOLOGO ? 0 : 10), + tab_stave_lower_spacing: 10, + note_stave_lower_spacing: 0, + scale: 1.0 + }; + if (options != null) { + _.extend(this.options, options); + } + this.reset(); + } + + Artist.prototype.reset = function() { + this.tuning = new Vex.Flow.Tuning(); + this.key_manager = new Vex.Flow.KeyManager("C"); + this.music_api = new Vex.Flow.Music(); + this.customizations = { + "font-size": this.options.font_size, + "font-face": this.options.font_face, + "font-style": this.options.font_style, + "annotation-position": "bottom", + "scale": this.options.scale, + "width": this.width, + "stave-distance": 0, + "space": 0, + "player": "false", + "tempo": 120, + "instrument": "acoustic_grand_piano", + "accidentals": "standard", + "tab-stems": "false", + "tab-stem-direction": "up", + "beam-rests": "true", + "beam-stemlets": "true", + "beam-middle-only": "false", + "connector-space": 0 + }; + this.staves = []; + this.tab_articulations = []; + this.stave_articulations = []; + this.player_voices = []; + this.last_y = this.y; + this.current_duration = "q"; + this.current_clef = "treble"; + this.current_bends = {}; + this.current_octave_shift = 0; + this.bend_start_index = null; + this.bend_start_strings = null; + this.rendered = false; + return this.renderer_context = null; + }; + + Artist.prototype.attachPlayer = function(player) { + return this.player = player; + }; + + Artist.prototype.setOptions = function(options) { + var k, v, valid_options; + L("setOptions: ", options); + valid_options = _.keys(this.customizations); + for (k in options) { + v = options[k]; + if (__indexOf.call(valid_options, k) >= 0) { + this.customizations[k] = v; + } else { + throw new Vex.RERR("ArtistError", "Invalid option '" + k + "'"); + } + } + this.last_y += parseInt(this.customizations.space, 10); + if (this.customizations.player === "true") { + return this.last_y += 15; + } + }; + + Artist.prototype.getPlayerData = function() { + return { + voices: this.player_voices, + context: this.renderer_context, + scale: this.customizations.scale + }; + }; + + parseBool = function(str) { + return str === "true"; + }; + + formatAndRender = function(ctx, tab, score, text_notes, customizations, options) { + var align_rests, beam_config, beams, format_stave, format_voices, formatter, i, multi_voice, notes, score_stave, score_voices, stem_direction, tab_stave, tab_voices, text_stave, text_voices, voice, _i, _j, _k, _len, _len1, _len2, _ref, _ref1; + if (tab != null) { + tab_stave = tab.stave; + } + if (score != null) { + score_stave = score.stave; + } + tab_voices = []; + score_voices = []; + text_voices = []; + beams = []; + format_stave = null; + text_stave = null; + beam_config = { + beam_rests: parseBool(customizations["beam-rests"]), + show_stemlets: parseBool(customizations["beam-stemlets"]), + beam_middle_only: parseBool(customizations["beam-middle-only"]), + groups: options.beam_groups + }; + if (tab != null) { + multi_voice = tab.voices.length > 1 ? true : false; + _ref = tab.voices; + for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { + notes = _ref[i]; + if (_.isEmpty(notes)) { + continue; + } + _.each(notes, function(note) { + return note.setStave(tab_stave); + }); + voice = new Vex.Flow.Voice(Vex.Flow.TIME4_4).setMode(Vex.Flow.Voice.Mode.SOFT); + voice.addTickables(notes); + tab_voices.push(voice); + if (customizations["tab-stems"] === "true") { + if (multi_voice) { + beam_config.stem_direction = i === 0 ? 1 : -1; + } else { + beam_config.stem_direction = customizations["tab-stem-direction"] === "down" ? -1 : 1; + } + beam_config.beam_rests = false; + beams = beams.concat(Vex.Flow.Beam.generateBeams(voice.getTickables(), beam_config)); + } + } + format_stave = tab_stave; + text_stave = tab_stave; + } + beam_config.beam_rests = parseBool(customizations["beam-rests"]); + if (score != null) { + multi_voice = score.voices.length > 1 ? true : false; + _ref1 = score.voices; + for (i = _j = 0, _len1 = _ref1.length; _j < _len1; i = ++_j) { + notes = _ref1[i]; + if (_.isEmpty(notes)) { + continue; + } + stem_direction = i === 0 ? 1 : -1; + _.each(notes, function(note) { + return note.setStave(score_stave); + }); + voice = new Vex.Flow.Voice(Vex.Flow.TIME4_4).setMode(Vex.Flow.Voice.Mode.SOFT); + voice.addTickables(notes); + score_voices.push(voice); + if (multi_voice) { + beam_config.stem_direction = stem_direction; + beams = beams.concat(Vex.Flow.Beam.generateBeams(notes, beam_config)); + } else { + beam_config.stem_direction = null; + beams = beams.concat(Vex.Flow.Beam.generateBeams(notes, beam_config)); + } + } + format_stave = score_stave; + text_stave = score_stave; + } + for (_k = 0, _len2 = text_notes.length; _k < _len2; _k++) { + notes = text_notes[_k]; + if (_.isEmpty(notes)) { + continue; + } + _.each(notes, function(voice) { + return voice.setStave(text_stave); + }); + voice = new Vex.Flow.Voice(Vex.Flow.TIME4_4).setMode(Vex.Flow.Voice.Mode.SOFT); + voice.addTickables(notes); + text_voices.push(voice); + } + if (format_stave != null) { + format_voices = []; + formatter = new Vex.Flow.Formatter(); + align_rests = false; + if (tab != null) { + if (!_.isEmpty(tab_voices)) { + formatter.joinVoices(tab_voices); + } + format_voices = tab_voices; + } + if (score != null) { + if (!_.isEmpty(score_voices)) { + formatter.joinVoices(score_voices); + } + format_voices = format_voices.concat(score_voices); + if (score_voices.length > 1) { + align_rests = true; + } + } + if (!_.isEmpty(text_notes) && !_.isEmpty(text_voices)) { + formatter.joinVoices(text_voices); + format_voices = format_voices.concat(text_voices); + } + if (!_.isEmpty(format_voices)) { + formatter.formatToStave(format_voices, format_stave, { + align_rests: align_rests + }); + } + if (tab != null) { + _.each(tab_voices, function(voice) { + return voice.draw(ctx, tab_stave); + }); + } + if (score != null) { + _.each(score_voices, function(voice) { + return voice.draw(ctx, score_stave); + }); + } + _.each(beams, function(beam) { + return beam.setContext(ctx).draw(); + }); + if (!_.isEmpty(text_notes)) { + _.each(text_voices, function(voice) { + return voice.draw(ctx, text_stave); + }); + } + if ((tab != null) && (score != null)) { + (new Vex.Flow.StaveConnector(score.stave, tab.stave)).setContext(ctx).draw(); + } + if (score != null) { + return score_voices; + } else { + return tab_voices; + } + } + }; + + Artist.prototype.render = function(renderer) { + var LOGO, articulation, ctx, setBar, stave, voices, width, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2; + L("Render: ", this.options); + this.closeBends(); + renderer.resize(this.customizations.width * this.customizations.scale, (this.last_y + this.options.bottom_spacing) * this.customizations.scale); + ctx = renderer.getContext(); + ctx.scale(this.customizations.scale, this.customizations.scale); + ctx.clear(); + ctx.setFont(this.options.font_face, this.options.font_size, ""); + this.renderer_context = ctx; + setBar = function(stave, notes) { + var last_note; + last_note = _.last(notes); + if (last_note instanceof Vex.Flow.BarNote) { + notes.pop(); + return stave.setEndBarType(last_note.getType()); + } + }; + _ref = this.staves; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + stave = _ref[_i]; + L("Rendering staves."); + if (stave.tab != null) { + setBar(stave.tab, stave.tab_notes); + } + if (stave.note != null) { + setBar(stave.note, stave.note_notes); + } + if (stave.tab != null) { + stave.tab.setContext(ctx).draw(); + } + if (stave.note != null) { + stave.note.setContext(ctx).draw(); + } + stave.tab_voices.push(stave.tab_notes); + stave.note_voices.push(stave.note_notes); + voices = formatAndRender(ctx, stave.tab != null ? { + stave: stave.tab, + voices: stave.tab_voices + } : null, stave.note != null ? { + stave: stave.note, + voices: stave.note_voices + } : null, stave.text_voices, this.customizations, { + beam_groups: stave.beam_groups + }); + this.player_voices.push(voices); + } + L("Rendering tab articulations."); + _ref1 = this.tab_articulations; + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + articulation = _ref1[_j]; + articulation.setContext(ctx).draw(); + } + L("Rendering note articulations."); + _ref2 = this.stave_articulations; + for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { + articulation = _ref2[_k]; + articulation.setContext(ctx).draw(); + } + if (this.player != null) { + if (this.customizations.player === "true") { + this.player.setTempo(parseInt(this.customizations.tempo, 10)); + this.player.setInstrument(this.customizations.instrument); + this.player.render(); + } else { + this.player.removeControls(); + } + } + this.rendered = true; + if (!Vex.Flow.Artist.NOLOGO) { + LOGO = "vexflow.com"; + width = ctx.measureText(LOGO).width; + ctx.save(); + ctx.setFont("Times", 10, "italic"); + ctx.fillText(LOGO, (this.customizations.width - width) / 2, this.last_y + 25); + return ctx.restore(); + } + }; + + Artist.prototype.isRendered = function() { + return this.rendered; + }; + + Artist.prototype.draw = function(renderer) { + return this.render(renderer); + }; + + Artist.prototype.getNoteForFret = function(fret, string) { + var accidental, new_note, new_octave, new_root, old_root, selected_note, spec, spec_props; + spec = this.tuning.getNoteForFret(fret, string); + spec_props = Vex.Flow.keyProperties(spec); + selected_note = this.key_manager.selectNote(spec_props.key); + accidental = null; + switch (this.customizations.accidentals) { + case "standard": + if (selected_note.change) { + accidental = selected_note.accidental != null ? selected_note.accidental : "n"; + } + break; + case "cautionary": + if (selected_note.change) { + accidental = selected_note.accidental != null ? selected_note.accidental : "n"; + } else { + accidental = selected_note.accidental != null ? selected_note.accidental + "_c" : void 0; + } + break; + default: + throw new Vex.RERR("ArtistError", "Invalid value for option 'accidentals': " + this.customizations.accidentals); + } + new_note = selected_note.note; + new_octave = spec_props.octave; + old_root = this.music_api.getNoteParts(spec_props.key).root; + new_root = this.music_api.getNoteParts(selected_note.note).root; + if (new_root === "b" && old_root === "c") { + new_octave--; + } else if (new_root === "c" && old_root === "b") { + new_octave++; + } + return [new_note, new_octave, accidental]; + }; + + Artist.prototype.getNoteForABC = function(abc, string) { + var accidental, key, octave; + key = abc.key; + octave = string; + accidental = abc.accidental; + if (abc.accidental_type != null) { + accidental += "_" + abc.accidental_type; + } + return [key, octave, accidental]; + }; + + Artist.prototype.addStaveNote = function(note_params) { + var acc, index, new_accidental, params, parts, stave_note, stave_notes, _i, _len, _ref; + params = { + is_rest: false, + play_note: null + }; + _.extend(params, note_params); + stave_notes = _.last(this.staves).note_notes; + stave_note = new Vex.Flow.StaveNote({ + keys: params.spec, + duration: this.current_duration + (params.is_rest ? "r" : ""), + clef: params.is_rest ? "treble" : this.current_clef, + auto_stem: params.is_rest ? false : true + }); + _ref = params.accidentals; + for (index = _i = 0, _len = _ref.length; _i < _len; index = ++_i) { + acc = _ref[index]; + if (acc != null) { + parts = acc.split("_"); + new_accidental = new Vex.Flow.Accidental(parts[0]); + if (parts.length > 1 && parts[1] === "c") { + new_accidental.setAsCautionary(); + } + stave_note.addAccidental(index, new_accidental); + } + } + if (this.current_duration[this.current_duration.length - 1] === "d") { + stave_note.addDotToAll(); + } + if (params.play_note != null) { + stave_note.setPlayNote(params.play_note); + } + return stave_notes.push(stave_note); + }; + + Artist.prototype.addTabNote = function(spec, play_note) { + var new_tab_note, tab_notes; + if (play_note == null) { + play_note = null; + } + tab_notes = _.last(this.staves).tab_notes; + new_tab_note = new Vex.Flow.TabNote({ + positions: spec, + duration: this.current_duration + }, this.customizations["tab-stems"] === "true"); + if (play_note != null) { + new_tab_note.setPlayNote(play_note); + } + tab_notes.push(new_tab_note); + if (this.current_duration[this.current_duration.length - 1] === "d") { + return new_tab_note.addDot(); + } + }; + + makeDuration = function(time, dot) { + return time + (dot ? "d" : ""); + }; + + Artist.prototype.setDuration = function(time, dot) { + var t; + if (dot == null) { + dot = false; + } + t = time.split(/\s+/); + L("setDuration: ", t[0], dot); + return this.current_duration = makeDuration(t[0], dot); + }; + + Artist.prototype.addBar = function(type) { + var TYPE, bar_note, stave; + L("addBar: ", type); + this.closeBends(); + this.key_manager.reset(); + stave = _.last(this.staves); + TYPE = Vex.Flow.Barline.type; + type = (function() { + switch (type) { + case "single": + return TYPE.SINGLE; + case "double": + return TYPE.DOUBLE; + case "end": + return TYPE.END; + case "repeat-begin": + return TYPE.REPEAT_BEGIN; + case "repeat-end": + return TYPE.REPEAT_END; + case "repeat-both": + return TYPE.REPEAT_BOTH; + default: + return TYPE.SINGLE; + } + })(); + bar_note = new Vex.Flow.BarNote().setType(type); + stave.tab_notes.push(bar_note); + if (stave.note != null) { + return stave.note_notes.push(bar_note); + } + }; + + makeBend = function(from_fret, to_fret) { + var direction, text; + direction = Vex.Flow.Bend.UP; + text = ""; + if (parseInt(from_fret, 10) > parseInt(to_fret, 10)) { + direction = Vex.Flow.Bend.DOWN; + } else { + text = (function() { + switch (Math.abs(to_fret - from_fret)) { + case 1: + return "1/2"; + case 2: + return "Full"; + case 3: + return "1 1/2"; + default: + return "Bend to " + to_fret; + } + })(); + } + return { + type: direction, + text: text + }; + }; + + Artist.prototype.openBends = function(first_note, last_note, first_indices, last_indices) { + var first_frets, from_fret, i, index, last_frets, last_index, start_indices, start_note, tab_notes, to_fret, _base, _i, _len, _results; + L("openBends", first_note, last_note, first_indices, last_indices); + tab_notes = _.last(this.staves).tab_notes; + start_note = first_note; + start_indices = first_indices; + if (_.isEmpty(this.current_bends)) { + this.bend_start_index = tab_notes.length - 2; + this.bend_start_strings = first_indices; + } else { + start_note = tab_notes[this.bend_start_index]; + start_indices = this.bend_start_strings; + } + first_frets = start_note.getPositions(); + last_frets = last_note.getPositions(); + _results = []; + for (i = _i = 0, _len = start_indices.length; _i < _len; i = ++_i) { + index = start_indices[i]; + last_index = last_indices[i]; + from_fret = first_note.getPositions()[first_indices[i]]; + to_fret = last_frets[last_index]; + if ((_base = this.current_bends)[index] == null) { + _base[index] = []; + } + _results.push(this.current_bends[index].push(makeBend(from_fret.fret, to_fret.fret))); + } + return _results; + }; + + Artist.prototype.closeBends = function(offset) { + var bend, k, phrase, tab_note, tab_notes, v, _i, _j, _len, _len1, _ref, _ref1; + if (offset == null) { + offset = 1; + } + if (this.bend_start_index == null) { + return; + } + L("closeBends(" + offset + ")"); + tab_notes = _.last(this.staves).tab_notes; + _ref = this.current_bends; + for (k in _ref) { + v = _ref[k]; + phrase = []; + for (_i = 0, _len = v.length; _i < _len; _i++) { + bend = v[_i]; + phrase.push(bend); + } + tab_notes[this.bend_start_index].addModifier(new Vex.Flow.Bend(null, null, phrase), k); + } + _ref1 = tab_notes.slice(this.bend_start_index + 1, +((tab_notes.length - 2) + offset) + 1 || 9e9); + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + tab_note = _ref1[_j]; + tab_note.setGhost(true); + } + this.current_bends = {}; + return this.bend_start_index = null; + }; + + Artist.prototype.makeTuplets = function(tuplets, notes) { + var modifier, stave_notes, tab_modifier, tab_notes; + L("makeTuplets", tuplets, notes); + if (notes == null) { + notes = tuplets; + } + if (!_.last(this.staves).note) { + return; + } + stave_notes = _.last(this.staves).note_notes; + tab_notes = _.last(this.staves).tab_notes; + if (stave_notes.length < notes) { + throw new Vex.RERR("ArtistError", "Not enough notes for tuplet"); + } + modifier = new Vex.Flow.Tuplet(stave_notes.slice(stave_notes.length - notes), { + num_notes: tuplets + }); + this.stave_articulations.push(modifier); + tab_modifier = new Vex.Flow.Tuplet(tab_notes.slice(tab_notes.length - notes), { + num_notes: tuplets + }); + if (this.customizations["tab-stems"] === "true") { + return this.tab_articulations.push(tab_modifier); + } + }; + + getFingering = function(text) { + return text.match(/^\.fingering\/([^.]+)\./); + }; + + Artist.prototype.makeFingering = function(text) { + var POS, badFingering, finger, fingering, fingers, modifier, note_number, number, p, parts, pieces, position, _i, _len; + parts = getFingering(text); + POS = Vex.Flow.Modifier.Position; + fingers = []; + fingering = []; + if (parts != null) { + fingers = (function() { + var _i, _len, _ref, _results; + _ref = parts[1].split(/-/); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + p = _ref[_i]; + _results.push(p.trim()); + } + return _results; + })(); + } else { + return null; + } + badFingering = function() { + return new Vex.RERR("ArtistError", "Bad fingering: " + parts[1]); + }; + for (_i = 0, _len = fingers.length; _i < _len; _i++) { + finger = fingers[_i]; + pieces = finger.match(/(\d+):([ablr]):([fs]):([^-.]+)/); + if (pieces == null) { + throw badFingering(); + } + note_number = parseInt(pieces[1], 10) - 1; + position = POS.RIGHT; + switch (pieces[2]) { + case "l": + position = POS.LEFT; + break; + case "r": + position = POS.RIGHT; + break; + case "a": + position = POS.ABOVE; + break; + case "b": + position = POS.BELOW; + } + modifier = null; + number = pieces[4]; + switch (pieces[3]) { + case "s": + modifier = new Vex.Flow.StringNumber(number).setPosition(position); + break; + case "f": + modifier = new Vex.Flow.FretHandFinger(number).setPosition(position); + } + fingering.push({ + num: note_number, + modifier: modifier + }); + } + return fingering; + }; + + getStrokeParts = function(text) { + return text.match(/^\.stroke\/([^.]+)\./); + }; + + Artist.prototype.makeStroke = function(text) { + var TYPE, parts, type; + parts = getStrokeParts(text); + TYPE = Vex.Flow.Stroke.Type; + type = null; + if (parts != null) { + switch (parts[1]) { + case "bu": + type = TYPE.BRUSH_UP; + break; + case "bd": + type = TYPE.BRUSH_DOWN; + break; + case "ru": + type = TYPE.ROLL_UP; + break; + case "rd": + type = TYPE.ROLL_DOWN; + break; + case "qu": + type = TYPE.RASQUEDO_UP; + break; + case "qd": + type = TYPE.RASQUEDO_DOWN; + break; + default: + throw new Vex.RERR("ArtistError", "Invalid stroke type: " + parts[1]); + } + return new Vex.Flow.Stroke(type); + } else { + return null; + } + }; + + getScoreArticulationParts = function(text) { + return text.match(/^\.(a[^\/]*)\/(t|b)[^.]*\./); + }; + + Artist.prototype.makeScoreArticulation = function(text) { + var POSTYPE, parts, pos, position, type; + parts = getScoreArticulationParts(text); + if (parts != null) { + type = parts[1]; + position = parts[2]; + POSTYPE = Vex.Flow.Modifier.Position; + pos = position === "t" ? POSTYPE.ABOVE : POSTYPE.BELOW; + return new Vex.Flow.Articulation(type).setPosition(pos); + } else { + return null; + } + }; + + Artist.prototype.makeAnnotation = function(text) { + var VJUST, aposition, default_vjust, font_face, font_size, font_style, just, makeIt, parts; + font_face = this.customizations["font-face"]; + font_size = this.customizations["font-size"]; + font_style = this.customizations["font-style"]; + aposition = this.customizations["annotation-position"]; + VJUST = Vex.Flow.Annotation.VerticalJustify; + default_vjust = aposition === "top" ? VJUST.TOP : VJUST.BOTTOM; + makeIt = function(text, just) { + if (just == null) { + just = default_vjust; + } + return new Vex.Flow.Annotation(text).setFont(font_face, font_size, font_style).setVerticalJustification(just); + }; + parts = text.match(/^\.([^-]*)-([^-]*)-([^.]*)\.(.*)/); + if (parts != null) { + font_face = parts[1]; + font_size = parts[2]; + font_style = parts[3]; + text = parts[4]; + if (text) { + return makeIt(text); + } else { + return null; + } + } + parts = text.match(/^\.([^.]*)\.(.*)/); + if (parts != null) { + just = default_vjust; + text = parts[2]; + switch (parts[1]) { + case "big": + font_style = "bold"; + font_size = "14"; + break; + case "italic": + case "italics": + font_face = "Times"; + font_style = "italic"; + break; + case "medium": + font_size = "12"; + break; + case "top": + just = VJUST.TOP; + this.customizations["annotation-position"] = "top"; + break; + case "bottom": + just = VJUST.BOTTOM; + this.customizations["annotation-position"] = "bottom"; + } + if (text) { + return makeIt(text, just); + } else { + return null; + } + } + return makeIt(text); + }; + + Artist.prototype.addAnnotations = function(annotations) { + var annotation, e, fingering, fingerings, i, note, score_articulation, stave, stave_notes, stroke, tab_note, tab_notes, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _results; + stave = _.last(this.staves); + stave_notes = stave.note_notes; + tab_notes = stave.tab_notes; + if (annotations.length > tab_notes.length) { + throw new Vex.RERR("ArtistError", "More annotations than note elements"); + } + if (stave.tab) { + _ref = tab_notes.slice(tab_notes.length - annotations.length); + for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { + tab_note = _ref[i]; + if (getScoreArticulationParts(annotations[i])) { + score_articulation = this.makeScoreArticulation(annotations[i]); + tab_note.addModifier(score_articulation, 0); + } else if (getStrokeParts(annotations[i])) { + stroke = this.makeStroke(annotations[i]); + tab_note.addModifier(stroke, 0); + } else { + annotation = this.makeAnnotation(annotations[i]); + if (annotation) { + tab_note.addModifier(this.makeAnnotation(annotations[i]), 0); + } + } + } + } else { + _ref1 = stave_notes.slice(stave_notes.length - annotations.length); + for (i = _j = 0, _len1 = _ref1.length; _j < _len1; i = ++_j) { + note = _ref1[i]; + if (!getScoreArticulationParts(annotations[i])) { + annotation = this.makeAnnotation(annotations[i]); + if (annotation) { + note.addAnnotation(0, this.makeAnnotation(annotations[i])); + } + } + } + } + if (stave.note) { + _ref2 = stave_notes.slice(stave_notes.length - annotations.length); + _results = []; + for (i = _k = 0, _len2 = _ref2.length; _k < _len2; i = ++_k) { + note = _ref2[i]; + score_articulation = this.makeScoreArticulation(annotations[i]); + if (score_articulation != null) { + note.addArticulation(0, score_articulation); + } + stroke = this.makeStroke(annotations[i]); + if (stroke != null) { + note.addStroke(0, stroke); + } + fingerings = this.makeFingering(annotations[i]); + if (fingerings != null) { + try { + _results.push((function() { + var _l, _len3, _results1; + _results1 = []; + for (_l = 0, _len3 = fingerings.length; _l < _len3; _l++) { + fingering = fingerings[_l]; + _results1.push(note.addModifier(fingering.num, fingering.modifier)); + } + return _results1; + })()); + } catch (_error) { + e = _error; + throw new Vex.RERR("ArtistError", "Bad note number in fingering: " + annotations[i]); + } + } else { + _results.push(void 0); + } + } + return _results; + } + }; + + Artist.prototype.addTabArticulation = function(type, first_note, last_note, first_indices, last_indices) { + var articulation; + L("addTabArticulations: ", type, first_note, last_note, first_indices, last_indices); + if (type === "t") { + last_note.addModifier(new Vex.Flow.Annotation("T").setVerticalJustification(Vex.Flow.Annotation.VerticalJustify.BOTTOM)); + } + if (_.isEmpty(first_indices) && _.isEmpty(last_indices)) { + return; + } + articulation = null; + if (type === "s") { + articulation = new Vex.Flow.TabSlide({ + first_note: first_note, + last_note: last_note, + first_indices: first_indices, + last_indices: last_indices + }); + } + if (type === "h" || type === "p") { + articulation = new Vex.Flow.TabTie({ + first_note: first_note, + last_note: last_note, + first_indices: first_indices, + last_indices: last_indices + }, type.toUpperCase()); + } + if (type === "T" || type === "t") { + articulation = new Vex.Flow.TabTie({ + first_note: first_note, + last_note: last_note, + first_indices: first_indices, + last_indices: last_indices + }, " "); + } + if (type === "b") { + this.openBends(first_note, last_note, first_indices, last_indices); + } + if (articulation != null) { + return this.tab_articulations.push(articulation); + } + }; + + Artist.prototype.addStaveArticulation = function(type, first_note, last_note, first_indices, last_indices) { + var articulation; + L("addStaveArticulations: ", type, first_note, last_note, first_indices, last_indices); + articulation = null; + if (type === "b" || type === "s" || type === "h" || type === "p" || type === "t" || type === "T") { + articulation = new Vex.Flow.StaveTie({ + first_note: first_note, + last_note: last_note, + first_indices: first_indices, + last_indices: last_indices + }); + } + if (articulation != null) { + return this.stave_articulations.push(articulation); + } + }; + + Artist.prototype.getPreviousNoteIndex = function() { + var index, note, tab_notes; + tab_notes = _.last(this.staves).tab_notes; + index = 2; + while (index <= tab_notes.length) { + note = tab_notes[tab_notes.length - index]; + if (note instanceof Vex.Flow.TabNote) { + return tab_notes.length - index; + } + index++; + } + return -1; + }; + + Artist.prototype.addDecorator = function(decorator) { + var modifier, score_modifier, score_notes, stave, tab_notes, _ref; + L("addDecorator: ", decorator); + if (decorator == null) { + return; + } + stave = _.last(this.staves); + tab_notes = stave.tab_notes; + score_notes = stave.note_notes; + modifier = null; + score_modifier = null; + if (decorator === "v") { + modifier = new Vex.Flow.Vibrato(); + } + if (decorator === "V") { + modifier = new Vex.Flow.Vibrato().setHarsh(true); + } + if (decorator === "u") { + modifier = new Vex.Flow.Articulation("a|").setPosition(Vex.Flow.Modifier.Position.BOTTOM); + score_modifier = new Vex.Flow.Articulation("a|").setPosition(Vex.Flow.Modifier.Position.BOTTOM); + } + if (decorator === "d") { + modifier = new Vex.Flow.Articulation("am").setPosition(Vex.Flow.Modifier.Position.BOTTOM); + score_modifier = new Vex.Flow.Articulation("am").setPosition(Vex.Flow.Modifier.Position.BOTTOM); + } + if (modifier != null) { + _.last(tab_notes).addModifier(modifier, 0); + } + if (score_modifier != null) { + return (_ref = _.last(score_notes)) != null ? _ref.addArticulation(0, score_modifier) : void 0; + } + }; + + Artist.prototype.addArticulations = function(articulations) { + var art, current_indices, current_tab_note, has_bends, i, indices, n, pos, prev_index, prev_indices, prev_tab_note, stave, stave_notes, tab_notes, this_strings, valid_articulation, valid_strings, _i, _len, _ref; + L("addArticulations: ", articulations); + stave = _.last(this.staves); + tab_notes = stave.tab_notes; + stave_notes = stave.note_notes; + if (_.isEmpty(tab_notes) || _.isEmpty(articulations)) { + this.closeBends(0); + return; + } + current_tab_note = _.last(tab_notes); + has_bends = false; + _ref = ["b", "s", "h", "p", "t", "T", "v", "V"]; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + valid_articulation = _ref[_i]; + indices = (function() { + var _j, _len1, _results; + _results = []; + for (i = _j = 0, _len1 = articulations.length; _j < _len1; i = ++_j) { + art = articulations[i]; + if ((art != null) && art === valid_articulation) { + _results.push(i); + } + } + return _results; + })(); + if (_.isEmpty(indices)) { + continue; + } + if (valid_articulation === "b") { + has_bends = true; + } + prev_index = this.getPreviousNoteIndex(); + if (prev_index === -1) { + prev_tab_note = null; + prev_indices = null; + } else { + prev_tab_note = tab_notes[prev_index]; + this_strings = (function() { + var _j, _len1, _ref1, _results; + _ref1 = current_tab_note.getPositions(); + _results = []; + for (i = _j = 0, _len1 = _ref1.length; _j < _len1; i = ++_j) { + n = _ref1[i]; + if (__indexOf.call(indices, i) >= 0) { + _results.push(n.str); + } + } + return _results; + })(); + valid_strings = (function() { + var _j, _len1, _ref1, _ref2, _results; + _ref1 = prev_tab_note.getPositions(); + _results = []; + for (i = _j = 0, _len1 = _ref1.length; _j < _len1; i = ++_j) { + pos = _ref1[i]; + if (_ref2 = pos.str, __indexOf.call(this_strings, _ref2) >= 0) { + _results.push(pos.str); + } + } + return _results; + })(); + prev_indices = (function() { + var _j, _len1, _ref1, _ref2, _results; + _ref1 = prev_tab_note.getPositions(); + _results = []; + for (i = _j = 0, _len1 = _ref1.length; _j < _len1; i = ++_j) { + n = _ref1[i]; + if (_ref2 = n.str, __indexOf.call(valid_strings, _ref2) >= 0) { + _results.push(i); + } + } + return _results; + })(); + current_indices = (function() { + var _j, _len1, _ref1, _ref2, _results; + _ref1 = current_tab_note.getPositions(); + _results = []; + for (i = _j = 0, _len1 = _ref1.length; _j < _len1; i = ++_j) { + n = _ref1[i]; + if (_ref2 = n.str, __indexOf.call(valid_strings, _ref2) >= 0) { + _results.push(i); + } + } + return _results; + })(); + } + if (stave.tab != null) { + this.addTabArticulation(valid_articulation, prev_tab_note, current_tab_note, prev_indices, current_indices); + } + if (stave.note != null) { + this.addStaveArticulation(valid_articulation, stave_notes[prev_index], _.last(stave_notes), prev_indices, current_indices); + } + } + if (!has_bends) { + return this.closeBends(0); + } + }; + + Artist.prototype.addRest = function(params) { + var position, tab_note, tab_notes; + L("addRest: ", params); + this.closeBends(); + if (params["position"] === 0) { + this.addStaveNote({ + spec: ["r/4"], + accidentals: [], + is_rest: true + }); + } else { + position = this.tuning.getNoteForFret((parseInt(params["position"], 10) + 5) * 2, 6); + this.addStaveNote({ + spec: [position], + accidentals: [], + is_rest: true + }); + } + tab_notes = _.last(this.staves).tab_notes; + if (this.customizations["tab-stems"] === "true") { + tab_note = new Vex.Flow.StaveNote({ + keys: [position || "r/4"], + duration: this.current_duration + "r", + clef: "treble", + auto_stem: false + }); + if (this.current_duration[this.current_duration.length - 1] === "d") { + tab_note.addDot(0); + } + return tab_notes.push(tab_note); + } else { + return tab_notes.push(new Vex.Flow.GhostNote(this.current_duration)); + } + }; + + Artist.prototype.addChord = function(chord, chord_articulation, chord_decorator) { + var acc, accidental, accidentals, art, articulations, current_duration, current_position, current_string, decorators, durations, i, new_note, new_octave, note, num, num_notes, octave, play_note, play_notes, play_octave, saved_duration, spec, specs, stave, tab_specs, _i, _j, _k, _len, _len1, _ref, _ref1, _ref2; + if (_.isEmpty(chord)) { + return; + } + L("addChord: ", chord); + stave = _.last(this.staves); + specs = []; + play_notes = []; + accidentals = []; + articulations = []; + decorators = []; + tab_specs = []; + durations = []; + num_notes = 0; + current_string = _.first(chord).string; + current_position = 0; + for (_i = 0, _len = chord.length; _i < _len; _i++) { + note = chord[_i]; + num_notes++; + if ((note.abc != null) || note.string !== current_string) { + current_position = 0; + current_string = note.string; + } + if (specs[current_position] == null) { + specs[current_position] = []; + play_notes[current_position] = []; + accidentals[current_position] = []; + tab_specs[current_position] = []; + articulations[current_position] = []; + decorators[current_position] = []; + } + _ref = [null, null, null], new_note = _ref[0], new_octave = _ref[1], accidental = _ref[2]; + play_note = null; + if (note.abc != null) { + octave = note.octave != null ? note.octave : note.string; + _ref1 = this.getNoteForABC(note.abc, octave), new_note = _ref1[0], new_octave = _ref1[1], accidental = _ref1[2]; + if (accidental != null) { + acc = accidental.split("_")[0]; + } else { + acc = ""; + } + play_note = "" + new_note + acc; + if (note.fret == null) { + note.fret = 'X'; + } + } else if (note.fret != null) { + _ref2 = this.getNoteForFret(note.fret, note.string), new_note = _ref2[0], new_octave = _ref2[1], accidental = _ref2[2]; + play_note = this.tuning.getNoteForFret(note.fret, note.string).split("/")[0]; + } else { + throw new Vex.RERR("ArtistError", "No note specified"); + } + play_octave = parseInt(new_octave, 10) + this.current_octave_shift; + current_duration = note.time != null ? { + time: note.time, + dot: note.dot + } : null; + specs[current_position].push("" + new_note + "/" + new_octave); + play_notes[current_position].push("" + play_note + "/" + play_octave); + accidentals[current_position].push(accidental); + tab_specs[current_position].push({ + fret: note.fret, + str: note.string + }); + if (note.articulation != null) { + articulations[current_position].push(note.articulation); + } + durations[current_position] = current_duration; + if (note.decorator != null) { + decorators[current_position] = note.decorator; + } + current_position++; + } + for (i = _j = 0, _len1 = specs.length; _j < _len1; i = ++_j) { + spec = specs[i]; + saved_duration = this.current_duration; + if (durations[i] != null) { + this.setDuration(durations[i].time, durations[i].dot); + } + this.addTabNote(tab_specs[i], play_notes[i]); + if (stave.note != null) { + this.addStaveNote({ + spec: spec, + accidentals: accidentals[i], + play_note: play_notes[i] + }); + } + this.addArticulations(articulations[i]); + if (decorators[i] != null) { + this.addDecorator(decorators[i]); + } + } + if (chord_articulation != null) { + art = []; + for (num = _k = 1; 1 <= num_notes ? _k <= num_notes : _k >= num_notes; num = 1 <= num_notes ? ++_k : --_k) { + art.push(chord_articulation); + } + this.addArticulations(art); + } + if (chord_decorator != null) { + return this.addDecorator(chord_decorator); + } + }; + + Artist.prototype.addNote = function(note) { + return this.addChord([note]); + }; + + Artist.prototype.addTextVoice = function() { + return _.last(this.staves).text_voices.push([]); + }; + + Artist.prototype.setTextFont = function(font) { + var parts; + if (font != null) { + parts = font.match(/([^-]*)-([^-]*)-([^.]*)/); + if (parts != null) { + this.customizations["font-face"] = parts[1]; + this.customizations["font-size"] = parseInt(parts[2], 10); + return this.customizations["font-style"] = parts[3]; + } + } + }; + + Artist.prototype.addTextNote = function(text, position, justification, smooth, ignore_ticks) { + var duration, font_face, font_size, font_style, just, note, struct, voices; + if (position == null) { + position = 0; + } + if (justification == null) { + justification = "center"; + } + if (smooth == null) { + smooth = true; + } + if (ignore_ticks == null) { + ignore_ticks = false; + } + voices = _.last(this.staves).text_voices; + if (_.isEmpty(voices)) { + throw new Vex.RERR("ArtistError", "Can't add text note without text voice"); + } + font_face = this.customizations["font-face"]; + font_size = this.customizations["font-size"]; + font_style = this.customizations["font-style"]; + just = (function() { + switch (justification) { + case "center": + return Vex.Flow.TextNote.Justification.CENTER; + case "left": + return Vex.Flow.TextNote.Justification.LEFT; + case "right": + return Vex.Flow.TextNote.Justification.RIGHT; + default: + return Vex.Flow.TextNote.Justification.CENTER; + } + })(); + duration = ignore_ticks ? "b" : this.current_duration; + struct = { + text: text, + duration: duration, + smooth: smooth, + ignore_ticks: ignore_ticks, + font: { + family: font_face, + size: font_size, + weight: font_style + } + }; + if (text[0] === "#") { + struct.glyph = text.slice(1); + } + note = new Vex.Flow.TextNote(struct).setLine(position).setJustification(just); + return _.last(voices).push(note); + }; + + Artist.prototype.addVoice = function(options) { + var stave; + this.closeBends(); + stave = _.last(this.staves); + if (stave == null) { + return this.addStave(options); + } + if (!_.isEmpty(stave.tab_notes)) { + stave.tab_voices.push(stave.tab_notes); + stave.tab_notes = []; + } + if (!_.isEmpty(stave.note_notes)) { + stave.note_voices.push(stave.note_notes); + return stave.note_notes = []; + } + }; + + Artist.prototype.addStave = function(element, options) { + var beam_groups, note_stave, opts, start_x, tab_stave, tabstave_start_x; + opts = { + tuning: "standard", + clef: "treble", + key: "C", + notation: element === "tabstave" ? "false" : "true", + tablature: element === "stave" ? "false" : "true", + strings: 6 + }; + _.extend(opts, options); + L("addStave: ", element, opts); + tab_stave = null; + note_stave = null; + start_x = this.x + this.customizations["connector-space"]; + tabstave_start_x = 40; + if (opts.notation === "true") { + note_stave = new Vex.Flow.Stave(start_x, this.last_y, this.customizations.width - 20).addClef(opts.clef).addKeySignature(opts.key); + if (opts.time != null) { + note_stave.addTimeSignature(opts.time); + } + this.last_y += note_stave.getHeight() + this.options.note_stave_lower_spacing + parseInt(this.customizations["stave-distance"], 10); + tabstave_start_x = note_stave.getNoteStartX(); + this.current_clef = opts.clef; + } + if (opts.tablature === "true") { + tab_stave = new Vex.Flow.TabStave(start_x, this.last_y, this.customizations.width - 20).setNumLines(opts.strings).addTabGlyph().setNoteStartX(tabstave_start_x); + this.last_y += tab_stave.getHeight() + this.options.tab_stave_lower_spacing; + } + this.closeBends(); + beam_groups = Vex.Flow.Beam.getDefaultBeamGroups(opts.time); + this.staves.push({ + tab: tab_stave, + note: note_stave, + tab_voices: [], + note_voices: [], + tab_notes: [], + note_notes: [], + text_voices: [], + beam_groups: beam_groups + }); + this.tuning.setTuning(opts.tuning); + this.key_manager.setKey(opts.key); + }; + + Artist.prototype.runCommand = function(line, _l, _c) { + var words; + if (_l == null) { + _l = 0; + } + if (_c == null) { + _c = 0; + } + L("runCommand: ", line); + words = line.split(/\s+/); + switch (words[0]) { + case "octave-shift": + this.current_octave_shift = parseInt(words[1], 10); + return L("Octave shift: ", this.current_octave_shift); + default: + throw new Vex.RERR("ArtistError", "Invalid command '" + words[0] + "' at line " + _l + " column " + _c); + } + }; + + return Artist; + + })(); + +}).call(this); +// Generated by CoffeeScript 1.8.0 +(function() { + var __slice = [].slice, + __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + + Vex.Flow.VexTab = (function() { + var L, newError; + + VexTab.DEBUG = false; + + L = function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + if (Vex.Flow.VexTab.DEBUG) { + return typeof console !== "undefined" && console !== null ? console.log.apply(console, ["(Vex.Flow.VexTab)"].concat(__slice.call(args))) : void 0; + } + }; + + newError = function(object, msg) { + return new Vex.RERR("ParseError", "" + msg + " in line " + object._l + " column " + object._c); + }; + + function VexTab(artist) { + this.artist = artist; + this.reset(); + } + + VexTab.prototype.reset = function() { + this.valid = false; + return this.elements = false; + }; + + VexTab.prototype.isValid = function() { + return this.valid; + }; + + VexTab.prototype.getArtist = function() { + return this.artist; + }; + + VexTab.prototype.parseStaveOptions = function(options) { + var clefs, e, error, notation_option, num_strings, option, params, voices, _i, _len, _ref, _ref1, _ref2; + params = {}; + if (options == null) { + return params; + } + notation_option = null; + for (_i = 0, _len = options.length; _i < _len; _i++) { + option = options[_i]; + error = function(msg) { + return newError(option, msg); + }; + params[option.key] = option.value; + switch (option.key) { + case "notation": + case "tablature": + notation_option = option; + if ((_ref = option.value) !== "true" && _ref !== "false") { + throw error("'" + option.key + "' must be 'true' or 'false'"); + } + break; + case "key": + if (!_.has(Vex.Flow.keySignature.keySpecs, option.value)) { + throw error("Invalid key signature '" + option.value + "'"); + } + break; + case "clef": + clefs = ["treble", "bass", "tenor", "alto", "percussion", "none"]; + if (_ref1 = option.value, __indexOf.call(clefs, _ref1) < 0) { + throw error("'clef' must be one of " + (clefs.join(', '))); + } + break; + case "voice": + voices = ["top", "bottom", "new"]; + if (_ref2 = option.value, __indexOf.call(voices, _ref2) < 0) { + throw error("'voice' must be one of " + (voices.join(', '))); + } + break; + case "time": + try { + new Vex.Flow.TimeSignature(option.value); + } catch (_error) { + e = _error; + throw error("Invalid time signature: '" + option.value + "'"); + } + break; + case "tuning": + try { + new Vex.Flow.Tuning(option.value); + } catch (_error) { + e = _error; + throw error("Invalid tuning: '" + option.value + "'"); + } + break; + case "strings": + num_strings = parseInt(option.value); + if (num_strings < 4 || num_strings > 8) { + throw error("Invalid number of strings: " + num_strings); + } + break; + default: + throw error("Invalid option '" + option.key + "'"); + } + } + if (params.notation === "false" && params.tablature === "false") { + throw newError(notation_option, "Both 'notation' and 'tablature' can't be invisible"); + } + return params; + }; + + VexTab.prototype.parseCommand = function(element) { + if (element.command === "bar") { + this.artist.addBar(element.type); + } + if (element.command === "tuplet") { + this.artist.makeTuplets(element.params.tuplet, element.params.notes); + } + if (element.command === "annotations") { + this.artist.addAnnotations(element.params); + } + if (element.command === "rest") { + this.artist.addRest(element.params); + } + if (element.command === "command") { + return this.artist.runCommand(element.params, element._l, element._c); + } + }; + + VexTab.prototype.parseChord = function(element) { + L("parseChord:", element); + return this.artist.addChord(_.map(element.chord, function(note) { + return _.pick(note, 'time', 'dot', 'fret', 'abc', 'octave', 'string', 'articulation', 'decorator'); + }), element.articulation, element.decorator); + }; + + VexTab.prototype.parseFret = function(note) { + return this.artist.addNote(_.pick(note, 'time', 'dot', 'fret', 'string', 'articulation', 'decorator')); + }; + + VexTab.prototype.parseABC = function(note) { + return this.artist.addNote(_.pick(note, 'time', 'dot', 'fret', 'abc', 'octave', 'string', 'articulation', 'decorator')); + }; + + VexTab.prototype.parseStaveElements = function(notes) { + var element, _i, _len, _results; + L("parseStaveElements:", notes); + _results = []; + for (_i = 0, _len = notes.length; _i < _len; _i++) { + element = notes[_i]; + if (element.time) { + this.artist.setDuration(element.time, element.dot); + } + if (element.command) { + this.parseCommand(element); + } + if (element.chord) { + this.parseChord(element); + } + if (element.abc) { + _results.push(this.parseABC(element)); + } else if (element.fret) { + _results.push(this.parseFret(element)); + } else { + _results.push(void 0); + } + } + return _results; + }; + + VexTab.prototype.parseStaveText = function(text_line) { + var bartext, command, createNote, font, justification, position, smooth, str, text, _i, _len, _results; + if (!_.isEmpty(text_line)) { + this.artist.addTextVoice(); + } + position = 0; + justification = "center"; + smooth = true; + font = null; + bartext = (function(_this) { + return function() { + return _this.artist.addTextNote("", 0, justification, false, true); + }; + })(this); + createNote = (function(_this) { + return function(text) { + var e, ignore_ticks; + ignore_ticks = false; + if (text[0] === "|") { + ignore_ticks = true; + text = text.slice(1); + } + try { + return _this.artist.addTextNote(text, position, justification, smooth, ignore_ticks); + } catch (_error) { + e = _error; + throw newError(str, "Bad text or duration. Did you forget a comma?" + e); + } + }; + })(this); + _results = []; + for (_i = 0, _len = text_line.length; _i < _len; _i++) { + str = text_line[_i]; + text = str.text.trim(); + if (text.match(/\.font=.*/)) { + font = text.slice(6); + _results.push(this.artist.setTextFont(font)); + } else if (text[0] === ":") { + _results.push(this.artist.setDuration(text)); + } else if (text[0] === ".") { + command = text.slice(1); + switch (command) { + case "center": + case "left": + case "right": + _results.push(justification = command); + break; + case "strict": + _results.push(smooth = false); + break; + case "smooth": + _results.push(smooth = true); + break; + case "bar": + case "|": + _results.push(bartext()); + break; + default: + _results.push(position = parseInt(text.slice(1), 10)); + } + } else if (text === "|") { + _results.push(bartext()); + } else if (text.slice(0, 2) === "++") { + _results.push(this.artist.addTextVoice()); + } else { + _results.push(createNote(text)); + } + } + return _results; + }; + + VexTab.prototype.generate = function() { + var e, option, options, stave, _i, _j, _len, _len1, _ref, _ref1, _results; + _ref = this.elements; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + stave = _ref[_i]; + switch (stave.element) { + case "stave": + case "tabstave": + this.artist.addStave(stave.element, this.parseStaveOptions(stave.options)); + if (stave.notes != null) { + this.parseStaveElements(stave.notes); + } + if (stave.text != null) { + _results.push(this.parseStaveText(stave.text)); + } else { + _results.push(void 0); + } + break; + case "voice": + this.artist.addVoice(this.parseStaveOptions(stave.options)); + if (stave.notes != null) { + this.parseStaveElements(stave.notes); + } + if (stave.text != null) { + _results.push(this.parseStaveText(stave.text)); + } else { + _results.push(void 0); + } + break; + case "options": + options = {}; + _ref1 = stave.params; + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + option = _ref1[_j]; + options[option.key] = option.value; + } + try { + _results.push(this.artist.setOptions(options)); + } catch (_error) { + e = _error; + throw newError(stave, e.message); + } + break; + default: + throw newError(stave, "Invalid keyword '" + stave.element + "'"); + } + } + return _results; + }; + + VexTab.prototype.parse = function(code) { + var line, stripped_code; + vextab_parser.parseError = function(message, hash) { + L("VexTab parse error: ", message, hash); + message = "Unexpected text '" + hash.text + "' at line " + hash.loc.first_line + " column " + hash.loc.first_column + "."; + throw new Vex.RERR("ParseError", message); + }; + if (code == null) { + throw new Vex.RERR("ParseError", "No code"); + } + L("Parsing:\n" + code); + stripped_code = (function() { + var _i, _len, _ref, _results; + _ref = code.split(/\r\n|\r|\n/); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + line = _ref[_i]; + _results.push(line.trim()); + } + return _results; + })(); + this.elements = vextab_parser.parse(stripped_code.join("\n")); + if (this.elements) { + this.generate(); + this.valid = true; + } + return this.elements; + }; + + return VexTab; + + })(); + +}).call(this); diff --git a/webclient/js/vextab/underscore-min.js b/webclient/js/vextab/underscore-min.js new file mode 100644 index 0000000000..7ed6e5284f --- /dev/null +++ b/webclient/js/vextab/underscore-min.js @@ -0,0 +1 @@ +(function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,v=e.reduce,h=e.reduceRight,g=e.filter,d=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,_=Object.keys,j=i.bind,w=function(n){return n instanceof w?n:this instanceof w?(this._wrapped=n,void 0):new w(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=w),exports._=w):n._=w,w.VERSION="1.4.3";var A=w.each=w.forEach=function(n,t,e){if(null!=n)if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a in n)if(w.has(n,a)&&t.call(e,n[a],a,n)===r)return};w.map=w.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e[e.length]=t.call(r,n,u,i)}),e)};var O="Reduce of empty array with no initial value";w.reduce=w.foldl=w.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduce===v)return e&&(t=w.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(O);return r},w.reduceRight=w.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduceRight===h)return e&&(t=w.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=w.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(O);return r},w.find=w.detect=function(n,t,r){var e;return E(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},w.filter=w.select=function(n,t,r){var e=[];return null==n?e:g&&n.filter===g?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&(e[e.length]=n)}),e)},w.reject=function(n,t,r){return w.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},w.every=w.all=function(n,t,e){t||(t=w.identity);var u=!0;return null==n?u:d&&n.every===d?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var E=w.some=w.any=function(n,t,e){t||(t=w.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};w.contains=w.include=function(n,t){return null==n?!1:y&&n.indexOf===y?-1!=n.indexOf(t):E(n,function(n){return n===t})},w.invoke=function(n,t){var r=o.call(arguments,2);return w.map(n,function(n){return(w.isFunction(t)?t:n[t]).apply(n,r)})},w.pluck=function(n,t){return w.map(n,function(n){return n[t]})},w.where=function(n,t){return w.isEmpty(t)?[]:w.filter(n,function(n){for(var r in t)if(t[r]!==n[r])return!1;return!0})},w.max=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.max.apply(Math,n);if(!t&&w.isEmpty(n))return-1/0;var e={computed:-1/0,value:-1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;a>=e.computed&&(e={value:n,computed:a})}),e.value},w.min=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.min.apply(Math,n);if(!t&&w.isEmpty(n))return 1/0;var e={computed:1/0,value:1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;e.computed>a&&(e={value:n,computed:a})}),e.value},w.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=w.random(r++),e[r-1]=e[t],e[t]=n}),e};var F=function(n){return w.isFunction(n)?n:function(t){return t[n]}};w.sortBy=function(n,t,r){var e=F(t);return w.pluck(w.map(n,function(n,t,u){return{value:n,index:t,criteria:e.call(r,n,t,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||void 0===r)return 1;if(e>r||void 0===e)return-1}return n.indexi;){var o=i+a>>>1;u>r.call(e,n[o])?i=o+1:a=o}return i},w.toArray=function(n){return n?w.isArray(n)?o.call(n):n.length===+n.length?w.map(n,w.identity):w.values(n):[]},w.size=function(n){return null==n?0:n.length===+n.length?n.length:w.keys(n).length},w.first=w.head=w.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:o.call(n,0,t)},w.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},w.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},w.rest=w.tail=w.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},w.compact=function(n){return w.filter(n,w.identity)};var R=function(n,t,r){return A(n,function(n){w.isArray(n)?t?a.apply(r,n):R(n,t,r):r.push(n)}),r};w.flatten=function(n,t){return R(n,t,[])},w.without=function(n){return w.difference(n,o.call(arguments,1))},w.uniq=w.unique=function(n,t,r,e){w.isFunction(t)&&(e=r,r=t,t=!1);var u=r?w.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:w.contains(a,r))||(a.push(r),i.push(n[e]))}),i},w.union=function(){return w.uniq(c.apply(e,arguments))},w.intersection=function(n){var t=o.call(arguments,1);return w.filter(w.uniq(n),function(n){return w.every(t,function(t){return w.indexOf(t,n)>=0})})},w.difference=function(n){var t=c.apply(e,o.call(arguments,1));return w.filter(n,function(n){return!w.contains(t,n)})},w.zip=function(){for(var n=o.call(arguments),t=w.max(w.pluck(n,"length")),r=Array(t),e=0;t>e;e++)r[e]=w.pluck(n,""+e);return r},w.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},w.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=w.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},w.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},w.range=function(n,t,r){1>=arguments.length&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=Array(e);e>u;)i[u++]=n,n+=r;return i};var I=function(){};w.bind=function(n,t){var r,e;if(n.bind===j&&j)return j.apply(n,o.call(arguments,1));if(!w.isFunction(n))throw new TypeError;return r=o.call(arguments,2),e=function(){if(!(this instanceof e))return n.apply(t,r.concat(o.call(arguments)));I.prototype=n.prototype;var u=new I;I.prototype=null;var i=n.apply(u,r.concat(o.call(arguments)));return Object(i)===i?i:u}},w.bindAll=function(n){var t=o.call(arguments,1);return 0==t.length&&(t=w.functions(n)),A(t,function(t){n[t]=w.bind(n[t],n)}),n},w.memoize=function(n,t){var r={};return t||(t=w.identity),function(){var e=t.apply(this,arguments);return w.has(r,e)?r[e]:r[e]=n.apply(this,arguments)}},w.delay=function(n,t){var r=o.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},w.defer=function(n){return w.delay.apply(w,[n,1].concat(o.call(arguments,1)))},w.throttle=function(n,t){var r,e,u,i,a=0,o=function(){a=new Date,u=null,i=n.apply(r,e)};return function(){var c=new Date,l=t-(c-a);return r=this,e=arguments,0>=l?(clearTimeout(u),u=null,a=c,i=n.apply(r,e)):u||(u=setTimeout(o,l)),i}},w.debounce=function(n,t,r){var e,u;return function(){var i=this,a=arguments,o=function(){e=null,r||(u=n.apply(i,a))},c=r&&!e;return clearTimeout(e),e=setTimeout(o,t),c&&(u=n.apply(i,a)),u}},w.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},w.wrap=function(n,t){return function(){var r=[n];return a.apply(r,arguments),t.apply(this,r)}},w.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},w.after=function(n,t){return 0>=n?t():function(){return 1>--n?t.apply(this,arguments):void 0}},w.keys=_||function(n){if(n!==Object(n))throw new TypeError("Invalid object");var t=[];for(var r in n)w.has(n,r)&&(t[t.length]=r);return t},w.values=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push(n[r]);return t},w.pairs=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push([r,n[r]]);return t},w.invert=function(n){var t={};for(var r in n)w.has(n,r)&&(t[n[r]]=r);return t},w.functions=w.methods=function(n){var t=[];for(var r in n)w.isFunction(n[r])&&t.push(r);return t.sort()},w.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},w.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},w.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)w.contains(r,u)||(t[u]=n[u]);return t},w.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)null==n[r]&&(n[r]=t[r])}),n},w.clone=function(n){return w.isObject(n)?w.isArray(n)?n.slice():w.extend({},n):n},w.tap=function(n,t){return t(n),n};var S=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof w&&(n=n._wrapped),t instanceof w&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==t+"";case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;r.push(n),e.push(t);var a=0,o=!0;if("[object Array]"==u){if(a=n.length,o=a==t.length)for(;a--&&(o=S(n[a],t[a],r,e)););}else{var c=n.constructor,f=t.constructor;if(c!==f&&!(w.isFunction(c)&&c instanceof c&&w.isFunction(f)&&f instanceof f))return!1;for(var s in n)if(w.has(n,s)&&(a++,!(o=w.has(t,s)&&S(n[s],t[s],r,e))))break;if(o){for(s in t)if(w.has(t,s)&&!a--)break;o=!a}}return r.pop(),e.pop(),o};w.isEqual=function(n,t){return S(n,t,[],[])},w.isEmpty=function(n){if(null==n)return!0;if(w.isArray(n)||w.isString(n))return 0===n.length;for(var t in n)if(w.has(n,t))return!1;return!0},w.isElement=function(n){return!(!n||1!==n.nodeType)},w.isArray=x||function(n){return"[object Array]"==l.call(n)},w.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){w["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),w.isArguments(arguments)||(w.isArguments=function(n){return!(!n||!w.has(n,"callee"))}),w.isFunction=function(n){return"function"==typeof n},w.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},w.isNaN=function(n){return w.isNumber(n)&&n!=+n},w.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},w.isNull=function(n){return null===n},w.isUndefined=function(n){return void 0===n},w.has=function(n,t){return f.call(n,t)},w.noConflict=function(){return n._=t,this},w.identity=function(n){return n},w.times=function(n,t,r){for(var e=Array(n),u=0;n>u;u++)e[u]=t.call(r,u);return e},w.random=function(n,t){return null==t&&(t=n,n=0),n+(0|Math.random()*(t-n+1))};var T={escape:{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}};T.unescape=w.invert(T.escape);var M={escape:RegExp("["+w.keys(T.escape).join("")+"]","g"),unescape:RegExp("("+w.keys(T.unescape).join("|")+")","g")};w.each(["escape","unescape"],function(n){w[n]=function(t){return null==t?"":(""+t).replace(M[n],function(t){return T[n][t]})}}),w.result=function(n,t){if(null==n)return null;var r=n[t];return w.isFunction(r)?r.call(n):r},w.mixin=function(n){A(w.functions(n),function(t){var r=w[t]=n[t];w.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),z.call(this,r.apply(w,n))}})};var N=0;w.uniqueId=function(n){var t=""+ ++N;return n?n+t:t},w.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var q=/(.)^/,B={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},D=/\\|'|\r|\n|\t|\u2028|\u2029/g;w.template=function(n,t,r){r=w.defaults({},r,w.templateSettings);var e=RegExp([(r.escape||q).source,(r.interpolate||q).source,(r.evaluate||q).source].join("|")+"|$","g"),u=0,i="__p+='";n.replace(e,function(t,r,e,a,o){return i+=n.slice(u,o).replace(D,function(n){return"\\"+B[n]}),r&&(i+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(i+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),a&&(i+="';\n"+a+"\n__p+='"),u=o+t.length,t}),i+="';\n",r.variable||(i="with(obj||{}){\n"+i+"}\n"),i="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+i+"return __p;\n";try{var a=Function(r.variable||"obj","_",i)}catch(o){throw o.source=i,o}if(t)return a(t,w);var c=function(n){return a.call(this,n,w)};return c.source="function("+(r.variable||"obj")+"){\n"+i+"}",c},w.chain=function(n){return w(n).chain()};var z=function(n){return this._chain?w(n).chain():n};w.mixin(w),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];w.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],z.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];w.prototype[n]=function(){return z.call(this,t.apply(this._wrapped,arguments))}}),w.extend(w.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this); \ No newline at end of file diff --git a/webclient/js/vextab/vexflow-debug.js b/webclient/js/vextab/vexflow-debug.js new file mode 100644 index 0000000000..b050187c44 --- /dev/null +++ b/webclient/js/vextab/vexflow-debug.js @@ -0,0 +1,12803 @@ +/** + * VexFlow Engraver 1.2 Custom + * A library for rendering musical notation and guitar tablature in HTML5. + * + * http://www.vexflow.com + * + * Copyright (c) 2010 Mohit Muthanna Cheppudira + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This library makes use of Simon Tatham's awesome font - Gonville. + * + * Build ID: 0xFE@4af446259300db66856c51b1face77457796ab5f + * Build date: 2014-05-01 12:29:38 -0400 + */ +// [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010. +// +// ## Description +// +// This file implements utility methods used by the rest of the VexFlow +// codebase. +// +// ## JSHint Settings +// +/* global window: false */ +/* global document: false */ + +function Vex() {} + +// Default log function sends all arguments to console. +Vex.L = function(block, args) { + if (!args) return; + var line = Array.prototype.slice.call(args).join(" "); + window.console.log(block + ": " + line); +}; + +// Default runtime exception. +Vex.RuntimeError = function(code, message) { + this.code = code; + this.message = message; +}; +Vex.RuntimeError.prototype.toString = function() { + return "RuntimeError: " + this.message; +}; + +// Shortcut method for `RuntimeError`. +Vex.RERR = Vex.RuntimeError; + +// Merge `destination` hash with `source` hash, overwriting like keys +// in `source` if necessary. +Vex.Merge = function(destination, source) { + for (var property in source) + destination[property] = source[property]; + return destination; +}; + +// DEPRECATED. Use `Math.min`. +Vex.Min = function(a, b) { + return (a > b) ? b : a; +}; + +// DEPRECATED. Use `Math.max`. +Vex.Max = function(a, b) { + return (a > b) ? a : b; +}; + +// Round number to nearest fractional value (`.5`, `.25`, etc.) +Vex.RoundN = function(x, n) { + return (x % n) >= (n/2) ? + parseInt(x / n, 10) * n + n : parseInt(x / n, 10) * n; +}; + +// Locate the mid point between stave lines. Returns a fractional line if a space. +Vex.MidLine = function(a, b) { + var mid_line = b + (a - b) / 2; + if (mid_line % 2 > 0) { + mid_line = Vex.RoundN(mid_line * 10, 5) / 10; + } + return mid_line; +}; + +// Take `arr` and return a new list consisting of the sorted, unique, +// contents of arr. Does not modify `arr`. +Vex.SortAndUnique = function(arr, cmp, eq) { + if (arr.length > 1) { + var newArr = []; + var last; + arr.sort(cmp); + + for (var i = 0; i < arr.length; ++i) { + if (i === 0 || !eq(arr[i], last)) { + newArr.push(arr[i]); + } + last = arr[i]; + } + + return newArr; + } else { + return arr; + } +}; + +// Check if array `a` contains `obj`. +Vex.Contains = function(a, obj) { + var i = a.length; + while (i--) { + if (a[i] === obj) { + return true; + } + } + return false; +}; + +// Get the 2D Canvas context from DOM element `canvas_sel`. +Vex.getCanvasContext = function(canvas_sel) { + if (!canvas_sel) + throw new Vex.RERR("BadArgument", "Invalid canvas selector: " + canvas_sel); + + var canvas = document.getElementById(canvas_sel); + if (!(canvas && canvas.getContext)) { + throw new Vex.RERR("UnsupportedBrowserError", + "This browser does not support HTML5 Canvas"); + } + + return canvas.getContext('2d'); +}; + +// Draw a tiny dot marker on the specified canvas. A great debugging aid. +// +// `ctx`: Canvas context. +// `x`, `y`: Dot coordinates. +Vex.drawDot = function(ctx, x, y, color) { + var c = color || "#f55"; + ctx.save(); + ctx.fillStyle = c; + + //draw a circle + ctx.beginPath(); + ctx.arc(x, y, 3, 0, Math.PI*2, true); + ctx.closePath(); + ctx.fill(); + ctx.restore(); +}; + +// Benchmark. Run function `f` once and report time elapsed shifted by `s` milliseconds. +Vex.BM = function(s, f) { + var start_time = new Date().getTime(); + f(); + var elapsed = new Date().getTime() - start_time; + Vex.L(s + elapsed + "ms"); +}; + +// Basic classical inheritance helper. Usage: +// ``` +// // Vex.Inherit(Child, Parent, { +// // getName: function() {return this.name;}, +// // setName: function(name) {this.name = name} +// // }); +// // +// // Returns 'Child'. +// ``` +Vex.Inherit = (function () { + var F = function () {}; + // `C` is Child. `P` is parent. `O` is an object to + // to extend `C` with. + return function (C, P, O) { + F.prototype = P.prototype; + C.prototype = new F(); + C.superclass = P.prototype; + C.prototype.constructor = C; + Vex.Merge(C.prototype, O); + return C; + }; +}());/** + * Vex Flow - Mohit Muthanna + */ + +/** + * New namespace. + */ +Vex.Flow = { + /** + * The resolution used for all the rhythm timing in this + * library. + * + * @const + * @type {number} + */ + RESOLUTION: 16384, + + /* Kerning (DEPRECATED) */ + IsKerned: true +}; +// Fraction class that represents a rational number +// @author zz85 +// @author incompleteopus (modifications) + +Vex.Flow.Fraction = (function() { + function Fraction(numerator, denominator) { + this.set(numerator, denominator); + } + + /** + * GCD: Find greatest common divisor using Euclidean algorithm + */ + Fraction.GCD = function(a, b) { + if (typeof a !== "number" || typeof b !== "number") { + throw new Vex.RERR("BadArgument", "Invalid numbers: " + a + ", " + b); + } + + var t; + + while (b !== 0) { + t = b; + b = a % b; + a = t; + } + + return a; + }; + + /** + * LCM: Lowest common multiple + */ + Fraction.LCM = function(a, b) { + return ((a * b) / Fraction.GCD(a, b)); + }; + + /** + * LCMM: Lowest common multiple for more than two numbers + */ + Fraction.LCMM = function(args) { + if (args.length === 0) { + return 0; + } else if (args.length == 1) { + return args[0]; + } else if (args.length == 2) { + return Vex.Flow.Fraction.LCM(args[0], args[1]); + } else { + var arg0 = args[0]; + args.shift(); + return Fraction.LCM(arg0, Vex.Flow.Fraction.LCMM(args)); + } + }; + + Fraction.prototype = { + set: function(numerator, denominator) { + this.numerator = numerator === undefined ? 1 : numerator; + this.denominator = denominator === undefined ? 1 : denominator; + return this; + }, + + value: function() { + return this.numerator / this.denominator; + }, + + simplify: function() { + var u = this.numerator; + var d = this.denominator; + + var gcd = Vex.Flow.Fraction.GCD(u, d); + u /= gcd; + d /= gcd; + + if (d < 0) { + d = -d; + u = -u; + } + return this.set(u, d); + }, + + add: function(param1, param2) { + var otherNumerator; + var otherDenominator; + + if (param1 instanceof Vex.Flow.Fraction) { + otherNumerator = param1.numerator; + otherDenominator = param1.denominator; + } else { + if (param1 !== undefined) { + otherNumerator = param1; + } else { + otherNumerator = 0; + } + + if (param2 !== undefined) { + otherDenominator = param2; + } else { + otherDenominator = 1; + } + } + + var lcm = Vex.Flow.Fraction.LCM(this.denominator, otherDenominator); + var a = lcm / this.denominator; + var b = lcm / otherDenominator; + + var u = this.numerator * a + otherNumerator * b; + return this.set(u, lcm); + }, + + subtract: function(param1, param2) { + var otherNumerator; + var otherDenominator; + + if (param1 instanceof Vex.Flow.Fraction) { + otherNumerator = param1.numerator; + otherDenominator = param1.denominator; + } else { + if (param1 !== undefined) { + otherNumerator = param1; + } else { + otherNumerator = 0; + } + + if (param2 !== undefined) { + otherDenominator = param2; + } else { + otherDenominator = 1; + } + } + + var lcm = Vex.Flow.Fraction.LCM(this.denominator, otherDenominator); + var a = lcm / this.denominator; + var b = lcm / otherDenominator; + + var u = this.numerator * a - otherNumerator * b; + return this.set(u, lcm); + }, + + multiply: function(param1, param2) { + var otherNumerator; + var otherDenominator; + + if (param1 instanceof Vex.Flow.Fraction) { + otherNumerator = param1.numerator; + otherDenominator = param1.denominator; + } else { + if (param1 !== undefined) { + otherNumerator = param1; + } else { + otherNumerator = 1; + } + + if (param2 !== undefined) { + otherDenominator = param2; + } else { + otherDenominator = 1; + } + } + + return this.set(this.numerator * otherNumerator, this.denominator * otherDenominator); + }, + + divide: function(param1, param2) { + var otherNumerator; + var otherDenominator; + + if (param1 instanceof Vex.Flow.Fraction) { + otherNumerator = param1.numerator; + otherDenominator = param1.denominator; + } else { + if (param1 !== undefined) { + otherNumerator = param1; + } else { + otherNumerator = 1; + } + + if (param2 !== undefined) { + otherDenominator = param2; + } else { + otherDenominator = 1; + } + } + + return this.set(this.numerator * otherDenominator, this.denominator * otherNumerator); + }, + + + // Simplifies both sides and checks if they are equal + equals: function(compare) { + var a = Vex.Flow.Fraction.__compareA.copy(compare).simplify(); + var b = Vex.Flow.Fraction.__compareB.copy(this).simplify(); + + return (a.numerator === b.numerator) && (a.denominator === b.denominator); + }, + + // Creates a new copy with this current values + clone: function() { + return new Vex.Flow.Fraction(this.numerator, this.denominator); + }, + + // Copies value of another Fraction into itself + copy: function(copy) { + return this.set(copy.numerator, copy.denominator); + }, + + // Returns the integer component eg. (4/2) == 2 + quotient: function() { + return Math.floor(this.numerator / this.denominator); + }, + + // Returns the fraction component when reduced to a mixed number + fraction: function() { + return this.numerator % this.denominator; + }, + + // Returns the absolute value + abs: function() { + this.denominator = Math.abs(this.denominator); + this.numerator = Math.abs(this.numerator); + return this; + }, + + // Returns a raw string representation + toString: function() { + return this.numerator + '/' + this.denominator; + }, + + // Returns a simplified string respresentation + toSimplifiedString: function() { + return Vex.Flow.Fraction.__tmp.copy(this).simplify().toString(); + }, + + // Returns string representation in mixed form + toMixedString: function() { + var s = ''; + var q = this.quotient(); + var f = Vex.Flow.Fraction.__tmp.copy(this); + + if (q < 0) { + f.abs().fraction(); + } else { + f.fraction(); + } + + if (q !== 0) { + s += q; + + if (f.numerator !== 0) { + s += ' ' + f.toSimplifiedString(); + } + } else { + if (f.numerator === 0) { + s = '0'; + } else { + s = f.toSimplifiedString(); + } + } + + return s; + }, + + // Parses a fraction string + parse: function(str) { + var i = str.split('/'); + var n = parseInt(i[0], 10); + var d = (i[1]) ? parseInt(i[1], 10) : 1; + + return this.set(n, d); + } + }; + + // Temporary cached objects + Fraction.__compareA = new Fraction(); + Fraction.__compareB = new Fraction(); + Fraction.__tmp = new Fraction(); + + return Fraction; +}()); + +// Vex Flow Notation +// Mohit Muthanna +// +// Copyright Mohit Muthanna 2010 +// +// Requires vex.js. + +Vex.Flow.STEM_WIDTH = 1.5; +Vex.Flow.STEM_HEIGHT = 32; +Vex.Flow.STAVE_LINE_THICKNESS = 2; + +Vex.Flow.clefProperties = function(clef) { + if (!clef) throw new Vex.RERR("BadArgument", "Invalid clef: " + clef); + + var props = Vex.Flow.clefProperties.values[clef]; + if (!props) throw new Vex.RERR("BadArgument", "Invalid clef: " + clef); + + return props; +}; + +Vex.Flow.clefProperties.values = { + 'treble': { line_shift: 0 }, + 'bass': { line_shift: 6 }, + 'tenor': { line_shift: 4 }, + 'alto': { line_shift: 3 }, + 'soprano': { line_shift: 1 }, + 'percussion': { line_shift: 0 }, + 'mezzo-soprano': { line_shift: 2 }, + 'baritone-c': { line_shift: 5 }, + 'baritone-f': { line_shift: 5 }, + 'subbass': { line_shift: 7 }, + 'french': { line_shift: -1 } +}; + +/* + Take a note in the format "Key/Octave" (e.g., "C/5") and return properties. +*/ +Vex.Flow.keyProperties = function(key, clef) { + if (clef === undefined) { + clef = 'treble'; + } + + var pieces = key.split("/"); + + if (pieces.length < 2) { + throw new Vex.RERR("BadArguments", + "Key must have note + octave and an optional glyph: " + key); + } + + var k = pieces[0].toUpperCase(); + var value = Vex.Flow.keyProperties.note_values[k]; + if (!value) throw new Vex.RERR("BadArguments", "Invalid key name: " + k); + if (value.octave) pieces[1] = value.octave; + + var o = pieces[1]; + var base_index = (o * 7) - (4 * 7); + var line = (base_index + value.index) / 2; + line += Vex.Flow.clefProperties(clef).line_shift; + + var stroke = 0; + + if (line <= 0 && (((line * 2) % 2) === 0)) stroke = 1; // stroke up + if (line >= 6 && (((line * 2) % 2) === 0)) stroke = -1; // stroke down + + // Integer value for note arithmetic. + var int_value = (typeof(value.int_val)!='undefined') ? (o * 12) + + value.int_val : null; + + /* Check if the user specified a glyph. */ + var code = value.code; + var shift_right = value.shift_right; + if ((pieces.length > 2) && (pieces[2])) { + var glyph_name = pieces[2].toUpperCase(); + var note_glyph = Vex.Flow.keyProperties.note_glyph[glyph_name]; + if (note_glyph) { + code = note_glyph.code; + shift_right = note_glyph.shift_right; + } + } + + return { + key: k, + octave: o, + line: line, + int_value: int_value, + accidental: value.accidental, + code: code, + stroke: stroke, + shift_right: shift_right, + displaced: false + }; +}; + +Vex.Flow.keyProperties.note_values = { + 'C': { index: 0, int_val: 0, accidental: null }, + 'CN': { index: 0, int_val: 0, accidental: "n" }, + 'C#': { index: 0, int_val: 1, accidental: "#" }, + 'C##': { index: 0, int_val: 2, accidental: "##" }, + 'CB': { index: 0, int_val: -1, accidental: "b" }, + 'CBB': { index: 0, int_val: -2, accidental: "bb" }, + 'D': { index: 1, int_val: 2, accidental: null }, + 'DN': { index: 1, int_val: 2, accidental: "n" }, + 'D#': { index: 1, int_val: 3, accidental: "#" }, + 'D##': { index: 1, int_val: 4, accidental: "##" }, + 'DB': { index: 1, int_val: 1, accidental: "b" }, + 'DBB': { index: 1, int_val: 0, accidental: "bb" }, + 'E': { index: 2, int_val: 4, accidental: null }, + 'EN': { index: 2, int_val: 4, accidental: "n" }, + 'E#': { index: 2, int_val: 5, accidental: "#" }, + 'E##': { index: 2, int_val: 6, accidental: "##" }, + 'EB': { index: 2, int_val: 3, accidental: "b" }, + 'EBB': { index: 2, int_val: 2, accidental: "bb" }, + 'F': { index: 3, int_val: 5, accidental: null }, + 'FN': { index: 3, int_val: 5, accidental: "n" }, + 'F#': { index: 3, int_val: 6, accidental: "#" }, + 'F##': { index: 3, int_val: 7, accidental: "##" }, + 'FB': { index: 3, int_val: 4, accidental: "b" }, + 'FBB': { index: 3, int_val: 3, accidental: "bb" }, + 'G': { index: 4, int_val: 7, accidental: null }, + 'GN': { index: 4, int_val: 7, accidental: "n" }, + 'G#': { index: 4, int_val: 8, accidental: "#" }, + 'G##': { index: 4, int_val: 9, accidental: "##" }, + 'GB': { index: 4, int_val: 6, accidental: "b" }, + 'GBB': { index: 4, int_val: 5, accidental: "bb" }, + 'A': { index: 5, int_val: 9, accidental: null }, + 'AN': { index: 5, int_val: 9, accidental: "n" }, + 'A#': { index: 5, int_val: 10, accidental: "#" }, + 'A##': { index: 5, int_val: 11, accidental: "##" }, + 'AB': { index: 5, int_val: 8, accidental: "b" }, + 'ABB': { index: 5, int_val: 7, accidental: "bb" }, + 'B': { index: 6, int_val: 11, accidental: null }, + 'BN': { index: 6, int_val: 11, accidental: "n" }, + 'B#': { index: 6, int_val: 12, accidental: "#" }, + 'B##': { index: 6, int_val: 13, accidental: "##" }, + 'BB': { index: 6, int_val: 10, accidental: "b" }, + 'BBB': { index: 6, int_val: 9, accidental: "bb" }, + 'R': { index: 6, int_val: 9, rest: true }, // Rest + 'X': { + index: 6, + accidental: "", + octave: 4, + code: "v3e", + shift_right: 5.5 + } +}; + +Vex.Flow.keyProperties.note_glyph = { + /* Diamond */ + 'D0': { code: "v27", shift_right: -0.5 }, + 'D1': { code: "v2d", shift_right: -0.5 }, + 'D2': { code: "v22", shift_right: -0.5 }, + 'D3': { code: "v70", shift_right: -0.5 }, + + /* Triangle */ + 'T0': { code: "v49", shift_right: -2 }, + 'T1': { code: "v93", shift_right: 0.5 }, + 'T2': { code: "v40", shift_right: 0.5 }, + 'T3': { code: "v7d", shift_right: 0.5 }, + + /* Cross */ + 'X0': { code: "v92", shift_right: -2 }, + 'X1': { code: "v95", shift_right: -0.5 }, + 'X2': { code: "v7f", shift_right: 0.5 }, + 'X3': { code: "v3b", shift_right: -2 } +}; + +Vex.Flow.integerToNote = function(integer) { + if (typeof(integer) == "undefined") + throw new Vex.RERR("BadArguments", "Undefined integer for integerToNote"); + + if (integer < -2) + throw new Vex.RERR("BadArguments", + "integerToNote requires integer > -2: " + integer); + + var noteValue = Vex.Flow.integerToNote.table[integer]; + if (!noteValue) + throw new Vex.RERR("BadArguments", "Unknown note value for integer: " + + integer); + + return noteValue; +}; + +Vex.Flow.integerToNote.table = { + 0: "C", + 1: "C#", + 2: "D", + 3: "D#", + 4: "E", + 5: "F", + 6: "F#", + 7: "G", + 8: "G#", + 9: "A", + 10: "A#", + 11: "B" +}; + + +Vex.Flow.tabToGlyph = function(fret) { + var glyph = null; + var width = 0; + var shift_y = 0; + + if (fret.toString().toUpperCase() == "X") { + glyph = "v7f"; + width = 7; + shift_y = -4.5; + } else { + width = Vex.Flow.textWidth(fret.toString()); + } + + return { + text: fret, + code: glyph, + width: width, + shift_y: shift_y + }; +}; + +Vex.Flow.textWidth = function(text) { + return 6 * text.toString().length; +}; + +Vex.Flow.articulationCodes = function(artic) { + return Vex.Flow.articulationCodes.articulations[artic]; +}; + +Vex.Flow.articulationCodes.articulations = { + "a.": { // Staccato + code: "v23", + width: 4, + shift_right: -2, + shift_up: 8, + shift_down: 0, + between_lines: true + }, + "av": { // Staccatissimo + code: "v28", + width: 4, + shift_right: 0, + shift_up: 11, + shift_down: 5, + between_lines: true + }, + "a>": { // Accent + code: "v42", + width: 10, + shift_right: 5, + shift_up: 8, + shift_down: 1, + between_lines: true + }, + "a-": { // Tenuto + code: "v25", + width: 9, + shift_right: -4, + shift_up: 17, + shift_down: 10, + between_lines: true + }, + "a^": { // Marcato + code: "va", + width: 8, + shift_right: 0, + shift_up: -4, + shift_down: -2, + between_lines: false + }, + "a+": { // Left hand pizzicato + code: "v8b", + width: 9, + shift_right: -4, + shift_up: 12, + shift_down: 12, + between_lines: false + }, + "ao": { // Snap pizzicato + code: "v94", + width: 8, + shift_right: 0, + shift_up: -4, + shift_down: 6, + between_lines: false + }, + "ah": { // Natural harmonic or open note + code: "vb9", + width: 7, + shift_right: 0, + shift_up: -4, + shift_down: 4, + between_lines: false + }, + "a@a": { // Fermata above staff + code: "v43", + width: 25, + shift_right: 0, + shift_up: 8, + shift_down: 10, + between_lines: false + }, + "a@u": { // Fermata below staff + code: "v5b", + width: 25, + shift_right: 0, + shift_up: 0, + shift_down: -4, + between_lines: false + }, + "a|": { // Bow up - up stroke + code: "v75", + width: 8, + shift_right: 0, + shift_up: 8, + shift_down: 10, + between_lines: false + }, + "am": { // Bow down - down stroke + code: "v97", + width: 13, + shift_right: 0, + shift_up: 10, + shift_down: 12, + between_lines: false + }, + "a,": { // Choked + code: "vb3", + width: 6, + shift_right: 8, + shift_up: -4, + shift_down: 4, + between_lines: false + } +}; + +Vex.Flow.accidentalCodes = function(acc) { + return Vex.Flow.accidentalCodes.accidentals[acc]; +}; + +Vex.Flow.accidentalCodes.accidentals = { + "#": { + code: "v18", + width: 10, + gracenote_width: 4.5, + shift_right: 0, + shift_down: 0 + }, + "##": { + code: "v7f", + width: 13, + gracenote_width: 6, + shift_right: -1, + shift_down: 0 + }, + "b": { + code: "v44", + width: 8, + gracenote_width: 4.5, + shift_right: 0, + shift_down: 0 + }, + "bb": { + code: "v26", + width: 14, + gracenote_width: 8, + shift_right: -3, + shift_down: 0 + }, + "n": { + code: "v4e", + width: 8, + gracenote_width: 4.5, + shift_right: 0, + shift_down: 0 + }, + "{": { // Left paren for cautionary accidentals + code: "v9c", + width: 5, + shift_right: 2, + shift_down: 0 + }, + "}": { // Right paren for cautionary accidentals + code: "v84", + width: 5, + shift_right: 0, + shift_down: 0 + }, + "db": { + code: "v9e", + width: 16, + shift_right: 0, + shift_down: 0 + }, + "d": { + code: "vab", + width: 10, + shift_right: 0, + shift_down: 0 + }, + "bbs": { + code: "v90", + width: 13, + shift_right: 0, + shift_down: 0 + }, + "++": { + code: "v51", + width: 13, + shift_right: 0, + shift_down: 0 + }, + "+": { + code: "v78", + width: 8, + shift_right: 0, + shift_down: 0 + } +}; + +Vex.Flow.keySignature = function(spec) { + var keySpec = Vex.Flow.keySignature.keySpecs[spec]; + + if (!keySpec) { + throw new Vex.RERR("BadKeySignature", + "Bad key signature spec: '" + spec + "'"); + } + + if (!keySpec.acc) { + return []; + } + + var code = Vex.Flow.accidentalCodes.accidentals[keySpec.acc].code; + var notes = Vex.Flow.keySignature.accidentalList(keySpec.acc); + + var acc_list = []; + for (var i = 0; i < keySpec.num; ++i) { + var line = notes[i]; + acc_list.push({glyphCode: code, line: line}); + } + + return acc_list; +}; + +Vex.Flow.keySignature.keySpecs = { + "C": {acc: null, num: 0}, + "Am": {acc: null, num: 0}, + "F": {acc: "b", num: 1}, + "Dm": {acc: "b", num: 1}, + "Bb": {acc: "b", num: 2}, + "Gm": {acc: "b", num: 2}, + "Eb": {acc: "b", num: 3}, + "Cm": {acc: "b", num: 3}, + "Ab": {acc: "b", num: 4}, + "Fm": {acc: "b", num: 4}, + "Db": {acc: "b", num: 5}, + "Bbm": {acc: "b", num: 5}, + "Gb": {acc: "b", num: 6}, + "Ebm": {acc: "b", num: 6}, + "Cb": {acc: "b", num: 7}, + "Abm": {acc: "b", num: 7}, + "G": {acc: "#", num: 1}, + "Em": {acc: "#", num: 1}, + "D": {acc: "#", num: 2}, + "Bm": {acc: "#", num: 2}, + "A": {acc: "#", num: 3}, + "F#m": {acc: "#", num: 3}, + "E": {acc: "#", num: 4}, + "C#m": {acc: "#", num: 4}, + "B": {acc: "#", num: 5}, + "G#m": {acc: "#", num: 5}, + "F#": {acc: "#", num: 6}, + "D#m": {acc: "#", num: 6}, + "C#": {acc: "#", num: 7}, + "A#m": {acc: "#", num: 7} +}; + +Vex.Flow.unicode = { + // Unicode accidentals + "sharp": String.fromCharCode(parseInt('266F', 16)), + "flat" : String.fromCharCode(parseInt('266D', 16)), + "natural": String.fromCharCode(parseInt('266E', 16)), + // Major Chord + "triangle": String.fromCharCode(parseInt('25B3', 16)), + // half-diminished + "o-with-slash": String.fromCharCode(parseInt('00F8', 16)), + // Diminished + "degrees": String.fromCharCode(parseInt('00B0', 16)), + "circle": String.fromCharCode(parseInt('25CB', 16)) +}; + +Vex.Flow.keySignature.accidentalList = function(acc) { + if (acc == "b") { + return [2, 0.5, 2.5, 1, 3, 1.5, 3.5]; + } + else if (acc == "#") { + return [0, 1.5, -0.5, 1, 2.5, 0.5, 2]; } +}; + +Vex.Flow.parseNoteDurationString = function(durationString) { + if (typeof(durationString) !== "string") { + return null; + } + + var regexp = /(\d+|[a-z])(d*)([nrhms]|$)/; + + var result = regexp.exec(durationString); + if (!result) { + return null; + } + + var duration = result[1]; + var dots = result[2].length; + var type = result[3]; + + if (type.length === 0) { + type = "n"; + } + + return { + duration: duration, + dots: dots, + type: type + }; +}; + +Vex.Flow.parseNoteData = function(noteData) { + var duration = noteData.duration; + + // Preserve backwards-compatibility + var durationStringData = Vex.Flow.parseNoteDurationString(duration); + if (!durationStringData) { + return null; + } + + var ticks = Vex.Flow.durationToTicks(durationStringData.duration); + if (ticks == null) { + return null; + } + + var type = noteData.type; + + if (type) { + if (!(type === "n" || type === "r" || type === "h" || + type === "m" || type === "s")) { + return null; + } + } else { + type = durationStringData.type; + if (!type) { + type = "n"; + } + } + + var dots = 0; + if (noteData.dots) { + dots = noteData.dots; + } else { + dots = durationStringData.dots; + } + + if (typeof(dots) !== "number") { + return null; + } + + var currentTicks = ticks; + + for (var i = 0; i < dots; i++) { + if (currentTicks <= 1) { + return null; + } + + currentTicks = currentTicks / 2; + ticks += currentTicks; + } + + return { + duration: durationStringData.duration, + type: type, + dots: dots, + ticks: ticks + }; +}; + +Vex.Flow.durationToTicks = function(duration) { + var alias = Vex.Flow.durationAliases[duration]; + if (alias !== undefined) { + duration = alias; + } + + var ticks = Vex.Flow.durationToTicks.durations[duration]; + if (ticks === undefined) { + return null; + } + + return ticks; +}; + +Vex.Flow.durationToTicks.durations = { + "1": Vex.Flow.RESOLUTION / 1, + "2": Vex.Flow.RESOLUTION / 2, + "4": Vex.Flow.RESOLUTION / 4, + "8": Vex.Flow.RESOLUTION / 8, + "16": Vex.Flow.RESOLUTION / 16, + "32": Vex.Flow.RESOLUTION / 32, + "64": Vex.Flow.RESOLUTION / 64, + "128": Vex.Flow.RESOLUTION / 128, + "256": Vex.Flow.RESOLUTION / 256 +}; + +Vex.Flow.durationAliases = { + "w": "1", + "h": "2", + "q": "4", + + // This is the default duration used to render bars (BarNote). Bars no longer + // consume ticks, so this should be a no-op. + // + // TODO(0xfe): This needs to be cleaned up. + "b": "256" +}; + +Vex.Flow.durationToGlyph = function(duration, type) { + var alias = Vex.Flow.durationAliases[duration]; + if (alias !== undefined) { + duration = alias; + } + + var code = Vex.Flow.durationToGlyph.duration_codes[duration]; + if (code === undefined) { + return null; + } + + if (!type) { + type = "n"; + } + + var glyphTypeProperties = code.type[type]; + if (glyphTypeProperties === undefined) { + return null; + } + + return Vex.Merge(Vex.Merge({}, code.common), glyphTypeProperties); +}; + +Vex.Flow.durationToGlyph.duration_codes = { + "1": { + common: { + head_width: 16, + stem: false, + stem_offset: 0, + flag: false, + stem_up_extension: -Vex.Flow.STEM_HEIGHT, + stem_down_extension: -Vex.Flow.STEM_HEIGHT, + gracenote_stem_up_extension: -Vex.Flow.STEM_HEIGHT, + gracenote_stem_down_extension: -Vex.Flow.STEM_HEIGHT, + tabnote_stem_up_extension: -Vex.Flow.STEM_HEIGHT, + tabnote_stem_down_extension: -Vex.Flow.STEM_HEIGHT, + dot_shiftY: 0, + line_above: 0, + line_below: 0 + }, + type: { + "n": { // Whole note + code_head: "v1d" + }, + "h": { // Whole note harmonic + code_head: "v46" + }, + "m": { // Whole note muted + code_head: "v92", + stem_offset: -3 + }, + "r": { // Whole rest + code_head: "v5c", + head_width: 12, + rest: true, + position: "D/5", + dot_shiftY: 0.5 + }, + "s": { // Whole note slash + // Drawn with canvas primitives + head_width: 15, + position: "B/4" + } + } + }, + "2": { + common: { + head_width: 10, + stem: true, + stem_offset: 0, + flag: false, + stem_up_extension: 0, + stem_down_extension: 0, + gracenote_stem_up_extension: -14, + gracenote_stem_down_extension: -14, + tabnote_stem_up_extension: 0, + tabnote_stem_down_extension: 0, + dot_shiftY: 0, + line_above: 0, + line_below: 0 + }, + type: { + "n": { // Half note + code_head: "v81" + }, + "h": { // Half note harmonic + code_head: "v2d" + }, + "m": { // Half note muted + code_head: "v95", + stem_offset: -3 + }, + "r": { // Half rest + code_head: "vc", + head_width: 12, + stem: false, + rest: true, + position: "B/4", + dot_shiftY: -0.5 + }, + "s": { // Half note slash + // Drawn with canvas primitives + head_width: 15, + position: "B/4" + } + } + }, + "4": { + common: { + head_width: 10, + stem: true, + stem_offset: 0, + flag: false, + stem_up_extension: 0, + stem_down_extension: 0, + gracenote_stem_up_extension: -14, + gracenote_stem_down_extension: -14, + tabnote_stem_up_extension: 0, + tabnote_stem_down_extension: 0, + dot_shiftY: 0, + line_above: 0, + line_below: 0 + }, + type: { + "n": { // Quarter note + code_head: "vb" + }, + "h": { // Quarter harmonic + code_head: "v22" + }, + "m": { // Quarter muted + code_head: "v3e", + stem_offset: -3 + }, + "r": { // Quarter rest + code_head: "v7c", + head_width: 8, + stem: false, + rest: true, + position: "B/4", + dot_shiftY: -0.5, + line_above: 1.5, + line_below: 1.5 + }, + "s": { // Quarter slash + // Drawn with canvas primitives + head_width: 15, + position: "B/4" + } + } + }, + "8": { + common: { + head_width: 10, + stem: true, + stem_offset: 0, + flag: true, + beam_count: 1, + code_flag_upstem: "v54", + code_flag_downstem: "v9a", + stem_up_extension: 0, + stem_down_extension: 0, + gracenote_stem_up_extension: -14, + gracenote_stem_down_extension: -14, + tabnote_stem_up_extension: 0, + tabnote_stem_down_extension: 0, + dot_shiftY: 0, + line_above: 0, + line_below: 0 + }, + type: { + "n": { // Eighth note + code_head: "vb" + }, + "h": { // Eighth note harmonic + code_head: "v22" + }, + "m": { // Eighth note muted + code_head: "v3e" + }, + "r": { // Eighth rest + code_head: "va5", + stem: false, + flag: false, + rest: true, + position: "B/4", + dot_shiftY: -0.5, + line_above: 1.0, + line_below: 1.0 + }, + "s": { // Eight slash + // Drawn with canvas primitives + head_width: 15, + position: "B/4" + } + } + }, + "16": { + common: { + beam_count: 2, + head_width: 10, + stem: true, + stem_offset: 0, + flag: true, + code_flag_upstem: "v3f", + code_flag_downstem: "v8f", + stem_up_extension: 4, + stem_down_extension: 0, + gracenote_stem_up_extension: -14, + gracenote_stem_down_extension: -14, + tabnote_stem_up_extension: 0, + tabnote_stem_down_extension: 0, + dot_shiftY: 0, + line_above: 0, + line_below: 0 + }, + type: { + "n": { // Sixteenth note + code_head: "vb" + }, + "h": { // Sixteenth note harmonic + code_head: "v22" + }, + "m": { // Sixteenth note muted + code_head: "v3e" + }, + "r": { // Sixteenth rest + code_head: "v3c", + head_width: 13, + stem: false, + flag: false, + rest: true, + position: "B/4", + dot_shiftY: -0.5, + line_above: 1.0, + line_below: 2.0 + }, + "s": { // Sixteenth slash + // Drawn with canvas primitives + head_width: 15, + position: "B/4" + } + } + }, + "32": { + common: { + beam_count: 3, + head_width: 10, + stem: true, + stem_offset: 0, + flag: true, + code_flag_upstem: "v47", + code_flag_downstem: "v2a", + stem_up_extension: 13, + stem_down_extension: 9, + gracenote_stem_up_extension: -12, + gracenote_stem_down_extension: -12, + tabnote_stem_up_extension: 9, + tabnote_stem_down_extension: 5, + dot_shiftY: 0, + line_above: 0, + line_below: 0 + }, + type: { + "n": { // Thirty-second note + code_head: "vb" + }, + "h": { // Thirty-second harmonic + code_head: "v22" + }, + "m": { // Thirty-second muted + code_head: "v3e" + }, + "r": { // Thirty-second rest + code_head: "v55", + head_width: 16, + stem: false, + flag: false, + rest: true, + position: "B/4", + dot_shiftY: -1.5, + line_above: 2.0, + line_below: 2.0 + }, + "s": { // Thirty-second slash + // Drawn with canvas primitives + head_width: 15, + position: "B/4" + } + } + }, + "64": { + common: { + beam_count: 4, + head_width: 10, + stem: true, + stem_offset: 0, + flag: true, + code_flag_upstem: "va9", + code_flag_downstem: "v58", + stem_up_extension: 17, + stem_down_extension: 13, + gracenote_stem_up_extension: -10, + gracenote_stem_down_extension: -10, + tabnote_stem_up_extension: 13, + tabnote_stem_down_extension: 9, + dot_shiftY: 0, + line_above: 0, + line_below: 0 + }, + type: { + "n": { // Sixty-fourth note + code_head: "vb" + }, + "h": { // Sixty-fourth harmonic + code_head: "v22" + }, + "m": { // Sixty-fourth muted + code_head: "v3e" + }, + "r": { // Sixty-fourth rest + code_head: "v38", + head_width: 18, + stem: false, + flag: false, + rest: true, + position: "B/4", + dot_shiftY: -1.5, + line_above: 2.0, + line_below: 3.0 + }, + "s": { // Sixty-fourth slash + // Drawn with canvas primitives + head_width: 15, + position: "B/4" + } + } + }, + "128": { + common: { + beam_count: 5, + head_width: 10, + stem: true, + stem_offset:0, + flag: true, + code_flag_upstem: "v9b", + code_flag_downstem: "v30", + stem_up_extension: 26, + stem_down_extension: 22, + gracenote_stem_up_extension: -8, + gracenote_stem_down_extension: -8, + tabnote_stem_up_extension: 22, + tabnote_stem_down_extension: 18, + dot_shiftY: 0, + line_above: 0, + line_below: 0 + }, + type: { + "n": { // Hundred-twenty-eight note + code_head: "vb" + }, + "h": { // Hundred-twenty-eight harmonic + code_head: "v22" + }, + "m": { // Hundred-twenty-eight muted + code_head: "v3e" + }, + "r": { // Hundred-twenty-eight rest + code_head: "vaa", + head_width: 20, + stem: false, + flag: false, + rest: true, + position: "B/4", + dot_shiftY: 1.5, + line_above: 3.0, + line_below: 3.0 + }, + "s": { // Hundred-twenty-eight rest + // Drawn with canvas primitives + head_width: 15, + position: "B/4" + } + } + } +}; + +// Some defaults +Vex.Flow.TIME4_4 = { + num_beats: 4, + beat_value: 4, + resolution: Vex.Flow.RESOLUTION +}; +Vex.Flow.Font = {"glyphs":{"v0":{"x_min":0,"x_max":514.5,"ha":525,"o":"m 236 648 b 246 648 238 648 242 648 b 288 646 261 648 283 648 b 472 513 364 634 428 587 b 514 347 502 464 514 413 b 462 163 514 272 499 217 b 257 44 409 83 333 44 b 50 163 181 44 103 83 b 0 347 14 217 0 272 b 40 513 0 413 12 464 b 236 648 87 591 155 638 m 277 614 b 253 616 273 616 261 616 b 242 616 247 616 243 616 b 170 499 193 609 181 589 b 159 348 163 446 159 398 b 166 222 159 308 161 266 b 201 91 174 138 183 106 b 257 76 215 81 235 76 b 311 91 277 76 299 81 b 347 222 330 106 338 138 b 353 348 352 266 353 308 b 344 499 353 398 351 446 b 277 614 333 587 322 606 m 257 -1 l 258 -1 l 255 -1 l 257 -1 m 257 673 l 258 673 l 255 673 l 257 673 "},"v1":{"x_min":-1.359375,"x_max":344.359375,"ha":351,"o":"m 126 637 l 129 638 l 198 638 l 266 638 l 269 635 b 274 631 272 634 273 632 l 277 627 l 277 395 b 279 156 277 230 277 161 b 329 88 281 123 295 106 b 344 69 341 81 344 79 b 337 55 344 62 343 59 l 333 54 l 197 54 l 61 54 l 58 55 b 50 69 53 59 50 62 b 65 88 50 79 53 81 b 80 97 72 91 74 93 b 117 156 103 113 112 129 b 117 345 117 161 117 222 l 117 528 l 100 503 l 38 406 b 14 383 24 384 23 383 b -1 398 5 383 -1 390 b 4 415 -1 403 1 409 b 16 437 5 416 10 426 l 72 539 l 100 596 b 121 632 119 631 119 631 b 126 637 122 634 125 635 m 171 -1 l 172 -1 l 170 -1 l 171 -1 m 171 673 l 172 673 l 170 673 l 171 673 "},"v2":{"x_min":-1.359375,"x_max":458.6875,"ha":468,"o":"m 197 648 b 216 648 201 648 208 648 b 258 646 232 648 253 648 b 419 546 333 637 393 599 b 432 489 428 528 432 509 b 356 342 432 440 405 384 b 235 278 322 313 288 295 b 69 170 166 256 107 217 b 69 169 69 170 69 169 b 69 169 69 169 69 169 b 74 173 69 169 72 170 b 209 222 112 204 163 222 b 310 195 247 222 274 215 b 371 179 332 184 352 179 b 396 181 379 179 387 179 b 428 202 409 184 423 194 b 442 212 431 209 436 212 b 458 197 450 212 458 206 b 441 148 458 190 449 165 b 299 44 409 84 353 44 b 288 45 295 44 292 44 b 250 61 274 45 268 49 b 122 99 212 86 164 99 b 73 91 104 99 88 97 b 28 63 53 84 34 72 b 14 54 25 56 20 54 b 1 62 9 54 4 56 l -1 65 l -1 79 b 0 99 -1 91 0 95 b 2 113 1 102 2 108 b 164 309 20 197 81 272 b 285 470 232 341 277 398 b 287 487 287 476 287 481 b 171 595 287 551 239 595 b 155 595 166 595 160 595 b 142 592 145 594 142 594 b 145 589 142 592 142 591 b 179 527 168 576 179 551 b 132 455 179 496 163 467 b 104 451 122 452 112 451 b 27 530 62 451 27 487 b 29 555 27 538 27 546 b 197 648 44 601 115 639 m 228 -1 l 230 -1 l 227 -1 l 228 -1 m 228 673 l 230 673 l 227 673 l 228 673 "},"v3":{"x_min":-1.359375,"x_max":409.6875,"ha":418,"o":"m 174 648 b 191 648 176 648 183 648 b 225 648 204 648 220 648 b 402 523 317 638 389 588 b 404 503 404 517 404 510 b 402 484 404 495 404 488 b 264 373 389 437 334 394 b 257 370 259 371 257 371 b 257 370 257 370 257 370 b 264 369 258 370 261 369 b 409 202 359 334 409 267 b 318 72 409 152 381 104 b 200 43 281 52 240 43 b 23 113 134 43 69 68 b 0 169 6 129 0 149 b 77 249 0 210 29 249 l 77 249 b 152 174 125 249 152 212 b 103 102 152 145 137 116 b 103 102 103 102 103 102 b 147 94 103 101 132 95 b 153 94 149 94 151 94 b 265 206 219 94 265 141 b 264 226 265 213 265 219 b 147 355 253 299 204 353 b 126 371 133 356 126 362 b 147 388 126 383 132 388 b 254 474 196 391 238 424 b 259 502 258 484 259 494 b 182 592 259 544 228 582 b 156 595 175 595 166 595 b 115 592 142 595 129 594 l 111 591 l 115 588 b 152 524 141 574 152 549 b 92 449 152 491 130 458 b 76 448 87 448 81 448 b -1 530 32 448 -1 488 b 20 581 -1 548 5 566 b 174 648 55 619 108 641 m 204 -1 l 205 -1 l 202 -1 l 204 -1 m 204 673 l 205 673 l 202 673 l 204 673 "},"v4":{"x_min":0,"x_max":468.21875,"ha":478,"o":"m 174 637 b 232 638 175 638 189 638 b 277 638 245 638 259 638 l 378 638 l 381 635 b 389 623 386 632 389 627 b 382 609 389 617 386 613 b 366 589 381 606 372 598 l 313 528 l 245 451 l 209 410 l 155 348 l 84 267 b 59 240 72 252 59 240 b 59 240 59 240 59 240 b 151 238 59 238 68 238 l 242 238 l 242 303 b 243 371 242 369 242 370 b 289 426 245 374 254 385 l 303 441 l 317 456 l 338 483 l 360 506 l 371 520 b 386 527 375 526 381 527 b 400 519 392 527 397 524 b 401 440 401 516 401 514 b 401 377 401 423 401 402 l 401 238 l 426 238 b 453 237 449 238 450 238 b 465 217 461 234 465 226 b 460 202 465 212 464 206 b 426 197 454 197 453 197 l 401 197 l 401 180 b 451 88 402 129 412 109 b 468 69 465 81 468 79 b 461 55 468 62 466 59 l 458 54 l 321 54 l 185 54 l 182 55 b 175 69 176 59 175 62 b 191 88 175 79 176 81 b 240 180 230 109 240 129 l 240 197 l 125 197 b 73 195 104 195 87 195 b 8 197 10 195 9 197 b 0 212 2 199 0 205 b 0 212 0 212 0 212 b 20 242 0 219 0 219 b 163 610 104 344 163 492 b 174 637 163 628 166 634 m 234 -1 l 235 -1 l 232 -1 l 234 -1 m 234 673 l 235 673 l 232 673 l 234 673 "},"v5":{"x_min":0,"x_max":409.6875,"ha":418,"o":"m 47 637 b 53 638 49 638 50 638 b 69 634 55 638 61 637 b 210 610 114 619 161 610 b 363 634 259 610 311 619 b 382 638 372 637 378 638 b 392 634 386 638 389 637 b 397 623 396 630 397 627 b 393 610 397 620 396 616 b 298 505 368 552 338 520 b 212 494 277 498 246 494 b 65 517 163 494 106 502 b 61 517 62 517 61 517 b 61 517 61 517 61 517 b 51 408 61 517 51 412 b 51 408 51 408 51 408 b 51 408 51 408 51 408 b 61 412 53 408 55 409 b 125 434 80 421 103 430 b 185 441 145 440 166 441 b 409 244 310 441 409 353 b 401 191 409 227 406 209 b 197 43 375 105 287 43 b 159 47 183 43 171 44 b 23 123 112 56 61 86 b 0 180 6 140 0 159 b 76 260 0 220 31 260 b 92 259 81 260 87 259 b 152 183 132 251 152 216 b 100 112 152 152 134 122 b 95 111 98 112 95 111 b 95 111 95 111 95 111 b 129 98 95 109 119 101 b 148 97 136 97 141 97 b 264 235 206 97 261 158 b 265 248 265 240 265 244 b 210 398 265 312 243 373 b 179 408 201 406 194 408 b 174 408 178 408 176 408 b 53 369 130 408 88 394 b 34 359 39 359 38 359 b 17 374 24 359 17 365 b 39 628 17 384 38 625 b 47 637 40 631 43 635 m 204 -1 l 205 -1 l 202 -1 l 204 -1 m 204 673 l 205 673 l 202 673 l 204 673 "},"v6":{"x_min":0,"x_max":475.03125,"ha":485,"o":"m 255 648 b 274 648 259 648 266 648 b 314 646 288 648 307 648 b 450 555 374 637 438 594 b 454 530 453 546 454 538 b 375 451 454 485 416 451 b 328 467 359 451 343 455 b 300 526 310 483 300 503 b 352 598 300 557 319 589 b 356 599 355 598 356 599 b 352 602 356 599 355 601 b 288 616 330 612 308 616 b 210 584 257 616 230 605 b 164 433 189 559 174 508 b 160 374 163 415 160 381 b 160 374 160 374 160 374 b 160 374 160 374 160 374 b 168 377 160 374 164 376 b 258 395 200 390 228 395 b 366 367 294 395 328 387 b 475 223 436 333 475 283 b 472 197 475 215 473 206 b 349 65 462 141 419 95 b 259 43 317 51 288 43 b 167 69 230 43 200 52 b 4 290 80 113 20 195 b 0 349 1 309 0 328 b 20 467 0 391 6 433 b 255 648 58 563 155 637 m 269 363 b 257 363 265 363 261 363 b 210 345 236 363 220 356 b 186 226 196 324 186 272 b 187 198 186 216 186 206 b 213 95 191 151 202 112 b 257 76 221 83 238 76 b 270 77 261 76 266 76 b 321 156 299 81 310 99 b 329 229 326 183 329 206 b 321 301 329 252 326 274 b 269 363 311 342 298 359 m 236 -1 l 238 -1 l 235 -1 l 236 -1 m 236 673 l 238 673 l 235 673 l 236 673 "},"v7":{"x_min":0,"x_max":442.359375,"ha":451,"o":"m 147 648 b 166 649 153 649 160 649 b 313 598 217 649 273 630 b 340 587 323 588 328 587 l 341 587 b 412 628 367 587 390 601 b 427 638 416 635 421 638 b 439 632 431 638 435 637 b 442 623 441 630 442 628 b 430 569 442 616 439 603 b 352 369 408 492 377 410 b 300 259 325 324 313 298 b 273 84 283 205 273 140 b 265 55 273 65 272 59 l 261 54 l 181 54 l 99 54 l 96 55 b 91 61 95 56 92 59 l 89 63 l 89 77 b 147 263 89 133 111 202 b 261 401 176 313 212 355 b 378 541 315 449 349 489 l 382 548 l 375 544 b 240 495 333 512 285 495 b 129 535 198 495 160 509 b 84 560 108 552 95 560 b 76 559 81 560 78 560 b 31 487 59 555 43 530 b 14 470 27 473 24 470 b 1 477 8 470 4 471 l 0 480 l 0 553 l 0 627 l 1 630 b 16 638 4 635 9 638 b 23 635 17 638 20 637 b 49 626 36 626 39 626 b 96 638 59 626 80 630 b 104 639 99 638 102 639 b 117 644 107 641 112 642 b 147 648 125 645 137 648 m 220 -1 l 221 -1 l 219 -1 l 220 -1 m 220 673 l 221 673 l 219 673 l 220 673 "},"v8":{"x_min":0,"x_max":488.640625,"ha":499,"o":"m 217 648 b 245 649 225 648 235 649 b 453 516 343 649 430 595 b 458 478 455 503 458 491 b 412 370 458 440 441 398 b 411 369 412 369 411 369 b 415 365 411 367 412 367 b 488 231 462 331 488 281 b 472 165 488 208 483 186 b 243 43 434 86 338 43 b 63 104 178 43 112 62 b 0 233 20 140 0 186 b 73 365 0 283 24 331 l 77 369 l 72 374 b 29 476 42 406 29 441 b 217 648 29 557 103 635 m 258 605 b 242 606 253 605 247 606 b 157 552 198 606 157 580 b 160 541 157 548 159 544 b 319 413 176 503 242 452 l 337 403 l 338 406 b 359 476 352 428 359 452 b 258 605 359 537 318 595 m 138 326 b 130 330 134 328 130 330 b 130 330 130 330 130 330 b 107 305 127 330 112 313 b 84 231 91 281 84 256 b 243 86 84 156 151 86 b 249 87 245 86 246 87 b 347 156 303 88 347 120 b 344 172 347 162 345 167 b 156 319 325 227 257 281 b 138 326 151 322 144 324 m 243 -1 l 245 -1 l 242 -1 l 243 -1 m 243 673 l 245 673 l 242 673 l 243 673 "},"v9":{"x_min":0,"x_max":475.03125,"ha":485,"o":"m 191 646 b 212 649 198 648 205 649 b 255 644 227 649 243 646 b 458 448 348 616 428 539 b 475 342 469 415 475 378 b 460 244 475 308 469 274 b 193 44 421 124 303 44 b 91 69 157 44 122 51 b 19 161 43 97 19 126 b 21 181 19 167 20 174 b 98 241 32 220 65 241 b 170 186 129 241 160 223 b 172 166 171 179 172 173 b 121 94 172 134 152 102 b 117 93 118 94 117 93 b 121 90 117 93 118 91 b 185 76 142 80 164 76 b 270 119 220 76 251 91 b 308 259 287 145 300 194 b 313 317 310 277 313 310 b 313 317 313 317 313 317 b 313 317 313 317 313 317 b 304 315 313 317 308 316 b 216 295 273 302 245 295 b 145 308 193 295 170 299 b 19 398 88 327 42 360 b 0 469 5 420 0 444 b 24 551 0 496 8 526 b 191 646 54 596 125 637 m 227 614 b 215 616 224 616 220 616 b 202 614 210 616 206 616 b 152 535 174 610 163 592 b 144 463 147 509 144 485 b 152 391 144 440 147 417 b 216 328 163 344 179 328 b 280 391 253 328 269 344 b 288 463 285 417 288 440 b 280 535 288 485 285 509 b 227 614 269 594 258 610 m 236 -1 l 238 -1 l 235 -1 l 236 -1 m 236 673 l 238 673 l 235 673 l 236 673 "},"va":{"x_min":-149.71875,"x_max":148.359375,"ha":151,"o":"m -8 -1 b -1 0 -5 -1 -4 0 b 16 -11 5 0 13 -4 b 83 -186 17 -12 47 -90 l 148 -358 l 148 -363 b 127 -385 148 -376 138 -385 b 112 -378 122 -385 118 -383 b 54 -226 110 -374 114 -385 b 0 -81 24 -147 0 -81 b -55 -226 -1 -81 -25 -147 b -114 -378 -115 -385 -111 -374 b -129 -385 -119 -383 -123 -385 b -149 -363 -140 -385 -149 -376 l -149 -358 l -84 -186 b -19 -11 -49 -90 -19 -12 b -8 -1 -17 -8 -12 -4 "},"vb":{"x_min":0,"x_max":428.75,"ha":438,"o":"m 262 186 b 273 186 266 186 272 186 b 274 186 273 186 274 186 b 285 186 274 186 280 186 b 428 48 375 181 428 122 b 386 -68 428 12 416 -29 b 155 -187 329 -145 236 -187 b 12 -111 92 -187 38 -162 b 0 -51 4 -91 0 -72 b 262 186 0 58 122 179 "},"vc":{"x_min":0,"x_max":447.8125,"ha":457,"o":"m 0 86 l 0 173 l 223 173 l 447 173 l 447 86 l 447 0 l 223 0 l 0 0 l 0 86 "},"vf":{"x_min":0,"x_max":370.21875,"ha":378,"o":"m 0 0 l 0 277 l 61 277 l 122 277 l 122 0 l 122 -278 l 61 -278 l 0 -278 l 0 0 m 246 -1 l 246 277 l 308 277 l 370 277 l 370 -1 l 370 -278 l 308 -278 l 246 -278 l 246 -1 "},"v10":{"x_min":0,"x_max":559.421875,"ha":571,"o":"m 5 127 b 14 127 6 127 9 127 b 51 126 25 127 43 127 b 175 98 93 122 138 112 l 186 94 b 279 51 210 86 255 65 b 285 47 280 51 283 48 b 319 27 291 44 311 31 l 326 22 b 359 0 332 19 352 4 l 367 -6 b 371 -9 368 -6 370 -8 l 379 -15 b 387 -22 383 -18 386 -20 l 398 -30 l 411 -40 l 417 -47 l 427 -55 l 434 -61 b 441 -66 436 -62 439 -65 l 446 -72 l 453 -77 l 462 -87 b 558 -188 490 -113 549 -176 b 559 -195 559 -191 559 -194 b 548 -205 559 -201 555 -205 b 541 -204 547 -205 544 -205 b 534 -198 539 -201 536 -199 l 525 -191 b 481 -162 518 -187 490 -167 b 472 -155 477 -159 472 -156 b 468 -152 470 -155 469 -154 b 461 -149 466 -152 464 -151 b 428 -130 454 -145 441 -137 b 371 -99 413 -122 372 -99 b 363 -95 371 -99 367 -98 b 353 -91 357 -94 353 -91 b 348 -90 353 -91 352 -91 b 332 -81 343 -87 341 -86 b 27 -12 230 -37 127 -13 b 0 -5 4 -11 2 -11 b 0 58 0 -2 0 27 b 0 122 0 88 0 120 b 5 127 1 124 4 126 "},"v11":{"x_min":-155.171875,"x_max":153.8125,"ha":157,"o":"m -137 353 b -130 353 -136 353 -133 353 b -112 349 -125 353 -119 352 b -100 342 -110 347 -104 344 b 0 317 -69 326 -35 317 b 111 349 38 317 76 328 b 129 353 117 352 123 353 b 153 327 142 353 153 344 b 144 302 153 320 153 317 b 27 6 93 226 50 113 b 21 -13 24 -11 24 -11 b 0 -26 17 -22 8 -26 b -24 -12 -9 -26 -19 -22 b -28 5 -24 -9 -27 -2 b -145 302 -53 117 -95 224 b -155 327 -155 317 -155 320 b -137 353 -155 340 -148 349 "},"v18":{"x_min":0,"x_max":323.9375,"ha":331,"o":"m 217 535 b 225 537 220 537 221 537 b 245 524 235 537 242 533 l 246 521 l 247 390 l 247 258 l 273 265 b 306 270 288 269 299 270 b 322 259 315 270 319 267 b 323 208 323 256 323 233 b 322 158 323 184 323 159 b 288 140 318 148 315 147 b 247 130 254 131 247 130 b 247 65 247 130 247 104 b 247 20 247 51 247 36 l 247 -88 l 273 -81 b 306 -76 289 -77 299 -76 b 318 -81 311 -76 315 -77 b 323 -123 323 -87 323 -86 l 323 -138 l 323 -154 b 318 -195 323 -191 323 -190 b 269 -210 314 -199 315 -199 b 249 -216 259 -213 250 -216 l 247 -216 l 247 -349 l 246 -483 l 245 -487 b 225 -499 242 -495 234 -499 b 206 -487 219 -499 210 -495 l 205 -483 l 205 -355 l 205 -227 l 204 -227 l 181 -233 l 138 -244 b 117 -249 127 -247 117 -249 b 115 -385 115 -249 115 -256 l 115 -523 l 114 -526 b 95 -538 110 -534 102 -538 b 74 -526 87 -538 78 -534 l 73 -523 l 73 -391 b 72 -260 73 -269 73 -260 b 72 -260 72 -260 72 -260 b 19 -273 61 -263 23 -273 b 0 -260 10 -273 4 -267 b 0 -209 0 -256 0 -256 l 0 -162 l 1 -158 b 61 -134 5 -148 5 -148 l 73 -131 l 73 -22 b 72 86 73 79 73 86 b 72 86 72 86 72 86 b 19 74 61 83 23 74 b 0 86 10 74 4 79 b 0 137 0 90 0 90 l 0 184 l 1 188 b 61 212 5 198 5 198 l 73 215 l 73 348 l 73 481 l 74 485 b 95 498 78 492 87 498 b 103 495 98 498 100 496 b 114 485 107 494 111 489 l 115 481 l 115 353 l 115 226 l 121 226 b 159 235 123 227 141 231 l 198 247 l 205 248 l 205 384 l 205 521 l 206 524 b 217 535 209 528 212 533 m 205 9 b 205 119 205 70 205 119 l 205 119 b 182 113 204 119 194 116 l 138 102 b 117 97 127 99 117 97 b 115 -12 115 97 115 91 l 115 -122 l 121 -120 b 159 -111 123 -119 141 -115 l 198 -101 l 205 -98 l 205 9 "},"v1b":{"x_min":0,"x_max":559.421875,"ha":571,"o":"m 544 204 b 548 204 545 204 547 204 b 559 194 555 204 559 199 b 559 190 559 192 559 191 b 530 156 559 188 556 184 b 462 86 510 134 481 104 b 453 76 458 81 454 77 l 446 70 l 441 65 b 434 59 439 63 436 61 l 427 54 b 409 37 426 51 416 44 b 392 23 398 29 394 26 b 387 19 389 22 387 20 b 379 13 386 19 383 16 l 371 8 l 367 5 l 359 -1 l 337 -16 b 285 -48 319 -29 298 -41 l 279 -52 b 186 -95 255 -66 210 -87 l 175 -99 b 23 -129 127 -117 68 -129 b 17 -129 20 -129 19 -129 b 1 -123 2 -129 2 -129 b 0 -49 0 -122 0 -83 b 0 4 0 -22 0 1 b 27 11 2 9 4 9 b 185 31 78 12 145 20 b 198 34 186 31 193 33 b 314 73 234 44 277 58 b 349 88 328 79 340 84 b 353 90 352 90 353 90 b 363 94 353 90 357 93 b 371 98 367 97 371 98 b 428 129 372 98 413 120 b 461 148 441 136 454 144 b 468 151 464 149 466 151 b 472 154 469 152 470 154 b 481 161 473 155 477 158 b 525 190 490 166 518 186 l 534 197 b 540 201 536 198 539 199 b 544 204 541 202 544 204 "},"v1d":{"x_min":0,"x_max":619.3125,"ha":632,"o":"m 274 184 b 307 186 285 186 296 186 b 616 22 465 186 597 116 b 619 -1 617 13 619 5 b 308 -187 619 -104 483 -187 b 0 -1 133 -187 0 -102 b 5 36 0 11 1 23 b 274 184 29 115 141 176 m 289 161 b 272 162 284 162 277 162 b 171 41 209 162 171 108 b 205 -73 171 5 182 -34 b 345 -163 243 -133 298 -163 b 436 -98 385 -163 420 -142 b 446 -43 443 -80 446 -62 b 289 161 446 47 377 147 "},"v1e":{"x_min":-402.890625,"x_max":401.53125,"ha":410,"o":"m -219 173 b -213 174 -217 174 -215 174 b -202 173 -209 174 -205 173 b -114 86 -200 172 -179 151 b -28 0 -66 37 -28 0 b 40 84 -28 0 2 37 b 117 174 111 173 110 172 b 122 174 118 174 119 174 b 132 173 125 174 129 173 b 295 11 134 172 171 134 l 307 -1 l 336 34 b 374 76 366 72 368 74 b 381 77 375 77 378 77 b 401 56 392 77 401 68 b 400 48 401 54 401 51 b 223 -172 397 41 230 -166 b 210 -176 220 -174 215 -176 b 201 -174 206 -176 204 -176 b 112 -87 198 -173 178 -152 b 27 0 65 -38 27 0 b -42 -86 27 0 -4 -38 b -118 -174 -112 -174 -111 -173 b -123 -176 -119 -176 -121 -176 b -133 -174 -126 -176 -130 -174 b -296 -12 -136 -173 -172 -137 l -308 0 l -337 -34 b -375 -77 -367 -73 -370 -76 b -382 -79 -377 -79 -379 -79 b -402 -58 -393 -79 -402 -69 b -401 -49 -402 -55 -402 -52 b -224 172 -398 -43 -228 167 b -219 173 -223 172 -220 173 "},"v1f":{"x_min":-340.28125,"x_max":338.921875,"ha":346,"o":"m -32 520 b -29 521 -31 520 -31 521 b -23 519 -27 521 -24 520 b -20 513 -21 517 -20 516 b -21 506 -20 512 -20 509 b -31 474 -23 502 -27 488 l -53 402 l -66 352 l -68 349 l -57 349 b -32 351 -51 349 -40 351 b 123 370 19 352 74 359 b 137 371 127 370 133 371 b 170 356 152 371 164 366 b 171 355 170 355 170 355 b 216 366 174 355 183 358 b 280 378 268 377 266 377 b 287 378 283 378 284 378 b 332 349 307 378 322 369 b 338 319 336 341 338 330 b 332 301 338 310 336 302 b 242 280 329 299 246 280 b 242 280 242 280 242 280 b 235 288 236 280 235 283 b 235 292 235 290 235 291 b 236 302 236 297 236 299 b 220 337 236 316 230 330 l 216 340 l 210 335 b 159 276 189 322 172 301 b 118 149 152 265 156 274 b 81 34 84 36 85 36 b -8 13 78 33 -4 13 b -8 13 -8 13 -8 13 b -14 20 -12 15 -14 15 b -8 44 -14 24 -12 31 b -2 66 -5 55 -2 65 b -2 66 -2 66 -2 66 l -2 66 b -43 41 -2 66 -21 55 b -114 4 -98 8 -98 8 b -144 0 -123 0 -134 0 b -242 99 -197 0 -242 43 b -242 109 -242 102 -242 105 b -212 219 -240 122 -242 116 b -185 312 -197 270 -185 312 l -185 312 b -189 312 -185 312 -186 312 b -259 312 -200 312 -227 312 b -321 310 -291 312 -310 310 b -334 312 -330 310 -334 312 b -340 319 -338 313 -340 316 b -336 326 -340 322 -338 324 b -291 337 -334 326 -314 331 l -247 347 l -210 348 b -172 348 -190 348 -172 348 b -168 363 -172 348 -171 355 b -145 442 -151 424 -145 441 b -133 452 -144 444 -140 446 l -77 489 b -32 520 -53 506 -32 520 m 57 334 b 53 335 55 335 54 335 b 44 334 50 335 49 335 b -70 316 8 326 -28 320 b -78 309 -78 316 -78 316 b -108 202 -80 305 -88 274 b -141 81 -136 112 -141 93 b -140 74 -141 79 -141 77 b -117 49 -137 59 -127 49 b -107 52 -114 49 -110 51 b 16 127 -106 54 14 126 b 42 217 16 127 42 215 b 49 241 42 222 44 229 b 73 320 53 251 73 317 b 57 334 73 327 65 333 "},"v22":{"x_min":0,"x_max":432.828125,"ha":442,"o":"m 209 186 b 213 187 210 187 212 187 b 216 187 215 187 216 187 b 224 174 216 186 220 180 b 420 -1 269 105 338 43 b 432 -12 431 -8 432 -9 b 421 -23 432 -15 432 -16 b 228 -180 345 -70 264 -137 b 219 -188 221 -188 221 -188 l 219 -188 b 208 -177 215 -188 215 -188 b 10 1 163 -106 93 -44 b 0 11 0 6 0 8 b 10 22 0 13 0 15 b 202 179 87 69 167 136 b 209 186 206 183 209 186 "},"v23":{"x_min":0,"x_max":133.390625,"ha":136,"o":"m 54 66 b 65 68 58 68 61 68 b 122 37 88 68 110 56 b 133 -1 130 26 133 12 b 104 -58 133 -23 123 -44 b 66 -69 92 -65 78 -69 b 10 -38 44 -69 23 -58 b 0 -1 2 -27 0 -13 b 54 66 0 30 20 61 "},"v25":{"x_min":0,"x_max":318.5,"ha":325,"o":"m 20 376 b 167 377 23 377 96 377 b 296 376 231 377 294 377 b 318 347 311 371 318 359 b 296 316 318 333 311 320 b 159 315 294 315 227 315 b 21 316 91 315 24 315 b 0 345 6 320 0 333 b 20 376 0 359 6 371 "},"v26":{"x_min":-21.78125,"x_max":483.1875,"ha":493,"o":"m -8 631 b -1 632 -6 632 -4 632 b 19 620 8 632 16 628 b 20 383 20 616 20 616 l 20 148 l 21 151 b 140 199 59 183 102 199 b 206 179 164 199 187 192 l 210 176 l 210 396 l 210 617 l 212 621 b 231 632 216 628 223 632 b 250 620 239 632 247 628 b 251 383 251 616 251 616 l 251 148 l 254 151 b 370 199 291 183 332 199 b 415 191 385 199 400 197 b 483 84 458 176 483 134 b 461 0 483 58 476 29 b 332 -142 439 -40 411 -72 l 255 -215 b 231 -229 240 -229 239 -229 b 216 -223 224 -229 220 -227 b 210 -158 210 -217 210 -223 b 210 -120 210 -148 210 -136 l 210 -29 l 205 -34 b 100 -142 182 -65 159 -88 l 23 -215 b -1 -229 9 -229 6 -229 b -20 -216 -9 -229 -17 -224 l -21 -212 l -21 201 l -21 616 l -20 620 b -8 631 -17 624 -13 630 m 110 131 b 96 133 106 133 100 133 b 89 133 93 133 91 133 b 24 87 63 129 40 113 l 20 80 l 20 -37 l 20 -156 l 23 -152 b 144 81 96 -72 144 20 l 144 83 b 110 131 144 113 134 126 m 341 131 b 328 133 337 133 332 133 b 322 133 326 133 323 133 b 257 87 296 129 273 113 l 251 80 l 251 -37 l 251 -156 l 255 -152 b 375 81 328 -72 375 20 l 375 83 b 341 131 375 113 367 126 "},"v27":{"x_min":0,"x_max":432.828125,"ha":442,"o":"m 208 184 b 213 187 209 186 212 187 b 224 176 217 187 221 183 b 245 147 225 172 235 159 b 419 -1 288 90 347 38 b 431 -8 424 -4 431 -8 b 432 -12 432 -9 432 -11 b 430 -18 432 -13 432 -16 b 364 -61 424 -20 383 -47 b 225 -183 307 -102 250 -152 b 223 -187 224 -184 223 -187 b 220 -188 221 -188 220 -188 b 208 -176 216 -188 210 -184 b 187 -148 205 -173 197 -159 b 12 0 144 -90 84 -38 b 0 11 4 5 0 8 b 16 24 0 13 4 18 b 183 158 83 69 141 115 b 208 184 194 169 198 173 m 183 105 b 176 113 181 109 176 113 b 172 109 176 113 175 112 b 92 45 149 90 117 62 l 88 41 l 102 31 b 247 -105 160 -6 210 -55 l 254 -115 l 257 -112 l 269 -102 b 340 -45 287 -87 319 -61 l 344 -43 l 330 -33 b 183 105 272 6 221 54 "},"v28":{"x_min":-73.5,"x_max":72.140625,"ha":74,"o":"m -72 252 l -73 254 l 0 254 l 72 254 l 70 252 b 0 -1 70 248 0 -1 b -72 252 -1 -1 -72 248 "},"v29":{"x_min":-590.71875,"x_max":589.359375,"ha":601,"o":"m 175 273 b 182 274 178 273 181 274 b 202 262 190 274 198 269 b 204 158 204 259 204 259 l 204 56 l 250 112 b 303 174 296 172 298 172 b 308 174 304 174 307 174 b 318 173 313 174 317 173 b 481 11 322 172 357 134 l 494 -1 l 522 34 b 560 76 553 72 555 74 b 567 77 563 77 564 77 b 589 56 579 77 589 68 b 586 48 589 54 588 51 b 411 -172 583 41 416 -166 b 397 -176 406 -174 401 -176 b 387 -174 393 -176 390 -176 b 299 -87 386 -173 366 -152 b 213 0 253 -38 213 0 b 208 -6 213 0 210 -2 l 204 -12 l 204 -147 b 204 -210 204 -173 204 -194 b 198 -292 204 -297 204 -287 b 183 -299 194 -297 189 -299 b 164 -287 175 -299 167 -295 b 163 -174 163 -284 163 -284 l 161 -63 l 119 -117 b 65 -176 76 -170 73 -176 b 61 -176 63 -176 62 -176 b -35 -87 51 -174 57 -180 b -121 0 -83 -38 -121 0 b -190 -86 -122 0 -152 -38 b -266 -174 -261 -174 -259 -173 b -272 -176 -268 -176 -270 -176 b -281 -174 -276 -176 -280 -174 b -371 -86 -284 -173 -304 -152 b -457 0 -417 -38 -457 0 l -457 0 b -477 -26 -457 0 -470 -16 b -548 -227 -524 -88 -548 -161 b -536 -303 -548 -254 -544 -280 b -533 -317 -534 -309 -533 -313 b -553 -338 -533 -330 -541 -338 b -577 -315 -566 -338 -571 -333 b -590 -227 -586 -287 -590 -258 b -518 -9 -590 -154 -564 -77 b -465 56 -509 2 -504 8 l -402 134 b -363 174 -374 170 -371 174 b -359 174 -362 174 -360 174 b -262 86 -351 174 -356 179 b -176 0 -216 37 -176 0 b -107 84 -176 0 -145 37 b -31 174 -36 173 -38 172 b -25 174 -29 174 -28 174 b -16 173 -23 174 -19 173 b 147 11 -13 172 35 123 l 157 -1 l 160 1 l 163 4 l 163 130 b 164 260 163 256 163 258 b 175 273 166 266 170 270 "},"v2a":{"x_min":-21.78125,"x_max":366.140625,"ha":374,"o":"m 276 1378 b 284 1379 279 1379 281 1379 b 306 1360 292 1379 298 1374 b 352 1247 326 1326 343 1286 b 366 1139 362 1213 366 1175 b 347 1009 366 1093 359 1049 l 344 1002 l 347 992 b 352 971 348 986 351 977 b 366 863 362 936 366 899 b 347 732 366 818 359 773 l 344 725 l 347 716 b 352 695 348 710 351 700 b 366 588 362 659 366 623 b 223 262 366 464 314 345 b 189 233 212 252 212 252 b 35 76 126 183 73 129 b -1 16 20 56 2 27 b -19 4 -4 9 -12 4 l -21 4 l -21 137 l -21 270 l -17 270 b 186 344 59 281 134 308 b 319 606 270 399 319 499 b 317 650 319 620 319 635 l 315 659 l 314 655 b 223 537 288 607 258 570 b 189 509 212 528 212 528 b 35 352 126 459 73 405 b -1 292 20 333 2 303 b -19 280 -4 285 -12 280 l -21 280 l -21 413 l -21 546 l -17 546 b 186 620 59 557 134 584 b 319 882 270 675 319 775 b 317 925 319 896 319 911 l 315 935 l 314 931 b 223 813 288 884 258 846 b 189 785 212 805 212 805 b 35 628 126 735 73 681 b -1 569 20 609 2 580 b -19 556 -4 562 -12 556 l -21 556 l -21 689 l -21 823 l -17 823 b 202 907 68 835 152 867 b 319 1157 280 968 319 1061 b 270 1338 319 1218 303 1281 b 262 1358 264 1349 262 1353 b 262 1364 262 1360 262 1363 b 276 1378 265 1371 269 1376 "},"v2d":{"x_min":0,"x_max":438.28125,"ha":447,"o":"m 212 190 b 219 191 213 191 216 191 b 236 176 225 191 228 190 b 419 18 277 105 341 49 b 436 5 431 13 434 11 b 438 -1 438 4 438 1 b 424 -16 438 -8 432 -13 b 356 -49 409 -20 379 -36 b 234 -180 306 -83 258 -133 b 219 -192 230 -188 224 -192 b 200 -176 213 -192 206 -187 b 9 -15 157 -102 89 -45 b 0 0 2 -12 0 -6 b 16 18 0 9 2 12 b 200 176 93 48 159 104 b 212 190 205 186 208 188 m 239 113 b 236 117 238 116 238 117 b 230 108 235 117 234 115 b 92 -15 196 58 140 8 b 88 -18 91 -16 88 -18 b 92 -20 88 -18 91 -19 b 198 -116 130 -43 166 -74 b 200 -117 200 -117 200 -117 b 201 -117 200 -117 201 -117 b 264 -43 212 -98 242 -62 b 345 15 288 -19 321 4 b 348 18 347 16 348 16 b 344 20 348 18 347 19 b 239 113 307 41 266 79 "},"v2f":{"x_min":-1.359375,"x_max":680.5625,"ha":694,"o":"m 597 1042 b 604 1042 600 1042 602 1042 b 642 1002 627 1042 642 1022 b 619 966 642 988 635 974 b 439 927 574 942 503 927 l 426 927 l 426 921 b 430 838 428 893 430 866 b 345 480 430 696 398 560 b 179 391 307 423 249 391 b 156 392 171 391 164 392 b 138 394 149 394 142 394 b 103 434 115 396 103 416 b 129 471 103 451 111 466 b 141 474 133 473 137 474 b 172 459 153 474 164 469 b 181 455 175 456 176 455 b 187 456 182 455 185 455 b 253 520 212 460 234 483 b 315 836 294 605 315 714 b 311 928 315 867 314 898 b 302 945 310 943 311 942 b 245 953 283 950 262 953 b 130 891 193 953 149 931 b 84 860 119 870 102 860 b 36 905 61 860 39 877 b 36 910 36 907 36 909 b 80 970 36 931 50 949 b 249 1017 125 1000 187 1017 b 322 1009 273 1017 299 1014 l 341 1003 b 436 991 372 995 406 991 b 577 1031 495 991 545 1004 b 597 1042 583 1038 590 1041 m 416 360 b 424 360 419 360 421 360 b 481 309 454 360 479 338 b 503 145 484 280 495 199 b 585 -185 525 16 555 -106 b 630 -245 596 -213 613 -237 l 634 -247 l 638 -245 b 647 -244 641 -245 645 -244 b 680 -278 666 -244 680 -262 b 664 -308 680 -290 675 -301 b 638 -312 658 -310 650 -312 b 613 -309 631 -312 623 -310 b 477 -201 555 -303 502 -260 b 417 -2 460 -159 434 -72 b 416 5 417 1 416 5 b 416 5 416 5 416 5 b 411 -5 415 5 413 0 b 359 -97 397 -33 377 -70 b 353 -106 355 -102 353 -105 b 359 -112 353 -108 355 -109 b 409 -130 375 -123 390 -129 b 426 -134 420 -130 421 -131 b 431 -147 428 -137 431 -141 b 420 -162 431 -152 427 -159 b 382 -169 409 -166 396 -169 b 323 -155 363 -169 341 -165 l 317 -152 l 314 -155 b 62 -303 240 -240 148 -295 b 36 -305 55 -305 44 -305 b 23 -303 29 -305 24 -305 b -1 -273 6 -299 -1 -287 b 31 -240 -1 -256 10 -240 b 36 -240 32 -240 34 -240 b 42 -241 38 -241 39 -241 b 134 -204 63 -241 99 -226 b 367 288 265 -115 357 81 b 375 330 368 313 370 320 b 416 360 383 347 400 358 m 360 -359 b 379 -359 363 -359 371 -359 b 424 -360 396 -359 416 -359 b 646 -502 536 -373 624 -430 b 649 -527 649 -510 649 -519 b 530 -673 649 -578 604 -635 l 521 -677 l 529 -681 b 653 -811 592 -714 637 -762 b 660 -853 658 -827 660 -839 b 645 -911 660 -873 656 -892 b 426 -1021 608 -981 519 -1021 b 283 -989 377 -1021 328 -1011 b 235 -949 249 -972 239 -964 b 234 -936 234 -946 234 -941 b 234 -928 234 -934 234 -931 l 235 -925 l 234 -927 l 225 -934 b 87 -982 186 -966 138 -982 b 80 -982 85 -982 83 -982 b 55 -981 70 -981 58 -981 b 17 -943 32 -981 17 -964 b 54 -904 17 -921 35 -904 b 78 -914 62 -904 72 -909 l 83 -918 l 88 -918 b 190 -831 122 -918 166 -881 b 269 -506 242 -727 269 -612 b 268 -462 269 -492 269 -477 b 266 -449 266 -458 266 -452 b 265 -444 266 -445 266 -444 b 257 -446 264 -444 261 -445 b 132 -545 196 -470 152 -505 b 88 -573 122 -563 104 -573 b 39 -523 63 -573 39 -553 b 63 -476 39 -505 44 -494 b 360 -359 136 -408 235 -369 m 419 -424 b 393 -423 411 -423 406 -423 l 375 -423 l 377 -426 b 379 -439 377 -427 378 -434 b 383 -510 382 -463 383 -487 b 314 -811 383 -609 360 -710 b 266 -893 296 -850 285 -870 b 264 -898 265 -896 264 -898 l 264 -898 b 264 -898 264 -898 264 -898 b 268 -898 264 -898 266 -898 b 273 -898 270 -898 272 -898 b 300 -909 283 -898 291 -900 b 426 -957 340 -941 385 -957 b 476 -949 443 -957 460 -954 b 547 -853 522 -931 547 -893 b 485 -745 547 -816 526 -775 b 397 -707 460 -727 432 -714 b 366 -675 375 -703 366 -692 b 396 -642 366 -657 377 -645 b 530 -557 455 -637 511 -601 b 536 -527 534 -548 536 -537 b 419 -424 536 -480 490 -437 "},"v30":{"x_min":-21.78125,"x_max":367.5,"ha":375,"o":"m 276 1900 b 284 1901 279 1900 281 1901 b 306 1883 291 1901 298 1896 b 367 1686 347 1825 367 1757 b 343 1558 367 1643 359 1600 l 338 1549 l 343 1537 b 367 1411 359 1497 367 1454 b 343 1282 367 1367 359 1324 l 338 1272 l 343 1261 b 367 1135 359 1221 367 1178 b 343 1007 367 1090 359 1047 l 338 996 l 343 985 b 367 859 359 945 367 902 b 343 731 367 814 359 771 l 338 720 l 343 709 b 367 582 359 667 367 626 b 289 362 367 503 340 426 b 239 312 276 345 259 330 b 29 77 152 237 76 152 b -1 18 14 54 2 30 b -19 4 -4 11 -12 4 l -21 4 l -21 133 l -20 260 l -13 262 b 98 299 17 269 62 284 b 111 305 103 302 110 305 b 167 334 123 310 156 327 b 319 595 264 391 319 491 b 313 659 319 616 318 638 b 310 667 311 664 311 667 b 307 663 310 667 308 666 b 240 588 289 637 269 614 b 16 331 141 505 62 413 b -1 294 8 316 1 302 b -19 280 -4 287 -12 280 l -21 280 l -21 408 l -20 537 l -13 538 b 98 576 17 545 62 560 b 111 581 103 578 110 581 b 167 610 123 587 156 603 b 319 871 264 667 319 767 b 313 935 319 892 318 913 b 310 942 311 941 311 942 b 307 939 310 942 308 941 b 240 864 289 913 269 889 b 16 607 141 781 62 689 b -1 570 8 592 1 578 b -19 556 -4 563 -12 556 l -21 556 l -21 684 l -20 813 l -13 814 b 98 852 17 821 62 836 b 111 857 103 855 110 857 b 167 886 123 863 156 880 b 319 1147 264 943 319 1043 b 313 1211 319 1168 318 1189 b 310 1218 311 1217 311 1218 b 307 1215 310 1218 308 1217 b 240 1140 289 1188 269 1165 b 16 884 141 1057 62 966 b -1 846 8 868 1 855 b -19 832 -4 839 -12 832 l -21 832 l -21 960 l -20 1089 l -13 1090 b 98 1128 17 1097 62 1111 b 111 1134 103 1131 110 1134 b 167 1163 123 1139 156 1156 b 319 1424 264 1220 319 1320 b 313 1486 319 1444 318 1465 b 310 1494 311 1493 311 1494 b 307 1492 310 1494 308 1493 b 240 1417 289 1464 269 1442 b 16 1160 141 1333 62 1242 b -1 1121 8 1145 1 1131 b -19 1109 -4 1115 -12 1109 l -21 1109 l -21 1236 l -20 1365 l -13 1367 b 98 1404 17 1374 62 1388 b 111 1410 103 1407 110 1410 b 250 1508 172 1437 215 1467 b 319 1701 296 1564 319 1633 b 270 1859 319 1757 303 1814 b 262 1882 265 1868 262 1875 b 276 1900 262 1890 266 1896 "},"v33":{"x_min":-423.3125,"x_max":421.9375,"ha":431,"o":"m -10 276 b -2 277 -8 277 -5 277 b 17 265 5 277 13 273 b 19 163 19 260 19 260 l 19 68 l 39 45 b 277 -95 122 -34 200 -81 b 289 -97 281 -97 285 -97 b 378 0 332 -97 371 -54 b 378 11 378 4 378 6 b 302 83 378 55 345 83 b 242 66 283 83 262 77 b 208 56 231 59 219 56 b 148 120 175 56 148 81 b 200 186 148 151 164 172 b 261 198 220 194 240 198 b 420 45 341 198 411 137 b 421 22 421 37 421 29 b 257 -198 421 -86 347 -188 b 242 -198 251 -198 247 -198 b 20 -105 181 -198 95 -163 l 19 -104 l 19 -183 b 19 -216 19 -195 19 -206 b 12 -273 19 -272 17 -267 b -2 -278 8 -277 2 -278 b -21 -266 -10 -278 -19 -274 b -23 -165 -23 -263 -23 -262 l -23 -69 l -44 -47 b -250 86 -117 23 -183 66 b -295 94 -270 93 -284 94 b -315 91 -302 94 -308 94 b -381 5 -356 81 -381 43 b -355 -56 -381 -16 -372 -40 b -299 -81 -338 -73 -319 -81 b -246 -68 -283 -81 -265 -77 b -212 -58 -234 -61 -223 -58 b -168 -77 -196 -58 -179 -65 b -151 -122 -156 -90 -151 -105 b -179 -174 -151 -141 -160 -162 b -239 -195 -194 -184 -217 -192 b -257 -197 -245 -195 -250 -197 b -423 -5 -349 -197 -423 -113 b -423 0 -423 -4 -423 -1 b -277 194 -420 97 -362 173 b -247 197 -268 197 -258 197 b -24 104 -185 197 -100 162 l -23 102 l -23 181 b -21 265 -23 260 -23 260 b -10 276 -20 269 -14 274 "},"v34":{"x_min":0,"x_max":622.03125,"ha":635,"o":"m 398 417 b 406 419 401 419 404 419 b 427 398 417 419 427 409 b 427 391 427 395 427 392 b 34 -274 424 385 38 -272 b 20 -280 29 -278 25 -280 b 0 -259 9 -280 0 -270 b 0 -252 0 -256 0 -254 b 393 413 2 -247 389 410 b 398 417 394 415 397 416 m 592 417 b 600 419 594 419 597 419 b 622 398 611 419 622 409 b 620 391 622 395 620 392 b 227 -274 617 385 231 -272 b 213 -280 223 -278 219 -280 b 193 -259 202 -280 193 -270 b 194 -252 193 -256 193 -254 b 586 413 196 -247 582 410 b 592 417 588 415 590 416 "},"v36":{"x_min":-1.359375,"x_max":1064.390625,"ha":1086,"o":"m 296 692 b 314 694 302 694 307 694 b 386 685 337 694 366 689 b 548 498 480 660 548 580 b 548 481 548 492 548 487 b 455 395 541 426 499 395 b 370 462 420 395 383 417 b 362 496 364 477 362 488 b 377 514 362 509 367 514 b 393 501 386 514 390 510 b 432 474 397 484 413 474 b 470 487 445 474 458 478 b 491 530 484 496 491 510 b 490 544 491 534 491 539 b 333 660 479 606 411 657 l 323 662 l 315 646 b 269 524 285 591 269 556 b 321 431 269 492 287 466 b 349 395 338 413 343 408 b 363 342 359 378 363 362 b 359 312 363 333 362 322 b 285 158 348 266 318 206 b 281 152 283 155 281 152 b 281 152 281 152 281 152 b 287 154 283 152 284 152 b 318 155 298 154 308 155 b 461 98 371 155 419 136 l 464 97 l 483 112 b 503 129 494 120 503 127 b 504 130 503 129 504 129 b 503 138 504 131 503 134 b 500 180 500 152 500 166 b 553 326 500 238 518 288 b 604 366 560 331 592 358 b 649 381 617 376 632 381 b 696 362 665 381 681 374 b 724 302 714 347 724 324 b 695 238 724 278 714 255 b 660 210 691 234 662 212 b 579 148 658 209 582 151 b 579 148 579 148 579 148 b 596 106 579 144 589 119 b 622 77 604 88 609 83 b 657 69 632 72 645 69 b 748 112 688 69 721 84 b 755 123 754 117 755 120 b 755 127 755 124 755 126 b 751 165 752 137 751 151 b 758 219 751 183 754 202 b 894 387 774 290 820 347 b 896 390 896 388 896 388 b 891 398 896 391 895 392 b 622 560 827 477 730 535 b 600 580 605 564 600 569 b 617 596 600 591 607 596 b 628 595 622 596 624 596 b 1057 248 846 552 1020 412 b 1064 191 1061 229 1064 209 b 922 0 1064 94 1005 9 b 902 -1 916 -1 909 -1 b 774 76 847 -1 800 26 b 769 83 770 81 770 83 b 769 81 769 83 769 83 b 627 -1 733 29 677 -1 b 548 27 597 -1 570 8 b 515 88 537 37 525 61 l 513 95 l 510 93 l 453 45 b 390 0 396 0 396 0 b 390 0 390 0 390 0 b 374 15 381 0 377 4 b 268 105 359 69 314 105 b 250 104 262 105 257 105 l 243 102 l 234 90 b 155 1 201 49 159 2 b 147 -1 152 0 149 -1 b 130 15 138 -1 130 6 b 132 20 130 18 132 19 b 136 31 133 22 134 27 b 220 131 149 74 178 109 b 231 137 225 134 230 136 b 302 278 280 202 302 244 b 265 335 302 299 295 309 b 209 442 234 363 213 402 b 209 455 209 446 209 451 b 279 648 209 502 232 564 l 285 659 l 283 659 b 176 627 238 653 210 645 b 57 477 111 594 66 538 b 55 459 55 471 55 464 b 72 409 55 437 61 415 b 93 403 78 405 87 403 b 152 467 123 403 151 431 b 168 488 153 483 157 488 b 185 462 181 488 185 483 l 185 460 b 137 344 183 409 168 369 b 78 322 119 328 98 322 b 13 360 50 322 25 335 b -1 426 4 380 -1 402 b 89 610 -1 488 32 559 b 296 692 147 659 210 685 m 926 348 b 921 353 924 351 922 353 b 914 348 920 353 918 351 b 823 167 857 306 823 237 b 828 124 823 154 826 138 b 890 31 837 79 862 40 b 896 31 892 31 894 31 b 956 104 916 31 940 59 b 970 191 965 129 970 159 b 966 241 970 208 969 224 b 926 348 959 277 945 313 m 627 326 b 619 326 624 326 622 326 b 598 316 611 326 604 323 b 568 215 579 288 568 255 b 568 208 568 213 568 210 b 571 183 570 195 570 184 l 571 183 b 594 201 571 183 582 191 l 634 231 b 660 259 653 247 656 248 b 664 278 662 266 664 272 b 627 326 664 299 649 320 "},"v38":{"x_min":-1.359375,"x_max":651.96875,"ha":665,"o":"m 389 644 b 405 645 394 645 400 645 b 504 566 450 645 492 613 b 507 541 506 557 507 549 b 480 471 507 514 498 489 l 477 467 l 483 470 b 609 591 539 485 586 531 b 613 601 611 595 613 599 b 631 609 619 607 624 609 b 651 588 641 609 651 602 b 200 -946 651 584 204 -941 b 182 -957 197 -953 190 -957 b 163 -945 174 -957 166 -953 b 161 -939 161 -942 161 -942 b 217 -743 161 -931 170 -904 b 272 -555 247 -639 272 -555 b 272 -555 272 -555 272 -555 b 264 -560 272 -555 268 -557 b 140 -603 227 -589 182 -603 b 36 -567 102 -603 65 -592 b -1 -487 12 -548 -1 -517 b 17 -427 -1 -466 5 -445 b 103 -380 38 -395 70 -380 b 191 -433 137 -380 172 -398 b 205 -484 201 -448 205 -466 b 178 -553 205 -509 196 -535 l 175 -557 l 182 -555 b 307 -435 236 -539 284 -494 b 372 -213 308 -430 372 -215 b 372 -213 372 -213 372 -213 b 364 -219 372 -213 368 -216 b 240 -262 328 -247 283 -262 b 137 -226 202 -262 166 -249 b 99 -145 112 -206 99 -176 b 118 -84 99 -124 106 -104 b 204 -38 138 -54 171 -38 b 292 -91 238 -38 273 -56 b 306 -141 302 -106 306 -124 b 279 -212 306 -167 296 -194 l 276 -215 l 281 -213 b 408 -93 336 -198 385 -151 b 473 129 409 -88 473 127 b 473 129 473 129 473 129 b 465 122 473 129 469 126 b 341 80 428 94 383 80 b 236 115 303 80 266 91 b 200 195 213 136 200 165 b 217 256 200 217 206 238 b 304 303 239 287 272 303 b 393 249 338 303 374 285 b 406 199 402 234 406 217 b 379 129 406 173 397 148 l 377 126 l 382 127 b 509 248 436 142 485 190 b 574 470 510 254 574 469 b 574 470 574 470 574 470 b 566 464 574 470 570 467 b 442 421 529 435 484 421 b 337 458 404 421 367 433 b 300 537 313 478 300 508 b 389 644 300 585 334 635 "},"v3b":{"x_min":0,"x_max":484.5625,"ha":494,"o":"m 228 245 b 239 247 234 247 239 247 b 243 247 240 247 242 247 b 303 238 257 247 287 242 b 484 -2 417 208 484 104 b 412 -177 484 -65 461 -127 b 243 -248 363 -226 303 -248 b 6 -63 138 -248 36 -180 b 0 -1 1 -41 0 -20 b 228 245 0 127 98 240 m 255 181 b 240 183 247 183 245 183 b 232 181 238 183 235 183 b 142 152 200 180 168 170 l 138 149 l 190 97 l 242 44 l 294 97 l 345 149 l 340 152 b 255 181 315 169 284 180 m 147 -54 l 197 -1 l 147 51 l 95 104 l 91 99 b 62 -1 72 70 62 34 b 66 -43 62 -15 63 -29 b 91 -101 72 -63 80 -84 l 95 -106 l 147 -54 m 393 99 b 389 104 390 102 389 104 b 337 51 389 104 366 80 l 285 -1 l 337 -54 l 389 -106 l 393 -101 b 421 -1 412 -72 421 -36 b 393 99 421 34 412 69 m 294 -98 b 242 -45 265 -69 242 -45 b 190 -98 242 -45 219 -69 l 138 -151 l 142 -154 b 242 -184 172 -174 206 -184 b 340 -154 276 -184 311 -174 l 345 -151 l 294 -98 "},"v3c":{"x_min":0,"x_max":450.53125,"ha":460,"o":"m 189 302 b 204 303 193 302 198 303 b 303 224 250 303 292 270 b 306 199 304 216 306 208 b 279 129 306 173 296 147 l 276 126 l 281 127 b 408 249 337 142 385 190 b 412 259 409 254 412 258 b 430 267 417 265 423 267 b 450 247 441 267 450 259 b 200 -605 450 242 204 -599 b 182 -616 197 -612 190 -616 b 163 -602 174 -616 166 -610 b 161 -598 161 -601 161 -601 b 217 -402 161 -589 170 -562 b 272 -213 247 -298 272 -213 b 272 -213 272 -213 272 -213 b 264 -219 272 -213 268 -216 b 140 -262 227 -247 182 -262 b 36 -226 102 -262 65 -249 b 0 -145 12 -206 0 -176 b 17 -84 0 -124 5 -104 b 103 -38 38 -54 70 -38 b 191 -91 137 -38 172 -56 b 205 -141 201 -106 205 -124 b 178 -212 205 -167 196 -194 l 175 -215 l 182 -213 b 307 -93 236 -198 284 -151 b 372 129 308 -88 372 127 b 372 129 372 129 372 129 b 364 122 372 129 368 126 b 240 80 328 94 283 80 b 137 115 202 80 166 91 b 99 194 111 136 99 165 b 189 302 99 244 133 292 "},"v3e":{"x_min":0,"x_max":406.96875,"ha":415,"o":"m 21 183 b 28 183 24 183 25 183 b 42 181 34 183 39 183 b 127 108 47 179 47 179 b 202 41 168 72 202 41 b 279 108 204 41 238 72 b 357 177 321 145 356 176 b 375 183 363 181 370 183 b 406 151 392 183 406 169 b 404 137 406 147 405 141 b 322 62 401 131 398 129 b 251 0 284 27 251 0 b 322 -63 251 -1 284 -29 b 404 -138 398 -130 401 -133 b 406 -152 405 -142 406 -148 b 375 -184 406 -170 392 -184 b 357 -179 370 -184 363 -183 b 279 -109 356 -177 321 -147 b 202 -43 238 -73 204 -43 b 127 -109 202 -43 168 -73 b 49 -179 85 -147 50 -177 b 31 -184 43 -183 36 -184 b 0 -152 13 -184 0 -170 b 2 -138 0 -148 0 -142 b 83 -63 5 -133 8 -130 b 155 0 122 -29 155 -1 b 83 62 155 0 122 27 b 8 129 43 97 10 127 b 0 151 2 136 0 144 b 21 183 0 165 8 177 "},"v3f":{"x_min":-24.5,"x_max":317.140625,"ha":324,"o":"m -24 -147 l -24 -5 l -20 -5 b -1 -19 -12 -5 -4 -11 b 58 -123 6 -43 31 -86 b 196 -278 93 -173 134 -219 b 317 -570 274 -356 317 -460 b 294 -713 317 -617 308 -666 l 289 -724 l 294 -735 b 317 -873 308 -780 317 -827 b 235 -1132 317 -963 288 -1054 b 209 -1165 228 -1140 224 -1146 b 189 -1177 204 -1172 196 -1177 b 171 -1164 182 -1177 175 -1172 b 168 -1154 170 -1161 168 -1159 b 181 -1132 168 -1149 172 -1142 b 269 -891 238 -1064 269 -975 b 269 -881 269 -886 269 -884 b 262 -814 269 -857 265 -827 b 258 -800 261 -811 259 -806 b 142 -628 240 -731 198 -667 b -8 -589 112 -606 47 -589 b -20 -589 -13 -589 -19 -589 l -24 -589 l -24 -449 l -24 -308 l -20 -308 b -1 -322 -12 -308 -4 -313 b 58 -424 6 -345 31 -388 b 194 -580 93 -476 136 -523 b 259 -660 221 -606 245 -635 b 261 -663 259 -662 261 -663 b 264 -656 262 -663 262 -660 b 269 -587 268 -632 269 -610 b 264 -521 269 -566 268 -544 b 262 -512 264 -517 262 -513 b 258 -498 261 -509 259 -503 b 142 -326 240 -428 198 -365 b -8 -287 112 -303 47 -288 b -20 -287 -13 -287 -19 -287 l -24 -287 l -24 -147 "},"v40":{"x_min":-1.359375,"x_max":436.921875,"ha":446,"o":"m 213 205 b 217 205 215 205 216 205 b 234 194 224 205 234 199 b 236 187 234 194 235 190 l 245 167 l 261 129 l 270 106 b 355 -61 294 54 329 -13 b 420 -163 381 -105 402 -138 b 436 -188 435 -184 436 -184 b 436 -191 436 -190 436 -190 b 421 -206 436 -201 431 -206 l 421 -206 l 416 -206 l 405 -201 b 217 -158 347 -172 283 -158 b 31 -201 153 -158 88 -172 l 20 -206 l 14 -206 l 14 -206 b 0 -191 5 -206 0 -201 b -1 -188 0 -190 -1 -190 b 14 -163 -1 -186 0 -184 b 95 -34 36 -136 72 -77 b 166 106 119 8 148 68 l 175 129 l 183 148 l 200 188 b 213 205 205 199 208 202 "},"v41":{"x_min":-1.359375,"x_max":556.6875,"ha":568,"o":"m 294 322 b 318 323 299 322 308 323 b 360 320 334 323 352 322 b 526 217 430 310 490 273 b 543 166 537 202 543 184 b 447 70 543 117 503 70 b 445 70 447 70 446 70 b 359 159 394 72 359 113 b 368 201 359 173 362 187 b 442 245 382 229 412 245 b 455 244 446 245 451 245 b 460 244 458 244 460 244 b 460 244 460 244 460 244 b 454 248 460 244 458 245 b 325 291 417 276 372 291 b 285 287 313 291 299 290 b 144 -2 183 269 144 190 b 281 -290 144 -208 179 -280 b 304 -291 289 -291 298 -291 b 524 -105 412 -291 506 -212 b 541 -84 526 -88 530 -84 b 556 -101 551 -84 556 -90 b 549 -138 556 -111 553 -122 b 334 -322 521 -237 435 -310 b 302 -324 323 -323 313 -324 b 13 -101 172 -324 54 -234 b -1 -1 4 -68 -1 -34 b 294 322 -1 161 121 303 "},"v42":{"x_min":-348.4375,"x_max":24.5,"ha":25,"o":"m -330 155 b -322 156 -329 156 -326 156 b -315 156 -319 156 -317 156 b -298 147 -311 155 -308 154 b -19 30 -224 98 -122 55 l 2 26 b 24 -1 17 22 24 13 b 2 -27 24 -15 17 -23 l -19 -31 b -298 -148 -122 -56 -224 -99 b -322 -158 -313 -158 -315 -158 b -348 -131 -338 -158 -348 -145 b -344 -117 -348 -127 -347 -122 b -328 -104 -341 -112 -338 -111 b -127 -8 -269 -65 -202 -33 b -106 0 -115 -4 -106 -1 b -127 6 -106 0 -115 2 b -328 102 -202 31 -269 63 b -344 116 -338 109 -341 111 b -348 130 -347 120 -348 124 b -330 155 -348 141 -341 152 "},"v43":{"x_min":-442.359375,"x_max":441,"ha":450,"o":"m -31 487 b -1 488 -21 488 -10 488 b 434 104 216 488 397 330 b 441 27 438 79 441 47 b 439 12 441 20 439 15 b 419 0 435 4 427 0 b 404 5 413 0 408 1 b 398 30 400 11 398 13 b 0 351 390 213 213 351 b -59 348 -20 351 -39 349 b -400 30 -251 324 -393 191 b -405 5 -400 13 -401 11 b -420 0 -409 1 -415 0 b -441 12 -428 0 -436 4 b -442 27 -441 15 -442 20 b -435 104 -442 47 -439 79 b -31 487 -401 316 -235 474 m -13 131 b -1 133 -9 133 -5 133 b 51 105 19 133 39 123 b 61 70 58 95 61 83 b 51 34 61 58 58 45 b -1 6 39 16 19 6 b -46 27 -17 6 -34 13 b -62 69 -57 38 -62 54 b -13 131 -62 98 -44 124 "},"v44":{"x_min":-21.78125,"x_max":251.8125,"ha":257,"o":"m -8 631 b -1 632 -6 632 -4 632 b 19 620 8 632 16 628 b 20 383 20 616 20 616 l 20 148 l 21 151 b 137 199 59 183 99 199 b 182 191 152 199 167 197 b 251 84 227 176 251 134 b 228 0 251 58 243 29 b 100 -142 206 -40 178 -72 l 23 -215 b 0 -229 9 -229 6 -229 b -20 -216 -9 -229 -17 -224 l -21 -212 l -21 201 l -21 616 l -20 620 b -8 631 -17 624 -13 630 m 110 131 b 96 133 106 133 100 133 b 89 133 93 133 91 133 b 24 87 63 129 40 113 l 20 80 l 20 -37 l 20 -156 l 23 -152 b 144 81 96 -72 144 20 l 144 83 b 110 131 144 113 134 126 "},"v45":{"x_min":-402.890625,"x_max":401.53125,"ha":410,"o":"m -10 273 b -4 274 -9 273 -6 274 b 16 262 4 274 12 269 b 17 158 17 259 17 259 l 17 56 l 62 112 b 117 174 110 172 110 172 b 122 174 118 174 119 174 b 132 173 125 174 129 173 b 295 11 134 172 171 134 l 307 -1 l 336 34 b 374 76 366 72 368 74 b 381 77 375 77 378 77 b 401 56 392 77 401 68 b 400 48 401 54 401 51 b 223 -172 397 41 230 -166 b 210 -176 220 -174 215 -176 b 201 -174 206 -176 204 -176 b 112 -87 198 -173 178 -152 b 27 0 65 -38 27 0 b 21 -6 27 0 24 -2 l 17 -12 l 17 -147 b 17 -210 17 -173 17 -194 b 10 -292 17 -297 16 -287 b -2 -299 6 -297 2 -299 b -21 -287 -10 -299 -19 -295 b -24 -174 -23 -284 -23 -284 l -24 -63 l -66 -117 b -121 -176 -110 -170 -114 -176 b -125 -176 -122 -176 -123 -176 b -296 -12 -134 -174 -125 -184 l -308 0 l -337 -34 b -375 -77 -367 -73 -370 -76 b -382 -79 -377 -79 -379 -79 b -402 -58 -393 -79 -402 -69 b -401 -49 -402 -55 -402 -52 b -224 170 -398 -43 -231 165 b -212 174 -221 173 -216 174 b -202 173 -208 174 -205 174 b -39 11 -200 172 -151 122 l -28 -1 l -25 1 l -24 4 l -24 130 b -23 260 -24 256 -24 258 b -10 273 -20 266 -16 270 "},"v46":{"x_min":0,"x_max":627.46875,"ha":640,"o":"m 306 190 b 314 191 308 191 311 191 b 326 184 318 191 322 190 l 336 173 b 510 52 377 127 442 80 b 515 49 513 51 515 49 b 611 16 537 40 579 24 b 627 0 624 13 627 9 b 607 -18 627 -11 624 -13 b 330 -181 490 -49 389 -109 b 314 -192 323 -190 319 -192 b 306 -191 311 -192 308 -192 b 294 -177 302 -188 302 -188 b 257 -140 287 -170 265 -148 b 19 -18 193 -84 114 -44 b 0 0 2 -13 0 -11 b 16 16 0 9 2 13 b 110 49 47 24 89 40 b 117 52 111 49 114 51 b 145 65 126 56 130 58 b 281 163 200 93 245 124 b 300 186 288 170 291 174 b 306 190 300 187 303 188 m 317 137 b 313 142 315 141 314 142 b 308 137 313 142 311 141 b 161 4 276 84 220 33 b 155 0 159 1 155 0 b 163 -4 155 0 159 -2 b 308 -138 220 -34 276 -84 b 313 -142 311 -141 313 -142 b 317 -138 314 -142 315 -141 b 464 -4 351 -84 406 -34 b 470 0 468 -2 470 0 b 464 4 470 0 468 1 b 317 137 406 33 351 84 "},"v47":{"x_min":-24.5,"x_max":315.78125,"ha":322,"o":"m -24 -145 l -24 -5 l -20 -5 b 1 -26 -10 -5 -6 -9 b 175 -241 31 -86 96 -166 b 314 -548 259 -323 304 -420 b 315 -589 315 -555 315 -571 b 314 -630 315 -606 315 -623 b 298 -730 311 -664 306 -699 l 295 -742 l 296 -748 b 314 -850 304 -778 311 -813 b 315 -892 315 -857 315 -874 b 314 -932 315 -909 315 -925 b 298 -1032 311 -967 306 -1002 l 295 -1045 l 296 -1050 b 314 -1153 304 -1081 311 -1115 b 315 -1193 315 -1160 315 -1177 b 314 -1235 315 -1211 315 -1228 b 217 -1526 306 -1338 270 -1444 b 201 -1533 213 -1532 208 -1533 b 182 -1522 193 -1533 185 -1529 b 179 -1514 181 -1518 179 -1517 b 189 -1489 179 -1508 182 -1501 b 266 -1217 240 -1403 266 -1308 b 262 -1156 266 -1196 265 -1177 b 110 -907 247 -1043 190 -950 b 0 -889 87 -895 50 -889 l -1 -889 l -24 -889 l -24 -749 l -24 -610 l -20 -610 b 1 -631 -10 -610 -6 -614 b 175 -846 31 -691 96 -771 b 259 -956 213 -884 236 -914 b 265 -966 262 -961 264 -966 b 265 -966 265 -966 265 -966 b 265 -953 265 -964 265 -959 b 266 -920 266 -943 266 -932 b 262 -853 266 -898 265 -873 b 110 -605 247 -741 190 -648 b 0 -587 87 -592 50 -587 l -1 -587 l -24 -587 l -24 -448 l -24 -308 l -20 -308 b 1 -328 -10 -308 -6 -312 b 175 -544 31 -388 96 -469 b 259 -655 213 -581 236 -612 b 265 -663 262 -659 264 -663 b 265 -663 265 -663 265 -663 b 265 -650 265 -663 265 -657 b 266 -617 266 -641 266 -630 b 262 -551 266 -595 265 -570 b 110 -303 247 -438 190 -345 b 0 -284 87 -290 50 -284 l -1 -284 l -24 -284 l -24 -145 "},"v49":{"x_min":0,"x_max":630.203125,"ha":643,"o":"m 308 204 b 314 205 310 205 313 205 b 326 201 319 205 323 204 b 355 154 328 199 338 180 b 401 83 362 142 392 95 l 409 72 b 431 41 412 66 424 49 b 619 -174 498 -51 570 -134 b 630 -192 626 -180 630 -186 b 626 -202 630 -195 628 -199 b 616 -206 623 -205 620 -206 b 552 -188 608 -206 592 -202 b 310 -155 488 -169 392 -155 b 268 -156 295 -155 281 -155 b 77 -188 197 -161 126 -173 b 13 -206 35 -202 20 -206 b 9 -206 12 -206 10 -206 b 0 -191 2 -202 0 -197 b 8 -176 0 -186 2 -180 b 204 49 58 -136 138 -43 l 220 72 l 227 83 b 295 188 245 108 281 166 b 308 204 299 197 304 202 m 315 147 b 314 147 315 147 314 147 b 314 147 314 147 314 147 b 306 129 314 145 310 138 l 296 105 b 281 72 292 97 284 77 l 274 56 b 181 -123 247 -4 212 -72 l 174 -134 l 176 -133 b 314 -123 215 -127 272 -123 b 451 -133 356 -123 413 -127 l 454 -134 l 449 -123 b 353 56 417 -72 381 -4 l 347 72 b 332 105 344 77 336 97 l 322 129 b 315 147 318 138 315 145 "},"v4a":{"x_min":70.78125,"x_max":378.390625,"ha":315,"o":"m 246 373 b 254 373 249 373 251 373 b 372 324 303 373 360 351 b 378 302 377 317 378 309 b 338 251 378 278 362 255 b 328 249 334 249 332 249 b 283 294 303 249 283 270 b 288 315 283 301 284 308 b 289 319 289 317 289 319 b 289 319 289 319 289 319 b 283 320 289 320 287 320 b 270 322 279 322 274 322 b 206 288 242 322 215 308 b 206 283 206 287 206 285 b 257 223 206 267 230 238 b 284 206 272 213 277 210 b 351 90 328 173 351 130 b 340 47 351 74 348 59 b 205 -30 314 -2 264 -30 b 182 -29 198 -30 190 -30 b 84 15 147 -24 103 -5 b 70 48 74 24 70 36 b 108 99 70 70 85 94 b 121 102 112 101 117 102 b 167 56 147 102 167 80 b 159 31 167 48 164 40 l 156 26 l 157 26 b 190 20 167 22 178 20 b 220 26 201 20 212 22 b 258 65 243 34 258 51 b 257 70 258 66 258 69 b 204 126 249 94 234 109 b 114 258 148 158 114 209 b 125 302 114 273 118 288 b 246 373 147 342 193 370 "},"v4b":{"x_min":0,"x_max":503.609375,"ha":514,"o":"m 274 430 b 277 430 276 430 277 430 b 310 394 296 430 310 415 b 308 383 310 391 308 387 b 306 367 307 381 307 374 b 236 120 298 305 272 210 b 40 -273 189 -5 125 -134 b 20 -287 35 -283 27 -287 b 5 -281 14 -287 9 -285 b 0 -267 1 -277 0 -273 b 9 -242 0 -262 2 -255 b 246 395 137 -12 232 242 b 274 430 249 416 257 427 m 468 430 b 472 430 469 430 470 430 b 503 394 490 430 503 415 b 502 383 503 391 503 387 b 499 367 502 381 500 374 b 431 120 491 305 465 210 b 234 -273 382 -5 318 -134 b 213 -287 228 -283 220 -287 b 198 -281 208 -287 202 -285 b 193 -267 194 -277 193 -273 b 202 -242 193 -262 196 -255 b 439 395 330 -12 426 242 b 468 430 442 416 451 427 "},"v4d":{"x_min":-311.6875,"x_max":310.328125,"ha":317,"o":"m -9 388 b -2 390 -8 390 -5 390 b 5 388 1 390 4 390 b 19 378 10 387 16 383 b 23 333 23 371 23 371 b 24 298 23 299 24 298 b 81 276 34 298 65 285 b 213 91 145 240 190 177 b 224 24 217 76 224 36 b 257 24 224 24 235 24 b 299 19 292 24 292 24 b 310 -1 306 15 310 6 b 299 -23 310 -11 306 -19 b 257 -27 292 -27 292 -27 b 224 -29 235 -27 224 -29 b 213 -95 224 -40 217 -80 b 81 -280 190 -181 145 -244 b 24 -301 65 -290 34 -301 b 23 -335 24 -301 23 -303 l 23 -340 b 17 -381 23 -374 23 -374 b -1 -391 13 -388 5 -391 b -21 -381 -9 -391 -17 -388 b -27 -340 -27 -374 -27 -374 l -27 -335 b -28 -301 -27 -303 -27 -301 b -85 -280 -38 -301 -69 -290 b -217 -95 -149 -244 -194 -181 b -228 -29 -221 -80 -228 -40 b -259 -27 -228 -29 -238 -27 b -300 -23 -294 -27 -294 -27 b -311 -2 -307 -19 -311 -11 b -294 23 -311 8 -304 19 b -259 24 -291 23 -284 24 b -228 24 -239 24 -228 24 b -217 91 -228 36 -221 76 b -85 276 -194 177 -149 240 b -28 298 -69 285 -38 298 b -27 333 -27 298 -27 299 b -27 371 -27 362 -27 369 b -9 388 -24 378 -17 385 m -27 136 b -28 247 -27 197 -28 247 b -61 216 -31 247 -53 226 b -123 33 -95 172 -121 98 l -125 24 l -76 24 l -27 24 l -27 136 m 29 242 b 24 247 27 245 24 247 b 23 136 24 247 23 197 l 23 24 l 72 24 l 121 24 l 119 33 b 29 242 115 116 77 206 m -27 -140 l -27 -27 l -76 -27 l -125 -27 l -123 -36 b -61 -220 -121 -102 -95 -176 b -28 -251 -53 -230 -31 -251 b -27 -140 -28 -251 -27 -201 m 119 -36 l 121 -27 l 72 -27 l 23 -27 l 23 -140 b 24 -251 23 -201 24 -251 b 57 -220 27 -251 49 -230 b 119 -36 91 -176 117 -102 "},"v4e":{"x_min":0,"x_max":239.5625,"ha":244,"o":"m 10 460 b 20 462 13 462 14 462 b 39 449 28 462 35 458 l 40 446 l 40 326 b 40 205 40 259 40 205 b 127 227 40 205 80 215 b 220 249 196 244 213 249 b 227 247 224 249 225 248 b 238 237 231 245 235 241 l 239 233 l 239 -106 l 239 -448 l 238 -451 b 219 -463 234 -459 225 -463 b 198 -451 210 -463 202 -459 l 197 -448 l 197 -324 b 197 -201 197 -248 197 -201 b 110 -223 196 -201 157 -210 b 17 -245 42 -240 24 -245 b 10 -242 13 -245 13 -244 b 0 -233 6 -241 2 -237 l 0 -230 l 0 108 l 0 446 l 0 449 b 10 460 2 453 6 458 m 197 22 b 197 70 197 41 197 58 b 196 116 197 113 197 116 l 196 116 b 118 97 196 116 160 106 l 40 77 l 40 -18 b 40 -112 40 -69 40 -112 l 119 -93 l 197 -73 l 197 22 "},"v51":{"x_min":-1.359375,"x_max":455.96875,"ha":465,"o":"m 352 541 b 357 542 353 542 355 542 b 377 530 364 542 372 537 l 378 526 l 378 394 l 379 262 l 404 266 b 436 270 420 269 430 270 b 450 265 443 270 446 269 b 455 220 455 259 455 260 l 455 208 l 455 161 l 454 156 b 411 140 449 147 447 147 b 378 133 393 137 379 134 b 378 68 378 133 378 106 b 378 22 378 54 378 38 l 379 -87 l 404 -83 b 436 -79 420 -80 430 -79 b 450 -84 443 -79 446 -80 b 455 -129 455 -90 455 -88 l 455 -141 l 455 -188 l 454 -192 b 413 -209 449 -202 447 -202 b 382 -215 398 -212 383 -215 l 378 -215 l 378 -345 l 378 -380 b 375 -485 378 -484 378 -480 b 357 -494 371 -491 364 -494 b 340 -485 351 -494 344 -491 b 336 -383 337 -480 336 -484 l 336 -349 l 336 -223 l 334 -223 b 291 -231 334 -223 314 -227 l 247 -240 l 247 -371 l 246 -503 l 245 -506 b 225 -519 242 -514 234 -519 b 206 -506 219 -519 210 -514 l 205 -503 l 205 -376 l 205 -248 l 160 -256 l 115 -265 l 115 -396 l 115 -527 l 114 -531 b 95 -544 110 -539 102 -544 b 76 -531 87 -544 78 -539 l 73 -527 l 73 -399 b 73 -273 73 -330 73 -273 b 49 -277 73 -273 61 -274 b 17 -281 32 -280 24 -281 b 4 -276 10 -281 8 -280 b -1 -234 0 -269 -1 -272 b 0 -219 -1 -229 0 -224 l 0 -170 l 1 -167 b 10 -158 2 -163 6 -159 b 49 -149 13 -156 16 -155 l 73 -145 l 73 -34 b 73 76 73 26 73 76 b 49 72 73 76 61 74 b 17 68 32 69 24 68 b 4 73 10 68 8 69 b -1 115 0 80 -1 77 b 0 130 -1 120 0 124 l 0 179 l 1 181 b 10 191 2 186 6 190 b 49 199 13 192 16 194 l 73 204 l 73 338 b 73 374 73 352 73 365 b 77 483 73 484 73 477 b 95 492 81 489 88 492 b 111 483 100 492 107 489 b 115 378 115 477 115 483 l 115 342 b 117 212 115 223 115 212 b 204 229 117 212 200 227 l 205 229 l 205 365 l 205 502 l 206 505 b 225 517 210 513 219 517 b 245 505 234 517 242 513 l 246 502 l 247 369 l 247 237 l 249 237 b 336 254 253 238 336 254 b 337 390 336 254 337 302 l 337 526 l 338 530 b 352 541 341 535 347 539 m 336 15 b 336 126 336 102 336 126 l 336 126 b 291 117 336 126 315 122 l 247 109 l 247 -1 l 247 -112 l 249 -112 b 336 -95 253 -111 336 -95 b 336 15 336 -95 336 -56 m 205 -120 b 205 -55 205 -120 205 -93 b 205 -9 205 -41 205 -24 l 205 101 l 160 93 l 115 84 l 115 -26 b 115 -83 115 -49 115 -69 b 117 -137 115 -133 115 -137 b 205 -120 118 -137 204 -120 "},"v52":{"x_min":-10.890625,"x_max":298.078125,"ha":294,"o":"m 138 473 b 142 474 140 473 141 474 b 164 459 148 474 153 470 b 191 402 183 442 191 423 b 181 353 191 388 187 371 b 178 349 179 352 178 349 b 179 348 178 348 179 348 b 185 349 181 348 182 348 b 255 376 210 355 234 363 b 272 381 264 381 266 381 b 298 355 287 381 298 370 b 288 330 298 348 298 345 b 171 34 238 254 194 141 b 166 13 168 16 168 16 b 144 1 161 5 152 1 b 121 15 134 1 125 5 b 115 33 119 18 117 24 b 0 330 91 145 49 252 b -10 355 -9 345 -10 348 b 13 381 -10 371 0 381 b 31 376 19 381 25 380 b 132 345 61 358 103 345 l 136 345 l 137 355 b 145 378 138 359 142 370 b 152 415 149 394 152 405 b 137 452 152 427 148 438 b 133 464 134 458 133 460 b 138 473 133 467 134 470 "},"v54":{"x_min":-24.5,"x_max":317.140625,"ha":324,"o":"m -24 -161 l -24 -5 l -20 -5 b 0 -24 -9 -5 -2 -12 b 171 -315 21 -124 84 -233 b 317 -660 268 -406 317 -531 b 187 -1014 317 -782 274 -909 b 161 -1034 172 -1034 171 -1034 b 141 -1013 149 -1034 141 -1025 b 152 -991 141 -1004 142 -1002 b 266 -682 228 -899 266 -788 b 174 -430 266 -588 236 -498 b -23 -317 136 -388 66 -348 b -24 -161 -23 -316 -24 -285 "},"v55":{"x_min":0,"x_max":551.25,"ha":563,"o":"m 289 644 b 304 645 294 645 299 645 b 404 566 349 645 392 613 b 406 541 405 557 406 549 b 379 471 406 514 397 489 l 377 467 l 382 470 b 509 591 438 485 485 531 b 513 601 510 595 513 599 b 530 609 518 607 524 609 b 551 588 540 609 551 602 b 200 -605 551 584 204 -599 b 182 -616 197 -612 190 -616 b 163 -602 174 -616 166 -610 b 161 -598 161 -601 161 -601 b 217 -402 161 -589 170 -562 b 272 -213 247 -298 272 -213 b 272 -213 272 -213 272 -213 b 264 -219 272 -213 268 -216 b 140 -262 227 -247 182 -262 b 36 -226 102 -262 65 -249 b 0 -145 12 -206 0 -176 b 17 -84 0 -124 5 -104 b 103 -38 38 -54 70 -38 b 191 -91 137 -38 172 -56 b 205 -141 201 -106 205 -124 b 178 -212 205 -167 196 -194 l 175 -215 l 182 -213 b 307 -93 236 -198 284 -151 b 372 129 308 -88 372 127 b 372 129 372 129 372 129 b 364 122 372 129 368 126 b 240 80 328 94 283 80 b 137 115 202 80 166 91 b 99 195 112 136 99 165 b 118 256 99 217 106 238 b 204 303 138 287 171 303 b 292 249 238 303 273 285 b 306 199 302 234 306 217 b 279 129 306 173 296 148 l 276 126 l 281 127 b 408 248 336 142 385 190 b 473 470 409 254 473 469 b 473 470 473 470 473 470 b 465 464 473 470 469 467 b 341 421 428 435 383 421 b 236 458 303 421 266 433 b 200 537 212 478 200 508 b 289 644 200 585 234 635 "},"v58":{"x_min":-21.78125,"x_max":367.5,"ha":375,"o":"m 259 1553 b 265 1553 261 1553 264 1553 b 288 1540 272 1553 277 1550 b 367 1351 340 1493 367 1424 b 336 1221 367 1308 357 1263 l 332 1211 l 333 1208 b 367 1077 356 1170 367 1124 b 336 945 367 1032 357 986 l 332 935 l 333 932 b 367 800 356 893 367 848 b 336 669 367 756 357 710 l 332 659 l 333 656 b 367 523 356 617 367 571 b 345 412 367 485 360 446 b 231 273 322 356 284 310 b -1 19 121 195 27 93 b -17 4 -4 11 -10 5 l -21 4 l -21 134 l -21 265 l -17 265 b 133 291 20 265 96 278 b 318 537 245 328 318 433 b 307 603 318 559 315 582 b 303 614 304 612 304 614 b 298 609 302 614 300 613 b 231 549 281 589 258 567 b -1 295 121 471 27 369 b -17 280 -4 287 -10 281 l -21 280 l -21 410 l -21 541 l -17 541 b 133 567 20 541 96 555 b 318 813 245 605 318 709 b 307 880 318 835 315 859 b 303 891 304 888 304 891 b 298 885 302 891 300 888 b 231 825 281 866 258 843 b -1 571 121 748 27 645 b -17 556 -4 563 -10 557 l -21 556 l -21 687 l -21 817 l -17 817 b 133 843 20 817 96 830 b 318 1089 245 881 318 985 b 307 1156 318 1111 315 1134 b 303 1167 304 1164 304 1167 b 298 1161 302 1167 300 1164 b 231 1102 281 1140 258 1120 b -1 848 121 1024 27 921 b -17 832 -4 839 -10 834 l -21 832 l -21 963 l -21 1093 l -17 1093 b 114 1113 12 1093 78 1103 b 313 1314 215 1142 289 1218 b 318 1364 317 1331 318 1347 b 255 1511 318 1422 295 1478 b 243 1532 247 1519 243 1525 b 259 1553 243 1540 250 1550 "},"v59":{"x_min":0,"x_max":464.140625,"ha":474,"o":"m 0 0 l 0 347 l 76 347 l 153 347 l 153 0 l 153 -348 l 76 -348 l 0 -348 l 0 0 m 308 -1 l 308 347 l 386 347 l 464 347 l 464 -1 l 464 -348 l 386 -348 l 308 -348 l 308 -1 "},"v5b":{"x_min":-441,"x_max":439.640625,"ha":449,"o":"m -428 -2 b -421 0 -427 -1 -424 0 b -406 -6 -416 0 -409 -2 b -400 -31 -401 -12 -400 -15 b -1 -352 -392 -215 -215 -352 b 58 -349 19 -352 38 -351 b 398 -31 250 -326 392 -192 b 404 -6 398 -15 400 -12 b 419 -1 408 -2 413 -1 b 439 -13 427 -1 435 -5 b 439 -29 439 -16 439 -22 b 434 -105 439 -48 438 -80 b 0 -489 397 -333 213 -489 b -68 -484 -23 -489 -44 -488 b -441 -36 -280 -452 -436 -263 b -441 -30 -441 -34 -441 -31 b -428 -2 -441 -11 -439 -5 m -13 -9 b -1 -8 -9 -8 -5 -8 b 50 -36 19 -8 39 -19 b 61 -72 57 -47 61 -59 b 50 -106 61 -84 57 -97 b -1 -134 39 -124 19 -134 b -46 -115 -17 -134 -34 -129 b -62 -72 -57 -102 -62 -87 b -13 -9 -62 -44 -44 -16 "},"v5c":{"x_min":0,"x_max":447.8125,"ha":457,"o":"m 0 -87 l 0 0 l 223 0 l 447 0 l 447 -87 l 447 -174 l 223 -174 l 0 -174 l 0 -87 "},"v5d":{"x_min":-1.359375,"x_max":592.078125,"ha":604,"o":"m 280 692 b 295 694 283 692 289 694 b 310 692 300 694 307 692 b 357 630 340 684 357 657 b 336 580 357 612 351 594 b 311 538 321 566 311 549 b 352 492 311 512 330 492 b 366 495 357 492 362 492 b 397 553 390 503 397 517 b 415 603 397 576 402 591 b 460 623 427 617 443 623 b 509 599 479 623 498 614 b 522 559 518 587 522 573 b 494 506 522 538 513 519 b 451 495 481 498 473 496 b 415 488 432 495 426 494 b 394 449 404 483 394 464 b 394 448 394 448 394 448 l 394 440 l 397 433 b 428 409 404 420 413 413 b 438 408 431 408 435 408 b 479 431 450 408 462 415 b 528 455 495 448 510 455 b 548 452 534 455 541 453 b 592 391 577 442 592 416 b 549 331 592 365 577 340 b 528 327 541 328 534 327 b 479 351 510 327 495 335 b 438 374 464 367 450 374 b 417 369 431 374 424 373 b 394 333 402 360 394 348 b 400 312 394 326 396 319 b 451 287 408 294 420 288 b 513 258 484 285 499 278 b 522 223 519 247 522 234 b 461 159 522 190 496 159 b 449 161 457 159 453 159 b 397 229 416 167 397 191 b 366 288 397 265 390 278 b 352 290 362 290 357 290 b 315 262 336 290 321 280 b 311 245 313 256 311 251 b 334 204 311 233 318 220 b 355 170 348 190 351 184 b 357 152 356 166 357 159 b 355 136 357 147 356 140 b 295 88 345 104 321 88 b 232 152 264 88 232 112 b 255 204 232 174 238 186 b 279 244 273 222 279 231 l 279 245 b 238 290 279 270 259 290 b 224 288 234 290 228 290 b 193 229 200 278 193 265 b 141 161 193 191 174 167 b 129 159 137 159 133 159 b 68 223 93 159 68 190 b 77 258 68 234 70 247 b 138 287 91 278 106 285 b 185 302 166 287 175 291 b 196 333 193 312 196 323 b 174 369 196 347 187 360 b 152 374 166 373 159 374 b 111 351 140 374 126 367 b 62 327 95 335 80 327 b 51 328 58 327 54 327 b -1 391 16 334 -1 363 b 53 455 -1 420 17 449 b 62 455 57 455 59 455 b 111 431 80 455 95 448 b 152 408 127 415 140 408 b 161 409 155 408 159 408 b 193 433 176 413 186 420 l 196 440 l 196 448 b 196 451 196 449 196 449 b 190 471 196 459 194 463 b 137 495 182 489 167 495 l 134 495 l 134 495 b 68 560 95 495 68 521 b 129 623 68 596 95 623 b 144 621 134 623 138 623 b 193 553 175 614 193 589 b 224 495 193 517 200 503 b 238 492 228 492 234 492 b 279 538 259 492 279 512 b 254 580 279 549 269 566 b 232 630 239 594 232 612 b 280 692 232 657 250 684 m 307 456 b 295 458 303 458 299 458 b 230 391 258 458 230 426 b 236 360 230 381 231 371 b 295 324 249 337 272 324 b 353 360 318 324 341 337 b 360 391 357 370 360 381 b 307 456 360 421 340 451 "},"v62":{"x_min":46.28125,"x_max":669.671875,"ha":563,"o":"m 183 376 b 189 376 185 376 187 376 b 212 374 197 376 208 376 b 265 337 234 369 253 355 b 274 317 268 331 273 320 b 274 316 274 317 274 316 b 280 323 276 316 276 319 b 311 358 288 337 299 348 b 319 366 315 360 318 365 b 356 376 326 373 340 376 b 382 371 364 376 374 374 b 428 337 400 366 417 352 b 436 317 431 331 436 320 b 438 316 436 317 436 316 b 442 323 438 316 439 319 b 475 358 451 337 462 348 b 483 366 477 360 481 365 b 518 376 488 373 503 376 b 544 373 528 376 536 376 b 604 285 579 360 604 326 b 597 249 604 273 601 258 b 543 63 596 247 544 70 b 541 54 543 61 541 55 b 540 44 540 51 540 47 b 552 23 540 33 545 23 b 552 23 552 23 552 23 b 647 126 586 29 627 72 b 658 138 651 136 653 138 b 660 138 660 138 660 138 b 669 129 666 137 669 136 b 654 88 669 122 665 109 b 562 -12 631 43 602 9 l 549 -19 b 521 -27 540 -24 530 -27 b 447 30 490 -27 458 -4 b 443 58 445 38 443 48 b 450 93 443 72 446 84 b 504 278 453 97 504 272 b 507 288 506 283 506 287 b 509 298 507 292 509 295 b 491 326 509 310 502 320 b 487 327 490 327 488 327 b 479 324 484 327 483 326 b 441 270 462 316 443 288 b 435 249 441 265 436 254 b 398 127 434 248 419 195 b 362 4 379 61 362 5 b 328 -1 359 -1 362 -1 b 314 -1 323 -1 319 -1 b 302 -1 310 -1 306 -1 b 266 4 266 -1 269 -1 b 265 6 265 5 265 5 b 303 144 265 13 272 34 b 343 278 325 216 343 276 b 344 288 343 281 344 285 b 345 298 345 291 345 295 b 330 326 345 310 340 320 b 323 327 328 327 325 327 b 317 324 322 327 321 326 b 279 270 300 316 281 288 b 273 249 279 265 274 254 b 236 127 272 248 255 195 b 200 4 216 61 200 5 b 164 -1 197 -1 198 -1 b 151 -1 161 -1 156 -1 b 140 -1 147 -1 142 -1 b 103 4 104 -1 106 -1 b 103 6 103 5 103 5 b 141 144 103 13 108 34 b 181 278 161 216 179 276 b 182 288 181 281 181 285 b 183 298 182 291 183 295 b 168 324 183 310 178 320 b 160 327 166 326 163 327 b 141 320 156 327 151 324 b 69 230 112 305 85 272 b 57 215 65 217 62 215 b 55 215 57 215 55 215 b 46 224 49 215 46 217 b 59 260 46 231 50 242 b 151 363 81 306 112 341 b 161 369 155 365 160 367 b 183 376 166 371 174 374 "},"v68":{"x_min":-597.53125,"x_max":596.171875,"ha":608,"o":"m -533 324 b -525 327 -530 326 -528 327 b -504 305 -514 327 -504 317 b -504 305 -504 305 -504 305 b -513 284 -504 299 -504 299 b -556 112 -541 226 -556 167 b -545 33 -556 84 -552 58 b -524 -20 -541 15 -532 -9 l -522 -23 l -491 15 l -413 111 b -355 174 -367 169 -363 174 b -351 174 -353 174 -352 174 b -254 86 -343 174 -348 179 b -168 -1 -208 37 -168 -1 b -100 84 -168 -1 -137 37 b -23 173 -28 173 -29 172 b -19 174 -21 174 -20 174 b -8 173 -14 174 -10 173 b 155 11 -5 172 43 123 l 166 -1 l 168 1 l 170 4 l 170 130 b 171 260 170 256 170 258 b 191 274 175 269 183 274 b 205 267 196 274 201 272 b 212 158 212 262 210 273 l 212 56 l 257 112 b 311 173 304 172 304 172 b 317 174 313 174 314 174 b 326 173 319 174 323 173 b 490 11 329 172 366 134 l 502 -1 l 530 34 b 568 76 560 72 563 74 b 575 77 570 77 573 77 b 596 56 586 77 596 68 b 594 48 596 54 596 51 b 417 -172 592 41 424 -166 b 405 -176 415 -174 409 -176 b 396 -174 401 -176 398 -176 b 307 -87 393 -173 372 -152 b 221 -1 259 -38 221 -1 b 216 -6 221 -1 219 -2 l 212 -12 l 212 -147 b 212 -210 212 -173 212 -194 b 205 -292 212 -297 210 -287 b 191 -299 201 -297 196 -299 b 172 -287 183 -299 175 -295 b 170 -174 171 -284 171 -284 l 170 -63 l 127 -117 b 73 -176 84 -170 80 -176 b 68 -176 72 -176 70 -176 b -27 -87 59 -174 65 -180 b -114 0 -74 -38 -112 0 b -182 -86 -114 0 -145 -38 b -258 -174 -253 -174 -253 -173 b -264 -176 -259 -176 -262 -176 b -274 -174 -268 -176 -272 -174 b -438 -11 -277 -173 -348 -102 l -449 0 l -479 -37 b -524 -80 -513 -80 -514 -80 l -524 -80 b -553 -52 -534 -80 -540 -74 b -597 109 -583 -8 -597 48 b -560 280 -597 165 -585 224 b -533 324 -548 310 -540 322 "},"v6c":{"x_min":-1.359375,"x_max":193.28125,"ha":197,"o":"m 78 233 b 87 233 81 233 84 233 b 187 140 132 233 174 195 b 193 102 190 127 193 115 b 43 -113 193 22 136 -62 b 27 -119 36 -116 31 -119 b 19 -108 21 -119 19 -115 b 29 -97 19 -102 20 -101 b 102 13 73 -72 102 -27 b 92 51 102 26 98 40 l 91 54 l 84 54 b 8 104 53 54 21 74 b -1 142 1 116 -1 130 b 78 233 -1 187 31 227 "},"v6f":{"x_min":-80.3125,"x_max":78.9375,"ha":81,"o":"m 63 191 b 69 192 65 192 66 192 b 77 188 72 192 76 191 b 78 183 78 187 78 186 b 74 158 78 179 77 172 l 66 115 b 9 -161 49 30 10 -158 b -10 -187 6 -172 -1 -181 b -34 -194 -17 -191 -25 -194 b -80 -147 -58 -194 -80 -174 b -80 -141 -80 -144 -80 -142 b 9 70 -80 -134 -73 -117 l 49 163 b 63 191 59 188 61 190 "},"v70":{"x_min":0,"x_max":436.921875,"ha":446,"o":"m 213 190 b 217 191 215 191 216 191 b 231 184 223 191 228 188 b 249 154 240 167 246 159 b 419 18 292 91 348 45 b 436 -1 435 11 436 8 b 424 -16 436 -9 434 -13 b 308 -87 394 -26 340 -59 b 231 -186 276 -117 257 -142 b 219 -192 228 -191 225 -192 b 198 -174 209 -192 208 -191 b 47 -33 161 -113 110 -63 b 10 -16 34 -26 17 -19 b 0 -1 2 -13 0 -9 b 17 18 0 8 1 11 b 198 173 95 48 156 101 b 213 190 206 187 208 188 "},"v72":{"x_min":-423.3125,"x_max":421.9375,"ha":431,"o":"m -262 197 b -247 197 -257 197 -253 197 b -118 162 -210 197 -163 184 b 40 45 -61 134 -13 98 b 277 -95 119 -33 200 -81 b 289 -97 281 -97 285 -97 b 378 0 332 -97 371 -55 b 378 11 378 4 378 6 b 302 83 378 55 345 83 b 242 66 283 83 262 77 b 208 56 231 59 219 56 b 148 120 175 56 148 81 b 201 186 148 151 164 172 b 261 198 220 194 240 198 b 420 45 341 198 411 136 b 421 22 421 37 421 29 b 245 -199 421 -93 338 -199 b 238 -198 243 -199 240 -199 b -44 -47 148 -194 50 -141 b -250 86 -114 22 -183 66 b -295 94 -270 91 -283 94 b -315 91 -302 94 -307 94 b -381 4 -356 81 -381 43 b -355 -56 -381 -18 -372 -40 b -298 -81 -338 -73 -319 -81 b -246 -68 -283 -81 -265 -77 b -212 -58 -234 -61 -223 -58 b -178 -69 -200 -58 -189 -62 b -151 -122 -160 -81 -151 -101 b -171 -167 -151 -138 -157 -155 b -239 -195 -185 -181 -213 -192 b -257 -197 -245 -197 -250 -197 b -423 -5 -352 -197 -423 -109 b -412 65 -423 16 -419 40 b -262 197 -389 137 -329 188 "},"v74":{"x_min":-206.890625,"x_max":428.75,"ha":438,"o":"m 389 -351 b 394 -351 390 -351 393 -351 b 428 -385 413 -351 428 -367 b 428 -394 428 -388 428 -391 b 394 -428 426 -406 421 -410 l 332 -473 l 269 -516 l 205 -560 l 141 -603 l 77 -648 l 13 -692 l -50 -737 l -114 -780 l -145 -802 b -171 -813 -157 -810 -163 -813 b -175 -813 -172 -813 -174 -813 b -206 -777 -194 -811 -206 -795 b -202 -760 -206 -771 -205 -766 b -87 -675 -197 -752 -206 -757 l -34 -639 l 83 -557 l 145 -514 l 209 -470 l 272 -427 b 389 -351 375 -356 381 -352 "},"v75":{"x_min":-149.71875,"x_max":148.359375,"ha":151,"o":"m -137 381 b -130 383 -134 383 -133 383 b -111 371 -122 383 -114 378 b -55 224 -110 370 -85 305 b 0 80 -25 145 -1 80 b 54 224 0 80 24 145 b 112 377 114 384 110 373 b 127 384 118 381 122 384 b 148 362 138 384 148 374 l 148 356 l 83 183 b 16 9 47 88 17 11 b -1 0 12 2 5 0 b -14 5 -5 0 -10 1 b -84 183 -19 9 -13 -6 l -149 356 l -149 362 b -137 381 -149 371 -145 378 "},"v78":{"x_min":0,"x_max":193.28125,"ha":197,"o":"m 85 514 b 95 517 88 517 89 517 b 114 505 103 517 110 513 l 115 502 l 115 376 b 115 249 115 306 115 249 b 141 258 117 249 127 252 l 167 266 l 172 266 b 190 254 181 265 187 262 l 193 251 l 193 202 l 193 188 b 187 147 193 149 191 152 b 147 130 183 142 182 141 l 115 119 l 115 9 b 115 -99 115 -51 115 -99 b 141 -91 115 -99 127 -95 b 171 -81 166 -81 167 -81 l 171 -81 b 191 -94 181 -81 189 -87 b 193 -142 191 -97 193 -120 b 191 -195 193 -167 191 -194 b 125 -227 187 -205 187 -204 l 115 -230 l 115 -366 l 115 -503 l 114 -506 b 95 -519 110 -514 102 -519 b 74 -506 87 -519 78 -514 l 73 -503 l 73 -374 b 73 -245 73 -260 73 -245 b 73 -245 73 -245 73 -245 b 55 -252 72 -245 63 -249 l 32 -260 b 19 -263 27 -262 23 -263 b 4 -256 13 -263 8 -260 b 0 -215 0 -251 0 -254 b 0 -199 0 -210 0 -206 l 0 -152 l 1 -149 b 8 -140 2 -145 5 -141 b 42 -127 9 -140 24 -133 l 73 -116 l 73 -5 b 73 23 73 4 73 15 b 73 105 73 70 73 105 b 49 97 73 105 61 101 b 17 88 32 91 23 88 b 4 95 10 88 8 91 b 0 137 0 101 0 98 b 0 151 0 141 0 145 l 0 199 l 1 202 b 43 224 5 212 5 212 l 73 234 l 73 367 l 73 502 l 74 505 b 85 514 77 509 81 513 "},"v79":{"x_min":-1.359375,"x_max":899.703125,"ha":918,"o":"m 307 349 b 332 351 315 351 323 351 b 443 340 367 351 408 347 b 741 47 607 306 720 195 b 744 0 743 31 744 16 b 660 -303 744 -90 713 -206 b 28 -755 534 -531 304 -695 b 14 -756 23 -755 19 -756 b -1 -741 4 -756 -1 -750 b 21 -720 -1 -731 1 -728 b 567 -56 337 -601 548 -344 b 568 -11 568 -41 568 -24 b 442 285 568 129 525 233 b 325 319 406 308 367 319 b 93 177 232 319 137 266 b 84 154 91 170 84 155 b 84 154 84 154 84 154 b 88 156 84 154 85 155 b 159 177 110 170 134 177 b 257 134 194 177 231 162 b 294 41 281 108 294 73 b 171 -97 294 -24 246 -90 b 156 -98 166 -97 161 -98 b 6 74 73 -98 6 -22 b 6 80 6 76 6 79 b 307 349 10 223 141 340 m 839 215 b 845 216 841 216 842 216 b 862 213 852 216 860 215 b 899 163 887 206 899 184 b 872 117 899 145 890 127 b 847 111 865 112 856 111 b 808 130 833 111 818 117 b 796 162 800 140 796 151 b 839 215 796 187 812 212 m 839 -112 b 845 -112 841 -112 842 -112 b 862 -115 852 -112 860 -113 b 899 -165 887 -122 899 -144 b 872 -210 899 -183 890 -201 b 847 -217 865 -215 856 -217 b 808 -198 833 -217 818 -210 b 796 -165 800 -188 796 -177 b 839 -112 796 -140 812 -116 "},"v7c":{"x_min":0,"x_max":300.8125,"ha":307,"o":"m 49 505 b 53 506 50 505 51 506 b 70 496 58 506 62 503 b 81 485 73 492 78 488 l 96 473 l 111 459 l 122 449 l 134 438 l 182 396 l 255 330 b 292 291 292 298 292 298 l 292 290 l 292 284 l 283 270 b 209 36 234 197 209 113 b 288 -170 209 -44 235 -119 b 299 -184 295 -179 299 -181 b 300 -191 300 -187 300 -188 b 285 -206 300 -199 294 -206 b 280 -206 283 -206 281 -206 b 247 -201 270 -202 259 -201 b 176 -222 223 -201 197 -208 b 114 -340 136 -249 114 -292 b 172 -471 114 -384 134 -433 b 185 -492 182 -481 185 -487 b 181 -502 185 -496 183 -499 b 171 -508 176 -505 174 -508 b 152 -498 166 -508 160 -503 b 0 -284 65 -428 12 -352 b 0 -260 0 -278 0 -270 b 1 -238 0 -252 0 -242 b 148 -140 16 -177 73 -140 b 209 -148 167 -140 189 -142 b 215 -149 212 -148 215 -149 b 215 -149 215 -149 215 -149 l 215 -149 b 201 -136 215 -148 209 -142 l 157 -97 l 96 -41 b 17 34 21 24 17 29 b 17 37 17 36 17 36 b 17 38 17 37 17 38 b 25 56 17 44 17 44 b 110 298 81 131 110 219 b 46 474 110 367 88 431 b 38 491 40 480 38 487 b 49 505 38 498 42 502 "},"v7d":{"x_min":-1.359375,"x_max":436.921875,"ha":446,"o":"m 213 205 b 217 205 215 205 216 205 b 234 194 224 205 234 199 b 236 187 234 194 235 190 l 245 167 l 261 129 l 270 106 b 355 -61 294 54 329 -13 b 420 -163 381 -105 402 -138 b 436 -188 435 -184 436 -184 b 436 -191 436 -190 436 -190 b 421 -206 436 -201 431 -206 l 421 -206 l 416 -206 l 405 -201 b 217 -158 347 -172 283 -158 b 31 -201 153 -158 88 -172 l 20 -206 l 14 -206 l 14 -206 b 0 -191 5 -206 0 -201 b -1 -188 0 -190 -1 -190 b 14 -163 -1 -186 0 -184 b 95 -34 36 -136 72 -77 b 166 106 119 8 148 68 l 175 129 l 183 148 l 200 188 b 213 205 205 199 208 202 "},"v7f":{"x_min":0,"x_max":367.5,"ha":375,"o":"m 0 124 l 0 187 l 61 187 l 122 187 l 122 138 l 122 91 l 153 61 l 183 30 l 213 61 l 243 91 l 243 138 l 243 187 l 306 187 l 367 187 l 367 124 l 367 61 l 321 61 l 274 61 l 243 30 l 213 0 l 243 -31 l 274 -62 l 321 -62 l 367 -62 l 367 -124 l 367 -188 l 306 -188 l 243 -188 l 243 -140 l 243 -93 l 213 -62 l 183 -31 l 153 -62 l 122 -93 l 122 -140 l 122 -188 l 61 -188 l 0 -188 l 0 -124 l 0 -62 l 46 -62 l 92 -62 l 123 -31 l 153 0 l 123 30 l 92 61 l 46 61 l 0 61 l 0 124 "},"v80":{"x_min":29.9375,"x_max":420.578125,"ha":371,"o":"m 115 345 b 221 347 117 345 166 347 b 411 345 306 347 409 345 b 420 330 416 342 420 335 b 415 319 420 326 419 321 b 178 118 397 303 179 118 b 178 117 178 118 178 117 b 181 117 178 117 178 117 b 189 117 182 117 185 117 b 193 117 190 117 191 117 b 247 98 215 117 232 111 b 296 75 266 83 280 76 b 302 75 299 75 300 75 b 322 91 311 75 315 79 b 322 91 322 91 322 91 b 322 91 322 91 322 91 b 319 91 322 91 321 91 b 313 90 318 90 315 90 b 283 107 300 90 288 97 b 277 126 279 114 277 121 b 319 167 277 149 295 167 b 319 167 319 167 319 167 b 362 118 347 167 362 147 b 355 82 362 108 359 96 b 311 33 349 65 340 55 b 224 1 284 12 253 1 b 194 5 213 1 204 2 b 168 18 183 8 178 11 b 110 36 151 30 130 36 b 57 15 88 36 68 29 b 47 11 54 12 51 11 b 31 20 40 11 34 13 b 29 26 31 22 29 25 b 68 66 29 36 39 45 b 285 250 73 71 281 248 b 285 250 285 250 285 250 b 231 252 285 252 261 252 b 137 250 190 252 141 250 b 93 227 122 248 110 241 b 78 220 88 222 83 220 b 66 227 74 220 70 222 b 63 234 65 229 63 231 b 85 291 63 241 69 252 b 115 345 108 342 108 344 "},"v81":{"x_min":0,"x_max":428.75,"ha":438,"o":"m 262 186 b 273 186 266 186 272 186 b 274 186 273 186 274 186 b 285 186 274 186 280 186 b 428 48 375 181 428 122 b 386 -68 428 12 416 -29 b 155 -187 329 -145 236 -187 b 12 -111 92 -187 38 -162 b 0 -51 4 -91 0 -72 b 262 186 0 58 122 179 m 366 131 b 352 134 362 133 357 134 b 219 81 321 134 269 115 b 47 -111 126 23 50 -62 b 47 -112 47 -111 47 -112 b 77 -136 47 -129 58 -136 b 264 -45 118 -136 194 -101 b 382 109 336 12 382 76 b 366 131 382 120 377 129 "},"v83":{"x_min":-1.359375,"x_max":847.96875,"ha":865,"o":"m 488 1499 b 495 1500 490 1500 492 1500 b 541 1465 507 1500 521 1490 b 679 1078 622 1372 679 1210 b 677 1050 679 1068 677 1060 b 477 642 668 893 604 764 l 443 609 l 431 596 l 431 592 l 438 562 l 449 508 l 460 458 b 481 355 475 390 481 355 b 481 355 481 355 481 355 b 490 356 481 355 485 355 b 528 358 495 356 511 358 b 558 356 540 358 552 356 b 839 95 699 338 808 237 b 847 22 845 72 847 47 b 631 -303 847 -113 766 -242 b 620 -309 623 -308 620 -309 l 620 -310 b 631 -359 620 -310 626 -333 l 646 -435 l 660 -496 b 672 -588 668 -535 672 -563 b 664 -653 672 -610 669 -630 b 383 -875 630 -792 509 -875 b 201 -810 321 -875 257 -855 b 129 -680 151 -768 129 -730 b 274 -530 129 -592 200 -530 b 351 -553 300 -530 326 -538 b 412 -669 393 -582 412 -626 b 287 -805 412 -735 366 -800 l 279 -805 l 285 -809 b 383 -830 318 -823 351 -830 b 586 -718 464 -830 540 -789 b 626 -584 612 -678 626 -631 b 619 -528 626 -566 623 -548 b 612 -495 619 -526 616 -510 b 577 -324 590 -387 577 -324 b 577 -324 577 -324 577 -324 b 568 -326 575 -324 571 -324 b 528 -334 558 -328 537 -333 b 465 -338 506 -337 485 -338 b 24 -11 269 -338 87 -206 b -1 145 8 41 -1 93 b 96 442 -1 249 32 351 b 322 714 166 541 236 626 l 352 745 l 345 782 l 332 843 l 315 921 b 303 984 310 950 304 978 b 295 1082 298 1017 295 1049 b 413 1426 295 1208 336 1329 b 488 1499 436 1456 477 1496 m 549 1301 b 541 1301 547 1301 544 1301 b 411 1207 500 1301 447 1263 b 355 1004 374 1152 355 1079 b 359 942 355 984 356 963 b 371 881 362 927 363 917 l 385 818 b 392 782 389 799 392 784 l 392 782 b 434 828 393 782 424 816 b 607 1165 534 941 594 1060 b 608 1193 608 1175 608 1183 b 597 1270 608 1224 604 1254 b 549 1301 589 1286 571 1299 m 398 528 b 393 555 396 542 393 553 b 392 555 393 555 393 555 b 317 470 390 555 347 505 b 190 298 266 408 212 334 b 127 70 148 227 127 148 b 155 -77 127 19 137 -30 b 468 -303 209 -216 333 -303 b 519 -299 484 -303 502 -302 b 568 -284 541 -295 568 -287 l 568 -284 b 563 -263 568 -284 566 -274 l 534 -120 l 511 -13 l 496 61 l 480 133 b 469 187 472 176 469 187 b 468 188 469 187 469 188 b 416 162 462 188 430 172 b 337 13 364 126 337 69 b 413 -124 337 -40 363 -93 b 428 -144 424 -131 428 -137 b 428 -149 428 -145 428 -148 b 409 -166 426 -161 419 -166 b 394 -162 405 -166 400 -165 b 240 77 302 -122 240 -27 l 240 77 b 430 342 240 197 315 301 l 436 344 l 426 394 l 398 528 m 548 194 b 526 195 540 195 532 195 b 519 195 524 195 521 195 l 514 195 l 518 177 l 539 79 l 552 15 l 566 -48 l 594 -187 l 605 -240 b 612 -266 609 -254 611 -266 b 612 -266 612 -266 612 -266 b 641 -248 613 -266 630 -256 b 744 -98 692 -212 730 -156 b 751 -40 749 -79 751 -59 b 548 194 751 76 665 181 "},"v84":{"x_min":25.859375,"x_max":164.6875,"ha":168,"o":"m 34 369 b 40 370 35 370 38 370 b 59 353 49 370 50 367 b 164 40 122 254 155 158 b 164 0 164 33 164 16 b 164 -40 164 -16 164 -34 b 59 -353 155 -158 122 -254 b 40 -371 53 -366 47 -371 b 34 -370 38 -371 36 -370 b 25 -358 28 -367 25 -363 b 31 -337 25 -352 27 -347 b 92 0 72 -234 92 -117 b 31 335 92 116 72 233 b 25 356 27 345 25 352 b 34 369 25 363 28 366 "},"v86":{"x_min":-571.671875,"x_max":570.3125,"ha":582,"o":"m -386 173 b -381 174 -385 174 -383 174 b -370 173 -377 174 -372 173 b -281 86 -367 172 -347 151 b -196 0 -235 37 -196 0 b -126 84 -196 0 -164 37 b -50 174 -55 173 -57 172 b -44 174 -49 174 -47 174 b -35 173 -42 174 -38 173 b 53 86 -32 172 -12 151 b 138 0 100 37 138 0 b 208 84 140 0 170 37 b 284 174 279 173 277 172 b 289 174 285 174 288 174 b 299 173 294 174 298 173 b 462 11 303 172 338 134 l 475 -1 l 503 34 b 541 76 534 72 536 74 b 548 77 544 77 545 77 b 570 56 560 77 570 68 b 567 48 570 54 568 51 b 392 -172 564 41 397 -166 b 378 -176 387 -174 382 -176 b 368 -174 374 -176 371 -176 b 280 -87 367 -173 345 -152 b 194 0 234 -38 194 0 b 125 -86 194 0 163 -38 b 49 -174 54 -174 55 -173 b 43 -176 47 -176 46 -176 b 34 -174 40 -176 36 -174 b -54 -87 31 -173 10 -152 b -140 0 -102 -38 -140 0 b -209 -86 -141 0 -171 -38 b -285 -174 -280 -174 -279 -173 b -291 -176 -287 -176 -289 -176 b -300 -174 -295 -176 -299 -174 b -464 -12 -304 -173 -340 -137 l -476 0 l -504 -34 b -543 -77 -534 -73 -537 -76 b -549 -79 -545 -79 -547 -79 b -571 -58 -562 -79 -571 -69 b -568 -49 -571 -55 -570 -52 b -392 172 -566 -43 -396 167 b -386 173 -390 172 -387 173 "},"v8b":{"x_min":0,"x_max":319.859375,"ha":326,"o":"m 149 508 b 159 509 152 509 155 509 b 186 494 170 509 181 503 b 190 440 190 487 190 488 l 190 430 l 190 377 l 242 377 l 251 377 b 303 373 298 377 296 377 b 319 345 314 367 319 356 b 304 319 319 335 314 324 b 250 315 296 315 299 315 l 242 315 l 190 315 l 190 262 l 190 252 b 186 198 190 204 190 205 b 159 183 179 188 170 183 b 132 198 148 183 138 188 b 127 252 127 205 127 204 l 127 262 l 127 315 l 76 315 l 68 315 b 14 319 20 315 21 315 b 0 347 4 324 0 335 b 14 373 0 356 4 367 b 68 377 21 377 20 377 l 76 377 l 127 377 l 127 430 l 127 440 b 132 494 127 488 127 487 b 149 508 136 501 142 505 "},"v8c":{"x_min":-330.75,"x_max":329.390625,"ha":336,"o":"m -133 483 b -117 484 -127 484 -122 484 b 31 373 -51 484 9 440 b 35 348 34 365 35 356 b -25 285 35 313 10 285 b -87 331 -55 285 -76 302 b -167 402 -100 376 -133 402 b -191 398 -175 402 -183 401 b -227 341 -215 388 -227 369 b -225 320 -227 334 -227 327 b -13 74 -209 230 -125 133 b 6 65 -4 70 5 66 l 9 63 l 10 65 b 117 231 12 68 40 112 l 189 341 l 242 424 b 268 460 262 456 264 458 b 283 464 273 463 277 464 b 308 438 296 464 308 453 l 308 437 b 287 396 308 430 308 428 l 95 98 l 59 43 l 58 41 l 65 37 b 253 -156 151 -8 217 -77 b 281 -285 272 -199 281 -244 b 148 -481 281 -381 231 -463 b 115 -485 137 -484 126 -485 b -32 -376 51 -485 -9 -442 b -36 -349 -35 -366 -36 -358 b 25 -287 -36 -315 -12 -287 b 85 -333 54 -287 74 -302 b 166 -403 99 -377 133 -403 b 190 -399 174 -403 182 -402 b 225 -342 215 -390 225 -370 b 224 -322 225 -335 225 -328 b 12 -76 208 -231 125 -134 b -8 -66 2 -72 -6 -68 l -10 -65 l -12 -66 b -118 -231 -13 -68 -42 -113 l -190 -342 l -243 -426 b -269 -462 -264 -458 -265 -458 b -284 -466 -274 -464 -279 -466 b -310 -440 -298 -466 -310 -455 l -310 -438 b -288 -398 -310 -430 -308 -430 l -96 -99 l -59 -44 l -59 -43 l -66 -38 b -281 284 -198 33 -281 158 l -281 284 b -133 483 -281 392 -220 474 m 254 177 b 266 179 258 177 262 179 b 319 149 287 179 307 167 b 329 115 326 140 329 127 b 319 79 329 102 326 90 b 268 51 307 61 287 51 b 221 72 250 51 234 58 b 205 115 210 84 205 99 b 254 177 205 142 223 170 m -281 -54 b -269 -52 -277 -52 -273 -52 b -223 -73 -253 -52 -235 -59 b -206 -116 -212 -84 -206 -101 b -216 -151 -206 -129 -209 -141 b -269 -179 -228 -170 -249 -179 b -314 -159 -285 -179 -302 -173 b -330 -116 -325 -147 -330 -131 b -281 -54 -330 -88 -313 -61 "},"v8f":{"x_min":-21.78125,"x_max":362.0625,"ha":369,"o":"m 302 1031 b 308 1032 304 1032 307 1032 b 330 1016 318 1032 325 1027 b 362 867 351 970 362 920 b 340 738 362 824 353 780 l 336 727 l 340 717 b 362 591 355 677 362 634 b 257 323 362 496 325 401 b 204 272 243 306 227 290 b 20 56 129 206 66 133 b -1 18 12 44 0 22 b -19 4 -4 9 -12 4 l -21 4 l -21 140 l -21 276 l -12 277 b 167 333 61 288 127 309 b 319 598 262 388 319 491 b 311 664 319 620 317 642 l 310 673 l 304 664 b 204 548 279 620 250 587 b 20 333 129 483 66 409 b -1 292 12 320 0 298 b -19 280 -4 285 -12 280 l -21 280 l -21 416 l -21 552 l -12 553 b 167 609 61 564 127 585 b 319 874 264 666 319 770 b 294 992 319 914 311 954 b 288 1011 288 1004 288 1007 b 302 1031 288 1021 294 1028 "},"v90":{"x_min":-171.5,"x_max":483.1875,"ha":493,"o":"m -8 631 b -1 632 -6 632 -4 632 b 19 620 8 632 16 628 b 20 495 20 616 20 616 b 20 373 20 427 20 373 b 115 410 20 373 63 390 l 210 448 l 210 531 b 212 620 210 614 210 616 b 231 632 215 628 223 632 b 246 627 236 632 242 631 b 251 541 251 620 251 628 l 251 463 l 315 489 b 387 514 368 509 381 514 b 393 513 390 514 392 514 b 406 494 402 510 406 502 b 397 476 406 487 404 480 b 323 446 396 474 363 462 l 251 417 l 251 283 l 251 148 l 254 151 b 370 199 291 183 332 199 b 415 191 385 199 400 197 b 483 84 458 176 483 134 b 461 0 483 58 476 29 b 332 -142 439 -40 411 -72 l 255 -215 b 231 -229 240 -229 239 -229 b 216 -223 224 -229 220 -227 b 210 -158 210 -217 210 -223 b 210 -120 210 -148 210 -136 l 210 -29 l 205 -34 b 100 -142 182 -65 159 -88 l 23 -215 b -1 -229 9 -229 6 -229 b -19 -217 -9 -229 -16 -224 l -20 -215 l -21 48 l -21 310 l -83 287 b -152 262 -133 266 -145 262 b -157 263 -153 262 -155 262 b -171 283 -166 266 -171 274 b -161 301 -171 290 -167 297 b -91 328 -160 302 -129 315 l -21 356 l -21 487 l -20 617 l -19 621 b -8 631 -17 626 -12 630 m 210 288 b 210 401 210 351 210 401 b 114 365 209 401 167 384 l 20 327 l 20 238 l 20 148 l 21 151 b 140 199 59 183 102 199 b 206 180 164 199 187 192 l 209 177 b 209 177 209 177 209 177 b 210 288 210 177 210 199 m 110 131 b 96 133 106 133 100 133 b 89 133 93 133 91 133 b 24 87 63 129 40 113 l 20 80 l 20 -37 l 20 -156 l 23 -152 b 144 81 96 -72 144 20 l 144 83 b 110 131 144 113 134 126 m 341 131 b 328 133 337 133 332 133 b 322 133 326 133 323 133 b 257 87 296 129 273 113 l 251 80 l 251 -37 l 251 -156 l 255 -152 b 375 81 328 -72 375 20 l 375 83 b 341 131 375 113 367 126 "},"v92":{"x_min":0,"x_max":598.890625,"ha":611,"o":"m 62 181 b 77 183 66 183 72 183 b 91 181 83 183 88 183 b 202 131 100 180 106 177 l 299 87 l 394 131 b 517 183 499 181 502 183 b 519 183 517 183 518 183 b 598 104 567 183 598 144 b 577 49 598 84 592 65 b 518 15 567 38 563 37 b 484 0 499 6 484 0 b 518 -16 484 -1 499 -8 b 577 -51 563 -38 567 -40 b 598 -105 592 -66 598 -86 b 519 -184 598 -145 567 -184 b 517 -184 518 -184 517 -184 b 394 -133 502 -184 499 -183 l 299 -88 l 202 -133 b 81 -184 99 -183 95 -184 b 77 -184 80 -184 78 -184 b 0 -105 29 -184 0 -145 b 20 -51 0 -86 5 -66 b 80 -16 29 -40 34 -38 b 114 -1 98 -8 114 -1 b 80 15 114 0 98 6 b 20 49 34 37 29 38 b 0 104 6 65 0 84 b 62 181 0 140 23 174 m 88 134 b 74 136 85 134 80 136 b 68 134 72 136 69 136 b 46 104 54 130 46 117 b 55 81 46 95 49 88 b 149 34 59 76 53 80 b 224 -1 190 15 224 0 b 144 -38 224 -1 187 -18 b 54 -84 59 -79 58 -79 b 46 -105 49 -90 46 -98 b 76 -137 46 -122 58 -137 b 78 -137 77 -137 77 -137 b 194 -86 87 -137 76 -141 b 298 -36 250 -58 298 -36 b 298 -36 298 -36 298 -36 b 402 -84 299 -36 345 -58 b 518 -137 522 -141 510 -137 b 521 -137 519 -137 519 -137 b 551 -105 539 -137 551 -122 b 541 -83 551 -98 548 -90 b 447 -36 537 -77 544 -81 b 374 -1 406 -16 374 -1 b 447 34 374 0 406 15 b 541 81 544 80 537 76 b 551 104 548 88 551 97 b 521 136 551 120 539 136 b 518 136 519 136 519 136 b 517 136 518 136 517 136 l 517 136 b 402 83 511 136 511 136 b 298 34 345 56 299 34 b 298 34 298 34 298 34 b 194 84 298 34 250 56 b 88 134 137 111 89 133 "},"v93":{"x_min":0,"x_max":438.28125,"ha":447,"o":"m 212 205 b 219 205 213 205 216 205 b 239 183 228 205 231 204 b 421 -163 298 40 363 -83 b 438 -191 434 -180 438 -186 b 436 -197 438 -192 438 -195 b 424 -206 434 -204 431 -206 b 406 -201 420 -206 415 -205 b 216 -156 347 -172 281 -156 b 23 -205 148 -156 80 -173 b 14 -206 20 -206 17 -206 b 0 -191 6 -206 0 -201 b 6 -176 0 -187 1 -183 b 202 192 63 -104 142 45 b 212 205 205 199 208 202 m 264 48 l 249 81 l 243 94 l 242 91 b 89 -126 208 36 137 -66 b 81 -138 85 -133 81 -138 b 81 -138 81 -138 81 -138 b 81 -138 81 -138 81 -138 b 95 -133 81 -138 87 -136 b 280 -94 156 -108 221 -94 b 334 -98 299 -94 317 -95 b 343 -99 338 -99 343 -99 b 343 -99 343 -99 343 -99 b 338 -94 343 -99 341 -97 b 264 48 318 -58 287 1 "},"v94":{"x_min":-149.71875,"x_max":148.359375,"ha":151,"o":"m -9 215 b 0 217 -6 217 -4 217 b 19 205 8 217 14 213 b 20 142 20 202 20 201 l 20 84 l 23 84 b 144 -27 81 74 129 30 b 148 -66 147 -40 148 -54 b 36 -213 148 -134 103 -197 b 0 -219 24 -217 12 -219 b -145 -104 -68 -219 -129 -173 b -149 -68 -148 -91 -149 -79 b -24 84 -149 6 -98 74 l -21 84 l -21 142 b -19 205 -20 201 -20 202 b -9 215 -17 209 -13 213 m -21 -15 b -23 41 -21 37 -21 41 b -23 41 -23 41 -23 41 b -76 11 -35 40 -62 26 b -108 -65 -98 -11 -108 -38 b -1 -176 -108 -122 -65 -176 b 107 -65 63 -176 107 -122 b 74 11 107 -38 96 -11 b 20 41 61 26 32 41 b 20 -15 20 41 20 15 b 19 -74 20 -72 20 -72 b 0 -87 14 -83 6 -87 b -19 -74 -8 -87 -16 -83 b -21 -15 -20 -72 -20 -72 "},"v95":{"x_min":0,"x_max":406.96875,"ha":415,"o":"m 55 181 b 70 183 61 183 66 183 b 111 170 85 183 99 179 b 160 130 115 167 137 149 l 202 95 l 245 130 b 319 181 299 176 302 179 b 334 183 325 183 330 183 b 406 109 375 183 406 148 b 401 81 406 99 405 91 b 348 24 394 65 390 59 b 318 -1 332 11 318 0 b 348 -26 318 -1 332 -12 b 401 -83 390 -61 394 -66 b 406 -111 405 -93 406 -101 b 334 -184 406 -149 375 -184 b 319 -183 330 -184 325 -184 b 245 -131 302 -180 299 -177 l 202 -97 l 160 -131 b 85 -183 107 -177 103 -180 b 70 -184 80 -184 76 -184 b 0 -111 31 -184 0 -149 b 4 -83 0 -101 1 -93 b 58 -26 10 -66 16 -61 b 88 -1 74 -12 88 -1 b 58 24 88 0 74 11 b 10 69 23 54 17 59 b 0 109 2 81 0 95 b 55 181 0 142 21 173 m 83 133 b 72 136 78 136 76 136 b 57 131 66 136 61 134 b 46 109 49 126 46 117 b 50 93 46 104 47 98 b 107 45 51 91 77 70 b 160 0 137 20 160 0 b 107 -47 160 -1 137 -22 b 50 -94 77 -72 51 -93 b 46 -111 47 -99 46 -105 b 59 -134 46 -120 50 -130 b 72 -137 62 -136 68 -137 b 83 -136 76 -137 80 -136 b 144 -84 84 -134 107 -116 b 202 -36 176 -58 202 -36 b 261 -84 202 -36 230 -58 b 323 -136 299 -116 321 -134 b 334 -137 326 -136 330 -137 b 345 -134 338 -137 343 -136 b 360 -111 355 -130 360 -120 b 355 -94 360 -105 359 -99 b 299 -47 353 -93 329 -72 b 245 0 269 -22 245 -1 b 299 45 245 0 269 20 b 355 93 329 70 353 91 b 360 109 359 98 360 104 b 345 133 360 119 355 129 b 334 136 343 134 338 136 b 323 134 330 136 326 134 b 261 83 321 133 299 115 b 202 34 230 56 202 34 b 144 83 202 34 176 56 b 83 133 106 115 84 133 "},"v97":{"x_min":-228.671875,"x_max":227.3125,"ha":232,"o":"m -217 487 l -213 488 l 0 488 l 212 488 l 216 487 b 225 476 220 484 224 480 l 227 473 l 227 244 l 227 15 l 225 12 b 206 0 223 4 215 0 b 197 1 204 0 200 0 b 187 12 193 4 189 6 l 186 15 l 186 138 l 186 262 l -1 262 l -187 262 l -187 138 l -187 15 l -189 12 b -208 0 -193 4 -200 0 b -227 12 -216 0 -223 4 l -228 15 l -228 244 l -228 473 l -227 476 b -217 487 -225 480 -221 484 "},"v9a":{"x_min":-21.78125,"x_max":367.5,"ha":375,"o":"m 230 1031 b 238 1032 232 1032 235 1032 b 259 1014 245 1032 251 1027 b 367 662 330 906 367 782 b 364 602 367 641 367 621 b 232 317 352 488 304 384 b 57 120 155 245 103 187 b -1 18 31 84 6 40 b -19 4 -4 11 -12 4 l -21 4 l -21 159 l -21 315 l -16 315 b 96 335 10 315 62 324 b 315 695 227 380 315 527 b 313 738 315 709 314 724 b 224 991 304 825 273 916 b 216 1013 219 999 216 1007 b 230 1031 216 1021 220 1028 "},"v9b":{"x_min":-24.5,"x_max":313.0625,"ha":319,"o":"m -24 -133 l -24 -5 l -20 -5 b -1 -19 -12 -5 -4 -11 b 142 -213 13 -61 74 -144 b 258 -376 196 -269 230 -315 b 313 -605 295 -449 313 -528 b 292 -742 313 -652 306 -699 b 288 -752 289 -748 288 -752 b 288 -752 288 -752 288 -752 b 292 -764 289 -753 291 -757 b 313 -907 306 -811 313 -860 b 292 -1045 313 -954 306 -1002 b 288 -1054 289 -1050 288 -1054 b 288 -1054 288 -1054 288 -1054 b 292 -1067 289 -1054 291 -1060 b 313 -1210 306 -1113 313 -1161 b 292 -1346 313 -1257 306 -1304 b 288 -1357 289 -1353 288 -1357 b 288 -1357 288 -1357 288 -1357 b 292 -1368 289 -1357 291 -1363 b 313 -1512 306 -1415 313 -1464 b 292 -1648 313 -1560 306 -1605 b 288 -1660 289 -1654 288 -1660 b 288 -1660 288 -1660 288 -1660 b 292 -1671 289 -1660 291 -1665 b 313 -1814 306 -1719 313 -1766 b 250 -2040 313 -1897 291 -1977 b 232 -2062 238 -2057 236 -2059 b 221 -2065 230 -2063 225 -2065 b 200 -2045 210 -2065 201 -2057 b 200 -2043 200 -2044 200 -2044 b 208 -2026 200 -2037 202 -2034 b 269 -1826 249 -1966 269 -1897 b 153 -1544 269 -1726 230 -1625 b -9 -1472 115 -1506 58 -1481 b -21 -1471 -14 -1471 -19 -1471 l -24 -1471 l -24 -1343 l -24 -1215 l -20 -1215 b -1 -1229 -12 -1215 -4 -1221 b 142 -1424 13 -1270 74 -1353 b 257 -1582 196 -1478 228 -1524 b 264 -1594 261 -1589 264 -1594 l 264 -1594 b 265 -1582 264 -1594 264 -1589 b 270 -1525 268 -1562 270 -1544 b 153 -1243 270 -1424 228 -1321 b -9 -1170 115 -1203 58 -1178 b -21 -1168 -14 -1170 -19 -1168 l -24 -1168 l -24 -1041 l -24 -913 l -20 -913 b -1 -927 -12 -913 -4 -918 b 142 -1121 13 -967 74 -1050 b 257 -1281 196 -1175 228 -1221 b 264 -1292 261 -1286 264 -1292 l 264 -1292 b 265 -1279 264 -1292 264 -1286 b 270 -1222 268 -1261 270 -1242 b 153 -941 270 -1121 228 -1018 b -9 -867 115 -900 58 -875 b -21 -866 -14 -867 -19 -866 l -24 -866 l -24 -738 l -24 -610 l -20 -610 b -1 -624 -12 -610 -4 -616 b 142 -818 13 -664 74 -749 b 257 -978 196 -873 228 -918 b 264 -989 261 -984 264 -989 l 264 -989 b 265 -977 264 -989 264 -984 b 270 -920 268 -959 270 -939 b 153 -638 270 -818 228 -716 b -9 -564 115 -598 58 -573 b -21 -563 -14 -564 -19 -563 l -24 -563 l -24 -435 l -24 -308 l -20 -308 b -1 -322 -12 -308 -4 -313 b 142 -516 13 -363 74 -446 b 257 -675 196 -571 228 -616 b 264 -687 261 -681 264 -687 l 264 -687 b 265 -674 264 -687 264 -681 b 270 -617 268 -656 270 -637 b 153 -335 270 -516 228 -413 b -9 -262 115 -295 58 -270 b -21 -260 -14 -262 -19 -260 l -24 -260 l -24 -133 "},"v9c":{"x_min":-166.0625,"x_max":-25.859375,"ha":0,"o":"m -49 369 b -42 370 -46 369 -44 370 b -27 360 -36 370 -29 366 b -25 355 -27 359 -25 358 b -32 335 -25 351 -28 347 b -92 52 -66 248 -87 159 b -93 -1 -93 43 -93 20 b -92 -54 -93 -23 -93 -45 b -32 -337 -85 -162 -66 -251 b -25 -355 -27 -349 -25 -352 b -42 -371 -25 -365 -32 -371 b -61 -353 -50 -371 -51 -369 b -163 -63 -119 -262 -153 -165 b -166 -1 -166 -37 -166 -31 b -163 62 -166 30 -166 36 b -61 352 -153 163 -119 260 b -49 369 -54 365 -51 366 "},"v9e":{"x_min":0,"x_max":607.0625,"ha":619,"o":"m 243 631 b 250 632 246 632 249 632 b 270 620 259 632 268 628 l 272 616 l 272 201 l 272 -212 l 270 -216 b 251 -229 268 -224 259 -229 b 227 -215 243 -229 240 -229 l 151 -142 b 32 -16 81 -80 53 -49 b 0 84 9 18 0 52 b 111 199 0 149 42 199 b 137 197 119 199 127 198 b 228 151 168 191 197 177 l 231 148 l 231 383 b 232 620 231 616 231 616 b 243 631 234 624 238 630 m 168 131 b 152 133 163 133 157 133 b 107 102 130 133 111 120 b 106 86 107 97 106 91 b 111 41 106 73 108 56 b 227 -152 125 -13 171 -90 l 231 -156 l 231 -37 l 231 80 l 225 87 b 168 131 210 111 190 126 m 347 631 b 353 632 348 632 351 632 b 374 620 363 632 371 628 b 375 383 375 616 375 616 l 375 148 l 377 151 b 492 199 415 183 454 199 b 537 191 507 199 522 197 b 607 84 582 176 607 134 b 583 0 607 58 598 29 b 455 -142 562 -40 533 -72 l 378 -215 b 355 -229 364 -229 362 -229 b 334 -216 345 -229 337 -224 l 333 -212 l 333 201 l 333 616 l 334 620 b 347 631 337 624 341 630 m 465 131 b 451 133 461 133 455 133 b 445 133 449 133 446 133 b 379 87 419 129 396 113 l 375 80 l 375 -37 l 375 -156 l 378 -152 b 499 81 451 -72 499 20 l 499 83 b 465 131 499 113 490 126 "},"va3":{"x_min":58.53125,"x_max":228.671875,"ha":294,"o":"m 138 371 b 142 373 140 371 141 373 b 178 342 149 373 156 366 b 228 251 217 297 228 278 b 228 244 228 248 228 247 b 176 147 227 212 212 184 b 123 73 152 122 132 93 b 121 62 122 70 121 66 b 145 13 121 48 129 31 b 153 -2 151 6 153 1 b 149 -9 153 -5 152 -6 b 144 -11 148 -11 145 -11 b 129 -1 140 -11 136 -8 b 61 87 89 37 68 68 b 58 113 59 95 58 105 b 110 215 58 144 74 177 b 163 287 134 240 155 269 b 166 299 166 291 166 295 b 141 348 166 313 157 330 b 133 360 134 356 133 358 b 133 363 133 362 133 362 b 138 371 133 367 136 370 "},"va5":{"x_min":0,"x_max":349.8125,"ha":357,"o":"m 88 302 b 103 303 93 302 98 303 b 202 224 149 303 191 270 b 205 199 204 216 205 208 b 178 129 205 173 196 147 l 175 126 l 182 127 b 307 249 236 142 284 190 b 313 259 308 254 311 258 b 329 267 317 265 323 267 b 349 247 340 267 349 259 b 201 -263 349 242 204 -258 b 182 -273 197 -270 190 -273 b 163 -260 174 -273 166 -269 b 161 -256 161 -259 161 -258 b 217 -59 161 -248 170 -220 b 272 129 247 43 272 127 b 272 129 272 129 272 129 b 264 122 272 129 268 126 b 140 80 227 94 183 80 b 36 115 102 80 65 91 b 0 194 10 136 0 165 b 88 302 0 244 32 292 "},"va9":{"x_min":-24.5,"x_max":314.421875,"ha":321,"o":"m -24 -145 l -24 -5 l -20 -5 b 0 -23 -9 -5 -2 -12 b 27 -87 4 -38 14 -66 b 138 -220 53 -136 88 -177 b 235 -328 179 -255 208 -288 b 314 -592 287 -409 314 -501 b 292 -732 314 -639 307 -687 l 289 -742 l 294 -756 b 314 -896 307 -802 314 -849 b 292 -1035 314 -943 307 -991 l 289 -1045 l 294 -1057 b 314 -1197 307 -1104 314 -1152 b 292 -1338 314 -1246 307 -1292 l 289 -1347 l 294 -1360 b 314 -1500 307 -1407 314 -1454 b 273 -1689 314 -1565 300 -1628 b 250 -1712 265 -1710 261 -1712 b 228 -1691 236 -1712 228 -1704 l 228 -1685 l 234 -1675 b 270 -1507 258 -1621 270 -1564 b 98 -1193 270 -1381 209 -1261 b 40 -1174 76 -1179 58 -1174 b -10 -1189 24 -1174 8 -1178 b -20 -1192 -14 -1192 -16 -1192 l -24 -1192 l -24 -1052 l -24 -913 l -20 -913 b 0 -931 -9 -913 -2 -920 b 27 -995 4 -946 14 -974 b 138 -1128 53 -1043 88 -1085 b 257 -1275 190 -1172 228 -1220 b 262 -1283 259 -1279 262 -1283 l 262 -1283 b 269 -1249 264 -1282 268 -1260 b 270 -1206 270 -1233 270 -1220 b 98 -891 270 -1075 206 -957 b 40 -871 76 -877 58 -871 b -10 -886 24 -871 8 -875 b -20 -889 -14 -889 -16 -889 l -24 -889 l -24 -749 l -24 -610 l -20 -610 b 0 -628 -9 -610 -2 -617 b 27 -692 4 -644 14 -671 b 138 -825 53 -741 88 -782 b 257 -973 190 -870 228 -917 b 262 -981 259 -977 262 -981 l 262 -981 b 269 -946 264 -979 268 -957 b 270 -903 270 -931 270 -917 b 98 -588 270 -774 206 -655 b 40 -569 76 -574 58 -569 b -10 -584 24 -569 8 -574 b -20 -587 -14 -587 -16 -587 l -24 -587 l -24 -448 l -24 -308 l -20 -308 b 0 -326 -9 -308 -2 -315 b 27 -390 4 -341 14 -369 b 138 -523 53 -438 88 -480 b 257 -670 190 -567 228 -614 b 262 -678 259 -674 262 -678 b 262 -678 262 -678 262 -678 b 269 -644 264 -677 268 -656 b 270 -601 270 -628 270 -614 b 98 -285 270 -471 206 -352 b 40 -266 76 -273 58 -266 b -10 -281 24 -266 8 -272 b -20 -284 -14 -284 -16 -284 l -24 -284 l -24 -145 "},"vaa":{"x_min":-1.359375,"x_max":752.703125,"ha":768,"o":"m 490 985 b 504 986 495 986 500 986 b 604 907 551 986 593 954 b 607 884 607 900 607 892 b 581 813 607 857 597 831 l 578 810 l 583 811 b 710 932 638 827 687 873 b 714 943 711 936 713 942 b 730 952 720 949 725 952 b 752 931 741 952 752 943 b 200 -946 752 927 204 -941 b 182 -957 197 -953 190 -957 b 163 -945 174 -957 166 -953 b 161 -939 161 -942 161 -942 b 217 -743 161 -931 170 -904 b 272 -555 247 -639 272 -555 b 272 -555 272 -555 272 -555 b 264 -560 272 -555 268 -557 b 140 -603 227 -589 182 -603 b 36 -567 102 -603 65 -592 b -1 -487 12 -548 -1 -517 b 17 -427 -1 -466 5 -445 b 103 -380 38 -395 70 -380 b 191 -433 137 -380 172 -398 b 205 -484 201 -448 205 -466 b 178 -553 205 -509 196 -535 l 175 -557 l 182 -555 b 307 -435 236 -539 284 -494 b 372 -213 308 -430 372 -215 b 372 -213 372 -213 372 -213 b 364 -219 372 -213 368 -216 b 240 -262 328 -247 283 -262 b 137 -226 202 -262 166 -249 b 99 -145 112 -206 99 -176 b 118 -84 99 -124 106 -104 b 204 -38 138 -54 171 -38 b 292 -91 238 -38 273 -56 b 306 -141 302 -106 306 -124 b 279 -212 306 -167 296 -194 l 276 -215 l 281 -213 b 408 -93 336 -198 385 -151 b 473 129 409 -88 473 127 b 473 129 473 129 473 129 b 465 122 473 129 469 126 b 341 80 428 94 383 80 b 236 115 303 80 266 91 b 200 195 213 136 200 165 b 217 256 200 217 206 238 b 304 303 239 287 272 303 b 393 249 338 303 374 285 b 406 199 402 234 406 217 b 379 129 406 173 397 148 l 377 126 l 382 127 b 509 248 436 142 485 190 b 574 470 510 254 574 469 b 574 470 574 470 574 470 b 566 464 574 470 570 467 b 442 421 529 435 484 421 b 337 458 404 421 367 433 b 300 538 314 477 300 508 b 318 598 300 559 306 580 b 404 645 340 630 372 645 b 494 592 439 645 475 627 b 507 541 502 577 507 559 b 480 471 507 516 498 489 l 477 467 l 483 470 b 608 589 537 485 586 531 b 675 811 611 595 675 810 b 675 811 675 811 675 811 b 666 806 675 811 671 809 b 543 763 628 777 585 763 b 438 799 504 763 468 775 b 401 878 412 820 401 849 b 490 985 401 928 434 977 "},"vab":{"x_min":0,"x_max":272.21875,"ha":278,"o":"m 243 631 b 250 632 246 632 249 632 b 270 620 259 632 268 628 l 272 616 l 272 201 l 272 -212 l 270 -216 b 251 -229 268 -224 259 -229 b 227 -215 243 -229 240 -229 l 151 -142 b 32 -16 81 -80 53 -49 b 0 84 9 18 0 52 b 111 199 0 149 42 199 b 137 197 119 199 127 198 b 228 151 168 191 197 177 l 231 148 l 231 383 b 232 620 231 616 231 616 b 243 631 234 624 238 630 m 168 131 b 152 133 163 133 157 133 b 107 102 130 133 111 120 b 106 86 107 97 106 91 b 111 41 106 73 108 56 b 227 -152 125 -13 171 -90 l 231 -156 l 231 -37 l 231 80 l 225 87 b 168 131 210 111 190 126 "},"vad":{"x_min":0,"x_max":873.828125,"ha":892,"o":"m 0 0 l 0 703 l 81 703 l 164 703 l 164 0 l 164 -705 l 81 -705 l 0 -705 l 0 0 m 225 0 l 225 703 l 246 703 l 268 703 l 268 366 l 268 30 l 274 36 b 314 79 284 44 302 63 b 413 302 357 137 392 213 b 432 327 419 324 421 327 b 449 306 443 327 447 322 b 611 115 457 195 529 115 b 651 122 624 115 638 117 b 728 316 705 140 724 188 b 729 388 728 342 729 366 b 671 635 729 533 711 602 b 581 662 649 652 616 662 b 477 637 545 662 510 653 l 475 635 l 477 634 b 503 627 488 632 495 631 b 545 556 532 612 545 584 b 491 480 545 524 526 491 b 465 474 481 476 473 474 b 379 563 417 474 379 516 b 389 602 379 576 382 588 b 541 691 409 641 479 681 b 582 694 555 692 568 694 b 865 462 714 694 834 598 b 873 392 871 440 873 416 b 865 317 873 367 871 341 b 639 84 839 194 748 101 b 612 83 630 83 620 83 b 511 116 577 83 543 94 b 504 120 509 119 506 120 b 504 120 504 120 504 120 b 469 59 504 120 488 93 l 432 -1 l 469 -61 b 504 -122 488 -94 504 -122 b 504 -122 504 -122 504 -122 b 511 -117 506 -122 509 -120 b 612 -84 543 -95 577 -84 b 665 -91 630 -84 647 -87 b 869 -338 771 -122 850 -216 b 873 -392 872 -356 873 -374 b 798 -595 873 -469 847 -539 b 581 -695 741 -662 660 -695 b 406 -626 517 -695 454 -671 b 381 -563 389 -607 381 -585 b 465 -477 381 -519 413 -477 b 545 -559 514 -477 545 -519 b 503 -628 545 -587 532 -613 b 477 -635 495 -632 488 -634 l 475 -637 l 477 -638 b 581 -663 510 -655 545 -663 b 671 -637 616 -663 649 -653 b 729 -391 711 -603 729 -534 b 728 -317 729 -367 728 -344 b 623 -117 722 -173 698 -124 b 611 -116 619 -116 615 -116 b 449 -308 528 -116 457 -198 b 432 -328 447 -323 443 -328 b 413 -303 421 -328 419 -326 b 314 -80 392 -215 357 -138 b 274 -37 302 -65 284 -45 l 268 -31 l 268 -367 l 268 -705 l 246 -705 l 225 -705 l 225 0 "},"vb3":{"x_min":0,"x_max":227.3125,"ha":232,"o":"m 91 213 b 100 215 93 215 96 215 b 227 58 167 215 224 144 b 227 52 227 56 227 54 b 61 -201 227 -43 164 -138 b 29 -216 44 -212 36 -216 b 23 -210 27 -216 24 -213 b 21 -205 21 -208 21 -206 b 34 -192 21 -201 25 -197 b 122 -55 89 -161 122 -106 b 104 6 122 -33 117 -12 l 103 9 l 96 9 b 4 79 57 9 17 38 b 0 112 1 90 0 101 b 91 213 0 163 36 209 "},"vb6":{"x_min":0,"x_max":556.6875,"ha":568,"o":"m 289 545 b 298 546 292 545 295 546 b 318 533 306 546 315 541 b 319 428 319 530 319 528 l 319 327 l 334 327 b 526 223 412 326 485 285 b 543 172 537 206 543 190 b 447 76 543 122 503 76 b 445 76 446 76 446 76 b 359 165 394 77 359 119 b 368 205 359 179 362 192 b 441 251 382 233 412 251 b 455 249 446 251 451 251 b 460 248 458 249 460 248 b 460 248 460 248 460 248 b 454 254 460 249 458 251 b 334 295 419 280 378 294 l 319 295 l 319 4 l 319 -287 l 321 -285 b 328 -285 322 -285 325 -285 b 524 -99 424 -277 507 -198 b 541 -79 526 -84 530 -79 b 556 -97 551 -79 556 -84 b 548 -133 556 -105 553 -117 b 334 -317 521 -233 434 -306 b 322 -319 329 -317 323 -317 l 319 -319 l 319 -424 b 319 -471 319 -444 319 -459 b 313 -541 319 -544 318 -535 b 298 -548 308 -545 303 -548 b 279 -534 289 -548 281 -542 b 277 -424 277 -531 277 -530 l 277 -317 l 273 -317 b 13 -95 153 -305 51 -217 b 0 2 4 -62 0 -29 b 182 295 0 126 66 238 b 274 324 210 309 249 320 l 277 324 l 277 427 b 279 533 277 528 277 530 b 289 545 281 538 285 542 m 277 2 b 277 291 277 161 277 291 b 268 288 277 291 273 290 b 144 1 179 265 144 184 b 276 -284 144 -199 175 -267 l 277 -285 l 277 2 "},"vb9":{"x_min":-122.5,"x_max":121.140625,"ha":124,"o":"m -16 145 b 0 147 -10 147 -5 147 b 121 -1 66 147 121 77 b 114 -49 121 -16 118 -33 b -1 -148 95 -112 47 -148 b -85 -106 -31 -148 -61 -134 b -122 -1 -110 -76 -122 -38 b -16 145 -122 68 -81 134 m 12 111 b 0 113 8 113 4 113 b -68 22 -29 113 -61 73 b -70 0 -69 15 -70 6 b -13 -113 -70 -49 -47 -98 b -1 -115 -9 -115 -5 -115 b 63 -40 24 -115 53 -83 b 68 -1 66 -27 68 -15 b 12 111 68 48 46 97 "},"vba":{"x_min":-118.421875,"x_max":597.53125,"ha":381,"o":"m 460 574 b 464 574 461 574 462 574 b 488 574 470 574 481 574 b 500 573 491 574 498 574 b 594 503 543 570 588 538 b 597 488 596 498 597 494 b 528 417 597 449 564 417 b 502 423 519 417 510 419 b 465 481 477 434 465 458 b 488 528 465 499 472 516 b 490 530 490 530 490 530 b 490 530 490 530 490 530 b 468 517 488 530 475 523 b 349 340 419 485 377 420 b 347 330 348 334 347 330 b 383 328 347 328 363 328 b 428 326 423 328 424 328 b 442 302 438 320 442 312 b 430 281 442 294 438 285 b 385 276 424 277 426 276 l 377 276 l 332 276 l 330 269 b 178 -117 303 126 250 -9 b 1 -249 129 -194 69 -237 b -20 -251 -6 -251 -13 -251 b -114 -187 -65 -251 -100 -227 b -118 -156 -117 -177 -118 -166 b -51 -84 -118 -116 -91 -84 b -31 -87 -46 -84 -39 -86 b 16 -152 0 -95 16 -124 b -12 -205 16 -173 8 -194 b -16 -208 -14 -206 -16 -208 b -14 -208 -16 -208 -14 -208 b -9 -206 -14 -208 -12 -208 b 74 -124 23 -197 54 -166 b 172 224 98 -79 125 22 b 185 276 178 252 183 274 b 185 276 185 276 185 276 b 141 276 185 276 181 276 b 91 280 96 276 96 276 b 77 302 83 285 77 294 b 91 326 77 312 83 320 b 148 328 95 328 96 328 l 198 330 l 202 341 b 460 574 249 473 351 566 "},"vbf":{"x_min":-53.078125,"x_max":513.140625,"ha":485,"o":"m 185 383 b 196 384 187 383 191 384 b 277 334 230 384 259 365 b 288 301 281 324 288 306 b 288 297 288 298 288 297 b 294 302 289 297 291 299 b 394 370 323 338 367 367 b 404 371 398 370 401 371 b 510 272 453 371 498 328 b 513 237 513 262 513 251 b 507 172 513 217 511 192 b 326 -34 487 59 412 -26 b 314 -36 322 -36 318 -36 b 274 -24 298 -36 283 -31 l 265 -16 b 224 44 246 -1 232 20 b 223 49 224 47 223 49 b 223 49 223 49 223 49 b 149 -197 221 48 149 -194 b 149 -198 149 -197 149 -198 b 170 -210 149 -202 155 -205 b 187 -215 174 -210 175 -212 b 204 -231 201 -219 204 -222 b 197 -245 204 -240 202 -242 l 194 -248 l 76 -248 l -42 -248 l -46 -245 b -53 -231 -51 -242 -53 -240 b -35 -215 -53 -222 -49 -217 b -13 -210 -21 -212 -20 -212 b -6 -208 -10 -209 -8 -208 b 0 -206 -6 -208 -2 -206 b 25 -188 13 -201 21 -195 b 163 280 28 -183 163 276 b 166 291 163 283 164 287 b 167 302 167 295 167 299 b 155 324 167 315 161 324 b 155 324 155 324 155 324 b 65 230 125 322 85 280 b 53 215 61 217 58 215 b 51 215 53 215 51 215 b 42 224 46 215 42 217 b 57 263 42 231 47 244 b 140 360 77 305 104 337 b 152 370 144 365 149 369 b 185 383 157 376 172 381 m 374 306 b 366 308 371 308 368 308 b 300 273 348 308 321 294 b 284 254 288 262 287 259 b 280 242 283 249 281 245 b 257 169 279 240 270 213 l 236 98 l 236 93 b 251 48 238 77 243 61 b 279 27 258 37 272 27 b 281 27 279 27 280 27 b 291 31 281 27 287 30 b 396 170 334 52 378 109 b 406 247 402 197 406 224 b 401 277 406 259 405 270 b 374 306 397 290 383 303 "},"vc3":{"x_min":-10.890625,"x_max":299.4375,"ha":294,"o":"m 136 460 b 142 462 137 462 140 462 b 166 449 152 462 161 456 b 171 428 168 446 168 445 b 288 131 194 322 238 209 b 298 115 295 120 296 117 b 299 106 298 112 299 109 b 273 81 299 91 287 81 b 255 86 268 81 261 83 b 155 116 225 104 183 116 l 152 116 l 149 108 b 141 83 148 102 144 91 b 134 48 137 69 134 58 b 149 9 134 34 140 24 b 153 -1 152 5 153 1 b 149 -9 153 -5 152 -6 b 144 -11 148 -11 147 -11 b 122 2 138 -11 133 -6 b 95 61 104 20 95 38 b 107 108 95 74 99 90 b 108 113 107 111 108 112 b 107 113 108 113 108 113 b 102 113 106 113 104 113 b 31 86 76 108 53 98 b 14 80 24 81 20 80 b -10 106 0 80 -10 91 b 0 131 -10 115 -9 116 b 115 430 49 209 91 317 b 136 460 119 451 123 456 "}},"cssFontWeight":"normal","ascender":1903,"underlinePosition":-125,"cssFontStyle":"normal","boundingBox":{"yMin":-2065.375,"xMin":-695.53125,"yMax":1901.578125,"xMax":1159.671875},"resolution":1000,"descender":-2066,"familyName":"VexFlow-18","lineHeight":4093,"underlineThickness":50};// Vex Flow +// Mohit Muthanna +// +// Copyright Mohit Muthanna 2010 +// +// Requires a glyph font to be loaded and Vex.Flow.Font to be set. + +/** + * A quick and dirty static glyph renderer. Renders glyphs from the default + * font defined in Vex.Flow.Font. + * + * @param {!Object} ctx The canvas context. + * @param {number} x_pos X coordinate. + * @param {number} y_pos Y coordinate. + * @param {number} point The point size to use. + * @param {string} val The glyph code in Vex.Flow.Font. + * @param {boolean} nocache If set, disables caching of font outline. + */ +Vex.Flow.renderGlyph = function(ctx, x_pos, y_pos, point, val, nocache) { + var scale = point * 72.0 / (Vex.Flow.Font.resolution * 100.0); + var metrics = Vex.Flow.Glyph.loadMetrics(Vex.Flow.Font, val, !nocache); + Vex.Flow.Glyph.renderOutline(ctx, metrics.outline, scale, x_pos, y_pos); +}; + +/** + * @constructor + */ +Vex.Flow.Glyph = (function() { + function Glyph(code, point, options) { + this.code = code; + this.point = point; + this.context = null; + this.options = { + cache: true, + font: Vex.Flow.Font + }; + + this.width = null; + this.metrics = null; + this.x_shift = 0; + this.y_shift = 0; + + if (options) this.setOptions(options); else this.reset(); + } + + Glyph.prototype = { + setOptions: function(options) { + Vex.Merge(this.options, options); + this.reset(); + }, + + setStave: function(stave) { this.stave = stave; return this; }, + setXShift: function(x_shift) { this.x_shift = x_shift; return this; }, + setYShift: function(y_shift) { this.y_shift = y_shift; return this; }, + setContext: function(context) { this.context = context; return this; }, + getContext: function() { return this.context; }, + + reset: function() { + this.metrics = Vex.Flow.Glyph.loadMetrics(this.options.font, this.code, + this.options.cache); + this.scale = this.point * 72 / (this.options.font.resolution * 100); + }, + + getMetrics: function() { + if (!this.metrics) throw new Vex.RuntimeError("BadGlyph", "Glyph " + + this.code + " is not initialized."); + return { + x_min: this.metrics.x_min * this.scale, + x_max: this.metrics.x_max * this.scale, + width: (this.metrics.x_max - this.metrics.x_min) * this.scale, + height: this.metrics.ha * this.scale + }; + }, + + render: function(ctx, x_pos, y_pos) { + if (!this.metrics) throw new Vex.RuntimeError("BadGlyph", "Glyph " + + this.code + " is not initialized."); + + var outline = this.metrics.outline; + var scale = this.scale; + + Glyph.renderOutline(ctx, outline, scale, x_pos, y_pos); + }, + + renderToStave: function(x) { + if (!this.metrics) throw new Vex.RuntimeError("BadGlyph", "Glyph " + + this.code + " is not initialized."); + if (!this.stave) throw new Vex.RuntimeError("GlyphError", "No valid stave"); + if (!this.context) throw new Vex.RERR("GlyphError", "No valid context"); + + var outline = this.metrics.outline; + var scale = this.scale; + + Glyph.renderOutline(this.context, outline, scale, + x + this.x_shift, this.stave.getYForGlyphs() + this.y_shift); + } + }; + + /* Static methods used to implement loading / unloading of glyphs */ + Glyph.loadMetrics = function(font, code, cache) { + var glyph = font.glyphs[code]; + if (!glyph) throw new Vex.RuntimeError("BadGlyph", "Glyph " + code + + " does not exist in font."); + + var x_min = glyph.x_min; + var x_max = glyph.x_max; + var ha = glyph.ha; + + var outline; + + if (glyph.o) { + if (cache) { + if (glyph.cached_outline) { + outline = glyph.cached_outline; + } else { + outline = glyph.o.split(' '); + glyph.cached_outline = outline; + } + } else { + if (glyph.cached_outline) delete glyph.cached_outline; + outline = glyph.o.split(' '); + } + + return { + x_min: x_min, + x_max: x_max, + ha: ha, + outline: outline + }; + } else { + throw new Vex.RuntimeError("BadGlyph", "Glyph " + this.code + + " has no outline defined."); + } + }; + + Glyph.renderOutline = function(ctx, outline, scale, x_pos, y_pos) { + var outlineLength = outline.length; + + ctx.beginPath(); + + ctx.moveTo(x_pos, y_pos); + + for (var i = 0; i < outlineLength; ) { + var action = outline[i++]; + + switch(action) { + case 'm': + ctx.moveTo(x_pos + outline[i++] * scale, + y_pos + outline[i++] * -scale); + break; + case 'l': + ctx.lineTo(x_pos + outline[i++] * scale, + y_pos + outline[i++] * -scale); + break; + + case 'q': + var cpx = x_pos + outline[i++] * scale; + var cpy = y_pos + outline[i++] * -scale; + + ctx.quadraticCurveTo( + x_pos + outline[i++] * scale, + y_pos + outline[i++] * -scale, cpx, cpy); + break; + + case 'b': + var x = x_pos + outline[i++] * scale; + var y = y_pos + outline[i++] * -scale; + + ctx.bezierCurveTo( + x_pos + outline[i++] * scale, y_pos + outline[i++] * -scale, + x_pos + outline[i++] * scale, y_pos + outline[i++] * -scale, + x, y); + break; + } + } + ctx.fill(); + }; + + return Glyph; +}()); +// Vex Flow +// Mohit Muthanna +// +// Copyright Mohit Cheppudira 2010 + +/** @constructor */ +Vex.Flow.Stave = (function() { + function Stave(x, y, width, options) { + if (arguments.length > 0) this.init(x, y, width, options); + } + + var THICKNESS = (Vex.Flow.STAVE_LINE_THICKNESS > 1 ? + Vex.Flow.STAVE_LINE_THICKNESS : 0); + Stave.prototype = { + init: function(x, y, width, options) { + this.x = x; + this.y = y; + this.width = width; + this.glyph_start_x = x + 5; + this.glyph_end_x = x + width; + this.start_x = this.glyph_start_x; + this.end_x = this.glyph_end_x; + this.context = null; + this.glyphs = []; + this.end_glyphs = []; + this.modifiers = []; // non-glyph stave items (barlines, coda, segno, etc.) + this.measure = 0; + this.clef = "treble"; + this.font = { + family: "sans-serif", + size: 8, + weight: "" + }; + this.options = { + vertical_bar_width: 10, // Width around vertical bar end-marker + glyph_spacing_px: 10, + num_lines: 5, + fill_style: "#999999", + spacing_between_lines_px: 10, // in pixels + space_above_staff_ln: 4, // in staff lines + space_below_staff_ln: 4, // in staff lines + top_text_position: 1 // in staff lines + }; + this.bounds = {x: this.x, y: this.y, w: this.width, h: 0}; + Vex.Merge(this.options, options); + + this.resetLines(); + + this.modifiers.push( + new Vex.Flow.Barline(Vex.Flow.Barline.type.SINGLE, this.x)); // beg bar + this.modifiers.push( + new Vex.Flow.Barline(Vex.Flow.Barline.type.SINGLE, + this.x + this.width)); // end bar + }, + + resetLines: function() { + this.options.line_config = []; + for (var i = 0; i < this.options.num_lines; i++) { + this.options.line_config.push({visible: true}); + } + this.height = (this.options.num_lines + this.options.space_above_staff_ln) * + this.options.spacing_between_lines_px; + this.options.bottom_text_position = this.options.num_lines + 1; + }, + + setNoteStartX: function(x) { this.start_x = x; return this; }, + getNoteStartX: function() { + var start_x = this.start_x; + + // Add additional space if left barline is REPEAT_BEGIN and there are other + // start modifiers than barlines + if (this.modifiers[0].barline == Vex.Flow.Barline.type.REPEAT_BEGIN && + this.modifiers.length > 2) + start_x += 20; + return start_x; + }, + + getNoteEndX: function() { return this.end_x; }, + getTieStartX: function() { return this.start_x; }, + getTieEndX: function() { return this.x + this.width; }, + setContext: function(context) { this.context = context; return this; }, + getContext: function() { return this.context; }, + getX: function() { return this.x; }, + getNumLines: function() { return this.options.num_lines; }, + setNumLines: function(lines) { + this.options.num_lines = parseInt(lines, 10); + this.resetLines(); + return this; + }, + setY: function(y) { this.y = y; return this; }, + + setWidth: function(width) { + this.width = width; + this.glyph_end_x = this.x + width; + this.end_x = this.glyph_end_x; + + // reset the x position of the end barline + this.modifiers[1].setX(this.end_x); + return this; + }, + + getWidth: function() { + return this.width; + }, + + setMeasure: function(measure) { this.measure = measure; return this; }, + + // Bar Line functions + setBegBarType: function(type) { + // Only valid bar types at beginning of stave is none, single or begin repeat + if (type == Vex.Flow.Barline.type.SINGLE || + type == Vex.Flow.Barline.type.REPEAT_BEGIN || + type == Vex.Flow.Barline.type.NONE) { + this.modifiers[0] = new Vex.Flow.Barline(type, this.x); + } + return this; + }, + + setEndBarType: function(type) { + // Repeat end not valid at end of stave + if (type != Vex.Flow.Barline.type.REPEAT_BEGIN) + this.modifiers[1] = new Vex.Flow.Barline(type, this.x + this.width); + return this; + }, + + /** + * Gets the pixels to shift from the beginning of the stave + * following the modifier at the provided index + * @param {Number} index The index from which to determine the shift + * @return {Number} The amount of pixels shifted + */ + getModifierXShift: function(index) { + if (typeof index === 'undefined') index = this.glyphs.length -1; + if (typeof index !== 'number') new Vex.RERR("InvalidIndex", + "Must be of number type"); + + var x = this.glyph_start_x; + var bar_x_shift = 0; + + for (var i = 0; i < index + 1; ++i) { + var glyph = this.glyphs[i]; + x += glyph.getMetrics().width; + bar_x_shift += glyph.getMetrics().width; + } + + // Add padding after clef, time sig, key sig + if (bar_x_shift > 0) bar_x_shift += this.options.vertical_bar_width + 10; + + return bar_x_shift; + }, + + // Coda & Segno Symbol functions + setRepetitionTypeLeft: function(type, y) { + this.modifiers.push(new Vex.Flow.Repetition(type, this.x, y)); + return this; + }, + + setRepetitionTypeRight: function(type, y) { + this.modifiers.push(new Vex.Flow.Repetition(type, this.x, y) ); + return this; + }, + + // Volta functions + setVoltaType: function(type, number_t, y) { + this.modifiers.push(new Vex.Flow.Volta(type, number_t, this.x, y)); + return this; + }, + + // Section functions + setSection: function(section, y) { + this.modifiers.push(new Vex.Flow.StaveSection(section, this.x, y)); + return this; + }, + + // Tempo functions + setTempo: function(tempo, y) { + this.modifiers.push(new Vex.Flow.StaveTempo(tempo, this.x, y)); + return this; + }, + + // Text functions + setText: function(text, position, options) { + this.modifiers.push(new Vex.Flow.StaveText(text, position, options)); + return this; + }, + + getHeight: function() { + return this.height; + }, + + getSpacingBetweenLines: function() { + return this.options.spacing_between_lines_px; + }, + + getBoundingBox: function() { + return new Vex.Flow.BoundingBox(this.x, this.y, this.width, this.getBottomY() - this.y); + // body... + }, + + getBottomY: function() { + var options = this.options; + var spacing = options.spacing_between_lines_px; + var score_bottom = this.getYForLine(options.num_lines) + + (options.space_below_staff_ln * spacing); + + return score_bottom; + }, + + getBottomLineY: function() { + return this.getYForLine(this.options.num_lines); + }, + + getYForLine: function(line) { + var options = this.options; + var spacing = options.spacing_between_lines_px; + var headroom = options.space_above_staff_ln; + + var y = this.y + ((line * spacing) + (headroom * spacing)) - + (THICKNESS / 2); + + return y; + }, + + getYForTopText: function(line) { + var l = line || 0; + return this.getYForLine(-l - this.options.top_text_position); + }, + + getYForBottomText: function(line) { + var l = line || 0; + return this.getYForLine(this.options.bottom_text_position + l); + }, + + getYForNote: function(line) { + var options = this.options; + var spacing = options.spacing_between_lines_px; + var headroom = options.space_above_staff_ln; + var y = this.y + (headroom * spacing) + (5 * spacing) - (line * spacing); + + return y; + }, + + getYForGlyphs: function() { + return this.getYForLine(3); + }, + + addGlyph: function(glyph) { + glyph.setStave(this); + this.glyphs.push(glyph); + this.start_x += glyph.getMetrics().width; + return this; + }, + + addEndGlyph: function(glyph) { + glyph.setStave(this); + this.end_glyphs.push(glyph); + this.end_x -= glyph.getMetrics().width; + return this; + }, + + addModifier: function(modifier) { + this.modifiers.push(modifier); + modifier.addToStave(this, (this.glyphs.length === 0)); + return this; + }, + + addEndModifier: function(modifier) { + this.modifiers.push(modifier); + modifier.addToStaveEnd(this, (this.end_glyphs.length === 0)); + return this; + }, + + addKeySignature: function(keySpec) { + this.addModifier(new Vex.Flow.KeySignature(keySpec)); + return this; + }, + + addClef: function(clef) { + this.clef = clef; + this.addModifier(new Vex.Flow.Clef(clef)); + return this; + }, + + addEndClef: function(clef) { + this.addEndModifier(new Vex.Flow.Clef(clef)); + return this; + }, + + addTimeSignature: function(timeSpec, customPadding) { + this.addModifier(new Vex.Flow.TimeSignature(timeSpec, customPadding)); + return this; + }, + + addEndTimeSignature: function(timeSpec, customPadding) { + this.addEndModifier(new Vex.Flow.TimeSignature(timeSpec, customPadding)); + }, + + addTrebleGlyph: function() { + this.clef = "treble"; + this.addGlyph(new Vex.Flow.Glyph("v83", 40)); + return this; + }, + + /** + * All drawing functions below need the context to be set. + */ + draw: function() { + if (!this.context) throw new Vex.RERR("NoCanvasContext", + "Can't draw stave without canvas context."); + + var num_lines = this.options.num_lines; + var width = this.width; + var x = this.x; + var y; + var glyph; + + // Render lines + for (var line=0; line < num_lines; line++) { + y = this.getYForLine(line); + + this.context.save(); + this.context.setFillStyle(this.options.fill_style); + this.context.setStrokeStyle(this.options.fill_style); + if (this.options.line_config[line].visible) { + this.context.fillRect(x, y, width, Vex.Flow.STAVE_LINE_THICKNESS); + } + this.context.restore(); + } + + // Render glyphs + x = this.glyph_start_x; + for (var i = 0; i < this.glyphs.length; ++i) { + glyph = this.glyphs[i]; + if (!glyph.getContext()) { + glyph.setContext(this.context); + } + glyph.renderToStave(x); + x += glyph.getMetrics().width; + } + + // Render end glyphs + x = this.glyph_end_x; + for (i = 0; i < this.end_glyphs.length; ++i) { + glyph = this.end_glyphs[i]; + if (!glyph.getContext()) { + glyph.setContext(this.context); + } + x -= glyph.getMetrics().width; + glyph.renderToStave(x); + } + + // Draw the modifiers (bar lines, coda, segno, repeat brackets, etc.) + for (i = 0; i < this.modifiers.length; i++) { + // Only draw modifier if it has a draw function + if (typeof this.modifiers[i].draw == "function") + this.modifiers[i].draw(this, this.getModifierXShift()); + } + + // Render measure numbers + if (this.measure > 0) { + this.context.save(); + this.context.setFont(this.font.family, this.font.size, this.font.weight); + var text_width = this.context.measureText("" + this.measure).width; + y = this.getYForTopText(0) + 3; + this.context.fillText("" + this.measure, this.x - text_width / 2, y); + this.context.restore(); + } + + return this; + }, + + // Draw Simple barlines for backward compatability + // Do not delete - draws the beginning bar of the stave + drawVertical: function(x, isDouble) { + this.drawVerticalFixed(this.x + x, isDouble); + }, + + drawVerticalFixed: function(x, isDouble) { + if (!this.context) throw new Vex.RERR("NoCanvasContext", + "Can't draw stave without canvas context."); + + var top_line = this.getYForLine(0); + var bottom_line = this.getYForLine(this.options.num_lines - 1); + if (isDouble) + this.context.fillRect(x - 3, top_line, 1, bottom_line - top_line + 1); + this.context.fillRect(x, top_line, 1, bottom_line - top_line + 1); + }, + + drawVerticalBar: function(x) { + this.drawVerticalBarFixed(this.x + x, false); + }, + + drawVerticalBarFixed: function(x) { + if (!this.context) throw new Vex.RERR("NoCanvasContext", + "Can't draw stave without canvas context."); + + var top_line = this.getYForLine(0); + var bottom_line = this.getYForLine(this.options.num_lines - 1); + this.context.fillRect(x, top_line, 1, bottom_line - top_line + 1); + }, + + /** + * Get the current configuration for the Stave. + * @return {Array} An array of configuration objects. + */ + getConfigForLines: function() { + return this.options.line_config; + }, + + /** + * Configure properties of the lines in the Stave + * @param line_number The index of the line to configure. + * @param line_config An configuration object for the specified line. + * @throws Vex.RERR "StaveConfigError" When the specified line number is out of + * range of the number of lines specified in the constructor. + */ + setConfigForLine: function(line_number, line_config) { + if (line_number >= this.options.num_lines || line_number < 0) { + throw new Vex.RERR("StaveConfigError", + "The line number must be within the range of the number of lines in the Stave."); + } + if (!line_config.hasOwnProperty('visible')) { + throw new Vex.RERR("StaveConfigError", + "The line configuration object is missing the 'visible' property."); + } + if (typeof(line_config.visible) !== 'boolean') { + throw new Vex.RERR("StaveConfigError", + "The line configuration objects 'visible' property must be true or false."); + } + + this.options.line_config[line_number] = line_config; + + return this; + }, + + /** + * Set the staff line configuration array for all of the lines at once. + * @param lines_configuration An array of line configuration objects. These objects + * are of the same format as the single one passed in to setLineConfiguration(). + * The caller can set null for any line config entry if it is desired that the default be used + * @throws Vex.RERR "StaveConfigError" When the lines_configuration array does not have + * exactly the same number of elements as the num_lines configuration object set in + * the constructor. + */ + setConfigForLines: function(lines_configuration) { + if (lines_configuration.length !== this.options.num_lines) { + throw new Vex.RERR("StaveConfigError", + "The length of the lines configuration array must match the number of lines in the Stave"); + } + + // Make sure the defaults are present in case an incomplete set of + // configuration options were supplied. + for (var line_config in lines_configuration) { + // Allow 'null' to be used if the caller just wants the default for a particular node. + if (!lines_configuration[line_config]) { + lines_configuration[line_config] = this.options.line_config[line_config]; + } + Vex.Merge(this.options.line_config[line_config], lines_configuration[line_config]); + } + + this.options.line_config = lines_configuration; + + return this; + } + }; + + return Stave; +}());// Vex Flow Notation +// Mohit Muthanna +// +// Copyright Mohit Muthanna 2010 +// +// Requires vex.js. + +/** @constructor */ +Vex.Flow.StaveConnector = (function() { + function StaveConnector(top_stave, bottom_stave) { + this.init(top_stave, bottom_stave); + } + + // SINGLE_LEFT and SINGLE are the same value for compatibility + // with older versions of vexflow which didn't have right sided + // stave connectors + StaveConnector.type = { + SINGLE_RIGHT : 0, + SINGLE_LEFT : 1, + SINGLE: 1, + DOUBLE: 2, + BRACE: 3, + BRACKET: 4, + BOLD_DOUBLE_LEFT: 5, + BOLD_DOUBLE_RIGHT: 6, + THIN_DOUBLE: 7 + }; + + var THICKNESS = Vex.Flow.STAVE_LINE_THICKNESS; + + StaveConnector.prototype = { + init: function(top_stave, bottom_stave) { + this.width = 3; + this.top_stave = top_stave; + this.bottom_stave = bottom_stave; + this.type = StaveConnector.type.DOUBLE; + this.x_shift = 0; // Mainly used to offset Bold Double Left to align with offset Repeat Begin bars + }, + + setContext: function(ctx) { + this.ctx = ctx; + return this; + }, + + setType: function(type) { + if (type >= StaveConnector.type.SINGLE_RIGHT && + type <= StaveConnector.type.THIN_DOUBLE) + this.type = type; + return this; + }, + + setText: function(text, text_options) { + this.text = text; + this.text_options = { + shift_x: 0, + shift_y: 0 + }; + Vex.Merge(this.text_options, text_options); + + this.font = { + family: "times", + size: 16, + weight: "normal" + }; + return this; + }, + + setFont: function(font) { + Vex.Merge(this.font, font); + }, + + setXShift: function(x_shift){ + if (typeof x_shift !== 'number') { + throw Vex.RERR("InvalidType", "x_shift must be a Number"); + } + + this.x_shift = x_shift; + return this; + }, + + draw: function() { + if (!this.ctx) throw new Vex.RERR( + "NoContext", "Can't draw without a context."); + var topY = this.top_stave.getYForLine(0); + var botY = this.bottom_stave.getYForLine(this.bottom_stave.getNumLines() - 1) + + THICKNESS; + var width = this.width; + var topX = this.top_stave.getX(); + + var isRightSidedConnector = ( + this.type === StaveConnector.type.SINGLE_RIGHT || + this.type === StaveConnector.type.BOLD_DOUBLE_RIGHT || + this.type === StaveConnector.type.THIN_DOUBLE + ); + + if (isRightSidedConnector){ + topX = this.top_stave.getX() + this.top_stave.width; + } + + var attachment_height = botY - topY; + switch (this.type) { + case StaveConnector.type.SINGLE: + width = 1; + break; + case StaveConnector.type.SINGLE_LEFT: + width = 1; + break; + case StaveConnector.type.SINGLE_RIGHT: + width = 1; + break; + case StaveConnector.type.DOUBLE: + topX -= (this.width + 2); + break; + case StaveConnector.type.BRACE: + width = 12; + // May need additional code to draw brace + var x1 = this.top_stave.getX() - 2; + var y1 = topY; + var x3 = x1; + var y3 = botY; + var x2 = x1 - width; + var y2 = y1 + attachment_height/2.0; + var cpx1 = x2 - (0.90 * width); + var cpy1 = y1 + (0.2 * attachment_height); + var cpx2 = x1 + (1.10 * width); + var cpy2 = y2 - (0.135 * attachment_height); + var cpx3 = cpx2; + var cpy3 = y2 + (0.135 * attachment_height); + var cpx4 = cpx1; + var cpy4 = y3 - (0.2 * attachment_height); + var cpx5 = x2 - width; + var cpy5 = cpy4; + var cpx6 = x1 + (0.40 * width); + var cpy6 = y2 + (0.135 * attachment_height); + var cpx7 = cpx6; + var cpy7 = y2 - (0.135 * attachment_height); + var cpx8 = cpx5; + var cpy8 = cpy1; + this.ctx.beginPath(); + this.ctx.moveTo(x1, y1); + this.ctx.bezierCurveTo(cpx1, cpy1, cpx2, cpy2, x2, y2); + this.ctx.bezierCurveTo(cpx3, cpy3, cpx4, cpy4, x3, y3); + this.ctx.bezierCurveTo(cpx5, cpy5, cpx6, cpy6, x2, y2); + this.ctx.bezierCurveTo(cpx7, cpy7, cpx8, cpy8, x1, y1); + this.ctx.fill(); + this.ctx.stroke(); + break; + case StaveConnector.type.BRACKET: + topY -= 4; + botY += 4; + attachment_height = botY - topY; + Vex.Flow.renderGlyph(this.ctx, topX - 5, topY - 3, 40, "v1b", true); + Vex.Flow.renderGlyph(this.ctx, topX - 5, botY + 3, 40, "v10", true); + topX -= (this.width + 2); + break; + case StaveConnector.type.BOLD_DOUBLE_LEFT: + drawBoldDoubleLine(this.ctx, this.type, topX + this.x_shift, topY, botY); + break; + case StaveConnector.type.BOLD_DOUBLE_RIGHT: + drawBoldDoubleLine(this.ctx, this.type, topX, topY, botY); + break; + case StaveConnector.type.THIN_DOUBLE: + width = 1; + break; + } + + if (this.type !== StaveConnector.type.BRACE && + this.type !== StaveConnector.type.BOLD_DOUBLE_LEFT && + this.type !== StaveConnector.type.BOLD_DOUBLE_RIGHT) { + this.ctx.fillRect(topX , topY, width, attachment_height); + } + + // If the connector is a thin double barline, draw the paralell line + if (this.type === StaveConnector.type.THIN_DOUBLE) { + this.ctx.fillRect(topX - 3, topY, width, attachment_height); + } + + // Add stave connector text + if (this.text !== undefined) { + this.ctx.save(); + this.ctx.lineWidth = 2; + this.ctx.setFont(this.font.family, this.font.size, this.font.weight); + var text_width = this.ctx.measureText("" + this.text).width; + + var x = this.top_stave.getX() - text_width - 24 + this.text_options.shift_x; + var y = (this.top_stave.getYForLine(0) + this.bottom_stave.getBottomLineY()) / 2 + + this.text_options.shift_y; + + this.ctx.fillText("" + this.text, x, y + 4); + this.ctx.restore(); + } + } + }; + + function drawBoldDoubleLine(ctx, type, topX, topY, botY){ + if (type !== StaveConnector.type.BOLD_DOUBLE_LEFT && + type !== StaveConnector.type.BOLD_DOUBLE_RIGHT) { + throw Vex.RERR("InvalidConnector", + "A REPEAT_BEGIN or REPEAT_END type must be provided."); + } + + var x_shift = 3; + var variableWidth = 3.5; // Width for avoiding anti-aliasing width issues + var thickLineOffset = 2; // For aesthetics + + if (type === StaveConnector.type.BOLD_DOUBLE_RIGHT) { + x_shift = -5; // Flips the side of the thin line + variableWidth = 3; + } + + // Thin line + ctx.fillRect(topX + x_shift, topY, 1, botY - topY); + // Thick line + ctx.fillRect(topX - thickLineOffset, topY, variableWidth, botY - topY); + } + + return StaveConnector; +}());// Vex Flow +// Mohit Muthanna +// +// Copyright Mohit Cheppudira 2010 + +/** @constructor */ +Vex.Flow.TabStave = (function() { + function TabStave(x, y, width, options) { + if (arguments.length > 0) this.init(x, y, width, options); + } + + Vex.Inherit(TabStave, Vex.Flow.Stave, { + init: function(x, y, width, options) { + var tab_options = { + spacing_between_lines_px: 13, + num_lines: 6, + top_text_position: 1 + }; + + Vex.Merge(tab_options, options); + TabStave.superclass.init.call(this, x, y, width, tab_options); + }, + + getYForGlyphs: function() { + return this.getYForLine(2.5); + }, + + addTabGlyph: function() { + var glyphScale; + var glyphOffset; + + switch(this.options.num_lines) { + case 8: + glyphScale = 55; + glyphOffset = 14; + break; + case 7: + glyphScale = 47; + glyphOffset = 8; + break; + case 6: + glyphScale = 40; + glyphOffset = 1; + break; + case 5: + glyphScale = 30; + glyphOffset = -6; + break; + case 4: + glyphScale = 23; + glyphOffset = -12; + break; + } + + var tabGlyph = new Vex.Flow.Glyph("v2f", glyphScale); + tabGlyph.y_shift = glyphOffset; + this.addGlyph(tabGlyph); + return this; + } + }); + + return TabStave; +}());// Vex Flow +// Copyright Mohit Cheppudira +// +// A formatter for abstract tickable objects, such as notes, chords, +// tabs, etc. + +/** @constructor */ +Vex.Flow.TickContext = (function() { + function TickContext() { + this.init(); + } + + TickContext.prototype = { + init: function() { + this.currentTick = new Vex.Flow.Fraction(0, 1); + this.maxTicks = new Vex.Flow.Fraction(0, 1); + this.minTicks = null; + this.width = 0; + this.padding = 3; // padding on each side (width += padding * 2) + this.pixelsUsed = 0; + this.x = 0; + this.tickables = []; // Notes, tabs, chords, lyrics. + this.notePx = 0; // width of widest note in this context + this.extraLeftPx = 0; // Extra left pixels for modifers & displace notes + this.extraRightPx = 0; // Extra right pixels for modifers & displace notes + + // Ignore this tick context for formatting and justification + this.ignore_ticks = true; + this.preFormatted = false; + this.postFormatted = false; + this.context = null; // Rendering context + }, + + setContext: function(context) { this.context = context; return this; }, + getContext: function() { return this.context; }, + shouldIgnoreTicks: function() { return this.ignore_ticks; }, + getWidth: function() { return this.width + (this.padding * 2); }, + getX: function() { return this.x; }, + setX: function(x) { this.x = x; return this; }, + getPixelsUsed: function() { return this.pixelsUsed; }, + setPixelsUsed: function(pixelsUsed) { this.pixelsUsed = pixelsUsed; return this; }, + setPadding: function(padding) { this.padding = padding; return this; }, + getMaxTicks: function() { return this.maxTicks; }, + getMinTicks: function() { return this.minTicks; }, + getTickables: function() { return this.tickables; }, + + // Get widths context, note and left/right modifiers for formatting + getMetrics: function() { + return { width: this.width, notePx: this.notePx, + extraLeftPx: this.extraLeftPx, extraRightPx: this.extraRightPx }; + }, + + getCurrentTick: function() { return this.currentTick; }, + setCurrentTick: function(tick) { + this.currentTick = tick; + this.preFormatted = false; + }, + + // Get left & right pixels used for modifiers + getExtraPx: function() { + var left_shift = 0; + var right_shift = 0; + var extraLeftPx = 0; + var extraRightPx = 0; + for (var i = 0; i < this.tickables.length; i++) { + extraLeftPx = Math.max(this.tickables[i].extraLeftPx, extraLeftPx); + extraRightPx = Math.max(this.tickables[i].extraRightPx, extraRightPx); + var mContext = this.tickables[i].modifierContext; + if (mContext && mContext != null) { + left_shift = Math.max( left_shift, mContext.state.left_shift); + right_shift = Math.max( right_shift, mContext.state.right_shift); + } + } + return { left: left_shift, right: right_shift, + extraLeft: extraLeftPx, extraRight: extraRightPx }; + }, + + addTickable: function(tickable) { + if (!tickable) { + throw new Vex.RERR("BadArgument", "Invalid tickable added."); + } + + if (!tickable.shouldIgnoreTicks()) { + this.ignore_ticks = false; + + var ticks = tickable.getTicks(); + + if (ticks.value() > this.maxTicks.value()) { + this.maxTicks = ticks.clone(); + } + + if (this.minTicks == null) { + this.minTicks = ticks.clone(); + } else if (ticks.value() < this.minTicks.value()) { + this.minTicks = ticks.clone(); + } + } + + tickable.setTickContext(this); + this.tickables.push(tickable); + this.preFormatted = false; + return this; + }, + + preFormat: function() { + if (this.preFormatted) return; + + for (var i = 0; i < this.tickables.length; ++i) { + var tickable = this.tickables[i]; + tickable.preFormat(); + var metrics = tickable.getMetrics(); + + // Maintain max extra pixels from all tickables in the context + this.extraLeftPx = Math.max(this.extraLeftPx, + metrics.extraLeftPx + metrics.modLeftPx); + this.extraRightPx = Math.max(this.extraRightPx, + metrics.extraRightPx + metrics.modRightPx); + + // Maintain the widest note for all tickables in the context + this.notePx = Math.max(this.notePx, metrics.noteWidth); + + // Recalculate the tick context total width + this.width = this.notePx + + this.extraLeftPx + + this.extraRightPx; + } + + return this; + }, + + postFormat: function() { + if (this.postFormatted) return this; + this.postFormatted = true; + return this; + } + }; + + return TickContext; +}()); +// Vex Flow +// Copyright Mohit Cheppudira +// +// The tickable interface. Tickables are things that sit on a score and +// have a duration, i.e., they occupy space in the musical rendering dimension. + +/** @constructor */ +Vex.Flow.Tickable = (function() { + function Tickable() { + this.init(); + } + + Tickable.prototype = { + init: function() { + this.intrinsicTicks = 0; + this.tickMultiplier = new Vex.Flow.Fraction(1, 1); + this.ticks = new Vex.Flow.Fraction(0, 1); + this.width = 0; + this.x_shift = 0; // Shift from tick context + this.voice = null; + this.tickContext = null; + this.modifierContext = null; + this.modifiers = []; + this.preFormatted = false; + this.postFormatted = false; + this.tuplet = null; + + // This flag tells the formatter to ignore this tickable during + // formatting and justification. It is set by tickables such as BarNote. + this.ignore_ticks = false; + this.context = null; + }, + + setContext: function(context) { this.context = context; }, + getBoundingBox: function() { return null; }, + getTicks: function() { return this.ticks; }, + shouldIgnoreTicks: function() { return this.ignore_ticks; }, + getWidth: function() { return this.width; }, + setXShift: function(x) { this.x_shift = x; }, + + // Every tickable must be associated with a voice. This allows formatters + // and preFormatter to associate them with the right modifierContexts. + getVoice: function() { + if (!this.voice) throw new Vex.RERR("NoVoice", "Tickable has no voice."); + return this.voice; + }, + setVoice: function(voice) { this.voice = voice; }, + + getTuplet: function() { return this.tuplet; }, + setTuplet: function(tuplet) { + // Detach from previous tuplet + var noteCount, beatsOccupied; + + if (this.tuplet) { + noteCount = this.tuplet.getNoteCount(); + beatsOccupied = this.tuplet.getBeatsOccupied(); + + // Revert old multiplier + this.applyTickMultiplier(noteCount, beatsOccupied); + } + + // Attach to new tuplet + if (tuplet) { + noteCount = tuplet.getNoteCount(); + beatsOccupied = tuplet.getBeatsOccupied(); + + this.applyTickMultiplier(beatsOccupied, noteCount); + } + + this.tuplet = tuplet; + + return this; + }, + + /** optional, if tickable has modifiers **/ + addToModifierContext: function(mc) { + this.modifierContext = mc; + // Add modifiers to modifier context (if any) + this.preFormatted = false; + }, + + /** optional, if tickable has modifiers **/ + addModifier: function(mod) { + this.modifiers.push(mod); + this.preFormatted = false; + return this; + }, + + setTickContext: function(tc) { + this.tickContext = tc; + this.preFormatted = false; + }, + + preFormat: function() { + if (this.preFormatted) return; + + this.width = 0; + if (this.modifierContext) { + this.modifierContext.preFormat(); + this.width += this.modifierContext.getWidth(); + } + }, + + postFormat: function() { + if (this.postFormatted) return; + this.postFormatted = true; + return this; + }, + + getIntrinsicTicks: function() { + return this.intrinsicTicks; + }, + setIntrinsicTicks: function(intrinsicTicks) { + this.intrinsicTicks = intrinsicTicks; + this.ticks = this.tickMultiplier.clone().multiply(this.intrinsicTicks); + }, + + getTickMultiplier: function() { + return this.tickMultiplier; + }, + applyTickMultiplier: function(numerator, denominator) { + this.tickMultiplier.multiply(numerator, denominator); + this.ticks = this.tickMultiplier.clone().multiply(this.intrinsicTicks); + } + }; + + return Tickable; +}()); +// [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010. +// +// ## Description +// +// This file implements an abstract interface for notes and chords that +// are rendered on a stave. Notes have some common properties: All of them +// have a value (e.g., pitch, fret, etc.) and a duration (quarter, half, etc.) +// +// Some notes have stems, heads, dots, etc. Most notational elements that +// surround a note are called *modifiers*, and every note has an associated +// array of them. All notes also have a rendering context and belong to a stave. + +Vex.Flow.Note = (function() { + // To create a new note you need to provide a `note_struct`, which consists + // of the following fields: + // + // `type`: The note type (e.g., `r` for rest, `s` for slash notes, etc.) + // `dots`: The number of dots, which affects the duration. + // `duration`: The time length (e.g., `q` for quarter, `h` for half, `8` for eighth etc.) + // + // The range of values for these parameters are available in `src/tables.js`. + function Note(note_struct) { + if (arguments.length > 0) this.init(note_struct); + } + + // ## Prototype Methods + // + // Every note is a tickable, i.e., it can be mutated by the `Formatter` class for + // positioning and layout. + Vex.Inherit(Note, Vex.Flow.Tickable, { + // See constructor above for how to create a note. + init: function(note_struct) { + Note.superclass.init.call(this); + + if (!note_struct) { + throw new Vex.RuntimeError("BadArguments", + "Note must have valid initialization data to identify " + + "duration and type."); + } + + // Parse `note_struct` and get note properties. + var initData = Vex.Flow.parseNoteData(note_struct); + if (!initData) { + throw new Vex.RuntimeError("BadArguments", + "Invalid note initialization object: " + JSON.stringify(note_struct)); + } + + // Set note properties from parameters. + this.duration = initData.duration; + this.dots = initData.dots; + this.noteType = initData.type; + this.setIntrinsicTicks(initData.ticks); + this.modifiers = []; + + // Get the glyph code for this note from the font. + this.glyph = Vex.Flow.durationToGlyph(this.duration, this.noteType); + + if (this.positions && + (typeof(this.positions) != "object" || !this.positions.length)) { + throw new Vex.RuntimeError( + "BadArguments", "Note keys must be array type."); + } + + // Note to play for audio players. + this.playNote = null; + + // Positioning contexts used by the Formatter. + this.tickContext = null; // The current tick context. + this.modifierContext = null; + this.ignore_ticks = false; + + // Positioning variables + this.width = 0; // Width in pixels calculated after preFormat + this.extraLeftPx = 0; // Extra room on left for offset note head + this.extraRightPx = 0; // Extra room on right for offset note head + this.x_shift = 0; // X shift from tick context X + this.left_modPx = 0; // Max width of left modifiers + this.right_modPx = 0; // Max width of right modifiers + this.voice = null; // The voice that this note is in + this.preFormatted = false; // Is this note preFormatted? + this.ys = []; // list of y coordinates for each note + // we need to hold on to these for ties and beams. + + // The render surface. + this.context = null; + this.stave = null; + this.render_options = { + annotation_spacing: 5, + stave_padding: 12 + }; + }, + + // Get and set the play note, which is arbitrary data that can be used by an + // audio player. + getPlayNote: function() { return this.playNote; }, + setPlayNote: function(note) { this.playNote = note; return this; }, + + // Don't play notes by default, call them rests. This is also used by things like + // beams and dots for positioning. + isRest: function() { return false; }, + + // TODO(0xfe): Why is this method here? + addStroke: function(index, stroke) { + stroke.setNote(this); + stroke.setIndex(index); + this.modifiers.push(stroke); + this.setPreFormatted(false); + return this; + }, + + // Get and set the target stave. + getStave: function() { return this.stave; }, + setStave: function(stave) { + this.stave = stave; + this.setYs([stave.getYForLine(0)]); // Update Y values if the stave is changed. + this.context = this.stave.context; + return this; + }, + + // Set the rendering context for the note. + setContext: function(context) { this.context = context; return this; }, + + // Get and set spacing to the left and right of the notes. + getExtraLeftPx: function() { return this.extraLeftPx; }, + getExtraRightPx: function() { return this.extraRightPx; }, + setExtraLeftPx: function(x) { this.extraLeftPx = x; return this; }, + setExtraRightPx: function(x) { this.extraRightPx = x; return this; }, + + // Returns true if this note has no duration (e.g., bar notes, spacers, etc.) + shouldIgnoreTicks: function() { return this.ignore_ticks; }, + + // Get the stave line number for the note. + getLineNumber: function() { return 0; }, + + // Get the stave line number for rest. + getLineForRest: function() { return 0; }, + + // Get the glyph associated with this note. + getGlyph: function() { return this.glyph; }, + + // Set and get Y positions for this note. Each Y value is associated with + // an individual pitch/key within the note/chord. + setYs: function(ys) { this.ys = ys; return this; }, + getYs: function() { + if (this.ys.length === 0) throw new Vex.RERR("NoYValues", + "No Y-values calculated for this note."); + return this.ys; + }, + + // Get the Y position of the space above the stave onto which text can + // be rendered. + getYForTopText: function(text_line) { + if (!this.stave) throw new Vex.RERR("NoStave", + "No stave attached to this note."); + return this.stave.getYForTopText(text_line); + }, + + // Get a `BoundingBox` for this note. + getBoundingBox: function() { return null; }, + + // Returns the voice that this note belongs in. + getVoice: function() { + if (!this.voice) throw new Vex.RERR("NoVoice", "Note has no voice."); + return this.voice; + }, + + // Attach this note to `voice`. + setVoice: function(voice) { + this.voice = voice; + this.preFormatted = false; + return this; + }, + + // Get and set the `TickContext` for this note. + getTickContext: function() { return this.tickContext; }, + setTickContext: function(tc) { + this.tickContext = tc; + this.preFormatted = false; + return this; + }, + + // Accessors for the note type. + getDuration: function() { return this.duration; }, + isDotted: function() { return (this.dots > 0); }, + hasStem: function() { return false; }, + getDots: function() { return this.dots; }, + getNoteType: function() { return this.noteType; }, + setBeam: function() { return this; }, // ignore parameters + + // Attach this note to a modifier context. + setModifierContext: function(mc) { this.modifierContext = mc; return this; }, + + // Attach a modifier to this note. + addModifier: function(modifier, index) { + modifier.setNote(this); + modifier.setIndex(index || 0); + this.modifiers.push(modifier); + this.setPreFormatted(false); + return this; + }, + + // Get the coordinates for where modifiers begin. + getModifierStartXY: function() { + if (!this.preFormatted) throw new Vex.RERR("UnformattedNote", + "Can't call GetModifierStartXY on an unformatted note"); + return {x: this.getAbsoluteX(), y: this.ys[0]}; + }, + + // Get bounds and metrics for this note. + // + // Returns a struct with fields: + // `width`: The total width of the note (including modifiers.) + // `noteWidth`: The width of the note head only. + // `left_shift`: The horizontal displacement of the note. + // `modLeftPx`: Start `X` for left modifiers. + // `modRightPx`: Start `X` for right modifiers. + // `extraLeftPx`: Extra space on left of note. + // `extraRightPx`: Extra space on right of note. + getMetrics: function() { + if (!this.preFormatted) throw new Vex.RERR("UnformattedNote", + "Can't call getMetrics on an unformatted note."); + var modLeftPx = 0; + var modRightPx = 0; + if (this.modifierContext != null) { + modLeftPx = this.modifierContext.state.left_shift; + modRightPx = this.modifierContext.state.right_shift; + } + + var width = this.getWidth(); + return { width: width, + noteWidth: width - + modLeftPx - modRightPx - // used by accidentals and modifiers + this.extraLeftPx - this.extraRightPx, + left_shift: this.x_shift, // TODO(0xfe): Make style consistent + modLeftPx: modLeftPx, + modRightPx: modRightPx, + extraLeftPx: this.extraLeftPx, + extraRightPx: this.extraRightPx }; + }, + + // Get and set width of note. Used by the formatter for positioning. + setWidth: function(width) { this.width = width; }, + getWidth: function() { + if (!this.preFormatted) throw new Vex.RERR("UnformattedNote", + "Can't call GetWidth on an unformatted note."); + return this.width + + (this.modifierContext ? this.modifierContext.getWidth() : 0); + }, + + // Displace note by `x` pixels. + setXShift: function(x) { + this.x_shift = x; + return this; + }, + + // Get `X` position of this tick context. + getX: function() { + if (!this.tickContext) throw new Vex.RERR("NoTickContext", + "Note needs a TickContext assigned for an X-Value"); + return this.tickContext.getX() + this.x_shift; + }, + + // Get the absolute `X` position of this note relative to the stave. + getAbsoluteX: function() { + if (!this.tickContext) throw new Vex.RERR("NoTickContext", + "Note needs a TickContext assigned for an X-Value"); + + // {osition note to left edge of tick context. + var x = this.tickContext.getX(); + if (this.stave) x += this.stave.getNoteStartX() + this.render_options.stave_padding; + return x; + }, + + setPreFormatted: function(value) { + this.preFormatted = value; + + // Maintain the width of left and right modifiers in pixels. + if (this.preFormatted) { + var extra = this.tickContext.getExtraPx(); + this.left_modPx = Math.max(this.left_modPx, extra.left); + this.right_modPx = Math.max(this.right_modPx, extra.right); + } + } + }); + + return Note; +}());// [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010. +// +// ## Description +// +// This file implements `NoteHeads`. `NoteHeads` are typically not manipulated +// directly, but used internally in `StaveNote`. +// +// See `tests/notehead_tests.js` for usage examples. +Vex.Flow.NoteHead = (function() { + var NoteHead = function(head_options) { + if (arguments.length > 0) this.init(head_options); + }; + + // To enable logging for this class. Set `Vex.Flow.NoteHead.DEBUG` to `true`. + function L() { if (NoteHead.DEBUG) Vex.L("Vex.Flow.NoteHead", arguments); } + + + // Draw slashnote head manually. No glyph exists for this. + // + // Parameters: + // * `ctx`: the Canvas context + // * `duration`: the duration of the note. ex: "4" + // * `x`: the x coordinate to draw at + // * `y`: the y coordinate to draw at + // * `stem_direction`: the direction of the stem + function drawSlashNoteHead(ctx, duration, x, y, stem_direction) { + var width = 15 + (Vex.Flow.STEM_WIDTH / 2); + ctx.setLineWidth(Vex.Flow.STEM_WIDTH); + + var fill = false; + if (duration != 1 && + duration != 2 && + duration != "h" && + duration != "w") { + fill = true; + } + + if (!fill) x -= (Vex.Flow.STEM_WIDTH / 2) * stem_direction; + + ctx.beginPath(); + ctx.moveTo(x, y + 11); + ctx.lineTo(x, y + 1); + ctx.lineTo(x + width, y - 10); + ctx.lineTo(x + width, y); + ctx.lineTo(x, y + 11); + ctx.closePath(); + + if (fill) { + ctx.fill(); + } else { + ctx.stroke(); + } + ctx.setLineWidth(1); + } + + // ## Prototype Methods + Vex.Inherit(NoteHead, Vex.Flow.Note, { + init: function(head_options) { + NoteHead.superclass.init.call(this, head_options); + this.index = head_options.index; + this.x = head_options.x || 0; + this.y = head_options.y || 0; + this.note_type = head_options.note_type; + this.duration = head_options.duration; + this.displaced = head_options.displaced || false; + this.stem_direction = head_options.stem_direction || Vex.Flow.StaveNote.STEM_UP; + this.line = head_options.line; + + // Get glyph code based on duration and note type. This could be + // regular notes, rests, or other custom codes. + this.glyph = Vex.Flow.durationToGlyph(this.duration, this.note_type); + if (!this.glyph) { + throw new Vex.RuntimeError("BadArguments", + "No glyph found for duration '" + this.duration + + "' and type '" + this.note_type + "'"); + } + + this.glyph_code = this.glyph.code_head; + this.x_shift = head_options.x_shift; + if (head_options.custom_glyph_code) { + this.custom_glyph = true; + this.glyph_code = head_options.custom_glyph_code; + } + + this.context = null; + this.style = head_options.style; + this.slashed = head_options.slashed; + + Vex.Merge(this.render_options, { + glyph_font_scale: 35, // font size for note heads + stroke_px: 3 // number of stroke px to the left and right of head + }); + + if (head_options.glyph_font_scale) { + this.render_options.glyph_font_scale = head_options.glyph_font_scale; + } + + this.setWidth(this.glyph.head_width); + }, + + // Get the `ModifierContext` category + getCategory: function() { return "notehead"; }, + + // Set the Cavnas context for drawing + setContext: function(context) { this.context = context; return this;}, + + // Get the width of the notehead + getWidth: function() { return this.width; }, + + // Determine if the notehead is displaced + isDisplaced: function() { return this.displaced === true; }, + + // Get/set the notehead's style + // + // `style` is an `object` with the following properties: `shadowColor`, + // `shadowBlur`, `fillStyle`, `strokeStyle` + getStyle: function() { return this.style; }, + setStyle: function(style) { this.style = style; return this; }, + + // Get the glyph data + getGlyph: function(){ return this.glyph; }, + + // Set the X coordinate + setX: function(x){ this.x = x; return this; }, + + // get/set the Y coordinate + getY: function() { return this.y; }, + setY: function(y) { this.y = y; return this; }, + + // Get the stave line the notehead is placed on + getLine: function(){ return this.line; }, + + // Get the canvas `x` coordinate position of the notehead. + getAbsoluteX: function() { + var getAbsoluteX = NoteHead.superclass.getAbsoluteX; + + // If the note has not been preformatted, then get the static x value + // Otherwise, it's been formatted and we should use it's x value relative + // to its tick context + var x = !this.preFormatted ? this.x : getAbsoluteX.call(this); + + return x + (this.displaced ? this.width * this.stem_direction : 0); + }, + + // Get the `BoundingBox` for the `NoteHead` + getBoundingBox: function() { + if (!this.preFormatted) throw new Vex.RERR("UnformattedNote", + "Can't call getBoundingBox on an unformatted note."); + + var spacing = this.stave.getSpacingBetweenLines(); + var half_spacing = spacing/2; + var min_y = this.y - half_spacing; + + return new Vex.Flow.BoundingBox(this.getAbsoluteX(), min_y, this.width, spacing); + }, + + // Apply current style to Canvas `context` + applyStyle: function(context) { + var style = this.getStyle(); + if (style.shadowColor) context.setShadowColor(style.shadowColor); + if (style.shadowBlur) context.setShadowBlur(style.shadowBlur); + if (style.fillStyle) context.setFillStyle(style.fillStyle); + if (style.strokeStyle) context.setStrokeStyle(style.strokeStyle); + return this; + }, + + // Set notehead to a provided `stave` + setStave: function(stave){ + var line = this.getLine(); + + this.stave = stave; + this.setY(stave.getYForNote(line)); + this.context = this.stave.context; + return this; + }, + + // Pre-render formatting + preFormat: function() { + if (this.preFormatted) return this; + + var glyph = this.getGlyph(); + var width = glyph.head_width + this.extraLeftPx + this.extraRightPx; + + this.setWidth(width); + this.setPreFormatted(true); + return this; + }, + + // Draw the notehead + draw: function() { + if (!this.context) throw new Vex.RERR("NoCanvasContext", + "Can't draw without a canvas context."); + + var ctx = this.context; + var head_x = this.getAbsoluteX(); + var y = this.y; + + L("Drawing note head '", this.note_type, this.duration, "' at", head_x, y); + + // Begin and end positions for head. + var stem_direction = this.stem_direction; + var glyph_font_scale = this.render_options.glyph_font_scale; + + var line = this.line; + + // If note above/below the staff, draw the small staff + if (line <= 0 || line >= 6) { + var line_y = y; + var floor = Math.floor(line); + if (line < 0 && floor - line == -0.5) + line_y -= 5; + else if (line > 6 && floor - line == -0.5) + line_y += 5; + ctx.fillRect( + head_x - this.render_options.stroke_px, line_y, + (this.getGlyph().head_width) + + (this.render_options.stroke_px * 2), 1); + } + + if (this.note_type == "s") { + drawSlashNoteHead(ctx, this.duration, + head_x, y, stem_direction); + } else { + if (this.style) { + ctx.save(); + this.applyStyle(ctx); + Vex.Flow.renderGlyph(ctx, head_x, y, glyph_font_scale, this.glyph_code); + ctx.restore(); + } else { + Vex.Flow.renderGlyph(ctx, head_x, y, glyph_font_scale, this.glyph_code); + } + } + } + }); + + return NoteHead; +}()); +// [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010. +// +// ## Description +// +// This file implements the `Stem` object. Generally this object is handled +// by its parent `StemmableNote`. +// +Vex.Flow.Stem = (function() { + var Stem = function(options) { + if (arguments.length > 0) this.init(options); + }; + + // To enable logging for this class. Set `Vex.Flow.Stem.DEBUG` to `true`. + function L() { if (Stem.DEBUG) Vex.L("Vex.Flow.Stem", arguments); } + + // Stem directions + Stem.UP = 1; + Stem.DOWN = -1; + + // Theme + Stem.WIDTH = Vex.Flow.STEM_WIDTH; + Stem.HEIGHT = Vex.Flow.STEM_HEIGHT; + + // ## Prototype Methods + Stem.prototype = { + init: function(options) { + // Default notehead x bounds + this.x_begin = options.x_begin || 0; + this.x_end = options.x_end || 0; + + // Y bounds for top/bottom most notehead + this.y_top = options.y_top || 0; + this.y_bottom = options.y_bottom || 0; + + // Stem base extension + this.y_extend = options.y_extend || 0; + // Stem top extension + this.stem_extension = options.stem_extension || 0; + + // Direction of the stem + this.stem_direction = options.stem_direction || 0; + + // Flag to override all draw calls + this.hide = false; + }, + + // Set the x bounds for the default notehead + setNoteHeadXBounds: function(x_begin, x_end) { + this.x_begin = x_begin; + this.x_end = x_end; + return this; + }, + + // Set the direction of the stem in relation to the noteheads + setDirection: function(direction){ this.stem_direction = direction; }, + + // Set the extension for the stem, generally for flags or beams + setExtension: function(ext) { this.stem_extension = ext; }, + + // The the y bounds for the top and bottom noteheads + setYBounds: function(y_top, y_bottom) { + this.y_top = y_top; + this.y_bottom = y_bottom; + }, + + // The category of the object + getCategory: function() { return "stem"; }, + + // Set the canvas context to render on + setContext: function(context) { this.context = context; return this;}, + + // Gets the entire height for the stem + getHeight: function() { + return ((this.y_bottom - this.y_top) * this.stem_direction) + + ((Stem.HEIGHT + this.stem_extension) * this.stem_direction); + }, + + getBoundingBox: function() { + throw new Vex.RERR("NotImplemented", "getBoundingBox() not implemented."); + }, + + // Get the y coordinates for the very base of the stem to the top of + // the extension + getExtents: function() { + var ys = [this.y_top, this.y_bottom]; + + var top_pixel = this.y_top; + var base_pixel = this.y_bottom; + var stem_height = Stem.HEIGHT + this.stem_extension; + + for (var i = 0; i < ys.length; ++i) { + var stem_top = ys[i] + (stem_height * -this.stem_direction); + + if (this.stem_direction == Stem.DOWN) { + top_pixel = (top_pixel > stem_top) ? top_pixel : stem_top; + base_pixel = (base_pixel < ys[i]) ? base_pixel : ys[i]; + } else { + top_pixel = (top_pixel < stem_top) ? top_pixel : stem_top; + base_pixel = (base_pixel > ys[i]) ? base_pixel : ys[i]; + } + } + + return { topY: top_pixel, baseY: base_pixel }; + }, + + // Render the stem onto the canvas + draw: function() { + if (!this.context) throw new Vex.RERR("NoCanvasContext", + "Can't draw without a canvas context."); + + if (this.hide) return; + + var ctx = this.context; + var stem_x, stem_y; + var stem_direction = this.stem_direction; + + if (stem_direction == Stem.DOWN) { + // Down stems are rendered to the left of the head. + stem_x = this.x_begin + (Stem.WIDTH / 2); + stem_y = this.y_top + 2; + } else { + // Up stems are rendered to the right of the head. + stem_x = this.x_end + (Stem.WIDTH / 2); + stem_y = this.y_bottom - 2; + } + + stem_y += this.y_extend * stem_direction; + + L("Rendering stem - ", "Top Y: ", this.y_top, "Bottom Y: ", this.y_bottom); + + // Draw the stem + ctx.beginPath(); + ctx.setLineWidth(Stem.WIDTH); + ctx.moveTo(stem_x, stem_y); + ctx.lineTo(stem_x, stem_y - this.getHeight()); + ctx.stroke(); + ctx.setLineWidth(1); + } + }; + + return Stem; +}()); +// [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010. +// +// ## Description +// +// `StemmableNote` is an abstract interface for notes with optional stems. +// Examples of stemmable notes are `StaveNote` and `TabNote` +Vex.Flow.StemmableNote = (function(){ + var StemmableNote = function(note_struct) { + if (arguments.length > 0) this.init(note_struct); + }; + + // To enable logging for this class. Set `Vex.Flow.StemmableNote.DEBUG` to `true`. + function L() { if (StemmableNote.DEBUG) Vex.L("Vex.Flow.StemmableNote", arguments); } + + var Stem = Vex.Flow.Stem; + + Vex.Inherit(StemmableNote, Vex.Flow.Note, { + init: function(note_struct){ + StemmableNote.superclass.init.call(this, note_struct); + + this.stem = null; + this.stem_extension_override = null; + this.beam = null; + + }, + + // Get and set the note's `Stem` + getStem: function() {return this.stem; }, + setStem: function(stem) { this.stem = stem; return this; }, + + // Builds and sets a new stem + buildStem: function() { + var stem = new Stem(); + this.setStem(stem); + return this; + }, + + // Get the full length of stem + getStemLength: function() { + return Stem.HEIGHT + this.getStemExtension(); + }, + + // Get the number of beams for this duration + getBeamCount: function(){ + var glyph = this.getGlyph(); + + if (glyph) { + return glyph.beam_count; + } else { + return 0; + } + }, + + // Get the minimum length of stem + getStemMinumumLength: function() { + var length = this.duration == "w" || this.duration == "1" ? 0 : 20; + // if note is flagged, cannot shorten beam + switch (this.duration) { + case "8": + if (this.beam == null) length = 35; + break; + case "16": + if (this.beam == null) + length = 35; + else + length = 25; + break; + case "32": + if (this.beam == null) + length = 45; + else + length = 35; + break; + case "64": + if (this.beam == null) + length = 50; + else + length = 40; + break; + case "128": + if (this.beam == null) + length = 55; + else + length = 45; + } + return length; + }, + + // Get/set the direction of the stem + getStemDirection: function() { return this.stem_direction; }, + setStemDirection: function(direction) { + if (!direction) direction = Stem.UP; + if (direction != Stem.UP && + direction != Stem.DOWN) { + throw new Vex.RERR("BadArgument", "Invalid stem direction: " + + direction); + } + + this.stem_direction = direction; + if (this.stem) { + this.stem.setDirection(direction); + this.stem.setExtension(this.getStemExtension()); + } + + this.beam = null; + if (this.preFormatted) { + this.preFormat(); + } + return this; + }, + + // Get the `x` coordinate of the stem + getStemX: function() { + var x_begin = this.getAbsoluteX() + this.x_shift; + var x_end = this.getAbsoluteX() + this.x_shift + this.glyph.head_width; + + var stem_x = this.stem_direction == Stem.DOWN ? + x_begin : x_end; + + stem_x -= ((Stem.WIDTH / 2) * this.stem_direction); + + return stem_x; + }, + + // Get the `x` coordinate for the center of the glyph. + // Used for `TabNote` stems and stemlets over rests + getCenterGlyphX: function(){ + return this.getAbsoluteX() + this.x_shift + (this.glyph.head_width / 2); + }, + + // Get the stem extension for the current duration + getStemExtension: function(){ + var glyph = this.getGlyph(); + + if (this.stem_extension_override != null) { + return this.stem_extension_override; + } + + if (glyph) { + return this.getStemDirection() === 1 ? glyph.stem_up_extension : + glyph.stem_down_extension; + } + + return 0; + }, + + // Set the stem length to a specific. Will override the default length. + setStemLength: function(height) { + this.stem_extension_override = (height - Stem.HEIGHT); + return this; + }, + + // Get the top and bottom `y` values of the stem. + getStemExtents: function() { + if (!this.ys || this.ys.length === 0) throw new Vex.RERR("NoYValues", + "Can't get top stem Y when note has no Y values."); + + var top_pixel = this.ys[0]; + var base_pixel = this.ys[0]; + var stem_height = Stem.HEIGHT + this.getStemExtension(); + + for (var i = 0; i < this.ys.length; ++i) { + var stem_top = this.ys[i] + (stem_height * -this.stem_direction); + + if (this.stem_direction == Stem.DOWN) { + top_pixel = (top_pixel > stem_top) ? top_pixel : stem_top; + base_pixel = (base_pixel < this.ys[i]) ? base_pixel : this.ys[i]; + } else { + top_pixel = (top_pixel < stem_top) ? top_pixel : stem_top; + base_pixel = (base_pixel > this.ys[i]) ? base_pixel : this.ys[i]; + } + + if(this.noteType == "s" || this.noteType == 'x') { + top_pixel -= this.stem_direction * 7; + base_pixel -= this.stem_direction * 7; + } + } + + L("Stem extents: ", top_pixel, base_pixel); + return { topY: top_pixel, baseY: base_pixel }; + }, + + // Sets the current note's beam + setBeam: function(beam) { this.beam = beam; return this; }, + + // Get the `y` value for the top/bottom modifiers at a specific `text_line` + getYForTopText: function(text_line) { + var extents = this.getStemExtents(); + if (this.hasStem()) { + return Vex.Min(this.stave.getYForTopText(text_line), + extents.topY - (this.render_options.annotation_spacing * (text_line + 1))); + } else { + return this.stave.getYForTopText(text_line); + } + }, + getYForBottomText: function(text_line) { + var extents = this.getStemExtents(); + if (this.hasStem()) { + return Vex.Max(this.stave.getYForTopText(text_line), + extents.baseY + (this.render_options.annotation_spacing * (text_line))); + } else { + return this.stave.getYForBottomText(text_line); + } + }, + + // Post format the note + postFormat: function() { + if (this.beam) { + this.beam.postFormat(); + } + this.postFormatted = true; + return this; + }, + + // Render the stem onto the canvas + drawStem: function(stem_struct){ + if (!this.context) throw new Vex.RERR("NoCanvasContext", + "Can't draw without a canvas context."); + + this.setStem(new Stem(stem_struct)); + this.stem.setContext(this.context).draw(); + } + }); + + return StemmableNote; +}()); +// [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010. +// +// ## Description +// +// This file implements notes for standard notation. This consists of one or +// more `NoteHeads`, an optional stem, and an optional flag. +// +// *Throughout these comments, a "note" refers to the entire `StaveNote`, +// and a "key" refers to a specific pitch/notehead within a note.* +// +// See `tests/stavenote_tests.js` for usage examples. +Vex.Flow.StaveNote = (function() { + var StaveNote = function(note_struct) { + if (arguments.length > 0) this.init(note_struct); + }; + + // To enable logging for this class. Set `Vex.Flow.StaveNote.DEBUG` to `true`. + function L() { if (StaveNote.DEBUG) Vex.L("Vex.Flow.StaveNote", arguments); } + + var Stem = Vex.Flow.Stem; + var NoteHead = Vex.Flow.NoteHead; + + // Stem directions + StaveNote.STEM_UP = Stem.UP; + StaveNote.STEM_DOWN = Stem.DOWN; + + // ## Prototype Methods + // + Vex.Inherit(StaveNote, Vex.Flow.StemmableNote, { + init: function(note_struct) { + StaveNote.superclass.init.call(this, note_struct); + + this.keys = note_struct.keys; + this.clef = note_struct.clef; + this.beam = null; + + // Pull note rendering properties + this.glyph = Vex.Flow.durationToGlyph(this.duration, this.noteType); + if (!this.glyph) { + throw new Vex.RuntimeError("BadArguments", + "Invalid note initialization data (No glyph found): " + + JSON.stringify(note_struct)); + } + + // if true, displace note to right + this.displaced = false; + this.dot_shiftY = 0; + // per-pitch properties + this.keyProps = []; + // for displaced ledger lines + this.use_default_head_x = false; + + // Drawing + this.note_heads = []; + this.modifiers = []; + + Vex.Merge(this.render_options, { + // font size for note heads and rests + glyph_font_scale: 35, + // number of stroke px to the left and right of head + stroke_px: 3 + }); + + this.calculateKeyProps(); + + this.buildStem(); + + // Set the stem direction + if (note_struct.auto_stem) { + this.autoStem(); + } else { + this.setStemDirection(note_struct.stem_direction); + } + + this.buildNoteHeads(); + + // Calculate left/right padding + this.calcExtraPx(); + }, + + // Builds a `Stem` for the note + buildStem: function() { + var glyph = this.getGlyph(); + + var y_extend = 0; + if (glyph.code_head == "v95" || glyph.code_head == "v3e") { + y_extend = -4; + } + + var stem = new Stem({ + y_extend: y_extend + }); + + if (this.isRest()) { + stem.hide = true; + } + + this.setStem(stem); + }, + + // Builds a `NoteHead` for each key in the note + buildNoteHeads: function() { + var stem_direction = this.getStemDirection(); + + var keys = this.getKeys(); + + var last_line = null; + var line_diff = null; + var displaced = false; + + // Draw notes from bottom to top. + var start_i = 0; + var end_i = keys.length; + var step_i = 1; + + // For down-stem notes, we draw from top to bottom. + if (stem_direction === Stem.DOWN) { + start_i = keys.length - 1; + end_i = -1; + step_i = -1; + } + + for (var i = start_i; i != end_i; i += step_i) { + var note_props = this.keyProps[i]; + + var line = note_props.line; + + // Keep track of last line with a note head, so that consecutive heads + // are correctly displaced. + if (last_line === null) { + last_line = line; + } else { + line_diff = Math.abs(last_line - line); + if (line_diff === 0 || line_diff === 0.5) { + displaced = !displaced; + } else { + displaced = false; + this.use_default_head_x = true; + } + } + last_line = line; + + var note_head = new NoteHead({ + duration: this.duration, + note_type: this.noteType, + displaced: displaced, + stem_direction: stem_direction, + custom_glyph_code: note_props.code, + glyph_font_scale: this.render_options.glyph_font_scale, + x_shift: note_props.shift_right, + line: note_props.line + }); + + this.note_heads[i] = note_head; + } + }, + + // Automatically sets the stem direction based on the keys in the note + autoStem: function() { + var auto_stem_direction; + + // Figure out optimal stem direction based on given notes + this.min_line = this.keyProps[0].line; + this.max_line = this.keyProps[this.keyProps.length - 1].line; + var decider = (this.min_line + this.max_line) / 2; + + if (decider < 3) { + auto_stem_direction = 1; + } else { + auto_stem_direction = -1; + } + + this.setStemDirection(auto_stem_direction); + }, + + // Calculates and stores the properties for each key in the note + calculateKeyProps: function() { + var last_line = null; + for (var i = 0; i < this.keys.length; ++i) { + var key = this.keys[i]; + + // All rests use the same position on the line. + // if (this.glyph.rest) key = this.glyph.position; + if (this.glyph.rest) this.glyph.position = key; + var props = Vex.Flow.keyProperties(key, this.clef); + if (!props) { + throw new Vex.RuntimeError("BadArguments", + "Invalid key for note properties: " + key); + } + + // Calculate displacement of this note + var line = props.line; + if (last_line === null) { + last_line = line; + } else { + if (Math.abs(last_line - line) == 0.5) { + this.displaced = true; + props.displaced = true; + + // Have to mark the previous note as + // displaced as well, for modifier placement + if (this.keyProps.length > 0) { + this.keyProps[i-1].displaced = true; + } + } + } + + last_line = line; + this.keyProps.push(props); + } + + // Sort the notes from lowest line to highest line + this.keyProps.sort(function(a, b) { return a.line - b.line; }); + }, + + // Get modifier category for `ModifierContext` + getCategory: function() { return "stavenotes"; }, + + // Get the `BoundingBox` for the entire note + getBoundingBox: function() { + if (!this.preFormatted) throw new Vex.RERR("UnformattedNote", + "Can't call getBoundingBox on an unformatted note."); + + var metrics = this.getMetrics(); + + var w = metrics.width; + var x = this.getAbsoluteX() - metrics.modLeftPx - metrics.extraLeftPx; + + var min_y = 0; + var max_y = 0; + var half_line_spacing = this.getStave().getSpacingBetweenLines() / 2; + var line_spacing = half_line_spacing * 2; + + if (this.isRest()) { + var y = this.ys[0]; + if (this.duration == "w" || this.duration == "h" || + this.duration == "1" || this.duration == "2") { + min_y = y - half_line_spacing; + max_y = y + half_line_spacing; + } else { + min_y = y - (this.glyph.line_above * line_spacing); + max_y = y + (this.glyph.line_below * line_spacing); + } + } else if (this.glyph.stem) { + var ys = this.getStemExtents(); + ys.baseY += half_line_spacing * this.stem_direction; + min_y = Vex.Min(ys.topY, ys.baseY); + max_y = Vex.Max(ys.topY, ys.baseY); + } else { + min_y = null; + max_y = null; + + for (var i=0; i < this.ys.length; ++i) { + var yy = this.ys[i]; + if (i === 0) { + min_y = yy; + max_y = yy; + } else { + min_y = Vex.Min(yy, min_y); + max_y = Vex.Max(yy, max_y); + } + min_y -= half_line_spacing; + max_y += half_line_spacing; + } + } + + return new Vex.Flow.BoundingBox(x, min_y, w, max_y - min_y); + }, + + // Gets the line number of the top or bottom note in the chord. + // If `is_top_note` is `true` then get the top note + getLineNumber: function(is_top_note) { + if(!this.keyProps.length) throw new Vex.RERR("NoKeyProps", + "Can't get bottom note line, because note is not initialized properly."); + var result_line = this.keyProps[0].line; + + // No precondition assumed for sortedness of keyProps array + for(var i=0; i result_line) + result_line = this_line; + else + if(this_line < result_line) + result_line = this_line; + } + + return result_line; + }, + + // Determine if current note is a rest + isRest: function() { return this.glyph.rest; }, + + // Determine if the current note is a chord + isChord: function() { return !this.isRest() && this.keys.length > 1; }, + + // Determine if the `StaveNote` has a stem + hasStem: function() { return this.glyph.stem; }, + + // Get the `y` coordinate for text placed on the top/bottom of a + // note at a desired `text_line` + getYForTopText: function(text_line) { + var extents = this.getStemExtents(); + return Vex.Min(this.stave.getYForTopText(text_line), + extents.topY - (this.render_options.annotation_spacing * (text_line + 1))); + }, + getYForBottomText: function(text_line) { + var extents = this.getStemExtents(); + return Vex.Max(this.stave.getYForTopText(text_line), + extents.baseY + (this.render_options.annotation_spacing * (text_line))); + }, + + // Sets the current note to the provided `stave`. This applies + // `y` values to the `NoteHeads`. + setStave: function(stave) { + var superclass = Vex.Flow.StaveNote.superclass; + superclass.setStave.call(this, stave); + + var ys = this.note_heads.map(function(note_head) { + note_head.setStave(stave); + return note_head.getY(); + }); + + this.setYs(ys); + + var bounds = this.getNoteHeadBounds(); + this.stem.setYBounds(bounds.y_top, bounds.y_bottom); + + return this; + }, + + // Get the pitches in the note + getKeys: function() { return this.keys; }, + + // Get the properties for all the keys in the note + getKeyProps: function() { + return this.keyProps; + }, + + // Check if note is shifted to the right + isDisplaced: function() { + return this.displaced; + }, + + // Sets whether shift note to the right. `displaced` is a `boolean` + setNoteDisplaced: function(displaced) { + this.displaced = displaced; + return this; + }, + + // Get the starting `x` coordinate for a `StaveTie` + getTieRightX: function() { + var tieStartX = this.getAbsoluteX(); + tieStartX += this.glyph.head_width + this.x_shift + this.extraRightPx; + if (this.modifierContext) tieStartX += this.modifierContext.getExtraRightPx(); + return tieStartX; + }, + + // Get the ending `x` coordinate for a `StaveTie` + getTieLeftX: function() { + var tieEndX = this.getAbsoluteX(); + tieEndX += this.x_shift - this.extraLeftPx; + return tieEndX; + }, + + // Get the stave line on which to place a rest + getLineForRest: function() { + var rest_line = this.keyProps[0].line; + if (this.keyProps.length > 1) { + var last_line = this.keyProps[this.keyProps.length - 1].line; + var top = Vex.Max(rest_line, last_line); + var bot = Vex.Min(rest_line, last_line); + rest_line = Vex.MidLine(top, bot); + } + + return rest_line; + }, + + // Get the default `x` and `y` coordinates for the provided `position` + // and key `index` + getModifierStartXY: function(position, index) { + if (!this.preFormatted) throw new Vex.RERR("UnformattedNote", + "Can't call GetModifierStartXY on an unformatted note"); + + if (this.ys.length === 0) throw new Vex.RERR("NoYValues", + "No Y-Values calculated for this note."); + + var x = 0; + if (position == Vex.Flow.Modifier.Position.LEFT) { + // extra_left_px + x = -1 * 2; + } else if (position == Vex.Flow.Modifier.Position.RIGHT) { + // extra_right_px + x = this.glyph.head_width + this.x_shift + 2; + } else if (position == Vex.Flow.Modifier.Position.BELOW || + position == Vex.Flow.Modifier.Position.ABOVE) { + x = this.glyph.head_width / 2; + } + + return { x: this.getAbsoluteX() + x, y: this.ys[index] }; + }, + + // Sets the notehead at `index` to the provided coloring `style`. + // + // `style` is an `object` with the following properties: `shadowColor`, + // `shadowBlur`, `fillStyle`, `strokeStyle` + setKeyStyle: function(index, style) { + this.note_heads[index].setStyle(style); + return this; + }, + + // Add self to modifier context. `mContext` is the `ModifierContext` + // to be added to. + addToModifierContext: function(mContext) { + this.setModifierContext(mContext); + for (var i = 0; i < this.modifiers.length; ++i) { + this.modifierContext.addModifier(this.modifiers[i]); + } + this.modifierContext.addModifier(this); + this.setPreFormatted(false); + return this; + }, + + // Generic function to add modifiers to a note + // + // Parameters: + // * `index`: The index of the key that we're modifying + // * `modifier`: The modifier to add + addModifier: function(index, modifier) { + modifier.setNote(this); + modifier.setIndex(index); + this.modifiers.push(modifier); + this.setPreFormatted(false); + return this; + }, + + // Helper function to add an accidental to a key + addAccidental: function(index, accidental) { + return this.addModifier(index, accidental); + }, + + // Helper function to add an articulation to a key + addArticulation: function(index, articulation) { + return this.addModifier(index, articulation); + }, + + // Helper function to add an annotation to a key + addAnnotation: function(index, annotation) { + return this.addModifier(index, annotation); + }, + + // Helper function to add a dot on a specific key + addDot: function(index) { + var dot = new Vex.Flow.Dot(); + dot.setDotShiftY(this.glyph.dot_shiftY); + this.dots++; + return this.addModifier(index, dot); + }, + + // Convenience method to add dot to all keys in note + addDotToAll: function() { + for (var i = 0; i < this.keys.length; ++i) + this.addDot(i); + return this; + }, + + // Get all accidentals in the `ModifierContext` + getAccidentals: function() { + return this.modifierContext.getModifiers("accidentals"); + }, + + // Get all dots in the `ModifierContext` + getDots: function() { + return this.modifierContext.getModifiers("dots"); + }, + + // Get the width of the note if it is displaced. Used for `Voice` + // formatting + getVoiceShiftWidth: function() { + // TODO: may need to accomodate for dot here. + return this.glyph.head_width * (this.displaced ? 2 : 1); + }, + + // Calculates and sets the extra pixels to the left or right + // if the note is displaced + calcExtraPx: function() { + this.setExtraLeftPx((this.displaced && this.stem_direction == -1) ? + this.glyph.head_width : 0); + this.setExtraRightPx((this.displaced && this.stem_direction == 1) ? + this.glyph.head_width : 0); + }, + + // Pre-render formatting + preFormat: function() { + if (this.preFormatted) return; + if (this.modifierContext) this.modifierContext.preFormat(); + + var width = this.glyph.head_width + this.extraLeftPx + this.extraRightPx; + + // For upward flagged notes, the width of the flag needs to be added + if (this.glyph.flag && this.beam === null && this.stem_direction == 1) { + width += this.glyph.head_width; + } + + this.setWidth(width); + this.setPreFormatted(true); + }, + + // Gets the staff line and y value for the highest and lowest noteheads + getNoteHeadBounds: function() { + // Top and bottom Y values for stem. + var y_top = null; + var y_bottom = null; + + var highest_line = this.stave.getNumLines(); + var lowest_line = 1; + + this.note_heads.forEach(function(note_head) { + var line = note_head.getLine(); + var y = note_head.getY(); + + if (y_top === null || y < y_top) { + y_top = y; + } + + if (y_bottom === null || y > y_bottom) { + y_bottom = y; + } + + highest_line = line > highest_line ? line : highest_line; + lowest_line = line < lowest_line ? line : lowest_line; + + }, this); + + return { + y_top: y_top, + y_bottom: y_bottom, + highest_line: highest_line, + lowest_line: lowest_line + }; + }, + + // Get the starting `x` coordinate for the noteheads + getNoteHeadBeginX: function(){ + return this.getAbsoluteX() + this.x_shift; + }, + + // Get the ending `x` coordinate for the noteheads + getNoteHeadEndX: function(){ + var x_begin = this.getNoteHeadBeginX(); + return x_begin + this.glyph.head_width - (Vex.Flow.STEM_WIDTH / 2); + }, + + // Draw the ledger lines between the stave and the highest/lowest keys + drawLedgerLines: function(){ + if (!this.context) throw new Vex.RERR("NoCanvasContext", + "Can't draw without a canvas context."); + var ctx = this.context; + + var bounds = this.getNoteHeadBounds(); + var highest_line = bounds.highest_line; + var lowest_line = bounds.lowest_line; + var head_x = this.note_heads[0].getAbsoluteX(); + + var that = this; + function stroke(y) { + if (that.use_default_head_x === true) { + head_x = that.getAbsoluteX() + that.x_shift; + } + var x = head_x - that.render_options.stroke_px; + var length = ((head_x + that.glyph.head_width) - head_x) + + (that.render_options.stroke_px * 2); + + ctx.fillRect(x, y, length, 1); + } + + var line; // iterator + for (line = 6; line <= highest_line; ++line) { + stroke(this.stave.getYForNote(line)); + } + + for (line = 0; line >= lowest_line; --line) { + stroke(this.stave.getYForNote(line)); + } + }, + + // Draw all key modifiers + drawModifiers: function(){ + if (!this.context) throw new Vex.RERR("NoCanvasContext", + "Can't draw without a canvas context."); + var ctx = this.context; + for (var i = 0; i < this.modifiers.length; i++) { + var mod = this.modifiers[i]; + var note_head = this.note_heads[mod.getIndex()]; + var key_style = note_head.getStyle(); + if(key_style) { + ctx.save(); + note_head.applyKeyStyle(ctx); + } + mod.setContext(ctx); + mod.draw(); + if(key_style) { + ctx.restore(); + } + } + }, + + // Draw the flag for the note + drawFlag: function(){ + if (!this.context) throw new Vex.RERR("NoCanvasContext", + "Can't draw without a canvas context."); + var ctx = this.context; + var glyph = this.getGlyph(); + var render_flag = this.beam === null; + var bounds = this.getNoteHeadBounds(); + + var x_begin = this.getNoteHeadBeginX(); + var x_end = this.getNoteHeadEndX(); + + if (glyph.flag && render_flag) { + var note_stem_height = this.stem.getHeight(); + var flag_x, flag_y, flag_code; + + if (this.getStemDirection() === Stem.DOWN) { + // Down stems have flags on the left. + flag_x = x_begin + 1; + flag_y = bounds.y_top - note_stem_height + 2; + flag_code = glyph.code_flag_downstem; + + } else { + // Up stems have flags on the left. + flag_x = x_end + 1; + flag_y = bounds.y_bottom - note_stem_height - 2; + flag_code = glyph.code_flag_upstem; + } + + // Draw the Flag + Vex.Flow.renderGlyph(ctx, flag_x, flag_y, + this.render_options.glyph_font_scale, flag_code); + } + }, + + // Draw the NoteHeads + drawNoteHeads: function(){ + this.note_heads.forEach(function(note_head) { + note_head.setContext(this.context).draw(); + }, this); + }, + + // Render the stem onto the canvas + drawStem: function(stem_struct){ + if (!this.context) throw new Vex.RERR("NoCanvasContext", + "Can't draw without a canvas context."); + + if (stem_struct) { + this.setStem(new Stem(stem_struct)); + } + + this.stem.setContext(this.context).draw(); + }, + + // Draws all the `StaveNote` parts. This is the main drawing method. + draw: function() { + if (!this.context) throw new Vex.RERR("NoCanvasContext", + "Can't draw without a canvas context."); + if (!this.stave) throw new Vex.RERR("NoStave", + "Can't draw without a stave."); + if (this.ys.length === 0) throw new Vex.RERR("NoYValues", + "Can't draw note without Y values."); + + var x_begin = this.getNoteHeadBeginX(); + var x_end = this.getNoteHeadEndX(); + + var render_stem = this.hasStem() && !this.beam; + + // Format note head x positions + this.note_heads.forEach(function(note_head) { + note_head.setX(x_begin); + }, this); + + // Format stem x positions + this.stem.setNoteHeadXBounds(x_begin, x_end); + + L("Rendering ", this.isChord() ? "chord :" : "note :", this.keys); + + // Draw each part of the note + this.drawLedgerLines(); + if (render_stem) this.drawStem(); + this.drawNoteHeads(); + this.drawFlag(); + this.drawModifiers(); + } + }); + + return StaveNote; +}()); +// Vex Flow Notation +// Mohit Muthanna +// +// Copyright Mohit Muthanna 2010 +// +// Requires vex.js. + +/** @constructor */ +Vex.Flow.TabNote = (function() { + function TabNote(tab_struct, draw_stem) { + if (arguments.length > 0) this.init(tab_struct, draw_stem); + } + + var Stem = Vex.Flow.Stem; + + Vex.Inherit(TabNote, Vex.Flow.StemmableNote, { + init: function(tab_struct, draw_stem) { + var superclass = Vex.Flow.TabNote.superclass; + superclass.init.call(this, tab_struct); + + this.ghost = false; // Renders parenthesis around notes + // Note properties + this.positions = tab_struct.positions; // [{ str: X, fret: X }] + Vex.Merge(this.render_options, { + glyph_font_scale: 30, // font size for note heads and rests + draw_stem: draw_stem, + draw_dots: draw_stem + }); + + this.glyph = + Vex.Flow.durationToGlyph(this.duration, this.noteType); + if (!this.glyph) { + throw new Vex.RuntimeError("BadArguments", + "Invalid note initialization data (No glyph found): " + + JSON.stringify(tab_struct)); + } + this.buildStem(); + this.setStemDirection(Stem.UP); + + this.updateWidth(); + }, + + getCategory: function() { return "tabnotes"; }, + setGhost: function(ghost) { + this.ghost = ghost; + this.updateWidth(); + return this; + }, + + hasStem: function() { + return this.render_options.draw_stem; + }, + + getStemExtension: function(){ + var glyph = this.getGlyph(); + + if (this.stem_extension_override != null) { + return this.stem_extension_override; + } + + if (glyph) { + return this.getStemDirection() === 1 ? glyph.tabnote_stem_up_extension : + glyph.tabnote_stem_down_extension; + } + + return 0; + }, + + addDot: function() { + var dot = new Vex.Flow.Dot(); + this.dots++; + return this.addModifier(dot, 0); + }, + + updateWidth: function() { + this.glyphs = []; + this.width = 0; + for (var i = 0; i < this.positions.length; ++i) { + var fret = this.positions[i].fret; + if (this.ghost) fret = "(" + fret + ")"; + var glyph = Vex.Flow.tabToGlyph(fret); + this.glyphs.push(glyph); + this.width = (glyph.width > this.width) ? glyph.width : this.width; + } + }, + + setStave: function(stave) { + var superclass = Vex.Flow.TabNote.superclass; + superclass.setStave.call(this, stave); + this.context = stave.context; + this.width = 0; + + // Calculate the fret number width based on font used + var i; + if (this.context) { + for (i = 0; i < this.glyphs.length; ++i) { + var text = "" + this.glyphs[i].text; + if (text.toUpperCase() != "X") + this.glyphs[i].width = this.context.measureText(text).width; + this.width = (this.glyphs[i].width > this.width) ? + this.glyphs[i].width : this.width; + } + } + + var ys = []; + + // Setup y coordinates for score. + for (i = 0; i < this.positions.length; ++i) { + var line = this.positions[i].str; + ys.push(this.stave.getYForLine(line - 1)); + } + + return this.setYs(ys); + }, + + // Get the Tab Positions for each note in chord + getPositions: function() { + return this.positions; + }, + + addToModifierContext: function(mc) { + this.setModifierContext(mc); + for (var i = 0; i < this.modifiers.length; ++i) { + this.modifierContext.addModifier(this.modifiers[i]); + } + this.modifierContext.addModifier(this); + this.preFormatted = false; + return this; + }, + + getTieRightX: function() { + var tieStartX = this.getAbsoluteX(); + var note_glyph_width = this.glyph.head_width; + tieStartX += (note_glyph_width / 2); + tieStartX += ((-this.width / 2) + this.width + 2); + + return tieStartX; + }, + + getTieLeftX: function() { + var tieEndX = this.getAbsoluteX(); + var note_glyph_width = this.glyph.head_width; + tieEndX += (note_glyph_width / 2); + tieEndX -= ((this.width / 2) + 2); + + return tieEndX; + }, + + getModifierStartXY: function(position, index) { + if (!this.preFormatted) throw new Vex.RERR("UnformattedNote", + "Can't call GetModifierStartXY on an unformatted note"); + + if (this.ys.length === 0) throw new Vex.RERR("NoYValues", + "No Y-Values calculated for this note."); + + var x = 0; + if (position == Vex.Flow.Modifier.Position.LEFT) { + x = -1 * 2; // extra_left_px + } else if (position == Vex.Flow.Modifier.Position.RIGHT) { + x = this.width + 2; // extra_right_px + } else if (position == Vex.Flow.Modifier.Position.BELOW || + position == Vex.Flow.Modifier.Position.ABOVE) { + var note_glyph_width = this.glyph.head_width; + x = note_glyph_width / 2; + } + + return {x: this.getAbsoluteX() + x, y: this.ys[index]}; + }, + + getLineForRest: function() { + return this.positions[0].str; + }, + + // Pre-render formatting + preFormat: function() { + if (this.preFormatted) return; + if (this.modifierContext) this.modifierContext.preFormat(); + // width is already set during init() + this.setPreFormatted(true); + }, + + getStemX: function() { + return this.getCenterGlyphX(); + }, + + getStemY: function(){ + // The decimal staff line amounts provide optimal spacing between the + // fret number and the stem + var stemUpLine = -0.7; + var stemDownLine = this.stave.options.num_lines - 0.3; + var stemStartLine = Stem.UP === this.stem_direction ? stemUpLine : stemDownLine; + + return this.stave.getYForLine(stemStartLine); + }, + + getStemExtents: function() { + var stem_base_y = this.getStemY(); + var stem_top_y = stem_base_y + (Stem.HEIGHT * -this.stem_direction); + + return { topY: stem_top_y , baseY: stem_base_y}; + }, + + draw: function() { + if (!this.context) throw new Vex.RERR("NoCanvasContext", + "Can't draw without a canvas context."); + if (!this.stave) throw new Vex.RERR("NoStave", "Can't draw without a stave."); + if (this.ys.length === 0) throw new Vex.RERR("NoYValues", + "Can't draw note without Y values."); + + var ctx = this.context; + var x = this.getAbsoluteX(); + var ys = this.ys; + var y; + + var render_stem = this.beam == null && this.render_options.draw_stem; + var render_flag = this.beam == null && render_stem; + + var i; + for (i = 0; i < this.positions.length; ++i) { + y = ys[i]; + + var glyph = this.glyphs[i]; + + // Center the fret text beneath the notation note head + var note_glyph_width = this.glyph.head_width; + var tab_x = x + (note_glyph_width / 2) - (glyph.width / 2); + + ctx.clearRect(tab_x - 2, y - 3, glyph.width + 4, 6); + + if (glyph.code) { + Vex.Flow.renderGlyph(ctx, tab_x, y + 5 + glyph.shift_y, + this.render_options.glyph_font_scale, glyph.code); + } else { + var text = glyph.text.toString(); + ctx.fillText(text, tab_x, y + 5); + } + } + + var stem_x = this.getStemX(); + var stem_y = this.getStemY(); + if (render_stem) { + this.drawStem({ + x_begin: stem_x, + x_end: stem_x, + y_top: stem_y, + y_bottom: stem_y, + y_extend: 0, + stem_extension: this.getStemExtension(), + stem_direction: this.stem_direction + }); + } + + // Now it's the flag's turn. + if (this.glyph.flag && render_flag) { + var flag_x = this.getStemX() + 1 ; + var flag_y = this.getStemY() - (this.stem.getHeight()); + var flag_code; + + if (this.stem_direction == Stem.DOWN) { + // Down stems have flags on the left. + flag_code = this.glyph.code_flag_downstem; + } else { + // Up stems have flags on the left. + flag_code = this.glyph.code_flag_upstem; + } + + // Draw the Flag + Vex.Flow.renderGlyph(ctx, flag_x, flag_y, + this.render_options.glyph_font_scale, flag_code); + } + + // Draw the modifiers + this.modifiers.forEach(function(modifier) { + // Only draw the dots if enabled + if (modifier.getCategory() === 'dots' && !this.render_options.draw_dots) return; + + modifier.setContext(this.context); + modifier.draw(); + }, this); + } + }); + + return TabNote; +}()); +// Vex Flow Notation +// Mohit Muthanna +// +// Copyright Mohit Muthanna 2010 +// +// Requires vex.js. + +/** @constructor */ +Vex.Flow.GhostNote = (function() { + function GhostNote(duration) { + if (arguments.length > 0) this.init(duration); + } + + Vex.Inherit(GhostNote, Vex.Flow.StemmableNote, { + init: function(parameter) { + // Sanity check + if (!parameter) { + throw new Vex.RuntimeError("BadArguments", + "Ghost note must have valid initialization data to identify " + + "duration."); + } + + var note_struct; + + // Preserve backwards-compatibility + if (typeof(parameter) === "string") { + note_struct = { duration: parameter }; + } else if (typeof(parameter) === "object") { + note_struct = parameter; + } else { + throw new Vex.RuntimeError("BadArguments", + "Ghost note must have valid initialization data to identify " + + "duration."); + } + + GhostNote.superclass.init.call(this, note_struct); + + // Note properties + this.setWidth(0); + }, + + isRest: function() { return true; }, + + setStave: function(stave) { GhostNote.superclass.setStave.call(this, stave); }, + + addToModifierContext: function() + { /* intentionally overridden */ return this; }, + + preFormat: function() { + this.setPreFormatted(true); + return this; + }, + + draw: function() { + if (!this.stave) throw new Vex.RERR("NoStave", "Can't draw without a stave."); + + // Draw the modifiers + for (var i = 0; i < this.modifiers.length; ++i) { + var modifier = this.modifiers[i]; + modifier.setContext(this.context); + modifier.draw(); + } + } + }); + + return GhostNote; +}()); +// Vex Flow Notation +// Copyright Mohit Muthanna 2010 +// +// Author Taehoon Moon 2014 + +/** @constructor */ +Vex.Flow.ClefNote = (function() { + function ClefNote(clef) { this.init(clef); } + + Vex.Inherit(ClefNote, Vex.Flow.Note, { + init: function(clef) { + ClefNote.superclass.init.call(this, {duration: "b"}); + + this.setClef(clef); + + // Note properties + this.ignore_ticks = true; + }, + + setClef: function(clef) { + this.clef = Vex.Flow.Clef.types[clef]; + this.glyph = new Vex.Flow.Glyph(this.clef.code, this.clef.point); + this.setWidth(this.glyph.getMetrics().width); + return this; + }, + + getClef: function() { + return this.clef; + }, + + setStave: function(stave) { + var superclass = Vex.Flow.ClefNote.superclass; + superclass.setStave.call(this, stave); + }, + + getBoundingBox: function() { + return new Vex.Flow.BoundingBox(0, 0, 0, 0); + }, + + addToModifierContext: function() { + /* overridden to ignore */ + return this; + }, + + preFormat: function() { + this.setPreFormatted(true); + return this; + }, + + draw: function() { + if (!this.stave) throw new Vex.RERR("NoStave", "Can't draw without a stave."); + + if (!this.glyph.getContext()) { + this.glyph.setContext(this.context); + } + + this.glyph.setStave(this.stave); + this.glyph.setYShift( + this.stave.getYForLine(this.clef.line) - this.stave.getYForGlyphs()); + this.glyph.renderToStave(this.getAbsoluteX()); + } + }); + + return ClefNote; +}()); +// Vex Flow Notation +// Copyright Mohit Muthanna 2010 +// +// Author Taehoon Moon 2014 + +/** @constructor */ +Vex.Flow.TimeSigNote = (function() { + function TimeSigNote(timeSpec, customPadding) { + if (arguments.length > 0) this.init(timeSpec, customPadding); + } + + Vex.Inherit(TimeSigNote, Vex.Flow.Note, { + init: function(timeSpec, customPadding) { + TimeSigNote.superclass.init.call(this, {duration: "b"}); + + var timeSignature = new Vex.Flow.TimeSignature(timeSpec, customPadding); + this.timeSig = timeSignature.getTimeSig(); + this.setWidth(this.timeSig.glyph.getMetrics().width); + + // Note properties + this.ignore_ticks = true; + }, + + setStave: function(stave) { + var superclass = Vex.Flow.TimeSigNote.superclass; + superclass.setStave.call(this, stave); + }, + + getBoundingBox: function() { + return new Vex.Flow.BoundingBox(0, 0, 0, 0); + }, + + addToModifierContext: function() { + /* overridden to ignore */ + return this; + }, + + preFormat: function() { + this.setPreFormatted(true); + return this; + }, + + draw: function() { + if (!this.stave) throw new Vex.RERR("NoStave", "Can't draw without a stave."); + + if (!this.timeSig.glyph.getContext()) { + this.timeSig.glyph.setContext(this.context); + } + + this.timeSig.glyph.setStave(this.stave); + this.timeSig.glyph.setYShift( + this.stave.getYForLine(this.timeSig.line) - this.stave.getYForGlyphs()); + this.timeSig.glyph.renderToStave(this.getAbsoluteX()); + } + }); + + return TimeSigNote; +}()); +// [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010. +// +// ## Description +// +// This file implements `Beams` that span over a set of `StemmableNotes`. +// +// Requires: vex.js, vexmusic.js, note.js +Vex.Flow.Beam = (function() { + function Beam(notes, auto_stem) { + if (arguments.length > 0) this.init(notes, auto_stem); + } + + var Stem = Vex.Flow.Stem; + + // ## Prototype Methods + Beam.prototype = { + init: function(notes, auto_stem) { + if (!notes || notes == []) { + throw new Vex.RuntimeError("BadArguments", "No notes provided for beam."); + } + + if (notes.length == 1) { + throw new Vex.RuntimeError("BadArguments", "Too few notes for beam."); + } + + // Validate beam line, direction and ticks. + this.ticks = notes[0].getIntrinsicTicks(); + + if (this.ticks >= Vex.Flow.durationToTicks("4")) { + throw new Vex.RuntimeError("BadArguments", + "Beams can only be applied to notes shorter than a quarter note."); + } + + var i; // shared iterator + var note; + + this.stem_direction = 1; + + for (i = 0; i < notes.length; ++i) { + note = notes[i]; + if (note.hasStem()) { + this.stem_direction = note.getStemDirection(); + break; + } + } + + var stem_direction = -1; + + // Figure out optimal stem direction based on given notes + if (auto_stem && notes[0].getCategory() === 'stavenotes') { + // Auto Stem StaveNotes + this.min_line = 1000; + + for (i = 0; i < notes.length; ++i) { + note = notes[i]; + if (note.getKeyProps) { + var props = note.getKeyProps(); + var center_line = (props[0].line + props[props.length - 1].line) / 2; + this.min_line = Math.min(center_line, this.min_line); + } + } + + if (this.min_line < 3) stem_direction = 1; + } else if (auto_stem && notes[0].getCategory() === 'tabnotes') { + // Auto Stem TabNotes + var stem_weight = notes.reduce(function(memo, note) { + return memo + note.stem_direction; + }, 0); + + stem_direction = stem_weight > -1 ? 1 : -1; + } + + // Apply stem directions and attach beam to notes + for (i = 0; i < notes.length; ++i) { + note = notes[i]; + if (auto_stem) { + note.setStemDirection(stem_direction); + this.stem_direction = stem_direction; + } + note.setBeam(this); + } + + this.postFormatted = false; + this.notes = notes; + this.beam_count = this.getBeamCount(); + this.break_on_indices = []; + this.render_options = { + beam_width: 5, + max_slope: 0.25, + min_slope: -0.25, + slope_iterations: 20, + slope_cost: 25, + show_stemlets: false, + stemlet_extension: 7, + partial_beam_length: 10 + }; + }, + + // The the rendering `context` + setContext: function(context) { this.context = context; return this; }, + + // Get the notes in this beam + getNotes: function() { return this.notes; }, + + // Get the max number of beams in the set of notes + getBeamCount: function(){ + var beamCounts = this.notes.map(function(note) { + return note.getGlyph().beam_count; + }); + + var maxBeamCount = beamCounts.reduce(function(max, beamCount) { + return beamCount > max ? beamCount : max; + }); + + return maxBeamCount; + }, + + // Set which note `indices` to break the secondary beam at + breakSecondaryAt: function(indices) { + this.break_on_indices = indices; + return this; + }, + + // Return the y coordinate for linear function + getSlopeY: function(x, first_x_px, first_y_px, slope) { + return first_y_px + ((x - first_x_px) * slope); + }, + + // Calculate the best possible slope for the provided notes + calculateSlope: function() { + var first_note = this.notes[0]; + var first_y_px = first_note.getStemExtents().topY; + var first_x_px = first_note.getStemX(); + + var inc = (this.render_options.max_slope - this.render_options.min_slope) / + this.render_options.slope_iterations; + var min_cost = Number.MAX_VALUE; + var best_slope = 0; + var y_shift = 0; + + // iterate through slope values to find best weighted fit + for (var slope = this.render_options.min_slope; + slope <= this.render_options.max_slope; + slope += inc) { + var total_stem_extension = 0; + var y_shift_tmp = 0; + + // iterate through notes, calculating y shift and stem extension + for (var i = 1; i < this.notes.length; ++i) { + var note = this.notes[i]; + + var x_px = note.getStemX(); + var y_px = note.getStemExtents().topY; + var slope_y_px = this.getSlopeY(x_px, first_x_px, first_y_px, slope) + y_shift_tmp; + + // beam needs to be shifted up to accommodate note + if (y_px * this.stem_direction < slope_y_px * this.stem_direction) { + var diff = Math.abs(y_px - slope_y_px); + y_shift_tmp += diff * -this.stem_direction; + total_stem_extension += (diff * i); + } else { // beam overshoots note, account for the difference + total_stem_extension += (y_px - slope_y_px) * this.stem_direction; + } + + } + /* + // This causes too many zero-slope beams. + + var cost = this.render_options.slope_cost * Math.abs(slope) + + Math.abs(total_stem_extension); + */ + + // Pick a beam that minimizes stem extension. + var cost = Math.abs(total_stem_extension); + + // update state when a more ideal slope is found + if (cost < min_cost) { + min_cost = cost; + best_slope = slope; + y_shift = y_shift_tmp; + } + } + + this.slope = best_slope; + this.y_shift = y_shift; + }, + + // Create new stems for the notes in the beam, so that each stem + // extends into the beams. + applyStemExtensions: function(){ + var first_note = this.notes[0]; + var first_y_px = first_note.getStemExtents().topY; + var first_x_px = first_note.getStemX(); + + for (var i = 0; i < this.notes.length; ++i) { + var note = this.notes[i]; + + var x_px = note.getStemX(); + var y_extents = note.getStemExtents(); + var base_y_px = y_extents.baseY; + var top_y_px = y_extents.topY; + + // For harmonic note heads, shorten stem length by 3 pixels + base_y_px += this.stem_direction * note.glyph.stem_offset; + + // Don't go all the way to the top (for thicker stems) + var y_displacement = Vex.Flow.STEM_WIDTH; + + if (!note.hasStem()) { + if (note.isRest() && this.render_options.show_stemlets) { + var centerGlyphX = note.getCenterGlyphX(); + + var width = this.render_options.beam_width; + var total_width = ((this.beam_count - 1)* width * 1.5) + width; + + var stemlet_height = (total_width - y_displacement + + this.render_options.stemlet_extension); + + var beam_y = this.getSlopeY(centerGlyphX, first_x_px, + first_y_px, this.slope) + this.y_shift; + var start_y = beam_y + (Vex.Flow.Stem.HEIGHT * this.stem_direction); + var end_y = beam_y + (stemlet_height * this.stem_direction); + + // Draw Stemlet + note.setStem(new Vex.Flow.Stem({ + x_begin: centerGlyphX, + x_end: centerGlyphX, + y_bottom: this.stem_direction === 1 ? end_y : start_y, + y_top: this.stem_direction === 1 ? start_y : end_y, + y_extend: y_displacement, + stem_extension: -1, // To avoid protruding through the beam + stem_direction: this.stem_direction + })); + } + + continue; + } + + var slope_y = this.getSlopeY(x_px, first_x_px, first_y_px, + this.slope) + this.y_shift; + + note.setStem(new Vex.Flow.Stem({ + x_begin: x_px - (Vex.Flow.STEM_WIDTH/2), + x_end: x_px, + y_top: this.stem_direction === 1 ? top_y_px : base_y_px, + y_bottom: this.stem_direction === 1 ? base_y_px : top_y_px , + y_extend: y_displacement, + stem_extension: Math.abs(top_y_px - slope_y) - Stem.HEIGHT - 1, + stem_direction: this.stem_direction + })); + } + }, + + // Get the x coordinates for the beam lines of specific `duration` + getBeamLines: function(duration) { + var beam_lines = []; + var beam_started = false; + var current_beam; + var partial_beam_length = this.render_options.partial_beam_length; + + function determinePartialSide (prev_note, next_note){ + // Compare beam counts and store differences + var unshared_beams = 0; + if (next_note && prev_note) { + unshared_beams = prev_note.getBeamCount() - next_note.getBeamCount(); + } + + var left_partial = duration !== "8" && unshared_beams > 0; + var right_partial = duration !== "8" && unshared_beams < 0; + + return { + left: left_partial, + right: right_partial + }; + } + + for (var i = 0; i < this.notes.length; ++i) { + var note = this.notes[i]; + var prev_note = this.notes[i-1]; + var next_note = this.notes[i+1]; + var ticks = note.getIntrinsicTicks(); + var partial = determinePartialSide(prev_note, next_note); + var stem_x = note.isRest() ? note.getCenterGlyphX() : note.getStemX(); + + // Check whether to apply beam(s) + if (ticks < Vex.Flow.durationToTicks(duration)) { + if (!beam_started) { + var new_line = {start: stem_x, end: null}; + + if (partial.left) { + new_line.end = stem_x - partial_beam_length; + } + + beam_lines.push(new_line); + beam_started = true; + } else { + current_beam = beam_lines[beam_lines.length - 1]; + current_beam.end = stem_x; + + // Should break secondary beams on note + var should_break = this.break_on_indices.indexOf(i) !== -1; + // Shorter than or eq an 8th note duration + var can_break = parseInt(duration, 10) >= 8; + if (should_break && can_break) { + beam_started = false; + } + } + } else { + if (!beam_started) { + // we don't care + } else { + current_beam = beam_lines[beam_lines.length - 1]; + if (current_beam.end == null) { + // single note + current_beam.end = current_beam.start + + partial_beam_length; + } else { + // we don't care + } + } + + beam_started = false; + } + } + + if (beam_started === true) { + current_beam = beam_lines[beam_lines.length - 1]; + if (current_beam.end == null) { + // single note + current_beam.end = current_beam.start - + partial_beam_length; + } + } + + return beam_lines; + }, + + // Render the stems for each notes + drawStems: function() { + this.notes.forEach(function(note) { + if (note.getStem()) { + note.getStem().setContext(this.context).draw(); + } + }, this); + }, + + // Render the beam lines + drawBeamLines: function() { + if (!this.context) throw new Vex.RERR("NoCanvasContext", + "Can't draw without a canvas context."); + + var valid_beam_durations = ["4", "8", "16", "32", "64"]; + + var first_note = this.notes[0]; + var last_note = this.notes[this.notes.length - 1]; + + var first_y_px = first_note.getStemExtents().topY; + var last_y_px = last_note.getStemExtents().topY; + + var first_x_px = first_note.getStemX(); + + var beam_width = this.render_options.beam_width * this.stem_direction; + + // Draw the beams. + for (var i = 0; i < valid_beam_durations.length; ++i) { + var duration = valid_beam_durations[i]; + var beam_lines = this.getBeamLines(duration); + + for (var j = 0; j < beam_lines.length; ++j) { + var beam_line = beam_lines[j]; + var first_x = beam_line.start - (this.stem_direction == -1 ? Vex.Flow.STEM_WIDTH/2:0); + var first_y = this.getSlopeY(first_x, first_x_px, first_y_px, this.slope); + + var last_x = beam_line.end + + (this.stem_direction == 1 ? (Vex.Flow.STEM_WIDTH/3):(-Vex.Flow.STEM_WIDTH/3)); + var last_y = this.getSlopeY(last_x, first_x_px, first_y_px, this.slope); + + this.context.beginPath(); + this.context.moveTo(first_x, first_y + this.y_shift); + this.context.lineTo(first_x, first_y + beam_width + this.y_shift); + this.context.lineTo(last_x + 1, last_y + beam_width + this.y_shift); + this.context.lineTo(last_x + 1, last_y + this.y_shift); + this.context.closePath(); + this.context.fill(); + } + + first_y_px += beam_width * 1.5; + last_y_px += beam_width * 1.5; + } + }, + + // Pre-format the beam + preFormat: function() { return this; }, + + // Post-format the beam. This can only be called after + // the notes in the beam have both `x` and `y` values. ie: they've + // been formatted and have staves + postFormat: function() { + if (this.postFormatted) return; + + this.calculateSlope(); + this.applyStemExtensions(); + + this.postFormatted = true; + }, + + // Render the beam to the canvas context + draw: function() { + if (!this.context) throw new Vex.RERR("NoCanvasContext", + "Can't draw without a canvas context."); + + if (this.unbeamable) return; + + if (!this.postFormatted) { + this.postFormat(); + } + + this.drawStems(); + this.drawBeamLines(); + + return true; + } + }; + + // ## Static Methods + // + // Gets the default beam groups for a provided time signature. + // Attempts to guess if the time signature is not found in table. + // Currently this is fairly naive. + Beam.getDefaultBeamGroups = function(time_sig){ + if (!time_sig || time_sig == "c") time_sig = "4/4"; + + var defaults = { + '1/2' : ['1/2'], + '2/2' : ['1/2'], + '3/2' : ['1/2'], + '4/2' : ['1/2'], + + '1/4' : ['1/4'], + '2/4' : ['1/4'], + '3/4' : ['1/4'], + '4/4' : ['1/4'], + + '1/8' : ['1/8'], + '2/8' : ['2/8'], + '3/8' : ['3/8'], + '4/8' : ['2/8'], + + '1/16' : ['1/16'], + '2/16' : ['2/16'], + '3/16' : ['3/16'], + '4/16' : ['2/16'] + }; + + var Fraction = Vex.Flow.Fraction; + var groups = defaults[time_sig]; + + if (!groups) { + // If no beam groups found, naively determine + // the beam groupings from the time signature + var beatTotal = parseInt(time_sig.split('/')[0], 10); + var beatValue = parseInt(time_sig.split('/')[1], 10); + + var tripleMeter = beatTotal % 3 === 0; + + if (tripleMeter) { + return [new Fraction(3, beatValue)]; + } else if (beatValue > 4) { + return [new Fraction(2, beatValue)]; + } else if (beatValue <= 4) { + return [new Fraction(1, beatValue)]; + } + } else { + return groups.map(function(group) { + return new Fraction().parse(group); + }); + } + }; + + // A helper function to automatically build basic beams for a voice. For more + // complex auto-beaming use `Beam.generateBeams()`. + // + // Parameters: + // * `voice` - The voice to generate the beams for + // * `stem_direction` - A stem direction to apply to the entire voice + // * `groups` - An array of `Fraction` representing beat groupings for the beam + Beam.applyAndGetBeams = function(voice, stem_direction, groups) { + return Beam.generateBeams(voice.getTickables(), { + groups: groups, + stem_direction: stem_direction + }); + }; + + // A helper function to autimatically build beams for a voice with + // configuration options. + // + // Example configuration object: + // + // ``` + // config = { + // groups: [new Vex.Flow.Fraction(2, 8)], + // stem_direction: -1, + // beam_rests: true, + // beam_middle_only: true, + // show_stemlets: false + // }; + // ``` + // + // Parameters: + // * `notes` - An array of notes to create the beams for + // * `config` - The configuration object + // * `groups` - Array of `Fractions` that represent the beat structure to beam the notes + // * `stem_direction` - Set to apply the same direction to all notes + // * `beam_rests` - Set to `true` to include rests in the beams + // * `beam_middle_only` - Set to `true` to only beam rests in the middle of the beat + // * `show_stemlets` - Set to `true` to draw stemlets for rests + // + Beam.generateBeams = function(notes, config) { + + if (!config) config = {}; + + if (!config.groups || !config.groups.length) { + config.groups = [new Vex.Flow.Fraction(2, 8)]; + } + + // Convert beam groups to tick amounts + var tickGroups = config.groups.map(function(group) { + if (!group.multiply) { + throw new Vex.RuntimeError("InvalidBeamGroups", + "The beam groups must be an array of Vex.Flow.Fractions"); + } + return group.clone().multiply(Vex.Flow.RESOLUTION, 1); + }); + + var unprocessedNotes = notes; + var currentTickGroup = 0; + var noteGroups = []; + var currentGroup = []; + + function getTotalTicks(vf_notes){ + return vf_notes.reduce(function(memo,note){ + return note.getTicks().clone().add(memo); + }, new Vex.Flow.Fraction(0, 1)); + } + + function nextTickGroup() { + if (tickGroups.length - 1 > currentTickGroup) { + currentTickGroup += 1; + } else { + currentTickGroup = 0; + } + } + + function createGroups(){ + var nextGroup = []; + + unprocessedNotes.forEach(function(unprocessedNote){ + nextGroup = []; + if (unprocessedNote.shouldIgnoreTicks()) { + noteGroups.push(currentGroup); + currentGroup = nextGroup; + return; // Ignore untickables (like bar notes) + } + + currentGroup.push(unprocessedNote); + var ticksPerGroup = tickGroups[currentTickGroup].value(); + var totalTicks = getTotalTicks(currentGroup).value(); + + // Double the amount of ticks in a group, if it's an unbeamable tuplet + if (parseInt(unprocessedNote.duration, 10) < 8 && unprocessedNote.tuplet) { + ticksPerGroup *= 2; + } + + // If the note that was just added overflows the group tick total + if (totalTicks > ticksPerGroup) { + nextGroup.push(currentGroup.pop()); + noteGroups.push(currentGroup); + currentGroup = nextGroup; + nextTickGroup(); + } else if (totalTicks == ticksPerGroup) { + noteGroups.push(currentGroup); + currentGroup = nextGroup; + nextTickGroup(); + } + }); + + // Adds any remainder notes + if (currentGroup.length > 0) + noteGroups.push(currentGroup); + } + + function getBeamGroups() { + return noteGroups.filter(function(group){ + if (group.length > 1) { + var beamable = true; + group.forEach(function(note) { + if (note.getIntrinsicTicks() >= Vex.Flow.durationToTicks("4")) { + beamable = false; + } + }); + return beamable; + } + return false; + }); + } + + // Splits up groups by Rest + function sanitizeGroups() { + var sanitizedGroups = []; + noteGroups.forEach(function(group) { + var tempGroup = []; + group.forEach(function(note, index, group) { + var isFirstOrLast = index === 0 || index === group.length - 1; + + var breaksOnEachRest = !config.beam_rests && note.isRest(); + var breaksOnFirstOrLastRest = (config.beam_rests && + config.beam_middle_only && note.isRest() && isFirstOrLast); + + var shouldBreak = breaksOnEachRest || breaksOnFirstOrLastRest; + + if (shouldBreak) { + if (tempGroup.length > 0) { + sanitizedGroups.push(tempGroup); + } + tempGroup = []; + } else { + tempGroup.push(note); + } + }); + + if (tempGroup.length > 0) { + sanitizedGroups.push(tempGroup); + } + }); + + noteGroups = sanitizedGroups; + } + + function formatStems() { + noteGroups.forEach(function(group){ + var stemDirection = determineStemDirection(group); + applyStemDirection(group, stemDirection); + }); + } + + function determineStemDirection(group) { + if (config.stem_direction) return config.stem_direction; + + var lineSum = 0; + group.forEach(function(note) { + if (note.keyProps) { + note.keyProps.forEach(function(keyProp){ + lineSum += (keyProp.line - 2.5); + }); + } + }); + + if (lineSum > 0) + return -1; + return 1; + } + + function applyStemDirection(group, direction) { + group.forEach(function(note){ + if (note.hasStem()) note.setStemDirection(direction); + }); + } + + function getTupletGroups() { + return noteGroups.filter(function(group){ + if (group[0]) return group[0].tuplet; + }); + } + + + // Using closures to store the variables throughout the various functions + // IMO Keeps it this process lot cleaner - but not super consistent with + // the rest of the API's style - Silverwolf90 (Cyril) + createGroups(); + sanitizeGroups(); + formatStems(); + + // Get the notes to be beamed + var beamedNoteGroups = getBeamGroups(); + + // Get the tuplets in order to format them accurately + var tupletGroups = getTupletGroups(); + + // Create a Vex.Flow.Beam from each group of notes to be beamed + var beams = []; + beamedNoteGroups.forEach(function(group){ + var beam = new Vex.Flow.Beam(group); + + if (config.show_stemlets) { + beam.render_options.show_stemlets = true; + } + + beams.push(beam); + }); + + // Reformat tuplets + tupletGroups.forEach(function(group){ + var firstNote = group[0]; + for (var i=0; i 0) this.init(time); + } + + // Modes allow the addition of ticks in three different ways: + // + // STRICT: This is the default. Ticks must fill the voice. + // SOFT: Ticks can be added without restrictions. + // FULL: Ticks do not need to fill the voice, but can't exceed the maximum + // tick length. + Voice.Mode = { + STRICT: 1, + SOFT: 2, + FULL: 3 + }; + + // ## Prototype Methods + Voice.prototype = { + init: function(time) { + this.time = Vex.Merge({ + num_beats: 4, + beat_value: 4, + resolution: Vex.Flow.RESOLUTION + }, time); + + // Recalculate total ticks. + this.totalTicks = new Vex.Flow.Fraction( + this.time.num_beats * (this.time.resolution / this.time.beat_value), 1); + + this.resolutionMultiplier = 1; + + // Set defaults + this.tickables = []; + this.ticksUsed = new Vex.Flow.Fraction(0, 1); + this.smallestTickCount = this.totalTicks.clone(); + this.largestTickWidth = 0; + this.stave = null; + this.boundingBox = null; + // Do we care about strictly timed notes + this.mode = Vex.Flow.Voice.Mode.STRICT; + + // This must belong to a VoiceGroup + this.voiceGroup = null; + }, + + // Get the total ticks in the voice + getTotalTicks: function() { return this.totalTicks; }, + + // Get the total ticks used in the voice by all the tickables + getTicksUsed: function() { return this.ticksUsed; }, + + // Get the largest width of all the tickables + getLargestTickWidth: function() { return this.largestTickWidth; }, + + // Get the tick count for the shortest tickable + getSmallestTickCount: function() { return this.smallestTickCount; }, + + // Get the tickables in the voice + getTickables: function() { return this.tickables; }, + + // Get/set the voice mode, use a value from `Voice.Mode` + getMode: function() { return this.mode; }, + setMode: function(mode) { this.mode = mode; return this; }, + + // Get the resolution multiplier for the voice + getResolutionMultiplier: function() { return this.resolutionMultiplier; }, + + // Get the actual tick resolution for the voice + getActualResolution: function() { return this.resolutionMultiplier * this.time.resolution; }, + + // Set the voice's stave + setStave: function(stave) { + this.stave = stave; + this.boundingBox = null; // Reset bounding box so we can reformat + return this; + }, + + // Get the bounding box for the voice + getBoundingBox: function() { + if (!this.boundingBox) { + if (!this.stave) throw Vex.RERR("NoStave", "Can't get bounding box without stave."); + var stave = this.stave; + + var boundingBox = null; + if (this.tickables[0]) { + this.tickables[0].setStave(stave); + boundingBox = this.tickables[0].getBoundingBox(); + } + + for (var i = 0; i < this.tickables.length; ++i) { + this.tickables[i].setStave(stave); + if (i > 0 && boundingBox) { + var bb = this.tickables[i].getBoundingBox(); + if (bb) boundingBox.mergeWith(bb); + } + } + + this.boundingBox = boundingBox; + } + return this.boundingBox; + }, + + // Every tickable must be associated with a voiceGroup. This allows formatters + // and preformatters to associate them with the right modifierContexts. + getVoiceGroup: function() { + if (!this.voiceGroup) + throw new Vex.RERR("NoVoiceGroup", "No voice group for voice."); + return this.voiceGroup; + }, + + // Set the voice group + setVoiceGroup: function(g) { this.voiceGroup = g; return this; }, + + // Set the voice mode to strict or soft + setStrict: function(strict) { + this.mode = strict ? Vex.Flow.Voice.Mode.STRICT : Vex.Flow.Voice.Mode.SOFT; + return this; + }, + + // Determine if the voice is complete according to the voice mode + isComplete: function() { + if (this.mode == Vex.Flow.Voice.Mode.STRICT || + this.mode == Vex.Flow.Voice.Mode.FULL) { + return this.ticksUsed.equals(this.totalTicks); + } else { + return true; + } + }, + + // Add a tickable to the voice + addTickable: function(tickable) { + if (!tickable.shouldIgnoreTicks()) { + var ticks = tickable.getTicks(); + + // Update the total ticks for this line + this.ticksUsed.add(ticks); + + if ((this.mode == Vex.Flow.Voice.Mode.STRICT || + this.mode == Vex.Flow.Voice.Mode.FULL) && + this.ticksUsed.value() > this.totalTicks.value()) { + this.totalTicks.subtract(ticks); + throw new Vex.RERR("BadArgument", "Too many ticks."); + } + + // Track the smallest tickable for formatting + if (ticks.value() < this.smallestTickCount.value()) { + this.smallestTickCount = ticks.clone(); + } + + this.resolutionMultiplier = this.ticksUsed.denominator; + + // Expand total ticks using denominator from ticks used + this.totalTicks.add(0, this.ticksUsed.denominator); + } + + // Add the tickable to the line + this.tickables.push(tickable); + tickable.setVoice(this); + return this; + }, + + // Add an array of tickables to the voice + addTickables: function(tickables) { + for (var i = 0; i < tickables.length; ++i) { + this.addTickable(tickables[i]); + } + + return this; + }, + + // Preformats the voice by applying the voice's stave to each note + preFormat: function(){ + if (this.preFormatted) return; + + this.tickables.forEach(function(tickable) { + if (!tickable.getStave()) { + tickable.setStave(this.stave); + } + }, this); + + this.preFormatted = true; + return this; + }, + + // Render the voice onto the canvas `context` and an optional `stave`. + // If `stave` is omitted, it is expected that the notes have staves + // already set. + draw: function(context, stave) { + var boundingBox = null; + for (var i = 0; i < this.tickables.length; ++i) { + var tickable = this.tickables[i]; + + // Set the stave if provided + if (stave) tickable.setStave(stave); + + if (!tickable.getStave()) { + throw new Vex.RuntimeError("MissingStave", + "The voice cannot draw tickables without staves."); + } + + if (i === 0) boundingBox = tickable.getBoundingBox(); + + if (i > 0 && boundingBox) { + var tickable_bb = tickable.getBoundingBox(); + if (tickable_bb) boundingBox.mergeWith(tickable_bb); + } + + tickable.setContext(context); + tickable.draw(); + } + + this.boundingBox = boundingBox; + } + }; + + return Voice; +}());// Vex Music Notation +// Mohit Muthanna +// +// Copyright Mohit Muthanna 2010 + +/** @constructor */ +Vex.Flow.VoiceGroup = (function() { + function VoiceGroup() { + this.init(); + } + + VoiceGroup.prototype = { + init: function() { + this.voices = []; + this.modifierContexts = []; + }, + + // Every tickable must be associated with a voiceGroup. This allows formatters + // and preformatters to associate them with the right modifierContexts. + getVoices: function() { return this.voices; }, + getModifierContexts: function() { return this.modifierContexts; }, + + addVoice: function(voice) { + if (!voice) throw new Vex.RERR("BadArguments", "Voice cannot be null."); + this.voices.push(voice); + voice.setVoiceGroup(this); + } + }; + + return VoiceGroup; +}());// [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010. +// +// ## Description +// +// `Modifier` is an abstract interface for notational elements that modify +// a `Note`. Examples of modifiers are `Accidental`, `Annotation`, `Stroke`, etc. +// +// For a `Modifier` instance to be positioned correctly, it must be part of +// a `ModifierContext`. All modifiers in the same context are rendered relative to +// one another. +// +// Typically, all modifiers to a note are part of the same `ModifierContext` instance. Also, +// in multi-voice staves, all modifiers to notes on the same `tick` are part of the same +// `ModifierContext`. This ensures that multiple voices don't trample all over each other. + +Vex.Flow.Modifier = (function() { + function Modifier() { this.init(); } + // To enable logging for this class. Set `Vex.Flow.Modifier.DEBUG` to `true`. + function L() { if (Modifier.DEBUG) Vex.L("Vex.Flow.Modifier", arguments); } + + // Modifiers can be positioned almost anywhere, relative to a note. + Modifier.Position = { + LEFT: 1, + RIGHT: 2, + ABOVE: 3, + BELOW: 4 + }; + + // ## Prototype Methods + Modifier.prototype = { + + // The constructor sets initial widhts and constants. + init: function() { + this.width = 0; + this.context = null; + + // Modifiers are attached to a note and an index. An index is a + // specific head in a chord. + this.note = null; + this.index = null; + + // The `text_line` is reserved space above or below a stave. + this.text_line = 0; + this.position = Modifier.Position.LEFT; + this.modifier_context = null; + this.x_shift = 0; + this.y_shift = 0; + L("Created new modifier"); + }, + + // Every modifier has a category. The `ModifierContext` uses this to determine + // the type and order of the modifiers. + getCategory: function() { return "none"; }, + + // Get and set modifier widths. + getWidth: function() { return this.width; }, + setWidth: function(width) { this.width = width; return this; }, + + // Get and set attached note (`StaveNote`, `TabNote`, etc.) + getNote: function() { return this.note; }, + setNote: function(note) { this.note = note; return this; }, + + // Get and set note index, which is a specific note in a chord. + getIndex: function() { return this.index; }, + setIndex: function(index) { this.index = index; return this; }, + + // Get and set rendering context. + getContext: function() { return this.context; }, + setContext: function(context) { this.context = context; return this; }, + + // Every modifier must be part of a `ModifierContext`. + getModifierContext: function() { return this.modifier_context; }, + setModifierContext: function(c) { this.modifier_context = c; return this; }, + + // Get and set articulation position. + getPosition: function() { return this.position; }, + setPosition: function(position) { this.position = position; return this; }, + + // Set the `text_line` for the modifier. + setTextLine: function(line) { this.text_line = line; return this; }, + + // Shift modifier down `y` pixels. Negative values shift up. + setYShift: function(y) { this.y_shift = y; return this; }, + + // Shift modifier `x` pixels in the direction of the modifier. Negative values + // shift reverse. + setXShift: function(x) { + this.x_shift = 0; + if (this.position == Modifier.Position.LEFT) { + this.x_shift -= x; + } else { + this.x_shift += x; + } + }, + + // Render the modifier onto the canvas. + draw: function() { + if (!this.context) throw new Vex.RERR("NoCanvasContext", + "Can't draw without a canvas context."); + throw new Vex.RERR("MethodNotImplemented", + "Draw() not implemented for this modifier."); + } + }; + + return Modifier; +}()); +// VexFlow - Music Engraving for HTML5 +// Copyright Mohit Muthanna 2010 +// +// This class implements various types of modifiers to notes (e.g. bends, +// fingering positions etc.) Accidentals should also be implemented as +// modifiers, eventually. + +/** + * @constructor + */ +Vex.Flow.ModifierContext = (function() { + function ModifierContext() { + // Current modifiers + this.modifiers = {}; + + // Formatting data. + this.preFormatted = false; + this.postFormatted = false; + this.width = 0; + this.spacing = 0; + this.state = { + left_shift: 0, + right_shift: 0, + text_line: 0 + }; + } + + // Static method. Called from formatNotes :: shift rests vertically + var shiftRestVertical = function(rest, note, dir) { + if (!Vex.Debug) return; + + var delta = 0; + var padding; + + if (dir == 1) { + padding = note.isrest ? 0.0 : 0.5; + delta = note.max_line - rest.min_line; + delta += padding; + } else { + padding = note.isrest ? 0.0 : 0.5; + delta = note.min_line - rest.max_line; + delta -= padding; + } + + rest.line += delta; + rest.max_line += delta; + rest.min_line += delta; + rest.note.keyProps[0].line += delta; + }; + +// Called from formatNotes :: center a rest between two notes + var centerRest = function(rest, noteU, noteL) { + var delta = rest.line - Vex.MidLine(noteU.min_line, noteL.max_line); + rest.note.keyProps[0].line -= delta; + rest.line -= delta; + rest.max_line -= delta; + rest.min_line -= delta; + }; + + ModifierContext.prototype = { + addModifier: function(modifier) { + var type = modifier.getCategory(); + if (!this.modifiers[type]) this.modifiers[type] = []; + this.modifiers[type].push(modifier); + modifier.setModifierContext(this); + this.preFormatted = false; + return this; + }, + + getModifiers: function(type) { return this.modifiers[type]; }, + + getWidth: function() { return this.width; }, + + getExtraLeftPx: function() { return this.state.left_shift; }, + + getExtraRightPx: function() { return this.state.right_shift; }, + + getMetrics: function() { + if (!this.formatted) throw new Vex.RERR("UnformattedModifier", + "Unformatted modifier has no metrics."); + + return { + width: this.state.left_shift + this.state.right_shift + this.spacing, + spacing: this.spacing, + extra_left_px: this.state.left_shift, + extra_right_px: this.state.right_shift + }; + }, + + formatNotes: function() { + var notes = this.modifiers['stavenotes']; + if (!notes || notes.length < 2) return this; + + if (notes[0].getStave() != null) + return this.formatNotesByY(notes); + + var notes_list= []; + + for (var i = 0; i < notes.length; i++) { + var props = notes[i].getKeyProps(); + var line = props[0].line; + var minL = props[props.length -1].line; + var stem_dir = notes[i].getStemDirection(); + var stem_max = notes[i].getStemLength() / 10; + var stem_min = notes[i].getStemMinumumLength() / 10; + + var maxL; + if (notes[i].isRest()) { + maxL = line + notes[i].glyph.line_above; + minL = line - notes[i].glyph.line_below; + } else { + maxL = stem_dir == 1 ? props[props.length -1].line + stem_max + : props[props.length -1].line; + minL = stem_dir == 1 ? props[0].line + : props[0].line - stem_max; + } + notes_list.push( + {line: props[0].line, // note/rest base line + max_line: maxL, // note/rest upper bounds line + min_line: minL, // note/rest lower bounds line + isrest: notes[i].isRest(), + stem_dir: stem_dir, + stem_max: stem_max, // Maximum (default) note stem length; + stem_min: stem_min, // minimum note stem length + voice_shift: notes[i].getVoiceShiftWidth(), + is_displaced: notes[i].isDisplaced(), // note manually displaced + note: notes[i]}); + } + + var voices = notes_list.length; + + var noteU = notes_list[0]; + var noteM = voices > 2 ? notes_list[1] : null; + var noteL = voices > 2 ? notes_list[2] : notes_list[1]; + + // for two voice backward compatibility, ensure upper voice is stems up + // for three voices, the voices must be in order (upper, middle, lower) + if (voices == 2 && noteU.stem_dir == -1 && noteL.stem_dir == 1) { + noteU = notes_list[1]; + noteL = notes_list[0]; + } + + var voice_x_shift = Math.max(noteU.voice_shift, noteL.voice_shift); + var x_shift = 0; + var stem_delta; + + // Test for two voice note intersection + if (voices == 2) { + var line_spacing = noteU.stem_dir == noteL.stem_dir ? 0.0 : 0.5; + // if top voice is a middle voice, check stem intersection with lower voice + if (noteU.stem_dir == noteL.stem_dir && + noteU.min_line <= noteL.max_line) { + if (!noteU.isrest) { + stem_delta = Math.abs(noteU.line - (noteL.max_line + 0.5)); + stem_delta = Math.max(stem_delta, noteU.stem_min); + noteU.min_line = noteU.line - stem_delta; + noteU.note.setStemLength(stem_delta * 10); + } + } + if (noteU.min_line <= noteL.max_line + line_spacing) { + if (noteU.isrest) + // shift rest up + shiftRestVertical(noteU, noteL, 1); + else if (noteL.isrest) + // shift rest down + shiftRestVertical(noteL, noteU, -1); + else { + x_shift = voice_x_shift; + if (noteU.stem_dir == noteL.stem_dir) + // upper voice is middle voice, so shift it right + noteU.note.setXShift(x_shift + 3); + else + // shift lower voice right + noteL.note.setXShift(x_shift); + } + } + + // format complete + return this; + } + + // Check middle voice stem intersection with lower voice + if (noteM != null && noteM.min_line < noteL.max_line + 0.5) { + if (!noteM.isrest) { + stem_delta = Math.abs(noteM.line - (noteL.max_line + 0.5)); + stem_delta = Math.max(stem_delta, noteM.stem_min); + noteM.min_line = noteM.line - stem_delta; + noteM.note.setStemLength(stem_delta * 10); + } + } + + // For three voices, test if rests can be repositioned + // + // Special case 1 :: middle voice rest between two notes + // + if (noteM.isrest && !noteU.isrest && !noteL.isrest) { + if (noteU.min_line <= noteM.max_line || + noteM.min_line <= noteL.max_line) { + var rest_height = noteM.max_line - noteM.min_line; + var space = noteU.min_line - noteL.max_line; + if (rest_height < space) + // center middle voice rest between the upper and lower voices + centerRest(noteM, noteU, noteL); + else { + x_shift = voice_x_shift + 3; // shift middle rest right + noteM.note.setXShift(x_shift); + } + // format complete + return this; + } + } + + // Special case 2 :: all voices are rests + if (noteU.isrest && noteM.isrest && noteL.isrest) { + // Shift upper voice rest up + shiftRestVertical(noteU, noteM, 1); + // Shift lower voice rest down + shiftRestVertical(noteL, noteM, -1); + // format complete + return this; + } + + // Test if any other rests can be repositioned + if (noteM.isrest && noteU.isrest && noteM.min_line <= noteL.max_line) + // Shift middle voice rest up + shiftRestVertical(noteM, noteL, 1); + if (noteM.isrest && noteL.isrest && noteU.min_line <= noteM.max_line) + // Shift middle voice rest down + shiftRestVertical(noteM, noteU, -1); + if (noteU.isrest && noteU.min_line <= noteM.max_line) + // shift upper voice rest up; + shiftRestVertical(noteU, noteM, 1); + if (noteL.isrest && noteM.min_line <= noteL.max_line) + // shift lower voice rest down + shiftRestVertical(noteL, noteM, -1); + + // If middle voice intersects upper or lower voice + if ((!noteU.isrest && !noteM.isrest && noteU.min_line <= noteM.max_line + 0.5) || + (!noteM.isrest && !noteL.isrest && noteM.min_line <= noteL.max_line)) { + x_shift = voice_x_shift + 3; // shift middle note right + noteM.note.setXShift(x_shift); + } + + // Format complete + return this; + + }, + + formatNotesByY: function(notes) { + // NOTE: this function does not support more than two voices per stave + // use with care. + var hasStave = true; + var i; + + for (i = 0; i < notes.length; i++) { + hasStave = hasStave && notes[i].getStave() != null; + } + + if (!hasStave) throw new Vex.RERR("Stave Missing", + "All notes must have a stave - Vex.Flow.ModifierContext.formatMultiVoice!"); + + var x_shift = 0; + + for (i = 0; i < notes.length - 1; i++) { + var top_note = notes[i]; + var bottom_note = notes[i + 1]; + + if (top_note.getStemDirection() == Vex.Flow.StaveNote.STEM_DOWN) { + top_note = notes[i + 1]; + bottom_note = notes[i]; + } + + var top_keys = top_note.getKeyProps(); + var bottom_keys = bottom_note.getKeyProps(); + + var topY = top_note.getStave().getYForLine(top_keys[0].line); + var bottomY = bottom_note.getStave().getYForLine(bottom_keys[bottom_keys.length - 1].line); + + var line_space = top_note.getStave().options.spacing_between_lines_px; + if (Math.abs(topY - bottomY) == line_space / 2) { + x_shift = top_note.getVoiceShiftWidth(); + bottom_note.setXShift(x_shift); + } + } + + this.state.right_shift += x_shift; + return this; + }, + + formatDots: function() { + var right_shift = this.state.right_shift; + var dots = this.modifiers['dots']; + var dot_spacing = 1; + + if (!dots || dots.length === 0) return this; + + var i, dot, note, shift; + var dot_list = []; + for (i = 0; i < dots.length; ++i) { + dot = dots[i]; + note = dot.getNote(); + + var props; + // Only StaveNote has .getKeyProps() + if (typeof note.getKeyProps === 'function') { + props = note.getKeyProps()[dot.getIndex()]; + shift = (props.displaced ? note.getExtraRightPx() : 0); + } else { // Else it's a TabNote + props = { line: 0.5 }; // Shim key props for dot placement + shift = 0; + } + + dot_list.push({ line: props.line, shift: shift, note: note, dot: dot }); + } + + // Sort dots by line number. + dot_list.sort(function(a, b) { return (b.line - a.line); }); + + var dot_shift = right_shift; + var x_width = 0; + var last_line = null; + var last_note = null; + var prev_dotted_space = null; + var half_shiftY = 0; + + for (i = 0; i < dot_list.length; ++i) { + dot = dot_list[i].dot; + note = dot_list[i].note; + shift = dot_list[i].shift; + var line = dot_list[i].line; + + // Reset the position of the dot every line. + if (line != last_line || note != last_note) { + dot_shift = shift; + } + + if (!note.isRest() && line != last_line) { + if (line % 1 == 0.5) { + // note is on a space, so no dot shift + half_shiftY = 0; + } else if (!note.isRest()) { + // note is on a line, so shift dot to space above the line + half_shiftY = 0.5; + if (last_note != null && + !last_note.isRest() && last_line - line == 0.5) { + // previous note on a space, so shift dot to space below the line + half_shiftY = -0.5; + } else if (line + half_shiftY == prev_dotted_space) { + // previous space is dotted, so shift dot to space below the line + half_shiftY = -0.5; + } + } + } + + // convert half_shiftY to a multiplier for dots.draw() + dot.dot_shiftY += (-half_shiftY); + prev_dotted_space = line + half_shiftY; + + dot.setXShift(dot_shift); + dot_shift += dot.getWidth() + dot_spacing; // spacing + x_width = (dot_shift > x_width) ? dot_shift : x_width; + last_line = line; + last_note = note; + } + + this.state.right_shift += x_width; + return this; + }, + + formatAccidentals: function() { + var left_shift = this.state.left_shift; + var accidentals = this.modifiers['accidentals']; + var accidental_spacing = 2; + + if (!accidentals || accidentals.length === 0) return this; + + var acc_list = []; + var hasStave = false; + var prev_note = null; + var shiftL = 0; + + var i, acc, props_tmp; + for (i = 0; i < accidentals.length; ++i) { + acc = accidentals[i]; + var note = acc.getNote(); + var stave = note.getStave(); + var props = note.getKeyProps()[acc.getIndex()]; + if (note != prev_note) { + // Iterate through all notes to get the displaced pixels + for (var n = 0; n < note.keys.length; ++n) { + props_tmp = note.getKeyProps()[n]; + shiftL = (props_tmp.displaced ? note.getExtraLeftPx() : shiftL); + } + prev_note = note; + } + if (stave != null) { + hasStave = true; + var line_space = stave.options.spacing_between_lines_px; + var y = stave.getYForLine(props.line); + acc_list.push({ y: y, shift: shiftL, acc: acc, lineSpace: line_space }); + } else { + acc_list.push({ line: props.line, shift: shiftL, acc: acc }); + } + } + + // If stave assigned, format based on note y-position + if (hasStave) + return this.formatAccidentalsByY(acc_list); + + // Sort accidentals by line number. + acc_list.sort(function(a, b) { return (b.line - a.line); }); + + // If first note left shift in case it is displaced + var acc_shift = acc_list[0].shift; + var x_width = 0; + var top_line = acc_list[0].line; + for (i = 0; i < acc_list.length; ++i) { + acc = acc_list[i].acc; + var line = acc_list[i].line; + var shift = acc_list[i].shift; + + // Once you hit three stave lines, you can reset the position of the + // accidental. + if (line < top_line - 3.0) { + top_line = line; + acc_shift = shift; + } + + acc.setXShift(left_shift + acc_shift); + acc_shift += acc.getWidth() + accidental_spacing; // spacing + x_width = (acc_shift > x_width) ? acc_shift : x_width; + } + + this.state.left_shift += x_width; + return this; + }, + + formatAccidentalsByY: function(acc_list) { + var left_shift = this.state.left_shift; + var accidental_spacing = 2; + + // Sort accidentals by Y-position. + acc_list.sort(function(a, b) { return (b.y - a.y); }); + + // If first note is displaced, get the correct left shift + var acc_shift = acc_list[0].shift; + var x_width = 0; + var top_y = acc_list[0].y; + + for (var i = 0; i < acc_list.length; ++i) { + var acc = acc_list[i].acc; + var y = acc_list[i].y; + var shift = acc_list[i].shift; + + // Once you hit three stave lines, you can reset the position of the + // accidental. + if (top_y - y > 3 * acc_list[i].lineSpace) { + top_y = y; + acc_shift = shift; + } + + acc.setXShift(acc_shift + left_shift); + acc_shift += acc.getWidth() + accidental_spacing; // spacing + x_width = (acc_shift > x_width) ? acc_shift : x_width; + } + + this.state.left_shift += x_width; + return this; + }, + + formatStrokes: function() { + var left_shift = this.state.left_shift; + var strokes = this.modifiers['strokes']; + var stroke_spacing = 0; + + if (!strokes || strokes.length === 0) return this; + + var str_list = []; + var i, str, shift; + for (i = 0; i < strokes.length; ++i) { + str = strokes[i]; + var note = str.getNote(); + var props; + if (note instanceof Vex.Flow.StaveNote) { + props = note.getKeyProps()[str.getIndex()]; + shift = (props.displaced ? note.getExtraLeftPx() : 0); + str_list.push({ line: props.line, shift: shift, str: str }); + } else { + props = note.getPositions()[str.getIndex()]; + str_list.push({ line: props.str, shift: 0, str: str }); + } + } + + var str_shift = left_shift; + var x_shift = 0; + + // There can only be one stroke .. if more than one, they overlay each other + for (i = 0; i < str_list.length; ++i) { + str = str_list[i].str; + shift = str_list[i].shift; + + str.setXShift(str_shift + shift); + x_shift = Math.max(str.getWidth() + stroke_spacing, x_shift); + } + + this.state.left_shift += x_shift; + return this; + }, + + formatStringNumbers: function() { + var left_shift = this.state.left_shift; + var right_shift = this.state.right_shift; + var nums = this.modifiers['stringnumber']; + var num_spacing = 1; + + if (!nums || nums.length === 0) return this; + + var nums_list = []; + var prev_note = null; + var shift_left = 0; + var shift_right = 0; + + var i, num, note, pos, props_tmp; + for (i = 0; i < nums.length; ++i) { + num = nums[i]; + note = num.getNote(); + + for (i = 0; i < nums.length; ++i) { + num = nums[i]; + note = num.getNote(); + pos = num.getPosition(); + var props = note.getKeyProps()[num.getIndex()]; + + if (note != prev_note) { + for (var n = 0; n < note.keys.length; ++n) { + props_tmp = note.getKeyProps()[n]; + if (left_shift === 0) + shift_left = (props_tmp.displaced ? note.getExtraLeftPx() : shift_left); + if (right_shift === 0) + shift_right = (props_tmp.displaced ? note.getExtraRightPx() : shift_right); + } + prev_note = note; + } + + nums_list.push({ line: props.line, pos: pos, shiftL: shift_left, shiftR: shift_right, note: note, num: num }); + } + } + + // Sort string numbers by line number. + nums_list.sort(function(a, b) { return (b.line - a.line); }); + + var num_shiftL = 0; + var num_shiftR = 0; + var x_widthL = 0; + var x_widthR = 0; + var last_line = null; + var last_note = null; + for (i = 0; i < nums_list.length; ++i) { + var num_shift = 0; + note = nums_list[i].note; + pos = nums_list[i].pos; + num = nums_list[i].num; + var line = nums_list[i].line; + var shiftL = nums_list[i].shiftL; + var shiftR = nums_list[i].shiftR; + + // Reset the position of the string number every line. + if (line != last_line || note != last_note) { + num_shiftL = left_shift + shiftL; + num_shiftR = right_shift + shiftR; + } + + var num_width = num.getWidth() + num_spacing; + if (pos == Vex.Flow.Modifier.Position.LEFT) { + num.setXShift(left_shift); + num_shift = shift_left + num_width; // spacing + x_widthL = (num_shift > x_widthL) ? num_shift : x_widthL; + } else if (pos == Vex.Flow.Modifier.Position.RIGHT) { + num.setXShift(num_shiftR); + num_shift += num_width; // spacing + x_widthR = (num_shift > x_widthR) ? num_shift : x_widthR; + } + last_line = line; + last_note = note; + } + + this.state.left_shift += x_widthL; + this.state.right_shift += x_widthR; + return this; + }, + + formatFretHandFingers: function() { + var left_shift = this.state.left_shift; + var right_shift = this.state.right_shift; + var nums = this.modifiers['frethandfinger']; + var num_spacing = 1; + + if (!nums || nums.length === 0) return this; + + var nums_list = []; + var prev_note = null; + var shift_left = 0; + var shift_right = 0; + + var i, num, note, pos, props_tmp; + for (i = 0; i < nums.length; ++i) { + num = nums[i]; + note = num.getNote(); + pos = num.getPosition(); + var props = note.getKeyProps()[num.getIndex()]; + if (note != prev_note) { + for (var n = 0; n < note.keys.length; ++n) { + props_tmp = note.getKeyProps()[n]; + if (left_shift === 0) + shift_left = (props_tmp.displaced ? note.getExtraLeftPx() : shift_left); + if (right_shift === 0) + shift_right = (props_tmp.displaced ? note.getExtraRightPx() : shift_right); + } + prev_note = note; + } + + nums_list.push({ line: props.line, pos: pos, shiftL: shift_left, shiftR: shift_right, note: note, num: num }); + } + + // Sort fingernumbers by line number. + nums_list.sort(function(a, b) { return (b.line - a.line); }); + + var num_shiftL = 0; + var num_shiftR = 0; + var x_widthL = 0; + var x_widthR = 0; + var last_line = null; + var last_note = null; + + for (i = 0; i < nums_list.length; ++i) { + var num_shift = 0; + note = nums_list[i].note; + pos = nums_list[i].pos; + num = nums_list[i].num; + var line = nums_list[i].line; + var shiftL = nums_list[i].shiftL; + var shiftR = nums_list[i].shiftR; + + // Reset the position of the string number every line. + if (line != last_line || note != last_note) { + num_shiftL = left_shift + shiftL; + num_shiftR = right_shift + shiftR; + } + + var num_width = num.getWidth() + num_spacing; + if (pos == Vex.Flow.Modifier.Position.LEFT) { + num.setXShift(left_shift + num_shiftL); + num_shift = left_shift + num_width; // spacing + x_widthL = (num_shift > x_widthL) ? num_shift : x_widthL; + } else if (pos == Vex.Flow.Modifier.Position.RIGHT) { + num.setXShift(num_shiftR); + num_shift = shift_right + num_width; // spacing + x_widthR = (num_shift > x_widthR) ? num_shift : x_widthR; + } + last_line = line; + last_note = note; + } + + this.state.left_shift += x_widthL; + this.state.right_shift += x_widthR; + return this; + }, + + formatBends: function() { + var bends = this.modifiers['bends']; + if (!bends || bends.length === 0) return this; + + var last_width = 0; + var text_line = this.state.text_line; + + // Format Bends + for (var i = 0; i < bends.length; ++i) { + var bend = bends[i]; + bend.setXShift(last_width); + last_width = bend.getWidth(); + bend.setTextLine(text_line); + } + + this.state.right_shift += last_width; + this.state.text_line += 1; + return this; + }, + + formatVibratos: function() { + var vibratos = this.modifiers['vibratos']; + if (!vibratos || vibratos.length === 0) return this; + + var text_line = this.state.text_line; + var width = 0; + var shift = this.state.right_shift - 7; + + // If there's a bend, drop the text line + var bends = this.modifiers['bends']; + if (bends && bends.length > 0) { + text_line--; + } + + // Format Vibratos + for (var i = 0; i < vibratos.length; ++i) { + var vibrato = vibratos[i]; + vibrato.setXShift(shift); + vibrato.setTextLine(text_line); + width += vibrato.getWidth(); + shift += width; + } + + this.state.right_shift += width; + this.state.text_line += 1; + return this; + }, + + formatAnnotations: function() { + var annotations = this.modifiers['annotations']; + if (!annotations || annotations.length === 0) return this; + + var text_line = this.state.text_line; + var max_width = 0; + + // Format Annotations + var width; + for (var i = 0; i < annotations.length; ++i) { + var annotation = annotations[i]; + annotation.setTextLine(text_line); + width = annotation.getWidth() > max_width ? + annotation.getWidth() : max_width; + text_line++; + } + + this.state.left_shift += width / 2; + this.state.right_shift += width / 2; + // No need to update text_line because we leave lots of room on the same + // line. + return this; + }, + + formatArticulations: function() { + var articulations = this.modifiers['articulations']; + if (!articulations || articulations.length === 0) return this; + + var text_line = this.state.text_line; + var max_width = 0; + + // Format Articulations + var width; + for (var i = 0; i < articulations.length; ++i) { + var articulation = articulations[i]; + articulation.setTextLine(text_line); + width = articulation.getWidth() > max_width ? + articulation.getWidth() : max_width; + + var type = Vex.Flow.articulationCodes(articulation.type); + if(type.between_lines) + text_line += 1; + else + text_line += 1.5; + } + + this.state.left_shift += width / 2; + this.state.right_shift += width / 2; + this.state.text_line = text_line; + return this; + }, + + formatGraceNoteGroups: function(){ + var gracenote_groups = this.modifiers['gracenotegroups']; + var gracenote_spacing = 4; + + if (!gracenote_groups || gracenote_groups.length === 0) return this; + + var group_list = []; + var hasStave = false; + var prev_note = null; + var shiftL = 0; + + var i, gracenote_group, props_tmp; + for (i = 0; i < gracenote_groups.length; ++i) { + gracenote_group = gracenote_groups[i]; + var note = gracenote_group.getNote(); + var stave = note.getStave(); + if (note != prev_note) { + // Iterate through all notes to get the displaced pixels + for (var n = 0; n < note.keys.length; ++n) { + props_tmp = note.getKeyProps()[n]; + shiftL = (props_tmp.displaced ? note.getExtraLeftPx() : shiftL); + } + prev_note = note; + } + if (stave != null) { + hasStave = true; + group_list.push({shift: shiftL, gracenote_group: gracenote_group}); + } else { + group_list.push({shift: shiftL, gracenote_group: gracenote_group }); + } + } + + // If first note left shift in case it is displaced + var group_shift = group_list[0].shift; + for (i = 0; i < group_list.length; ++i) { + gracenote_group = group_list[i].gracenote_group; + gracenote_group.preFormat(); + group_shift = gracenote_group.getWidth() + gracenote_spacing; + } + + this.state.left_shift += group_shift; + return this; + }, + + postFormatNotes: function() { + var notes = this.modifiers['stavenotes']; + + if (!notes) return; + + notes.forEach(function(note) { + note.postFormat(); + }); + + return this; + }, + + preFormat: function() { + if (this.preFormatted) return; + + // Format modifiers in the following order: + this.formatNotes(). + formatDots(). + formatFretHandFingers(). + formatAccidentals(). + formatGraceNoteGroups(). + formatStrokes(). + formatStringNumbers(). + formatArticulations(). + formatAnnotations(). + formatBends(). + formatVibratos(); + + // Update width of this modifier context + this.width = this.state.left_shift + this.state.right_shift; + this.preFormatted = true; + }, + + postFormat: function() { + if (this.postFormatted) return; + + this.postFormatNotes(); + return this; + } + }; + + return ModifierContext; +}()); +// [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010. +// +// ## Description +// +// This file implements accidentals as modifiers that can be attached to +// notes. Support is included for both western and microtonal accidentals. +// +// See `tests/accidental_tests.js` for usage examples. + +Vex.Flow.Accidental = (function(){ + function Accidental(type) { + if (arguments.length > 0) this.init(type); + } + + // To enable logging for this class. Set `Vex.Flow.Accidental.DEBUG` to `true`. + function L() { if (Accidental.DEBUG) Vex.L("Vex.Flow.Accidental", arguments); } + + var Modifier = Vex.Flow.Modifier; + + // ## Prototype Methods + // + // An `Accidental` inherits from `Modifier`, and is formatted within a + // `ModifierContext`. + Vex.Inherit(Accidental, Modifier, { + // Create accidental. `type` can be a value from the + // `Vex.Flow.accidentalCodes.accidentals` table in `tables.js`. For + // example: `#`, `##`, `b`, `n`, etc. + init: function(type) { + Accidental.superclass.init.call(this); + L("New accidental: ", type); + + this.note = null; + // The `index` points to a specific note in a chord. + this.index = null; + this.type = type; + this.position = Modifier.Position.LEFT; + + this.render_options = { + // Font size for glyphs + font_scale: 38, + + // Length of stroke across heads above or below the stave. + stroke_px: 3 + }; + + this.accidental = Vex.Flow.accidentalCodes(this.type); + if (!this.accidental) throw new Vex.RERR("ArgumentError", "Unknown accidental type: " + type); + + // Cautionary accidentals have parentheses around them + this.cautionary = false; + this.paren_left = null; + this.paren_right = null; + + // Initial width is set from table. + this.setWidth(this.accidental.width); + }, + + // Return the modifier type. Used by the `ModifierContext` to calculate + // layout. + getCategory: function() { return "accidentals"; }, + + // Attach this accidental to `note`, which must be a `StaveNote`. + setNote: function(note){ + if (!note) throw new Vex.RERR("ArgumentError", "Bad note value: " + note); + this.note = note; + + // Accidentals attached to grace notes are rendered smaller. + if (this.note.getCategory() === 'gracenotes') { + this.render_options.font_scale = 25; + this.setWidth(this.accidental.gracenote_width); + } + }, + + // If called, draws parenthesis around accidental. + setAsCautionary: function() { + this.cautionary = true; + this.render_options.font_scale = 28; + this.paren_left = Vex.Flow.accidentalCodes("{"); + this.paren_right = Vex.Flow.accidentalCodes("}"); + var width_adjust = (this.type == "##" || this.type == "bb") ? 6 : 4; + + // Make sure `width` accomodates for parentheses. + this.setWidth(this.paren_left.width + this.accidental.width + this.paren_right.width - width_adjust); + return this; + }, + + // Render accidental onto canvas. + draw: function() { + if (!this.context) throw new Vex.RERR("NoContext", + "Can't draw accidental without a context."); + if (!(this.note && (this.index != null))) throw new Vex.RERR("NoAttachedNote", + "Can't draw accidental without a note and index."); + + // Figure out the start `x` and `y` coordinates for this note and index. + var start = this.note.getModifierStartXY(this.position, this.index); + var acc_x = (start.x + this.x_shift) - this.width; + var acc_y = start.y + this.y_shift; + L("Rendering: ", this.type, acc_x, acc_y); + + if (!this.cautionary) { + // Render the accidental alone. + Vex.Flow.renderGlyph(this.context, acc_x, acc_y, + this.render_options.font_scale, this.accidental.code); + } else { + // Render the accidental in parentheses. + acc_x += 3; + Vex.Flow.renderGlyph(this.context, acc_x, acc_y, + this.render_options.font_scale, this.paren_left.code); + acc_x += 2; + Vex.Flow.renderGlyph(this.context, acc_x, acc_y, + this.render_options.font_scale, this.accidental.code); + acc_x += this.accidental.width - 2; + if (this.type == "##" || this.type == "bb") acc_x -= 2; + Vex.Flow.renderGlyph(this.context, acc_x, acc_y, + this.render_options.font_scale, this.paren_right.code); + } + } + }); + + return Accidental; +}());// VexFlow - Music Engraving for HTML5 +// Copyright Mohit Muthanna 2010 +// +// This class implements dot modifiers for notes. + +/** + * @constructor + */ +Vex.Flow.Dot = (function() { + function Dot() { + this.init(); + } + + var Modifier = Vex.Flow.Modifier; + Vex.Inherit(Dot, Modifier, { + init: function() { + Dot.superclass.init.call(this); + + this.note = null; + this.index = null; + this.position = Modifier.Position.RIGHT; + + this.radius = 2; + this.setWidth(5); + this.dot_shiftY = 0; + }, + + setNote: function(note){ + this.note = note; + + if (this.note.getCategory() === 'gracenotes') { + this.radius *= 0.50; + this.setWidth(3); + } + }, + + getCategory: function() { return "dots"; }, + + setDotShiftY: function(y) { this.dot_shiftY = y; return this; }, + + draw: function() { + if (!this.context) throw new Vex.RERR("NoContext", + "Can't draw dot without a context."); + if (!(this.note && (this.index != null))) throw new Vex.RERR("NoAttachedNote", + "Can't draw dot without a note and index."); + + var line_space = this.note.stave.options.spacing_between_lines_px; + + var start = this.note.getModifierStartXY(this.position, this.index); + + // Set the starting y coordinate to the base of the stem for TabNotes + if (this.note.getCategory() === 'tabnotes') { + start.y = this.note.getStemExtents().baseY; + } + + var dot_x = (start.x + this.x_shift) + this.width - this.radius; + var dot_y = start.y + this.y_shift + (this.dot_shiftY * line_space); + var ctx = this.context; + + ctx.beginPath(); + ctx.arc(dot_x, dot_y, this.radius, 0, Math.PI * 2, false); + ctx.fill(); + } + }); + + return Dot; +}()); +// [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010. +// +// ## Description +// +// This file implements the formatting and layout algorithms that are used +// to position notes in a voice. The algorithm can align multiple voices both +// within a stave, and across multiple staves. +// +// To do this, the formatter breaks up voices into a grid of rational-valued +// `ticks`, to which each note is assigned. Then, minimum widths are assigned +// to each tick based on the widths of the notes and modifiers in that tick. This +// establishes the smallest amount of space required for each tick. +// +// Finally, the formatter distributes the left over space proportionally to +// all the ticks, setting the `x` values of the notes in each tick. +// +// See `tests/formatter_tests.js` for usage examples. The helper functions included +// here (`FormatAndDraw`, `FormatAndDrawTab`) also serve as useful usage examples. + +Vex.Flow.Formatter = (function() { + function Formatter() { + // Minimum width required to render all the notes in the voices. + this.minTotalWidth = 0; + + // This is set to `true` after `minTotalWidth` is calculated. + this.hasMinTotalWidth = false; + + // The suggested amount of space for each tick. + this.pixelsPerTick = 0; + + // Total number of ticks in the voice. + this.totalTicks = new Vex.Flow.Fraction(0, 1); + + // Arrays of tick and modifier contexts. + this.tContexts = null; + this.mContexts = null; + } + + // To enable logging for this class. Set `Vex.Flow.Formatter.DEBUG` to `true`. + function L() { if (Formatter.DEBUG) Vex.L("Vex.Flow.Formatter", arguments); } + + // ## Private Helpers + // + // Helper function to locate the next non-rest note(s). + function lookAhead(notes, rest_line, i, compare) { + // If no valid next note group, next_rest_line is same as current. + var next_rest_line = rest_line; + + // Get the rest line for next valid non-rest note group. + i++; + while (i < notes.length) { + if (!notes[i].isRest() && !notes[i].shouldIgnoreTicks()) { + next_rest_line = notes[i].getLineForRest(); + break; + } + i++; + } + + // Locate the mid point between two lines. + if (compare && rest_line != next_rest_line) { + var top = Vex.Max(rest_line, next_rest_line); + var bot = Vex.Min(rest_line, next_rest_line); + next_rest_line = Vex.MidLine(top, bot); + } + return next_rest_line; + } + + // Take an array of `voices` and place aligned tickables in the same context. Returns + // a mapping from `tick` to `context_type`, a list of `tick`s, and the resolution + // multiplier. + // + // Params: + // * `voices`: Array of `Voice` instances. + // * `context_type`: A context class (e.g., `ModifierContext`, `TickContext`) + // * `add_fn`: Function to add tickable to context. + function createContexts(voices, context_type, add_fn) { + if (!voices || !voices.length) throw new Vex.RERR("BadArgument", + "No voices to format"); + + // Initialize tick maps. + var totalTicks = voices[0].getTotalTicks(); + var tickToContextMap = {}; + var tickList = []; + + var resolutionMultiplier = 1; + + // Find out highest common multiple of resolution multipliers. + // The purpose of this is to find out a common denominator + // for all fractional tick values in all tickables of all voices, + // so that the values can be expanded and the numerator used + // as an integer tick value. + var i; // shared iterator + var voice; + for (i = 0; i < voices.length; ++i) { + voice = voices[i]; + if (voice.getTotalTicks().value() != totalTicks.value()) { + throw new Vex.RERR("TickMismatch", + "Voices should have same time signature."); + } + + if (voice.getMode() == Vex.Flow.Voice.Mode.STRICT && !voice.isComplete()) + throw new Vex.RERR("IncompleteVoice", + "Voice does not have enough notes."); + + var lcm = Vex.Flow.Fraction.LCM(resolutionMultiplier, + voice.getResolutionMultiplier()); + if (resolutionMultiplier < lcm) { + resolutionMultiplier = lcm; + } + } + + // For each voice, extract notes and create a context for every + // new tick that hasn't been seen before. + for (i = 0; i < voices.length; ++i) { + voice = voices[i]; + + var tickables = voice.getTickables(); + + // Use resolution multiplier as denominator to expand ticks + // to suitable integer values, so that no additional expansion + // of fractional tick values is needed. + var ticksUsed = new Vex.Flow.Fraction(0, resolutionMultiplier); + + for (var j = 0; j < tickables.length; ++j) { + var tickable = tickables[j]; + var integerTicks = ticksUsed.numerator; + + // If we have no tick context for this tick, create one. + if (!tickToContextMap[integerTicks]) + tickToContextMap[integerTicks] = new context_type(); + + // Add this tickable to the TickContext. + add_fn(tickable, tickToContextMap[integerTicks]); + + // Maintain a sorted list of tick contexts. + tickList.push(integerTicks); + ticksUsed.add(tickable.getTicks()); + } + } + + return { + map: tickToContextMap, + list: Vex.SortAndUnique(tickList, function(a, b) { return a - b; }, + function(a, b) { return a === b; } ), + resolutionMultiplier: resolutionMultiplier + }; + } + + + // ## Static Methods + // + // Helper function to format and draw a single voice. Returns a bounding + // box for the notation. + // + // Parameters: + // * `ctx` - The rendering context + // * `stave` - The stave to which to draw (`Stave` or `TabStave`) + // * `notes` - Array of `Note` instances (`StaveNote`, `TextNote`, `TabNote`, etc.) + // * `params` - One of below: + // * Setting `autobeam` only `(context, stave, notes, true)` or `(ctx, stave, notes, {autobeam: true})` + // * Setting `align_rests` a struct is needed `(context, stave, notes, {align_rests: true})` + // * Setting both a struct is needed `(context, stave, notes, {autobeam: true, align_rests: true})` + // + // `autobeam` automatically generates beams for the notes. + // `align_rests` aligns rests with nearby notes. + Formatter.FormatAndDraw = function(ctx, stave, notes, params) { + var opts = { + auto_beam: false, + align_rests: false + }; + + if (typeof params == "object") { + Vex.Merge(opts, params); + } else if (typeof params == "boolean") { + opts.auto_beam = params; + } + + // Start by creating a voice and adding all the notes to it. + var voice = new Vex.Flow.Voice(Vex.Flow.TIME4_4). + setMode(Vex.Flow.Voice.Mode.SOFT); + voice.addTickables(notes); + + // Then create beams, if requested. + var beams = null; + if (opts.auto_beam) { + beams = Vex.Flow.Beam.applyAndGetBeams(voice); + } + + // Instantiate a `Formatter` and format the notes. + new Formatter(). + joinVoices([voice], {align_rests: opts.align_rests}). + formatToStave([voice], stave, {align_rests: opts.align_rests}); + + // Render the voice and beams to the stave. + voice.setStave(stave); + voice.draw(ctx, stave); + if (beams != null) { + for (var i=0; i 0 && i < notes.length) { + // If previous note is a rest, use its line number. + var rest_line; + if (notes[i-1].isRest()) { + rest_line = notes[i-1].getKeyProps()[0].line; + props.line = rest_line; + } else { + rest_line = notes[i-1].getLineForRest(); + // Get the rest line for next valid non-rest note group. + props.line = lookAhead(notes, rest_line, i, true); + } + } + } + } + } + + return this; + }; + + // ## Prototype Methods + Formatter.prototype = { + // Find all the rests in each of the `voices` and align them + // to neighboring notes. If `align_all_notes` is `false`, then only + // align non-beamed notes. + alignRests: function(voices, align_all_notes) { + if (!voices || !voices.length) throw new Vex.RERR("BadArgument", + "No voices to format rests"); + for (var i = 0; i < voices.length; i++) { + new Formatter.AlignRestsToNotes(voices[i].tickables, align_all_notes); + } + }, + + // Calculate the minimum width required to align and format `voices`. + preCalculateMinTotalWidth: function(voices) { + // Cache results. + if (this.hasMinTotalWidth) return; + + // Create tick contexts if not already created. + if (!this.tContexts) { + if (!voices) { + throw new Vex.RERR("BadArgument", + "'voices' required to run preCalculateMinTotalWidth"); + } + this.createTickContexts(voices); + } + + var contexts = this.tContexts; + var contextList = contexts.list; + var contextMap = contexts.map; + + this.minTotalWidth = 0; + + // Go through each tick context and calculate total width. + for (var i = 0; i < contextList.length; ++i) { + var context = contextMap[contextList[i]]; + + // `preFormat` gets them to descend down to their tickables and modifier + // contexts, and calculate their widths. + context.preFormat(); + this.minTotalWidth += context.getWidth(); + } + + this.hasMinTotalWidth = true; + + return this.minTotalWidth; + }, + + // Get minimum width required to render all voices. Either `format` or + // `preCalculateMinTotalWidth` must be called before this method. + getMinTotalWidth: function() { + if (!this.hasMinTotalWidth) { + throw new Vex.RERR("NoMinTotalWidth", + "Need to call 'preCalculateMinTotalWidth' or 'preFormat' before" + + " calling 'getMinTotalWidth'"); + } + + return this.minTotalWidth; + }, + + // Create `ModifierContext`s for each tick in `voices`. + createModifierContexts: function(voices) { + var contexts = createContexts(voices, + Vex.Flow.ModifierContext, + function(tickable, context) { + tickable.addToModifierContext(context); + }); + this.mContexts = contexts; + return contexts; + }, + + // Create `TickContext`s for each tick in `voices`. Also calculate the + // total number of ticks in voices. + createTickContexts: function(voices) { + var contexts = createContexts(voices, + Vex.Flow.TickContext, + function(tickable, context) { context.addTickable(tickable); }); + + this.totalTicks = voices[0].getTicksUsed().clone(); + this.tContexts = contexts; + return contexts; + }, + + // This is the core formatter logic. Format voices and justify them + // to `justifyWidth` pixels. `rendering_context` is required to justify elements + // that can't retreive widths without a canvas. This method sets the `x` positions + // of all the tickables/notes in the formatter. + preFormat: function(justifyWidth, rendering_context, voices, stave) { + // Initialize context maps. + var contexts = this.tContexts; + var contextList = contexts.list; + var contextMap = contexts.map; + + // If voices and a stave were provided, set the Stave for each voice + // and preFormat to apply Y values to the notes; + if (voices && stave) { + voices.forEach(function(voice) { + voice.setStave(stave); + voice.preFormat(); + }); + } + + // Figure out how many pixels to allocate per tick. + if (!justifyWidth) { + justifyWidth = 0; + this.pixelsPerTick = 0; + } else { + this.pixelsPerTick = justifyWidth / (this.totalTicks.value() * contexts.resolutionMultiplier); + } + + // Now distribute the ticks to each tick context, and assign them their + // own X positions. + var x = 0; + var white_space = 0; // White space to right of previous note + var tick_space = 0; // Pixels from prev note x-pos to curent note x-pos + var prev_tick = 0; + var prev_width = 0; + var lastMetrics = null; + var initial_justify_width = justifyWidth; + this.minTotalWidth = 0; + + var i, tick, context; + + // Pass 1: Give each note maximum width requested by context. + for (i = 0; i < contextList.length; ++i) { + tick = contextList[i]; + context = contextMap[tick]; + if (rendering_context) context.setContext(rendering_context); + + // Make sure that all tickables in this context have calculated their + // space requirements. + context.preFormat(); + + var thisMetrics = context.getMetrics(); + var width = context.getWidth(); + this.minTotalWidth += width; + var min_x = 0; + var pixels_used = width; + + // Calculate space between last note and next note. + tick_space = Math.min((tick - prev_tick) * this.pixelsPerTick, pixels_used); + + // Shift next note up `tick_space` pixels. + var set_x = x + tick_space; + + // Calculate the minimum next note position to allow for right modifiers. + if (lastMetrics != null) { + min_x = x + prev_width - lastMetrics.extraLeftPx; + } + + // Determine the space required for the previous tick. + // The `shouldIgnoreTicks` bool is true for elements in the stave + // that don't consume ticks (bar lines, key and time signatures, etc.) + set_x = context.shouldIgnoreTicks() ? + (min_x + context.getWidth()) : Math.max(set_x, min_x); + + if (context.shouldIgnoreTicks() && justifyWidth) { + // This note stole room... recalculate with new justification width. + justifyWidth -= context.getWidth(); + this.pixelsPerTick = justifyWidth / + (this.totalTicks.value() * contexts.resolutionMultiplier); + } + + // Determine pixels needed for left modifiers. + var left_px = thisMetrics.extraLeftPx; + + // Determine white space to right of previous tick (from right modifiers.) + if (lastMetrics != null) { + white_space = (set_x - x) - (prev_width - + lastMetrics.extraLeftPx); + } + + // Deduct pixels from white space quota. + if (i > 0) { + if (white_space > 0) { + if (white_space >= left_px) { + // Have enough white space for left modifiers - no offset needed. + left_px = 0; + } else { + // Decrease left modifier offset by amount of white space. + left_px -= white_space; + } + } + } + + // Adjust the tick x position with the left modifier offset. + set_x += left_px; + + // Set the `x` value for the context, which sets the `x` value for all + // tickables in this context. + context.setX(set_x); + context.setPixelsUsed(pixels_used); // ??? Remove this if nothing breaks + + lastMetrics = thisMetrics; + prev_width = width; + prev_tick = tick; + x = set_x; + } + + this.hasMinTotalWidth = true; + if (justifyWidth > 0) { + // Pass 2: Take leftover width, and distribute it to proportionately to + // all notes. + var remaining_x = initial_justify_width - (x + prev_width); + var leftover_pixels_per_tick = remaining_x / (this.totalTicks.value() * contexts.resolutionMultiplier); + var accumulated_space = 0; + prev_tick = 0; + + for (i = 0; i < contextList.length; ++i) { + tick = contextList[i]; + context = contextMap[tick]; + tick_space = (tick - prev_tick) * leftover_pixels_per_tick; + accumulated_space = accumulated_space + tick_space; + context.setX(context.getX() + accumulated_space); + prev_tick = tick; + } + } + }, + + // This is the top-level call for all formatting logic completed + // after `x` *and* `y` values have been computed for the notes + // in the voices. + postFormat: function() { + // Postformat modifier contexts + this.mContexts.list.forEach(function(mContext) { + this.mContexts.map[mContext].postFormat(); + }, this); + + // Postformat tick contexts + this.tContexts.list.forEach(function(tContext) { + this.tContexts.map[tContext].postFormat(); + }, this); + + return this; + }, + + // Take all `voices` and create `ModifierContext`s out of them. This tells + // the formatters that the voices belong on a single stave. + joinVoices: function(voices) { + this.createModifierContexts(voices); + this.hasMinTotalWidth = false; + return this; + }, + + // Align rests in voices, justify the contexts, and position the notes + // so voices are aligned and ready to render onto the stave. This method + // mutates the `x` positions of all tickables in `voices`. + // + // Voices are full justified to fit in `justifyWidth` pixels. + // + // Set `options.context` to the rendering context. Set `options.align_rests` + // to true to enable rest alignment. + format: function(voices, justifyWidth, options) { + var opts = { + align_rests: false, + context: null, + stave: null + }; + + Vex.Merge(opts, options); + this.alignRests(voices, opts.align_rests); + this.createTickContexts(voices); + this.preFormat(justifyWidth, opts.context, voices, opts.stave); + + // Only postFormat if a stave was supplied for y value formatting + if (opts.stave) this.postFormat(); + + return this; + }, + + // This method is just like `format` except that the `justifyWidth` is inferred + // from the `stave`. + formatToStave: function(voices, stave, options) { + var justifyWidth = stave.getNoteEndX() - stave.getNoteStartX() - 10; + L("Formatting voices to width: ", justifyWidth); + var opts = {context: stave.getContext()}; + Vex.Merge(opts, options); + return this.format(voices, justifyWidth, opts); + } + }; + + return Formatter; +}());// VexFlow - Music Engraving for HTML5 +// Copyright Mohit Muthanna 2010 +// +// This class implements varies types of ties between contiguous notes. The +// ties include: regular ties, hammer ons, pull offs, and slides. + +/** + * Create a new tie from the specified notes. The notes must + * be part of the same line, and have the same duration (in ticks). + * + * @constructor + * @param {!Object} context The canvas context. + * @param {!Object} notes The notes to tie up. + * @param {!Object} Options + */ +Vex.Flow.StaveTie = (function() { + function StaveTie(notes, text) { + if (arguments.length > 0) this.init(notes, text); + } + + StaveTie.prototype = { + init: function(notes, text) { + /** + * Notes is a struct that has: + * + * { + * first_note: Note, + * last_note: Note, + * first_indices: [n1, n2, n3], + * last_indices: [n1, n2, n3] + * } + * + **/ + this.notes = notes; + this.context = null; + this.text = text; + + this.render_options = { + cp1: 8, // Curve control point 1 + cp2: 12, // Curve control point 2 + text_shift_x: 0, + first_x_shift: 0, + last_x_shift: 0, + y_shift: 7, + tie_spacing: 0, + font: { family: "Arial", size: 10, style: "" } + }; + + this.font = this.render_options.font; + this.setNotes(notes); + }, + + setContext: function(context) { this.context = context; return this; }, + setFont: function(font) { this.font = font; return this; }, + + /** + * Set the notes to attach this tie to. + * + * @param {!Object} notes The notes to tie up. + */ + setNotes: function(notes) { + if (!notes.first_note && !notes.last_note) + throw new Vex.RuntimeError("BadArguments", + "Tie needs to have either first_note or last_note set."); + + if (!notes.first_indices) notes.first_indices = [0]; + if (!notes.last_indices) notes.last_indices = [0]; + + if (notes.first_indices.length != notes.last_indices.length) + throw new Vex.RuntimeError("BadArguments", "Tied notes must have similar" + + " index sizes"); + + // Success. Lets grab 'em notes. + this.first_note = notes.first_note; + this.first_indices = notes.first_indices; + this.last_note = notes.last_note; + this.last_indices = notes.last_indices; + return this; + }, + + /** + * @return {boolean} Returns true if this is a partial bar. + */ + isPartial: function() { + return (!this.first_note || !this.last_note); + }, + + renderTie: function(params) { + if (params.first_ys.length === 0 || params.last_ys.length === 0) + throw new Vex.RERR("BadArguments", "No Y-values to render"); + + var ctx = this.context; + var cp1 = this.render_options.cp1; + var cp2 = this.render_options.cp2; + + if (Math.abs(params.last_x_px - params.first_x_px) < 10) { + cp1 = 2; cp2 = 8; + } + + var first_x_shift = this.render_options.first_x_shift; + var last_x_shift = this.render_options.last_x_shift; + var y_shift = this.render_options.y_shift * params.direction; + + for (var i = 0; i < this.first_indices.length; ++i) { + var cp_x = ((params.last_x_px + last_x_shift) + + (params.first_x_px + first_x_shift)) / 2; + var first_y_px = params.first_ys[this.first_indices[i]] + y_shift; + var last_y_px = params.last_ys[this.last_indices[i]] + y_shift; + + if (isNaN(first_y_px) || isNaN(last_y_px)) + throw new Vex.RERR("BadArguments", "Bad indices for tie rendering."); + + var top_cp_y = ((first_y_px + last_y_px) / 2) + (cp1 * params.direction); + var bottom_cp_y = ((first_y_px + last_y_px) / 2) + (cp2 * params.direction); + + ctx.beginPath(); + ctx.moveTo(params.first_x_px + first_x_shift, first_y_px); + ctx.quadraticCurveTo(cp_x, top_cp_y, + params.last_x_px + last_x_shift, last_y_px); + ctx.quadraticCurveTo(cp_x, bottom_cp_y, + params.first_x_px + first_x_shift, first_y_px); + + ctx.closePath(); + ctx.fill(); + } + }, + + renderText: function(first_x_px, last_x_px) { + if (!this.text) return; + var center_x = (first_x_px + last_x_px) / 2; + center_x -= this.context.measureText(this.text).width / 2; + + this.context.save(); + this.context.setFont(this.font.family, this.font.size, this.font.style); + this.context.fillText( + this.text, center_x + this.render_options.text_shift_x, + (this.first_note || this.last_note).getStave().getYForTopText() - 1); + this.context.restore(); + }, + + draw: function() { + if (!this.context) + throw new Vex.RERR("NoContext", "No context to render tie."); + var first_note = this.first_note; + var last_note = this.last_note; + var first_x_px, last_x_px, first_ys, last_ys, stem_direction; + + if (first_note) { + first_x_px = first_note.getTieRightX() + this.render_options.tie_spacing; + stem_direction = first_note.getStemDirection(); + first_ys = first_note.getYs(); + } else { + first_x_px = last_note.getStave().getTieStartX(); + first_ys = last_note.getYs(); + this.first_indices = this.last_indices; + } + + if (last_note) { + last_x_px = last_note.getTieLeftX() + this.render_options.tie_spacing; + stem_direction = last_note.getStemDirection(); + last_ys = last_note.getYs(); + } else { + last_x_px = first_note.getStave().getTieEndX(); + last_ys = first_note.getYs(); + this.last_indices = this.first_indices; + } + + this.renderTie({ + first_x_px: first_x_px, + last_x_px: last_x_px, + first_ys: first_ys, + last_ys: last_ys, + direction: stem_direction + }); + + this.renderText(first_x_px, last_x_px); + return true; + } + }; + + return StaveTie; +}()); +// VexFlow - Music Engraving for HTML5 +// Copyright Mohit Muthanna 2010 +// +// This class implements varies types of ties between contiguous notes. The +// ties include: regular ties, hammer ons, pull offs, and slides. + +/** + * Create a new tie from the specified notes. The notes must + * be part of the same line, and have the same duration (in ticks). + * + * @constructor + * @param {!Object} context The canvas context. + * @param {!Object} notes The notes to tie up. + * @param {!Object} Options + */ +Vex.Flow.TabTie = (function() { + function TabTie(notes, text) { + if (arguments.length > 0) this.init(notes, text); + } + + TabTie.createHammeron = function(notes) { + return new TabTie(notes, "H"); + }; + + TabTie.createPulloff = function(notes) { + return new TabTie(notes, "P"); + }; + + Vex.Inherit(TabTie, Vex.Flow.StaveTie, { + init: function(notes, text) { + /** + * Notes is a struct that has: + * + * { + * first_note: Note, + * last_note: Note, + * first_indices: [n1, n2, n3], + * last_indices: [n1, n2, n3] + * } + * + **/ + TabTie.superclass.init.call(this, notes, text); + this.render_options.cp1 = 9; + this.render_options.cp2 = 11; + this.render_options.y_shift = 3; + + this.setNotes(notes); + }, + + draw: function() { + if (!this.context) + throw new Vex.RERR("NoContext", "No context to render tie."); + var first_note = this.first_note; + var last_note = this.last_note; + var first_x_px, last_x_px, first_ys, last_ys; + + if (first_note) { + first_x_px = first_note.getTieRightX() + this.render_options.tie_spacing; + first_ys = first_note.getYs(); + } else { + first_x_px = last_note.getStave().getTieStartX(); + first_ys = last_note.getYs(); + this.first_indices = this.last_indices; + } + + if (last_note) { + last_x_px = last_note.getTieLeftX() + this.render_options.tie_spacing; + last_ys = last_note.getYs(); + } else { + last_x_px = first_note.getStave().getTieEndX(); + last_ys = first_note.getYs(); + this.last_indices = this.first_indices; + } + + this.renderTie({ + first_x_px: first_x_px, + last_x_px: last_x_px, + first_ys: first_ys, + last_ys: last_ys, + direction: -1 // Tab tie's are always face up. + }); + + this.renderText(first_x_px, last_x_px); + return true; + } + }); + + return TabTie; +}()); +// VexFlow - Music Engraving for HTML5 +// Copyright Mohit Muthanna 2010 +// +// This class implements varies types of ties between contiguous notes. The +// ties include: regular ties, hammer ons, pull offs, and slides. + +/** + * Create a new tie from the specified notes. The notes must + * be part of the same line, and have the same duration (in ticks). + * + * @constructor + * @param {!Object} context The canvas context. + * @param {!Object} notes The notes to tie up. + * @param {!Object} Options + */ +Vex.Flow.TabSlide = (function() { + function TabSlide(notes, direction) { + if (arguments.length > 0) this.init(notes, direction); + } + + TabSlide.SLIDE_UP = 1; + TabSlide.SLIDE_DOWN = -1; + + TabSlide.createSlideUp = function(notes) { + return new TabSlide(notes, TabSlide.SLIDE_UP); + }; + + TabSlide.createSlideDown = function(notes) { + return new TabSlide(notes, TabSlide.SLIDE_DOWN); + }; + + Vex.Inherit(TabSlide, Vex.Flow.TabTie, { + init: function(notes, direction) { + /** + * Notes is a struct that has: + * + * { + * first_note: Note, + * last_note: Note, + * first_indices: [n1, n2, n3], + * last_indices: [n1, n2, n3] + * } + * + **/ + TabSlide.superclass.init.call(this, notes, "sl."); + if (!direction) { + var first_fret = notes.first_note.getPositions()[0].fret; + var last_fret = notes.last_note.getPositions()[0].fret; + + direction = ((parseInt(first_fret, 10) > parseInt(last_fret, 10)) ? + TabSlide.SLIDE_DOWN : TabSlide.SLIDE_UP); + } + + this.slide_direction = direction; + this.render_options.cp1 = 11; + this.render_options.cp2 = 14; + this.render_options.y_shift = 0.5; + + this.setFont({font: "Times", size: 10, style: "bold italic"}); + this.setNotes(notes); + }, + + renderTie: function(params) { + if (params.first_ys.length === 0 || params.last_ys.length === 0) + throw new Vex.RERR("BadArguments", "No Y-values to render"); + + var ctx = this.context; + var first_x_px = params.first_x_px; + var first_ys = params.first_ys; + var last_x_px = params.last_x_px; + + var direction = this.slide_direction; + if (direction != TabSlide.SLIDE_UP && + direction != TabSlide.SLIDE_DOWN) { + throw new Vex.RERR("BadSlide", "Invalid slide direction"); + } + + for (var i = 0; i < this.first_indices.length; ++i) { + var slide_y = first_ys[this.first_indices[i]] + + this.render_options.y_shift; + + if (isNaN(slide_y)) + throw new Vex.RERR("BadArguments", "Bad indices for slide rendering."); + + ctx.beginPath(); + ctx.moveTo(first_x_px, slide_y + (3 * direction)); + ctx.lineTo(last_x_px, slide_y - (3 * direction)); + ctx.closePath(); + ctx.stroke(); + } + } + }); + + return TabSlide; +}());// VexFlow - Music Engraving for HTML5 +// Copyright Mohit Muthanna 2010 +// +// This class implements bends. + +/** + @constructor + + @param text Text for bend ("Full", "Half", etc.) (DEPRECATED) + @param release If true, render a release. (DEPRECATED) + @param phrase If set, ignore "text" and "release", and use the more + sophisticated phrase specified. + + Example of a phrase: + + [{ + type: UP, + text: "whole" + width: 8; + }, + { + type: DOWN, + text: "whole" + width: 8; + }, + { + type: UP, + text: "half" + width: 8; + }, + { + type: UP, + text: "whole" + width: 8; + }, + { + type: DOWN, + text: "1 1/2" + width: 8; + }] + */ +Vex.Flow.Bend = (function() { + function Bend(text, release, phrase) { + if (arguments.length > 0) this.init(text, release, phrase); + } + + Bend.UP = 0; + Bend.DOWN = 1; + + var Modifier = Vex.Flow.Modifier; + Vex.Inherit(Bend, Modifier, { + init: function(text, release, phrase) { + var superclass = Vex.Flow.Bend.superclass; + superclass.init.call(this); + + this.text = text; + this.x_shift = 0; + this.release = release || false; + this.font = "10pt Arial"; + this.render_options = { + line_width: 1.5, + line_style: "#777777", + bend_width: 8, + release_width: 8 + }; + + if (phrase) { + this.phrase = phrase; + } else { + // Backward compatibility + this.phrase = [{type: Bend.UP, text: this.text}]; + if (this.release) this.phrase.push({type: Bend.DOWN, text: ""}); + } + + this.updateWidth(); + }, + + setXShift: function(value) { + this.x_shift = value; + this.updateWidth(); + }, + + setFont: function(font) { this.font = font; return this; }, + + getCategory: function() { return "bends"; }, + + getText: function() { return this.text; }, + + updateWidth: function() { + var that = this; + + function measure_text(text) { + var text_width; + if (that.context) { + text_width = that.context.measureText(text).width; + } else { + text_width = Vex.Flow.textWidth(text); + } + + return text_width; + } + + var total_width = 0; + for (var i=0; i 0) this.init(text); + } + + // To enable logging for this class. Set `Vex.Flow.Annotation.DEBUG` to `true`. + function L() { if (Annotation.DEBUG) Vex.L("Vex.Flow.Annotation", arguments); } + + // Text annotations can be positioned and justified relative to the note. + Annotation.Justify = { + LEFT: 1, + CENTER: 2, + RIGHT: 3, + CENTER_STEM: 4 + }; + + Annotation.VerticalJustify = { + TOP: 1, + CENTER: 2, + BOTTOM: 3, + CENTER_STEM: 4 + }; + + // ## Prototype Methods + // + // Annotations inherit from `Modifier` and is positioned correctly when + // in a `ModifierContext`. + var Modifier = Vex.Flow.Modifier; + Vex.Inherit(Annotation, Modifier, { + // Create a new `Annotation` with the string `text`. + init: function(text) { + Annotation.superclass.init.call(this); + + this.note = null; + this.index = null; + this.text_line = 0; + this.text = text; + this.justification = Annotation.Justify.CENTER; + this.vert_justification = Annotation.VerticalJustify.TOP; + this.font = { + family: "Arial", + size: 10, + weight: "" + }; + + // The default width is calculated from the text. + this.setWidth(Vex.Flow.textWidth(text)); + }, + + // Return the modifier type. Used by the `ModifierContext` to calculate + // layout. + getCategory: function() { return "annotations"; }, + + // Set the vertical position of the text relative to the stave. + setTextLine: function(line) { this.text_line = line; return this; }, + + // Set font family, size, and weight. E.g., `Arial`, `10pt`, `Bold`. + setFont: function(family, size, weight) { + this.font = { family: family, size: size, weight: weight }; + return this; + }, + + // Set vertical position of text (above or below stave). `just` must be + // a value in `Annotation.VerticalJustify`. + setVerticalJustification: function(just) { + this.vert_justification = just; + return this; + }, + + // Get and set horizontal justification. `justification` is a value in + // `Annotation.Justify`. + getJustification: function() { return this.justification; }, + setJustification: function(justification) { + this.justification = justification; return this; }, + + // Render text beside the note. + draw: function() { + if (!this.context) throw new Vex.RERR("NoContext", + "Can't draw text annotation without a context."); + if (!this.note) throw new Vex.RERR("NoNoteForAnnotation", + "Can't draw text annotation without an attached note."); + + var start = this.note.getModifierStartXY(Modifier.Position.ABOVE, + this.index); + + // We're changing context parameters. Save current state. + this.context.save(); + this.context.setFont(this.font.family, this.font.size, this.font.weight); + var text_width = this.context.measureText(this.text).width; + + // Estimate text height to be the same as the width of an 'm'. + // + // This is a hack to work around the inability to measure text height + // in HTML5 Canvas (and SVG). + var text_height = this.context.measureText("m").width; + var x, y; + + if (this.justification == Annotation.Justify.LEFT) { + x = start.x; + } else if (this.justification == Annotation.Justify.RIGHT) { + x = start.x - text_width; + } else if (this.justification == Annotation.Justify.CENTER) { + x = start.x - text_width / 2; + } else /* CENTER_STEM */ { + x = this.note.getStemX() - text_width / 2; + } + + var stem_ext, spacing; + var has_stem = this.note.hasStem(); + var stave = this.note.getStave(); + + // The position of the text varies based on whether or not the note + // has a stem. + if (has_stem) { + stem_ext = this.note.getStem().getExtents(); + spacing = stave.getSpacingBetweenLines(); + } + + if (this.vert_justification == Annotation.VerticalJustify.BOTTOM) { + y = stave.getYForBottomText(this.text_line); + if (has_stem) { + var stem_base = (this.note.getStemDirection() === 1 ? stem_ext.baseY : stem_ext.topY); + y = Math.max(y, stem_base + (spacing * (this.text_line + 2))); + } + } else if (this.vert_justification == + Annotation.VerticalJustify.CENTER) { + var yt = this.note.getYForTopText(this.text_line) - 1; + var yb = stave.getYForBottomText(this.text_line); + y = yt + ( yb - yt ) / 2 + text_height / 2; + } else if (this.vert_justification == + Annotation.VerticalJustify.TOP) { + y = Math.min(stave.getYForTopText(this.text_line), this.note.getYs()[0] - 10); + if (has_stem) { + y = Math.min(y, (stem_ext.topY - 5) - (spacing * this.text_line)); + } + } else /* CENTER_STEM */{ + var extents = this.note.getStemExtents(); + y = extents.topY + (extents.baseY - extents.topY) / 2 + + text_height / 2; + } + + L("Rendering annotation: ", this.text, x, y); + this.context.fillText(this.text, x, y); + this.context.restore(); + } + }); + + return Annotation; +}());// [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010. +// Author: Larry Kuhns. +// +// ## Description +// +// This file implements articulations and accents as modifiers that can be +// attached to notes. The complete list of articulations is available in +// `tables.js` under `Vex.Flow.articulationCodes`. +// +// See `tests/articulation_tests.js` for usage examples. + +Vex.Flow.Articulation = (function() { + function Articulation(type) { + if (arguments.length > 0) this.init(type); + } + + // To enable logging for this class. Set `Vex.Flow.Articulation.DEBUG` to `true`. + function L() { if (Articulation.DEBUG) Vex.L("Vex.Flow.Articulation", arguments); } + + var Modifier = Vex.Flow.Modifier; + + // ## Prototype Methods + Vex.Inherit(Articulation, Modifier, { + // Create a new articulation of type `type`, which is an entry in + // `Vex.Flow.articulationCodes` in `tables.js`. + init: function(type) { + Articulation.superclass.init.call(this); + + this.note = null; + this.index = null; + this.type = type; + this.position = Modifier.Position.BELOW; + + this.render_options = { + font_scale: 38 + }; + + this.articulation = Vex.Flow.articulationCodes(this.type); + if (!this.articulation) throw new Vex.RERR("ArgumentError", + "Articulation not found: '" + this.type + "'"); + + // Default width comes from articulation table. + this.setWidth(this.articulation.width); + }, + + // Get modifier category for `ModifierContext`. + getCategory: function() { return "articulations"; }, + + // Render articulation in position next to note. + draw: function() { + if (!this.context) throw new Vex.RERR("NoContext", + "Can't draw Articulation without a context."); + if (!(this.note && (this.index !== null))) throw new Vex.RERR("NoAttachedNote", + "Can't draw Articulation without a note and index."); + + var stem_direction = this.note.getStemDirection(); + var stave = this.note.getStave(); + + var is_on_head = (this.position === Modifier.Position.ABOVE && + stem_direction === Vex.Flow.StaveNote.STEM_DOWN) || + (this.position === Modifier.Position.BELOW && + stem_direction === Vex.Flow.StaveNote.STEM_UP); + + var needsLineAdjustment = function(articulation, note_line, line_spacing) { + var offset_direction = (articulation.position === Modifier.Position.ABOVE) ? 1 : -1; + var duration = articulation.getNote().getDuration(); + + if(!is_on_head && duration !== "w" && duration !== "1"){ + // Add stem length, inless it's on a whole note + note_line += offset_direction * 3.5; + } + + var articulation_line = note_line + (offset_direction * line_spacing); + + if(articulation_line >= 1 && + articulation_line <= 5 && + articulation_line % 1 === 0){ + return true; + } + + return false; + }; + + // Articulations are centered over/under the note head. + var start = this.note.getModifierStartXY(this.position, this.index); + var glyph_y = start.y; + var shiftY = 0; + var line_spacing = 1; + var spacing = stave.getSpacingBetweenLines(); + var is_tabnote = this.note.getCategory() === 'tabnotes'; + var stem_ext = this.note.getStem().getExtents(); + + var top = stem_ext.topY; + var bottom = stem_ext.baseY; + + if (stem_direction === Vex.Flow.StaveNote.STEM_DOWN) { + top = stem_ext.baseY; + bottom = stem_ext.topY; + } + + // TabNotes don't have stems attached to them. Tab stems are rendered + // outside the stave. + if (is_tabnote) { + if (this.note.hasStem()){ + if (stem_direction === Vex.Flow.StaveNote.STEM_UP) { + bottom = stave.getYForBottomText(this.text_line - 2); + } else if (stem_direction === Vex.Flow.StaveNote.STEM_DOWN ) { + top = stave.getYForTopText(this.text_line - 1.5); + } + } else { // Without a stem + top = stave.getYForTopText(this.text_line - 1); + bottom = stave.getYForBottomText(this.text_line - 2); + } + } + + var is_above = (this.position === Modifier.Position.ABOVE) ? true : false; + var note_line = this.note.getLineNumber(is_above); + + // Beamed stems are longer than quarter note stems. + if (!is_on_head && this.note.beam) line_spacing += 0.5; + + // If articulation will overlap a line, reposition it. + if (needsLineAdjustment(this, note_line, line_spacing)) line_spacing += 0.5; + + var glyph_y_between_lines; + if (this.position === Modifier.Position.ABOVE) { + shiftY = this.articulation.shift_up; + glyph_y_between_lines = (top - 7) - (spacing * (this.text_line + line_spacing)); + + if (this.articulation.between_lines) + glyph_y = glyph_y_between_lines; + else + glyph_y = Math.min(stave.getYForTopText(this.text_line) - 3, glyph_y_between_lines); + } else { + shiftY = this.articulation.shift_down - 10; + + glyph_y_between_lines = bottom + 10 + spacing * (this.text_line + line_spacing); + if (this.articulation.between_lines) + glyph_y = glyph_y_between_lines; + else + glyph_y = Math.max(stave.getYForBottomText(this.text_line), glyph_y_between_lines); + } + + var glyph_x = start.x + this.articulation.shift_right; + glyph_y += shiftY + this.y_shift; + + L("Rendering articulation: ", this.articulation, glyph_x, glyph_y); + Vex.Flow.renderGlyph(this.context, glyph_x, glyph_y, + this.render_options.font_scale, this.articulation.code); + } + }); + + return Articulation; +}());// VexFlow - Music Engraving for HTML5 +// Copyright Mohit Muthanna 2010 +// +// This class implements varies types of tunings for tablature. + +/** + * @constructor + */ +Vex.Flow.Tuning = (function() { + function Tuning(tuningString) { + this.init(tuningString); + } + + Tuning.names = { + "standard": "E/5,B/4,G/4,D/4,A/3,E/3", + "dagdad": "D/5,A/4,G/4,D/4,A/3,D/3", + "dropd": "E/5,B/4,G/4,D/4,A/3,D/3", + "eb": "Eb/5,Bb/4,Gb/4,Db/4,Ab/3,Db/3" + }; + + Tuning.prototype = { + init: function(tuningString) { + // Default to standard tuning. + this.setTuning(tuningString || "E/5,B/4,G/4,D/4,A/3,E/3,B/2,E/2"); + }, + + noteToInteger: function(noteString) { + return Vex.Flow.keyProperties(noteString).int_value; + }, + + setTuning: function(noteString) { + if (Vex.Flow.Tuning.names[noteString]) + noteString = Vex.Flow.Tuning.names[noteString]; + + this.tuningString = noteString; + this.tuningValues = []; + this.numStrings = 0; + + var keys = noteString.split(/\s*,\s*/); + if (keys.length === 0) + throw new Vex.RERR("BadArguments", "Invalid tuning string: " + noteString); + + this.numStrings = keys.length; + for (var i = 0; i < this.numStrings; ++i) { + this.tuningValues[i] = this.noteToInteger(keys[i]); + } + }, + + getValueForString: function(stringNum) { + var s = parseInt(stringNum, 10); + if (s < 1 || s > this.numStrings) + throw new Vex.RERR("BadArguments", "String number must be between 1 and " + + this.numStrings + ": " + stringNum); + + return this.tuningValues[s - 1]; + }, + + getValueForFret: function(fretNum, stringNum) { + var stringValue = this.getValueForString(stringNum); + var f = parseInt(fretNum, 10); + + if (f < 0) { + throw new Vex.RERR("BadArguments", "Fret number must be 0 or higher: " + + fretNum); + } + + return stringValue + f; + }, + + getNoteForFret: function(fretNum, stringNum) { + var noteValue = this.getValueForFret(fretNum, stringNum); + + var octave = Math.floor(noteValue / 12); + var value = noteValue % 12; + + return Vex.Flow.integerToNote(value) + "/" + octave; + } + }; + + return Tuning; +}()); +// VexFlow - Music Engraving for HTML5 +// +// A base class for stave modifiers (e.g. clefs, key signatures) +// + + +/** + * @constructor + */ +Vex.Flow.StaveModifier = (function() { + function StaveModifier() { + this.init(); + } + + StaveModifier.prototype = { + init: function() { + this.padding = 10; + }, + + getCategory: function() {return "";}, + makeSpacer: function(padding) { + return { + getContext: function() {return true;}, + setStave: function() {}, + renderToStave: function() {}, + getMetrics: function() { + return {width: padding}; + } + }; + }, + + placeGlyphOnLine: function(glyph, stave, line) { + glyph.setYShift(stave.getYForLine(line) - stave.getYForGlyphs()); + }, + + setPadding: function(padding) { + this.padding = padding; + }, + + addToStave: function(stave, firstGlyph) { + if (!firstGlyph) { + stave.addGlyph(this.makeSpacer(this.padding)); + } + + this.addModifier(stave); + return this; + }, + + addToStaveEnd: function(stave, firstGlyph) { + if (!firstGlyph) { + stave.addEndGlyph(this.makeSpacer(this.padding)); + } + else { + stave.addEndGlyph(this.makeSpacer(2)); + } + + this.addEndModifier(stave); + return this; + }, + + addModifier: function() { + throw new Vex.RERR("MethodNotImplemented", + "addModifier() not implemented for this stave modifier."); + }, + + addEndModifier: function() { + throw new Vex.RERR("MethodNotImplemented", + "addEndModifier() not implemented for this stave modifier."); + } + }; + + return StaveModifier; +}()); + +// Vex Flow Notation +// Implements key signatures +// +// Requires vex.js. + +/** + * @constructor + */ +Vex.Flow.KeySignature = (function() { + function KeySignature(keySpec) { + if (arguments.length > 0) this.init(keySpec); + } + + Vex.Inherit(KeySignature, Vex.Flow.StaveModifier, { + init: function(key_spec) { + KeySignature.superclass.init(); + + this.glyphFontScale = 38; // TODO(0xFE): Should this match StaveNote? + this.accList = Vex.Flow.keySignature(key_spec); + }, + + addAccToStave: function(stave, acc) { + var glyph = new Vex.Flow.Glyph(acc.glyphCode, this.glyphFontScale); + this.placeGlyphOnLine(glyph, stave, acc.line); + stave.addGlyph(glyph); + }, + + addModifier: function(stave) { + this.convertAccLines(stave.clef, this.accList[0].glyphCode); + for (var i = 0; i < this.accList.length; ++i) { + this.addAccToStave(stave, this.accList[i]); + } + }, + + addToStave: function(stave, firstGlyph) { + if (this.accList.length === 0) + return this; + + if (!firstGlyph) { + stave.addGlyph(this.makeSpacer(this.padding)); + } + + this.addModifier(stave); + return this; + }, + + convertAccLines: function(clef, code) { + var offset = 0.0; // if clef === "treble" + var tenorSharps; + var isTenorSharps = ((clef === "tenor") && (code === "v18")) ? true : false; + + switch (clef) { + case "bass": + offset = 1; + break; + case "alto": + offset = 0.5; + break; + case "tenor": + if (!isTenorSharps) { + offset = -0.5; + } + break; + } + + // Special-case for TenorSharps + var i; + if (isTenorSharps) { + tenorSharps = [3, 1, 2.5, 0.5, 2, 0, 1.5]; + for (i = 0; i < this.accList.length; ++i) { + this.accList[i].line = tenorSharps[i]; + } + } + else { + if (clef != "treble") { + for (i = 0; i < this.accList.length; ++i) { + this.accList[i].line += offset; + } + } + } + } + }); + + return KeySignature; +}());// Vex Flow Notation +// Implements time signatures glyphs for staffs +// See tables.js for the internal time signatures +// representation +// + +/** + * @param {string} timeSpec time signature, i.e. "4/4" + * @param {number} [customPadding] custom padding when using multi-stave/multi-instrument setting + * to align key/time signature (in pixels), optional + * @constructor + */ +Vex.Flow.TimeSignature = (function() { + function TimeSignature(timeSpec, customPadding) { + if (arguments.length > 0) this.init(timeSpec, customPadding); + } + + TimeSignature.glyphs = { + "C": { + code: "v41", + point: 40, + line: 2 + }, + "C|": { + code: "vb6", + point: 40, + line: 2 + } + }; + + Vex.Inherit(TimeSignature, Vex.Flow.StaveModifier, { + init: function(timeSpec, customPadding) { + TimeSignature.superclass.init(); + var padding = customPadding || 15; + + this.setPadding(padding); + this.point = 40; + this.topLine = 2; + this.bottomLine = 4; + this.timeSig = this.parseTimeSpec(timeSpec); + }, + + parseTimeSpec: function(timeSpec) { + if (timeSpec == "C" || timeSpec == "C|") { + var glyphInfo = TimeSignature.glyphs[timeSpec]; + return {num: false, line: glyphInfo.line, + glyph: new Vex.Flow.Glyph(glyphInfo.code, glyphInfo.point)}; + } + + var topNums = []; + var i, c; + for (i = 0; i < timeSpec.length; ++i) { + c = timeSpec.charAt(i); + if (c == "/") { + break; + } + else if (/[0-9]/.test(c)) { + topNums.push(c); + } + else { + throw new Vex.RERR("BadTimeSignature", + "Invalid time spec: " + timeSpec); + } + } + + if (i === 0) { + throw new Vex.RERR("BadTimeSignature", + "Invalid time spec: " + timeSpec); + } + + // skip the "/" + ++i; + + if (i == timeSpec.length) { + throw new Vex.RERR("BadTimeSignature", + "Invalid time spec: " + timeSpec); + } + + + var botNums = []; + for (; i < timeSpec.length; ++i) { + c = timeSpec.charAt(i); + if (/[0-9]/.test(c)) { + botNums.push(c); + } + else { + throw new Vex.RERR("BadTimeSignature", + "Invalid time spec: " + timeSpec); + } + } + + + return {num: true, glyph: this.makeTimeSignatureGlyph(topNums, botNums)}; + }, + + makeTimeSignatureGlyph: function(topNums, botNums) { + var glyph = new Vex.Flow.Glyph("v0", this.point); + glyph["topGlyphs"] = []; + glyph["botGlyphs"] = []; + + var topWidth = 0; + var i, num; + for (i = 0; i < topNums.length; ++i) { + num = topNums[i]; + var topGlyph = new Vex.Flow.Glyph("v" + num, this.point); + + glyph.topGlyphs.push(topGlyph); + topWidth += topGlyph.getMetrics().width; + } + + var botWidth = 0; + for (i = 0; i < botNums.length; ++i) { + num = botNums[i]; + var botGlyph = new Vex.Flow.Glyph("v" + num, this.point); + + glyph.botGlyphs.push(botGlyph); + botWidth += botGlyph.getMetrics().width; + } + + var width = (topWidth > botWidth ? topWidth : botWidth); + var xMin = glyph.getMetrics().x_min; + + glyph.getMetrics = function() { + return { + x_min: xMin, + x_max: xMin + width, + width: width + }; + }; + + var topStartX = (width - topWidth) / 2.0; + var botStartX = (width - botWidth) / 2.0; + + var that = this; + glyph.renderToStave = function(x) { + var start_x = x + topStartX; + var i, g; + for (i = 0; i < this.topGlyphs.length; ++i) { + g = this.topGlyphs[i]; + Vex.Flow.Glyph.renderOutline(this.context, g.metrics.outline, + g.scale, start_x + g.x_shift, this.stave.getYForLine(that.topLine) + 1); + start_x += g.getMetrics().width; + } + + start_x = x + botStartX; + for (i = 0; i < this.botGlyphs.length; ++i) { + g = this.botGlyphs[i]; + that.placeGlyphOnLine(g, this.stave, g.line); + Vex.Flow.Glyph.renderOutline(this.context, g.metrics.outline, + g.scale, start_x + g.x_shift, this.stave.getYForLine(that.bottomLine) + 1); + start_x += g.getMetrics().width; + } + }; + + return glyph; + }, + + getTimeSig: function() { + return this.timeSig; + }, + + addModifier: function(stave) { + if (!this.timeSig.num) { + this.placeGlyphOnLine(this.timeSig.glyph, stave, this.timeSig.line); + } + stave.addGlyph(this.timeSig.glyph); + }, + + addEndModifier: function(stave) { + if (!this.timeSig.num) { + this.placeGlyphOnLine(this.timeSig.glyph, stave, this.timeSig.line); + } + stave.addEndGlyph(this.timeSig.glyph); + } + }); + + return TimeSignature; +}()); +// [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna Cheppudira 2013. +// Co-author: Benjamin W. Bohl +// +// ## Description +// +// This file implements various types of clefs that can be rendered on a stave. +// +// See `tests/clef_tests.js` for usage examples. + +Vex.Flow.Clef = (function() { + function Clef(clef) { + if (arguments.length > 0) this.init(clef); + } + + // To enable logging for this class, set `Vex.Flow.Clef.DEBUG` to `true`. + function L() { if (Vex.Flow.Clef.DEBUG) Vex.L("Vex.Flow.Clef", arguments); } + + // Every clef name is associated with a glyph code from the font file, a + // point size, and a default stave line number. + Clef.types = { + "treble": { + code: "v83", + point: 40, + line: 3 + }, + "bass": { + code: "v79", + point: 40, + line: 1 + }, + "alto": { + code: "vad", + point: 40, + line: 2 + }, + "tenor": { + code: "vad", + point: 40, + line: 1 + }, + "percussion": { + code: "v59", + point: 40, + line: 2 + }, + "soprano": { + code: "vad", + point: 40, + line: 4 + }, + "mezzo-soprano": { + code: "vad", + point: 40, + line: 3 + }, + "baritone-c": { + code: "vad", + point: 40, + line: 0 + }, + "baritone-f": { + code: "v79", + point: 40, + line: 2 + }, + "subbass": { + code: "v79", + point: 40, + line: 0 + }, + "french": { + code: "v83", + point: 40, + line: 4 + }, + "treble_small": { + code: "v83", + point: 32, + line: 3 + }, + "bass_small": { + code: "v79", + point: 32, + line: 1 + }, + "alto_small": { + code: "vad", + point: 32, + line: 2 + }, + "tenor_small": { + code: "vad", + point: 32, + line: 1 + }, + "soprano_small": { + code: "vad", + point: 32, + line: 4 + }, + "mezzo-soprano_small": { + code: "vad", + point: 32, + line: 3 + }, + "baritone-c_small": { + code: "vad", + point: 32, + line: 0 + }, + "baritone-f_small": { + code: "v79", + point: 32, + line: 2 + }, + "subbass_small": { + code: "v79", + point: 32, + line: 0 + }, + "french_small": { + code: "v83", + point: 32, + line: 4 + }, + "percussion_small": { + code: "v59", + point: 32, + line: 2 + } + }; + + // ## Prototype Methods + Vex.Inherit(Clef, Vex.Flow.StaveModifier, { + // Create a new clef. The parameter `clef` must be a key from + // `Clef.types`. + init: function(clef) { + var superclass = Vex.Flow.Clef.superclass; + superclass.init.call(this); + + this.clef = Vex.Flow.Clef.types[clef]; + L("Creating clef:", clef); + }, + + // Add this clef to the start of the given `stave`. + addModifier: function(stave) { + var glyph = new Vex.Flow.Glyph(this.clef.code, this.clef.point); + this.placeGlyphOnLine(glyph, stave, this.clef.line); + stave.addGlyph(glyph); + }, + + // Add this clef to the end of the given `stave`. + addEndModifier: function(stave) { + var glyph = new Vex.Flow.Glyph(this.clef.code, this.clef.point); + this.placeGlyphOnLine(glyph, stave, this.clef.line); + stave.addEndGlyph(glyph); + } + }); + + return Clef; +}()); +// VexFlow - Music Engraving for HTML5 +// Copyright Mohit Muthanna 2010 +// +// This class implements some standard music theory routines. +// +// requires: vex.js (Vex) +// requires: flow.js (Vex.Flow) + +/** + * @constructor + */ +Vex.Flow.Music = (function() { + function Music() { + this.init(); + } + + Music.NUM_TONES = 12; + Music.roots = [ "c", "d", "e", "f", "g", "a", "b" ]; + Music.root_values = [ 0, 2, 4, 5, 7, 9, 11 ]; + Music.root_indices = { + "c": 0, + "d": 1, + "e": 2, + "f": 3, + "g": 4, + "a": 5, + "b": 6 + }; + + Music.canonical_notes = [ + "c", "c#", "d", "d#", + "e", "f", "f#", "g", + "g#", "a", "a#", "b" + ]; + + Music.diatonic_intervals = [ + "unison", "m2", "M2", "m3", "M3", + "p4", "dim5", "p5", "m6", "M6", + "b7", "M7", "octave" + ]; + + Music.diatonic_accidentals = { + "unison": {note: 0, accidental: 0}, + "m2": {note: 1, accidental: -1}, + "M2": {note: 1, accidental: 0}, + "m3": {note: 2, accidental: -1}, + "M3": {note: 2, accidental: 0}, + "p4": {note: 3, accidental: 0}, + "dim5": {note: 4, accidental: -1}, + "p5": {note: 4, accidental: 0}, + "m6": {note: 5, accidental: -1}, + "M6": {note: 5, accidental: 0}, + "b7": {note: 6, accidental: -1}, + "M7": {note: 6, accidental: 0}, + "octave": {note: 7, accidental: 0} + }; + + Music.intervals = { + "u": 0, "unison": 0, + "m2": 1, "b2": 1, "min2": 1, "S": 1, "H": 1, + "2": 2, "M2": 2, "maj2": 2, "T": 2, "W": 2, + "m3": 3, "b3": 3, "min3": 3, + "M3": 4, "3": 4, "maj3": 4, + "4": 5, "p4": 5, + "#4": 6, "b5": 6, "aug4": 6, "dim5": 6, + "5": 7, "p5": 7, + "#5": 8, "b6": 8, "aug5": 8, + "6": 9, "M6": 9, "maj6": 9, + "b7": 10, "m7": 10, "min7": 10, "dom7": 10, + "M7": 11, "maj7": 11, + "8": 12, "octave": 12 + }; + + Music.scales = { + major: [2, 2, 1, 2, 2, 2, 1], + dorian: [2, 1, 2, 2, 2, 1, 2], + mixolydian: [2, 2, 1, 2, 2, 1, 2], + minor: [2, 1, 2, 2, 1, 2, 2] + }; + + Music.accidentals = [ "bb", "b", "n", "#", "##" ]; + + Music.noteValues = { + 'c': { root_index: 0, int_val: 0 }, + 'cn': { root_index: 0, int_val: 0 }, + 'c#': { root_index: 0, int_val: 1 }, + 'c##': { root_index: 0, int_val: 2 }, + 'cb': { root_index: 0, int_val: 11 }, + 'cbb': { root_index: 0, int_val: 10 }, + 'd': { root_index: 1, int_val: 2 }, + 'dn': { root_index: 1, int_val: 2 }, + 'd#': { root_index: 1, int_val: 3 }, + 'd##': { root_index: 1, int_val: 4 }, + 'db': { root_index: 1, int_val: 1 }, + 'dbb': { root_index: 1, int_val: 0 }, + 'e': { root_index: 2, int_val: 4 }, + 'en': { root_index: 2, int_val: 4 }, + 'e#': { root_index: 2, int_val: 5 }, + 'e##': { root_index: 2, int_val: 6 }, + 'eb': { root_index: 2, int_val: 3 }, + 'ebb': { root_index: 2, int_val: 2 }, + 'f': { root_index: 3, int_val: 5 }, + 'fn': { root_index: 3, int_val: 5 }, + 'f#': { root_index: 3, int_val: 6 }, + 'f##': { root_index: 3, int_val: 7 }, + 'fb': { root_index: 3, int_val: 4 }, + 'fbb': { root_index: 3, int_val: 3 }, + 'g': { root_index: 4, int_val: 7 }, + 'gn': { root_index: 4, int_val: 7 }, + 'g#': { root_index: 4, int_val: 8 }, + 'g##': { root_index: 4, int_val: 9 }, + 'gb': { root_index: 4, int_val: 6 }, + 'gbb': { root_index: 4, int_val: 5 }, + 'a': { root_index: 5, int_val: 9 }, + 'an': { root_index: 5, int_val: 9 }, + 'a#': { root_index: 5, int_val: 10 }, + 'a##': { root_index: 5, int_val: 11 }, + 'ab': { root_index: 5, int_val: 8 }, + 'abb': { root_index: 5, int_val: 7 }, + 'b': { root_index: 6, int_val: 11 }, + 'bn': { root_index: 6, int_val: 11 }, + 'b#': { root_index: 6, int_val: 0 }, + 'b##': { root_index: 6, int_val: 1 }, + 'bb': { root_index: 6, int_val: 10 }, + 'bbb': { root_index: 6, int_val: 9 } + }; + + Music.prototype = { + init: function() {}, + + isValidNoteValue: function(note) { + if (note == null || note < 0 || note >= Vex.Flow.Music.NUM_TONES) + return false; + return true; + }, + + isValidIntervalValue: function(interval) { + return this.isValidNoteValue(interval); + }, + + getNoteParts: function(noteString) { + if (!noteString || noteString.length < 1) + throw new Vex.RERR("BadArguments", "Invalid note name: " + noteString); + + if (noteString.length > 3) + throw new Vex.RERR("BadArguments", "Invalid note name: " + noteString); + + var note = noteString.toLowerCase(); + + var regex = /^([cdefgab])(b|bb|n|#|##)?$/; + var match = regex.exec(note); + + if (match != null) { + var root = match[1]; + var accidental = match[2]; + + return { + 'root': root, + 'accidental': accidental + }; + } else { + throw new Vex.RERR("BadArguments", "Invalid note name: " + noteString); + } + }, + + getKeyParts: function(keyString) { + if (!keyString || keyString.length < 1) + throw new Vex.RERR("BadArguments", "Invalid key: " + keyString); + + var key = keyString.toLowerCase(); + + // Support Major, Minor, Melodic Minor, and Harmonic Minor key types. + var regex = /^([cdefgab])(b|#)?(mel|harm|m|M)?$/; + var match = regex.exec(key); + + if (match != null) { + var root = match[1]; + var accidental = match[2]; + var type = match[3]; + + // Unspecified type implies major + if (!type) type = "M"; + + return { + 'root': root, + 'accidental': accidental, + 'type': type + }; + } else { + throw new Vex.RERR("BadArguments", "Invalid key: " + keyString); + } + }, + + getNoteValue: function(noteString) { + var value = Music.noteValues[noteString]; + if (value == null) + throw new Vex.RERR("BadArguments", "Invalid note name: " + noteString); + + return value.int_val; + }, + + getIntervalValue: function(intervalString) { + var value = Music.intervals[intervalString]; + if (value == null) + throw new Vex.RERR("BadArguments", + "Invalid interval name: " + intervalString); + + return value; + }, + + getCanonicalNoteName: function(noteValue) { + if (!this.isValidNoteValue(noteValue)) + throw new Vex.RERR("BadArguments", + "Invalid note value: " + noteValue); + + return Music.canonical_notes[noteValue]; + }, + + getCanonicalIntervalName: function(intervalValue) { + if (!this.isValidIntervalValue(intervalValue)) + throw new Vex.RERR("BadArguments", + "Invalid interval value: " + intervalValue); + + return Music.diatonic_intervals[intervalValue]; + }, + + /* Given a note, interval, and interval direction, product the + * relative note. + */ + getRelativeNoteValue: function(noteValue, intervalValue, direction) { + if (direction == null) direction = 1; + if (direction != 1 && direction != -1) + throw new Vex.RERR("BadArguments", "Invalid direction: " + direction); + + var sum = (noteValue + (direction * intervalValue)) % Music.NUM_TONES; + if (sum < 0) sum += Music.NUM_TONES; + + return sum; + }, + + getRelativeNoteName: function(root, noteValue) { + var parts = this.getNoteParts(root); + var rootValue = this.getNoteValue(parts.root); + var interval = noteValue - rootValue; + + if (Math.abs(interval) > Music.NUM_TONES - 3) { + var multiplier = 1; + if (interval > 0 ) multiplier = -1; + + // Possibly wrap around. (Add +1 for modulo operator) + var reverse_interval = (((noteValue + 1) + (rootValue + 1)) % + Music.NUM_TONES) * multiplier; + + if (Math.abs(reverse_interval) > 2) { + throw new Vex.RERR("BadArguments", "Notes not related: " + root + ", " + + noteValue); + } else { + interval = reverse_interval; + } + } + + if (Math.abs(interval) > 2) + throw new Vex.RERR("BadArguments", "Notes not related: " + root + ", " + + noteValue); + + var relativeNoteName = parts.root; + var i; + if (interval > 0) { + for (i = 1; i <= interval; ++i) + relativeNoteName += "#"; + } else if (interval < 0) { + for (i = -1; i >= interval; --i) + relativeNoteName += "b"; + } + + return relativeNoteName; + }, + + /* Return scale tones, given intervals. Each successive interval is + * relative to the previous one, e.g., Major Scale: + * + * TTSTTTS = [2,2,1,2,2,2,1] + * + * When used with key = 0, returns C scale (which is isomorphic to + * interval list). + */ + getScaleTones: function(key, intervals) { + var tones = []; + tones.push(key); + + var nextNote = key; + for (var i = 0; i < intervals.length; ++i) { + nextNote = this.getRelativeNoteValue(nextNote, + intervals[i]); + if (nextNote != key) tones.push(nextNote); + } + + return tones; + }, + + /* Returns the interval of a note, given a diatonic scale. + * + * E.g., Given the scale C, and the note E, returns M3 + */ + getIntervalBetween: function(note1, note2, direction) { + if (direction == null) direction = 1; + if (direction != 1 && direction != -1) + throw new Vex.RERR("BadArguments", "Invalid direction: " + direction); + if (!this.isValidNoteValue(note1) || !this.isValidNoteValue(note2)) + throw new Vex.RERR("BadArguments", + "Invalid notes: " + note1 + ", " + note2); + + var difference; + if (direction == 1) + difference = note2 - note1; + else + difference = note1 - note2; + + if (difference < 0) difference += Music.NUM_TONES; + return difference; + } + }; + + return Music; +}()); +// VexFlow - Music Engraving for HTML5 +// Copyright Mohit Muthanna 2010 +// +// This class implements diatonic key management. +// +// requires: vex.js (Vex) +// requires: flow.js (Vex.Flow) +// requires: music.js (Vex.Flow.Music) + +/** + * @constructor + */ +Vex.Flow.KeyManager = (function() { + function KeyManager(key) { + this.init(key); + } + + KeyManager.scales = { + "M": Vex.Flow.Music.scales.major, + "m": Vex.Flow.Music.scales.minor + }; + + KeyManager.prototype = { + init: function(key) { + this.music = new Vex.Flow.Music(); + this.setKey(key); + }, + + setKey: function(key) { + this.key = key; + this.reset(); + return this; + }, + + getKey: function() { return this.key; }, + + reset: function() { + this.keyParts = this.music.getKeyParts(this.key); + + this.keyString = this.keyParts.root; + if (this.keyParts.accidental) this.keyString += this.keyParts.accidental; + + var is_supported_type = KeyManager.scales[this.keyParts.type]; + if (!is_supported_type) + throw new Vex.RERR("BadArguments", "Unsupported key type: " + this.key); + + this.scale = this.music.getScaleTones( + this.music.getNoteValue(this.keyString), + Vex.Flow.KeyManager.scales[this.keyParts.type]); + + this.scaleMap = {}; + this.scaleMapByValue = {}; + this.originalScaleMapByValue = {}; + + var noteLocation = Vex.Flow.Music.root_indices[this.keyParts.root]; + + for (var i = 0; i < Vex.Flow.Music.roots.length; ++i) { + var index = (noteLocation + i) % Vex.Flow.Music.roots.length; + var rootName = Vex.Flow.Music.roots[index]; + + var noteName = this.music.getRelativeNoteName(rootName, this.scale[i]); + this.scaleMap[rootName] = noteName; + this.scaleMapByValue[this.scale[i]] = noteName; + this.originalScaleMapByValue[this.scale[i]] = noteName; + } + + return this; + }, + + getAccidental: function(key) { + var root = this.music.getKeyParts(key).root; + var parts = this.music.getNoteParts(this.scaleMap[root]); + + return { + note: this.scaleMap[root], + accidental: parts.accidental + }; + }, + + selectNote: function(note) { + note = note.toLowerCase(); + var parts = this.music.getNoteParts(note); + + // First look for matching note in our altered scale + var scaleNote = this.scaleMap[parts.root]; + var modparts = this.music.getNoteParts(scaleNote); + + if (scaleNote == note) return { + "note": scaleNote, + "accidental": parts.accidental, + "change": false + }; + + // Then search for a note of equivalent value in our altered scale + var valueNote = this.scaleMapByValue[this.music.getNoteValue(note)]; + if (valueNote != null) { + return { + "note": valueNote, + "accidental": this.music.getNoteParts(valueNote).accidental, + "change": false + }; + } + + // Then search for a note of equivalent value in the original scale + var originalValueNote = this.originalScaleMapByValue[ + this.music.getNoteValue(note)]; + if (originalValueNote != null) { + this.scaleMap[modparts.root] = originalValueNote; + delete this.scaleMapByValue[this.music.getNoteValue(scaleNote)]; + this.scaleMapByValue[this.music.getNoteValue(note)] = originalValueNote; + return { + "note": originalValueNote, + "accidental": this.music.getNoteParts(originalValueNote).accidental, + "change": true + }; + } + + // Then try to unmodify a currently modified note. + if (modparts.root == note) { + delete this.scaleMapByValue[ + this.music.getNoteValue(this.scaleMap[parts.root])]; + this.scaleMapByValue[this.music.getNoteValue(modparts.root)] = + modparts.root; + this.scaleMap[modparts.root] = modparts.root; + return { + "note": modparts.root, + "accidental": null, + "change": true + }; + } + + // Last resort -- shitshoot + delete this.scaleMapByValue[ + this.music.getNoteValue(this.scaleMap[parts.root])]; + this.scaleMapByValue[this.music.getNoteValue(note)] = note; + + delete this.scaleMap[modparts.root]; + this.scaleMap[modparts.root] = note; + + return { + "note": note, + "accidental": parts.accidental, + "change": true + }; + } + }; + + return KeyManager; +}()); +// Vex Flow +// Mohit Muthanna +// +// Support for different rendering contexts: Canvas, Raphael +// +// Copyright Mohit Cheppudira 2010 + +/* global document: false */ + +Vex.Flow.Renderer = (function() { + function Renderer(sel, backend) { + if (arguments.length > 0) this.init(sel, backend); + } + + Renderer.Backends = { + CANVAS: 1, + RAPHAEL: 2, + SVG: 3, + VML: 4 + }; + + //End of line types + Renderer.LineEndType = { + NONE: 1, // No leg + UP: 2, // Upward leg + DOWN: 3 // Downward leg + }; + + // Set this to true if you're using VexFlow inside a runtime + // that does not allow modifiying canvas objects. There is a small + // performance degradation due to the extra indirection. + Renderer.USE_CANVAS_PROXY = false; + + Renderer.buildContext = function(sel, + backend, width, height, background) { + var renderer = new Renderer(sel, backend); + if (width && height) { renderer.resize(width, height); } + + if (!background) background = "#eed"; + var ctx = renderer.getContext(); + ctx.setBackgroundFillStyle(background); + return ctx; + }; + + Renderer.getCanvasContext = function(sel, width, height, background) { + return Renderer.buildContext(sel, Renderer.Backends.CANVAS, + width, height, background); + }; + + Renderer.getRaphaelContext = function(sel, width, height, background) { + return Renderer.buildContext(sel, Renderer.Backends.RAPHAEL, + width, height, background); + }; + + Renderer.bolsterCanvasContext = function(ctx) { + if (Renderer.USE_CANVAS_PROXY) { + return new Vex.Flow.CanvasContext(ctx); + } + + var methods = ["clear", "setFont", "setRawFont", "setFillStyle", "setBackgroundFillStyle", + "setStrokeStyle", "setShadowColor", "setShadowBlur", "setLineWidth"]; + ctx.vexFlowCanvasContext = ctx; + + for (var i in methods) { + var method = methods[i]; + ctx[method] = Vex.Flow.CanvasContext.prototype[method]; + } + + return ctx; + }; + + //Draw a dashed line (horizontal, vertical or diagonal + //dashPattern = [3,3] draws a 3 pixel dash followed by a three pixel space. + //setting the second number to 0 draws a solid line. + Renderer.drawDashedLine = function(context, fromX, fromY, toX, toY, dashPattern) { + context.beginPath(); + + var dx = toX - fromX; + var dy = toY - fromY; + var angle = Math.atan2(dy, dx); + var x = fromX; + var y = fromY; + context.moveTo(fromX, fromY); + var idx = 0; + var draw = true; + while (!((dx < 0 ? x <= toX : x >= toX) && (dy < 0 ? y <= toY : y >= toY))) { + var dashLength = dashPattern[idx++ % dashPattern.length]; + var nx = x + (Math.cos(angle) * dashLength); + x = dx < 0 ? Math.max(toX, nx) : Math.min(toX, nx); + var ny = y + (Math.sin(angle) * dashLength); + y = dy < 0 ? Math.max(toY, ny) : Math.min(toY, ny); + if (draw) { + context.lineTo(x, y); + } else { + context.moveTo(x, y); + } + draw = !draw; + } + + context.closePath(); + context.stroke(); + }; + + Renderer.prototype = { + init: function(sel, backend) { + // Verify selector + this.sel = sel; + if (!this.sel) throw new Vex.RERR("BadArgument", + "Invalid selector for renderer."); + + // Get element from selector + this.element = document.getElementById(sel); + if (!this.element) this.element = sel; + + // Verify backend and create context + this.ctx = null; + this.paper = null; + this.backend = backend; + if (this.backend == Renderer.Backends.CANVAS) { + // Create context. + if (!this.element.getContext) throw new Vex.RERR("BadElement", + "Can't get canvas context from element: " + sel); + this.ctx = Renderer.bolsterCanvasContext( + this.element.getContext('2d')); + } else if (this.backend == Renderer.Backends.RAPHAEL) { + this.ctx = new Vex.Flow.RaphaelContext(this.element); + } else { + throw new Vex.RERR("InvalidBackend", + "No support for backend: " + this.backend); + } + }, + + resize: function(width, height) { + if (this.backend == Renderer.Backends.CANVAS) { + if (!this.element.getContext) throw new Vex.RERR("BadElement", + "Can't get canvas context from element: " + this.sel); + this.element.width = width; + this.element.height = height; + this.ctx = Renderer.bolsterCanvasContext( + this.element.getContext('2d')); + } else { + this.ctx.resize(width, height); + } + + return this; + }, + + getContext: function() { return this.ctx; } + }; + + return Renderer; +}()); + + +// Vex Flow +// Mohit Muthanna +// +// A rendering context for the Raphael backend. +// +// Copyright Mohit Cheppudira 2010 + +/** @constructor */ +Vex.Flow.RaphaelContext = (function() { + function RaphaelContext(element) { + if (arguments.length > 0) this.init(element); + } + + RaphaelContext.prototype = { + init: function(element) { + this.element = element; + this.paper = Raphael(element); + this.path = ""; + this.pen = {x: 0, y: 0}; + this.lineWidth = 1.0; + this.state = { + scale: { x: 1, y: 1 }, + font_family: "Arial", + font_size: 8, + font_weight: 800 + }; + + this.attributes = { + "stroke-width": 0.3, + "fill": "black", + "stroke": "black", + "font": "10pt Arial" + }; + + this.background_attributes = { + "stroke-width": 0, + "fill": "white", + "stroke": "white", + "font": "10pt Arial" + }; + + this.shadow_attributes = { + width: 0, + color: "black" + }; + + this.state_stack= []; + }, + + setFont: function(family, size, weight) { + this.state.font_family = family; + this.state.font_size = size; + this.state.font_weight = weight; + this.attributes.font = (this.state.font_weight || "") + " " + + (this.state.font_size * this.state.scale.x) + "pt " + + this.state.font_family; + return this; + }, + + setRawFont: function(font) { + this.attributes.font = font; + return this; + }, + + setFillStyle: function(style) { + this.attributes.fill = style; + return this; + }, + + setBackgroundFillStyle: function(style) { + this.background_attributes.fill = style; + this.background_attributes.stroke = style; + return this; + }, + + setStrokeStyle: function(style) { + this.attributes.stroke = style; + return this; + }, + + setShadowColor: function(style) { + this.shadow_attributes.color = style; + return this; + }, + + setShadowBlur: function(blur) { + this.shadow_attributes.width = blur; + return this; + }, + + setLineWidth: function(width) { + this.attributes["stroke-width"] = width; + this.lineWidth = width; + }, + + scale: function(x, y) { + this.state.scale = { x: x, y: y }; + this.attributes.scale = x + "," + y + ",0,0"; + this.attributes.font = this.state.font_size * this.state.scale.x + "pt " + + this.state.font_family; + this.background_attributes.scale = x + "," + y + ",0,0"; + this.background_attributes.font = this.state.font_size * + this.state.scale.x + "pt " + + this.state.font_family; + return this; + }, + + clear: function() { this.paper.clear(); }, + + resize: function(width, height) { + this.element.style.width = width; + this.paper.setSize(width, height); + return this; + }, + + rect: function(x, y, width, height) { + if (height < 0) { + y += height; + height = -height; + } + + this.paper.rect(x, y, width - 0.5, height - 0.5). + attr(this.attributes). + attr("fill", "none"). + attr("stroke-width", this.lineWidth); return this; + }, + + fillRect: function(x, y, width, height) { + if (height < 0) { + y += height; + height = -height; + } + + this.paper.rect(x, y, width - 0.5, height - 0.5). + attr(this.attributes); + return this; + }, + + clearRect: function(x, y, width, height) { + if (height < 0) { + y += height; + height = -height; + } + + this.paper.rect(x, y, width - 0.5, height - 0.5). + attr(this.background_attributes); + return this; + }, + + beginPath: function() { + this.path = ""; + this.pen.x = 0; + this.pen.y = 0; + return this; + }, + + moveTo: function(x, y) { + this.path += "M" + x + "," + y; + this.pen.x = x; + this.pen.y = y; + return this; + }, + + lineTo: function(x, y) { + this.path += "L" + x + "," + y; + this.pen.x = x; + this.pen.y = y; + return this; + }, + + bezierCurveTo: function(x1, y1, x2, y2, x, y) { + this.path += "C" + + x1 + "," + + y1 + "," + + x2 + "," + + y2 + "," + + x + "," + + y; + this.pen.x = x; + this.pen.y = y; + return this; + }, + + quadraticCurveTo: function(x1, y1, x, y) { + this.path += "Q" + + x1 + "," + + y1 + "," + + x + "," + + y; + this.pen.x = x; + this.pen.y = y; + return this; + }, + + // This is an attempt (hack) to simulate the HTML5 canvas + // arc method. + arc: function(x, y, radius, startAngle, endAngle, antiClockwise) { + function normalizeAngle(angle) { + while (angle < 0) { + angle += Math.PI * 2; + } + + while (angle > Math.PI * 2) { + angle -= Math.PI * 2; + } + return angle; + } + + startAngle = normalizeAngle(startAngle); + endAngle = normalizeAngle(endAngle); + + if (startAngle > endAngle) { + var tmp = startAngle; + startAngle = endAngle; + endAngle = tmp; + antiClockwise = !antiClockwise; + } + + var delta = endAngle - startAngle; + + if (delta > Math.PI) { + this.arcHelper(x, y, radius, startAngle, startAngle + delta / 2, + antiClockwise); + this.arcHelper(x, y, radius, startAngle + delta / 2, endAngle, + antiClockwise); + } + else { + this.arcHelper(x, y, radius, startAngle, endAngle, antiClockwise); + } + return this; + }, + + arcHelper: function(x, y, radius, startAngle, endAngle, antiClockwise) { + var x1 = x + radius * Math.cos(startAngle); + var y1 = y + radius * Math.sin(startAngle); + + var x2 = x + radius * Math.cos(endAngle); + var y2 = y + radius * Math.sin(endAngle); + + var largeArcFlag = 0; + var sweepFlag = 0; + if (antiClockwise) { + sweepFlag = 1; + if (endAngle - startAngle < Math.PI) + largeArcFlag = 1; + } + else if (endAngle - startAngle > Math.PI) { + largeArcFlag = 1; + } + + this.path += "M" + x1 + "," + y1 + "," + "A" + + radius + "," + radius + "," + "0," + largeArcFlag + "," + sweepFlag + "," + + x2 + "," + y2 + "M" + this.pen.x + "," + this.pen.y; + }, + + // Adapted from the source for Raphael's Element.glow + glow: function() { + var out = this.paper.set(); + if (this.shadow_attributes.width > 0) { + var sa = this.shadow_attributes; + var num_paths = sa.width / 2; + for (var i = 1; i <= num_paths; i++) { + out.push(this.paper.path(this.path).attr({ + stroke: sa.color, + "stroke-linejoin": "round", + "stroke-linecap": "round", + "stroke-width": +(sa.width / num_paths * i).toFixed(3), + opacity: +((sa.opacity || 0.3) / num_paths).toFixed(3) + })); + } + } + return out; + }, + + fill: function() { + var elem = this.paper.path(this.path). + attr(this.attributes). + attr("stroke-width", 0); + this.glow(elem); + return this; + }, + + stroke: function() { + var elem = this.paper.path(this.path). + attr(this.attributes). + attr("fill", "none"). + attr("stroke-width", this.lineWidth); + this.glow(elem); + return this; + }, + + closePath: function() { + this.path += "Z"; + return this; + }, + + measureText: function(text) { + var txt = this.paper.text(0, 0, text). + attr(this.attributes). + attr("fill", "none"). + attr("stroke", "none"); + + return { + width: txt.getBBox().width, + height: txt.getBBox().height + }; + }, + + fillText: function(text, x, y) { + this.paper.text(x + (this.measureText(text).width / 2), + (y - (this.state.font_size / (2.25 * this.state.scale.y))), text). + attr(this.attributes); + return this; + }, + + save: function() { + // TODO(mmuthanna): State needs to be deep-copied. + this.state_stack.push({ + state: { + font_family: this.state.font_family + }, + attributes: { + font: this.attributes.font, + fill: this.attributes.fill, + stroke: this.attributes.stroke, + "stroke-width": this.attributes["stroke-width"] + }, + shadow_attributes: { + width: this.shadow_attributes.width, + color: this.shadow_attributes.color + } + }); + return this; + }, + + restore: function() { + // TODO(0xfe): State needs to be deep-restored. + var state = this.state_stack.pop(); + this.state.font_family = state.state.font_family; + this.attributes.font = state.attributes.font; + this.attributes.fill = state.attributes.fill; + this.attributes.stroke = state.attributes.stroke; + this.attributes["stroke-width"] = state.attributes["stroke-width"]; + this.shadow_attributes.width = state.shadow_attributes.width; + this.shadow_attributes.color = state.shadow_attributes.color; + return this; + } + }; + + return RaphaelContext; +}()); +// Vex Flow +// Mohit Muthanna +// +// A rendering context for the Raphael backend. +// +// Copyright Mohit Cheppudira 2010 + +/** @constructor */ +Vex.Flow.CanvasContext = (function() { + function CanvasContext(context) { + if (arguments.length > 0) this.init(context); + } + + CanvasContext.WIDTH = 600; + CanvasContext.HEIGHT = 400; + + CanvasContext.prototype = { + init: function(context) { + // Use a name that is unlikely to clash with a canvas context + // property + this.vexFlowCanvasContext = context; + if (!context.canvas) { + this.canvas = { + width: CanvasContext.WIDTH, + height: CanvasContext.HEIGHT + }; + } else { + this.canvas = this.context.canvas; + } + }, + + clear: function() { + this.vexFlowCanvasContext.clearRect(0, 0, this.canvas.width, this.canvas.height); + }, + + setFont: function(family, size, weight) { + this.vexFlowCanvasContext.font = (weight || "") + " " + size + "pt " + family; + return this; + }, + + setRawFont: function(font) { + this.vexFlowCanvasContext.font = font; + return this; + }, + + setFillStyle: function(style) { + this.vexFlowCanvasContext.fillStyle = style; + return this; + }, + + setBackgroundFillStyle: function(style) { + this.background_fillStyle = style; + return this; + }, + + setStrokeStyle: function(style) { + this.vexFlowCanvasContext.strokeStyle = style; + return this; + }, + + setShadowColor: function(style) { + this.vexFlowCanvasContext.shadowColor = style; + return this; + }, + + setShadowBlur: function(blur) { + this.vexFlowCanvasContext.shadowBlur = blur; + return this; + }, + + setLineWidth: function(width) { + this.vexFlowCanvasContext.lineWidth = width; + return this; + }, + + scale: function(x, y) { + return this.vexFlowCanvasContext.scale(parseFloat(x), parseFloat(y)); + }, + + resize: function(width, height) { + return this.vexFlowCanvasContext.resize( + parseInt(width, 10), parseInt(height, 10)); + }, + + rect: function(x, y, width, height) { + return this.vexFlowCanvasContext.rect(x, y, width, height); + }, + + fillRect: function(x, y, width, height) { + return this.vexFlowCanvasContext.fillRect(x, y, width, height); + }, + + clearRect: function(x, y, width, height) { + return this.vexFlowCanvasContext.clearRect(x, y, width, height); + }, + + beginPath: function() { + return this.vexFlowCanvasContext.beginPath(); + }, + + moveTo: function(x, y) { + return this.vexFlowCanvasContext.moveTo(x, y); + }, + + lineTo: function(x, y) { + return this.vexFlowCanvasContext.lineTo(x, y); + }, + + bezierCurveTo: function(x1, y1, x2, y2, x, y) { + return this.vexFlowCanvasContext.bezierCurveTo(x1, y1, x2, y2, x, y); + }, + + quadraticCurveTo: function(x1, y1, x, y) { + return this.vexFlowCanvasContext.quadraticCurveTo(x1, y1, x, y); + }, + + // This is an attempt (hack) to simulate the HTML5 canvas + // arc method. + arc: function(x, y, radius, startAngle, endAngle, antiClockwise) { + return this.vexFlowCanvasContext.arc(x, y, radius, startAngle, endAngle, antiClockwise); + }, + + // Adapted from the source for Raphael's Element.glow + glow: function() { + return this.vexFlowCanvasContext.glow(); + }, + + fill: function() { + return this.vexFlowCanvasContext.fill(); + }, + + stroke: function() { + return this.vexFlowCanvasContext.stroke(); + }, + + closePath: function() { + return this.vexFlowCanvasContext.closePath(); + }, + + measureText: function(text) { + return this.vexFlowCanvasContext.measureText(text); + }, + + fillText: function(text, x, y) { + return this.vexFlowCanvasContext.fillText(text, x, y); + }, + + save: function() { + return this.vexFlowCanvasContext.save(); + }, + + restore: function() { + return this.vexFlowCanvasContext.restore(); + } + }; + + return CanvasContext; +}()); +// Vex Flow Notation +// Author Larry Kuhns 2011 +// Implements barlines (single, double, repeat, end) +// +// Requires vex.js. + +/** + * @constructor + */ +Vex.Flow.Barline = (function() { + function Barline(type, x) { + if (arguments.length > 0) this.init(type, x); + } + + Barline.type = { + SINGLE: 1, + DOUBLE: 2, + END: 3, + REPEAT_BEGIN: 4, + REPEAT_END: 5, + REPEAT_BOTH: 6, + NONE: 7 + }; + + var THICKNESS = Vex.Flow.STAVE_LINE_THICKNESS; + + Vex.Inherit(Barline, Vex.Flow.StaveModifier, { + init: function(type, x) { + Barline.superclass.init.call(this); + this.barline = type; + this.x = x; // Left most x for the stave + }, + + getCategory: function() { return "barlines"; }, + setX: function(x) { this.x = x; return this; }, + + // Draw barlines + draw: function(stave, x_shift) { + x_shift = typeof x_shift !== 'number' ? 0 : x_shift; + + switch (this.barline) { + case Barline.type.SINGLE: + this.drawVerticalBar(stave, this.x, false); + break; + case Barline.type.DOUBLE: + this.drawVerticalBar(stave, this.x, true); + break; + case Barline.type.END: + this.drawVerticalEndBar(stave, this.x); + break; + case Barline.type.REPEAT_BEGIN: + // If the barline is shifted over (in front of clef/time/key) + // Draw vertical bar at the beginning. + if (x_shift > 0) { + this.drawVerticalBar(stave, this.x); + } + this.drawRepeatBar(stave, this.x + x_shift, true); + break; + case Barline.type.REPEAT_END: + this.drawRepeatBar(stave, this.x, false); + break; + case Barline.type.REPEAT_BOTH: + this.drawRepeatBar(stave, this.x, false); + this.drawRepeatBar(stave, this.x, true); + break; + default: + // Default is NONE, so nothing to draw + break; + } + }, + + drawVerticalBar: function(stave, x, double_bar) { + if (!stave.context) throw new Vex.RERR("NoCanvasContext", + "Can't draw stave without canvas context."); + var top_line = stave.getYForLine(0); + var bottom_line = stave.getYForLine(stave.options.num_lines - 1) + (THICKNESS / 2); + if (double_bar) + stave.context.fillRect(x - 3, top_line, 1, bottom_line - top_line + 1); + stave.context.fillRect(x, top_line, 1, bottom_line - top_line + 1); + }, + + drawVerticalEndBar: function(stave, x) { + if (!stave.context) throw new Vex.RERR("NoCanvasContext", + "Can't draw stave without canvas context."); + + var top_line = stave.getYForLine(0); + var bottom_line = stave.getYForLine(stave.options.num_lines - 1) + (THICKNESS / 2); + stave.context.fillRect(x - 5, top_line, 1, bottom_line - top_line + 1); + stave.context.fillRect(x - 2, top_line, 3, bottom_line - top_line + 1); + }, + + drawRepeatBar: function(stave, x, begin) { + if (!stave.context) throw new Vex.RERR("NoCanvasContext", + "Can't draw stave without canvas context."); + + var top_line = stave.getYForLine(0); + var bottom_line = stave.getYForLine(stave.options.num_lines - 1) + (THICKNESS / 2); + var x_shift = 3; + + if (!begin) { + x_shift = -5; + } + + stave.context.fillRect(x + x_shift, top_line, 1, bottom_line - top_line + 1); + stave.context.fillRect(x - 2, top_line, 3, bottom_line - top_line + 1); + + var dot_radius = 2; + + // Shift dots left or right + if (begin) { + x_shift += 4; + } else { + x_shift -= 4; + } + + var dot_x = (x + x_shift) + (dot_radius / 2); + + // calculate the y offset based on number of stave lines + var y_offset = (stave.options.num_lines -1) * + stave.options.spacing_between_lines_px; + y_offset = (y_offset / 2) - + (stave.options.spacing_between_lines_px / 2); + var dot_y = top_line + y_offset + (dot_radius / 2); + + // draw the top repeat dot + stave.context.beginPath(); + stave.context.arc(dot_x, dot_y, dot_radius, 0, Math.PI * 2, false); + stave.context.fill(); + + //draw the bottom repeat dot + dot_y += stave.options.spacing_between_lines_px; + stave.context.beginPath(); + stave.context.arc(dot_x, dot_y, dot_radius, 0, Math.PI * 2, false); + stave.context.fill(); + } + }); + + return Barline; +}()); +// VexFlow - Music Engraving for HTML5 +// Copyright Mohit Muthanna 2010 +// This class by Raffaele Viglianti, 2012 http://itisnotsound.wordpress.com/ +// +// This class implements hairpins between notes. +// Hairpins can be either Crescendo or Descrescendo. + +/** + * Create a new hairpin from the specified notes. + * + * @constructor + * @param {!Object} notes The notes to tie up. + * @param {!Object} type The type of hairpin + */ +Vex.Flow.StaveHairpin = (function() { + function StaveHairpin(notes, type) { + if (arguments.length > 0) this.init(notes, type); + } + + StaveHairpin.type = { + CRESC: 1, + DECRESC: 2 + }; + + /* Helper function to convert ticks into pixels. + * Requires a Formatter with voices joined and formatted (to + * get pixels per tick) + * + * options is struct that has: + * + * { + * height: px, + * y_shift: px, //vertical offset + * left_shift_ticks: 0, //left horizontal offset expressed in ticks + * right_shift_ticks: 0 // right horizontal offset expressed in ticks + * } + * + **/ + StaveHairpin.FormatByTicksAndDraw = function(ctx, formatter, notes, type, position, options) { + var ppt = formatter.pixelsPerTick; + + if (ppt == null){ + throw new Vex.RuntimeError("BadArguments", + "A valid Formatter must be provide to draw offsets by ticks.");} + + var l_shift_px = ppt * options.left_shift_ticks; + var r_shift_px = ppt * options.right_shift_ticks; + + var hairpin_options = { + height: options.height, + y_shift:options.y_shift, + left_shift_px:l_shift_px, + right_shift_px:r_shift_px}; + + new StaveHairpin({ + first_note: notes.first_note, + last_note: notes.last_note + }, type) + .setContext(ctx) + .setRenderOptions(hairpin_options) + .setPosition(position) + .draw(); + }; + + StaveHairpin.prototype = { + init: function(notes, type) { + /** + * Notes is a struct that has: + * + * { + * first_note: Note, + * last_note: Note, + * } + * + **/ + + this.notes = notes; + this.hairpin = type; + this.position = Vex.Flow.Modifier.Position.BELOW; + + this.context = null; + + this.render_options = { + height: 10, + y_shift: 0, //vertical offset + left_shift_px: 0, //left horizontal offset + right_shift_px: 0 // right horizontal offset + }; + + this.setNotes(notes); + }, + + setContext: function(context) { this.context = context; return this; }, + + setPosition: function(position) { + if (position == Vex.Flow.Modifier.Position.ABOVE || + position == Vex.Flow.Modifier.Position.BELOW) + this.position = position; + return this; + }, + + setRenderOptions: function(options) { + if (options.height != null && + options.y_shift != null && + options.left_shift_px != null && + options.right_shift_px != null){ + this.render_options = options; + } + return this; + }, + + /** + * Set the notes to attach this hairpin to. + * + * @param {!Object} notes The start and end notes. + */ + setNotes: function(notes) { + if (!notes.first_note && !notes.last_note) + throw new Vex.RuntimeError("BadArguments", + "Hairpin needs to have either first_note or last_note set."); + + // Success. Lets grab 'em notes. + this.first_note = notes.first_note; + this.last_note = notes.last_note; + return this; + }, + + renderHairpin: function(params) { + var ctx = this.context; + var dis = this.render_options.y_shift + 20; + var y_shift = params.first_y; + + if (this.position == Vex.Flow.Modifier.Position.ABOVE) { + dis = -dis +30; + y_shift = params.first_y - params.staff_height; + } + + var l_shift = this.render_options.left_shift_px; + var r_shift = this.render_options.right_shift_px; + + switch (this.hairpin) { + case StaveHairpin.type.CRESC: + ctx.moveTo(params.last_x + r_shift, y_shift + dis); + ctx.lineTo(params.first_x + l_shift, y_shift +(this.render_options.height/2) + dis); + ctx.lineTo(params.last_x + r_shift, y_shift + this.render_options.height + dis); + break; + case StaveHairpin.type.DECRESC: + ctx.moveTo(params.first_x + l_shift, y_shift + dis); + ctx.lineTo(params.last_x + r_shift, y_shift +(this.render_options.height/2) + dis); + ctx.lineTo(params.first_x + l_shift, y_shift + this.render_options.height + dis); + break; + default: + // Default is NONE, so nothing to draw + break; + } + + ctx.stroke(); + }, + + draw: function() { + if (!this.context) throw new Vex.RERR("NoContext", + "Can't draw Hairpin without a context."); + + var first_note = this.first_note; + var last_note = this.last_note; + + var start = first_note.getModifierStartXY(this.position, 0); + var end = last_note.getModifierStartXY(this.position, 0); + + this.renderHairpin({ + first_x: start.x, + last_x: end.x, + first_y: first_note.getStave().y + first_note.getStave().height, + last_y: last_note.getStave().y + last_note.getStave().height, + staff_height: first_note.getStave().height + }); + return true; + } + }; + return StaveHairpin; +}()); + +// Vex Flow Notation +// Author Larry Kuhns 2011 +// Implements voltas (repeat brackets) +// +// Requires vex.js. + +Vex.Flow.Volta = (function() { + function Volta(type, number, x, y_shift) { + if (arguments.length > 0) this.init(type, number, x, y_shift); + } + + Volta.type = { + NONE: 1, + BEGIN: 2, + MID: 3, + END: 4, + BEGIN_END: 5 + }; + + Vex.Inherit(Volta, Vex.Flow.StaveModifier, { + init: function(type, number, x, y_shift) { + Volta.superclass.init.call(this); + + this.volta = type; + this.x = x; + this.y_shift = y_shift; + this.number = number; + this.font = { + family: "sans-serif", + size: 9, + weight: "bold" + }; + }, + + getCategory: function() { return "voltas"; }, + setShiftY: function(y) { this.y_shift = y; return this; }, + + draw: function(stave, x) { + if (!stave.context) throw new Vex.RERR("NoCanvasContext", + "Can't draw stave without canvas context."); + var ctx = stave.context; + var width = stave.width; + var top_y = stave.getYForTopText(stave.options.num_lines) + this.y_shift; + var vert_height = 1.5 * stave.options.spacing_between_lines_px; + switch(this.volta) { + case Vex.Flow.Volta.type.BEGIN: + ctx.fillRect(this.x + x, top_y, 1, vert_height); + break; + case Vex.Flow.Volta.type.END: + width -= 5; + ctx.fillRect(this.x + x + width, top_y, 1, vert_height); + break; + case Vex.Flow.Volta.type.BEGIN_END: + width -= 3; + ctx.fillRect(this.x + x, top_y, 1, vert_height); + ctx.fillRect(this.x + x + width, top_y, 1, vert_height); + break; + } + // If the beginning of a volta, draw measure number + if (this.volta == Volta.type.BEGIN || + this.volta == Volta.type.BEGIN_END) { + ctx.save(); + ctx.setFont(this.font.family, this.font.size, this.font.weight); + ctx.fillText(this.number, this.x + x + 5, top_y + 15); + ctx.restore(); + } + ctx.fillRect(this.x + x, top_y, width, 1); + return this; + } + }); + + return Volta; +}());// Vex Flow Notation +// Author Larry Kuhns 2011 +// Implements Repetitions (Coda, signo, D.C., etc.) +// +// Requires vex.js. + +Vex.Flow.Repetition = (function() { + function Repetition(type, x, y_shift) { + if (arguments.length > 0) this.init(type, x, y_shift); + } + + Repetition.type = { + NONE: 1, // no coda or segno + CODA_LEFT: 2, // coda at beginning of stave + CODA_RIGHT: 3, // coda at end of stave + SEGNO_LEFT: 4, // segno at beginning of stave + SEGNO_RIGHT: 5, // segno at end of stave + DC: 6, // D.C. at end of stave + DC_AL_CODA: 7, // D.C. al coda at end of stave + DC_AL_FINE: 8, // D.C. al Fine end of stave + DS: 9, // D.S. at end of stave + DS_AL_CODA: 10, // D.S. al coda at end of stave + DS_AL_FINE: 11, // D.S. al Fine at end of stave + FINE: 12 // Fine at end of stave + }; + + Vex.Inherit(Repetition, Vex.Flow.StaveModifier, { + init: function(type, x, y_shift) { + Repetition.superclass.init.call(this); + + this.symbol_type = type; + this.x = x; + this.x_shift = 0; + this.y_shift = y_shift; + this.font = { + family: "times", + size: 12, + weight: "bold italic" + }; + }, + + getCategory: function() { return "repetitions"; }, + setShiftX: function(x) { this.x_shift = x; return this; }, + setShiftY: function(y) { this.y_shift = y; return this; }, + + draw: function(stave, x) { + switch (this.symbol_type) { + case Repetition.type.CODA_RIGHT: + this.drawCodaFixed(stave, x + stave.width); + break; + case Repetition.type.CODA_LEFT: + this.drawSymbolText(stave, x, "Coda", true); + break; + case Repetition.type.SEGNO_LEFT: + this.drawSignoFixed(stave, x); + break; + case Repetition.type.SEGNO_RIGHT: + this.drawSignoFixed(stave, x + stave.width); + break; + case Repetition.type.DC: + this.drawSymbolText(stave, x, "D.C.", false); + break; + case Repetition.type.DC_AL_CODA: + this.drawSymbolText(stave, x, "D.C. al", true); + break; + case Repetition.type.DC_AL_FINE: + this.drawSymbolText(stave, x, "D.C. al Fine", false); + break; + case Repetition.type.DS: + this.drawSymbolText(stave, x, "D.S.", false); + break; + case Repetition.type.DS_AL_CODA: + this.drawSymbolText(stave, x, "D.S. al", true); + break; + case Repetition.type.DS_AL_FINE: + this.drawSymbolText(stave, x, "D.S. al Fine", false); + break; + case Repetition.type.FINE: + this.drawSymbolText(stave, x, "Fine", false); + break; + default: + break; + } + + return this; + }, + + drawCodaFixed: function(stave, x) { + if (!stave.context) throw new Vex.RERR("NoCanvasContext", + "Can't draw stave without canvas context."); + + var y = stave.getYForTopText(stave.options.num_lines) + this.y_shift; + Vex.Flow.renderGlyph(stave.context, this.x + x + this.x_shift, + y + 25, 40, "v4d", true); + return this; + }, + + drawSignoFixed: function(stave, x) { + if (!stave.context) throw new Vex.RERR("NoCanvasContext", + "Can't draw stave without canvas context."); + var y = stave.getYForTopText(stave.options.num_lines) + this.y_shift; + Vex.Flow.renderGlyph(stave.context, this.x + x + this.x_shift, + y + 25, 30, "v8c", true); + return this; + }, + + drawSymbolText: function(stave, x, text, draw_coda) { + if (!stave.context) throw new Vex.RERR("NoCanvasContext", + "Can't draw stave without canvas context."); + + var ctx = stave.context; + ctx.save(); + ctx.setFont(this.font.family, this.font.size, this.font.weight); + // Default to right symbol + var text_x = 0 + this.x_shift; + var symbol_x = x + this.x_shift; + if (this.symbol_type == Vex.Flow.Repetition.type.CODA_LEFT) { + // Offset Coda text to right of stave beginning + text_x = this.x + stave.options.vertical_bar_width; + symbol_x = text_x + ctx.measureText(text).width + 12; + } else { + // Offset Signo text to left stave end + symbol_x = this.x + x + stave.width - 5 + this.x_shift; + text_x = symbol_x - + ctx.measureText(text).width - 12; + } + var y = stave.getYForTopText(stave.options.num_lines) + this.y_shift; + if (draw_coda) { + Vex.Flow.renderGlyph(ctx, symbol_x, y, 40, "v4d", true); + } + + ctx.fillText(text, text_x, y + 5); + ctx.restore(); + + return this; + } + }); + + return Repetition; +}());// VexFlow - Music Engraving for HTML5 +// Copyright Mohit Muthanna 2010 +// Author Larry Kuhns 2011 +// Implements stave section names. + +/** + * @constructor + */ +Vex.Flow.StaveSection = (function() { + function StaveSection(section, x, shift_y) { + if (arguments.length > 0) this.init(section, x, shift_y); + } + + var Modifier = Vex.Flow.Modifier; + Vex.Inherit(StaveSection, Modifier, { + init: function(section, x, shift_y) { + StaveSection.superclass.init.call(this); + + this.setWidth(16); + this.section = section; + this.position = Modifier.Position.ABOVE; + this.x = x; + this.shift_x = 0; + this.shift_y = shift_y; + this.font = { + family: "sans-serif", + size: 12, + weight: "bold" + }; + }, + + getCategory: function() { return "stavesection"; }, + setStaveSection: function(section) { this.section = section; return this; }, + setShiftX: function(x) { this.shift_x = x; return this; }, + setShiftY: function(y) { this.shift_y = y; return this; }, + + draw: function(stave, shift_x) { + if (!stave.context) throw new Vex.RERR("NoContext", + "Can't draw stave section without a context."); + + var ctx = stave.context; + + ctx.save(); + ctx.lineWidth = 2; + ctx.setFont(this.font.family, this.font.size, this.font.weight); + var text_width = ctx.measureText("" + this.section).width; + var width = text_width + 6; // add left & right padding + if (width < 18) width = 18; + var height = 20; + // Seems to be a good default y + var y = stave.getYForTopText(3) + this.shift_y; + var x = this.x + shift_x; + ctx.beginPath(); + ctx.lineWidth = 2; + ctx.rect(x, y, width, height); + ctx.stroke(); + x += (width - text_width) / 2; + ctx.fillText("" + this.section, x, y + 16); + ctx.restore(); + return this; + } + }); + + return StaveSection; +}());// VexFlow - Music Engraving for HTML5 +// Copyright Mohit Muthanna 2010 +// Author Radosaw Eichler 2012 +// Implements tempo marker. + +/** + * @constructor + * @param {Object} tempo Tempo parameters: { name, duration, dots, bpm } + */ +Vex.Flow.StaveTempo = (function() { + function StaveTempo(tempo, x, shift_y) { + if (arguments.length > 0) this.init(tempo, x, shift_y); + } + + Vex.Inherit(StaveTempo, Vex.Flow.StaveModifier, { + init: function(tempo, x, shift_y) { + StaveTempo.superclass.init.call(this); + + this.tempo = tempo; + this.position = Vex.Flow.Modifier.Position.ABOVE; + this.x = x; + this.shift_x = 10; + this.shift_y = shift_y; + this.font = { + family: "times", + size: 14, + weight: "bold" + }; + this.render_options = { + glyph_font_scale: 30 // font size for note + }; + }, + + getCategory: function() { return "stavetempo"; }, + setTempo: function(tempo) { this.tempo = tempo; return this; }, + setShiftX: function(x) { this.shift_x = x; return this; }, + setShiftY: function(y) { this.shift_y = y; return this; }, + + draw: function(stave, shift_x) { + if (!stave.context) throw new Vex.RERR("NoContext", + "Can't draw stave tempo without a context."); + + var options = this.render_options; + var scale = options.glyph_font_scale / 38; + var name = this.tempo.name; + var duration = this.tempo.duration; + var dots = this.tempo.dots; + var bpm = this.tempo.bpm; + var font = this.font; + var ctx = stave.context; + var x = this.x + this.shift_x + shift_x; + var y = stave.getYForTopText(1) + this.shift_y; + + ctx.save(); + + if (name) { + ctx.setFont(font.family, font.size, font.weight); + ctx.fillText(name, x, y); + x += ctx.measureText(name).width; + } + + if (duration && bpm) { + ctx.setFont(font.family, font.size, 'normal'); + + if (name) { + x += ctx.measureText(" ").width; + ctx.fillText("(", x, y); + x += ctx.measureText("(").width; + } + + var code = Vex.Flow.durationToGlyph(duration); + + x += 3 * scale; + Vex.Flow.renderGlyph(ctx, x, y, options.glyph_font_scale, code.code_head); + x += code.head_width * scale; + + // Draw stem and flags + if (code.stem) { + var stem_height = 30; + + if (code.beam_count) stem_height += 3 * (code.beam_count - 1); + + stem_height *= scale; + + var y_top = y - stem_height; + ctx.fillRect(x, y_top, scale, stem_height); + + if (code.flag) { + Vex.Flow.renderGlyph(ctx, x + scale, y_top, options.glyph_font_scale, + code.code_flag_upstem); + + if (!dots) x += 6 * scale; + } + } + + // Draw dot + for (var i = 0; i < dots; i++) { + x += 6 * scale; + ctx.beginPath(); + ctx.arc(x, y + 2 * scale, 2 * scale, 0, Math.PI * 2, false); + ctx.fill(); + } + + ctx.fillText(" = " + bpm + (name ? ")" : ""), x + 3 * scale, y); + } + + ctx.restore(); + return this; + } + }); + + return StaveTempo; +}()); +// VexFlow - Music Engraving for HTML5 +// Copyright Mohit Muthanna 2010 +// +// Author Taehoon Moon 2014 + +/** + * @constructor + */ +Vex.Flow.StaveText = (function() { + function StaveText(text, position, options) { + if (arguments.length > 0) this.init(text, position, options); + } + + var Modifier = Vex.Flow.Modifier; + Vex.Inherit(StaveText, Modifier, { + init: function(text, position, options) { + StaveText.superclass.init.call(this); + + this.setWidth(16); + this.text = text; + this.position = position; + this.options = { + shift_x: 0, + shift_y: 0, + justification: Vex.Flow.TextNote.Justification.CENTER + }; + Vex.Merge(this.options, options); + + this.font = { + family: "times", + size: 16, + weight: "normal" + }; + }, + + getCategory: function() { return "stavetext"; }, + setStaveText: function(text) { this.text = text; return this; }, + setShiftX: function(x) { this.shift_x = x; return this; }, + setShiftY: function(y) { this.shift_y = y; return this; }, + + setFont: function(font) { + Vex.Merge(this.font, font); + }, + + setText: function(text) { + this.text = text; + }, + + draw: function(stave) { + if (!stave.context) throw new Vex.RERR("NoContext", + "Can't draw stave text without a context."); + + var ctx = stave.context; + + ctx.save(); + ctx.lineWidth = 2; + ctx.setFont(this.font.family, this.font.size, this.font.weight); + var text_width = ctx.measureText("" + this.text).width; + + var x, y; + var Modifier = Vex.Flow.Modifier; + switch(this.position) { + case Modifier.Position.LEFT: + case Modifier.Position.RIGHT: + y = (stave.getYForLine(0) + stave.getBottomLineY()) / 2 + this.options.shift_y; + if(this.position == Modifier.Position.LEFT) { + x = stave.getX() - text_width - 24 + this.options.shift_x; + } + else { + x = stave.getX() + stave.getWidth() + 24 + this.options.shift_x; + } + break; + case Modifier.Position.ABOVE: + case Modifier.Position.BELOW: + var Justification = Vex.Flow.TextNote.Justification; + x = stave.getX() + this.options.shift_x; + if(this.options.justification == Justification.CENTER) { + x += stave.getWidth() / 2 - text_width / 2; + } + else if(this.options.justification == Justification.RIGHT) { + x += stave.getWidth() - text_width; + } + + if(this.position == Modifier.Position.ABOVE) { + y = stave.getYForTopText(2) + this.options.shift_y; + } + else { + y = stave.getYForBottomText(2) + this.options.shift_y; + } + break; + default: + throw new Vex.RERR("InvalidPosition", + "Value Must be in Modifier.Position."); + } + + ctx.fillText("" + this.text, x, y + 4); + ctx.restore(); + return this; + } + }); + + return StaveText; +}()); +// [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010. +// +// ## Description +// +// A `BarNote` is used to render bar lines (from `barline.js`). `BarNote`s can +// be added to a voice and rendered in the middle of a stave. Since it has no +// duration, it consumes no `tick`s, and is dealt with appropriately by the formatter. +// +// See `tests/barnote_tests.js` for usage examples. + +Vex.Flow.BarNote = (function() { + function BarNote() { this.init(); } + + // To enable logging for this class. Set `Vex.Flow.BarNote.DEBUG` to `true`. + function L() { if (BarNote.DEBUG) Vex.L("Vex.Flow.BarNote", arguments); } + + // ## Prototype Methods + Vex.Inherit(BarNote, Vex.Flow.Note, { + init: function() { + BarNote.superclass.init.call(this, {duration: "b"}); + + var TYPE = Vex.Flow.Barline.type; + this.metrics = { + widths: {} + }; + + // Defined this way to prevent lint errors. + this.metrics.widths[TYPE.SINGLE] = 8; + this.metrics.widths[TYPE.DOUBLE] = 12; + this.metrics.widths[TYPE.END] = 15; + this.metrics.widths[TYPE.REPEAT_BEGIN] = 14; + this.metrics.widths[TYPE.REPEAT_END] = 14; + this.metrics.widths[TYPE.REPEAT_BOTH] = 18; + this.metrics.widths[TYPE.NONE] = 0; + + // Tell the formatter that bar notes have no duration. + this.ignore_ticks = true; + this.type = TYPE.SINGLE; + + // Set width to width of relevant `Barline`. + this.setWidth(this.metrics.widths[this.type]); + }, + + // Get and set the type of Bar note. `type` must be one of `Vex.Flow.Barline.type`. + getType: function() { return this.type; }, + setType: function(type) { + this.type = type; + this.setWidth(this.metrics.widths[this.type]); + return this; + }, + + getBoundingBox: function() { + return new Vex.Flow.BoundingBox(0, 0, 0, 0); + }, + + addToModifierContext: function() { + /* overridden to ignore */ + return this; + }, + + preFormat: function() { + /* overridden to ignore */ + this.setPreFormatted(true); + return this; + }, + + // Render note to stave. + draw: function() { + if (!this.stave) throw new Vex.RERR("NoStave", "Can't draw without a stave."); + L("Rendering bar line at: ", this.getAbsoluteX()); + var barline = new Vex.Flow.Barline(this.type, this.getAbsoluteX()); + barline.draw(this.stave); + } + }); + + return BarNote; +}()); +// VexFlow - Music Engraving for HTML5 +// Author: Mike Corrigan +// +// This class implements tremolo notation. + +/** + * @constructor + */ +Vex.Flow.Tremolo = (function() { + function Tremolo(num) { + if (arguments.length > 0) this.init(num); + } + + var Modifier = Vex.Flow.Modifier; + Vex.Inherit(Tremolo, Modifier, { + init: function(num) { + Tremolo.superclass.init.call(this); + + this.num = num; + this.note = null; + this.index = null; + this.position = Modifier.Position.CENTER; + this.code = "v74"; + this.shift_right = -2; + this.y_spacing = 4; + + this.render_options = { + font_scale: 35, + stroke_px: 3, + stroke_spacing: 10 + }; + + this.font = { + family: "Arial", + size: 16, + weight: "" + }; + }, + + getCategory: function() { return "tremolo"; }, + + draw: function() { + if (!this.context) throw new Vex.RERR("NoContext", + "Can't draw Tremolo without a context."); + if (!(this.note && (this.index != null))) throw new Vex.RERR("NoAttachedNote", + "Can't draw Tremolo without a note and index."); + + var start = this.note.getModifierStartXY(this.position, this.index); + var x = start.x; + var y = start.y; + + x += this.shift_right; + for (var i = 0; i < this.num; ++i) { + Vex.Flow.renderGlyph(this.context, x, y, + this.render_options.font_scale, this.code); + y += this.y_spacing; + } + } + }); + + return Tremolo; +}()); +/** + * Create a new tuplet from the specified notes. The notes must + * be part of the same line, and have the same duration (in ticks). + * + * @constructor + * @param {Array.} A set of notes. + */ +Vex.Flow.Tuplet = (function() { + function Tuplet(notes, options) { + if (arguments.length > 0) this.init(notes, options); + } + + Tuplet.LOCATION_TOP = 1; + Tuplet.LOCATION_BOTTOM = -1; + + Tuplet.prototype = { + init: function(notes, options) { + if (!notes || notes == []) { + throw new Vex.RuntimeError("BadArguments", "No notes provided for tuplet."); + } + + if (notes.length == 1) { + throw new Vex.RuntimeError("BadArguments", "Too few notes for tuplet."); + } + + this.options = Vex.Merge({}, options); + this.notes = notes; + this.num_notes = 'num_notes' in this.options ? + this.options.num_notes : notes.length; + this.beats_occupied = 'beats_occupied' in this.options ? + this.options.beats_occupied : 2; + this.bracketed = (notes[0].beam == null); + this.ratioed = false; + this.point = 28; + this.y_pos = 16; + this.x_pos = 100; + this.width = 200; + this.location = Tuplet.LOCATION_TOP; + + Vex.Flow.Formatter.AlignRestsToNotes(notes, true, true); + this.resolveGlyphs(); + this.attach(); + }, + + attach: function () { + for (var i = 0; i < this.notes.length; i++) { + var note = this.notes[i]; + note.setTuplet(this); + } + }, + + detach: function () { + for (var i = 0; i < this.notes.length; i++) { + var note = this.notes[i]; + note.setTuplet(null); + } + }, + + setContext: function(context) { + this.context = context; + return this; + }, + + /** + * Set whether or not the bracket is drawn. + */ + setBracketed: function(bracketed) { + this.bracketed = bracketed ? true : false; + return this; + }, + + /** + * Set whether or not the ratio is shown. + */ + setRatioed: function(ratioed) { + this.ratioed = ratioed ? true : false; + return this; + }, + + /** + * Set the tuplet to be displayed either on the top or bottom of the stave + */ + setTupletLocation: function(location) { + if (!location) location = Tuplet.LOCATION_TOP; + else if (location != Tuplet.LOCATION_TOP && + location != Tuplet.LOCATION_BOTTOM) { + throw new Vex.RERR("BadArgument", "Invalid tuplet location: " + location); + } + + this.location = location; + return this; + }, + + getNotes: function() { + return this.notes; + }, + + getNoteCount: function() { + return this.num_notes; + }, + + getBeatsOccupied: function() { + return this.beats_occupied; + }, + + setBeatsOccupied: function(beats) { + this.detach(); + this.beats_occupied = beats; + this.resolveGlyphs(); + this.attach(); + }, + + resolveGlyphs: function() { + this.num_glyphs = []; + var n = this.num_notes; + while (n >= 1) { + this.num_glyphs.push(new Vex.Flow.Glyph("v" + (n % 10), this.point)); + n = parseInt(n / 10, 10); + } + + this.denom_glyphs = []; + n = this.beats_occupied; + while (n >= 1) { + this.denom_glyphs.push(new Vex.Flow.Glyph("v" + (n % 10), this.point)); + n = parseInt(n / 10, 10); + } + }, + + draw: function() { + if (!this.context) throw new Vex.RERR("NoCanvasContext", + "Can't draw without a canvas context."); + + // determine x value of left bound of tuplet + var first_note = this.notes[0]; + var last_note = this.notes[this.notes.length - 1]; + + if (!this.bracketed) { + this.x_pos = first_note.getStemX(); + this.width = last_note.getStemX() - this.x_pos; + } + else { + this.x_pos = first_note.getTieLeftX() - 5; + this.width = last_note.getTieRightX() - this.x_pos + 5; + } + + // determine y value for tuplet + var i; + if (this.location == Tuplet.LOCATION_TOP) { + this.y_pos = first_note.getStave().getYForLine(0) - 15; + //this.y_pos = first_note.getStemExtents().topY - 10; + + for (i=0; i this.y_pos) + this.y_pos = bottom_y; + } + } + + // calculate total width of tuplet notation + var width = 0; + var glyph; + for (glyph in this.num_glyphs) { + width += this.num_glyphs[glyph].getMetrics().width; + } + if (this.ratioed) { + for (glyph in this.denom_glyphs) { + width += this.denom_glyphs[glyph].getMetrics().width; + } + width += this.point * 0.32; + } + + var notation_center_x = this.x_pos + (this.width/2); + var notation_start_x = notation_center_x - (width/2); + + // draw bracket if the tuplet is not beamed + if (this.bracketed) { + var line_width = this.width/2 - width/2 - 5; + + // only draw the bracket if it has positive length + if (line_width > 0) { + this.context.fillRect(this.x_pos, this.y_pos,line_width, 1); + this.context.fillRect(this.x_pos + this.width / 2 + width / 2 + 5, + this.y_pos,line_width, 1); + this.context.fillRect(this.x_pos, + this.y_pos + (this.location == Tuplet.LOCATION_BOTTOM), + 1, this.location * 10); + this.context.fillRect(this.x_pos + this.width, + this.y_pos + (this.location == Tuplet.LOCATION_BOTTOM), + 1, this.location * 10); + } + } + + // draw numerator glyphs + var x_offset = 0; + var size = this.num_glyphs.length; + for (glyph in this.num_glyphs) { + this.num_glyphs[size-glyph-1].render( + this.context, notation_start_x + x_offset, + this.y_pos + (this.point/3) - 2); + x_offset += this.num_glyphs[size-glyph-1].getMetrics().width; + } + + // display colon and denominator if the ratio is to be shown + if (this.ratioed) { + var colon_x = notation_start_x + x_offset + this.point*0.16; + var colon_radius = this.point * 0.06; + this.context.beginPath(); + this.context.arc(colon_x, this.y_pos - this.point*0.08, + colon_radius, 0, Math.PI*2, true); + this.context.closePath(); + this.context.fill(); + this.context.beginPath(); + this.context.arc(colon_x, this.y_pos + this.point*0.12, + colon_radius, 0, Math.PI*2, true); + this.context.closePath(); + this.context.fill(); + x_offset += this.point*0.32; + size = this.denom_glyphs.length; + for (glyph in this.denom_glyphs) { + this.denom_glyphs[size-glyph-1].render( + this.context, notation_start_x + x_offset, + this.y_pos + (this.point/3) - 2); + x_offset += this.denom_glyphs[size-glyph-1].getMetrics().width; + } + } + } + }; + + return Tuplet; +}()); +// Vex Music Notation +// Mohit Muthanna +// +// Copyright Mohit Muthanna 2010 + +// Bounding boxes for interactive notation + +/** @constructor */ +Vex.Flow.BoundingBox = (function() { + function BoundingBox(x, y, w, h) { this.init(x, y, w, h); } + BoundingBox.copy = function(that) { + return new BoundingBox(that.x, that.y, that.w, that.h); }; + + BoundingBox.prototype = { + init: function(x, y, w, h) { + this.x = x; + this.y = y; + this.w = w; + this.h = h; + }, + + getX: function() { return this.x; }, + getY: function() { return this.y; }, + getW: function() { return this.w; }, + getH: function() { return this.h; }, + + setX: function(x) { this.x = x; return this; }, + setY: function(y) { this.y = y; return this; }, + setW: function(w) { this.w = w; return this; }, + setH: function(h) { this.h = h; return this; }, + + move: function(x, y) { this.x += x; this.y += y; }, + clone: function() { return BoundingBox.copy(this); }, + + // Merge my box with given box. Creates a bigger bounding box unless + // the given box is contained in this one. + mergeWith: function(boundingBox, ctx) { + var that = boundingBox; + + var new_x = this.x < that.x ? this.x : that.x; + var new_y = this.y < that.y ? this.y : that.y; + 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); + 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); + + this.x = new_x; + this.y = new_y; + this.w = new_w; + this.h = new_h; + + if (ctx) this.draw(ctx); + return this; + }, + + draw: function(ctx, x, y) { + if (!x) x = 0; + if (!y) y = 0; + ctx.rect(this.x + x, this.y + y, this.w, this.h); + ctx.stroke(); + } + }; + + return BoundingBox; +}());// [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010. +// +// ## Description +// +// `TextNote` is a notation element that is positioned in time. Generally +// meant for objects that sit above/below the staff and inline with each other. +// Examples of this would be such as dynamics, lyrics, chord changes, etc. +Vex.Flow.TextNote = (function() { + function TextNote(text_struct) { + if (arguments.length > 0) this.init(text_struct); + } + + TextNote.Justification = { + LEFT: 1, + CENTER: 2, + RIGHT: 3 + }; + + // Glyph data + TextNote.GLYPHS = { + "segno": { + code: "v8c", + point: 40, + x_shift: 0, + y_shift: -10 + // width: 10 // optional + }, + "tr": { + code: "v1f", + point: 40, + x_shift: 0, + y_shift: 0 + // width: 10 // optional + }, + "mordent_upper": { + code: "v1e", + point: 40, + x_shift: 0, + y_shift: 0 + // width: 10 // optional + }, + "mordent_lower": { + code: "v45", + point: 40, + x_shift: 0, + y_shift: 0 + // width: 10 // optional + }, + "f": { + code: "vba", + point: 40, + x_shift: 0, + y_shift: 0 + // width: 10 // optional + }, + "p": { + code: "vbf", + point: 40, + x_shift: 0, + y_shift: 0 + // width: 10 // optional + }, + "m": { + code: "v62", + point: 40, + x_shift: 0, + y_shift: 0 + // width: 10 // optional + }, + "s": { + code: "v4a", + point: 40, + x_shift: 0, + y_shift: 0 + // width: 10 // optional + }, + "z": { + code: "v80", + point: 40, + x_shift: 0, + y_shift: 0 + // width: 10 // optional + }, + "coda": { + code: "v4d", + point: 40, + x_shift: 0, + y_shift: -8 + // width: 10 // optional + }, + "pedal_open": { + code: "v36", + point:40, + x_shift:0, + y_shift:0 + }, + "pedal_close": { + code: "v5d", + point:40, + x_shift:0, + y_shift:3 + }, + "caesura_straight": { + code: "v34", + point:40, + x_shift:0, + y_shift:2 + }, + "caesura_curved": { + code: "v4b", + point:40, + x_shift:0, + y_shift:2 + }, + "breath": { + code: "v6c", + point:40, + x_shift:0, + y_shift:0 + }, + "tick": { + code: "v6f", + point:50, + x_shift:0, + y_shift:0 + }, + "turn": { + code: "v72", + point:40, + x_shift:0, + y_shift:0 + }, + "turn_inverted": { + code: "v33", + point:40, + x_shift:0, + y_shift:0 + }, + + // DEPRECATED - please use "mordent_upper" or "mordent_lower" + "mordent": { + code: "v1e", + point: 40, + x_shift: 0, + y_shift: 0 + // width: 10 // optional + }, + }; + + // ## Prototype Methods + Vex.Inherit(TextNote, Vex.Flow.Note, { + init: function(text_struct) { + TextNote.superclass.init.call(this, text_struct); + + // Note properties + this.text = text_struct.text; + this.superscript = text_struct.superscript; + this.subscript = text_struct.subscript; + this.glyph_type = text_struct.glyph; + this.glyph = null; + this.font = { + family: "Arial", + size: 12, + weight: "" + }; + + // Set font + if (text_struct.font) this.font = text_struct.font; + + // Determine and set initial note width. Note that the text width is + // an approximation and isn't very accurate. The only way to accurately + // measure the length of text is with `canvasContext.measureText()` + if (this.glyph_type) { + var struct = TextNote.GLYPHS[this.glyph_type]; + if (!struct) throw new Vex.RERR("Invalid glyph type: " + this.glyph_type); + + this.glyph = new Vex.Flow.Glyph(struct.code, struct.point, {cache: false}); + + if (struct.width) + this.setWidth(struct.width); + else + this.setWidth(this.glyph.getMetrics().width); + + this.glyph_struct = struct; + } else { + this.setWidth(Vex.Flow.textWidth(this.text)); + } + this.line = text_struct.line || 0; + this.smooth = text_struct.smooth || false; + this.ignore_ticks = text_struct.ignore_ticks || false; + this.justification = TextNote.Justification.LEFT; + }, + + // Set the horizontal justification of the TextNote + setJustification: function(just) { + this.justification = just; + return this; + }, + + // Set the Stave line on which the note should be placed + setLine: function(line) { + this.line = line; + return this; + }, + + // Pre-render formatting + preFormat: function() { + if (!this.context) throw new Vex.RERR("NoRenderContext", + "Can't measure text without rendering context."); + if (this.preFormatted) return; + + if (this.smooth) { + this.setWidth(0); + } else { + if (this.glyph) { + // Width already set. + } else { + this.setWidth(this.context.measureText(this.text).width); + } + } + + if (this.justification == TextNote.Justification.CENTER) { + this.extraLeftPx = this.width / 2; + } else if (this.justification == TextNote.Justification.RIGHT) { + this.extraLeftPx = this.width; + } + + this.setPreFormatted(true); + }, + + // Renders the TextNote + draw: function() { + if (!this.context) throw new Vex.RERR("NoCanvasContext", + "Can't draw without a canvas context."); + if (!this.stave) throw new Vex.RERR("NoStave", "Can't draw without a stave."); + + var ctx = this.context; + var x = this.getAbsoluteX(); + if (this.justification == TextNote.Justification.CENTER) { + x -= this.getWidth() / 2; + } else if (this.justification == TextNote.Justification.RIGHT) { + x -= this.getWidth(); + } + + var y; + if (this.glyph) { + y = this.stave.getYForLine(this.line + (-3)); + this.glyph.render(this.context, + x + this.glyph_struct.x_shift, + y + this.glyph_struct.y_shift); + } else { + y = this.stave.getYForLine(this.line + (-3)); + ctx.save(); + ctx.setFont(this.font.family, this.font.size, this.font.weight); + ctx.fillText(this.text, x, y); + + // Width of the letter M gives us the approximate height of the text + var height = ctx.measureText("M").width; + // Get accurate width of text + var width = ctx.measureText(this.text).width; + + // Write superscript + if (this.superscript) { + ctx.setFont(this.font.family, this.font.size / 1.3, this.font.weight); + ctx.fillText(this.superscript, x + width + 2, y - (height/2.2)); + } + + // Write subscript + if (this.subscript) { + ctx.setFont(this.font.family, this.font.size / 1.3, this.font.weight); + ctx.fillText(this.subscript, x + width + 2, y + (height/2.2) - 1); + } + + ctx.restore(); + } + } + }); + + return TextNote; +}()); +// VexFlow - Music Engraving for HTML5 +// Copyright Mohit Muthanna 2010 +// Author Larry Kuhns 2013 +// Class to draws string numbers into the notation. + +/** + * @constructor + */ +Vex.Flow.FretHandFinger = (function() { + function FretHandFinger(number) { + if (arguments.length > 0) this.init(number); + } + + var Modifier = Vex.Flow.Modifier; + Vex.Inherit(FretHandFinger, Modifier, { + init: function(number) { + var superclass = Vex.Flow.FretHandFinger.superclass; + superclass.init.call(this); + + this.note = null; + this.index = null; + this.finger = number; + this.width = 7; + this.position = Modifier.Position.LEFT; // Default position above stem or note head + this.x_shift = 0; + this.y_shift = 0; + this.x_offset = 0; // Horizontal offset from default + this.y_offset = 0; // Vertical offset from default + this.font = { + family: "sans-serif", + size: 9, + weight: "bold" + }; + }, + + getCategory: function() { return "frethandfinger"; }, + getNote: function() { return this.note; }, + setNote: function(note) { this.note = note; return this; }, + getIndex: function() { return this.index; }, + setIndex: function(index) { this.index = index; return this; }, + getPosition: function() { return this.position; }, + setPosition: function(position) { + if (position >= Modifier.Position.LEFT && + position <= Modifier.Position.BELOW) + this.position = position; + return this; + }, + setFretHandFinger: function(number) { this.finger = number; return this; }, + setOffsetX: function(x) { this.x_offset = x; return this; }, + setOffsetY: function(y) { this.y_offset = y; return this; }, + + draw: function() { + if (!this.context) throw new Vex.RERR("NoContext", + "Can't draw string number without a context."); + if (!(this.note && (this.index != null))) throw new Vex.RERR("NoAttachedNote", + "Can't draw string number without a note and index."); + + var ctx = this.context; + var start = this.note.getModifierStartXY(this.position, this.index); + var dot_x = (start.x + this.x_shift + this.x_offset); + var dot_y = start.y + this.y_shift + this.y_offset + 5; + + switch (this.position) { + case Modifier.Position.ABOVE: + dot_x -= 4; + dot_y -= 12; + break; + case Modifier.Position.BELOW: + dot_x -= 2; + dot_y += 10; + break; + case Modifier.Position.LEFT: + dot_x -= this.width; + break; + case Modifier.Position.RIGHT: + dot_x += 1; + break; + } + + ctx.save(); + ctx.setFont(this.font.family, this.font.size, this.font.weight); + ctx.fillText("" + this.finger, dot_x, dot_y); + + ctx.restore(); + } + }); + + return FretHandFinger; +}());// VexFlow - Music Engraving for HTML5 +// Copyright Mohit Muthanna 2010 +// Author Larry Kuhns 2013 +// Class to draws string numbers into the notation. + +Vex.Flow.StringNumber = (function() { + function StringNumber(number) { + if (arguments.length > 0) this.init(number); + } + + var Modifier = Vex.Flow.Modifier; + Vex.Inherit(StringNumber, Modifier, { + init: function(number) { + StringNumber.superclass.init.call(this); + + this.note = null; + this.last_note = null; + this.index = null; + this.string_number = number; + this.setWidth(20); // ??? + this.position = Modifier.Position.ABOVE; // Default position above stem or note head + this.x_shift = 0; + this.y_shift = 0; + this.x_offset = 0; // Horizontal offset from default + this.y_offset = 0; // Vertical offset from default + this.dashed = true; // true - draw dashed extension false - no extension + this.leg = Vex.Flow.Renderer.LineEndType.NONE; // draw upward/downward leg at the of extension line + this.radius = 8; + this.font = { + family: "sans-serif", + size: 10, + weight: "bold" + }; + }, + + getCategory: function() { return "stringnumber"; }, + getNote: function() { return this.note; }, + setNote: function(note) { this.note = note; return this; }, + getIndex: function() { return this.index; }, + setIndex: function(index) { this.index = index; return this; }, + + setLineEndType: function(leg) { + if (leg >= Vex.Flow.Renderer.LineEndType.NONE && + leg <= Vex.Flow.Renderer.LineEndType.DOWN) + this.leg = leg; + return this; + }, + + getPosition: function() { return this.position; }, + setPosition: function(position) { + if (position >= Modifier.Position.LEFT && + position <= Modifier.Position.BELOW) + this.position = position; + return this; + }, + + setStringNumber: function(number) { this.string_number = number; return this; }, + setOffsetX: function(x) { this.x_offset = x; return this; }, + setOffsetY: function(y) { this.y_offset = y; return this; }, + setLastNote: function(note) { this.last_note = note; return this; }, + setDashed: function(dashed) { this.dashed = dashed; return this; }, + + draw: function() { + if (!this.context) throw new Vex.RERR("NoContext", + "Can't draw string number without a context."); + if (!(this.note && (this.index != null))) throw new Vex.RERR("NoAttachedNote", + "Can't draw string number without a note and index."); + + var ctx = this.context; + var line_space = this.note.stave.options.spacing_between_lines_px; + + var start = this.note.getModifierStartXY(this.position, this.index); + var dot_x = (start.x + this.x_shift + this.x_offset); + var dot_y = start.y + this.y_shift + this.y_offset; + + switch (this.position) { + case Modifier.Position.ABOVE: + case Modifier.Position.BELOW: + var stem_ext = this.note.getStemExtents(); + var top = stem_ext.topY; + var bottom = stem_ext.baseY + 2; + + if (this.note.stem_direction == Vex.Flow.StaveNote.STEM_DOWN) { + top = stem_ext.baseY; + bottom = stem_ext.topY - 2; + } + + if (this.position == Modifier.Position.ABOVE) { + dot_y = this.note.hasStem() ? top - (line_space * 1.75) + : start.y - (line_space * 1.75); + } else { + dot_y = this.note.hasStem() ? bottom + (line_space * 1.5) + : start.y + (line_space * 1.75); + } + + dot_y += this.y_shift + this.y_offset; + + break; + case Modifier.Position.LEFT: + dot_x -= (this.radius / 2) + 5; + break; + case Modifier.Position.RIGHT: + dot_x += (this.radius / 2) + 6; + break; + } + + ctx.save(); + ctx.beginPath(); + ctx.arc(dot_x, dot_y, this.radius, 0, Math.PI * 2, false); + ctx.lineWidth = 1.5; + ctx.stroke(); + ctx.setFont(this.font.family, this.font.size, this.font.weight); + var x = dot_x - ctx.measureText(this.string_number).width / 2; + ctx.fillText("" + this.string_number, x, dot_y + 4.5); + + if (this.last_note != null) { + var end = this.last_note.getStemX() - this.note.getX() + 5; + ctx.strokeStyle="#000000"; + ctx.lineCap = "round"; + ctx.lineWidth = 0.6; + if (this.dashed) + Vex.Flow.Renderer.drawDashedLine(ctx, dot_x + 10, dot_y, dot_x + end, dot_y, [3,3]); + else + Vex.Flow.Renderer.drawDashedLine(ctx, dot_x + 10, dot_y, dot_x + end, dot_y, [3,0]); + + var len, pattern; + switch (this.leg) { + case Vex.Flow.Renderer.LineEndType.UP: + len = -10; + pattern = this.dashed ? [3,3] : [3,0]; + Vex.Flow.Renderer.drawDashedLine(ctx, dot_x + end, dot_y, dot_x + end, dot_y + len, pattern); + break; + case Vex.Flow.Renderer.LineEndType.DOWN: + len = 10; + pattern = this.dashed ? [3,3] : [3,0]; + Vex.Flow.Renderer.drawDashedLine(ctx, dot_x + end, dot_y, dot_x + end, dot_y + len, pattern); + break; + } + } + + ctx.restore(); + } + }); + + return StringNumber; +}()); +// VexFlow - Music Engraving for HTML5 +// Copyright Mohit Muthanna 2010 +// Author Larry Kuhns 2013 +// Class implements chord strokes - arpeggiated, brush & rasquedo. + +Vex.Flow.Stroke = (function() { + function Stroke(type, options) { + if (arguments.length > 0) this.init(type, options); + } + + Stroke.Type = { + BRUSH_DOWN: 1, + BRUSH_UP: 2, + ROLL_DOWN: 3, // Arpegiated chord + ROLL_UP: 4, // Arpegiated chord + RASQUEDO_DOWN: 5, + RASQUEDO_UP: 6 + }; + + var Modifier = Vex.Flow.Modifier; + Vex.Inherit(Stroke, Modifier, { + init: function(type, options) { + Stroke.superclass.init.call(this); + + this.note = null; + this.options = Vex.Merge({}, options); + + // multi voice - span stroke across all voices if true + this.all_voices = 'all_voices' in this.options ? + this.options.all_voices : true; + + // multi voice - end note of stroke, set in draw() + this.note_end = null; + this.index = null; + this.type = type; + this.position = Modifier.Position.LEFT; + + this.render_options = { + font_scale: 38, + stroke_px: 3, + stroke_spacing: 10 + }; + + this.font = { + family: "serif", + size: 10, + weight: "bold italic" + }; + + this.setXShift(0); + this.setWidth(10); + }, + + getCategory: function() { return "strokes"; }, + getPosition: function() { return this.position; }, + addEndNote: function(note) { this.note_end = note; return this; }, + + draw: function() { + if (!this.context) throw new Vex.RERR("NoContext", + "Can't draw stroke without a context."); + if (!(this.note && (this.index != null))) throw new Vex.RERR("NoAttachedNote", + "Can't draw stroke without a note and index."); + var start = this.note.getModifierStartXY(this.position, this.index); + var ys = this.note.getYs(); + var topY = start.y; + var botY = start.y; + var x = start.x - 5; + var line_space = this.note.stave.options.spacing_between_lines_px; + + var notes = this.getModifierContext().getModifiers(this.note.getCategory()); + var i; + for (i = 0; i < notes.length; i++) { + ys = notes[i].getYs(); + for (var n = 0; n < ys.length; n++) { + if (this.note == notes[i] || this.all_voices) { + topY = Vex.Min(topY, ys[n]); + botY = Vex.Max(botY, ys[n]); + } + } + } + + var arrow, arrow_shift_x, arrow_y, text_shift_x, text_y; + switch (this.type) { + case Stroke.Type.BRUSH_DOWN: + arrow = "vc3"; + arrow_shift_x = -3; + arrow_y = topY - (line_space / 2) + 10; + botY += (line_space / 2); + break; + case Stroke.Type.BRUSH_UP: + arrow = "v11"; + arrow_shift_x = 0.5; + arrow_y = botY + (line_space / 2); + topY -= (line_space / 2); + break; + case Stroke.Type.ROLL_DOWN: + case Stroke.Type.RASQUEDO_DOWN: + arrow = "vc3"; + arrow_shift_x = -3; + text_shift_x = this.x_shift + arrow_shift_x - 2; + if (this.note instanceof Vex.Flow.StaveNote) { + topY += 1.5 * line_space; + if ((botY - topY) % 2 !== 0) { + botY += 0.5 * line_space; + } else { + botY += line_space; + } + arrow_y = topY - line_space; + text_y = botY + line_space + 2; + } else { + topY += 1.5 * line_space; + botY += line_space; + arrow_y = topY - 0.75 * line_space; + text_y = botY + 0.25 * line_space; + } + break; + case Stroke.Type.ROLL_UP: + case Stroke.Type.RASQUEDO_UP: + arrow = "v52"; + arrow_shift_x = -4; + text_shift_x = this.x_shift + arrow_shift_x - 1; + if (this.note instanceof Vex.Flow.StaveNote) { + arrow_y = line_space / 2; + topY += 0.5 * line_space; + if ((botY - topY) % 2 === 0) { + botY += line_space / 2; + } + arrow_y = botY + 0.5 * line_space; + text_y = topY - 1.25 * line_space; + } else { + topY += 0.25 * line_space; + botY += 0.5 * line_space; + arrow_y = botY + 0.25 * line_space; + text_y = topY - line_space; + } + break; + } + + // Draw the stroke + if (this.type == Stroke.Type.BRUSH_DOWN || + this.type == Stroke.Type.BRUSH_UP) { + this.context.fillRect(x + this.x_shift, topY, 1, botY - topY); + } else { + if (this.note instanceof Vex.Flow.StaveNote) { + for (i = topY; i <= botY; i += line_space) { + Vex.Flow.renderGlyph(this.context, x + this.x_shift - 4, + i, + this.render_options.font_scale, "va3"); + } + } else { + for (i = topY; i <= botY; i+= 10) { + Vex.Flow.renderGlyph(this.context, x + this.x_shift - 4, + i, + this.render_options.font_scale, "va3"); + } + if (this.type == Vex.Flow.Stroke.Type.RASQUEDO_DOWN) + text_y = i + 0.25 * line_space; + } + } + + // Draw the arrow head + Vex.Flow.renderGlyph(this.context, x + this.x_shift + arrow_shift_x, arrow_y, + this.render_options.font_scale, arrow); + + // Draw the rasquedo "R" + if (this.type == Stroke.Type.RASQUEDO_DOWN || + this.type == Stroke.Type.RASQUEDO_UP) { + this.context.save(); + this.context.setFont(this.font.family, this.font.size, this.font.weight); + this.context.fillText("R", x + text_shift_x, text_y); + this.context.restore(); + } + } + }); + + return Stroke; +}());// VexFlow - Music Engraving for HTML5 +// Copyright Mohit Muthanna 2010 +// +// This class implements curves (for slurs) + +Vex.Flow.Curve = (function() { + // from: Start note + // to: End note + // options: + // cps: List of control points + // x_shift: pixels to shift + // y_shift: pixels to shift + function Curve(from, to, options) { + if (arguments.length > 0) this.init(from, to, options); + } + + Curve.Position = { + NEAR_HEAD: 1, + NEAR_TOP: 2 + }; + + Curve.DEBUG = true; + + Curve.prototype = { + init: function(from, to, options) { + this.render_options = { + spacing: 2, + thickness: 2, + x_shift: 0, + y_shift: 10, + position: Curve.Position.NEAR_HEAD, + invert: false, + cps: [{x: 0, y: 10}, {x: 0, y: 10}] + }; + + Vex.Merge(this.render_options, options); + this.setNotes(from, to); + }, + + setContext: function(context) { this.context = context; return this; }, + setNotes: function(from, to) { + if (!from && !to) + throw new Vex.RuntimeError("BadArguments", + "Curve needs to have either first_note or last_note set."); + + this.from = from; + this.to = to; + return this; + }, + + /** + * @return {boolean} Returns true if this is a partial bar. + */ + isPartial: function() { + return (!this.from || !this.to); + }, + + renderCurve: function(params) { + var ctx = this.context; + var cps = this.render_options.cps; + + var x_shift = this.render_options.x_shift; + var y_shift = this.render_options.y_shift * params.direction; + + var first_x = params.first_x + x_shift; + var first_y = params.first_y + y_shift; + var last_x = params.last_x - x_shift; + var last_y = params.last_y + y_shift; + var thickness = this.render_options.thickness; + + var cp_spacing = (last_x - first_x) / (cps.length + 2); + + ctx.beginPath(); + ctx.moveTo(first_x, first_y); + ctx.bezierCurveTo(first_x + cp_spacing + cps[0].x, + first_y + (cps[0].y * params.direction), + last_x - cp_spacing + cps[1].x, + last_y + (cps[1].y * params.direction), + last_x, last_y); + ctx.bezierCurveTo(last_x - cp_spacing + cps[1].x, + last_y + ((cps[1].y + thickness) * params.direction), + first_x + cp_spacing + cps[0].x, + first_y + ((cps[0].y + thickness) * params.direction), + first_x, first_y); + ctx.stroke(); + ctx.closePath(); + ctx.fill(); + }, + + draw: function() { + if (!this.context) + throw new Vex.RERR("NoContext", "No context to render tie."); + var first_note = this.from; + var last_note = this.to; + var first_x, last_x, first_y, last_y, stem_direction; + + var metric = "baseY"; + var end_metric = "baseY"; + var position = this.render_options.position; + var position_end = this.render_options.position_end; + + if (position === Curve.Position.NEAR_TOP) { + metric = "topY"; + end_metric = "topY"; + } + + if (position_end == Curve.Position.NEAR_HEAD) { + end_metric = "baseY"; + } else if (position_end == Curve.Position.NEAR_TOP) { + end_metric = "topY"; + } + + if (first_note) { + first_x = first_note.getTieRightX(); + stem_direction = first_note.getStemDirection(); + first_y = first_note.getStemExtents()[metric]; + } else { + first_x = last_note.getStave().getTieStartX(); + first_y = last_note.getStemExtents()[metric]; + } + + if (last_note) { + last_x = last_note.getTieLeftX(); + stem_direction = last_note.getStemDirection(); + last_y = last_note.getStemExtents()[end_metric]; + } else { + last_x = first_note.getStave().getTieEndX(); + last_y = first_note.getStemExtents()[end_metric]; + } + + this.renderCurve({ + first_x: first_x, + last_x: last_x, + first_y: first_y, + last_y: last_y, + direction: stem_direction * + (this.render_options.invert === true ? -1 : 1) + }); + return true; + } + }; + + return Curve; +}()); +// [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010. +// +// ## Description +// +// This file implements `StaveLine` which are simply lines that connect +// two notes. This object is highly configurable, see the `render_options`. +// A simple line is often used for notating glissando articulations, but you +// can format a `StaveLine` with arrows or colors for more pedagogical +// purposes, such as diagrams. +Vex.Flow.StaveLine = (function() { + function StaveLine(notes) { + if (arguments.length > 0) this.init(notes); + } + + // Text Positioning + StaveLine.TextVerticalPosition = { + TOP: 1, + BOTTOM: 2 + }; + + StaveLine.TextJustification = { + LEFT: 1, + CENTER: 2, + RIGHT: 3 + }; + + // ## Prototype Methods + StaveLine.prototype = { + // Initialize the StaveLine with the given `notes`. + // + // `notes` is a struct that has: + // + // ``` + // { + // first_note: Note, + // last_note: Note, + // first_indices: [n1, n2, n3], + // last_indices: [n1, n2, n3] + // } + // ``` + init: function(notes) { + this.notes = notes; + this.context = null; + + this.text = ""; + + this.font = { + family: "Arial", + size: 10, + weight: "" + }; + + this.render_options = { + // Space to add to the left or the right + padding_left: 4, + padding_right: 3, + + // The width of the line in pixels + line_width: 1, + // An array of line/space lengths + line_dash: null, + // Can draw rounded line end, instead of a square + rounded_end: true, + // The color of the line and arrowheads + color: null, + + // Flags to draw arrows on each end of the line + draw_start_arrow: false, + draw_end_arrow: false, + + // The length of the arrowhead sides + arrowhead_length: 10, + // The angle of the arrowhead + arrowhead_angle: Math.PI / 8, + + // The position of the text + text_position_vertical: StaveLine.TextVerticalPosition.TOP, + text_justification: StaveLine.TextJustification.CENTER + }; + + this.setNotes(notes); + }, + + // Set the rendering context + setContext: function(context) { this.context = context; return this; }, + // Set the font for the `StaveLine` text + setFont: function(font) { this.font = font; return this; }, + // The the annotation for the `StaveLine` + setText: function(text) { this.text = text; return this; }, + + // Set the notes for the `StaveLine` + setNotes: function(notes) { + if (!notes.first_note && !notes.last_note) + throw new Vex.RuntimeError("BadArguments", + "Notes needs to have either first_note or last_note set."); + + if (!notes.first_indices) notes.first_indices = [0]; + if (!notes.last_indices) notes.last_indices = [0]; + + if (notes.first_indices.length != notes.last_indices.length) + throw new Vex.RuntimeError("BadArguments", "Connected notes must have similar" + + " index sizes"); + + // Success. Lets grab 'em notes. + this.first_note = notes.first_note; + this.first_indices = notes.first_indices; + this.last_note = notes.last_note; + this.last_indices = notes.last_indices; + return this; + }, + + // Apply the style of the `StaveLine` to the context + applyLineStyle: function() { + if (!this.context) { + throw new Vex.RERR("NoContext","No context to apply the styling to"); + } + + var render_options = this.render_options; + var ctx = this.context; + + if (render_options.line_dash) { + ctx.setLineDash(render_options.line_dash); + } + + if (render_options.line_width) { + ctx.setLineWidth(render_options.line_width); + } + + if (render_options.rounded_end) { + ctx.lineCap = "round"; + } else { + ctx.lineCap = "square"; + } + + if (this.font) { + ctx.setFont(this.font.family, this.font.size, this.font.weight); + } + }, + + // Renders the `StaveLine` on the context + draw: function() { + if (!this.context) { + throw new Vex.RERR("NoContext", "No context to render StaveLine."); + } + + var ctx = this.context; + var first_note = this.first_note; + var last_note = this.last_note; + var render_options = this.render_options; + + ctx.save(); + this.applyLineStyle(); + + // Cycle through each set of indices and draw lines + var start_position; + var end_position; + this.first_indices.forEach(function(first_index, i) { + var last_index = this.last_indices[i]; + + // Get initial coordinates for the start/end of the line + start_position = first_note.getModifierStartXY(2, first_index); + end_position = last_note.getModifierStartXY(1, last_index); + var upwards_slope = start_position.y > end_position.y; + + // Adjust `x` coordinates for modifiers + start_position.x += first_note.getMetrics().modRightPx + + render_options.padding_left; + end_position.x -= last_note.getMetrics().modLeftPx + + render_options.padding_right; + + + // Adjust first `x` coordinates for displacements + var notehead_width = first_note.getGlyph().head_width; + var first_displaced = first_note.getKeyProps()[first_index].displaced; + if (first_displaced && first_note.getStemDirection() === 1) { + start_position.x += notehead_width + render_options.padding_left; + } + + // Adjust last `x` coordinates for displacements + var last_displaced = last_note.getKeyProps()[last_index].displaced; + if (last_displaced && last_note.getStemDirection() === -1) { + end_position.x -= notehead_width + render_options.padding_right; + } + + // Adjust y position better if it's not coming from the center of the note + start_position.y += upwards_slope ? -3 : 1; + end_position.y += upwards_slope ? 2 : 0; + + drawArrowLine(ctx, start_position, end_position, this.render_options); + + }, this); + + // Determine the x coordinate where to start the ext + var text_width = ctx.measureText(this.text).width; + var justification = render_options.text_justification; + var x = 0; + if (justification === StaveLine.TextJustification.LEFT) { + x = start_position.x; + } else if (justification === StaveLine.TextJustification.CENTER) { + var delta_x = (end_position.x - start_position.x); + var center_x = (delta_x / 2 ) + start_position.x; + x = center_x - (text_width / 2); + } else if (justification === StaveLine.TextJustification.RIGHT) { + x = end_position.x - text_width; + } + + // Determine the y value to start the text + var y; + var vertical_position = render_options.text_position_vertical; + if (vertical_position === StaveLine.TextVerticalPosition.TOP) { + y = first_note.getStave().getYForTopText(); + } else if (vertical_position === StaveLine.TextVerticalPosition.BOTTOM) { + y = first_note.getStave().getYForBottomText(); + } + + // Draw the text + ctx.fillText(this.text, x, y); + ctx.restore(); + + return this; + } + }; + + // ## Private Helpers + // + // Attribution: Arrow rendering implementations based off of + // Patrick Horgan's article, "Drawing lines and arcs with + // arrow heads on HTML5 Canvas" + // + // Draw an arrow head that connects between 3 coordinates + function drawArrowHead(ctx, x0, y0, x1, y1, x2, y2) { + // all cases do this. + ctx.save(); + ctx.beginPath(); + ctx.moveTo(x0,y0); + ctx.lineTo(x1,y1); + ctx.lineTo(x2,y2); + + // Close off the arrow lines with an arcTo curve + var backdist = Math.sqrt(((x2-x0)*(x2-x0))+((y2-y0)*(y2-y0))); + ctx.arcTo(x1, y1, x0, y0, 0.55 * backdist); + + ctx.fill(); + ctx.restore(); + } + + // Helper function to draw a line with arrow heads + function drawArrowLine(ctx, point1, point2, config) { + var both_arrows = config.draw_start_arrow && config.draw_end_arrow; + + var x1 = point1.x; + var y1 = point1.y; + var x2 = point2.x; + var y2 = point2.y; + + // For ends with arrow we actually want to stop before we get to the arrow + // so that wide lines won't put a flat end on the arrow. + var distance = Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)); + var ratio = (distance - config.arrowhead_length/3) / distance; + var end_x, end_y, start_x, start_y; + if (config.draw_end_arrow || both_arrows) { + end_x = Math.round(x1 + (x2 - x1) * ratio); + end_y = Math.round(y1 + (y2 - y1) * ratio); + } else { + end_x = x2; + end_y = y2; + } + + if (config.draw_start_arrow || both_arrows) { + start_x = x1 + (x2 - x1) * (1 - ratio); + start_y = y1 + (y2 - y1) * (1 - ratio); + } else { + start_x = x1; + start_y = y1; + } + + // Draw the shaft of the arrow + ctx.beginPath(); + ctx.setStrokeStyle(config.color); + ctx.setFillStyle(config.color); + ctx.moveTo(start_x, start_y); + ctx.lineTo(end_x,end_y); + ctx.stroke(); + + // calculate the angle of the line + var line_angle = Math.atan2(y2 - y1, x2 - x1); + // h is the line length of a side of the arrow head + var h = Math.abs(config.arrowhead_length / Math.cos(config.arrowhead_angle)); + + var angle1, angle2; + var top_x, top_y; + var bottom_x, bottom_y; + + if (config.draw_end_arrow || both_arrows) { + angle1 = line_angle + Math.PI + config.arrowhead_angle; + top_x = x2 + Math.cos(angle1) * h; + top_y = y2 + Math.sin(angle1) * h; + + angle2 = line_angle + Math.PI - config.arrowhead_angle; + bottom_x = x2 + Math.cos(angle2) * h; + bottom_y = y2 + Math.sin(angle2) * h; + + drawArrowHead(ctx, top_x, top_y, x2, y2, bottom_x, bottom_y); + } + + if (config.draw_start_arrow || both_arrows) { + angle1 = line_angle + config.arrowhead_angle; + top_x = x1 + Math.cos(angle1) * h; + top_y = y1 + Math.sin(angle1) * h; + + angle2 = line_angle - config.arrowhead_angle; + bottom_x = x1 + Math.cos(angle2) * h; + bottom_y = y1 + Math.sin(angle2) * h; + + drawArrowHead(ctx, top_x, top_y, x1, y1, bottom_x, bottom_y); + } + } + + return StaveLine; +}()); +Vex.Flow.GraceNote = (function() { + var GraceNote = function(note_struct) { + if (arguments.length > 0) this.init(note_struct); + }; + + Vex.Inherit(GraceNote, Vex.Flow.StaveNote, { + init: function(note_struct) { + GraceNote.superclass.init.call(this, note_struct); + + this.render_options.glyph_font_scale = 22; + this.render_options.stem_height = 20; + this.render_options.stroke_px = 2; + this.glyph.head_width = 6; + + this.slash = note_struct.slash; + this.slur = true; + + this.buildNoteHeads(); + + this.width = 3; + }, + + getStemExtension: function(){ + var glyph = this.getGlyph(); + + if (this.stem_extension_override != null) { + return this.stem_extension_override; + } + + if (glyph) { + return this.getStemDirection() === 1 ? glyph.gracenote_stem_up_extension : + glyph.gracenote_stem_down_extension; + } + + return 0; + }, + + getCategory: function() { return 'gracenotes'; }, + + draw: function(){ + GraceNote.superclass.draw.call(this); + var ctx = this.context; + var stem_direction = this.getStemDirection(); + + if (this.slash) { + ctx.beginPath(); + + var x = this.getAbsoluteX(); + var y = this.getYs()[0] - (this.stem.getHeight() / 2.8); + if (stem_direction === 1) { + x += 1; + ctx.lineTo(x, y); + ctx.lineTo(x + 13, y - 9); + } else if (stem_direction === -1) { + x -= 4; + y += 1; + ctx.lineTo(x, y); + ctx.lineTo(x + 13, y + 9); + } + + ctx.closePath(); + ctx.stroke(); + } + } + }); + + return GraceNote; +}()); +Vex.Flow.GraceNoteGroup = (function(){ + var GraceNoteGroup = function(grace_notes, config) { + if (arguments.length > 0) this.init(grace_notes, config); + }; + + Vex.Inherit(GraceNoteGroup, Vex.Flow.Modifier, { + init: function(grace_notes, show_slur) { + var superclass = GraceNoteGroup.superclass; + superclass.init.call(this); + + this.note = null; + this.index = null; + this.position = Vex.Flow.Modifier.Position.LEFT; + this.grace_notes = grace_notes; + this.width = 0; + + this.preFormatted = false; + + this.show_slur = show_slur; + this.slur = null; + + this.formatter = new Vex.Flow.Formatter(); + this.voice = new Vex.Flow.Voice({ + num_beats: 4, + beat_value: 4, + resolution: Vex.Flow.RESOLUTION + }).setStrict(false); + + this.voice.addTickables(this.grace_notes); + + return this; + }, + + preFormat: function(){ + if (this.preFormatted) return; + + this.formatter.joinVoices([this.voice]).format([this.voice], 0); + this.setWidth(this.formatter.getMinTotalWidth()); + + this.preFormatted = true; + }, + + beamNotes: function(){ + if (this.grace_notes.length > 1) { + var beam = new Vex.Flow.Beam(this.grace_notes); + + beam.render_options.beam_width = 3; + beam.render_options.partial_beam_length = 4; + + this.beam = beam; + } + + return this; + }, + + setNote: function(note) { + this.note = note; + }, + getCategory: function() { return "gracenotegroups"; }, + setWidth: function(width){ + this.width = width; + }, + getWidth: function(){ + return this.width; + }, + setXShift: function(x_shift) { + this.x_shift = x_shift; + }, + draw: function() { + if (!this.context) { + throw new Vex.RuntimeError("NoContext", + "Can't draw Grace note without a context."); + } + + var note = this.getNote(); + + if (!(note && (this.index !== null))) { + throw new Vex.RuntimeError("NoAttachedNote", + "Can't draw grace note without a parent note and parent note index."); + } + + function alignGraceNotesWithNote(grace_notes, note) { + // Shift over the tick contexts of each note + // So that th aligned with the note + var tickContext = note.getTickContext(); + var extraPx = tickContext.getExtraPx(); + var x = tickContext.getX() - extraPx.left - extraPx.extraLeft; + grace_notes.forEach(function(graceNote) { + var tick_context = graceNote.getTickContext(); + var x_offset = tick_context.getX(); + graceNote.setStave(note.stave); + tick_context.setX(x + x_offset); + }); + } + + alignGraceNotesWithNote(this.grace_notes, note); + + // Draw notes + this.grace_notes.forEach(function(graceNote) { + graceNote.setContext(this.context).draw(); + }, this); + + // Draw beam + if (this.beam) { + this.beam.setContext(this.context).draw(); + } + + if (this.show_slur) { + // Create and draw slur + this.slur = new Vex.Flow.StaveTie({ + last_note: this.grace_notes[0], + first_note: note, + first_indices: [0], + last_indices: [0] + }); + + this.slur.render_options.cp2 = 12; + this.slur.setContext(this.context).draw(); + } + } + }); + +return GraceNoteGroup; +}()); \ No newline at end of file diff --git a/webclient/js/vextab/vextab.js b/webclient/js/vextab/vextab.js new file mode 100644 index 0000000000..caf43e975c --- /dev/null +++ b/webclient/js/vextab/vextab.js @@ -0,0 +1,325 @@ +// Generated by CoffeeScript 1.7.1 +(function() { + var __slice = [].slice, + __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + + Vex.Flow.VexTab = (function() { + var L, newError; + + VexTab.DEBUG = false; + + L = function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + if (Vex.Flow.VexTab.DEBUG) { + return typeof console !== "undefined" && console !== null ? console.log.apply(console, ["(Vex.Flow.VexTab)"].concat(__slice.call(args))) : void 0; + } + }; + + newError = function(object, msg) { + return new Vex.RERR("ParseError", "" + msg + " in line " + object._l + " column " + object._c); + }; + + function VexTab(artist) { + this.artist = artist; + this.reset(); + } + + VexTab.prototype.reset = function() { + this.valid = false; + return this.elements = false; + }; + + VexTab.prototype.isValid = function() { + return this.valid; + }; + + VexTab.prototype.getArtist = function() { + return this.artist; + }; + + VexTab.prototype.parseStaveOptions = function(options) { + var clefs, e, error, notation_option, num_strings, option, params, voices, _i, _len, _ref, _ref1, _ref2; + params = {}; + if (options == null) { + return params; + } + notation_option = null; + for (_i = 0, _len = options.length; _i < _len; _i++) { + option = options[_i]; + error = function(msg) { + return newError(option, msg); + }; + params[option.key] = option.value; + switch (option.key) { + case "notation": + case "tablature": + notation_option = option; + if ((_ref = option.value) !== "true" && _ref !== "false") { + throw error("'" + option.key + "' must be 'true' or 'false'"); + } + break; + case "key": + if (!_.has(Vex.Flow.keySignature.keySpecs, option.value)) { + throw error("Invalid key signature '" + option.value + "'"); + } + break; + case "clef": + clefs = ["treble", "bass", "tenor", "alto", "percussion", "none"]; + if (_ref1 = option.value, __indexOf.call(clefs, _ref1) < 0) { + throw error("'clef' must be one of " + (clefs.join(', '))); + } + break; + case "voice": + voices = ["top", "bottom", "new"]; + if (_ref2 = option.value, __indexOf.call(voices, _ref2) < 0) { + throw error("'voice' must be one of " + (voices.join(', '))); + } + break; + case "time": + try { + new Vex.Flow.TimeSignature(option.value); + } catch (_error) { + e = _error; + throw error("Invalid time signature: '" + option.value + "'"); + } + break; + case "tuning": + try { + new Vex.Flow.Tuning(option.value); + } catch (_error) { + e = _error; + throw error("Invalid tuning: '" + option.value + "'"); + } + break; + case "strings": + num_strings = parseInt(option.value); + if (num_strings < 4 || num_strings > 8) { + throw error("Invalid number of strings: " + num_strings); + } + break; + default: + throw error("Invalid option '" + option.key + "'"); + } + } + if (params.notation === "false" && params.tablature === "false") { + throw newError(notation_option, "Both 'notation' and 'tablature' can't be invisible"); + } + return params; + }; + + VexTab.prototype.parseCommand = function(element) { + if (element.command === "bar") { + this.artist.addBar(element.type); + } + if (element.command === "tuplet") { + this.artist.makeTuplets(element.params.tuplet, element.params.notes); + } + if (element.command === "annotations") { + this.artist.addAnnotations(element.params); + } + if (element.command === "rest") { + this.artist.addRest(element.params); + } + if (element.command === "command") { + return this.artist.runCommand(element.params, element._l, element._c); + } + }; + + VexTab.prototype.parseChord = function(element) { + L("parseChord:", element); + return this.artist.addChord(_.map(element.chord, function(note) { + return _.pick(note, 'time', 'dot', 'fret', 'abc', 'octave', 'string', 'articulation', 'decorator'); + }), element.articulation, element.decorator); + }; + + VexTab.prototype.parseFret = function(note) { + return this.artist.addNote(_.pick(note, 'time', 'dot', 'fret', 'string', 'articulation', 'decorator')); + }; + + VexTab.prototype.parseABC = function(note) { + return this.artist.addNote(_.pick(note, 'time', 'dot', 'fret', 'abc', 'octave', 'string', 'articulation', 'decorator')); + }; + + VexTab.prototype.parseStaveElements = function(notes) { + var element, _i, _len, _results; + L("parseStaveElements:", notes); + _results = []; + for (_i = 0, _len = notes.length; _i < _len; _i++) { + element = notes[_i]; + if (element.time) { + this.artist.setDuration(element.time, element.dot); + } + if (element.command) { + this.parseCommand(element); + } + if (element.chord) { + this.parseChord(element); + } + if (element.abc) { + _results.push(this.parseABC(element)); + } else if (element.fret) { + _results.push(this.parseFret(element)); + } else { + _results.push(void 0); + } + } + return _results; + }; + + VexTab.prototype.parseStaveText = function(text_line) { + var bartext, command, createNote, font, justification, position, smooth, str, text, _i, _len, _results; + if (!_.isEmpty(text_line)) { + this.artist.addTextVoice(); + } + position = 0; + justification = "center"; + smooth = true; + font = null; + bartext = (function(_this) { + return function() { + return _this.artist.addTextNote("", 0, justification, false, true); + }; + })(this); + createNote = (function(_this) { + return function(text) { + var e, ignore_ticks; + ignore_ticks = false; + if (text[0] === "|") { + ignore_ticks = true; + text = text.slice(1); + } + try { + return _this.artist.addTextNote(text, position, justification, smooth, ignore_ticks); + } catch (_error) { + e = _error; + throw newError(str, "Bad text or duration. Did you forget a comma?" + e); + } + }; + })(this); + _results = []; + for (_i = 0, _len = text_line.length; _i < _len; _i++) { + str = text_line[_i]; + text = str.text.trim(); + if (text.match(/\.font=.*/)) { + font = text.slice(6); + _results.push(this.artist.setTextFont(font)); + } else if (text[0] === ":") { + _results.push(this.artist.setDuration(text)); + } else if (text[0] === ".") { + command = text.slice(1); + switch (command) { + case "center": + case "left": + case "right": + _results.push(justification = command); + break; + case "strict": + _results.push(smooth = false); + break; + case "smooth": + _results.push(smooth = true); + break; + case "bar": + case "|": + _results.push(bartext()); + break; + default: + _results.push(position = parseInt(text.slice(1), 10)); + } + } else if (text === "|") { + _results.push(bartext()); + } else if (text.slice(0, 2) === "++") { + _results.push(this.artist.addTextVoice()); + } else { + _results.push(createNote(text)); + } + } + return _results; + }; + + VexTab.prototype.generate = function() { + var e, option, options, stave, _i, _j, _len, _len1, _ref, _ref1, _results; + _ref = this.elements; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + stave = _ref[_i]; + switch (stave.element) { + case "stave": + case "tabstave": + this.artist.addStave(stave.element, this.parseStaveOptions(stave.options)); + if (stave.notes != null) { + this.parseStaveElements(stave.notes); + } + if (stave.text != null) { + _results.push(this.parseStaveText(stave.text)); + } else { + _results.push(void 0); + } + break; + case "voice": + this.artist.addVoice(this.parseStaveOptions(stave.options)); + if (stave.notes != null) { + this.parseStaveElements(stave.notes); + } + if (stave.text != null) { + _results.push(this.parseStaveText(stave.text)); + } else { + _results.push(void 0); + } + break; + case "options": + options = {}; + _ref1 = stave.params; + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + option = _ref1[_j]; + options[option.key] = option.value; + } + try { + _results.push(this.artist.setOptions(options)); + } catch (_error) { + e = _error; + throw newError(stave, e.message); + } + break; + default: + throw newError(stave, "Invalid keyword '" + stave.element + "'"); + } + } + return _results; + }; + + VexTab.prototype.parse = function(code) { + var line, stripped_code; + vextab_parser.parseError = function(message, hash) { + L("VexTab parse error: ", message, hash); + message = "Unexpected text '" + hash.text + "' at line " + hash.loc.first_line + " column " + hash.loc.first_column + "."; + throw new Vex.RERR("ParseError", message); + }; + if (code == null) { + throw new Vex.RERR("ParseError", "No code"); + } + L("Parsing:\n" + code); + stripped_code = (function() { + var _i, _len, _ref, _results; + _ref = code.split(/\r\n|\r|\n/); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + line = _ref[_i]; + _results.push(line.trim()); + } + return _results; + })(); + this.elements = vextab_parser.parse(stripped_code.join("\n")); + if (this.elements) { + this.generate(); + this.valid = true; + } + return this.elements; + }; + + return VexTab; + + })(); + +}).call(this); diff --git a/webclient/js/vextab/vextab_parser.js b/webclient/js/vextab/vextab_parser.js new file mode 100644 index 0000000000..0742eb4b6d --- /dev/null +++ b/webclient/js/vextab/vextab_parser.js @@ -0,0 +1,981 @@ +/* parser generated by jison 0.4.13 */ +/* + Returns a Parser object of the following structure: + + Parser: { + yy: {} + } + + Parser.prototype: { + yy: {}, + trace: function(), + symbols_: {associative list: name ==> number}, + terminals_: {associative list: number ==> name}, + productions_: [...], + performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$), + table: [...], + defaultActions: {...}, + parseError: function(str, hash), + parse: function(input), + + lexer: { + EOF: 1, + parseError: function(str, hash), + setInput: function(input), + input: function(), + unput: function(str), + more: function(), + less: function(n), + pastInput: function(), + upcomingInput: function(), + showPosition: function(), + test_match: function(regex_match_array, rule_index), + next: function(), + lex: function(), + begin: function(condition), + popState: function(), + _currentRules: function(), + topState: function(), + pushState: function(condition), + + options: { + ranges: boolean (optional: true ==> token location info will include a .range[] member) + flex: boolean (optional: true ==> flex-like lexing behaviour where the rules are tested exhaustively to find the longest match) + backtrack_lexer: boolean (optional: true ==> lexer regexes are tested in order and for each matching regex the action code is invoked; the lexer terminates the scan when a token is returned by the action code) + }, + + performAction: function(yy, yy_, $avoiding_name_collisions, YY_START), + rules: [...], + conditions: {associative list: name ==> set}, + } + } + + + token location info (@$, _$, etc.): { + first_line: n, + last_line: n, + first_column: n, + last_column: n, + range: [start_number, end_number] (where the numbers are indexes into the input string, regular zero-based) + } + + + the parseError function receives a 'hash' object with these members for lexer and parser errors: { + text: (matched text) + token: (the produced terminal token, if any) + line: (yylineno) + } + while parser (grammar) errors will also provide these members, i.e. parser errors deliver a superset of attributes: { + loc: (yylloc) + expected: (string describing the set of expected tokens) + recoverable: (boolean: TRUE when the parser has a error recovery rule available for this particular error) + } +*/ +var vextab_parser = (function(){ +var parser = {trace: function trace() { }, +yy: {}, +symbols_: {"error":2,"e":3,"maybe_vextab":4,"EOF":5,"vextab":6,"stave":7,"voice":8,"maybe_options":9,"stave_data":10,"OPTIONS":11,"options":12,"TABSTAVE":13,"STAVE":14,"VOICE":15,"stave_additions":16,"TEXT":17,"text":18,"NOTES":19,"notes":20,"SLUR":21,"WORD":22,"=":23,"STR":24,",":25,"lingo":26,"line":27,"chord":28,"time":29,"bar":30,"[":31,"]":32,"tuplets":33,"annotations":34,"command":35,"rest":36,"|":37,":":38,"frets":39,"maybe_decorator":40,"/":41,"string":42,"chord_line":43,".":44,"(":45,")":46,"articulation":47,"NUMBER":48,"abc":49,"_":50,"timed_fret":51,"time_values":52,"maybe_dot":53,"time_unit":54,"maybe_slash":55,"w":56,"h":57,"q":58,"d":59,"S":60,"-":61,"s":62,"t":63,"T":64,"b":65,"p":66,"v":67,"V":68,"u":69,"^":70,"$":71,"annotation_words":72,"!":73,"COMMAND":74,"#":75,"ABC":76,"abc_accidental":77,"accidental_type":78,"@":79,"n":80,"~":81,"$accept":0,"$end":1}, +terminals_: {2:"error",5:"EOF",11:"OPTIONS",13:"TABSTAVE",14:"STAVE",15:"VOICE",17:"TEXT",19:"NOTES",21:"SLUR",22:"WORD",23:"=",24:"STR",25:",",31:"[",32:"]",37:"|",38:":",41:"/",44:".",45:"(",46:")",48:"NUMBER",50:"_",56:"w",57:"h",58:"q",59:"d",60:"S",61:"-",62:"s",63:"t",64:"T",65:"b",66:"p",67:"v",68:"V",69:"u",70:"^",71:"$",73:"!",74:"COMMAND",75:"#",76:"ABC",79:"@",80:"n",81:"~"}, +productions_: [0,[3,2],[4,0],[4,1],[6,1],[6,2],[7,3],[7,2],[7,2],[8,1],[8,1],[8,1],[10,1],[10,2],[16,2],[16,2],[16,2],[9,0],[9,1],[12,3],[12,4],[18,1],[18,3],[20,1],[20,2],[26,1],[26,1],[26,1],[26,1],[26,1],[26,1],[26,1],[26,1],[26,1],[26,1],[30,1],[30,3],[30,3],[30,3],[30,3],[30,3],[27,4],[43,1],[43,3],[28,4],[28,5],[39,1],[39,1],[39,4],[39,2],[39,4],[51,5],[51,1],[51,5],[51,8],[51,1],[51,4],[29,3],[52,2],[54,1],[54,1],[54,1],[54,1],[53,0],[53,1],[55,0],[55,1],[42,1],[47,1],[47,1],[47,1],[47,1],[47,1],[47,1],[47,1],[40,1],[40,1],[40,1],[40,1],[40,0],[33,3],[33,5],[34,3],[72,1],[72,3],[35,3],[36,2],[36,3],[36,4],[49,3],[77,1],[77,2],[77,1],[77,2],[77,1],[77,0],[78,0],[78,1]], +performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) { +/* this == yyval */ + +var $0 = $$.length - 1; +switch (yystate) { +case 1: + if (Vex.Flow.VexTab.DEBUG && $$[$0-1]) { + console.log($$[$0-1]); + console.log(JSON.stringify($$[$0-1], null, " ")); + } + return $$[$0-1]; + +break; +case 2: this.$ = null +break; +case 3: this.$ = $$[$0] +break; +case 4: this.$ = [$$[$0]] +break; +case 5: this.$ = [].concat($$[$0-1], $$[$0]) +break; +case 6: this.$ = { + element: $$[$0-2], + options: $$[$0-1], + notes: $$[$0].notes, + text: $$[$0].text, + _l: _$[$0-2].first_line, + _c: _$[$0-2].first_column + } + +break; +case 7: this.$ = { + element: $$[$0-1], + options: $$[$0], + _l: _$[$0-1].first_line, + _c: _$[$0-1].first_column + } + +break; +case 8: + this.$ = { + element: "options", + params: $$[$0], + _l: _$[$0-1].first_line, + _c: _$[$0-1].first_column + } + +break; +case 12: this.$ = $$[$0] +break; +case 13: + var text = [].concat($$[$0-1].text, $$[$0].text); + var notes = [].concat($$[$0-1].notes, $$[$0].notes); + var slurs = [].concat($$[$0-1].slurs, $$[$0].slurs) + this.$ = {text: text, notes: notes, slurs: slurs}; + +break; +case 14:this.$ = {text: $$[$0], notes: [], slurs: []} +break; +case 15:this.$ = {notes: $$[$0], text: [], slurs: []} +break; +case 16:this.$ = {slurs: $$[$0], notes: [], text: []} +break; +case 17: this.$ = null +break; +case 18: this.$ = $$[$0] +break; +case 19: this.$ = [{ + key: $$[$0-2], + value: $$[$0], + _l: _$[$0-2].first_line, + _c: _$[$0-2].first_column + }] + +break; +case 20: this.$ = [].concat($$[$0-3], [{ + key: $$[$0-2], + value: $$[$0], + _l: _$[$0-2].first_line, + _c: _$[$0-2].first_column + }]) + +break; +case 21: this.$ = [{text: $$[$0], _l: _$[$0].first_line, _c: _$[$0].first_column}] +break; +case 22: this.$ = [].concat($$[$0-2], {text: $$[$0], _l: _$[$0].first_line, _c: _$[$0].first_column}) +break; +case 23: this.$ = $$[$0] +break; +case 24: this.$ = [].concat($$[$0-1], $$[$0]) +break; +case 25: this.$ = $$[$0] +break; +case 26: this.$ = $$[$0] +break; +case 27: this.$ = $$[$0] +break; +case 28: this.$ = [{ + command: "bar", + type: $$[$0], + _l: _$[$0].first_line, + _c: _$[$0].first_column + }] + +break; +case 29: this.$ = [{ + command: "open_beam", + _l: _$[$0].first_line, + _c: _$[$0].first_column + }] + +break; +case 30: this.$ = [{ + command: "close_beam", + _l: _$[$0].first_line, + _c: _$[$0].first_column + }] + +break; +case 31: this.$ = [{ + command: "tuplet", + params: $$[$0], + _l: _$[$0].first_line, + _c: _$[$0].first_column + }] + +break; +case 32: this.$ = [{ + command: "annotations", + params: $$[$0], + _l: _$[$0].first_line, + _c: _$[$0].first_column + }] + +break; +case 33: this.$ = [{ + command: "command", + params: $$[$0], + _l: _$[$0].first_line, + _c: _$[$0].first_column + }] + +break; +case 34: + this.$ = [{ + command: "rest", + params: $$[$0] + }] + +break; +case 35: this.$ = 'single' +break; +case 36: this.$ = 'double' +break; +case 37: this.$ = 'end' +break; +case 38: this.$ = 'repeat-end' +break; +case 39: this.$ = 'repeat-begin' +break; +case 40: this.$ = 'repeat-both' +break; +case 41: + _.extend(_.last($$[$0-3]), {decorator: $$[$0-2]}) + _.each($$[$0-3], function(fret) { fret['string'] = $$[$0] }) + this.$ = $$[$0-3] + +break; +case 42: this.$ = $$[$0] +break; +case 43: this.$ = [].concat($$[$0-2], $$[$0]) +break; +case 44: this.$ = [{chord: $$[$0-2], decorator: $$[$0]}] +break; +case 45: this.$ = [{chord: $$[$0-2], articulation: $$[$0-4], decorator: $$[$0]}] +break; +case 46: this.$ = [{ + fret: $$[$0], + _l: _$[$0].first_line, + _c: _$[$0].first_column}] + +break; +case 47: this.$ = [{abc: $$[$0], _l: _$[$0].first_line, _c: _$[$0].first_column}] +break; +case 48: this.$ = [{abc: $$[$0-3], octave: $$[$0-2], + fret: $$[$0], _l: _$[$0-3].first_line, _c: _$[$0-3].first_column}] +break; +case 49: this.$ = [_.extend($$[$0], {articulation: $$[$0-1]})] +break; +case 50: + _.extend(_.last($$[$0-3]), {decorator: $$[$0-2]}) + _.extend($$[$0], {articulation: $$[$0-1]}) + $$[$0-3].push($$[$0]) + this.$ = $$[$0-3] + +break; +case 51: this.$ = { + time: $$[$0-3], dot: $$[$0-2], fret: $$[$0], + _l: _$[$0-4].first_line, _c: _$[$0-4].first_column} +break; +case 52: this.$ = {fret: $$[$0], _l: _$[$0].first_line, _c: _$[$0].first_column} +break; +case 53: this.$ = {time: $$[$0-3], dot: $$[$0-2], abc: $$[$0]} +break; +case 54: this.$ = {time: $$[$0-6], dot: $$[$0-5], abc: $$[$0-3], octave: $$[$0-2], fret: $$[$0]} +break; +case 55: this.$ = {abc: $$[$0], _l: _$[$0].first_line, _c: _$[$0].first_column} +break; +case 56: this.$ = {abc: $$[$0-3], octave: $$[$0-2], + fret: $$[$0], _l: _$[$0-3].first_line, _c: _$[$0-3].first_column} +break; +case 57: this.$ = {time: $$[$0-1], dot: $$[$0]} +break; +case 58: this.$ = $$[$0-1] + $$[$0] +break; +case 59: this.$ = $$[$0] +break; +case 60: this.$ = $$[$0] +break; +case 61: this.$ = $$[$0] +break; +case 62: this.$ = $$[$0] +break; +case 63: this.$ = false +break; +case 64: this.$ = true +break; +case 65: this.$ = '' +break; +case 66: this.$ = 's' +break; +case 67: this.$ = $$[$0] +break; +case 68: this.$ = '-' +break; +case 69: this.$ = 's' +break; +case 70: this.$ = 't' +break; +case 71: this.$ = 'T' +break; +case 72: this.$ = 'b' +break; +case 73: this.$ = 'h' +break; +case 74: this.$ = 'p' +break; +case 75: this.$ = 'v' +break; +case 76: this.$ = 'V' +break; +case 77: this.$ = 'u' +break; +case 78: this.$ = 'd' +break; +case 79: this.$ = null +break; +case 80: this.$ = {tuplet: $$[$0-1]} +break; +case 81: this.$ = {tuplet: $$[$0-3], notes: $$[$0-1]} +break; +case 82: this.$ = $$[$0-1] +break; +case 83: this.$ = [$$[$0]] +break; +case 84: this.$ = [].concat($$[$0-2], $$[$0]) +break; +case 85: this.$ = $$[$0-1] +break; +case 86: this.$ = {position: 0} +break; +case 87: this.$ = {position: $$[$0-1]} +break; +case 88: this.$ = {position: $$[$0-1] * -1} +break; +case 89: this.$ = {key: $$[$0-2], accidental: $$[$0-1], accidental_type: $$[$0]} +break; +case 90: this.$ = "#" +break; +case 91: this.$ = "##" +break; +case 92: this.$ = "b" +break; +case 93: this.$ = "bb" +break; +case 94: this.$ = "n" +break; +case 96: this.$ = null; +break; +case 97: this.$ = "c" +break; +} +}, +table: [{3:1,4:2,5:[2,2],6:3,7:4,8:5,11:[1,6],13:[1,7],14:[1,8],15:[1,9]},{1:[3]},{5:[1,10]},{5:[2,3],7:11,8:5,11:[1,6],13:[1,7],14:[1,8],15:[1,9]},{5:[2,4],11:[2,4],13:[2,4],14:[2,4],15:[2,4]},{5:[2,17],9:12,11:[2,17],12:13,13:[2,17],14:[2,17],15:[2,17],17:[2,17],19:[2,17],21:[2,17],22:[1,14]},{12:15,22:[1,14]},{5:[2,9],11:[2,9],13:[2,9],14:[2,9],15:[2,9],17:[2,9],19:[2,9],21:[2,9],22:[2,9]},{5:[2,10],11:[2,10],13:[2,10],14:[2,10],15:[2,10],17:[2,10],19:[2,10],21:[2,10],22:[2,10]},{5:[2,11],11:[2,11],13:[2,11],14:[2,11],15:[2,11],17:[2,11],19:[2,11],21:[2,11],22:[2,11]},{1:[2,1]},{5:[2,5],11:[2,5],13:[2,5],14:[2,5],15:[2,5]},{5:[2,7],10:16,11:[2,7],13:[2,7],14:[2,7],15:[2,7],16:17,17:[1,18],19:[1,19],21:[1,20]},{5:[2,18],11:[2,18],13:[2,18],14:[2,18],15:[2,18],17:[2,18],19:[2,18],21:[2,18],22:[1,21]},{23:[1,22]},{5:[2,8],11:[2,8],13:[2,8],14:[2,8],15:[2,8],22:[1,21]},{5:[2,6],11:[2,6],13:[2,6],14:[2,6],15:[2,6],16:23,17:[1,18],19:[1,19],21:[1,20]},{5:[2,12],11:[2,12],13:[2,12],14:[2,12],15:[2,12],17:[2,12],19:[2,12],21:[2,12]},{18:24,24:[1,25]},{20:26,23:[1,43],26:27,27:28,28:29,29:30,30:31,31:[1,32],32:[1,33],33:34,34:35,35:36,36:37,37:[1,42],38:[1,41],39:38,45:[1,39],47:40,48:[1,48],49:49,57:[1,55],61:[1,50],62:[1,51],63:[1,52],64:[1,53],65:[1,54],66:[1,56],70:[1,44],71:[1,45],73:[1,46],75:[1,47],76:[1,57]},{5:[2,17],9:58,11:[2,17],12:13,13:[2,17],14:[2,17],15:[2,17],17:[2,17],19:[2,17],21:[2,17],22:[1,14]},{23:[1,59]},{22:[1,60]},{5:[2,13],11:[2,13],13:[2,13],14:[2,13],15:[2,13],17:[2,13],19:[2,13],21:[2,13]},{5:[2,14],11:[2,14],13:[2,14],14:[2,14],15:[2,14],17:[2,14],19:[2,14],21:[2,14],25:[1,61]},{5:[2,21],11:[2,21],13:[2,21],14:[2,21],15:[2,21],17:[2,21],19:[2,21],21:[2,21],25:[2,21]},{5:[2,15],11:[2,15],13:[2,15],14:[2,15],15:[2,15],17:[2,15],19:[2,15],21:[2,15],23:[1,43],26:62,27:28,28:29,29:30,30:31,31:[1,32],32:[1,33],33:34,34:35,35:36,36:37,37:[1,42],38:[1,41],39:38,45:[1,39],47:40,48:[1,48],49:49,57:[1,55],61:[1,50],62:[1,51],63:[1,52],64:[1,53],65:[1,54],66:[1,56],70:[1,44],71:[1,45],73:[1,46],75:[1,47],76:[1,57]},{5:[2,23],11:[2,23],13:[2,23],14:[2,23],15:[2,23],17:[2,23],19:[2,23],21:[2,23],23:[2,23],31:[2,23],32:[2,23],37:[2,23],38:[2,23],45:[2,23],48:[2,23],57:[2,23],61:[2,23],62:[2,23],63:[2,23],64:[2,23],65:[2,23],66:[2,23],70:[2,23],71:[2,23],73:[2,23],75:[2,23],76:[2,23]},{5:[2,25],11:[2,25],13:[2,25],14:[2,25],15:[2,25],17:[2,25],19:[2,25],21:[2,25],23:[2,25],31:[2,25],32:[2,25],37:[2,25],38:[2,25],45:[2,25],48:[2,25],57:[2,25],61:[2,25],62:[2,25],63:[2,25],64:[2,25],65:[2,25],66:[2,25],70:[2,25],71:[2,25],73:[2,25],75:[2,25],76:[2,25]},{5:[2,26],11:[2,26],13:[2,26],14:[2,26],15:[2,26],17:[2,26],19:[2,26],21:[2,26],23:[2,26],31:[2,26],32:[2,26],37:[2,26],38:[2,26],45:[2,26],48:[2,26],57:[2,26],61:[2,26],62:[2,26],63:[2,26],64:[2,26],65:[2,26],66:[2,26],70:[2,26],71:[2,26],73:[2,26],75:[2,26],76:[2,26]},{5:[2,27],11:[2,27],13:[2,27],14:[2,27],15:[2,27],17:[2,27],19:[2,27],21:[2,27],23:[2,27],31:[2,27],32:[2,27],37:[2,27],38:[2,27],45:[2,27],48:[2,27],57:[2,27],61:[2,27],62:[2,27],63:[2,27],64:[2,27],65:[2,27],66:[2,27],70:[2,27],71:[2,27],73:[2,27],75:[2,27],76:[2,27]},{5:[2,28],11:[2,28],13:[2,28],14:[2,28],15:[2,28],17:[2,28],19:[2,28],21:[2,28],23:[2,28],31:[2,28],32:[2,28],37:[2,28],38:[2,28],45:[2,28],48:[2,28],57:[2,28],61:[2,28],62:[2,28],63:[2,28],64:[2,28],65:[2,28],66:[2,28],70:[2,28],71:[2,28],73:[2,28],75:[2,28],76:[2,28]},{5:[2,29],11:[2,29],13:[2,29],14:[2,29],15:[2,29],17:[2,29],19:[2,29],21:[2,29],23:[2,29],31:[2,29],32:[2,29],37:[2,29],38:[2,29],45:[2,29],48:[2,29],57:[2,29],61:[2,29],62:[2,29],63:[2,29],64:[2,29],65:[2,29],66:[2,29],70:[2,29],71:[2,29],73:[2,29],75:[2,29],76:[2,29]},{5:[2,30],11:[2,30],13:[2,30],14:[2,30],15:[2,30],17:[2,30],19:[2,30],21:[2,30],23:[2,30],31:[2,30],32:[2,30],37:[2,30],38:[2,30],45:[2,30],48:[2,30],57:[2,30],61:[2,30],62:[2,30],63:[2,30],64:[2,30],65:[2,30],66:[2,30],70:[2,30],71:[2,30],73:[2,30],75:[2,30],76:[2,30]},{5:[2,31],11:[2,31],13:[2,31],14:[2,31],15:[2,31],17:[2,31],19:[2,31],21:[2,31],23:[2,31],31:[2,31],32:[2,31],37:[2,31],38:[2,31],45:[2,31],48:[2,31],57:[2,31],61:[2,31],62:[2,31],63:[2,31],64:[2,31],65:[2,31],66:[2,31],70:[2,31],71:[2,31],73:[2,31],75:[2,31],76:[2,31]},{5:[2,32],11:[2,32],13:[2,32],14:[2,32],15:[2,32],17:[2,32],19:[2,32],21:[2,32],23:[2,32],31:[2,32],32:[2,32],37:[2,32],38:[2,32],45:[2,32],48:[2,32],57:[2,32],61:[2,32],62:[2,32],63:[2,32],64:[2,32],65:[2,32],66:[2,32],70:[2,32],71:[2,32],73:[2,32],75:[2,32],76:[2,32]},{5:[2,33],11:[2,33],13:[2,33],14:[2,33],15:[2,33],17:[2,33],19:[2,33],21:[2,33],23:[2,33],31:[2,33],32:[2,33],37:[2,33],38:[2,33],45:[2,33],48:[2,33],57:[2,33],61:[2,33],62:[2,33],63:[2,33],64:[2,33],65:[2,33],66:[2,33],70:[2,33],71:[2,33],73:[2,33],75:[2,33],76:[2,33]},{5:[2,34],11:[2,34],13:[2,34],14:[2,34],15:[2,34],17:[2,34],19:[2,34],21:[2,34],23:[2,34],31:[2,34],32:[2,34],37:[2,34],38:[2,34],45:[2,34],48:[2,34],57:[2,34],61:[2,34],62:[2,34],63:[2,34],64:[2,34],65:[2,34],66:[2,34],70:[2,34],71:[2,34],73:[2,34],75:[2,34],76:[2,34]},{40:63,41:[2,79],57:[2,79],59:[1,67],61:[2,79],62:[2,79],63:[2,79],64:[2,79],65:[2,79],66:[2,79],67:[1,64],68:[1,65],69:[1,66]},{27:69,39:38,43:68,47:70,48:[1,48],49:49,57:[1,55],61:[1,50],62:[1,51],63:[1,52],64:[1,53],65:[1,54],66:[1,56],76:[1,57]},{38:[1,73],45:[1,71],48:[1,74],49:75,51:72,76:[1,57]},{48:[1,78],52:76,54:77,56:[1,79],57:[1,80],58:[1,81]},{5:[2,35],11:[2,35],13:[2,35],14:[2,35],15:[2,35],17:[2,35],19:[2,35],21:[2,35],23:[2,35],31:[2,35],32:[2,35],37:[2,35],38:[2,35],45:[2,35],48:[2,35],57:[2,35],61:[2,35],62:[2,35],63:[2,35],64:[2,35],65:[2,35],66:[2,35],70:[2,35],71:[2,35],73:[2,35],75:[2,35],76:[2,35]},{37:[1,82],38:[1,83]},{48:[1,84]},{22:[1,86],72:85},{74:[1,87]},{48:[1,89],61:[1,90],75:[1,88]},{41:[2,46],57:[2,46],59:[2,46],61:[2,46],62:[2,46],63:[2,46],64:[2,46],65:[2,46],66:[2,46],67:[2,46],68:[2,46],69:[2,46]},{41:[2,47],48:[1,91],57:[2,47],59:[2,47],61:[2,47],62:[2,47],63:[2,47],64:[2,47],65:[2,47],66:[2,47],67:[2,47],68:[2,47],69:[2,47]},{38:[2,68],45:[2,68],48:[2,68],76:[2,68]},{38:[2,69],45:[2,69],48:[2,69],76:[2,69]},{38:[2,70],45:[2,70],48:[2,70],76:[2,70]},{38:[2,71],45:[2,71],48:[2,71],76:[2,71]},{38:[2,72],45:[2,72],48:[2,72],76:[2,72]},{38:[2,73],45:[2,73],48:[2,73],76:[2,73]},{38:[2,74],45:[2,74],48:[2,74],76:[2,74]},{41:[2,95],48:[2,95],57:[2,95],59:[2,95],61:[2,95],62:[2,95],63:[2,95],64:[2,95],65:[2,95],66:[2,95],67:[2,95],68:[2,95],69:[2,95],75:[1,93],77:92,79:[1,94],80:[1,95],81:[2,95]},{5:[2,16],11:[2,16],13:[2,16],14:[2,16],15:[2,16],17:[2,16],19:[2,16],21:[2,16]},{22:[1,96]},{5:[2,19],11:[2,19],13:[2,19],14:[2,19],15:[2,19],17:[2,19],19:[2,19],21:[2,19],22:[2,19]},{24:[1,97]},{5:[2,24],11:[2,24],13:[2,24],14:[2,24],15:[2,24],17:[2,24],19:[2,24],21:[2,24],23:[2,24],31:[2,24],32:[2,24],37:[2,24],38:[2,24],45:[2,24],48:[2,24],57:[2,24],61:[2,24],62:[2,24],63:[2,24],64:[2,24],65:[2,24],66:[2,24],70:[2,24],71:[2,24],73:[2,24],75:[2,24],76:[2,24]},{41:[1,98],47:99,57:[1,55],61:[1,50],62:[1,51],63:[1,52],64:[1,53],65:[1,54],66:[1,56]},{5:[2,75],11:[2,75],13:[2,75],14:[2,75],15:[2,75],17:[2,75],19:[2,75],21:[2,75],23:[2,75],31:[2,75],32:[2,75],37:[2,75],38:[2,75],41:[2,75],45:[2,75],48:[2,75],57:[2,75],61:[2,75],62:[2,75],63:[2,75],64:[2,75],65:[2,75],66:[2,75],70:[2,75],71:[2,75],73:[2,75],75:[2,75],76:[2,75]},{5:[2,76],11:[2,76],13:[2,76],14:[2,76],15:[2,76],17:[2,76],19:[2,76],21:[2,76],23:[2,76],31:[2,76],32:[2,76],37:[2,76],38:[2,76],41:[2,76],45:[2,76],48:[2,76],57:[2,76],61:[2,76],62:[2,76],63:[2,76],64:[2,76],65:[2,76],66:[2,76],70:[2,76],71:[2,76],73:[2,76],75:[2,76],76:[2,76]},{5:[2,77],11:[2,77],13:[2,77],14:[2,77],15:[2,77],17:[2,77],19:[2,77],21:[2,77],23:[2,77],31:[2,77],32:[2,77],37:[2,77],38:[2,77],41:[2,77],45:[2,77],48:[2,77],57:[2,77],61:[2,77],62:[2,77],63:[2,77],64:[2,77],65:[2,77],66:[2,77],70:[2,77],71:[2,77],73:[2,77],75:[2,77],76:[2,77]},{5:[2,78],11:[2,78],13:[2,78],14:[2,78],15:[2,78],17:[2,78],19:[2,78],21:[2,78],23:[2,78],31:[2,78],32:[2,78],37:[2,78],38:[2,78],41:[2,78],45:[2,78],48:[2,78],57:[2,78],61:[2,78],62:[2,78],63:[2,78],64:[2,78],65:[2,78],66:[2,78],70:[2,78],71:[2,78],73:[2,78],75:[2,78],76:[2,78]},{44:[1,101],46:[1,100]},{44:[2,42],46:[2,42]},{38:[1,73],48:[1,74],49:75,51:72,76:[1,57]},{27:69,39:38,43:102,47:70,48:[1,48],49:49,57:[1,55],61:[1,50],62:[1,51],63:[1,52],64:[1,53],65:[1,54],66:[1,56],76:[1,57]},{41:[2,49],57:[2,49],59:[2,49],61:[2,49],62:[2,49],63:[2,49],64:[2,49],65:[2,49],66:[2,49],67:[2,49],68:[2,49],69:[2,49]},{48:[1,78],52:103,54:77,56:[1,79],57:[1,80],58:[1,81]},{41:[2,52],57:[2,52],59:[2,52],61:[2,52],62:[2,52],63:[2,52],64:[2,52],65:[2,52],66:[2,52],67:[2,52],68:[2,52],69:[2,52]},{41:[2,55],48:[1,104],57:[2,55],59:[2,55],61:[2,55],62:[2,55],63:[2,55],64:[2,55],65:[2,55],66:[2,55],67:[2,55],68:[2,55],69:[2,55]},{5:[2,63],11:[2,63],13:[2,63],14:[2,63],15:[2,63],17:[2,63],19:[2,63],21:[2,63],23:[2,63],31:[2,63],32:[2,63],37:[2,63],38:[2,63],45:[2,63],48:[2,63],53:105,57:[2,63],59:[1,106],61:[2,63],62:[2,63],63:[2,63],64:[2,63],65:[2,63],66:[2,63],70:[2,63],71:[2,63],73:[2,63],75:[2,63],76:[2,63]},{5:[2,65],11:[2,65],13:[2,65],14:[2,65],15:[2,65],17:[2,65],19:[2,65],21:[2,65],23:[2,65],31:[2,65],32:[2,65],37:[2,65],38:[2,65],45:[2,65],48:[2,65],55:107,57:[2,65],59:[2,65],60:[1,108],61:[2,65],62:[2,65],63:[2,65],64:[2,65],65:[2,65],66:[2,65],70:[2,65],71:[2,65],73:[2,65],75:[2,65],76:[2,65]},{5:[2,59],11:[2,59],13:[2,59],14:[2,59],15:[2,59],17:[2,59],19:[2,59],21:[2,59],23:[2,59],31:[2,59],32:[2,59],37:[2,59],38:[2,59],45:[2,59],48:[2,59],57:[2,59],59:[2,59],60:[2,59],61:[2,59],62:[2,59],63:[2,59],64:[2,59],65:[2,59],66:[2,59],70:[2,59],71:[2,59],73:[2,59],75:[2,59],76:[2,59]},{5:[2,60],11:[2,60],13:[2,60],14:[2,60],15:[2,60],17:[2,60],19:[2,60],21:[2,60],23:[2,60],31:[2,60],32:[2,60],37:[2,60],38:[2,60],45:[2,60],48:[2,60],57:[2,60],59:[2,60],60:[2,60],61:[2,60],62:[2,60],63:[2,60],64:[2,60],65:[2,60],66:[2,60],70:[2,60],71:[2,60],73:[2,60],75:[2,60],76:[2,60]},{5:[2,61],11:[2,61],13:[2,61],14:[2,61],15:[2,61],17:[2,61],19:[2,61],21:[2,61],23:[2,61],31:[2,61],32:[2,61],37:[2,61],38:[2,61],45:[2,61],48:[2,61],57:[2,61],59:[2,61],60:[2,61],61:[2,61],62:[2,61],63:[2,61],64:[2,61],65:[2,61],66:[2,61],70:[2,61],71:[2,61],73:[2,61],75:[2,61],76:[2,61]},{5:[2,62],11:[2,62],13:[2,62],14:[2,62],15:[2,62],17:[2,62],19:[2,62],21:[2,62],23:[2,62],31:[2,62],32:[2,62],37:[2,62],38:[2,62],45:[2,62],48:[2,62],57:[2,62],59:[2,62],60:[2,62],61:[2,62],62:[2,62],63:[2,62],64:[2,62],65:[2,62],66:[2,62],70:[2,62],71:[2,62],73:[2,62],75:[2,62],76:[2,62]},{23:[1,110],37:[1,109],38:[1,111]},{37:[1,112],38:[1,113]},{25:[1,115],70:[1,114]},{25:[1,117],71:[1,116]},{25:[2,83],71:[2,83]},{73:[1,118]},{5:[2,86],11:[2,86],13:[2,86],14:[2,86],15:[2,86],17:[2,86],19:[2,86],21:[2,86],23:[2,86],31:[2,86],32:[2,86],37:[2,86],38:[2,86],45:[2,86],48:[2,86],57:[2,86],61:[2,86],62:[2,86],63:[2,86],64:[2,86],65:[2,86],66:[2,86],70:[2,86],71:[2,86],73:[2,86],75:[2,86],76:[2,86]},{75:[1,119]},{48:[1,120]},{50:[1,121]},{41:[2,96],48:[2,96],57:[2,96],59:[2,96],61:[2,96],62:[2,96],63:[2,96],64:[2,96],65:[2,96],66:[2,96],67:[2,96],68:[2,96],69:[2,96],78:122,81:[1,123]},{41:[2,90],48:[2,90],57:[2,90],59:[2,90],61:[2,90],62:[2,90],63:[2,90],64:[2,90],65:[2,90],66:[2,90],67:[2,90],68:[2,90],69:[2,90],75:[1,124],81:[2,90]},{41:[2,92],48:[2,92],57:[2,92],59:[2,92],61:[2,92],62:[2,92],63:[2,92],64:[2,92],65:[2,92],66:[2,92],67:[2,92],68:[2,92],69:[2,92],79:[1,125],81:[2,92]},{41:[2,94],48:[2,94],57:[2,94],59:[2,94],61:[2,94],62:[2,94],63:[2,94],64:[2,94],65:[2,94],66:[2,94],67:[2,94],68:[2,94],69:[2,94],81:[2,94]},{5:[2,20],11:[2,20],13:[2,20],14:[2,20],15:[2,20],17:[2,20],19:[2,20],21:[2,20],22:[2,20]},{5:[2,22],11:[2,22],13:[2,22],14:[2,22],15:[2,22],17:[2,22],19:[2,22],21:[2,22],25:[2,22]},{42:126,48:[1,127]},{38:[1,73],48:[1,74],49:75,51:128,76:[1,57]},{5:[2,79],11:[2,79],13:[2,79],14:[2,79],15:[2,79],17:[2,79],19:[2,79],21:[2,79],23:[2,79],31:[2,79],32:[2,79],37:[2,79],38:[2,79],40:129,45:[2,79],48:[2,79],57:[2,79],59:[1,67],61:[2,79],62:[2,79],63:[2,79],64:[2,79],65:[2,79],66:[2,79],67:[1,64],68:[1,65],69:[1,66],70:[2,79],71:[2,79],73:[2,79],75:[2,79],76:[2,79]},{27:130,39:38,47:70,48:[1,48],49:49,57:[1,55],61:[1,50],62:[1,51],63:[1,52],64:[1,53],65:[1,54],66:[1,56],76:[1,57]},{44:[1,101],46:[1,131]},{38:[2,63],53:132,59:[1,106]},{50:[1,133]},{5:[2,57],11:[2,57],13:[2,57],14:[2,57],15:[2,57],17:[2,57],19:[2,57],21:[2,57],23:[2,57],31:[2,57],32:[2,57],37:[2,57],38:[2,57],45:[2,57],48:[2,57],57:[2,57],61:[2,57],62:[2,57],63:[2,57],64:[2,57],65:[2,57],66:[2,57],70:[2,57],71:[2,57],73:[2,57],75:[2,57],76:[2,57]},{5:[2,64],11:[2,64],13:[2,64],14:[2,64],15:[2,64],17:[2,64],19:[2,64],21:[2,64],23:[2,64],31:[2,64],32:[2,64],37:[2,64],38:[2,64],45:[2,64],48:[2,64],57:[2,64],61:[2,64],62:[2,64],63:[2,64],64:[2,64],65:[2,64],66:[2,64],70:[2,64],71:[2,64],73:[2,64],75:[2,64],76:[2,64]},{5:[2,58],11:[2,58],13:[2,58],14:[2,58],15:[2,58],17:[2,58],19:[2,58],21:[2,58],23:[2,58],31:[2,58],32:[2,58],37:[2,58],38:[2,58],45:[2,58],48:[2,58],57:[2,58],59:[2,58],61:[2,58],62:[2,58],63:[2,58],64:[2,58],65:[2,58],66:[2,58],70:[2,58],71:[2,58],73:[2,58],75:[2,58],76:[2,58]},{5:[2,66],11:[2,66],13:[2,66],14:[2,66],15:[2,66],17:[2,66],19:[2,66],21:[2,66],23:[2,66],31:[2,66],32:[2,66],37:[2,66],38:[2,66],45:[2,66],48:[2,66],57:[2,66],59:[2,66],61:[2,66],62:[2,66],63:[2,66],64:[2,66],65:[2,66],66:[2,66],70:[2,66],71:[2,66],73:[2,66],75:[2,66],76:[2,66]},{5:[2,36],11:[2,36],13:[2,36],14:[2,36],15:[2,36],17:[2,36],19:[2,36],21:[2,36],23:[2,36],31:[2,36],32:[2,36],37:[2,36],38:[2,36],45:[2,36],48:[2,36],57:[2,36],61:[2,36],62:[2,36],63:[2,36],64:[2,36],65:[2,36],66:[2,36],70:[2,36],71:[2,36],73:[2,36],75:[2,36],76:[2,36]},{5:[2,37],11:[2,37],13:[2,37],14:[2,37],15:[2,37],17:[2,37],19:[2,37],21:[2,37],23:[2,37],31:[2,37],32:[2,37],37:[2,37],38:[2,37],45:[2,37],48:[2,37],57:[2,37],61:[2,37],62:[2,37],63:[2,37],64:[2,37],65:[2,37],66:[2,37],70:[2,37],71:[2,37],73:[2,37],75:[2,37],76:[2,37]},{5:[2,39],11:[2,39],13:[2,39],14:[2,39],15:[2,39],17:[2,39],19:[2,39],21:[2,39],23:[2,39],31:[2,39],32:[2,39],37:[2,39],38:[2,39],45:[2,39],48:[2,39],57:[2,39],61:[2,39],62:[2,39],63:[2,39],64:[2,39],65:[2,39],66:[2,39],70:[2,39],71:[2,39],73:[2,39],75:[2,39],76:[2,39]},{5:[2,38],11:[2,38],13:[2,38],14:[2,38],15:[2,38],17:[2,38],19:[2,38],21:[2,38],23:[2,38],31:[2,38],32:[2,38],37:[2,38],38:[2,38],45:[2,38],48:[2,38],57:[2,38],61:[2,38],62:[2,38],63:[2,38],64:[2,38],65:[2,38],66:[2,38],70:[2,38],71:[2,38],73:[2,38],75:[2,38],76:[2,38]},{5:[2,40],11:[2,40],13:[2,40],14:[2,40],15:[2,40],17:[2,40],19:[2,40],21:[2,40],23:[2,40],31:[2,40],32:[2,40],37:[2,40],38:[2,40],45:[2,40],48:[2,40],57:[2,40],61:[2,40],62:[2,40],63:[2,40],64:[2,40],65:[2,40],66:[2,40],70:[2,40],71:[2,40],73:[2,40],75:[2,40],76:[2,40]},{5:[2,80],11:[2,80],13:[2,80],14:[2,80],15:[2,80],17:[2,80],19:[2,80],21:[2,80],23:[2,80],31:[2,80],32:[2,80],37:[2,80],38:[2,80],45:[2,80],48:[2,80],57:[2,80],61:[2,80],62:[2,80],63:[2,80],64:[2,80],65:[2,80],66:[2,80],70:[2,80],71:[2,80],73:[2,80],75:[2,80],76:[2,80]},{48:[1,134]},{5:[2,82],11:[2,82],13:[2,82],14:[2,82],15:[2,82],17:[2,82],19:[2,82],21:[2,82],23:[2,82],31:[2,82],32:[2,82],37:[2,82],38:[2,82],45:[2,82],48:[2,82],57:[2,82],61:[2,82],62:[2,82],63:[2,82],64:[2,82],65:[2,82],66:[2,82],70:[2,82],71:[2,82],73:[2,82],75:[2,82],76:[2,82]},{22:[1,135]},{5:[2,85],11:[2,85],13:[2,85],14:[2,85],15:[2,85],17:[2,85],19:[2,85],21:[2,85],23:[2,85],31:[2,85],32:[2,85],37:[2,85],38:[2,85],45:[2,85],48:[2,85],57:[2,85],61:[2,85],62:[2,85],63:[2,85],64:[2,85],65:[2,85],66:[2,85],70:[2,85],71:[2,85],73:[2,85],75:[2,85],76:[2,85]},{5:[2,87],11:[2,87],13:[2,87],14:[2,87],15:[2,87],17:[2,87],19:[2,87],21:[2,87],23:[2,87],31:[2,87],32:[2,87],37:[2,87],38:[2,87],45:[2,87],48:[2,87],57:[2,87],61:[2,87],62:[2,87],63:[2,87],64:[2,87],65:[2,87],66:[2,87],70:[2,87],71:[2,87],73:[2,87],75:[2,87],76:[2,87]},{75:[1,136]},{48:[1,137]},{41:[2,89],48:[2,89],57:[2,89],59:[2,89],61:[2,89],62:[2,89],63:[2,89],64:[2,89],65:[2,89],66:[2,89],67:[2,89],68:[2,89],69:[2,89]},{41:[2,97],48:[2,97],57:[2,97],59:[2,97],61:[2,97],62:[2,97],63:[2,97],64:[2,97],65:[2,97],66:[2,97],67:[2,97],68:[2,97],69:[2,97]},{41:[2,91],48:[2,91],57:[2,91],59:[2,91],61:[2,91],62:[2,91],63:[2,91],64:[2,91],65:[2,91],66:[2,91],67:[2,91],68:[2,91],69:[2,91],81:[2,91]},{41:[2,93],48:[2,93],57:[2,93],59:[2,93],61:[2,93],62:[2,93],63:[2,93],64:[2,93],65:[2,93],66:[2,93],67:[2,93],68:[2,93],69:[2,93],81:[2,93]},{5:[2,41],11:[2,41],13:[2,41],14:[2,41],15:[2,41],17:[2,41],19:[2,41],21:[2,41],23:[2,41],31:[2,41],32:[2,41],37:[2,41],38:[2,41],44:[2,41],45:[2,41],46:[2,41],48:[2,41],57:[2,41],61:[2,41],62:[2,41],63:[2,41],64:[2,41],65:[2,41],66:[2,41],70:[2,41],71:[2,41],73:[2,41],75:[2,41],76:[2,41]},{5:[2,67],11:[2,67],13:[2,67],14:[2,67],15:[2,67],17:[2,67],19:[2,67],21:[2,67],23:[2,67],31:[2,67],32:[2,67],37:[2,67],38:[2,67],44:[2,67],45:[2,67],46:[2,67],48:[2,67],57:[2,67],61:[2,67],62:[2,67],63:[2,67],64:[2,67],65:[2,67],66:[2,67],70:[2,67],71:[2,67],73:[2,67],75:[2,67],76:[2,67]},{41:[2,50],57:[2,50],59:[2,50],61:[2,50],62:[2,50],63:[2,50],64:[2,50],65:[2,50],66:[2,50],67:[2,50],68:[2,50],69:[2,50]},{5:[2,44],11:[2,44],13:[2,44],14:[2,44],15:[2,44],17:[2,44],19:[2,44],21:[2,44],23:[2,44],31:[2,44],32:[2,44],37:[2,44],38:[2,44],45:[2,44],48:[2,44],57:[2,44],61:[2,44],62:[2,44],63:[2,44],64:[2,44],65:[2,44],66:[2,44],70:[2,44],71:[2,44],73:[2,44],75:[2,44],76:[2,44]},{44:[2,43],46:[2,43]},{5:[2,79],11:[2,79],13:[2,79],14:[2,79],15:[2,79],17:[2,79],19:[2,79],21:[2,79],23:[2,79],31:[2,79],32:[2,79],37:[2,79],38:[2,79],40:138,45:[2,79],48:[2,79],57:[2,79],59:[1,67],61:[2,79],62:[2,79],63:[2,79],64:[2,79],65:[2,79],66:[2,79],67:[1,64],68:[1,65],69:[1,66],70:[2,79],71:[2,79],73:[2,79],75:[2,79],76:[2,79]},{38:[1,139]},{48:[1,140]},{70:[1,141]},{25:[2,84],71:[2,84]},{5:[2,88],11:[2,88],13:[2,88],14:[2,88],15:[2,88],17:[2,88],19:[2,88],21:[2,88],23:[2,88],31:[2,88],32:[2,88],37:[2,88],38:[2,88],45:[2,88],48:[2,88],57:[2,88],61:[2,88],62:[2,88],63:[2,88],64:[2,88],65:[2,88],66:[2,88],70:[2,88],71:[2,88],73:[2,88],75:[2,88],76:[2,88]},{41:[2,48],57:[2,48],59:[2,48],61:[2,48],62:[2,48],63:[2,48],64:[2,48],65:[2,48],66:[2,48],67:[2,48],68:[2,48],69:[2,48]},{5:[2,45],11:[2,45],13:[2,45],14:[2,45],15:[2,45],17:[2,45],19:[2,45],21:[2,45],23:[2,45],31:[2,45],32:[2,45],37:[2,45],38:[2,45],45:[2,45],48:[2,45],57:[2,45],61:[2,45],62:[2,45],63:[2,45],64:[2,45],65:[2,45],66:[2,45],70:[2,45],71:[2,45],73:[2,45],75:[2,45],76:[2,45]},{48:[1,142],49:143,76:[1,57]},{41:[2,56],57:[2,56],59:[2,56],61:[2,56],62:[2,56],63:[2,56],64:[2,56],65:[2,56],66:[2,56],67:[2,56],68:[2,56],69:[2,56]},{5:[2,81],11:[2,81],13:[2,81],14:[2,81],15:[2,81],17:[2,81],19:[2,81],21:[2,81],23:[2,81],31:[2,81],32:[2,81],37:[2,81],38:[2,81],45:[2,81],48:[2,81],57:[2,81],61:[2,81],62:[2,81],63:[2,81],64:[2,81],65:[2,81],66:[2,81],70:[2,81],71:[2,81],73:[2,81],75:[2,81],76:[2,81]},{41:[2,51],57:[2,51],59:[2,51],61:[2,51],62:[2,51],63:[2,51],64:[2,51],65:[2,51],66:[2,51],67:[2,51],68:[2,51],69:[2,51]},{41:[2,53],48:[1,144],57:[2,53],59:[2,53],61:[2,53],62:[2,53],63:[2,53],64:[2,53],65:[2,53],66:[2,53],67:[2,53],68:[2,53],69:[2,53]},{50:[1,145]},{48:[1,146]},{41:[2,54],57:[2,54],59:[2,54],61:[2,54],62:[2,54],63:[2,54],64:[2,54],65:[2,54],66:[2,54],67:[2,54],68:[2,54],69:[2,54]}], +defaultActions: {10:[2,1]}, +parseError: function parseError(str, hash) { + if (hash.recoverable) { + this.trace(str); + } else { + throw new Error(str); + } +}, +parse: function parse(input) { + var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = '', yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1; + var args = lstack.slice.call(arguments, 1); + this.lexer.setInput(input); + this.lexer.yy = this.yy; + this.yy.lexer = this.lexer; + this.yy.parser = this; + if (typeof this.lexer.yylloc == 'undefined') { + this.lexer.yylloc = {}; + } + var yyloc = this.lexer.yylloc; + lstack.push(yyloc); + var ranges = this.lexer.options && this.lexer.options.ranges; + if (typeof this.yy.parseError === 'function') { + this.parseError = this.yy.parseError; + } else { + this.parseError = Object.getPrototypeOf(this).parseError; + } + function popStack(n) { + stack.length = stack.length - 2 * n; + vstack.length = vstack.length - n; + lstack.length = lstack.length - n; + } + function lex() { + var token; + token = self.lexer.lex() || EOF; + if (typeof token !== 'number') { + token = self.symbols_[token] || token; + } + return token; + } + var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected; + while (true) { + state = stack[stack.length - 1]; + if (this.defaultActions[state]) { + action = this.defaultActions[state]; + } else { + if (symbol === null || typeof symbol == 'undefined') { + symbol = lex(); + } + action = table[state] && table[state][symbol]; + } + if (typeof action === 'undefined' || !action.length || !action[0]) { + var errStr = ''; + expected = []; + for (p in table[state]) { + if (this.terminals_[p] && p > TERROR) { + expected.push('\'' + this.terminals_[p] + '\''); + } + } + if (this.lexer.showPosition) { + errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + this.lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\''; + } else { + errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\''); + } + this.parseError(errStr, { + text: this.lexer.match, + token: this.terminals_[symbol] || symbol, + line: this.lexer.yylineno, + loc: yyloc, + expected: expected + }); + } + if (action[0] instanceof Array && action.length > 1) { + throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol); + } + switch (action[0]) { + case 1: + stack.push(symbol); + vstack.push(this.lexer.yytext); + lstack.push(this.lexer.yylloc); + stack.push(action[1]); + symbol = null; + if (!preErrorSymbol) { + yyleng = this.lexer.yyleng; + yytext = this.lexer.yytext; + yylineno = this.lexer.yylineno; + yyloc = this.lexer.yylloc; + if (recovering > 0) { + recovering--; + } + } else { + symbol = preErrorSymbol; + preErrorSymbol = null; + } + break; + case 2: + len = this.productions_[action[1]][1]; + yyval.$ = vstack[vstack.length - len]; + yyval._$ = { + first_line: lstack[lstack.length - (len || 1)].first_line, + last_line: lstack[lstack.length - 1].last_line, + first_column: lstack[lstack.length - (len || 1)].first_column, + last_column: lstack[lstack.length - 1].last_column + }; + if (ranges) { + yyval._$.range = [ + lstack[lstack.length - (len || 1)].range[0], + lstack[lstack.length - 1].range[1] + ]; + } + r = this.performAction.apply(yyval, [ + yytext, + yyleng, + yylineno, + this.yy, + action[1], + vstack, + lstack + ].concat(args)); + if (typeof r !== 'undefined') { + return r; + } + if (len) { + stack = stack.slice(0, -1 * len * 2); + vstack = vstack.slice(0, -1 * len); + lstack = lstack.slice(0, -1 * len); + } + stack.push(this.productions_[action[1]][0]); + vstack.push(yyval.$); + lstack.push(yyval._$); + newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; + stack.push(newState); + break; + case 3: + return true; + } + } + return true; +}}; + + Vex.L("Starting parser."); +/* generated by jison-lex 0.2.1 */ +var lexer = (function(){ +var lexer = { + +EOF:1, + +parseError:function parseError(str, hash) { + if (this.yy.parser) { + this.yy.parser.parseError(str, hash); + } else { + throw new Error(str); + } + }, + +// resets the lexer, sets new input +setInput:function (input) { + this._input = input; + this._more = this._backtrack = this.done = false; + this.yylineno = this.yyleng = 0; + this.yytext = this.matched = this.match = ''; + this.conditionStack = ['INITIAL']; + this.yylloc = { + first_line: 1, + first_column: 0, + last_line: 1, + last_column: 0 + }; + if (this.options.ranges) { + this.yylloc.range = [0,0]; + } + this.offset = 0; + return this; + }, + +// consumes and returns one char from the input +input:function () { + var ch = this._input[0]; + this.yytext += ch; + this.yyleng++; + this.offset++; + this.match += ch; + this.matched += ch; + var lines = ch.match(/(?:\r\n?|\n).*/g); + if (lines) { + this.yylineno++; + this.yylloc.last_line++; + } else { + this.yylloc.last_column++; + } + if (this.options.ranges) { + this.yylloc.range[1]++; + } + + this._input = this._input.slice(1); + return ch; + }, + +// unshifts one char (or a string) into the input +unput:function (ch) { + var len = ch.length; + var lines = ch.split(/(?:\r\n?|\n)/g); + + this._input = ch + this._input; + this.yytext = this.yytext.substr(0, this.yytext.length - len - 1); + //this.yyleng -= len; + this.offset -= len; + var oldLines = this.match.split(/(?:\r\n?|\n)/g); + this.match = this.match.substr(0, this.match.length - 1); + this.matched = this.matched.substr(0, this.matched.length - 1); + + if (lines.length - 1) { + this.yylineno -= lines.length - 1; + } + var r = this.yylloc.range; + + this.yylloc = { + first_line: this.yylloc.first_line, + last_line: this.yylineno + 1, + first_column: this.yylloc.first_column, + last_column: lines ? + (lines.length === oldLines.length ? this.yylloc.first_column : 0) + + oldLines[oldLines.length - lines.length].length - lines[0].length : + this.yylloc.first_column - len + }; + + if (this.options.ranges) { + this.yylloc.range = [r[0], r[0] + this.yyleng - len]; + } + this.yyleng = this.yytext.length; + return this; + }, + +// When called from action, caches matched text and appends it on next action +more:function () { + this._more = true; + return this; + }, + +// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead. +reject:function () { + if (this.options.backtrack_lexer) { + this._backtrack = true; + } else { + return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n' + this.showPosition(), { + text: "", + token: null, + line: this.yylineno + }); + + } + return this; + }, + +// retain first n characters of the match +less:function (n) { + this.unput(this.match.slice(n)); + }, + +// displays already matched input, i.e. for error messages +pastInput:function () { + var past = this.matched.substr(0, this.matched.length - this.match.length); + return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); + }, + +// displays upcoming input, i.e. for error messages +upcomingInput:function () { + var next = this.match; + if (next.length < 20) { + next += this._input.substr(0, 20-next.length); + } + return (next.substr(0,20) + (next.length > 20 ? '...' : '')).replace(/\n/g, ""); + }, + +// displays the character position where the lexing error occurred, i.e. for error messages +showPosition:function () { + var pre = this.pastInput(); + var c = new Array(pre.length + 1).join("-"); + return pre + this.upcomingInput() + "\n" + c + "^"; + }, + +// test the lexed token: return FALSE when not a match, otherwise return token +test_match:function (match, indexed_rule) { + var token, + lines, + backup; + + if (this.options.backtrack_lexer) { + // save context + backup = { + yylineno: this.yylineno, + yylloc: { + first_line: this.yylloc.first_line, + last_line: this.last_line, + first_column: this.yylloc.first_column, + last_column: this.yylloc.last_column + }, + yytext: this.yytext, + match: this.match, + matches: this.matches, + matched: this.matched, + yyleng: this.yyleng, + offset: this.offset, + _more: this._more, + _input: this._input, + yy: this.yy, + conditionStack: this.conditionStack.slice(0), + done: this.done + }; + if (this.options.ranges) { + backup.yylloc.range = this.yylloc.range.slice(0); + } + } + + lines = match[0].match(/(?:\r\n?|\n).*/g); + if (lines) { + this.yylineno += lines.length; + } + this.yylloc = { + first_line: this.yylloc.last_line, + last_line: this.yylineno + 1, + first_column: this.yylloc.last_column, + last_column: lines ? + lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length : + this.yylloc.last_column + match[0].length + }; + this.yytext += match[0]; + this.match += match[0]; + this.matches = match; + this.yyleng = this.yytext.length; + if (this.options.ranges) { + this.yylloc.range = [this.offset, this.offset += this.yyleng]; + } + this._more = false; + this._backtrack = false; + this._input = this._input.slice(match[0].length); + this.matched += match[0]; + token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1]); + if (this.done && this._input) { + this.done = false; + } + if (token) { + return token; + } else if (this._backtrack) { + // recover context + for (var k in backup) { + this[k] = backup[k]; + } + return false; // rule action called reject() implying the next rule should be tested instead. + } + return false; + }, + +// return next match in input +next:function () { + if (this.done) { + return this.EOF; + } + if (!this._input) { + this.done = true; + } + + var token, + match, + tempMatch, + index; + if (!this._more) { + this.yytext = ''; + this.match = ''; + } + var rules = this._currentRules(); + for (var i = 0; i < rules.length; i++) { + tempMatch = this._input.match(this.rules[rules[i]]); + if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { + match = tempMatch; + index = i; + if (this.options.backtrack_lexer) { + token = this.test_match(tempMatch, rules[i]); + if (token !== false) { + return token; + } else if (this._backtrack) { + match = false; + continue; // rule action called reject() implying a rule MISmatch. + } else { + // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) + return false; + } + } else if (!this.options.flex) { + break; + } + } + } + if (match) { + token = this.test_match(match, rules[index]); + if (token !== false) { + return token; + } + // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) + return false; + } + if (this._input === "") { + return this.EOF; + } else { + return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), { + text: "", + token: null, + line: this.yylineno + }); + } + }, + +// return next match that has a token +lex:function lex() { + var r = this.next(); + if (r) { + return r; + } else { + return this.lex(); + } + }, + +// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack) +begin:function begin(condition) { + this.conditionStack.push(condition); + }, + +// pop the previously active lexer condition state off the condition stack +popState:function popState() { + var n = this.conditionStack.length - 1; + if (n > 0) { + return this.conditionStack.pop(); + } else { + return this.conditionStack[0]; + } + }, + +// produce the lexer rule set which is active for the currently active lexer condition state +_currentRules:function _currentRules() { + if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) { + return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules; + } else { + return this.conditions["INITIAL"].rules; + } + }, + +// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available +topState:function topState(n) { + n = this.conditionStack.length - 1 - Math.abs(n || 0); + if (n >= 0) { + return this.conditionStack[n]; + } else { + return "INITIAL"; + } + }, + +// alias for begin(condition) +pushState:function pushState(condition) { + this.begin(condition); + }, + +// return the number of states currently on the stack +stateStackSize:function stateStackSize() { + return this.conditionStack.length; + }, +options: {}, +performAction: function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { + +var YYSTATE=YY_START; +switch($avoiding_name_collisions) { +case 0: this.begin('notes'); return 19; +break; +case 1: this.begin('options'); return 13; +break; +case 2: this.begin('options'); return 14; +break; +case 3: this.begin('options'); return 15; +break; +case 4: this.begin('options'); return 11; +break; +case 5: this.begin('text'); return 17; +break; +case 6: this.begin('options'); return 21; +break; +case 7:return 22 +break; +case 8: this.begin('annotations'); return "$" +break; +case 9: this.begin('notes'); return "$" +break; +case 10:return 22 +break; +case 11: this.begin('command'); return "!" +break; +case 12: this.begin('notes'); return "!" +break; +case 13:return 74 +break; +case 14:return 24 +break; +case 15:return 41 +break; +case 16:return '+' +break; +case 17:return 38 +break; +case 18:return 23 +break; +case 19:return 45 +break; +case 20:return 46 +break; +case 21:return 31 +break; +case 22:return 32 +break; +case 23:return 70 +break; +case 24:return 25 +break; +case 25:return 37 +break; +case 26:return 44 +break; +case 27:return 75 +break; +case 28:return 79 +break; +case 29:return 65 +break; +case 30:return 62 +break; +case 31:return 57 +break; +case 32:return 66 +break; +case 33:return 63 +break; +case 34:return 64 +break; +case 35:return 61 +break; +case 36:return 50 +break; +case 37:return 67 +break; +case 38:return 68 +break; +case 39:return 69 +break; +case 40:return 59 +break; +case 41:return 48 +break; +case 42:return 58 +break; +case 43:return 56 +break; +case 44:return 57 +break; +case 45:return 59 +break; +case 46:return 60 +break; +case 47:return 76 +break; +case 48:return 80 +break; +case 49:return 81 +break; +case 50: this.begin('INITIAL'); +break; +case 51:/* skip whitespace */ +break; +case 52:return 5 +break; +case 53:return 'INVALID' +break; +} +}, +rules: [/^(?:notes\b)/,/^(?:tabstave\b)/,/^(?:stave\b)/,/^(?:voice\b)/,/^(?:options\b)/,/^(?:text\b)/,/^(?:slur\b)/,/^(?:[^\s=]+)/,/^(?:[$])/,/^(?:[$])/,/^(?:[^,$]+)/,/^(?:[!])/,/^(?:[!])/,/^(?:[^!]+)/,/^(?:[^,\r\n]+)/,/^(?:\/)/,/^(?:\+)/,/^(?::)/,/^(?:=)/,/^(?:\()/,/^(?:\))/,/^(?:\[)/,/^(?:\])/,/^(?:\^)/,/^(?:,)/,/^(?:\|)/,/^(?:\.)/,/^(?:#)/,/^(?:@)/,/^(?:[b])/,/^(?:[s])/,/^(?:[h])/,/^(?:[p])/,/^(?:[t])/,/^(?:[T])/,/^(?:[-])/,/^(?:[_])/,/^(?:[v])/,/^(?:[V])/,/^(?:[u])/,/^(?:[d])/,/^(?:[0-9]+)/,/^(?:[q])/,/^(?:[w])/,/^(?:[h])/,/^(?:[d])/,/^(?:[S])/,/^(?:[A-GXLR])/,/^(?:[n])/,/^(?:[~])/,/^(?:[\r\n]+)/,/^(?:\s+)/,/^(?:$)/,/^(?:.)/], +conditions: {"notes":{"rules":[8,11,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53],"inclusive":true},"text":{"rules":[14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,41,42,43,44,45,50,51,52,53],"inclusive":true},"slur":{"rules":[15,16,17,18,19,20,21,22,23,24,25,26,27,28,50,51,52,53],"inclusive":true},"annotations":{"rules":[9,10,15,16,17,18,19,20,21,22,23,24,25,26,27,28,50,51,52,53],"inclusive":true},"options":{"rules":[7,15,16,17,18,19,20,21,22,23,24,25,26,27,28,50,51,52,53],"inclusive":true},"command":{"rules":[12,13,15,16,17,18,19,20,21,22,23,24,25,26,27,28,50,51,52,53],"inclusive":true},"INITIAL":{"rules":[0,1,2,3,4,5,6,7,15,16,17,18,19,20,21,22,23,24,25,26,27,28,50,51,52,53],"inclusive":true}} +}; +return lexer; +})(); +parser.lexer = lexer; +function Parser () { + this.yy = {}; +} +Parser.prototype = parser;parser.Parser = Parser; +return new Parser; +})(); + + +if (typeof require !== 'undefined' && typeof exports !== 'undefined') { +exports.parser = vextab_parser; +exports.Parser = vextab_parser.Parser; +exports.parse = function () { return vextab_parser.parse.apply(vextab_parser, arguments); }; +exports.main = function commonjsMain(args) { + if (!args[1]) { + console.log('Usage: '+args[0]+' FILE'); + process.exit(1); + } + var source = require('fs').readFileSync(require('path').normalize(args[1]), "utf8"); + return exports.parser.parse(source); +}; +if (typeof module !== 'undefined' && require.main === module) { + exports.main(process.argv.slice(1)); +} +} \ No newline at end of file