|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var parseCommon = require('../parse/abc_common'); |
|
|
var SynthController = require('../synth/synth-controller'); |
|
|
var supportsAudio = require('../synth/supports-audio'); |
|
|
var renderAbc = require('../api/abc_tunebook_svg'); |
|
|
var EditArea = require('./abc_editarea'); |
|
|
|
|
|
function gatherAbcParams(params) { |
|
|
|
|
|
var abcjsParams = {}; |
|
|
var key; |
|
|
if (params.abcjsParams) { |
|
|
for (key in params.abcjsParams) { |
|
|
if (params.abcjsParams.hasOwnProperty(key)) { |
|
|
abcjsParams[key] = params.abcjsParams[key]; |
|
|
} |
|
|
} |
|
|
} |
|
|
if (params.midi_options) { |
|
|
for (key in params.midi_options) { |
|
|
if (params.midi_options.hasOwnProperty(key)) { |
|
|
abcjsParams[key] = params.midi_options[key]; |
|
|
} |
|
|
} |
|
|
} |
|
|
if (params.parser_options) { |
|
|
for (key in params.parser_options) { |
|
|
if (params.parser_options.hasOwnProperty(key)) { |
|
|
abcjsParams[key] = params.parser_options[key]; |
|
|
} |
|
|
} |
|
|
} |
|
|
if (params.render_options) { |
|
|
for (key in params.render_options) { |
|
|
if (params.render_options.hasOwnProperty(key)) { |
|
|
abcjsParams[key] = params.render_options[key]; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (abcjsParams.tablature) { |
|
|
if (params.warnings_id) { |
|
|
|
|
|
abcjsParams.tablature.warnings_id = params.warnings_id; |
|
|
} |
|
|
} |
|
|
return abcjsParams; |
|
|
} |
|
|
|
|
|
var Editor = function(editarea, params) { |
|
|
|
|
|
this.abcjsParams = gatherAbcParams(params); |
|
|
|
|
|
if (params.indicate_changed) |
|
|
this.indicate_changed = true; |
|
|
if (typeof editarea === "string") { |
|
|
this.editarea = new EditArea(editarea); |
|
|
} else { |
|
|
this.editarea = editarea; |
|
|
} |
|
|
this.editarea.addSelectionListener(this); |
|
|
this.editarea.addChangeListener(this); |
|
|
|
|
|
if (params.canvas_id) { |
|
|
this.div = params.canvas_id; |
|
|
} else if (params.paper_id) { |
|
|
this.div = params.paper_id; |
|
|
} else { |
|
|
this.div = document.createElement("DIV"); |
|
|
this.editarea.getElem().parentNode.insertBefore(this.div, this.editarea.getElem()); |
|
|
} |
|
|
if (typeof this.div === 'string') |
|
|
this.div = document.getElementById(this.div); |
|
|
|
|
|
if (params.selectionChangeCallback) { |
|
|
this.selectionChangeCallback = params.selectionChangeCallback; |
|
|
} |
|
|
|
|
|
this.clientClickListener = this.abcjsParams.clickListener; |
|
|
this.abcjsParams.clickListener = this.highlight.bind(this); |
|
|
|
|
|
if (params.synth) { |
|
|
if (supportsAudio()) { |
|
|
this.synth = { |
|
|
el: params.synth.el, |
|
|
cursorControl: params.synth.cursorControl, |
|
|
options: params.synth.options |
|
|
}; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (params.generate_midi) { |
|
|
this.generate_midi = params.generate_midi; |
|
|
if (this.abcjsParams.generateDownload) { |
|
|
if (typeof params.midi_download_id === 'string') |
|
|
this.downloadMidi = document.getElementById(params.midi_download_id); |
|
|
else if (params.midi_download_id) |
|
|
this.downloadMidi = params.midi_download_id; |
|
|
} |
|
|
if (this.abcjsParams.generateInline !== false) { |
|
|
if (typeof params.midi_id === 'string') |
|
|
this.inlineMidi = document.getElementById(params.midi_id); |
|
|
else if (params.midi_id) |
|
|
this.inlineMidi = params.midi_id; |
|
|
} |
|
|
} |
|
|
|
|
|
if (params.warnings_id) { |
|
|
if (typeof(params.warnings_id) === "string") |
|
|
this.warningsdiv = document.getElementById(params.warnings_id); |
|
|
else |
|
|
this.warningsdiv = params.warnings_id; |
|
|
} else if (params.generate_warnings) { |
|
|
this.warningsdiv = document.createElement("div"); |
|
|
this.div.parentNode.insertBefore(this.warningsdiv, this.div); |
|
|
} |
|
|
|
|
|
this.onchangeCallback = params.onchange; |
|
|
|
|
|
this.currentAbc = ""; |
|
|
this.tunes = []; |
|
|
this.bReentry = false; |
|
|
this.parseABC(); |
|
|
this.modelChanged(); |
|
|
|
|
|
this.addClassName = function(element, className) { |
|
|
var hasClassName = function(element, className) { |
|
|
var elementClassName = element.className; |
|
|
return (elementClassName.length > 0 && (elementClassName === className || |
|
|
new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName))); |
|
|
}; |
|
|
|
|
|
if (!hasClassName(element, className)) |
|
|
element.className += (element.className ? ' ' : '') + className; |
|
|
return element; |
|
|
}; |
|
|
|
|
|
this.removeClassName = function(element, className) { |
|
|
element.className = parseCommon.strip(element.className.replace( |
|
|
new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ')); |
|
|
return element; |
|
|
}; |
|
|
|
|
|
this.setReadOnly = function(readOnly) { |
|
|
var readonlyClass = 'abc_textarea_readonly'; |
|
|
var el = this.editarea.getElem(); |
|
|
if (readOnly) { |
|
|
el.setAttribute('readonly', 'yes'); |
|
|
this.addClassName(el, readonlyClass); |
|
|
} else { |
|
|
el.removeAttribute('readonly'); |
|
|
this.removeClassName(el, readonlyClass); |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
Editor.prototype.redrawMidi = function() { |
|
|
if (this.generate_midi && !this.midiPause) { |
|
|
var event = new window.CustomEvent("generateMidi", { |
|
|
detail: { |
|
|
tunes: this.tunes, |
|
|
abcjsParams: this.abcjsParams, |
|
|
downloadMidiEl: this.downloadMidi, |
|
|
inlineMidiEl: this.inlineMidi, |
|
|
engravingEl: this.div |
|
|
} |
|
|
}); |
|
|
window.dispatchEvent(event); |
|
|
} |
|
|
if (this.synth) { |
|
|
var userAction = this.synth.synthControl; |
|
|
if (!this.synth.synthControl) { |
|
|
this.synth.synthControl = new SynthController(); |
|
|
this.synth.synthControl.load(this.synth.el, this.synth.cursorControl, this.synth.options); |
|
|
} |
|
|
this.synth.synthControl.setTune(this.tunes[0], userAction, this.synth.options); |
|
|
} |
|
|
}; |
|
|
|
|
|
Editor.prototype.modelChanged = function() { |
|
|
if (this.bReentry) |
|
|
return; |
|
|
this.bReentry = true; |
|
|
try { |
|
|
this.timerId = null; |
|
|
if (this.synth && this.synth.synthControl) |
|
|
this.synth.synthControl.disable(true); |
|
|
|
|
|
this.tunes = renderAbc(this.div, this.currentAbc, this.abcjsParams); |
|
|
if (this.tunes.length > 0) { |
|
|
this.warnings = this.tunes[0].warnings; |
|
|
} |
|
|
this.redrawMidi(); |
|
|
} catch(error) { |
|
|
console.error("ABCJS error: ", error); |
|
|
if (!this.warnings) |
|
|
this.warnings = []; |
|
|
this.warnings.push(error.message); |
|
|
} |
|
|
|
|
|
if (this.warningsdiv) { |
|
|
this.warningsdiv.innerHTML = (this.warnings) ? this.warnings.join("<br />") : "No errors"; |
|
|
} |
|
|
this.updateSelection(); |
|
|
this.bReentry = false; |
|
|
}; |
|
|
|
|
|
|
|
|
Editor.prototype.paramChanged = function(engraverParams) { |
|
|
if (engraverParams) { |
|
|
for (var key in engraverParams) { |
|
|
if (engraverParams.hasOwnProperty(key)) { |
|
|
this.abcjsParams[key] = engraverParams[key]; |
|
|
} |
|
|
} |
|
|
} |
|
|
this.currentAbc = ""; |
|
|
this.fireChanged(); |
|
|
}; |
|
|
|
|
|
Editor.prototype.synthParamChanged = function(options) { |
|
|
if (!this.synth) |
|
|
return; |
|
|
this.synth.options = {}; |
|
|
if (options) { |
|
|
for (var key in options) { |
|
|
if (options.hasOwnProperty(key)) { |
|
|
this.synth.options[key] = options[key]; |
|
|
} |
|
|
} |
|
|
} |
|
|
this.currentAbc = ""; |
|
|
this.fireChanged(); |
|
|
}; |
|
|
|
|
|
|
|
|
Editor.prototype.parseABC = function() { |
|
|
var t = this.editarea.getString(); |
|
|
if (t===this.currentAbc) { |
|
|
this.updateSelection(); |
|
|
return false; |
|
|
} |
|
|
|
|
|
this.currentAbc = t; |
|
|
return true; |
|
|
}; |
|
|
|
|
|
Editor.prototype.updateSelection = function() { |
|
|
var selection = this.editarea.getSelection(); |
|
|
try { |
|
|
if (this.tunes.length > 0 && this.tunes[0].engraver) |
|
|
this.tunes[0].engraver.rangeHighlight(selection.start, selection.end); |
|
|
} catch (e) {} |
|
|
if (this.selectionChangeCallback) |
|
|
this.selectionChangeCallback(selection.start, selection.end); |
|
|
}; |
|
|
|
|
|
|
|
|
Editor.prototype.fireSelectionChanged = function() { |
|
|
this.updateSelection(); |
|
|
}; |
|
|
|
|
|
Editor.prototype.setDirtyStyle = function(isDirty) { |
|
|
if (this.indicate_changed === undefined) |
|
|
return; |
|
|
var addClassName = function(element, className) { |
|
|
var hasClassName = function(element, className) { |
|
|
var elementClassName = element.className; |
|
|
return (elementClassName.length > 0 && (elementClassName === className || |
|
|
new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName))); |
|
|
}; |
|
|
|
|
|
if (!hasClassName(element, className)) |
|
|
element.className += (element.className ? ' ' : '') + className; |
|
|
return element; |
|
|
}; |
|
|
|
|
|
var removeClassName = function(element, className) { |
|
|
element.className = parseCommon.strip(element.className.replace( |
|
|
new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ')); |
|
|
return element; |
|
|
}; |
|
|
|
|
|
var readonlyClass = 'abc_textarea_dirty'; |
|
|
var el = this.editarea.getElem(); |
|
|
if (isDirty) { |
|
|
addClassName(el, readonlyClass); |
|
|
} else { |
|
|
removeClassName(el, readonlyClass); |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
Editor.prototype.fireChanged = function() { |
|
|
if (this.bIsPaused) |
|
|
return; |
|
|
if (this.parseABC()) { |
|
|
var self = this; |
|
|
if (this.timerId) |
|
|
clearTimeout(this.timerId); |
|
|
this.timerId = setTimeout(function () { |
|
|
self.modelChanged(); |
|
|
}, 300); |
|
|
var isDirty = this.isDirty(); |
|
|
if (this.wasDirty !== isDirty) { |
|
|
this.wasDirty = isDirty; |
|
|
this.setDirtyStyle(isDirty); |
|
|
} |
|
|
if (this.onchangeCallback) |
|
|
this.onchangeCallback(this); |
|
|
} |
|
|
}; |
|
|
|
|
|
Editor.prototype.setNotDirty = function() { |
|
|
this.editarea.initialText = this.editarea.getString(); |
|
|
this.wasDirty = false; |
|
|
this.setDirtyStyle(false); |
|
|
}; |
|
|
|
|
|
Editor.prototype.isDirty = function() { |
|
|
if (this.indicate_changed === undefined) |
|
|
return false; |
|
|
return this.editarea.initialText !== this.editarea.getString(); |
|
|
}; |
|
|
|
|
|
Editor.prototype.highlight = function(abcelem, tuneNumber, classes, analysis, drag, mouseEvent) { |
|
|
|
|
|
|
|
|
|
|
|
this.editarea.setSelection(abcelem.startChar, abcelem.endChar); |
|
|
if (this.selectionChangeCallback) |
|
|
this.selectionChangeCallback(abcelem.startChar, abcelem.endChar); |
|
|
if (this.clientClickListener) |
|
|
this.clientClickListener(abcelem, tuneNumber, classes, analysis, drag, mouseEvent); |
|
|
}; |
|
|
|
|
|
Editor.prototype.pause = function(shouldPause) { |
|
|
this.bIsPaused = shouldPause; |
|
|
if (!shouldPause) |
|
|
this.fireChanged(); |
|
|
}; |
|
|
|
|
|
Editor.prototype.millisecondsPerMeasure = function() { |
|
|
if (!this.synth || !this.synth.synthControl || !this.synth.synthControl.visualObj) |
|
|
return 0; |
|
|
return this.synth.synthControl.visualObj.millisecondsPerMeasure(); |
|
|
}; |
|
|
|
|
|
Editor.prototype.pauseMidi = function(shouldPause) { |
|
|
this.midiPause = shouldPause; |
|
|
if (!shouldPause) |
|
|
this.redrawMidi(); |
|
|
}; |
|
|
|
|
|
module.exports = Editor; |
|
|
|