From ab6d63e8e08262fffd958fa8bd267641e27f35ad Mon Sep 17 00:00:00 2001 From: Sahil Chinoy Date: Thu, 24 May 2018 13:53:27 -0400 Subject: [PATCH 1/5] add responsive height setting --- CHANGELOG.md | 2 ++ ai2html.js | 71 +++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 169c69f..16f8e25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +### v0.74.0 +- Add 'use_responsive_height' and 'responsive_height_limit' settings to limit the maximum artboard height based on the height of the window. ### v0.73.0 - Add data-name= properties to div symbols if they are named in Illustrator. diff --git a/ai2html.js b/ai2html.js index b1c54d1..52d8611 100644 --- a/ai2html.js +++ b/ai2html.js @@ -45,7 +45,7 @@ function main() { // - Update the version number in package.json // - Add an entry to CHANGELOG.md // - Run the release.sh script to create a new GitHub release -var scriptVersion = "0.73.0"; +var scriptVersion = "0.74.0"; // ================================================ // ai2html and config settings @@ -81,6 +81,8 @@ var nytBaseSettings = { center_html_output: {defaultValue: "true", includeInSettingsBlock: false, includeInConfigFile: false}, use_2x_images_if_possible: {defaultValue: "yes", includeInSettingsBlock: false, includeInConfigFile: false}, use_lazy_loader: {defaultValue: "yes", includeInSettingsBlock: true, includeInConfigFile: false}, + use_responsive_height: {defaultValue: "no", includeInSettingsBlock: false, includeInConfigFile: false}, + responsive_height_limit: {defaultValue: null, includeInSettingsBlock: false, includeInConfigFile: false}, include_resizer_css_js: {defaultValue: "yes", includeInSettingsBlock: false, includeInConfigFile: false}, include_resizer_classes: {defaultValue: "no", includeInSettingsBlock: false, includeInConfigFile: false}, include_resizer_widths: {defaultValue: "yes", includeInSettingsBlock: false, includeInConfigFile: false}, @@ -137,6 +139,8 @@ var defaultBaseSettings = { center_html_output: {defaultValue: "true", includeInSettingsBlock: false, includeInConfigFile: false}, use_2x_images_if_possible: {defaultValue: "yes", includeInSettingsBlock: false, includeInConfigFile: false}, use_lazy_loader: {defaultValue: "no", includeInSettingsBlock: false, includeInConfigFile: false}, + use_responsive_height: {defaultValue: "no", includeInSettingsBlock: false, includeInConfigFile: false}, + responsive_height_limit: {defaultValue: null, includeInSettingsBlock: false, includeInConfigFile: false}, include_resizer_css_js: {defaultValue: "no", includeInSettingsBlock: false, includeInConfigFile: false}, include_resizer_classes: {defaultValue: "no", includeInSettingsBlock: false, includeInConfigFile: false}, include_resizer_widths: {defaultValue: "yes", includeInSettingsBlock: false, includeInConfigFile: false}, @@ -3614,10 +3618,11 @@ function convertSettingsToYaml(settings) { return lines.join('\n'); } -function getResizerScript() { +function getResizerScript(settings) { // The resizer function is embedded in the HTML page -- external variables must // be passed in. - var resizer = function (scriptEnvironment, nameSpace) { + var resizer = function (opts) { + var nameSpace = opts.namespace; // Use a sentinel class to ensure that this version of the resizer only initializes once if (document.documentElement.className.indexOf(nameSpace + "resizer-v5-init") > -1) return; document.documentElement.className += " " + nameSpace + "resizer-v5-init"; @@ -3633,7 +3638,7 @@ function getResizerScript() { function updateAllGraphics() { selectElements(".ai2html-box-v5").forEach(updateGraphic); - if (scriptEnvironment == "nyt-preview") { + if (opts.environment == "nyt-preview") { nytOnResize(); } } @@ -3641,20 +3646,51 @@ function getResizerScript() { function updateGraphic(container) { var artboards = selectElements("." + nameSpace + "artboard[data-min-width]", container), width = Math.round(container.getBoundingClientRect().width), + height = Math.round(window.innerHeight), id = container.id, // assume container has an id - showImages = !observer || visibilityIndex[id] == 'visible'; + showImages = !observer || visibilityIndex[id] == 'visible', + heightLimit = parseFloat(opts.responsive_height_limit) || null, // accepts 90% or 90 + maxHeight = Math.round((heightLimit / 100) * height); - // Set artboard visibility based on container width - artboards.forEach(function(el) { + // Set artboard visibility + artboards.some(function(el, i) { var minwidth = el.getAttribute("data-min-width"), - maxwidth = el.getAttribute("data-max-width"); - if (+minwidth <= width && (+maxwidth >= width || maxwidth === null)) { + maxwidth = el.getAttribute("data-max-width") + aspectRatio = el.getAttribute("data-aspect-ratio"), + show = +minwidth <= width; + + // if the responsive height limit is on and responsiveness is dynamic, + // the width the artboard will display at is either the page width + // or the maxHeight * aspectRatio, whichever is smaller + if (heightLimit && opts.responsiveness == "dynamic") { + var widthAtMaxHeight = maxHeight * aspectRatio, + displayWidth = width > widthAtMaxHeight ? widthAtMaxHeight : width; + show = +minwidth <= displayWidth; + } + + // if responsive height limit is on and responsiveness is fixed + // the width the artboard will display at is the minimum width of the artboard + else if (heightLimit && opts.responsiveness == "fixed") { + // the height at this width is + var heightAtMinWidth = minwidth / aspectRatio; + // make sure it's not too tall + show = show && (heightAtMinWidth < maxHeight); + } + + // if none of the artboards are the right size, display the smallest one + var isSmallestArtboard = i == artboards.length - 1; + + if (show || isSmallestArtboard) { if (showImages) { selectElements("." + nameSpace + "aiImg", el).forEach(updateImgSrc); } + // hide all the artboards + artboards.forEach(function(el) { el.style.display = 'none' }); + // show just this one el.style.display = "block"; - } else { - el.style.display = "none"; + // explicitly set width of the artboard if responsiveness is dynamic + if (heightLimit && opts.responsiveness == "dynamic") { el.style.width = displayWidth + "px" } + return true; } }); @@ -3748,13 +3784,18 @@ function getResizerScript() { }; // convert function to JS source code + var opts = { + namespace: nameSpace, + environment: scriptEnvironment, + responsiveness: settings.responsiveness, + responsive_height_limit: settings.use_responsive_height == 'yes' ? settings.responsive_height_limit : null + }; var resizerJs = '(' + trim(resizer.toString().replace(/ /g, '\t')) + // indent with tabs - ')("' + scriptEnvironment + '", "' + nameSpace + '");'; + ')(' + JSON.stringify(opts) + ');'; return '\r'; } - // Write an HTML page to a file for NYT Preview function outputLocalPreviewPage(textForFile, localPreviewDestination, settings) { var localPreviewTemplateText = readTextFile(docPath + settings.local_preview_template); @@ -3799,7 +3840,7 @@ function generateOutputHtml(content, pageName, settings) { } } if (isTrue(settings.include_resizer_script)) { - responsiveJs = getResizerScript(); + responsiveJs = getResizerScript(settings); responsiveCss = ""; } @@ -3868,4 +3909,4 @@ function generateOutputHtml(content, pageName, settings) { } } } // end main() function definition -main(); +main(); \ No newline at end of file From 1c63f19487454a7cd4be8dafc434bb8d466a59ef Mon Sep 17 00:00:00 2001 From: Sahil Chinoy Date: Thu, 24 May 2018 14:22:24 -0400 Subject: [PATCH 2/5] validate options --- ai2html.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/ai2html.js b/ai2html.js index 52d8611..5468b84 100644 --- a/ai2html.js +++ b/ai2html.js @@ -3621,6 +3621,7 @@ function convertSettingsToYaml(settings) { function getResizerScript(settings) { // The resizer function is embedded in the HTML page -- external variables must // be passed in. + var resizer = function (opts) { var nameSpace = opts.namespace; // Use a sentinel class to ensure that this version of the resizer only initializes once @@ -3649,7 +3650,7 @@ function getResizerScript(settings) { height = Math.round(window.innerHeight), id = container.id, // assume container has an id showImages = !observer || visibilityIndex[id] == 'visible', - heightLimit = parseFloat(opts.responsive_height_limit) || null, // accepts 90% or 90 + heightLimit = opts.responsive_height_limit, maxHeight = Math.round((heightLimit / 100) * height); // Set artboard visibility @@ -3782,13 +3783,21 @@ function getResizerScript(settings) { }; } }; + + // validate settings for responsive height + if (settings.use_responsive_height == 'yes') { + var heightLimit = parseFloat(settings.responsive_height_limit) || null; // accepts 90% or 90 + if (heightLimit > 100 || heightLimit < 0) { + error('responsive_height_limit must be between 0 and 100.'); + } + } // convert function to JS source code var opts = { namespace: nameSpace, environment: scriptEnvironment, responsiveness: settings.responsiveness, - responsive_height_limit: settings.use_responsive_height == 'yes' ? settings.responsive_height_limit : null + responsive_height_limit: settings.use_responsive_height == 'yes' ? heightLimit : null }; var resizerJs = '(' + trim(resizer.toString().replace(/ /g, '\t')) + // indent with tabs From c8f4ed66bcbecbad80fce5ba9d6660eb73cbe0e2 Mon Sep 17 00:00:00 2001 From: Sahil Chinoy Date: Thu, 24 May 2018 14:32:44 -0400 Subject: [PATCH 3/5] sort artboards --- ai2html.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ai2html.js b/ai2html.js index 5468b84..6f05b1d 100644 --- a/ai2html.js +++ b/ai2html.js @@ -3645,7 +3645,7 @@ function getResizerScript(settings) { } function updateGraphic(container) { - var artboards = selectElements("." + nameSpace + "artboard[data-min-width]", container), + var artboards = selectElements("." + nameSpace + "artboard[data-min-width]", container); width = Math.round(container.getBoundingClientRect().width), height = Math.round(window.innerHeight), id = container.id, // assume container has an id @@ -3653,6 +3653,9 @@ function getResizerScript(settings) { heightLimit = opts.responsive_height_limit, maxHeight = Math.round((heightLimit / 100) * height); + // Sort from widest to narrowest + artboards.sort(function(a, b) { return b.getAttribute("data-min-width") - a.getAttribute("data-min-width")}); + // Set artboard visibility artboards.some(function(el, i) { var minwidth = el.getAttribute("data-min-width"), @@ -3783,8 +3786,9 @@ function getResizerScript(settings) { }; } }; - - // validate settings for responsive height + + // convert function to JS source code + // validate options for responsive height if (settings.use_responsive_height == 'yes') { var heightLimit = parseFloat(settings.responsive_height_limit) || null; // accepts 90% or 90 if (heightLimit > 100 || heightLimit < 0) { @@ -3792,7 +3796,6 @@ function getResizerScript(settings) { } } - // convert function to JS source code var opts = { namespace: nameSpace, environment: scriptEnvironment, From f2dc51c6b5ae2bdb92fb3652acb8a3371f890be0 Mon Sep 17 00:00:00 2001 From: Matthew Bloch Date: Thu, 31 May 2018 08:45:44 -0400 Subject: [PATCH 4/5] Refactored, added tests --- ai2html.js | 364 +++++++++++++++++++++++-------------------- package.json | 4 +- test/resizer-test.js | 190 ++++++++++++++++++++++ 3 files changed, 385 insertions(+), 173 deletions(-) create mode 100644 test/resizer-test.js diff --git a/ai2html.js b/ai2html.js index 6f05b1d..47b5b4a 100644 --- a/ai2html.js +++ b/ai2html.js @@ -82,7 +82,7 @@ var nytBaseSettings = { use_2x_images_if_possible: {defaultValue: "yes", includeInSettingsBlock: false, includeInConfigFile: false}, use_lazy_loader: {defaultValue: "yes", includeInSettingsBlock: true, includeInConfigFile: false}, use_responsive_height: {defaultValue: "no", includeInSettingsBlock: false, includeInConfigFile: false}, - responsive_height_limit: {defaultValue: null, includeInSettingsBlock: false, includeInConfigFile: false}, + responsive_height_limit: {defaultValue: "90%", includeInSettingsBlock: false, includeInConfigFile: false}, include_resizer_css_js: {defaultValue: "yes", includeInSettingsBlock: false, includeInConfigFile: false}, include_resizer_classes: {defaultValue: "no", includeInSettingsBlock: false, includeInConfigFile: false}, include_resizer_widths: {defaultValue: "yes", includeInSettingsBlock: false, includeInConfigFile: false}, @@ -140,7 +140,7 @@ var defaultBaseSettings = { use_2x_images_if_possible: {defaultValue: "yes", includeInSettingsBlock: false, includeInConfigFile: false}, use_lazy_loader: {defaultValue: "no", includeInSettingsBlock: false, includeInConfigFile: false}, use_responsive_height: {defaultValue: "no", includeInSettingsBlock: false, includeInConfigFile: false}, - responsive_height_limit: {defaultValue: null, includeInSettingsBlock: false, includeInConfigFile: false}, + responsive_height_limit: {defaultValue: "90%", includeInSettingsBlock: false, includeInConfigFile: false}, include_resizer_css_js: {defaultValue: "no", includeInSettingsBlock: false, includeInConfigFile: false}, include_resizer_classes: {defaultValue: "no", includeInSettingsBlock: false, includeInConfigFile: false}, include_resizer_widths: {defaultValue: "yes", includeInSettingsBlock: false, includeInConfigFile: false}, @@ -1132,7 +1132,8 @@ function exportFunctionsForTesting() { cleanObjectName, initDocumentSettings, uniqAssetName, - replaceSvgIds + replaceSvgIds, + findVisibleArtboard ].forEach(function(f) { module.exports[f.name] = f; }); @@ -3619,193 +3620,214 @@ function convertSettingsToYaml(settings) { } function getResizerScript(settings) { - // The resizer function is embedded in the HTML page -- external variables must - // be passed in. - - var resizer = function (opts) { - var nameSpace = opts.namespace; - // Use a sentinel class to ensure that this version of the resizer only initializes once - if (document.documentElement.className.indexOf(nameSpace + "resizer-v5-init") > -1) return; - document.documentElement.className += " " + nameSpace + "resizer-v5-init"; - // requires IE9+ - if (!("querySelector" in document)) return; - var observer = window.IntersectionObserver ? new IntersectionObserver(onIntersectionChange, {}) : null; - var visibilityIndex = {}; // visibility of each graphic, indexed by container id (used with InteractionObserver) - - updateAllGraphics(); - window.addEventListener('nyt:embed:load', updateAllGraphics); // for nyt vi compatibility - document.addEventListener("DOMContentLoaded", updateAllGraphics); - window.addEventListener("resize", throttle(updateAllGraphics, 200)); - - function updateAllGraphics() { - selectElements(".ai2html-box-v5").forEach(updateGraphic); - if (opts.environment == "nyt-preview") { - nytOnResize(); - } + var opts = { + namespace: nameSpace, + environment: scriptEnvironment, + responsiveness: settings.responsiveness, + responsive_height_limit: null + }; + var heightLimit; + + if (isTrue(settings.use_responsive_height)) { + // validate setting for responsive height + heightLimit = parseFloat(settings.responsive_height_limit); // accepts 90% or 90 + if ((heightLimit <= 150 && heightLimit >= 50) == false) { + error('responsive_height_limit must be between 50% and 150%.'); } + opts.responsive_height_limit = heightLimit; + } - function updateGraphic(container) { - var artboards = selectElements("." + nameSpace + "artboard[data-min-width]", container); - width = Math.round(container.getBoundingClientRect().width), - height = Math.round(window.innerHeight), - id = container.id, // assume container has an id - showImages = !observer || visibilityIndex[id] == 'visible', - heightLimit = opts.responsive_height_limit, - maxHeight = Math.round((heightLimit / 100) * height); - - // Sort from widest to narrowest - artboards.sort(function(a, b) { return b.getAttribute("data-min-width") - a.getAttribute("data-min-width")}); - - // Set artboard visibility - artboards.some(function(el, i) { - var minwidth = el.getAttribute("data-min-width"), - maxwidth = el.getAttribute("data-max-width") - aspectRatio = el.getAttribute("data-aspect-ratio"), - show = +minwidth <= width; - - // if the responsive height limit is on and responsiveness is dynamic, - // the width the artboard will display at is either the page width - // or the maxHeight * aspectRatio, whichever is smaller - if (heightLimit && opts.responsiveness == "dynamic") { - var widthAtMaxHeight = maxHeight * aspectRatio, - displayWidth = width > widthAtMaxHeight ? widthAtMaxHeight : width; - show = +minwidth <= displayWidth; - } + return '\r'; +} - // if responsive height limit is on and responsiveness is fixed - // the width the artboard will display at is the minimum width of the artboard - else if (heightLimit && opts.responsiveness == "fixed") { - // the height at this width is - var heightAtMinWidth = minwidth / aspectRatio; - // make sure it's not too tall - show = show && (heightAtMinWidth < maxHeight); - } +// Part of the embedded resizer function, placed here to allow testing. +// Returns an object containing metadata for the visible artboard, or null if +// no artboard fits in the available width. +function findVisibleArtboard(candidates, containerWidth, windowHeight, opts) { + // sort by ascending min width + candidates.sort(function(a, b) {return a.min_width - b.min_width;}); - // if none of the artboards are the right size, display the smallest one - var isSmallestArtboard = i == artboards.length - 1; - - if (show || isSmallestArtboard) { - if (showImages) { - selectElements("." + nameSpace + "aiImg", el).forEach(updateImgSrc); - } - // hide all the artboards - artboards.forEach(function(el) { el.style.display = 'none' }); - // show just this one - el.style.display = "block"; - // explicitly set width of the artboard if responsiveness is dynamic - if (heightLimit && opts.responsiveness == "dynamic") { el.style.width = displayWidth + "px" } - return true; - } - }); - - // Initialize lazy loading on first call, if IntersectionObserver is available - if (observer && !visibilityIndex[id]) { - if (containerIsVisible(container)) { - // Skip IntersectionObserver if graphic is initially visible - // Note: We're doing this after artboard visibility is first calculated - // (above) -- otherwise all artboard images are display:block and the - // calculated height of the graphic is huge. - // TODO: Consider making artboard images position:absolute and setting - // height as a padding % (calculated from the aspect ratio of the graphic). - // This will correctly set the initial height of the graphic before - // an image is loaded. - visibilityIndex[id] = 'visible'; - updateGraphic(container); // Call again to start loading right away - } else { - visibilityIndex[id] = 'hidden'; - observer.observe(container); - } - } - } + // pick the largest artboard that fits in the available width + return candidates.reduce(function(memo, data) { + var heightLimit, restrictedWidth; - function containerIsVisible(container) { - var bounds = container.getBoundingClientRect(); - return bounds.top < window.innerHeight && bounds.bottom > 0; - } + // reject this artboard if min width is larger than container width + if (data.min_width > containerWidth) return memo; - // Replace blank placeholder image with actual image - // (only relevant if use_lazy_loader option is true) - function updateImgSrc(img) { - var src = img.getAttribute("data-src"); - if (src && img.getAttribute("src") != src) { - img.setAttribute("src", src); + // handle responsive height + if (opts.responsive_height_limit) { + // calculate restricted width (in pixels) + heightLimit = windowHeight * opts.responsive_height_limit / 100; + restrictedWidth = Math.min(containerWidth, data.aspect_ratio * heightLimit); + + // handle vertical overflow caused by min width exceeding limit from responsive height + if (data.min_width > restrictedWidth) { + data.vertical_overflow = Math.round((data.min_width - restrictedWidth) / data.aspect_ratio); + + // reject this artboard if a previous candidate has a smaller overflow + if (memo && (memo.vertical_overflow || 0) < data.vertical_overflow) return memo; } - } - function onIntersectionChange(entries) { - entries.forEach(function(entry) { - if (entry.isIntersecting) { - visibilityIndex[entry.target.id] = 'visible'; - observer.unobserve(entry.target); - updateGraphic(entry.target); - } - }); + // calculate display width of height-restricted dynamic artboard + // (don't scale artboards to be narrower than their minimum widths, even if + // this violates the height limit) + if (opts.responsiveness == 'dynamic') { + data.display_width = Math.max(restrictedWidth, data.min_width); + } } - function selectElements(selector, parent) { - var selection = (parent || document).querySelectorAll(selector); - return Array.prototype.slice.call(selection); - } + // use this artboard (replacing any previous candidate) + // because artboards are sorted, wider artboards replace narrower ones + return data; + }, null); +} - function nytOnResize() { - // TODO: add comments - try { - if (window.parent && window.parent.$) { - window.parent.$("body").trigger("resizedcontent", [window]); - } - document.documentElement.dispatchEvent(new Event("resizedcontent")); - if (window.require && document.querySelector("meta[name=sourceApp]") && document.querySelector("meta[name=sourceApp]").content == "nyt-v5") { - require(["foundation/main"], function() { - require(["shared/interactive/instances/app-communicator"], function(AppCommunicator) { - AppCommunicator.triggerResize(); - }); - }); - } - } catch(e) { console.log(e); } +// The resizer function runs on the rendered HTML page -- it is unable to +// access functions or data in the ai2html program scope. +function ArtboardSwitcher(opts) { + var nameSpace = opts.namespace; + // Use a sentinel class to ensure that this version of the resizer only initializes once + if (document.documentElement.className.indexOf(nameSpace + "resizer-v6-init") > -1) return; + document.documentElement.className += " " + nameSpace + "resizer-v6-init"; + // requires IE9+ + if (!("querySelector" in document)) return; + var observer = window.IntersectionObserver ? new IntersectionObserver(onIntersectionChange, {}) : null; + var visibilityIndex = {}; // visibility of each graphic, indexed by container id (used with InteractionObserver) + + updateAllGraphics(); + window.addEventListener('nyt:embed:load', updateAllGraphics); // for nyt vi compatibility + document.addEventListener("DOMContentLoaded", updateAllGraphics); + window.addEventListener("resize", throttle(updateAllGraphics, 200)); + + function updateAllGraphics() { + selectElements(".ai2html-box-v6").forEach(updateGraphic); + if (opts.environment == "nyt-preview") { + nytOnResize(); } + } - // based on underscore.js - function throttle(func, wait) { - var _now = Date.now || function() { return +new Date(); }, - timeout = null, previous = 0; - var run = function() { - previous = _now(); - timeout = null; - func(); + function updateGraphic(container) { + var artboards = selectElements("." + nameSpace + "artboard[data-min-width]", container); + var id = container.id; // assume container has an id + var showImages = !observer || visibilityIndex[id] == 'visible'; + + // Pick which artboard to show + var artboardData = artboards.map(function(el, i) { + return { + id: i, + min_width: +el.getAttribute('data-min-width'), + aspect_ratio: +el.getAttribute('data-aspect-ratio') }; - return function() { - var remaining = wait - (_now() - previous); - if (remaining <= 0 || remaining > wait) { - if (timeout) { - clearTimeout(timeout); - } - run(); - } else if (!timeout) { - timeout = setTimeout(run, remaining); + }); + var containerWidth = Math.round(container.getBoundingClientRect().width); + var visibleTarget = findVisibleArtboard(artboardData, containerWidth, window.innerHeight, opts); + + // Set artboard visibility + artboards.forEach(function(el, i) { + if (visibleTarget && visibleTarget.id === i) { + if (showImages) { + selectElements("." + nameSpace + "aiImg", el).forEach(updateImgSrc); } - }; + if (visibleTarget.display_width) { + el.style.width = visibleTarget.display_width + "px"; + } + el.style.display = "block"; + } else { + el.style.display = "none"; + } + }); + + // Initialize lazy loading on first call, if IntersectionObserver is available + if (observer && !visibilityIndex[id]) { + if (containerIsVisible(container)) { + // Skip IntersectionObserver if graphic is initially visible + // Note: We're doing this after artboard visibility is first calculated + // (above) -- otherwise all artboard images are display:block and the + // calculated height of the graphic is huge. + // TODO: Consider making artboard images position:absolute and setting + // height as a padding % (calculated from the aspect ratio of the graphic). + // This will correctly set the initial height of the graphic before + // an image is loaded. + visibilityIndex[id] = 'visible'; + updateGraphic(container); // Call again to start loading right away + } else { + visibilityIndex[id] = 'hidden'; + observer.observe(container); + } } - }; + } - // convert function to JS source code - // validate options for responsive height - if (settings.use_responsive_height == 'yes') { - var heightLimit = parseFloat(settings.responsive_height_limit) || null; // accepts 90% or 90 - if (heightLimit > 100 || heightLimit < 0) { - error('responsive_height_limit must be between 0 and 100.'); + function containerIsVisible(container) { + var bounds = container.getBoundingClientRect(); + return bounds.top < window.innerHeight && bounds.bottom > 0; + } + + // Replace blank placeholder image with actual image + // (only relevant if use_lazy_loader option is true) + function updateImgSrc(img) { + var src = img.getAttribute("data-src"); + if (src && img.getAttribute("src") != src) { + img.setAttribute("src", src); } } - var opts = { - namespace: nameSpace, - environment: scriptEnvironment, - responsiveness: settings.responsiveness, - responsive_height_limit: settings.use_responsive_height == 'yes' ? heightLimit : null - }; - var resizerJs = '(' + - trim(resizer.toString().replace(/ /g, '\t')) + // indent with tabs - ')(' + JSON.stringify(opts) + ');'; - return '\r'; + function onIntersectionChange(entries) { + entries.forEach(function(entry) { + if (entry.isIntersecting) { + visibilityIndex[entry.target.id] = 'visible'; + observer.unobserve(entry.target); + updateGraphic(entry.target); + } + }); + } + + function selectElements(selector, parent) { + var selection = (parent || document).querySelectorAll(selector); + return Array.prototype.slice.call(selection); + } + + function nytOnResize() { + // TODO: add comments + try { + if (window.parent && window.parent.$) { + window.parent.$("body").trigger("resizedcontent", [window]); + } + document.documentElement.dispatchEvent(new Event("resizedcontent")); + if (window.require && document.querySelector("meta[name=sourceApp]") && document.querySelector("meta[name=sourceApp]").content == "nyt-v5") { + require(["foundation/main"], function() { + require(["shared/interactive/instances/app-communicator"], function(AppCommunicator) { + AppCommunicator.triggerResize(); + }); + }); + } + } catch(e) { console.log(e); } + } + + // based on underscore.js + function throttle(func, wait) { + var _now = Date.now || function() { return +new Date(); }, + timeout = null, previous = 0; + var run = function() { + previous = _now(); + timeout = null; + func(); + }; + return function() { + var remaining = wait - (_now() - previous); + if (remaining <= 0 || remaining > wait) { + if (timeout) { + clearTimeout(timeout); + } + run(); + } else if (!timeout) { + timeout = setTimeout(run, remaining); + } + }; + } } // Write an HTML page to a file for NYT Preview @@ -3868,7 +3890,7 @@ function generateOutputHtml(content, pageName, settings) { } // HTML - html = '
\r'; + html = '
\r'; if (linkSrc) { // optional link around content html += "\t\r"; diff --git a/package.json b/package.json index 7159b44..e500519 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,8 @@ "name": "ai2html", "version": "0.73.0", "description": "A script for Adobe Illustrator that converts your Illustrator artwork into an html page.", -"main": "./ai2html.js", -"scripts": { + "main": "./ai2html.js", + "scripts": { "test": "mocha --check-leaks", "prepublishOnly": "./prepublish.sh" }, diff --git a/test/resizer-test.js b/test/resizer-test.js new file mode 100644 index 0000000..bed951a --- /dev/null +++ b/test/resizer-test.js @@ -0,0 +1,190 @@ +var lib = require('../'), + assert = require('assert'), + findVisibleArtboard = lib.findVisibleArtboard; + +describe('findVisibleArtboard()', function() { + describe('no height limit', function () { + var data = [ + {id: 0, min_width: 300, aspect_ratio: 1}, + {id: 1, min_width: 600, aspect_ratio: 2}, + {id: 2, min_width: 900, aspect_ratio: 2}]; + + it('all fit container, "dynamic"', function () { + var opts = {responsiveness: 'dynamic', responsive_height_limit: null}; + var containerWidth = 900; + var windowHeight = 400; + var expect = {id: 2, min_width: 900, aspect_ratio: 2}; + var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + assert.deepEqual(result, expect); + }); + + it('middle ab fits container, "dynamic"', function () { + var opts = {responsiveness: 'dynamic', responsive_height_limit: null}; + var containerWidth = 899; + var windowHeight = 400; + var expect = {id: 1, min_width: 600, aspect_ratio: 2}; + var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + assert.deepEqual(result, expect); + }); + + it('middle ab fits container, "fixed"', function () { + var opts = {responsiveness: 'fixed', responsive_height_limit: null}; + var containerWidth = 899; + var windowHeight = 400; + var expect = {id: 1, min_width: 600, aspect_ratio: 2}; + var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + assert.deepEqual(result, expect); + }); + + it('only smallest ab fits container, "dynamic"', function () { + var opts = {responsiveness: 'dynamic', responsive_height_limit: null}; + var containerWidth = 300; + var windowHeight = 400; + var expect = {id: 0, min_width: 300, aspect_ratio: 1}; + var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + assert.deepEqual(result, expect); + }); + + it('no artboards fit the container, "dynamic"', function () { + var opts = {responsiveness: 'dynamic', responsive_height_limit: null}; + var containerWidth = 299; + var windowHeight = 400; + var expect = null; + var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + assert.deepEqual(result, expect); + }); + + }); + + describe('responsive_height_limit is set', function () { + + it('height-limited, "fixed", two artboards overflow height limit', function () { + var data = [ + // two abs have same overflow -> pick the widest one + {id: 0, min_width: 300, aspect_ratio: 1}, + {id: 1, min_width: 600, aspect_ratio: 2}, + {id: 2, min_width: 900, aspect_ratio: 2}]; + var opts = {responsiveness: 'fixed', responsive_height_limit: 90}; + var containerWidth = 1000; + var windowHeight = 300; // limit is 270px + // two abs have same overflow -> choose the wider one + var expect = { + id: 1, min_width: 600, aspect_ratio: 2, vertical_overflow: 30}; + var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + assert.deepEqual(result, expect); + }); + + it('height-limited, "dynamic", two artboards overflow limit (2)', function () { + var data = [ + // smallest ab has smaller overflow -> choose it + {id: 0, min_width: 300, aspect_ratio: 1}, + {id: 1, min_width: 600, aspect_ratio: 1.99}, + {id: 2, min_width: 900, aspect_ratio: 2}]; + var opts = {responsiveness: 'dynamic', responsive_height_limit: 90}; + var containerWidth = 1000; + var windowHeight = 300; + + var expect = { + id: 0, min_width: 300, aspect_ratio: 1, display_width: 300, vertical_overflow: 30}; + var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + assert.deepEqual(result, expect); + }); + + it('height-limited, "dynamic", two artboards overflow limit (3)', function () { + var data = [ + // middle ab has smaller overflow than smallest -> choose it + {id: 0, min_width: 300, aspect_ratio: 0.99}, + {id: 1, min_width: 600, aspect_ratio: 2}, + {id: 2, min_width: 900, aspect_ratio: 2}]; + var opts = {responsiveness: 'dynamic', responsive_height_limit: 90}; + var containerWidth = 1000; + var windowHeight = 300; + + var expect = { + id: 1, min_width: 600, aspect_ratio: 2, display_width: 600, vertical_overflow: 30}; + var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + assert.deepEqual(result, expect); + }); + + it('"dynamic", middle ab picked with limiting', function () { + var data = [ + {id: 0, min_width: 300, aspect_ratio: 1}, + {id: 1, min_width: 600, aspect_ratio: 1.5}, + {id: 2, min_width: 900, aspect_ratio: 1.5}]; + var opts = {responsiveness: 'dynamic', responsive_height_limit: 100}; + var containerWidth = 1000; + var windowHeight = 500; + var expect = {id: 1, min_width: 600, aspect_ratio: 1.5, display_width: 750}; + var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + assert.deepEqual(result, expect); + }); + + it('"fixed", middle ab picked with limiting', function () { + // same as above dynamic example, middle ab is selected + var data = [ + {id: 0, min_width: 300, aspect_ratio: 1}, + {id: 1, min_width: 600, aspect_ratio: 1.5}, + {id: 2, min_width: 900, aspect_ratio: 1.5}]; + var opts = {responsiveness: 'fixed', responsive_height_limit: 100}; + var containerWidth = 1000; + var windowHeight = 500; + var expect = {id: 1, min_width: 600, aspect_ratio: 1.5}; + var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + assert.deepEqual(result, expect); + }); + + it('"fixed", smallest ab picked with limiting', function () { + // same as above dynamic example, different ab is selected + var data = [ + {id: 0, min_width: 300, aspect_ratio: 1}, + {id: 1, min_width: 600, aspect_ratio: 1.5}, + {id: 2, min_width: 900, aspect_ratio: 1.5}]; + var opts = {responsiveness: 'fixed', responsive_height_limit: 100}; + var containerWidth = 1000; + var windowHeight = 350; + var expect = {id: 0, min_width: 300, aspect_ratio: 1}; + var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + assert.deepEqual(result, expect); + }); + + it('"dynamic", middle ab picked without limiting', function () { + var data = [ + {id: 0, min_width: 300, aspect_ratio: 1}, + {id: 1, min_width: 600, aspect_ratio: 1.5}, + {id: 2, min_width: 900, aspect_ratio: 1.5}]; + var opts = {responsiveness: 'dynamic', responsive_height_limit: 100}; + var containerWidth = 700; + var windowHeight = 500; + var expect = {id: 1, min_width: 600, aspect_ratio: 1.5, display_width: 700}; + var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + assert.deepEqual(result, expect); + }); + + it('"dynamic", middle ab picked without limiting, abs in reverse order', function () { + var data = [ + {id: 0, min_width: 900, aspect_ratio: 1.5}, + {id: 1, min_width: 600, aspect_ratio: 1.5}, + {id: 2, min_width: 300, aspect_ratio: 1}]; + var opts = {responsiveness: 'dynamic', responsive_height_limit: 100}; + var containerWidth = 700; + var windowHeight = 500; + var expect = {id: 1, min_width: 600, aspect_ratio: 1.5, display_width: 700}; + var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + assert.deepEqual(result, expect); + }); + + it('"dynamic", largest ab picked without limiting', function () { + var data = [ + {id: 0, min_width: 300, aspect_ratio: 1}, + {id: 1, min_width: 600, aspect_ratio: 1.5}, + {id: 2, min_width: 900, aspect_ratio: 1.5}]; + var opts = {responsiveness: 'dynamic', responsive_height_limit: 100}; + var containerWidth = 1000; + var windowHeight = 800; + var expect = {id: 2, min_width: 900, aspect_ratio: 1.5, display_width: 1000}; + var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + assert.deepEqual(result, expect); + }); + + }) +}); From 10ef404683b712e21bd8d65bfe73a396ab76a986 Mon Sep 17 00:00:00 2001 From: Matthew Bloch Date: Tue, 5 Jun 2018 16:05:54 -0400 Subject: [PATCH 5/5] Update resizer function to handle multiple visible artboards --- ai2html.js | 84 +++++++++++++--------- test/resizer-test.js | 161 +++++++++++++++++++++++++++++++++---------- 2 files changed, 176 insertions(+), 69 deletions(-) diff --git a/ai2html.js b/ai2html.js index 47b5b4a..e6c441c 100644 --- a/ai2html.js +++ b/ai2html.js @@ -1133,7 +1133,7 @@ function exportFunctionsForTesting() { initDocumentSettings, uniqAssetName, replaceSvgIds, - findVisibleArtboard + findVisibleArtboards ].forEach(function(f) { module.exports[f.name] = f; }); @@ -3639,51 +3639,69 @@ function getResizerScript(settings) { return '\r'; } // Part of the embedded resizer function, placed here to allow testing. -// Returns an object containing metadata for the visible artboard, or null if -// no artboard fits in the available width. -function findVisibleArtboard(candidates, containerWidth, windowHeight, opts) { - // sort by ascending min width - candidates.sort(function(a, b) {return a.min_width - b.min_width;}); - - // pick the largest artboard that fits in the available width - return candidates.reduce(function(memo, data) { - var heightLimit, restrictedWidth; - - // reject this artboard if min width is larger than container width - if (data.min_width > containerWidth) return memo; +// Returns an object with data about the visible artboard(s), or null +// if no artboard fits in the available width. +function findVisibleArtboards(candidates, containerWidth, windowHeight, opts) { + var heightLimit = null; + if (opts.responsive_height_limit) { + // calc height limit in pixels + heightLimit = windowHeight * opts.responsive_height_limit / 100; + } + + // reject artboards whose min width is larger than container width + candidates = candidates.filter(function(ab) {return ab.min_width <= containerWidth;}); + + // sort artboards by ascending min width, then by ascending aspect ratio + candidates.sort(function(a, b) {return a.min_width - b.min_width || a.aspect_ratio - b.aspect_ratio;}); + + // pick the best-fit group of same-width artboards to show + return groupByMinWidth(candidates).reduce(function(memo, group) { + var firstMember = group[0]; + var groupData = { + ids: group.map(function(item) {return item.id;}), + min_width: firstMember.min_width + }; + var restrictedWidth; // handle responsive height - if (opts.responsive_height_limit) { - // calculate restricted width (in pixels) - heightLimit = windowHeight * opts.responsive_height_limit / 100; - restrictedWidth = Math.min(containerWidth, data.aspect_ratio * heightLimit); - - // handle vertical overflow caused by min width exceeding limit from responsive height - if (data.min_width > restrictedWidth) { - data.vertical_overflow = Math.round((data.min_width - restrictedWidth) / data.aspect_ratio); - - // reject this artboard if a previous candidate has a smaller overflow - if (memo && (memo.vertical_overflow || 0) < data.vertical_overflow) return memo; + if (heightLimit) { + // calculate restricted width of group, based on aspect ratio of first member + // (group members are sorted with tallest ab first) + restrictedWidth = Math.min(containerWidth, firstMember.aspect_ratio * heightLimit); + if (firstMember.min_width > restrictedWidth) { + groupData.vertical_overflow = Math.round((firstMember.min_width - restrictedWidth) / firstMember.aspect_ratio); + // reject this group if previous group has a smaller overflow + if (memo && (memo.vertical_overflow || 0) < groupData.vertical_overflow) { + return memo; + } } // calculate display width of height-restricted dynamic artboard // (don't scale artboards to be narrower than their minimum widths, even if // this violates the height limit) if (opts.responsiveness == 'dynamic') { - data.display_width = Math.max(restrictedWidth, data.min_width); + groupData.display_width = Math.max(restrictedWidth, firstMember.min_width); } } - // use this artboard (replacing any previous candidate) - // because artboards are sorted, wider artboards replace narrower ones - return data; + // use this group (wider groups replace narrower ones) + return groupData; }, null); + + function groupByMinWidth(arr) { + return arr.reduce(function(memo, a, i) { + var group = i > 0 && arr[i-1].min_width == a.min_width ? memo.pop() : []; + group.push(a); + memo.push(group); + return memo; + }, []); + } } // The resizer function runs on the rendered HTML page -- it is unable to @@ -3724,16 +3742,16 @@ function ArtboardSwitcher(opts) { }; }); var containerWidth = Math.round(container.getBoundingClientRect().width); - var visibleTarget = findVisibleArtboard(artboardData, containerWidth, window.innerHeight, opts); + var target = findVisibleArtboards(artboardData, containerWidth, window.innerHeight, opts); // Set artboard visibility artboards.forEach(function(el, i) { - if (visibleTarget && visibleTarget.id === i) { + if (target && target.ids.indexOf(i) > -1) { if (showImages) { selectElements("." + nameSpace + "aiImg", el).forEach(updateImgSrc); } - if (visibleTarget.display_width) { - el.style.width = visibleTarget.display_width + "px"; + if (target.display_width) { + el.style.width = target.display_width + "px"; } el.style.display = "block"; } else { diff --git a/test/resizer-test.js b/test/resizer-test.js index bed951a..af18118 100644 --- a/test/resizer-test.js +++ b/test/resizer-test.js @@ -1,8 +1,8 @@ var lib = require('../'), assert = require('assert'), - findVisibleArtboard = lib.findVisibleArtboard; + findVisibleArtboards = lib.findVisibleArtboards; -describe('findVisibleArtboard()', function() { +describe('findVisibleArtboards()', function() { describe('no height limit', function () { var data = [ {id: 0, min_width: 300, aspect_ratio: 1}, @@ -13,8 +13,8 @@ describe('findVisibleArtboard()', function() { var opts = {responsiveness: 'dynamic', responsive_height_limit: null}; var containerWidth = 900; var windowHeight = 400; - var expect = {id: 2, min_width: 900, aspect_ratio: 2}; - var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + var expect = {ids: [2], min_width: 900}; + var result = findVisibleArtboards(data, containerWidth, windowHeight, opts); assert.deepEqual(result, expect); }); @@ -22,8 +22,8 @@ describe('findVisibleArtboard()', function() { var opts = {responsiveness: 'dynamic', responsive_height_limit: null}; var containerWidth = 899; var windowHeight = 400; - var expect = {id: 1, min_width: 600, aspect_ratio: 2}; - var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + var expect = {ids: [1], min_width: 600}; + var result = findVisibleArtboards(data, containerWidth, windowHeight, opts); assert.deepEqual(result, expect); }); @@ -31,8 +31,8 @@ describe('findVisibleArtboard()', function() { var opts = {responsiveness: 'fixed', responsive_height_limit: null}; var containerWidth = 899; var windowHeight = 400; - var expect = {id: 1, min_width: 600, aspect_ratio: 2}; - var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + var expect = {ids: [1], min_width: 600}; + var result = findVisibleArtboards(data, containerWidth, windowHeight, opts); assert.deepEqual(result, expect); }); @@ -40,8 +40,8 @@ describe('findVisibleArtboard()', function() { var opts = {responsiveness: 'dynamic', responsive_height_limit: null}; var containerWidth = 300; var windowHeight = 400; - var expect = {id: 0, min_width: 300, aspect_ratio: 1}; - var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + var expect = {ids: [0], min_width: 300}; + var result = findVisibleArtboards(data, containerWidth, windowHeight, opts); assert.deepEqual(result, expect); }); @@ -50,10 +50,37 @@ describe('findVisibleArtboard()', function() { var containerWidth = 299; var windowHeight = 400; var expect = null; - var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); - assert.deepEqual(result, expect); + var result = findVisibleArtboards(data, containerWidth, windowHeight, opts); + assert.equal(result, expect); }); + describe('artboards with identical widths appear together', function() { + var data = [ + {id: 0, min_width: 300, aspect_ratio: 1}, + {id: 1, min_width: 300, aspect_ratio: 1}, + {id: 2, min_width: 900, aspect_ratio: 2}, + {id: 3, min_width: 900, aspect_ratio: 2}]; + + it('only smallest group fits container, "dynamic"', function () { + var opts = {responsiveness: 'dynamic', responsive_height_limit: null}; + var containerWidth = 300; + var windowHeight = 400; + var expect = {ids: [0, 1], min_width: 300}; + var result = findVisibleArtboards(data, containerWidth, windowHeight, opts); + assert.deepEqual(result, expect); + }); + + it('all groups fit container, "dynamic"', function () { + var opts = {responsiveness: 'dynamic', responsive_height_limit: null}; + var containerWidth = 900; + var windowHeight = 400; + var expect = {ids: [2, 3], min_width: 900}; + var result = findVisibleArtboards(data, containerWidth, windowHeight, opts); + assert.deepEqual(result, expect); + }); + + }); + }); describe('responsive_height_limit is set', function () { @@ -68,9 +95,8 @@ describe('findVisibleArtboard()', function() { var containerWidth = 1000; var windowHeight = 300; // limit is 270px // two abs have same overflow -> choose the wider one - var expect = { - id: 1, min_width: 600, aspect_ratio: 2, vertical_overflow: 30}; - var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + var expect = {ids: [1], min_width: 600, vertical_overflow: 30}; + var result = findVisibleArtboards(data, containerWidth, windowHeight, opts); assert.deepEqual(result, expect); }); @@ -83,10 +109,8 @@ describe('findVisibleArtboard()', function() { var opts = {responsiveness: 'dynamic', responsive_height_limit: 90}; var containerWidth = 1000; var windowHeight = 300; - - var expect = { - id: 0, min_width: 300, aspect_ratio: 1, display_width: 300, vertical_overflow: 30}; - var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + var expect = {ids: [0], min_width: 300, vertical_overflow: 30, display_width: 300}; + var result = findVisibleArtboards(data, containerWidth, windowHeight, opts); assert.deepEqual(result, expect); }); @@ -99,10 +123,8 @@ describe('findVisibleArtboard()', function() { var opts = {responsiveness: 'dynamic', responsive_height_limit: 90}; var containerWidth = 1000; var windowHeight = 300; - - var expect = { - id: 1, min_width: 600, aspect_ratio: 2, display_width: 600, vertical_overflow: 30}; - var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + var expect = {ids: [1], min_width: 600, display_width: 600, vertical_overflow: 30}; + var result = findVisibleArtboards(data, containerWidth, windowHeight, opts); assert.deepEqual(result, expect); }); @@ -114,8 +136,8 @@ describe('findVisibleArtboard()', function() { var opts = {responsiveness: 'dynamic', responsive_height_limit: 100}; var containerWidth = 1000; var windowHeight = 500; - var expect = {id: 1, min_width: 600, aspect_ratio: 1.5, display_width: 750}; - var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + var expect = {ids: [1], min_width: 600, display_width: 750}; + var result = findVisibleArtboards(data, containerWidth, windowHeight, opts); assert.deepEqual(result, expect); }); @@ -128,8 +150,8 @@ describe('findVisibleArtboard()', function() { var opts = {responsiveness: 'fixed', responsive_height_limit: 100}; var containerWidth = 1000; var windowHeight = 500; - var expect = {id: 1, min_width: 600, aspect_ratio: 1.5}; - var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + var expect = {ids: [1], min_width: 600}; + var result = findVisibleArtboards(data, containerWidth, windowHeight, opts); assert.deepEqual(result, expect); }); @@ -142,8 +164,8 @@ describe('findVisibleArtboard()', function() { var opts = {responsiveness: 'fixed', responsive_height_limit: 100}; var containerWidth = 1000; var windowHeight = 350; - var expect = {id: 0, min_width: 300, aspect_ratio: 1}; - var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + var expect = {ids: [0], min_width: 300}; + var result = findVisibleArtboards(data, containerWidth, windowHeight, opts); assert.deepEqual(result, expect); }); @@ -155,8 +177,8 @@ describe('findVisibleArtboard()', function() { var opts = {responsiveness: 'dynamic', responsive_height_limit: 100}; var containerWidth = 700; var windowHeight = 500; - var expect = {id: 1, min_width: 600, aspect_ratio: 1.5, display_width: 700}; - var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + var expect = {ids: [1], min_width: 600, display_width: 700}; + var result = findVisibleArtboards(data, containerWidth, windowHeight, opts); assert.deepEqual(result, expect); }); @@ -168,8 +190,8 @@ describe('findVisibleArtboard()', function() { var opts = {responsiveness: 'dynamic', responsive_height_limit: 100}; var containerWidth = 700; var windowHeight = 500; - var expect = {id: 1, min_width: 600, aspect_ratio: 1.5, display_width: 700}; - var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + var expect = {ids: [1], min_width: 600, display_width: 700}; + var result = findVisibleArtboards(data, containerWidth, windowHeight, opts); assert.deepEqual(result, expect); }); @@ -181,10 +203,77 @@ describe('findVisibleArtboard()', function() { var opts = {responsiveness: 'dynamic', responsive_height_limit: 100}; var containerWidth = 1000; var windowHeight = 800; - var expect = {id: 2, min_width: 900, aspect_ratio: 1.5, display_width: 1000}; - var result = findVisibleArtboard(data, containerWidth, windowHeight, opts); + var expect = {ids: [2], min_width: 900, display_width: 1000}; + var result = findVisibleArtboards(data, containerWidth, windowHeight, opts); + assert.deepEqual(result, expect); + }); + + }); + + describe('responsive_height_limit is set, multiple artboards have the same width', function () { + it('"dynamic", middle abs picked with limiting', function () { + var data = [ + {id: 0, min_width: 300, aspect_ratio: 1}, + {id: 1, min_width: 300, aspect_ratio: 1}, + {id: 2, min_width: 600, aspect_ratio: 1.5}, + {id: 3, min_width: 600, aspect_ratio: 1.5}, + {id: 4, min_width: 900, aspect_ratio: 1.5}, + {id: 5, min_width: 900, aspect_ratio: 1.5}]; + var opts = {responsiveness: 'dynamic', responsive_height_limit: 100}; + var containerWidth = 1000; + var windowHeight = 500; + var expect = {ids: [2, 3], min_width: 600, display_width: 750}; + var result = findVisibleArtboards(data, containerWidth, windowHeight, opts); assert.deepEqual(result, expect); }); - }) + it('"dynamic", first abs picked (based on aspect ratio of tallest member of group)', function () { + var data = [ + {id: 0, min_width: 300, aspect_ratio: 1}, + {id: 1, min_width: 300, aspect_ratio: 1}, + {id: 2, min_width: 600, aspect_ratio: 1}, // changed from above + {id: 3, min_width: 600, aspect_ratio: 1.5}, + {id: 4, min_width: 900, aspect_ratio: 1.5}, + {id: 5, min_width: 900, aspect_ratio: 1.5}]; + var opts = {responsiveness: 'dynamic', responsive_height_limit: 100}; + var containerWidth = 1000; + var windowHeight = 500; + var expect = {ids: [0, 1], min_width: 300, display_width: 500}; + var result = findVisibleArtboards(data, containerWidth, windowHeight, opts); + assert.deepEqual(result, expect); + }); + + it('"dynamic", first abs picked with limiting (based on aspect ratio of tallest member of group)', function () { + var data = [ + {id: 0, min_width: 300, aspect_ratio: 1}, + {id: 1, min_width: 300, aspect_ratio: 1}, + {id: 2, min_width: 600, aspect_ratio: 1.5}, + {id: 3, min_width: 600, aspect_ratio: 1}, // changed + {id: 4, min_width: 900, aspect_ratio: 1.5}, + {id: 5, min_width: 900, aspect_ratio: 1.5}]; + var opts = {responsiveness: 'dynamic', responsive_height_limit: 100}; + var containerWidth = 1000; + var windowHeight = 500; + var expect = {ids: [0, 1], min_width: 300, display_width: 500}; + var result = findVisibleArtboards(data, containerWidth, windowHeight, opts); + assert.deepEqual(result, expect); + }); + + it('"dynamic", second abs picked (based on aspect ratio of tallest member of group)', function () { + var data = [ + {id: 0, min_width: 300, aspect_ratio: 1}, + {id: 1, min_width: 300, aspect_ratio: 1}, + {id: 2, min_width: 600, aspect_ratio: 1.5}, + {id: 3, min_width: 600, aspect_ratio: 1}, // changed + {id: 4, min_width: 900, aspect_ratio: 1.5}, + {id: 5, min_width: 900, aspect_ratio: 1}]; // changed + var opts = {responsiveness: 'dynamic', responsive_height_limit: 100}; + var containerWidth = 1000; + var windowHeight = 650; // changed + var expect = {ids: [3, 2], min_width: 600, display_width: 650}; + var result = findVisibleArtboards(data, containerWidth, windowHeight, opts); + assert.deepEqual(result, expect); + }); + }); + });