Comparing version 6.1.0 to 6.1.1
{ | ||
"name": "abcjs", | ||
"version": "6.1.0", | ||
"version": "6.1.1", | ||
"description": "Renderer for abc music notation", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -11,2 +11,6 @@ ![abcjs](https://paulrosen.github.io/abcjs/img/abcjs_comp_extended_08.svg) | ||
## Announcement: version 6.1.1 | ||
There is a little difference in the generated SVG: Now each line is surrounded with a `<g>` element. This probably won't affect your program unless you are doing very specific manipulation of the SVG. | ||
## Announcement: version 6.1.0 | ||
@@ -13,0 +17,0 @@ |
@@ -0,1 +1,15 @@ | ||
# Version 6.1.1 | ||
## Bugs | ||
* rewrite oneSvgPerLine; fixes selection and keeping multiline items (like slurs) correctly. | ||
## Features | ||
* added mobile browser TouchEvent support for dragging | ||
## Documentation | ||
* fixed some typescript definitions | ||
# Version 6.1.0 | ||
@@ -2,0 +16,0 @@ |
@@ -62,92 +62,2 @@ var tunebook = require('./abc_tunebook'); | ||
function renderEachLineSeparately(div, tune, params, tuneNumber) { | ||
function initializeTuneLine(tune) { | ||
var obj = new Tune(); | ||
obj.formatting = tune.formatting; | ||
obj.media = tune.media; | ||
obj.version = tune.version; | ||
return obj; | ||
} | ||
// Before rendering, chop up the returned tune into an array where each element is a line. | ||
// The first element of the array gets the title and other items that go on top, the last element | ||
// of the array gets the extra text that goes on bottom. Each element gets any non-music info that comes before it. | ||
var tunes = []; | ||
var tuneLine; | ||
for (var i = 0; i < tune.lines.length; i++) { | ||
var line = tune.lines[i]; | ||
if (!tuneLine) | ||
tuneLine = initializeTuneLine(tune); | ||
if (i === 0) { | ||
// These items go on top of the music | ||
tuneLine.copyTopInfo(tune); | ||
} | ||
// push the lines until we get to a music line | ||
tuneLine.lines.push(line); | ||
if (line.staff) { | ||
tunes.push(tuneLine); | ||
tuneLine = undefined; | ||
} | ||
} | ||
// Add any extra stuff to the last line. | ||
if (tuneLine) { | ||
var lastLine = tunes[tunes.length-1]; | ||
for (var j = 0; j < tuneLine.lines.length; j++) | ||
lastLine.lines.push(tuneLine.lines[j]); | ||
} | ||
// These items go below the music | ||
tuneLine = tunes[tunes.length-1]; | ||
tuneLine.copyBottomInfo(tune); | ||
// Now create sub-divs and render each line. Need to copy the params to change the padding for the interior slices. | ||
var ep = {}; | ||
for (var key in params) { | ||
if (params.hasOwnProperty(key)) { | ||
ep[key] = params[key]; | ||
} | ||
} | ||
var origPaddingTop = ep.paddingtop; | ||
var origPaddingBottom = ep.paddingbottom; | ||
var currentScrollY = div.parentNode.scrollTop; // If there is scrolling it will be lost during the redraw so remember it. | ||
var currentScrollX = div.parentNode.scrollLeft; | ||
div.innerHTML = ""; | ||
var lineCount = 0; | ||
for (var k = 0; k < tunes.length; k++) { | ||
var lineEl = document.createElement("div"); | ||
div.appendChild(lineEl); | ||
if (k === 0) { | ||
ep.paddingtop = origPaddingTop; | ||
ep.paddingbottom = 0; | ||
} else if (k === tunes.length-1) { | ||
ep.paddingtop = 10; | ||
ep.paddingbottom = origPaddingBottom; | ||
} else { | ||
ep.paddingtop = 10; | ||
ep.paddingbottom = 0; | ||
} | ||
if (k < tunes.length-1) { | ||
// If it is not the last line, force stretchlast. If it is, stretchlast might have been set by the input parameters. | ||
tunes[k].formatting = parseCommon.clone(tunes[k].formatting); | ||
tunes[k].formatting.stretchlast = true; | ||
} | ||
renderOne(lineEl, tunes[k], ep, tuneNumber, lineCount); | ||
lineCount += tunes[k].lines.length; | ||
if (k === 0) | ||
tune.engraver = tunes[k].engraver; | ||
else { | ||
if (!tune.engraver.staffgroups) | ||
tune.engraver.staffgroups = tunes[k].engraver.staffgroups; | ||
else if (tunes[k].engraver.staffgroups.length > 0) | ||
tune.engraver.staffgroups.push(tunes[k].engraver.staffgroups[0]); | ||
} | ||
} | ||
if (currentScrollX || currentScrollY) { | ||
div.parentNode.scrollTo(currentScrollX, currentScrollY); | ||
} | ||
} | ||
// A quick way to render a tune from javascript when interactivity is not required. | ||
@@ -218,6 +128,3 @@ // This is used when a javascript routine has some abc text that it wants to render | ||
} | ||
else if (removeDiv || !params.oneSvgPerLine || tune.lines.length < 2) | ||
renderOne(div, tune, params, tuneNumber, 0); | ||
else | ||
renderEachLineSeparately(div, tune, params, tuneNumber); | ||
renderOne(div, tune, params, tuneNumber, 0); | ||
if (removeDiv) | ||
@@ -244,6 +151,3 @@ div.parentNode.removeChild(div); | ||
} | ||
if (!params.oneSvgPerLine || tune.lines.length < 2) | ||
renderOne(div, tune, ret.revisedParams, tuneNumber, 0); | ||
else | ||
renderEachLineSeparately(div, tune, ret.revisedParams, tuneNumber); | ||
renderOne(div, tune, ret.revisedParams, tuneNumber, 0); | ||
tune.explanation = ret.explanation; | ||
@@ -250,0 +154,0 @@ return tune; |
@@ -35,2 +35,3 @@ // abc_engraver_controller.js: Controls the engraving process of an ABCJS abstract syntax tree as produced by ABCJS/parse | ||
params = params || {}; | ||
this.oneSvgPerLine = params.oneSvgPerLine; | ||
this.selectionColor = params.selectionColor; | ||
@@ -250,5 +251,67 @@ this.dragColor = params.dragColor ? params.dragColor : params.selectionColor; | ||
setupSelection(this); | ||
if (this.oneSvgPerLine) { | ||
var div = this.renderer.paper.svg.parentNode | ||
this.svgs = splitSvgIntoLines(div, abcTune.metaText.title) | ||
} else { | ||
this.svgs = [this.renderer.paper.svg]; | ||
} | ||
setupSelection(this, this.svgs); | ||
}; | ||
function splitSvgIntoLines(output, title) { | ||
// Each line is a top level <g> in the svg. To split it into separate | ||
// svgs iterate through each of those and put them in a new svg. Since | ||
// they are placed absolutely, the viewBox needs to be manipulated to | ||
// get the correct vertical positioning. | ||
// We copy all the attributes from the original svg except for the aria-label | ||
// since we want that to include a count. And the height is now a fraction of the original svg. | ||
if (!title) title = "Untitled" | ||
var source = output.querySelector("svg") | ||
var style = source.querySelector("style") | ||
var width = source.getAttribute("width") | ||
var sections = output.querySelectorAll("svg > g") // each section is a line, or the top matter or the bottom matter, or text that has been inserted. | ||
var nextTop = 0 // There are often gaps between the elements for spacing, so the actual top and height needs to be inferred. | ||
var wrappers = [] // Create all the elements and place them at once because we use the current svg to get data. It would disappear after placing the first line. | ||
var svgs = [] | ||
for (var i = 0; i < sections.length; i++) { | ||
var section = sections[i] | ||
var box = section.getBBox() | ||
var gapBetweenLines = box.y - nextTop // take the margin into account | ||
var height = box.height + gapBetweenLines; | ||
var wrapper = document.createElement("div"); | ||
wrapper.setAttribute("style", "overflow: hidden;height:"+height+"px;") | ||
var svg = duplicateSvg(source) | ||
var fullTitle = "Sheet Music for \"" + title + "\" section " + (i+1) | ||
svg.setAttribute("aria-label", fullTitle) | ||
svg.setAttribute("height", height) | ||
svg.setAttribute("viewBox", "0 " + nextTop + " " + width + " " + height ) | ||
svg.appendChild(style.cloneNode(true)) | ||
var titleEl = document.createElement("title") | ||
titleEl.innerText = fullTitle | ||
svg.appendChild(titleEl) | ||
svg.appendChild(section) | ||
wrapper.appendChild(svg) | ||
svgs.push(svg) | ||
output.appendChild(wrapper) | ||
//wrappers.push(wrapper) | ||
nextTop = box.y+box.height | ||
} | ||
// for (i = 0; i < wrappers.length; i++) | ||
// output.appendChild(wrappers[i]) | ||
output.removeChild(source) | ||
return svgs; | ||
} | ||
function duplicateSvg(source) { | ||
var svgNS = "http://www.w3.org/2000/svg"; | ||
var svg = document.createElementNS(svgNS, "svg"); | ||
for (var i = 0; i < source.attributes.length; i++) { | ||
var attr = source.attributes[i]; | ||
if (attr.name !== "height" && attr.name != "aria-label") | ||
svg.setAttribute(attr.name, attr.value) | ||
} | ||
return svg; | ||
} | ||
EngraverController.prototype.getDim = function(historyEl) { | ||
@@ -255,0 +318,0 @@ // Get the dimensions on demand because the getBBox call is expensive. |
@@ -9,4 +9,6 @@ var drawStaffGroup = require('./staff-group'); | ||
var selectables = new Selectables(renderer.paper, selectTypes, tuneNumber); | ||
renderer.paper.openGroup() | ||
renderer.moveY(renderer.padding.top); | ||
nonMusic(renderer, abcTune.topText, selectables); | ||
renderer.paper.closeGroup() | ||
renderer.moveY(renderer.spacing.music); | ||
@@ -18,2 +20,3 @@ var staffgroups = []; | ||
if (abcLine.staff) { | ||
renderer.paper.openGroup() | ||
if (abcLine.vskip) { | ||
@@ -27,4 +30,7 @@ renderer.moveY(abcLine.vskip); | ||
staffgroups.push(staffgroup); | ||
renderer.paper.closeGroup() | ||
} else if (abcLine.nonMusic) { | ||
renderer.paper.openGroup() | ||
nonMusic(renderer, abcLine.nonMusic, selectables); | ||
renderer.paper.closeGroup() | ||
} | ||
@@ -35,4 +41,6 @@ } | ||
if (abcTune.bottomText && abcTune.bottomText.rows && abcTune.bottomText.rows.length > 0) { | ||
renderer.paper.openGroup() | ||
renderer.moveY(24); // TODO-PER: Empirically discovered. What variable should this be? | ||
nonMusic(renderer, abcTune.bottomText, selectables); | ||
renderer.paper.closeGroup() | ||
} | ||
@@ -39,0 +47,0 @@ setPaperSize(renderer, maxWidth, scale, responsive); |
@@ -18,3 +18,3 @@ var spacing = require('../abc_spacing'); | ||
// An invisible marker is useful to be able to find where each system starts. | ||
addInvisibleMarker(renderer, "abcjs-top-of-system"); | ||
//addInvisibleMarker(renderer, "abcjs-top-of-system"); | ||
@@ -181,6 +181,6 @@ var startY = renderer.y; // So that it can be restored after we're done. | ||
function addInvisibleMarker(renderer, className) { | ||
var y = Math.round(renderer.y); | ||
renderer.paper.pathToBack({path:"M 0 " + y + " L 0 0", stroke:"none", fill:"none", "stroke-opacity": 0, "fill-opacity": 0, 'class': renderer.controller.classes.generate(className), 'data-vertical': y }); | ||
} | ||
// function addInvisibleMarker(renderer, className) { | ||
// var y = Math.round(renderer.y); | ||
// renderer.paper.pathToBack({path:"M 0 " + y + " L 0 0", stroke:"none", fill:"none", "stroke-opacity": 0, "fill-opacity": 0, 'class': renderer.controller.classes.generate(className), 'data-vertical': y }); | ||
// } | ||
@@ -187,0 +187,0 @@ function boxAllElements(renderer, voices, which) { |
var spacing = require('./abc_spacing'); | ||
function setupSelection(engraver) { | ||
function setupSelection(engraver, svgs) { | ||
engraver.rangeHighlight = rangeHighlight; | ||
@@ -17,10 +17,17 @@ if (engraver.dragging) { | ||
} | ||
engraver.renderer.paper.svg.addEventListener('mousedown', mouseDown.bind(engraver)); | ||
engraver.renderer.paper.svg.addEventListener('mousemove', mouseMove.bind(engraver)); | ||
engraver.renderer.paper.svg.addEventListener('mouseup', mouseUp.bind(engraver)); | ||
for (var i = 0; i < svgs.length; i++) { | ||
svgs[i].addEventListener('touchstart', mouseDown.bind(engraver)); | ||
svgs[i].addEventListener('touchmove', mouseMove.bind(engraver)); | ||
svgs[i].addEventListener('touchend', mouseUp.bind(engraver)); | ||
svgs[i].addEventListener('mousedown', mouseDown.bind(engraver)); | ||
svgs[i].addEventListener('mousemove', mouseMove.bind(engraver)); | ||
svgs[i].addEventListener('mouseup', mouseUp.bind(engraver)); | ||
} | ||
} | ||
function getCoord(ev, svg) { | ||
function getCoord(ev) { | ||
var scaleX = 1; | ||
var scaleY = 1; | ||
var svg = ev.target.closest('svg') | ||
var yOffset = 0 | ||
@@ -35,2 +42,3 @@ // when renderer.options.responsive === 'resize' the click coords are in relation to the HTML | ||
scaleY = svg.viewBox.baseVal.height / svg.clientHeight | ||
yOffset = svg.viewBox.baseVal.y | ||
} | ||
@@ -53,3 +61,3 @@ | ||
return [x, y]; | ||
return [x, y+yOffset]; | ||
} | ||
@@ -126,3 +134,3 @@ | ||
for (var i = 0; i < selectables.length; i++) { | ||
if (el === selectables[i].svgEl) | ||
if (el.dataset.index === selectables[i].svgEl.dataset.index) | ||
return i; | ||
@@ -223,3 +231,3 @@ } | ||
// See if they clicked close to an element. | ||
box = getCoord(ev, self.renderer.paper.svg); | ||
box = getCoord(ev); | ||
x = box[0]; | ||
@@ -233,9 +241,26 @@ y = box[1]; | ||
function attachMissingTouchEventAttributes(touchEv) { | ||
var rect = touchEv.target.getBoundingClientRect(); | ||
var offsetX = touchEv.touches[0].pageX - rect.left; | ||
var offsetY = touchEv.touches[0].pageY - rect.top; | ||
touchEv.touches[0].offsetX = offsetX; | ||
touchEv.touches[0].offsetY = offsetY; | ||
touchEv.touches[0].layerX = touchEv.touches[0].pageX; | ||
touchEv.touches[0].layerY = touchEv.touches[0].pageY; | ||
} | ||
function mouseDown(ev) { | ||
// "this" is the EngraverController because of the bind(this) when setting the event listener. | ||
var _ev = ev; | ||
if (ev.type === 'touchstart') { | ||
attachMissingTouchEventAttributes(ev); | ||
_ev = ev.touches[0]; | ||
} | ||
var positioning = getMousePosition(this, ev); | ||
var positioning = getMousePosition(this, _ev); | ||
// Only start dragging if the user clicked close enough to an element and clicked with the main mouse button. | ||
if (positioning.clickedOn >= 0 && ev.button === 0) { | ||
if (positioning.clickedOn >= 0 && (ev.type === 'touchstart' || ev.button === 0)) { | ||
this.dragTarget = this.selectables[positioning.clickedOn]; | ||
@@ -253,2 +278,8 @@ this.dragIndex = positioning.clickedOn; | ||
function mouseMove(ev) { | ||
var _ev = ev; | ||
if (ev.type === 'touchmove') { | ||
attachMissingTouchEventAttributes(ev); | ||
_ev = ev.touches[0]; | ||
} | ||
this.lastTouchMove = ev; | ||
// "this" is the EngraverController because of the bind(this) when setting the event listener. | ||
@@ -259,3 +290,3 @@ | ||
var positioning = getMousePosition(this, ev); | ||
var positioning = getMousePosition(this, _ev); | ||
@@ -271,2 +302,7 @@ var yDist = Math.round((positioning.y - this.dragMouseStart.y)/spacing.STEP); | ||
// "this" is the EngraverController because of the bind(this) when setting the event listener. | ||
var _ev = ev; | ||
if (ev.type === 'touchend') { | ||
attachMissingTouchEventAttributes(this.lastTouchMove); | ||
_ev = this.lastTouchMove.touches[0]; | ||
} | ||
@@ -282,3 +318,3 @@ if (!this.dragTarget) | ||
notifySelect.bind(this)(this.dragTarget, this.dragYStep, this.selectables.length, this.dragIndex, ev); | ||
notifySelect.bind(this)(this.dragTarget, this.dragYStep, this.selectables.length, this.dragIndex, _ev); | ||
if (this.dragTarget.svgEl && this.dragTarget.svgEl.focus) { | ||
@@ -285,0 +321,0 @@ this.dragTarget.svgEl.focus(); |
@@ -174,2 +174,4 @@ declare module 'abcjs' { | ||
export type AbstractEngraver = any; | ||
export type NoteProperties = any; // TODO | ||
@@ -508,2 +510,30 @@ | ||
export interface EngraverController { | ||
classes: any; | ||
dragColor: string; | ||
dragIndex: number; | ||
dragMouseStart: { x: number, y: number; }; | ||
dragTarget: null | any; | ||
dragYStep: number; | ||
dragging: boolean; | ||
engraver: AbstractEngraver; | ||
getFontAndAttr: any; | ||
getTextSize: any; | ||
listeners: [ClickListener]; | ||
rangeHighlight: any; | ||
renderer: any; | ||
responsive?: boolean; | ||
scale: number; | ||
initialClef?: any; | ||
selectTypes: boolean | Array<DragTypes>; | ||
selectables: Array<Selectable>; | ||
selected: Array<any>; | ||
selectionColor: string; | ||
space: number; | ||
staffgroups: [any]; | ||
staffwidthPrint: number; | ||
staffwidthScreen: number; | ||
width: number; | ||
} | ||
export interface MetaText { | ||
@@ -717,2 +747,3 @@ "abc-copyright"?: string; | ||
formatting: Formatting; | ||
engraver?: EngraverController; | ||
lines: Array<TuneLine>; | ||
@@ -738,2 +769,3 @@ media: Media; | ||
setUpAudio: (options: SynthOptions) => AudioTracks; | ||
makeVoicesArray: () => Array<Selectable[]> | ||
lineBreaks?: Array<number>; | ||
@@ -745,2 +777,12 @@ visualTranspose?: number; | ||
export interface Selectable { | ||
absEl: AbsoluteElement; | ||
isDraggable: boolean; | ||
staffPos: { | ||
height: number; | ||
top: number; | ||
zero: number; | ||
} | ||
} | ||
export interface AbcElem { | ||
@@ -1099,2 +1141,4 @@ el_type: string //TODO enumerate these | ||
export function extractMeasures(abc: string) : Array<MeasureList>; | ||
export function strTranspose(originalAbc: string, visualObj: TuneObject, steps: number): string; | ||
@@ -1101,0 +1145,0 @@ // |
@@ -1,3 +0,3 @@ | ||
var version = '6.1.0'; | ||
var version = '6.1.1'; | ||
module.exports = version; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
5473114
177
50053
115