PageRenderTime 82ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/abc2pd.js

https://code.google.com/p/sing/
JavaScript | 5260 lines | 4892 code | 72 blank | 296 comment | 236 complexity | 1c4a52606e3da5f826240f50381b4a2e MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. http://www.JSON.org/json2.js
  3. 2011-02-23
  4. Public Domain.
  5. NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
  6. See http://www.JSON.org/js.html
  7. This code should be minified before deployment.
  8. See http://javascript.crockford.com/jsmin.html
  9. USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
  10. NOT CONTROL.
  11. This file creates a global JSON object containing two methods: stringify
  12. and parse.
  13. JSON.stringify(value, replacer, space)
  14. value any JavaScript value, usually an object or array.
  15. replacer an optional parameter that determines how object
  16. values are stringified for objects. It can be a
  17. function or an array of strings.
  18. space an optional parameter that specifies the indentation
  19. of nested structures. If it is omitted, the text will
  20. be packed without extra whitespace. If it is a number,
  21. it will specify the number of spaces to indent at each
  22. level. If it is a string (such as '\t' or ' '),
  23. it contains the characters used to indent at each level.
  24. This method produces a JSON text from a JavaScript value.
  25. When an object value is found, if the object contains a toJSON
  26. method, its toJSON method will be called and the result will be
  27. stringified. A toJSON method does not serialize: it returns the
  28. value represented by the name/value pair that should be serialized,
  29. or undefined if nothing should be serialized. The toJSON method
  30. will be passed the key associated with the value, and this will be
  31. bound to the value
  32. For example, this would serialize Dates as ISO strings.
  33. Date.prototype.toJSON = function (key) {
  34. function f(n) {
  35. // Format integers to have at least two digits.
  36. return n < 10 ? '0' + n : n;
  37. }
  38. return this.getUTCFullYear() + '-' +
  39. f(this.getUTCMonth() + 1) + '-' +
  40. f(this.getUTCDate()) + 'T' +
  41. f(this.getUTCHours()) + ':' +
  42. f(this.getUTCMinutes()) + ':' +
  43. f(this.getUTCSeconds()) + 'Z';
  44. };
  45. You can provide an optional replacer method. It will be passed the
  46. key and value of each member, with this bound to the containing
  47. object. The value that is returned from your method will be
  48. serialized. If your method returns undefined, then the member will
  49. be excluded from the serialization.
  50. If the replacer parameter is an array of strings, then it will be
  51. used to select the members to be serialized. It filters the results
  52. such that only members with keys listed in the replacer array are
  53. stringified.
  54. Values that do not have JSON representations, such as undefined or
  55. functions, will not be serialized. Such values in objects will be
  56. dropped; in arrays they will be replaced with null. You can use
  57. a replacer function to replace those with JSON values.
  58. JSON.stringify(undefined) returns undefined.
  59. The optional space parameter produces a stringification of the
  60. value that is filled with line breaks and indentation to make it
  61. easier to read.
  62. If the space parameter is a non-empty string, then that string will
  63. be used for indentation. If the space parameter is a number, then
  64. the indentation will be that many spaces.
  65. Example:
  66. text = JSON.stringify(['e', {pluribus: 'unum'}]);
  67. // text is '["e",{"pluribus":"unum"}]'
  68. text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
  69. // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
  70. text = JSON.stringify([new Date()], function (key, value) {
  71. return this[key] instanceof Date ?
  72. 'Date(' + this[key] + ')' : value;
  73. });
  74. // text is '["Date(---current time---)"]'
  75. JSON.parse(text, reviver)
  76. This method parses a JSON text to produce an object or array.
  77. It can throw a SyntaxError exception.
  78. The optional reviver parameter is a function that can filter and
  79. transform the results. It receives each of the keys and values,
  80. and its return value is used instead of the original value.
  81. If it returns what it received, then the structure is not modified.
  82. If it returns undefined then the member is deleted.
  83. Example:
  84. // Parse the text. Values that look like ISO date strings will
  85. // be converted to Date objects.
  86. myData = JSON.parse(text, function (key, value) {
  87. var a;
  88. if (typeof value === 'string') {
  89. a =
  90. /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
  91. if (a) {
  92. return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
  93. +a[5], +a[6]));
  94. }
  95. }
  96. return value;
  97. });
  98. myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
  99. var d;
  100. if (typeof value === 'string' &&
  101. value.slice(0, 5) === 'Date(' &&
  102. value.slice(-1) === ')') {
  103. d = new Date(value.slice(5, -1));
  104. if (d) {
  105. return d;
  106. }
  107. }
  108. return value;
  109. });
  110. This is a reference implementation. You are free to copy, modify, or
  111. redistribute.
  112. */
  113. /*jslint evil: true, strict: false, regexp: false */
  114. /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
  115. call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
  116. getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
  117. lastIndex, length, parse, prototype, push, replace, slice, stringify,
  118. test, toJSON, toString, valueOf
  119. */
  120. // Create a JSON object only if one does not already exist. We create the
  121. // methods in a closure to avoid creating global variables.
  122. var JSON;
  123. if (!JSON) {
  124. JSON = {};
  125. }
  126. (function () {
  127. "use strict";
  128. function f(n) {
  129. // Format integers to have at least two digits.
  130. return n < 10 ? '0' + n : n;
  131. }
  132. if (typeof Date.prototype.toJSON !== 'function') {
  133. Date.prototype.toJSON = function (key) {
  134. return isFinite(this.valueOf()) ?
  135. this.getUTCFullYear() + '-' +
  136. f(this.getUTCMonth() + 1) + '-' +
  137. f(this.getUTCDate()) + 'T' +
  138. f(this.getUTCHours()) + ':' +
  139. f(this.getUTCMinutes()) + ':' +
  140. f(this.getUTCSeconds()) + 'Z' : null;
  141. };
  142. String.prototype.toJSON =
  143. Number.prototype.toJSON =
  144. Boolean.prototype.toJSON = function (key) {
  145. return this.valueOf();
  146. };
  147. }
  148. var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
  149. escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
  150. gap,
  151. indent,
  152. meta = { // table of character substitutions
  153. '\b': '\\b',
  154. '\t': '\\t',
  155. '\n': '\\n',
  156. '\f': '\\f',
  157. '\r': '\\r',
  158. '"' : '\\"',
  159. '\\': '\\\\'
  160. },
  161. rep;
  162. function quote(string) {
  163. // If the string contains no control characters, no quote characters, and no
  164. // backslash characters, then we can safely slap some quotes around it.
  165. // Otherwise we must also replace the offending characters with safe escape
  166. // sequences.
  167. escapable.lastIndex = 0;
  168. return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
  169. var c = meta[a];
  170. return typeof c === 'string' ? c :
  171. '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
  172. }) + '"' : '"' + string + '"';
  173. }
  174. function str(key, holder) {
  175. // Produce a string from holder[key].
  176. var i, // The loop counter.
  177. k, // The member key.
  178. v, // The member value.
  179. length,
  180. mind = gap,
  181. partial,
  182. value = holder[key];
  183. // If the value has a toJSON method, call it to obtain a replacement value.
  184. if (value && typeof value === 'object' &&
  185. typeof value.toJSON === 'function') {
  186. value = value.toJSON(key);
  187. }
  188. // If we were called with a replacer function, then call the replacer to
  189. // obtain a replacement value.
  190. if (typeof rep === 'function') {
  191. value = rep.call(holder, key, value);
  192. }
  193. // What happens next depends on the value's type.
  194. switch (typeof value) {
  195. case 'string':
  196. return quote(value);
  197. case 'number':
  198. // JSON numbers must be finite. Encode non-finite numbers as null.
  199. return isFinite(value) ? String(value) : 'null';
  200. case 'boolean':
  201. case 'null':
  202. // If the value is a boolean or null, convert it to a string. Note:
  203. // typeof null does not produce 'null'. The case is included here in
  204. // the remote chance that this gets fixed someday.
  205. return String(value);
  206. // If the type is 'object', we might be dealing with an object or an array or
  207. // null.
  208. case 'object':
  209. // Due to a specification blunder in ECMAScript, typeof null is 'object',
  210. // so watch out for that case.
  211. if (!value) {
  212. return 'null';
  213. }
  214. // Make an array to hold the partial results of stringifying this object value.
  215. gap += indent;
  216. partial = [];
  217. // Is the value an array?
  218. if (Object.prototype.toString.apply(value) === '[object Array]') {
  219. // The value is an array. Stringify every element. Use null as a placeholder
  220. // for non-JSON values.
  221. length = value.length;
  222. for (i = 0; i < length; i += 1) {
  223. partial[i] = str(i, value) || 'null';
  224. }
  225. // Join all of the elements together, separated with commas, and wrap them in
  226. // brackets.
  227. v = partial.length === 0 ? '[]' : gap ?
  228. '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
  229. '[' + partial.join(',') + ']';
  230. gap = mind;
  231. return v;
  232. }
  233. // If the replacer is an array, use it to select the members to be stringified.
  234. if (rep && typeof rep === 'object') {
  235. length = rep.length;
  236. for (i = 0; i < length; i += 1) {
  237. if (typeof rep[i] === 'string') {
  238. k = rep[i];
  239. v = str(k, value);
  240. if (v) {
  241. partial.push(quote(k) + (gap ? ': ' : ':') + v);
  242. }
  243. }
  244. }
  245. } else {
  246. // Otherwise, iterate through all of the keys in the object.
  247. for (k in value) {
  248. if (Object.prototype.hasOwnProperty.call(value, k)) {
  249. v = str(k, value);
  250. if (v) {
  251. partial.push(quote(k) + (gap ? ': ' : ':') + v);
  252. }
  253. }
  254. }
  255. }
  256. // Join all of the member texts together, separated with commas,
  257. // and wrap them in braces.
  258. v = partial.length === 0 ? '{}' : gap ?
  259. '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
  260. '{' + partial.join(',') + '}';
  261. gap = mind;
  262. return v;
  263. }
  264. }
  265. // If the JSON object does not yet have a stringify method, give it one.
  266. if (typeof JSON.stringify !== 'function') {
  267. JSON.stringify = function (value, replacer, space) {
  268. // The stringify method takes a value and an optional replacer, and an optional
  269. // space parameter, and returns a JSON text. The replacer can be a function
  270. // that can replace values, or an array of strings that will select the keys.
  271. // A default replacer method can be provided. Use of the space parameter can
  272. // produce text that is more easily readable.
  273. var i;
  274. gap = '';
  275. indent = '';
  276. // If the space parameter is a number, make an indent string containing that
  277. // many spaces.
  278. if (typeof space === 'number') {
  279. for (i = 0; i < space; i += 1) {
  280. indent += ' ';
  281. }
  282. // If the space parameter is a string, it will be used as the indent string.
  283. } else if (typeof space === 'string') {
  284. indent = space;
  285. }
  286. // If there is a replacer, it must be a function or an array.
  287. // Otherwise, throw an error.
  288. rep = replacer;
  289. if (replacer && typeof replacer !== 'function' &&
  290. (typeof replacer !== 'object' ||
  291. typeof replacer.length !== 'number')) {
  292. throw new Error('JSON.stringify');
  293. }
  294. // Make a fake root object containing our value under the key of ''.
  295. // Return the result of stringifying the value.
  296. return str('', {'': value});
  297. };
  298. }
  299. // If the JSON object does not yet have a parse method, give it one.
  300. if (typeof JSON.parse !== 'function') {
  301. JSON.parse = function (text, reviver) {
  302. // The parse method takes a text and an optional reviver function, and returns
  303. // a JavaScript value if the text is a valid JSON text.
  304. var j;
  305. function walk(holder, key) {
  306. // The walk method is used to recursively walk the resulting structure so
  307. // that modifications can be made.
  308. var k, v, value = holder[key];
  309. if (value && typeof value === 'object') {
  310. for (k in value) {
  311. if (Object.prototype.hasOwnProperty.call(value, k)) {
  312. v = walk(value, k);
  313. if (v !== undefined) {
  314. value[k] = v;
  315. } else {
  316. delete value[k];
  317. }
  318. }
  319. }
  320. }
  321. return reviver.call(holder, key, value);
  322. }
  323. // Parsing happens in four stages. In the first stage, we replace certain
  324. // Unicode characters with escape sequences. JavaScript handles many characters
  325. // incorrectly, either silently deleting them, or treating them as line endings.
  326. text = String(text);
  327. cx.lastIndex = 0;
  328. if (cx.test(text)) {
  329. text = text.replace(cx, function (a) {
  330. return '\\u' +
  331. ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
  332. });
  333. }
  334. // In the second stage, we run the text against regular expressions that look
  335. // for non-JSON patterns. We are especially concerned with '()' and 'new'
  336. // because they can cause invocation, and '=' because it can cause mutation.
  337. // But just to be safe, we want to reject all unexpected forms.
  338. // We split the second stage into 4 regexp operations in order to work around
  339. // crippling inefficiencies in IE's and Safari's regexp engines. First we
  340. // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
  341. // replace all simple value tokens with ']' characters. Third, we delete all
  342. // open brackets that follow a colon or comma or that begin the text. Finally,
  343. // we look to see that the remaining characters are only whitespace or ']' or
  344. // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
  345. if (/^[\],:{}\s]*$/
  346. .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
  347. .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
  348. .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
  349. // In the third stage we use the eval function to compile the text into a
  350. // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
  351. // in JavaScript: it can begin a block or an object literal. We wrap the text
  352. // in parens to eliminate the ambiguity.
  353. j = eval('(' + text + ')');
  354. // In the optional fourth stage, we recursively walk the new structure, passing
  355. // each name/value pair to a reviver function for possible transformation.
  356. return typeof reviver === 'function' ?
  357. walk({'': j}, '') : j;
  358. }
  359. // If the text is not JSON parseable, then a SyntaxError is thrown.
  360. throw new SyntaxError('JSON.parse');
  361. };
  362. }
  363. }());
  364. /*extern document */
  365. /*global Ajax */
  366. // A few useful prototype elements so we don't have to load the whole thing.
  367. Object.clone = function(source) {
  368. var destination = {};
  369. for (var property in source)
  370. destination[property] = source[property];
  371. return destination;
  372. };
  373. Object.keys = function(object) {
  374. var keys = [];
  375. for (var property in object)
  376. if (object.hasOwnProperty(property))
  377. keys.push(property);
  378. return keys;
  379. };
  380. Array.prototype.clone = function(source) {
  381. var destination = [];
  382. for (var i = 0; i < source.length; i++)
  383. destination.push(source[i]);
  384. return destination;
  385. };
  386. String.prototype.gsub = function(pattern, replacement) {
  387. return this.split(pattern).join(replacement);
  388. };
  389. String.prototype.strip = function() {
  390. return this.replace(/^\s+/, '').replace(/\s+$/, '');
  391. };
  392. String.prototype.startsWith = function(pattern) {
  393. return this.indexOf(pattern) === 0;
  394. };
  395. String.prototype.endsWith = function(pattern) {
  396. var d = this.length - pattern.length;
  397. return d >= 0 && this.lastIndexOf(pattern) === d;
  398. };
  399. Array.prototype.each = function(iterator, context) {
  400. for (var i = 0, length = this.length; i < length; i++)
  401. iterator.apply(context, [this[i],i]);
  402. };
  403. Array.prototype.last = function() {
  404. if (this.length === 0)
  405. return null;
  406. return this[this.length-1];
  407. };
  408. Array.prototype.compact = function() {
  409. var output = [];
  410. for (var i = 0; i < this.length; i++) {
  411. if (this[i])
  412. output.push(this[i]);
  413. }
  414. return output;
  415. };
  416. Array.prototype.detect = function(iterator) {
  417. for (var i = 0; i < this.length; i++) {
  418. if (iterator(this[i]))
  419. return true;
  420. }
  421. return false;
  422. };
  423. // abc_parse_header.js: parses a the header fields from a string representing ABC Music Notation into a usable internal structure.
  424. // Copyright (C) 2010 Paul Rosen (paul at paulrosen dot net)
  425. //
  426. // This program is free software: you can redistribute it and/or modify
  427. // it under the terms of the GNU General Public License as published by
  428. // the Free Software Foundation, either version 3 of the License, or
  429. // (at your option) any later version.
  430. //
  431. // This program is distributed in the hope that it will be useful,
  432. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  433. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  434. // GNU General Public License for more details.
  435. //
  436. // You should have received a copy of the GNU General Public License
  437. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  438. /*extern AbcParseHeader */
  439. function AbcParseHeader(tokenizer, warn, multilineVars, tune) {
  440. var key1sharp = {acc: 'sharp', note: 'f'};
  441. var key2sharp = {acc: 'sharp', note: 'c'};
  442. var key3sharp = {acc: 'sharp', note: 'g'};
  443. var key4sharp = {acc: 'sharp', note: 'd'};
  444. var key5sharp = {acc: 'sharp', note: 'A'};
  445. var key6sharp = {acc: 'sharp', note: 'e'};
  446. var key7sharp = {acc: 'sharp', note: 'B'};
  447. var key1flat = {acc: 'flat', note: 'B'};
  448. var key2flat = {acc: 'flat', note: 'e'};
  449. var key3flat = {acc: 'flat', note: 'A'};
  450. var key4flat = {acc: 'flat', note: 'd'};
  451. var key5flat = {acc: 'flat', note: 'G'};
  452. var key6flat = {acc: 'flat', note: 'c'};
  453. var key7flat = {acc: 'flat', note: 'f'};
  454. var keys = {
  455. 'C#': [ key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp ],
  456. 'A#m': [ key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp ],
  457. 'G#Mix': [ key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp ],
  458. 'D#Dor': [ key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp ],
  459. 'E#Phr': [ key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp ],
  460. 'F#Lyd': [ key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp ],
  461. 'B#Loc': [ key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp ],
  462. 'F#': [ key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp ],
  463. 'D#m': [ key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp ],
  464. 'C#Mix': [ key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp ],
  465. 'G#Dor': [ key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp ],
  466. 'A#Phr': [ key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp ],
  467. 'BLyd': [ key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp ],
  468. 'E#Loc': [ key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp ],
  469. 'B': [ key1sharp, key2sharp, key3sharp, key4sharp, key5sharp ],
  470. 'G#m': [ key1sharp, key2sharp, key3sharp, key4sharp, key5sharp ],
  471. 'F#Mix': [ key1sharp, key2sharp, key3sharp, key4sharp, key5sharp ],
  472. 'C#Dor': [ key1sharp, key2sharp, key3sharp, key4sharp, key5sharp ],
  473. 'D#Phr': [ key1sharp, key2sharp, key3sharp, key4sharp, key5sharp ],
  474. 'ELyd': [ key1sharp, key2sharp, key3sharp, key4sharp, key5sharp ],
  475. 'A#Loc': [ key1sharp, key2sharp, key3sharp, key4sharp, key5sharp ],
  476. 'E': [ key1sharp, key2sharp, key3sharp, key4sharp ],
  477. 'C#m': [ key1sharp, key2sharp, key3sharp, key4sharp ],
  478. 'BMix': [ key1sharp, key2sharp, key3sharp, key4sharp ],
  479. 'F#Dor': [ key1sharp, key2sharp, key3sharp, key4sharp ],
  480. 'G#Phr': [ key1sharp, key2sharp, key3sharp, key4sharp ],
  481. 'ALyd': [ key1sharp, key2sharp, key3sharp, key4sharp ],
  482. 'D#Loc': [ key1sharp, key2sharp, key3sharp, key4sharp ],
  483. 'A': [ key1sharp, key2sharp, key3sharp ],
  484. 'F#m': [ key1sharp, key2sharp, key3sharp ],
  485. 'EMix': [ key1sharp, key2sharp, key3sharp ],
  486. 'BDor': [ key1sharp, key2sharp, key3sharp ],
  487. 'C#Phr': [ key1sharp, key2sharp, key3sharp ],
  488. 'DLyd': [ key1sharp, key2sharp, key3sharp ],
  489. 'G#Loc': [ key1sharp, key2sharp, key3sharp ],
  490. 'D': [ key1sharp, key2sharp ],
  491. 'Bm': [ key1sharp, key2sharp ],
  492. 'AMix': [ key1sharp, key2sharp ],
  493. 'EDor': [ key1sharp, key2sharp ],
  494. 'F#Phr': [ key1sharp, key2sharp ],
  495. 'GLyd': [ key1sharp, key2sharp ],
  496. 'C#Loc': [ key1sharp, key2sharp ],
  497. 'G': [ key1sharp ],
  498. 'Em': [ key1sharp ],
  499. 'DMix': [ key1sharp ],
  500. 'ADor': [ key1sharp ],
  501. 'BPhr': [ key1sharp ],
  502. 'CLyd': [ key1sharp ],
  503. 'F#Loc': [ key1sharp ],
  504. 'C': [],
  505. 'Am': [],
  506. 'GMix': [],
  507. 'DDor': [],
  508. 'EPhr': [],
  509. 'FLyd': [],
  510. 'BLoc': [],
  511. 'F': [ key1flat ],
  512. 'Dm': [ key1flat ],
  513. 'CMix': [ key1flat ],
  514. 'GDor': [ key1flat ],
  515. 'APhr': [ key1flat ],
  516. 'BbLyd': [ key1flat ],
  517. 'ELoc': [ key1flat ],
  518. 'Bb': [ key1flat, key2flat ],
  519. 'Gm': [ key1flat, key2flat ],
  520. 'FMix': [ key1flat, key2flat ],
  521. 'CDor': [ key1flat, key2flat ],
  522. 'DPhr': [ key1flat, key2flat ],
  523. 'EbLyd': [ key1flat, key2flat ],
  524. 'ALoc': [ key1flat, key2flat ],
  525. 'Eb': [ key1flat, key2flat, key3flat ],
  526. 'Cm': [ key1flat, key2flat, key3flat ],
  527. 'BbMix': [ key1flat, key2flat, key3flat ],
  528. 'FDor': [ key1flat, key2flat, key3flat ],
  529. 'GPhr': [ key1flat, key2flat, key3flat ],
  530. 'AbLyd': [ key1flat, key2flat, key3flat ],
  531. 'DLoc': [ key1flat, key2flat, key3flat ],
  532. 'Ab': [ key1flat, key2flat, key3flat, key4flat ],
  533. 'Fm': [ key1flat, key2flat, key3flat, key4flat ],
  534. 'EbMix': [ key1flat, key2flat, key3flat, key4flat ],
  535. 'BbDor': [ key1flat, key2flat, key3flat, key4flat ],
  536. 'CPhr': [ key1flat, key2flat, key3flat, key4flat ],
  537. 'DbLyd': [ key1flat, key2flat, key3flat, key4flat ],
  538. 'GLoc': [ key1flat, key2flat, key3flat, key4flat ],
  539. 'Db': [ key1flat, key2flat, key3flat, key4flat, key5flat ],
  540. 'Bbm': [ key1flat, key2flat, key3flat, key4flat, key5flat ],
  541. 'AbMix': [ key1flat, key2flat, key3flat, key4flat, key5flat ],
  542. 'EbDor': [ key1flat, key2flat, key3flat, key4flat, key5flat ],
  543. 'FPhr': [ key1flat, key2flat, key3flat, key4flat, key5flat ],
  544. 'GbLyd': [ key1flat, key2flat, key3flat, key4flat, key5flat ],
  545. 'CLoc': [ key1flat, key2flat, key3flat, key4flat, key5flat ],
  546. 'Gb': [ key1flat, key2flat, key3flat, key4flat, key5flat, key6flat ],
  547. 'Ebm': [ key1flat, key2flat, key3flat, key4flat, key5flat, key6flat ],
  548. 'DbMix': [ key1flat, key2flat, key3flat, key4flat, key5flat, key6flat ],
  549. 'AbDor': [ key1flat, key2flat, key3flat, key4flat, key5flat, key6flat ],
  550. 'BbPhr': [ key1flat, key2flat, key3flat, key4flat, key5flat, key6flat ],
  551. 'CbLyd': [ key1flat, key2flat, key3flat, key4flat, key5flat, key6flat ],
  552. 'FLoc': [ key1flat, key2flat, key3flat, key4flat, key5flat, key6flat ],
  553. 'Cb': [ key1flat, key2flat, key3flat, key4flat, key5flat, key6flat, key7flat ],
  554. 'Abm': [ key1flat, key2flat, key3flat, key4flat, key5flat, key6flat, key7flat ],
  555. 'GbMix': [ key1flat, key2flat, key3flat, key4flat, key5flat, key6flat, key7flat ],
  556. 'DbDor': [ key1flat, key2flat, key3flat, key4flat, key5flat, key6flat, key7flat ],
  557. 'EbPhr': [ key1flat, key2flat, key3flat, key4flat, key5flat, key6flat, key7flat ],
  558. 'FbLyd': [ key1flat, key2flat, key3flat, key4flat, key5flat, key6flat, key7flat ],
  559. 'BbLoc': [ key1flat, key2flat, key3flat, key4flat, key5flat, key6flat, key7flat ],
  560. // The following are not in the 2.0 spec, but seem normal enough.
  561. // TODO-PER: These SOUND the same as what's written, but they aren't right
  562. 'A#': [ key1flat, key2flat ],
  563. 'B#': [],
  564. 'D#': [ key1flat, key2flat, key3flat ],
  565. 'E#': [ key1flat ],
  566. 'G#': [ key1flat, key2flat, key3flat, key4flat ],
  567. 'Gbm': [ key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp ]
  568. };
  569. var calcMiddle = function(clef, oct) {
  570. var mid = 0;
  571. switch(clef) {
  572. case 'treble':
  573. case 'perc':
  574. case 'none':
  575. case 'treble+8':
  576. case 'treble-8':
  577. break;
  578. case 'bass3':
  579. case 'bass':
  580. case 'bass+8':
  581. case 'bass-8':
  582. case 'bass+16':
  583. case 'bass-16':
  584. mid = -12;
  585. break;
  586. case 'tenor':
  587. mid = -8;
  588. break;
  589. case 'alto2':
  590. case 'alto1':
  591. case 'alto':
  592. case 'alto+8':
  593. case 'alto-8':
  594. mid = -6;
  595. break;
  596. }
  597. return mid+oct;
  598. };
  599. this.parseFontChangeLine = function(textstr) {
  600. var textParts = textstr.split('$');
  601. if (textParts.length > 1 && multilineVars.setfont) {
  602. var textarr = [ { text: textParts[0] }];
  603. for (var i = 1; i < textParts.length; i++) {
  604. if (textParts[i].charAt(0) === '0')
  605. textarr.push({ text: textParts[i].substring(1) });
  606. else if (textParts[i].charAt(0) === '1' && multilineVars.setfont[1])
  607. textarr.push({font: multilineVars.setfont[1], text: textParts[i].substring(1) });
  608. else if (textParts[i].charAt(0) === '2' && multilineVars.setfont[2])
  609. textarr.push({font: multilineVars.setfont[2], text: textParts[i].substring(1) });
  610. else if (textParts[i].charAt(0) === '3' && multilineVars.setfont[3])
  611. textarr.push({font: multilineVars.setfont[3], text: textParts[i].substring(1) });
  612. else if (textParts[i].charAt(0) === '4' && multilineVars.setfont[4])
  613. textarr.push({font: multilineVars.setfont[4], text: textParts[i].substring(1) });
  614. else
  615. textarr[textarr.length-1].text += '$' + textParts[i];
  616. }
  617. if (textarr.length > 1)
  618. return textarr;
  619. }
  620. return textstr;
  621. }
  622. this.deepCopyKey = function(key) {
  623. var ret = { accidentals: [], root: key.root, acc: key.acc, mode: key.mode };
  624. key.accidentals.each(function(k) {
  625. ret.accidentals.push(Object.clone(k));
  626. });
  627. return ret;
  628. };
  629. var pitches = {A: 5, B: 6, C: 0, D: 1, E: 2, F: 3, G: 4, a: 12, b: 13, c: 7, d: 8, e: 9, f: 10, g: 11};
  630. this.addPosToKey = function(clef, key) {
  631. // Shift the key signature from the treble positions to whatever position is needed for the clef.
  632. // This may put the key signature unnaturally high or low, so if it does, then shift it.
  633. var mid = clef.verticalPos;
  634. key.accidentals.each(function(acc) {
  635. var pitch = pitches[acc.note];
  636. pitch = pitch - mid;
  637. acc.verticalPos = pitch;
  638. });
  639. if (mid < -10) {
  640. key.accidentals.each(function(acc) {
  641. acc.verticalPos -= 7;
  642. if (acc.verticalPos >= 11 || (acc.verticalPos === 10 && acc.acc === 'flat'))
  643. acc.verticalPos -= 7;
  644. });
  645. } else if (mid < -4) {
  646. key.accidentals.each(function(acc) {
  647. acc.verticalPos -= 7;
  648. });
  649. } else if (mid >= 7) {
  650. key.accidentals.each(function(acc) {
  651. acc.verticalPos += 7;
  652. });
  653. }
  654. };
  655. this.fixKey = function(clef, key) {
  656. var fixedKey = Object.clone(key);
  657. this.addPosToKey(clef, fixedKey);
  658. return fixedKey;
  659. };
  660. var parseMiddle = function(str) {
  661. var mid = pitches[str.charAt(0)];
  662. for (var i = 1; i < str.length; i++) {
  663. if (str.charAt(i) === ',') mid -= 7;
  664. else if (str.charAt(i) === ',') mid += 7;
  665. else break;
  666. }
  667. return { mid: mid - 6, str: str.substring(i) }; // We get the note in the middle of the staff. We want the note that appears as the first ledger line below the staff.
  668. };
  669. var normalizeAccidentals = function(accs) {
  670. for (var i = 0; i < accs.length; i++) {
  671. if (accs[i].note === 'b')
  672. accs[i].note = 'B';
  673. else if (accs[i].note === 'a')
  674. accs[i].note = 'A';
  675. else if (accs[i].note === 'F')
  676. accs[i].note = 'f';
  677. else if (accs[i].note === 'E')
  678. accs[i].note = 'e';
  679. else if (accs[i].note === 'D')
  680. accs[i].note = 'd';
  681. else if (accs[i].note === 'C')
  682. accs[i].note = 'c';
  683. else if (accs[i].note === 'G' && accs[i].acc === 'sharp')
  684. accs[i].note = 'g';
  685. else if (accs[i].note === 'g' && accs[i].acc === 'flat')
  686. accs[i].note = 'G';
  687. }
  688. };
  689. this.parseKey = function(str) // (and clef)
  690. {
  691. // returns:
  692. // { foundClef: true, foundKey: true }
  693. // Side effects:
  694. // calls warn() when there is a syntax error
  695. // sets these members of multilineVars:
  696. // clef
  697. // key
  698. // style
  699. //
  700. // The format is:
  701. // K: [?key?] [?modifiers?*]
  702. // modifiers are any of the following in any order:
  703. // [?clef?] [middle=?pitch?] [transpose=[-]?number?] [stafflines=?number?] [staffscale=?number?][style=?style?]
  704. // key is none|HP|Hp|?specified_key?
  705. // clef is [clef=] [?clef type?] [?line number?] [+8|-8]
  706. // specified_key is ?pitch?[#|b][mode(first three chars are significant)][accidentals*]
  707. if (str.length === 0) {
  708. // an empty K: field is the same as K:none
  709. str = 'none';
  710. }
  711. var tokens = tokenizer.tokenize(str, 0, str.length);
  712. var ret = {};
  713. // first the key
  714. switch (tokens[0].token) {
  715. case 'HP':
  716. this.addDirective("bagpipes");
  717. multilineVars.key = { root: "HP", accidentals: [], acc: "", mode: "" };
  718. ret.foundKey = true;
  719. tokens.shift();
  720. break;
  721. case 'Hp':
  722. this.addDirective("bagpipes");
  723. multilineVars.key = { root: "Hp", accidentals: [{acc: 'natural', note: 'g'}, {acc: 'sharp', note: 'f'}, {acc: 'sharp', note: 'c'}], acc: "", mode: "" };
  724. ret.foundKey = true;
  725. tokens.shift();
  726. break;
  727. case 'none':
  728. // we got the none key - that's the same as C to us
  729. multilineVars.key = { root: "none", accidentals: [], acc: "", mode: "" };
  730. ret.foundKey = true;
  731. tokens.shift();
  732. break;
  733. default:
  734. var retPitch = tokenizer.getKeyPitch(tokens[0].token);
  735. if (retPitch.len > 0) {
  736. ret.foundKey = true;
  737. var acc = "";
  738. var mode = "";
  739. // The accidental and mode might be attached to the pitch, so we might want to just remove the first character.
  740. if (tokens[0].token.length > 1)
  741. tokens[0].token = tokens[0].token.substring(1);
  742. else
  743. tokens.shift();
  744. var key = retPitch.token;
  745. // We got a pitch to start with, so we might also have an accidental and a mode
  746. if (tokens.length > 0) {
  747. var retAcc = tokenizer.getSharpFlat(tokens[0].token);
  748. if (retAcc.len > 0) {
  749. if (tokens[0].token.length > 1)
  750. tokens[0].token = tokens[0].token.substring(1);
  751. else
  752. tokens.shift();
  753. key += retAcc.token;
  754. acc = retAcc.token;
  755. }
  756. if (tokens.length > 0) {
  757. var retMode = tokenizer.getMode(tokens[0].token);
  758. if (retMode.len > 0) {
  759. tokens.shift();
  760. key += retMode.token;
  761. mode = retMode.token;
  762. }
  763. }
  764. }
  765. // We need to do a deep copy because we are going to modify it
  766. multilineVars.key = this.deepCopyKey({accidentals: keys[key]});
  767. multilineVars.key.root = retPitch.token;
  768. multilineVars.key.acc = acc;
  769. multilineVars.key.mode = mode;
  770. }
  771. break;
  772. }
  773. // There are two special cases of deprecated syntax. Ignore them if they occur
  774. if (tokens.length === 0) return ret;
  775. if (tokens[0].token === 'exp') tokens.shift();
  776. if (tokens.length === 0) return ret;
  777. if (tokens[0].token === 'oct') tokens.shift();
  778. // now see if there are extra accidentals
  779. if (tokens.length === 0) return ret;
  780. var accs = tokenizer.getKeyAccidentals2(tokens);
  781. if (accs.warn)
  782. warn(accs.warn, str, 0);
  783. // If we have extra accidentals, first replace ones that are of the same pitch before adding them to the end.
  784. if (accs.accs) {
  785. if (!ret.foundKey) { // if there are only extra accidentals, make sure this is set.
  786. ret.foundKey = true;
  787. multilineVars.key = { root: "none", acc: "", mode: "", accidentals: [] };
  788. }
  789. normalizeAccidentals(accs.accs);
  790. for (var i = 0; i < accs.accs.length; i++) {
  791. var found = false;
  792. for (var j = 0; j < multilineVars.key.accidentals.length && !found; j++) {
  793. if (multilineVars.key.accidentals[j].note === accs.accs[i].note) {
  794. found = true;
  795. multilineVars.key.accidentals[j].acc = accs.accs[i].acc;
  796. }
  797. }
  798. if (!found)
  799. multilineVars.key.accidentals.push(accs.accs[i]);
  800. }
  801. }
  802. // Now see if any optional parameters are present. They have the form "key=value", except that "clef=" is optional
  803. var token;
  804. while (tokens.length > 0) {
  805. switch (tokens[0].token) {
  806. case "m":
  807. case "middle":
  808. tokens.shift();
  809. if (tokens.length === 0) { warn("Expected = after middle", str, 0); return ret; }
  810. token = tokens.shift();
  811. if (token.token !== "=") { warn("Expected = after middle", str, 0); break; }
  812. if (tokens.length === 0) { warn("Expected parameter after middle=", str, 0); return ret; }
  813. var pitch = tokenizer.getPitchFromTokens(tokens);
  814. if (pitch.warn)
  815. warn(pitch.warn, str, 0);
  816. if (pitch.position)
  817. multilineVars.clef.verticalPos = pitch.position - 6; // we get the position from the middle line, but want to offset it to the first ledger line.
  818. break;
  819. case "transpose":
  820. tokens.shift();
  821. if (tokens.length === 0) { warn("Expected = after transpose", str, 0); return ret; }
  822. token = tokens.shift();
  823. if (token.token !== "=") { warn("Expected = after transpose", str, 0); break; }
  824. if (tokens.length === 0) { warn("Expected parameter after transpose=", str, 0); return ret; }
  825. if (tokens[0].type !== 'number') { warn("Expected number after transpose", str, 0); break; }
  826. multilineVars.clef.transpose = parseInt(tokens[0].token);
  827. tokens.shift();
  828. break;
  829. case "stafflines":
  830. tokens.shift();
  831. if (tokens.length === 0) { warn("Expected = after stafflines", str, 0); return ret; }
  832. token = tokens.shift();
  833. if (token.token !== "=") { warn("Expected = after stafflines", str, 0); break; }
  834. if (tokens.length === 0) { warn("Expected parameter after stafflines=", str, 0); return ret; }
  835. if (tokens[0].type !== 'number') { warn("Expected number after stafflines", str, 0); break; }
  836. multilineVars.clef.stafflines = parseInt(tokens[0].token);
  837. tokens.shift();
  838. break;
  839. case "staffscale":
  840. tokens.shift();
  841. if (tokens.length === 0) { warn("Expected = after staffscale", str, 0); return ret; }
  842. token = tokens.shift();
  843. if (token.token !== "=") { warn("Expected = after staffscale", str, 0); break; }
  844. if (tokens.length === 0) { warn("Expected parameter after staffscale=", str, 0); return ret; }
  845. if (tokens[0].type !== 'number') { warn("Expected number after staffscale", str, 0); break; }
  846. multilineVars.clef.staffscale = parseInt(tokens[0].token);
  847. tokens.shift();
  848. break;
  849. case "style":
  850. tokens.shift();
  851. if (tokens.length === 0) { warn("Expected = after style", str, 0); return ret; }
  852. token = tokens.shift();
  853. if (token.token !== "=") { warn("Expected = after style", str, 0); break; }
  854. if (tokens.length === 0) { warn("Expected parameter after style=", str, 0); return ret; }
  855. switch (tokens[0].token) {
  856. case "normal":
  857. case "harmonic":
  858. case "rhythm":
  859. case "x":
  860. multilineVars.style = tokens[0].token;
  861. tokens.shift();
  862. break;
  863. default:
  864. warn("error parsing style element: " + tokens[0].token, str, 0);
  865. break;
  866. }
  867. break;
  868. case "clef":
  869. tokens.shift();
  870. if (tokens.length === 0) { warn("Expected = after clef", str, 0); return ret; }
  871. token = tokens.shift();
  872. if (token.token !== "=") { warn("Expected = after clef", str, 0); break; }
  873. if (tokens.length === 0) { warn("Expected parameter after clef=", str, 0); return ret; }
  874. //break; yes, we want to fall through. That allows "clef=" to be optional.
  875. case "treble":
  876. case "bass":
  877. case "alto":
  878. case "tenor":
  879. case "perc":
  880. // clef is [clef=] [?clef type?] [?line number?] [+8|-8]
  881. var clef = tokens.shift();
  882. switch (clef.token) {
  883. case 'treble':
  884. case 'tenor':
  885. case 'alto':
  886. case 'bass':
  887. case 'perc':
  888. case 'none':
  889. break;
  890. case 'C': clef.token = 'alto'; break;
  891. case 'F': clef.token = 'bass'; break;
  892. case 'G': clef.token = 'treble'; break;
  893. case 'c': clef.token = 'alto'; break;
  894. case 'f': clef.token = 'bass'; break;
  895. case 'g': clef.token = 'treble'; break;
  896. default:
  897. warn("Expected clef name. Found " + clef.token, str, 0);
  898. break;
  899. }
  900. if (tokens.length > 0 && tokens[0].type === 'number') {
  901. clef.token += tokens[0].token;
  902. tokens.shift();
  903. }
  904. if (tokens.length > 1 && (tokens[0].token === '-' || tokens[0].token === '+') && tokens[1].token === '8') {
  905. clef.token += tokens[0].token + tokens[1].token;
  906. tokens.shift();
  907. tokens.shift();
  908. }
  909. multilineVars.clef = {type: clef.token, verticalPos: calcMiddle(clef.token, 0)};
  910. ret.foundClef = true;
  911. break;
  912. default:
  913. warn("Unknown parameter: " + tokens[0].token, str, 0);
  914. tokens.shift();
  915. }
  916. }
  917. return ret;
  918. };
  919. this.parseKeyOld = function(str) // (and clef)
  920. {
  921. str = tokenizer.stripComment(str);
  922. var origStr = str;
  923. if (str.length === 0) {
  924. // an empty K: field is the same as K:none
  925. str = 'none';
  926. }
  927. // The format is:
  928. // [space][tonic[#|b][ ][3-letter-mode][ignored-chars][space]][ accidentals...][ clef=treble|bass|bass3|tenor|alto|alto2|alto1|none [+8|-8] [middle=note] [transpose=num] [stafflines=num] ]
  929. // K: ?key? [[clef=] [clef type] [line number] [+8|-8]] [middle=?pitch?] [transpose=] [stafflines=?number?] [staffscale=?number?][style=?style?]
  930. // V: ?voice name? [clef=] [clef type] [name=] [sname=] [merge] [stem=] [up | down | auto] [gstem=] [up | down | auto] [dyn=] [up | down | auto] [lyrics=] [up | down | auto] [gchord=] [up | down | auto] [scale=] [staffscale=] [stafflines=]
  931. // -- or -- the key can be "none"
  932. // First get the key letter: turn that into a index into the key array (0-11)
  933. // Then see if there is a sharp or flat. Increment or decrement.
  934. // Then see if there is a mode modifier. Add or subtract to the index.
  935. // Then do a mod 12 on the index and return the key.
  936. // TODO: This may leave unparsed characters at the end after something reasonable was found.
  937. // TODO: The above description does not seem to be valid as key names rather than indexes are used -- GD
  938. var setClefMiddle = function(str) {
  939. var i = tokenizer.skipWhiteSpace(str);
  940. str = str.substring(i);
  941. if (str.startsWith('m=') || str.startsWith('middle=')) {
  942. str = str.substring(str.indexOf('=')+1);
  943. var mid = parseMiddle(str);
  944. multilineVars.clef.verticalPos = mid.mid;
  945. str = mid.str;
  946. }
  947. i = tokenizer.skipWhiteSpace(str);
  948. str = str.substring(i);
  949. if (str.startsWith('transpose=')) {
  950. str = str.substring(str.indexOf('=')+1);
  951. var num = tokenizer.getInt(str);
  952. if (num.digits > 0) {
  953. str = str.substring(num.digits);
  954. multilineVars.clef.transpose = num.value;
  955. }
  956. }
  957. i = tokenizer.skipWhiteSpace(str);
  958. str = str.substring(i);
  959. if (str.startsWith('stafflines=')) {
  960. str = str.substring(str.indexOf('=')+1);
  961. var num2 = tokenizer.getInt(str);
  962. if (num2.digits > 0) {
  963. str = str.substring(num2.digits);
  964. multilineVars.clef.stafflines = num2.value;
  965. }
  966. }
  967. };
  968. // check first to see if there is only a clef. If so, just take that, but ignore an error after that.
  969. var retClef = tokenizer.getClef(str, true);
  970. if (retClef.token !== undefined && (retClef.explicit === true || retClef.token !== 'none')) { // none, C, F, and G are the only ambiguous marking. We need to assume that's a key
  971. multilineVars.clef = {type: retClef.token, verticalPos: calcMiddle(retClef.token, 0)};
  972. str = str.substring(retClef.len);
  973. setClefMiddle(str);
  974. return {foundClef: true};
  975. //TODO multilinevars.key is not set - is this normal? -- GD
  976. }
  977. var ret = { root: 'none', acc: '', mode: '' };
  978. var retPitch = tokenizer.getKeyPitch(str);
  979. if (retPitch.len > 0) {
  980. var key = retPitch.token;
  981. str = str.substring(retPitch.len);
  982. // We got a pitch to start with, so we might also have an accidental and a mode
  983. var retAcc = tokenizer.getSharpFlat(str);
  984. if (retAcc.len > 0) {
  985. key += retAcc.token;
  986. str = str.substring(retAcc.len);
  987. }
  988. var retMode = tokenizer.getMode(str);
  989. if (retMode.len > 0) {
  990. key += retMode.token;
  991. str = str.substring(retMode.len);
  992. }
  993. // We need to do a deep copy because we are going to modify it
  994. ret = this.deepCopyKey({accidentals: keys[key]});
  995. ret.root = retPitch.token;
  996. ret.acc = retAcc.token || "";
  997. ret.mode = retMode.token || "";
  998. } else if (str.startsWith('HP')) {
  999. this.addDirective("bagpipes");
  1000. ret.accidentals = [];
  1001. ret.root = "HP";
  1002. multilineVars.key = ret;
  1003. return {foundKey: true};
  1004. } else if (str.startsWith('Hp')) {
  1005. ret.accidentals = [ {acc: 'natural', note: 'g'}, {acc: 'sharp', note: 'f'}, {acc: 'sharp', note: 'c'} ];
  1006. this.addDirective("bagpipes");
  1007. ret.root = "Hp";
  1008. multilineVars.key = ret;
  1009. return {foundKey: true};
  1010. } else {
  1011. var retNone = tokenizer.isMatch(str, 'none');
  1012. if (retNone > 0) {
  1013. // we got the none key - that's the same as C to us
  1014. ret.accidentals = [];
  1015. str = str.substring(retNone);
  1016. }
  1017. }
  1018. // There are two special cases of deprecated syntax. Ignore them if they occur
  1019. var j = tokenizer.skipWhiteSpace(str);
  1020. str = str.substring(j);
  1021. if (str.startsWith('exp') || str.startsWith('oct'))
  1022. str = str.substring(3);
  1023. // now see if there are extra accidentals
  1024. var done = false;
  1025. while (!done) {
  1026. var retExtra = tokenizer.getKeyAccidental(str);
  1027. if (retExtra.len === 0)
  1028. done = true;
  1029. else {
  1030. str = str.substring(retExtra.len);
  1031. if (retExtra.warn)
  1032. warn("error parsing extra accidentals:", origStr, 0);
  1033. else {
  1034. if (!ret.accidentals)
  1035. ret.accidentals = [];
  1036. ret.accidentals.push(retExtra.token);
  1037. }
  1038. }
  1039. }
  1040. // now see if there is a clef
  1041. retClef = tokenizer.getClef(str, false);
  1042. if (retClef.len > 0) {
  1043. if (retClef.warn)
  1044. warn("error parsing clef:" + retClef.warn, origStr, 0);
  1045. else {
  1046. //ret.clef = retClef.token;
  1047. multilineVars.clef = {type: retClef.token, verticalPos: calcMiddle(retClef.token, 0)};
  1048. str = str.substring(retClef.len);
  1049. setClefMiddle(str);
  1050. }
  1051. }
  1052. // now see if there is a note style
  1053. i = tokenizer.skipWhiteSpace(str);
  1054. str = str.substring(i);
  1055. if (str.startsWith('style=')) {
  1056. var style = tokenizer.getToken(str, 6, str.length);
  1057. switch (style) {
  1058. case "normal":
  1059. case "harmonic":
  1060. case "rhythm":
  1061. case "x":
  1062. multilineVars.style = style;
  1063. break;
  1064. default:
  1065. warn("error parsing style element of key: ", origStr, 0);
  1066. break;
  1067. }
  1068. str = str.substring(6+style.length);
  1069. }
  1070. // if (ret.accidentals === undefined && retClef.token === undefined) {
  1071. // warn("error parsing key: ", origStr, 0);
  1072. // return {};
  1073. // }
  1074. var result = {};
  1075. if (retClef.token !== undefined)
  1076. result.foundClef = true;
  1077. if (ret.accidentals !== undefined) {
  1078. // Adjust the octave of the accidentals, if necessary
  1079. ret.accidentals.each(function(acc) {
  1080. if (retClef.token === 'bass') {
  1081. //if (acc.note === 'A') acc.note = 'a';
  1082. //if (acc.note === 'B') acc.note = 'b';
  1083. if (acc.note === 'C') acc.note = 'c';
  1084. if (acc.note === 'D' && acc.acc !== 'flat') acc.note = 'd';
  1085. if (acc.note === 'E' && acc.acc !== 'flat') acc.note = 'e';
  1086. if (acc.note === 'F' && acc.acc !== 'flat') acc.note = 'f';
  1087. if (acc.note === 'G' && acc.acc !== 'flat') acc.note = 'g';
  1088. } else {
  1089. if (acc.note === 'a') acc.note = 'A';
  1090. if (acc.note === 'b') acc.note = 'B';
  1091. if (acc.note === 'C') acc.note = 'c';
  1092. }
  1093. });
  1094. multilineVars.key = ret;
  1095. result.foundKey = true;
  1096. }
  1097. return result;
  1098. };
  1099. this.addDirective = function(str) {
  1100. var getRequiredMeasurement = function(cmd, tokens) {
  1101. var points = tokenizer.getMeasurement(tokens);
  1102. if (points.used === 0 || tokens.length !== 0)
  1103. return { error: "Directive \"" + cmd + "\" requires a measurement as a parameter."};
  1104. return points.value;
  1105. };
  1106. var oneParameterMeasurement = function(cmd, tokens) {
  1107. var points = tokenizer.getMeasurement(tokens);
  1108. if (points.used === 0 || tokens.length !== 0)
  1109. return "Directive \"" + cmd + "\" requires a measurement as a parameter.";
  1110. tune.formatting[cmd] = points.value;
  1111. return null;
  1112. };
  1113. var getFontParameter = function(tokens) {
  1114. var font = {};
  1115. var token = tokens.last();
  1116. if (token.type === 'number') {
  1117. font.size = parseInt(token.token);
  1118. tokens.pop();
  1119. }
  1120. if (tokens.length > 0) {
  1121. var scratch = "";
  1122. tokens.each(function(tok) {
  1123. if (tok.token !== '-') {
  1124. if (scratch.length > 0) scratch += ' ';
  1125. scratch += tok.token;
  1126. }
  1127. });
  1128. font.font = scratch;
  1129. }
  1130. return font;
  1131. };
  1132. var getChangingFont = function(cmd, tokens) {
  1133. if (tokens.length === 0)
  1134. return "Directive \"" + cmd + "\" requires a font as a parameter.";
  1135. multilineVars[cmd] = getFontParameter(tokens);
  1136. return null;
  1137. };
  1138. var getGlobalFont = function(cmd, tokens) {
  1139. if (tokens.length === 0)
  1140. return "Directive \"" + cmd + "\" requires a font as a parameter.";
  1141. tune.formatting[cmd] = getFontParameter(tokens);
  1142. return null;
  1143. };
  1144. var tokens = tokenizer.tokenize(str, 0, str.length); // 3 or more % in a row, or just spaces after %% is just a comment
  1145. if (tokens.length === 0 || tokens[0].type !== 'alpha') return null;
  1146. var restOfString = str.substring(str.indexOf(tokens[0].token)+tokens[0].token.length);
  1147. restOfString = tokenizer.stripComment(restOfString);
  1148. var cmd = tokens.shift().token.toLowerCase();
  1149. var num;
  1150. var scratch = "";
  1151. switch (cmd)
  1152. {
  1153. // The following directives were added to abc_parser_lint, but haven't been implemented here.
  1154. // Most of them are direct translations from the directives that will be parsed in. See abcm2ps's format.txt for info on each of these.
  1155. // alignbars: { type: "number", optional: true },
  1156. // aligncomposer: { type: "string", Enum: [ 'left', 'center','right' ], optional: true },
  1157. // annotationfont: fontType,
  1158. // barsperstaff: { type: "number", optional: true },
  1159. // bstemdown: { type: "boolean", optional: true },
  1160. // continueall: { type: "boolean", optional: true },
  1161. // dynalign: { type: "boolean", optional: true },
  1162. // exprabove: { type: "boolean", optional: true },
  1163. // exprbelow: { type: "boolean", optional: true },
  1164. // flatbeams: { type: "boolean", optional: true },
  1165. // footer: { type: "string", optional: true },
  1166. // footerfont: fontType,
  1167. // gchordbox: { type: "boolean", optional: true },
  1168. // graceslurs: { type: "boolean", optional: true },
  1169. // gracespacebefore: { type: "number", optional: true },
  1170. // gracespaceinside: { type: "number", optional: true },
  1171. // gracespaceafter: { type: "number", optional: true },
  1172. // header: { type: "string", optional: true },
  1173. // headerfont: fontType,
  1174. // historyfont: fontType,
  1175. // infofont: fontType,
  1176. // infospace: { type: "number", optional: true },
  1177. // lineskipfac: { type: "number", optional: true },
  1178. // maxshrink: { type: "number", optional: true },
  1179. // maxstaffsep: { type: "number", optional: true },
  1180. // maxsysstaffsep: { type: "number", optional: true },
  1181. // measurebox: { type: "boolean", optional: true },
  1182. // measurefont: fontType,
  1183. // notespacingfactor: { type: "number", optional: true },
  1184. // parskipfac: { type: "number", optional: true },
  1185. // partsbox: { t…

Large files files are truncated, but you can click here to view the full file