UNPKG

stitches

Version:

Stitches is an HTML5 sprite sheet generator.

724 lines (640 loc) 23.6 kB
/** * # module/stitches * * Constructor for the stitches module, which encapsulates all of the UI * functionality. Instantiated with a DOM element, into which all of the * markup is injected and to which the behaviors are attached. Typically * used in a DOM ready callback * * > http://draeton.github.com/stitches<br/> * > Copyright 2013 Matthew Cobbs<br/> * > Licensed under the MIT license. */ define([ "wrap/jquery", "wrap/modernizr", "../../../libs/store/store", "util/util", "util/templates", "manager/file", "manager/layout", "manager/stylesheet", "module/drop-box", "module/canvas", "module/toolbar", "module/palette" ], function($, Modernizr, store, util, templates, fileManager, layoutManager, stylesheetManager, DropBox, Canvas, Toolbar, Palette) { "use strict"; var defaults = { layout: "compact", // default canvas sprite placement layout prefix: "sprite", // default stylesheet class prefix padding: 5, // default padding around sprites in pixels uri: false, // whether or not to include the data-uri image (quite large) stylesheet: "css" // either css or less at the moment }; /** * ## Stitches * Create a new `Stitches` instance * * @constructor * @param {element} element * @param {object} options */ var Stitches = function (element, options) { this.$element = $(element); this.settings = $.extend({}, defaults, options); this.init(); }; Stitches.prototype = { constructor: Stitches, /** * ### @init * Run methods to prepare the instance for use */ init: function () { this.configure(); this.render(); this.test(); this.bind(); this.setDropBox(); this.setToolbar(); this.setImages(); this.setCanvas(); this.setManagers(); this.setPalettes(); this.canvas.init(); }, /** * ### @configure * If available, load settings saved by store.js */ configure: function () { var settings; if (store && !store.disabled) { settings = store.get("stitches-settings"); } if (settings) { this.settings = $.extend(this.settings, settings); } }, /** * ### @render * Render html using a JS template and grab references */ render: function () { var html = templates.stitches({}); this.$element.append(html); this.$overlay = this.$element.find(".stitches-overlay"); this.$dropBox = this.$element.find(".stitches-drop-box"); this.$toolbar = this.$element.find(".stitches-toolbar"); this.$canvas = this.$element.find(".stitches-canvas"); this.$progress = this.$element.find(".stitches-progress .progress"); this.$progressBar = this.$element.find(".stitches-progress .bar"); this.$about = this.$element.find(".stitches-about"); this.$downloads = this.$element.find(".stitches-downloads"); this.$settings = this.$element.find(".stitches-settings"); this.$properties = this.$element.find(".stitches-properties"); }, /** * ### @test * JS-rendered file inputs aren't useable in every browser. Probably * needs a workaround. At the moment, this test disables that feature */ test: function () { this.hasFileInput = this.$element.find("input.file").length; }, /** * ### @bind * Bind event handlers to DOM element. $.proxy is used to retain * this instance as the callback execution context */ bind: function () { this.$element.on("show-overlay", $.proxy(this.showOverlay, this)); this.$element.on("hide-overlay", $.proxy(this.hideOverlay, this)); this.$element.on("open-about", $.proxy(this.openAbout, this)); this.$element.on("close-about", $.proxy(this.closeAbout, this)); this.$element.on("open-downloads", $.proxy(this.openDownloads, this)); this.$element.on("close-downloads", $.proxy(this.closeDownloads, this)); this.$element.on("open-settings", $.proxy(this.openSettings, this)); this.$element.on("close-settings", $.proxy(this.closeSettings, this)); this.$element.on("open-properties", $.proxy(this.openProperties, this)); this.$element.on("close-properties", $.proxy(this.closeProperties, this)); this.$element.on("close-palettes", $.proxy(this.closePalettes, this)); this.$element.on("process-files", $.proxy(this.processFiles, this)); this.$element.on("update-toolbar", $.proxy(this.updateToolbar, this)); this.$element.on("update-downloads", $.proxy(this.updateDownloads, this)); this.$element.on("generate-sheets", $.proxy(this.generateSheets, this)); this.$element.on("error", $.proxy(this.errorHandler, this)); }, /** * ### @setDropBox * Create a `DropBox` instance for drag and drop file loading */ setDropBox: function () { this.dropBox = new DropBox(this.$dropBox); }, /** * ### @setManagers * Set various managers for working with files, sprite sheet layout * and stylesheets */ setManagers: function () { fileManager.set({ onload: $.proxy(this.canvas.createSprite, this.canvas), onprogress: $.proxy(this.updateProgress, this) }); layoutManager.set(this.settings.layout); stylesheetManager.set(this.settings.stylesheet); }, /** * ### @setImages * Set a reference to any pre-initialization images for processing */ setImages: function () { this.images = this.$element.find("> img").get(); }, /** * ### @setCanvas * Create a `Canvas` instance for sprite placement and manipulation */ setCanvas: function () { this.canvas = new Canvas(this.$canvas, { images: this.images, padding: this.settings.padding }, { onprogress: $.proxy(this.updateProgress, this) }); }, /** * ### @setToolbar * Create the `Toolbar` instance with handlers */ setToolbar: function () { var self = this; this.toolbar = new Toolbar(this.$toolbar, { name: "toolbar", actions: { open: { change: function (e) { var $input = self.$toolbar.find("input[type=file]"); var $clone = $input.clone(true).val(""); var files = e.target.files; self.$element.trigger("process-files", [files]); $input.replaceWith($clone); } }, settings: { click: function (e) { self.$element.trigger("open-settings"); } }, reset: { click: function (e) { self.canvas.reset(); } }, generate: { click: function (e) { self.$element.trigger("show-overlay"); self.$element.trigger("generate-sheets"); self.$element.trigger("hide-overlay"); } }, clear: { click: function (e) { self.canvas.clear(); } }, downloads: { click: function (e) { self.$element.trigger("open-downloads"); } }, about: { click: function (e) { self.$element.trigger("open-about"); } } } }); }, /** * ### @setPalettes * Create the various `Palette` instances with handlers */ setPalettes: function () { var self = this; // displays about info var about = new Palette(this.$about, { name: "about", visible: true, actions: { close: { click: function (e) { this.close(); } } } }); // displays the sprite, stylesheet and other info for download var downloads = new Palette(this.$downloads, { name: "downloads", visible: false, actions: { close: { click: function (e) { this.close(); } } } }); // display app settings var settings = new Palette(this.$settings, { name: "settings", visible: false, actions: { close: { click: function (e) { self.$element.trigger("close-settings"); } } }, fields: { layout: { "change": function (e) { var $checked = this.$element.find("input[name=layout]:checked"); var value = $checked.val(); this.source.layout = value; layoutManager.set(value); self.updateSettings(); } }, stylesheet: { "change": function (e) { var $checked = this.$element.find("input[name=stylesheet]:checked"); var value = $checked.val(); self.settings.stylesheet = value; stylesheetManager.set(value); self.updateSettings(); } }, prefix: { "input blur": function (e) { var value = $(e.currentTarget).val(); this.source.prefix = value; self.updateSettings(); } }, padding: { "input blur": function (e) { var value = $(e.currentTarget).val(); this.source.padding = value; self.canvas.padding = value; $.map(self.canvas.sprites, function (sprite) { sprite.configure({ padding: value }); }); self.updateSettings(); } }, uri: { "change": function (e) { var value = $(e.currentTarget).is(":checked"); this.source.uri = value; self.updateSettings(); } } } }); // displays sprite properties var properties = new Palette(this.$properties, { name: "properties", visible: false, actions: { close: { click: function (e) { self.$element.trigger("close-properties"); } }, remove: { click: function (e) { var sprite = this.source; self.canvas.remove(sprite); } } }, fields: { name: { "input blur": function (e) { var $input = $(e.currentTarget); var sprite = this.source; var name = $input.val(); var clean = sprite.cleanName(name); this.source.name = clean; if (name !== clean) { $input.val(clean); } } } } }); this.palettes = { about: about, downloads: downloads, settings: settings, properties: properties }; }, /** * ### @updateSettings * Perform any necessary recalculations and save the new settings * with store.js */ updateSettings: function () { this.showOverlay(); this.canvas.reset(); if (store && !store.disabled) { store.set("stitches-settings", this.settings); } }, /** * ### @showOverlay * Block the UI * * @param {event} e The event object */ showOverlay: function (e) { this.$overlay.fadeTo("fast", 0.4); }, /** * ### @hideOverlay * Unblock the UI * * @param {event} e The event object */ hideOverlay: function (e) { this.$overlay.fadeOut("fast"); }, /** * ### @openAbout * Open the about palette, hide others * * @param {event} e The event object */ openAbout: function (e) { this.closePalettes(); this.palettes.about.open(); }, /** * ### @closeAbout * Close the about palette, if visible * * @param {event} e The event object */ closeAbout: function (e) { if (this.palettes.about.visible) { this.palettes.about.close(); } }, /** * ### @openDownloads * Open the downloads palette, hide others * * @param {event} e The event object */ openDownloads: function (e) { this.closePalettes(); this.palettes.downloads.open(); }, /** * ### @closeDownloads * Close the downloads palette, if visible * * @param {event} e The event object */ closeDownloads: function (e) { if (this.palettes.downloads.visible) { this.palettes.downloads.close(); } }, /** * ### @openSettings * Open the settings palette, hide others. Configure the inputs * with the current settings * * @param {event} e The event object */ openSettings: function (e) { this.closePalettes(); this.palettes.settings.configure({ source: this.settings, inputs: { layout: this.settings.layout, stylesheet: this.settings.stylesheet, prefix: this.settings.prefix, padding: this.settings.padding, uri: this.settings.uri } }); this.palettes.settings.open(); }, /** * ### @closeSettings * Close the settings palette, if visible * * @param {event} e The event object */ closeSettings: function (e) { if (this.palettes.settings.visible) { this.palettes.settings.close(); } }, /** * ### @openProperties * Open the properties palette, hide others. Configure using the * properties of the sprite argument * * @param {event} e The event object * @param {Sprite} sprite Uses these properties */ openProperties: function (e, sprite) { this.closePalettes(); this.palettes.properties.configure({ source: sprite, inputs: { name: sprite.name, x: sprite.left(), y: sprite.top() } }); this.palettes.properties.open(); }, /** * ### @closeProperties * Close the properties palette, if visible. This also clears * any active sprites * * @param {event} e The event object */ closeProperties: function (e) { if (this.palettes.properties.visible) { this.palettes.properties.close(); this.canvas.$element.trigger("clear-active", [true]); } }, /** * ### @closePalettes * Close all open palettes * * @param {event} e The event object */ closePalettes: function (e) { this.closeAbout(); this.closeDownloads(); this.closeSettings(); this.closeProperties(); }, /** * ### @processFiles * Send a set of files to the file manager for processing * * @param {event} e The event object * @param {array} files For processing */ processFiles: function (e, files) { fileManager.processFiles(files); }, /** * ### @updateToolbar * Update the available actions on the toolbar based on the current * state * * @param {event} e The event object */ updateToolbar: function (e) { var toolbar = this.toolbar; var canvas = this.canvas; if (canvas.sprites.length) { toolbar.enable("reset generate clear downloads"); } else { toolbar.disable("reset generate clear downloads"); } if (this.hasFileInput) { toolbar.enable("open"); } else { toolbar.disable("open"); } }, /** * ### @updateDownloads * Update the downloads palette content based on the current state * * @param {event} e The event object */ updateDownloads: function (e) { var $section = this.$downloads.find("section"); var $spritesheet = this.$downloads.find(".downloads-spritesheet"); var $stylesheet = this.$downloads.find(".downloads-stylesheet"); var html = templates.downloads({ prefix: this.settings.prefix, spritesheet: this.spritesheet, stylesheet: this.stylesheet, stylesheetWithUri: this.stylesheetWithUri, stylesheetType: stylesheetManager.type, stylesheetLines: this.stylesheet.split("\n").length, markup: this.markup, markupLines: this.markup.split("\n").length, markupTooltip: this.markupTooltip }); $section.html(html); // buttons $spritesheet.attr({ "href": this.spritesheet, "target": "_blank" }); $stylesheet.attr({ "href": "data:text/plain," + encodeURIComponent(this.stylesheet), "target": "_blank" }); // tooltips if ($.fn.tooltip) { $section.find("[data-toggle=tooltip]").tooltip(); } }, /** * ### @updateProgress * Update the progress bar at the top of the UI, right below the toolbar * * @param {number} progress From 0 to 1 * @param {type} type Determines the color of the bar */ updateProgress: function (progress, type) { var percent = Math.ceil(progress * 100); if (percent === 100 && type !== "danger" && type !== "warning") { type = "success"; } if (type) { this.$progress.attr({ "class": "progress progress-striped progress-" + type }); } this.$progressBar.css({ width: percent + "%" }); }, /** * ### @generateSheets * Generate the sprite sheet and stylesheet using the layout manager * and stylesheet manager. This is the final product * * @param {event} e The event object */ generateSheets: function (e) { this.spritesheet = layoutManager.getSpritesheet({ sprites: this.canvas.sprites, dimensions: this.canvas.dimensions }); this.stylesheetWithUri = stylesheetManager.getStylesheet({ sprites: this.canvas.sprites, spritesheet: this.spritesheet, prefix: this.settings.prefix, uri: true }); this.markup = stylesheetManager.getMarkup({ sprites: this.canvas.sprites, prefix: this.settings.prefix }); this.markupTooltip = stylesheetManager.getMarkup({ sprites: this.canvas.sprites, prefix: this.settings.prefix, tooltip: true }); // if uri is not checked, we need to generate another // stylesheet with the data uri included for the // download example rendering if (this.settings.uri) { this.stylesheet = this.stylesheetWithUri; } else { this.stylesheet = stylesheetManager.getStylesheet({ sprites: this.canvas.sprites, spritesheet: this.spritesheet, prefix: this.settings.prefix, uri: this.settings.uri }); } this.$element.trigger("update-toolbar"); this.$element.trigger("update-downloads"); this.updateProgress(1, "success"); }, /** * ### @errorHandler * At the moment, this just changes the progress bar to yellow */ errorHandler: function (e, err, type) { this.updateProgress(1, type || "warning"); } }; return Stitches; });