UNPKG

comicgen

Version:

Create comics for your website or app

210 lines (196 loc) 8.59 kB
/* globals comicgen, showdown, hljs, ClipboardJS, PlainDraggable, saveSvgAsPng */ // If the URL hash has a path, it's a non-home tab. Change to it and exit var url = g1.url.parse(location.hash.replace(/^#/, '')) $('a[href="#' + url.pathname + '"]').tab('show') // Render part of README.md as Markdown. Sections delimited by <!-- var ... !>...<!-- end --> $.get('README.md') .done(function (text) { var converter = new showdown.Converter({ ghCodeBlocks: true, tables: true }) text.match(/<!--\s*var\s+\S*\s*-->[\s\S]*?<!--\s*end\s*-->/igm).forEach(function (match) { var lines = match.split(/\n/) var name = lines[0].replace(/^<!--\s*var\s+/, '').replace(/\s*-->$/, '') $('md[data-target="' + name + '"]') .addClass('d-block my-4') .html(converter.makeHtml(lines.slice(1, -1).join('\n'))) .find('pre') .each(function () { hljs.highlightBlock(this) }) }) $('md table').addClass('table table-sm') .find('a').each(function () { $(this).attr('target', '_blank') }) }) // q holds the current state of the application, and the comicgen parameters var q var defaults = comicgen.defaults $.getJSON('src/files.json') .done(function (data) { // Any change in selection changes the URL $('.selector').on('change', ':input', function () { location.hash = '?' + $('.selector').serialize() }) // Any tab selection updates the URL path without hashchange $('#comic-tab').on('shown.bs.tab', function (e) { var url = g1.url.parse(location.hash.replace(/^#/, '')) url.pathname = $(e.target).attr('href').replace(/^#/, '') url.pathname = url.pathname == 'home' ? '' : url.pathname location.hash = url.toString() }) // Any change in URL re-renders the strip $(window).on('#', function (e, url) { var node = data // Render the dropdowns q = url.searchKey $('.comicgen-attrs .attr').addClass('wip') // Everything starts with a name dropdown_options(q, 'name', node) node = node[q.name] var format = comicgen.formats[comicgen.namemap[q.name]] format.dirs.forEach(function (attr) { dropdown_options(q, attr, node) node = node[q[attr]] }) // Render dropdowns for each of the files. Use order in URL _.each(Object.assign({}, q, format.files), function (val, attr) { if (attr in format.files) { format.files[attr]['continuous'] ? slider_options(q, attr, node[attr]) : dropdown_options(q, attr, node[attr]) } }) dropdown_options(q, 'ext', ['svg', 'png']) dropdown_options(q, 'mirror', { '': '', 'mirror': '1' }) $('.comicgen-attrs .wip').remove() comicgen('.target', q) $('.target-container').css({ width: q.width + 'px', height: q.height + 'px' }) for (var key in defaults) { $('input[name="' + key + '"]').val(q[key] || defaults[key]) // If a value is the same as the default, drop it if (q[key] == defaults[key]) delete q[key] } $('.codegen').template({q: q}) }).urlchange() }) // Dragging the target image changes the x, y var pos new PlainDraggable($('.target').get(0), { containment: {left: -10000, top: -10000, width: 50000, height: 50000}, onDragStart: function () { pos = {left: this.left, top: this.top} }, onDragEnd: function () { $('input[name="x"]').val(Math.round(+(q.x || defaults.x) + this.left - pos.left)) $('input[name="y"]').val(Math.round(+(q.y || defaults.y) + this.top - pos.top)) location.hash = '?' + $('.selector').serialize() this.left = pos.left this.top = pos.top } }) // Zooming target changes scale $('.target').on('wheel', function (e) { if (!e.ctrlKey) return var scale0 = +(q.scale || defaults.scale) var scale = Math.round(scale0 * (1 - e.originalEvent.deltaY * 0.01) * 100, 2) / 100 if (scale == scale0 && e.originalEvent.deltaY < 0) scale = scale0 + 0.01 $('input[name="scale"]').val(scale) // Scale around the center $('input[name="x"]').val(Math.round(+(q.x || defaults.x) + (+q.width || defaults.width) * (scale0 - scale) / 2)) $('input[name="y"]').val(Math.round(+(q.y || defaults.y) + (+q.height || defaults.height) * (scale0 - scale) / 2)) location.hash = '?' + $('.selector').serialize() e.preventDefault() }) // Reset button reverts to defaults on size, position & scale $('.reset').attr('href', '?' + $.param(defaults)) $('body').urlfilter() // Change background / stroke colors if requested $('.bg-color').on('input, change', function () { // The first rule in the first sheet defines the background color for the target-container document.styleSheets[0].cssRules[0].style.backgroundColor = $(this).val() }) // Click on copy button to copy code new ClipboardJS('.copy') // Click on download button to download in the current format (SVG or PNG) $('.download').on('click', function () { var ext = $(':input[name="ext"]').val() var $target = $('.target > svg') // filename = all character attributes ... var filename = $('.comicgen-attrs select').map(function () { return $(this).val() }) // ... except the last 2 (ext, mirror) filename = filename.get().slice(0, -2).join('-') if (ext == 'png') saveSvgAsPng($target.get(0), filename + '.png') else if (ext == 'svg') { // Create a copy of the SVG and replace the <image>s with the actual HTML var $svg = $target.clone() $svg.attr('xmlns', 'http://www.w3.org/2000/svg') // Replace each image with the actual SVG var $images = $svg.find('image') $.when.apply($, $images.map(function () { return this.href.baseVal }).get().map($.get)) .done(function () { var docs = _.map(arguments, function (v) { return v[2].responseText }) $images.each(function (i) { var $this = $(this) $this.replaceWith('<g transform="' + $this.attr('transform') + '">' + docs[i] + '</g>') }) var link = document.createElement('a') link.href = URL.createObjectURL(new Blob([ '<?xml version="1.0" standalone="no"?>\r\n', $svg.get(0).outerHTML ], { type: 'image/svg+xml;charset=utf-8' })) link.download = filename + '.svg' document.body.appendChild(link) link.click() document.body.removeChild(link) }) } }) var template_arrows = _.template($('.arrows').html()) // Utility: Set a default value for q[key] using data. Render <select> dropdown using data function dropdown_options(q, key, data) { data = _.isArray(data) ? data : _.keys(data) // If q[key] is not in data, pick the first item from the data list/dict q[key] = q[key] && data.indexOf(q[key]) > 0 ? q[key] : data[0] var options = data.map(function (v) { return '<option>' + v + '</option>' }).join('') var $el = $('.comicgen-attrs .attr.input[name="' + key + '"]').removeClass('wip') if (!$el.length) { $el = $('<div>').addClass('attr input mr-2 mb-2').attr('name', key) $el.append($(template_arrows({ key: key }))) $el.append($('<select>').addClass('form-control').attr('name', key)) var $after = $('.comicgen-attrs .attr:not(.wip):last') if ($after.length) $el.insertAfter($after) else $el.appendTo('.comicgen-attrs') } $el.find('select').html(options).val(q[key]) } function slider_options(q, key, data) { q[key] = isNaN(q[key]) ? 0 : +q[key] var $el = $('.comicgen-attrs .attr.slider[name="' + key + '"]').removeClass('wip') if (!$el.length) { $el = $('<div>').addClass('attr slider mr-2 mb-2').attr('name', key) $el.append($(template_arrows({ key: key + '-' + data.join('-') }))) $el.append($('<input type="range" min="0" max="1" step="0.01" list="slider-ticks">').addClass('form-control-range').attr('name', key)) var $after = $('.comicgen-attrs .attr:not(.wip):last') if ($after.length) $el.insertAfter($after) else $el.appendTo('.comicgen-attrs') } $('.attr[name="' + key + '"] input').val(q[key]) } // Arrow buttons move _.each( [ {left_or_right: '.move-left', target: 'prev', insert: 'insertBefore'}, {left_or_right: '.move-right', target: 'next', insert: 'insertAfter'}, ], function (conf) { $('.comicgen-attrs').on('click', conf.left_or_right, function () { var $this = $(this).parents('.attr') var $target = $this[conf.target]() if ($target.length) { $this[conf.insert]($target) location.hash = '?' + $('.selector').serialize() $(window).trigger('#', g1.url.parse(location.hash.replace(/^#/, ''))) } }) })