├── .editorconfig ├── .eslintrc.json ├── .gitignore ├── CNAME ├── LICENSE ├── README.md ├── assets ├── img │ ├── GitHub-Mark-Light-32px.png │ ├── ic_add_circle_black_48px.svg │ ├── ic_clear_black_24dp_2x.png │ ├── ic_clear_black_24px.svg │ ├── ic_content_copy_black_48px.svg │ ├── ic_content_copy_white_48px.svg │ ├── ic_file_download_black_48px.svg │ ├── ic_file_download_white_48px.svg │ ├── ic_remove_circle_outline_black_24px.svg │ ├── tile-dark.jpg │ └── tile-light.jpg ├── js │ ├── app │ │ ├── controller.js │ │ ├── helpers.js │ │ ├── index.js │ │ ├── store.js │ │ ├── templates.js │ │ ├── texture-packer.js │ │ └── view.js │ ├── bundle-167107567a.min.js │ ├── bundle.js │ ├── index.js │ └── legacy │ │ ├── debounce.js │ │ ├── handleFiles.js │ │ ├── index.js │ │ └── packer.js └── styles │ ├── main.css │ ├── main.scss │ └── normalize.css ├── favicon.ico ├── gulpfile.js ├── index.html ├── manifest.webmanifest ├── package.json ├── src └── index.html └── webpack.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # we recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | 23 | [{package,bower}.json] 24 | indent_style = space 25 | indent_size = 2 -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true 5 | }, 6 | "parserOptions": { 7 | "ecmaVersion":6, 8 | "sourceType": "module" 9 | }, 10 | "extends": "eslint:recommended", 11 | "rules": { 12 | "quotes": [ 13 | "warn", 14 | "single" 15 | ], 16 | "semi": [ 17 | "warn", 18 | "always" 19 | ], 20 | "no-console": "warn" 21 | }, 22 | "globals": { 23 | "ga": true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /test 3 | .sass-cache 4 | **/*.map -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | responsive-css.us -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Responsive CSS Sprite Generator 2 | ###### A tool for generating CSS sprite sheets using percentage based background positions 3 | --- 4 | A hosted version of the tool can be found at [responsive-css.us](https://responsive-css.us/) -------------------------------------------------------------------------------- /assets/img/GitHub-Mark-Light-32px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eivers88/responsive-css-sprite-generator/f463d5509529407705c48470e6f5612068742396/assets/img/GitHub-Mark-Light-32px.png -------------------------------------------------------------------------------- /assets/img/ic_add_circle_black_48px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/img/ic_clear_black_24dp_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eivers88/responsive-css-sprite-generator/f463d5509529407705c48470e6f5612068742396/assets/img/ic_clear_black_24dp_2x.png -------------------------------------------------------------------------------- /assets/img/ic_clear_black_24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/img/ic_content_copy_black_48px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/img/ic_content_copy_white_48px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/img/ic_file_download_black_48px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/img/ic_file_download_white_48px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/img/ic_remove_circle_outline_black_24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/img/tile-dark.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eivers88/responsive-css-sprite-generator/f463d5509529407705c48470e6f5612068742396/assets/img/tile-dark.jpg -------------------------------------------------------------------------------- /assets/img/tile-light.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eivers88/responsive-css-sprite-generator/f463d5509529407705c48470e6f5612068742396/assets/img/tile-light.jpg -------------------------------------------------------------------------------- /assets/js/app/controller.js: -------------------------------------------------------------------------------- 1 | import TexturePacker from './texture-packer'; 2 | 3 | export default class Controller { 4 | /** 5 | * @param {!Store} store A Store instance 6 | * @param {!View} view A View instance 7 | */ 8 | constructor(store, view) { 9 | 10 | this.imgQueued = 0; 11 | this.imgLoaded = 0; 12 | this.loadInProgress = false; 13 | 14 | this.store = store; 15 | this.view = view; 16 | 17 | this.view.setSettingsValues(this.store.getSettings()); 18 | 19 | this.view.bindFileExplorer(this.addImages.bind(this)); 20 | this.view.bindDropboxImages(this.addImages.bind(this)); 21 | this.view.bindRemoveBtn(this.removeImage.bind(this)); 22 | this.view.bindDownloadBtn(this.download.bind(this)); 23 | this.view.bindSettingsInputs(this.updateSettingsValues.bind(this)); 24 | 25 | this.texturePacker = new TexturePacker(this.view.$canvas, this.view.getSettingsValues()); 26 | 27 | } 28 | 29 | addImages (data) { 30 | 31 | if(this.loadInProgress) { 32 | // Cannot add images while load is in progress 33 | return; 34 | } 35 | 36 | let files = []; 37 | 38 | // add only image files to our file list 39 | for(let prop in data) { 40 | if(data[prop].type === 'image/png' || data[prop].type === 'image/jpeg') { 41 | files.push(data[prop]); 42 | } 43 | } 44 | 45 | if(files.length === 0) { 46 | return; 47 | } 48 | 49 | this.loadInProgress = true; 50 | this.imgQueued += files.length; 51 | 52 | for(let i = 0; i < files.length; i++) { 53 | 54 | this.view.addListItem({ 55 | id: this.store.getNewId(), 56 | src: window.URL.createObjectURL(files[i]), 57 | name: files[i].name.substring(0, files[i].name.indexOf('.')), 58 | onLoadSuccess: this.onLoadSuccess.bind(this) 59 | }); 60 | 61 | } 62 | 63 | } 64 | 65 | removeImage (e) { 66 | if(this.loadInProgress) { 67 | // Cannot remove image while load is in progress 68 | return; 69 | } 70 | if(e.target && e.target.classList.contains('remove')) { 71 | e.target.parentNode.parentNode.removeChild(e.target.parentNode); 72 | let css = this.texturePacker.remove(parseInt(e.target.parentNode.getAttribute('data-id'))); 73 | this.update(css); 74 | } 75 | } 76 | 77 | 78 | onLoadSuccess (texture) { 79 | this.texturePacker.addTexture(texture); 80 | 81 | this.imgLoaded++; 82 | 83 | if(this.imgLoaded === this.imgQueued) { 84 | this.loadComplete(); 85 | } 86 | 87 | } 88 | 89 | loadComplete () { 90 | // all files loaded! 91 | this.loadInProgress = false; 92 | this.imgQueued = 0; 93 | this.imgLoaded = 0; 94 | let css = this.texturePacker.pack(); 95 | this.update(css); 96 | } 97 | 98 | updateSettingsValues (settings) { 99 | // update input values 100 | this.store.saveSettings(settings); 101 | let css = this.texturePacker.updateSettings(settings); 102 | this.update(css); 103 | } 104 | 105 | update (css) { 106 | if(this.texturePacker.textures.length) { 107 | this.view.setCSSValue(css); 108 | } else { 109 | this.view.setCSSValue(''); 110 | } 111 | } 112 | 113 | 114 | download() { 115 | let a = document.createElement('a'); 116 | a.setAttribute('target', '_blank'); 117 | a.href = this.texturePacker.canvas.toDataURL(); 118 | document.body.appendChild(a); 119 | a.click(); 120 | document.body.removeChild(a); 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /assets/js/app/helpers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * querySelector wrapper 3 | * 4 | * @param {string} selector Selector to query 5 | * @param {Element} [scope] Optional scope element for the selector 6 | */ 7 | export function qs(selector, scope) { 8 | return (scope || document).querySelector(selector); 9 | } 10 | 11 | /** 12 | * addEventListener wrapper 13 | * 14 | * @param {Element|Window} target Target Element 15 | * @param {string} type Event name to bind to 16 | * @param {Function} callback Event callback 17 | * @param {boolean} [capture] Capture the event 18 | */ 19 | export function $on(target, type, callback, capture) { 20 | target.addEventListener(type, callback, !!capture); 21 | } 22 | 23 | /** 24 | * Attach a handler to an event for all elements matching a selector. 25 | * 26 | * @param {Element} target Element which the event must bubble to 27 | * @param {string} selector Selector to match 28 | * @param {string} type Event name 29 | * @param {Function} handler Function called when the event bubbles to target 30 | * from an element matching selector 31 | * @param {boolean} [capture] Capture the event 32 | */ 33 | export function $delegate(target, selector, type, handler, capture) { 34 | const dispatchEvent = event => { 35 | const targetElement = event.target; 36 | const potentialElements = target.querySelectorAll(selector); 37 | let i = potentialElements.length; 38 | 39 | while (i--) { 40 | if (potentialElements[i] === targetElement) { 41 | handler.call(targetElement, event); 42 | break; 43 | } 44 | } 45 | }; 46 | 47 | $on(target, type, dispatchEvent, !!capture); 48 | } 49 | 50 | /** 51 | * Encode less-than and ampersand characters with entity codes to make user- 52 | * provided text safe to parse as HTML. 53 | * 54 | * @param {string} s String to escape 55 | * 56 | * @returns {string} String with unsafe characters escaped with entity codes 57 | */ 58 | export const escapeForHTML = s => s.replace(/[&<]/g, c => c === '&' ? '&' : '<'); 59 | 60 | /** 61 | * Returns a function, that, as long as it continues to be invoked, will not 62 | * be triggered. The function will be called after it stops being called for 63 | * N milliseconds. If `immediate` is passed, trigger the function on the 64 | * leading edge, instead of the trailing. 65 | * 66 | * @param {Function} func to be executed on debounce 67 | * @param {number} wait time in milliseconds 68 | * @param {boolean} immediate boolean 69 | * 70 | * */ 71 | export function debounce(func, wait, immediate) { 72 | let timeout; 73 | return function() { 74 | let context = this, args = arguments; 75 | let later = function() { 76 | timeout = null; 77 | if (!immediate) func.apply(context, args); 78 | }; 79 | let callNow = immediate && !timeout; 80 | clearTimeout(timeout); 81 | timeout = setTimeout(later, wait); 82 | if (callNow) func.apply(context, args); 83 | }; 84 | } 85 | -------------------------------------------------------------------------------- /assets/js/app/index.js: -------------------------------------------------------------------------------- 1 | import Store from './store'; 2 | import View from './view'; 3 | import Controller from './controller'; 4 | import Template from './templates'; 5 | import Clipboard from 'clipboard'; 6 | import WebFont from 'webfontloader'; 7 | 8 | /** 9 | * App structure inspired by https://github.com/tastejs/todomvc/tree/gh-pages/examples/vanilla-es6 10 | * */ 11 | 12 | let app = { 13 | 14 | start: function () { 15 | 16 | const template = new Template(); 17 | 18 | let store = new Store('responsive-css-sprite-generator'); 19 | let view = new View(template); 20 | 21 | new Controller(store, view); 22 | 23 | new Clipboard('#copy'); 24 | 25 | WebFont.load({ 26 | google: { 27 | families: ['Roboto Condensed:400,700'] 28 | } 29 | }); 30 | 31 | // app started! 32 | 33 | } 34 | 35 | 36 | }; 37 | 38 | export default app; 39 | -------------------------------------------------------------------------------- /assets/js/app/store.js: -------------------------------------------------------------------------------- 1 | const localStorage = window.localStorage; 2 | let instance = null; 3 | let defaultSettings = { 4 | prefix:'', 5 | padding: 2, 6 | path: 'sprite.png' 7 | }; 8 | 9 | export default class Store { 10 | /** 11 | * @param {!string} name Database name 12 | * @param {function()} [callback] Called when the Store is ready 13 | */ 14 | constructor(name, callback) { 15 | 16 | if(!instance) { 17 | instance = this; 18 | } else { 19 | return instance; 20 | } 21 | 22 | // init settings 23 | if(localStorage.getItem(name)) { 24 | // TODO: Check this object 25 | this.settings = JSON.parse(localStorage.getItem(name)); 26 | } else { 27 | this.settings = defaultSettings; 28 | localStorage.setItem(name, JSON.stringify(this.settings)); 29 | } 30 | 31 | this.name = name; 32 | this.id = 0; 33 | 34 | // console.log('store init', this.settings); 35 | 36 | if(callback){ 37 | callback(); 38 | } 39 | 40 | } 41 | 42 | getNewId () { 43 | let newId = this.id; 44 | this.id++; 45 | return newId; 46 | } 47 | 48 | getSettings () { 49 | return this.settings; 50 | } 51 | 52 | saveSettings (settings) { 53 | // TODO: Check this object 54 | this.settings = settings; 55 | localStorage.setItem(this.name, JSON.stringify(settings)); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /assets/js/app/templates.js: -------------------------------------------------------------------------------- 1 | export default class Template { 2 | listItem(item) { 3 | 4 | let li = document.createElement('li'); 5 | let img = document.createElement('img'); 6 | let info = document.createElement('span'); 7 | let remove = document.createElement('div'); 8 | 9 | li.setAttribute('data-id', item.id); 10 | 11 | img.src = item.src; 12 | img.height = 60; 13 | img.onload = function() { 14 | window.URL.revokeObjectURL(item.src); 15 | item.onLoadSuccess({ 16 | img: this, 17 | w: this.naturalWidth, 18 | h: this.naturalHeight, 19 | name: item.name, 20 | id: item.id 21 | }); 22 | }; 23 | 24 | li.appendChild(img); 25 | info.innerHTML = item.name; 26 | li.appendChild(info); 27 | 28 | remove.classList.add('remove'); 29 | li.appendChild(remove); 30 | 31 | return li; 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /assets/js/app/texture-packer.js: -------------------------------------------------------------------------------- 1 | const DEFAULT_SIZE = 256; 2 | const GITHUB_URL = '/*\nResponsive CSS Sprite created using: ' + 3 | 'https://responsive-css.us/\n' + 4 | '*/\n\n'; 5 | 6 | const DEBUG = false; 7 | 8 | function findNode(root, w, h) { 9 | if (root.used) { 10 | return findNode(root.right, w, h) || findNode(root.down, w, h); 11 | } else if ((w <= root.w) && (h <= root.h)) { 12 | return root; 13 | } else { 14 | return null; 15 | } 16 | } 17 | 18 | function splitNode(node, w, h) { 19 | node.used = true; 20 | node.down = {x: node.x, y: node.y + h, w: node.w, h: node.h - h}; 21 | node.right = {x: node.x + w, y: node.y, w: node.w - w, h: h}; 22 | return node; 23 | } 24 | 25 | export default class TexturePacker { 26 | 27 | constructor(canvas, {padding, prefix, path}) { 28 | 29 | this.canvas = canvas; 30 | this.ctx = canvas.getContext('2d'); 31 | 32 | this.textures = []; 33 | 34 | this.root = { 35 | x: 0, // origin x 36 | y: 0, // origin y 37 | w: DEFAULT_SIZE - padding, // width 38 | h: DEFAULT_SIZE - padding, // height 39 | p: padding 40 | }; 41 | 42 | this.prefix = prefix; 43 | this.path = path; 44 | 45 | } 46 | 47 | addTexture(texture) { 48 | this.textures.push(texture); 49 | } 50 | 51 | sort() { 52 | this.textures.sort((a, b) => { 53 | if (a.h < b.h) { 54 | return 1; 55 | } 56 | if (a.h > b.h) { 57 | return -1; 58 | } 59 | return 0; 60 | }); 61 | } 62 | 63 | fit() { 64 | let i, node, texture, pad = this.root.p; 65 | for (i = 0; i < this.textures.length; i++) { 66 | texture = this.textures[i]; 67 | texture.fit = false; 68 | node = findNode(this.root, texture.w + pad, texture.h + pad); 69 | if (node) { 70 | texture.fit = splitNode(node, texture.w + pad, texture.h + pad); 71 | } 72 | if (!texture.fit) { 73 | this.resize(); 74 | break; 75 | } 76 | } 77 | } 78 | 79 | resize() { 80 | let w, h, pad = this.root.p; 81 | if (this.root.w > this.root.h) { 82 | w = (this.root.w + pad); 83 | h = (this.root.h + pad) * 2; 84 | } else { 85 | w = (this.root.w + pad) * 2; 86 | h = (this.root.h + pad); 87 | } 88 | this.root = { 89 | x: 0, // origin x 90 | y: 0, // origin y 91 | w: w - pad, // width 92 | h: h - pad, // height 93 | p: pad 94 | }; 95 | this.fit(); 96 | } 97 | 98 | draw() { 99 | 100 | // TODO: Calc CSS output 101 | 102 | let canvas = this.canvas; 103 | let ctx = this.ctx; 104 | let pad = this.root.p; 105 | let prefix = this.prefix; 106 | let width = this.root.w + pad; 107 | let height = this.root.h + pad; 108 | 109 | let computedCSS = ''; 110 | let selectorsString = ''; 111 | let globalString = '\n{display:inline-block; overflow:hidden; ' + 112 | 'background-repeat: no-repeat;\n' + 113 | `background-image:url(${this.path});}\n\n`; 114 | let spriteString = ''; 115 | 116 | canvas.width = width; 117 | canvas.height = height; 118 | 119 | ctx.clearRect(0, 0, width, height); 120 | 121 | this.textures.sort((a, b) => { 122 | let nameA = a.name.toUpperCase(); 123 | let nameB = b.name.toUpperCase(); 124 | return (nameA < nameB) ? -1 : (nameA > nameB) ? 1 : 0; 125 | }); 126 | 127 | for(let i = 0; i < this.textures.length; i++) { 128 | let texture = this.textures[i]; 129 | if(texture.fit) { 130 | 131 | if(DEBUG) { 132 | ctx.fillRect(texture.fit.x + pad, texture.fit.y + pad, texture.w, texture.h); 133 | ctx.stroke(); 134 | } 135 | 136 | ctx.drawImage(texture.img, texture.fit.x + pad, texture.fit.y + pad); 137 | 138 | selectorsString += '.' + prefix + texture.name + (i === this.textures.length - 1 ? ' ' : ', '); 139 | 140 | spriteString += `.${prefix + texture.name} {width: ${texture.w}px; ` + 141 | `height: ${texture.h}px; ` + 142 | `background-position: ${(((texture.fit.x + pad) / (width - texture.w)) * 100).toPrecision(6)}% ` + 143 | `${(((texture.fit.y + pad) / (height - texture.h)) * 100).toPrecision(6)}%; ` + 144 | `background-size: ${((width / texture.w) * 100).toPrecision(6)}%; }\n`; 145 | 146 | } 147 | } 148 | 149 | computedCSS = GITHUB_URL + selectorsString + globalString + spriteString; 150 | 151 | return computedCSS; 152 | } 153 | 154 | pack () { 155 | this.sort(); 156 | this.fit(); 157 | return this.draw(); 158 | } 159 | 160 | remove (id) { 161 | 162 | for(let i = this.textures.length; i--;) { 163 | let texture = this.textures[i]; 164 | if(texture.id === id) { 165 | this.textures.splice(i, 1); 166 | break; 167 | } 168 | } 169 | 170 | this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); 171 | 172 | let pad = this.root.p; 173 | 174 | // reset the root 175 | this.root = { 176 | x: 0, // origin x 177 | y: 0, // origin y 178 | w: DEFAULT_SIZE - pad, // width 179 | h: DEFAULT_SIZE - pad, // height 180 | p: pad 181 | }; 182 | 183 | return this.pack(); 184 | 185 | } 186 | 187 | updateSettings({padding, prefix, path}) { 188 | 189 | let canvas = this.canvas; 190 | 191 | this.ctx.clearRect(0, 0, canvas.width || DEFAULT_SIZE, canvas.height || DEFAULT_SIZE); 192 | 193 | this.root = { 194 | x: 0, // origin x 195 | y: 0, // origin y 196 | w: DEFAULT_SIZE - padding, // width 197 | h: DEFAULT_SIZE - padding, // height 198 | p: padding 199 | }; 200 | 201 | this.prefix = prefix; 202 | this.path = path; 203 | 204 | return this.pack(); 205 | 206 | } 207 | 208 | } 209 | -------------------------------------------------------------------------------- /assets/js/app/view.js: -------------------------------------------------------------------------------- 1 | import {qs, $on, debounce} from './helpers'; 2 | let instance = null; 3 | 4 | function scrollIntoView(eleID) { 5 | var e = qs(eleID).getBoundingClientRect(); 6 | var h = qs('#header').getBoundingClientRect(); 7 | window.scrollTo(0, e.top - h.height - 100); 8 | } 9 | 10 | export default class View { 11 | 12 | constructor(template) { 13 | 14 | if(!instance) { 15 | instance = this; 16 | } else { 17 | return instance; 18 | } 19 | 20 | this.template = template; 21 | this.$fileInput = qs('#fileElem'); 22 | this.$fileList = qs('#fileList'); 23 | this.$listItems = document.createElement('ul'); 24 | this.$prefix = qs('#prefix'); 25 | this.$padding = qs('#padding'); 26 | this.$path = qs('#path'); 27 | this.$canvas = qs('#canvas'); 28 | this.$css = qs('#css'); 29 | this.$copy = qs('#copy'); 30 | this.$download = qs('#download'); 31 | this.dimensions = qs('#dimensions'); 32 | this.$dropbox = qs('#dropbox'); 33 | this.$fileList.appendChild(this.$listItems); 34 | 35 | $on(this.$copy, 'click', ()=>{ 36 | scrollIntoView('#css'); 37 | ga('send', { 38 | hitType: 'event', 39 | eventCategory: 'Copy CSS', 40 | eventAction: 'click' 41 | }); 42 | }); 43 | 44 | } 45 | 46 | bindFileExplorer (handler) { 47 | 48 | $on(this.$dropbox, 'click', (e)=>{ 49 | if(this.$fileInput) { 50 | this.$fileInput.click(); 51 | } 52 | e.preventDefault(); 53 | ga('send', { 54 | hitType: 'event', 55 | eventCategory: 'File Explorer', 56 | eventAction: 'click' 57 | }); 58 | }); 59 | 60 | $on(this.$fileInput, 'change', function(){ 61 | handler(this.files); 62 | }); 63 | 64 | } 65 | 66 | bindDropboxImages (handler) { 67 | 68 | $on(this.$dropbox, 'dragenter', (e)=>{ 69 | e.stopPropagation(); 70 | e.preventDefault(); 71 | }); 72 | 73 | $on(this.$dropbox, 'dragover', (e)=>{ 74 | e.stopPropagation(); 75 | e.preventDefault(); 76 | }); 77 | 78 | $on(this.$dropbox, 'drop', (e)=>{ 79 | e.stopPropagation(); 80 | e.preventDefault(); 81 | 82 | let dt = e.dataTransfer; 83 | let files = dt.files; 84 | 85 | handler(files); 86 | 87 | ga('send', { 88 | hitType: 'event', 89 | eventCategory: 'File Drop', 90 | eventAction: 'drop' 91 | }); 92 | 93 | }); 94 | 95 | } 96 | 97 | bindSettingsInputs (handler) { 98 | 99 | let returnValues = () => {handler(this.getSettingsValues());}; 100 | 101 | $on(this.$prefix, 'keyup', debounce(returnValues, 250, false)); 102 | $on(this.$padding, 'keyup', debounce(returnValues, 250, false)); 103 | $on(this.$path, 'keyup', debounce(returnValues, 250, false)); 104 | 105 | } 106 | 107 | bindRemoveBtn (handler) { 108 | $on(this.$fileList, 'click', handler); 109 | ga('send', { 110 | hitType: 'event', 111 | eventCategory: 'Remove Image', 112 | eventAction: 'click' 113 | }); 114 | } 115 | 116 | bindDownloadBtn(handler) { 117 | $on(this.$download, 'click', handler); 118 | ga('send', { 119 | hitType: 'event', 120 | eventCategory: 'Sprite Download', 121 | eventAction: 'click' 122 | }); 123 | } 124 | 125 | setSettingsValues (settings) { 126 | this.$prefix.value = settings.prefix; 127 | this.$padding.value = settings.padding; 128 | this.$path.value = settings.path; 129 | } 130 | 131 | getSettingsValues () { 132 | let p = parseInt(this.$padding.value); 133 | return { 134 | 'prefix': this.$prefix.value, 135 | 'padding': Number.isInteger(p) ? p : 0, 136 | 'path': this.$path.value 137 | }; 138 | } 139 | 140 | addListItem(item) { 141 | let li = this.template.listItem(item); 142 | this.$listItems.appendChild(li); 143 | } 144 | 145 | setCSSValue (css) { 146 | this.$css.value = css; 147 | if(css !== '') { 148 | this.$dropbox.classList.remove('is-empty'); 149 | } else { 150 | this.$dropbox.classList.add('is-empty'); 151 | } 152 | } 153 | 154 | } 155 | -------------------------------------------------------------------------------- /assets/js/bundle-167107567a.min.js: -------------------------------------------------------------------------------- 1 | !function(t){function e(i){if(n[i])return n[i].exports;var o=n[i]={i:i,l:!1,exports:{}};return t[i].call(o.exports,o,o.exports,e),o.l=!0,o.exports}var n={};e.m=t,e.c=n,e.d=function(t,n,i){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:i})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=1)}([function(t,e,n){"use strict";function i(t,e,n,i){t.addEventListener(e,n,!!i)}Object.defineProperty(e,"__esModule",{value:!0}),e.qs=function(t,e){return(e||document).querySelector(t)},e.$on=i,e.$delegate=function(t,e,n,o,r){i(t,n,function(n){for(var i=n.target,r=t.querySelectorAll(e),a=r.length;a--;)if(r[a]===i){o.call(i,n);break}},!!r)},e.debounce=function(t,e,n){var i=void 0;return function(){var o=this,r=arguments,a=n&&!i;clearTimeout(i),i=setTimeout(function(){i=null,n||t.apply(o,r)},e),a&&t.apply(o,r)}};e.escapeForHTML=function(t){return t.replace(/[&<]/g,function(t){return"&"===t?"&":"<"})}},function(t,e,n){"use strict";var i=function(t){return t&&t.__esModule?t:{default:t}}(n(2));(0,n(0).$on)(window,"load",i.default.start)},function(t,e,n){"use strict";function i(t){return t&&t.__esModule?t:{default:t}}Object.defineProperty(e,"__esModule",{value:!0});var o=i(n(3)),r=i(n(4)),a=i(n(5)),s=i(n(7)),u=i(n(8)),c=i(n(16)),l={start:function(){var t=new s.default,e=new o.default("responsive-css-sprite-generator"),n=new r.default(t);new a.default(e,n),new u.default("#copy"),c.default.load({google:{families:["Roboto Condensed:400,700"]}})}};e.default=l},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var i=function(){function t(t,e){for(var n=0;ne.h?-1:0})}},{key:"fit",value:function(){var t=void 0,e=void 0,n=void 0,r=this.root.p;for(t=0;tthis.root.h?(t=this.root.w+n,e=2*(this.root.h+n)):(t=2*(this.root.w+n),e=this.root.h+n),this.root={x:0,y:0,w:t-n,h:e-n,p:n},this.fit()}},{key:"draw",value:function(){var t=this.canvas,e=this.ctx,n=this.root.p,i=this.prefix,o=this.root.w+n,r=this.root.h+n,a="",s="\n{display:inline-block; overflow:hidden; background-repeat: no-repeat;\nbackground-image:url("+this.path+");}\n\n",u="";t.width=o,t.height=r,e.clearRect(0,0,o,r),this.textures.sort(function(t,e){var n=t.name.toUpperCase(),i=e.name.toUpperCase();return ni?1:0});for(var c=0;c0&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof t.action?t.action:this.defaultAction,this.target="function"==typeof t.target?t.target:this.defaultTarget,this.text="function"==typeof t.text?t.text:this.defaultText,this.container="object"===c(t.container)?t.container:document.body}},{key:"listenClick",value:function(t){var e=this;this.listener=(0,u.default)(t,"click",function(t){return e.onClick(t)})}},{key:"onClick",value:function(t){var e=t.delegateTarget||t.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new a.default({action:this.action(e),target:this.target(e),text:this.text(e),container:this.container,trigger:e,emitter:this})}},{key:"defaultAction",value:function(t){return r("action",t)}},{key:"defaultTarget",value:function(t){var e=r("target",t);if(e)return document.querySelector(e)}},{key:"defaultText",value:function(t){return r("text",t)}},{key:"destroy",value:function(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["copy","cut"],e="string"==typeof t?[t]:t,n=!!document.queryCommandSupported;return e.forEach(function(t){n=n&&!!document.queryCommandSupported(t)}),n}}]),e}();t.exports=f})},function(t,e,n){var i,o,r;!function(a,s){o=[t,n(10)],void 0===(r="function"==typeof(i=s)?i.apply(e,o):i)||(t.exports=r)}(0,function(t,e){"use strict";var n=function(t){return t&&t.__esModule?t:{default:t}}(e),i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},o=function(){function t(t,e){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};this.action=t.action,this.container=t.container,this.emitter=t.emitter,this.target=t.target,this.text=t.text,this.trigger=t.trigger,this.selectedText=""}},{key:"initSelection",value:function(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function(){var t=this,e="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return t.removeFake()},this.fakeHandler=this.container.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[e?"right":"left"]="-9999px";var i=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.style.top=i+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,this.container.appendChild(this.fakeElem),this.selectedText=(0,n.default)(this.fakeElem),this.copyText()}},{key:"removeFake",value:function(){this.fakeHandler&&(this.container.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(this.container.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function(){this.selectedText=(0,n.default)(this.target),this.copyText()}},{key:"copyText",value:function(){var t=void 0;try{t=document.execCommand(this.action)}catch(e){t=!1}this.handleResult(t)}},{key:"handleResult",value:function(t){this.emitter.emit(t?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function(){this.trigger&&this.trigger.focus(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function(){this.removeFake()}},{key:"action",set:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"copy";if(this._action=t,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function(){return this._action}},{key:"target",set:function(t){if(void 0!==t){if(!t||"object"!==(void 0===t?"undefined":i(t))||1!==t.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&t.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(t.hasAttribute("readonly")||t.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=t}},get:function(){return this._target}}]),t}();t.exports=r})},function(t,e){t.exports=function(t){var e;if("SELECT"===t.nodeName)t.focus(),e=t.value;else if("INPUT"===t.nodeName||"TEXTAREA"===t.nodeName){var n=t.hasAttribute("readonly");n||t.setAttribute("readonly",""),t.select(),t.setSelectionRange(0,t.value.length),n||t.removeAttribute("readonly"),e=t.value}else{t.hasAttribute("contenteditable")&&t.focus();var i=window.getSelection(),o=document.createRange();o.selectNodeContents(t),i.removeAllRanges(),i.addRange(o),e=i.toString()}return e}},function(t,e){function n(){}n.prototype={on:function(t,e,n){var i=this.e||(this.e={});return(i[t]||(i[t]=[])).push({fn:e,ctx:n}),this},once:function(t,e,n){function i(){o.off(t,i),e.apply(n,arguments)}var o=this;return i._=e,this.on(t,i,n)},emit:function(t){for(var e=[].slice.call(arguments,1),n=((this.e||(this.e={}))[t]||[]).slice(),i=0,o=n.length;iparseInt(t[1],10)||536===parseInt(t[1],10)&&11>=parseInt(t[2],10))}return J}function L(t,e,n){for(var i in Q)if(Q.hasOwnProperty(i)&&e===t.f[Q[i]]&&n===t.f[Q[i]])return!0;return!1}function P(t){var e,n=t.g.a.offsetWidth,i=t.h.a.offsetWidth;(e=n===t.f.serif&&i===t.f["sans-serif"])||(e=I()&&L(t,n,i)),e?W()-t.A>=t.w?I()&&L(t,n,i)&&(null===t.u||t.u.hasOwnProperty(t.a.c))?O(t,t.v):O(t,t.B):function(t){setTimeout(a(function(){P(this)},t),50)}(t):O(t,t.v)}function O(t,e){setTimeout(a(function(){c(this.g.a),c(this.h.a),c(this.j.a),c(this.m.a),e(this.a)},t),0)}function N(t,e,n){this.c=t,this.a=e,this.f=0,this.m=this.j=!1,this.s=n}function M(t){0==--t.f&&t.j&&(t.m?((t=t.a).g&&l(t.f,[t.a.c("wf","active")],[t.a.c("wf","loading"),t.a.c("wf","inactive")]),T(t,"active")):E(t.a))}function R(t){this.j=t,this.a=new function(){this.c={}},this.h=0,this.f=this.g=!0}function q(t,e,n,i,o){var r=0==--t.h;(t.f||t.g)&&setTimeout(function(){var t=o||null,s=i||{};if(0===n.length&&r)E(e.a);else{e.f+=n.length,r&&(e.j=r);var u,c=[];for(u=0;u=e.f?o():t.fonts.load(function(t){return x(t)+" "+t.f+"00 300px "+w(t.c)}(e.a),e.h).then(function(t){1<=t.length?i():setTimeout(r,25)},function(){o()})}r()}),o=null,r=new Promise(function(t,n){o=setTimeout(n,e.f)});Promise.race([r,i]).then(function(){o&&(clearTimeout(o),o=null),e.g(e.a)},function(){e.j(e.a)})};var Q={D:"serif",C:"sans-serif"},J=null;A.prototype.start=function(){this.f.serif=this.j.a.offsetWidth,this.f["sans-serif"]=this.m.a.offsetWidth,this.A=W(),P(this)};var X=null;N.prototype.g=function(t){var e=this.a;e.g&&l(e.f,[e.a.c("wf",t.c,k(t).toString(),"active")],[e.a.c("wf",t.c,k(t).toString(),"loading"),e.a.c("wf",t.c,k(t).toString(),"inactive")]),T(e,"fontactive",t),this.m=!0,M(this)},N.prototype.h=function(t){var e=this.a;if(e.g){var n=f(e.f,e.a.c("wf",t.c,k(t).toString(),"active")),i=[],o=[e.a.c("wf",t.c,k(t).toString(),"loading")];n||i.push(e.a.c("wf",t.c,k(t).toString(),"inactive")),l(e.f,i,o)}T(e,"fontinactive",t),M(this)},R.prototype.load=function(t){this.c=new function(t,e){this.a=t,this.o=e||t,this.c=this.o.document}(this.j,t.context||this.j),this.g=!1!==t.events,this.f=!1!==t.classes,F(this,new function(t,e){this.c=t,this.f=t.o.document.documentElement,this.h=e,this.a=new m("-"),this.j=!1!==e.events,this.g=!1!==e.classes}(this.c,t),t)},H.prototype.load=function(t){function e(){if(r["__mti_fntLst"+i]){var n,o=r["__mti_fntLst"+i](),a=[];if(o)for(var s=0;s b.h) { 745 | return -1; 746 | } 747 | return 0; 748 | }); 749 | } 750 | }, { 751 | key: 'fit', 752 | value: function fit() { 753 | var i = void 0, 754 | node = void 0, 755 | texture = void 0, 756 | pad = this.root.p; 757 | for (i = 0; i < this.textures.length; i++) { 758 | texture = this.textures[i]; 759 | texture.fit = false; 760 | node = findNode(this.root, texture.w + pad, texture.h + pad); 761 | if (node) { 762 | texture.fit = splitNode(node, texture.w + pad, texture.h + pad); 763 | } 764 | if (!texture.fit) { 765 | this.resize(); 766 | break; 767 | } 768 | } 769 | } 770 | }, { 771 | key: 'resize', 772 | value: function resize() { 773 | var w = void 0, 774 | h = void 0, 775 | pad = this.root.p; 776 | if (this.root.w > this.root.h) { 777 | w = this.root.w + pad; 778 | h = (this.root.h + pad) * 2; 779 | } else { 780 | w = (this.root.w + pad) * 2; 781 | h = this.root.h + pad; 782 | } 783 | this.root = { 784 | x: 0, // origin x 785 | y: 0, // origin y 786 | w: w - pad, // width 787 | h: h - pad, // height 788 | p: pad 789 | }; 790 | this.fit(); 791 | } 792 | }, { 793 | key: 'draw', 794 | value: function draw() { 795 | 796 | // TODO: Calc CSS output 797 | 798 | var canvas = this.canvas; 799 | var ctx = this.ctx; 800 | var pad = this.root.p; 801 | var prefix = this.prefix; 802 | var width = this.root.w + pad; 803 | var height = this.root.h + pad; 804 | 805 | var computedCSS = ''; 806 | var selectorsString = ''; 807 | var globalString = '\n{display:inline-block; overflow:hidden; ' + 'background-repeat: no-repeat;\n' + ('background-image:url(' + this.path + ');}\n\n'); 808 | var spriteString = ''; 809 | 810 | canvas.width = width; 811 | canvas.height = height; 812 | 813 | ctx.clearRect(0, 0, width, height); 814 | 815 | this.textures.sort(function (a, b) { 816 | var nameA = a.name.toUpperCase(); 817 | var nameB = b.name.toUpperCase(); 818 | return nameA < nameB ? -1 : nameA > nameB ? 1 : 0; 819 | }); 820 | 821 | for (var i = 0; i < this.textures.length; i++) { 822 | var texture = this.textures[i]; 823 | if (texture.fit) { 824 | 825 | if (DEBUG) { 826 | ctx.fillRect(texture.fit.x + pad, texture.fit.y + pad, texture.w, texture.h); 827 | ctx.stroke(); 828 | } 829 | 830 | ctx.drawImage(texture.img, texture.fit.x + pad, texture.fit.y + pad); 831 | 832 | selectorsString += '.' + prefix + texture.name + (i === this.textures.length - 1 ? ' ' : ', '); 833 | 834 | spriteString += '.' + (prefix + texture.name) + ' {width: ' + texture.w + 'px; ' + ('height: ' + texture.h + 'px; ') + ('background-position: ' + ((texture.fit.x + pad) / (width - texture.w) * 100).toPrecision(6) + '% ') + (((texture.fit.y + pad) / (height - texture.h) * 100).toPrecision(6) + '%; ') + ('background-size: ' + (width / texture.w * 100).toPrecision(6) + '%; }\n'); 835 | } 836 | } 837 | 838 | computedCSS = GITHUB_URL + selectorsString + globalString + spriteString; 839 | 840 | return computedCSS; 841 | } 842 | }, { 843 | key: 'pack', 844 | value: function pack() { 845 | this.sort(); 846 | this.fit(); 847 | return this.draw(); 848 | } 849 | }, { 850 | key: 'remove', 851 | value: function remove(id) { 852 | 853 | for (var i = this.textures.length; i--;) { 854 | var texture = this.textures[i]; 855 | if (texture.id === id) { 856 | this.textures.splice(i, 1); 857 | break; 858 | } 859 | } 860 | 861 | this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); 862 | 863 | var pad = this.root.p; 864 | 865 | // reset the root 866 | this.root = { 867 | x: 0, // origin x 868 | y: 0, // origin y 869 | w: DEFAULT_SIZE - pad, // width 870 | h: DEFAULT_SIZE - pad, // height 871 | p: pad 872 | }; 873 | 874 | return this.pack(); 875 | } 876 | }, { 877 | key: 'updateSettings', 878 | value: function updateSettings(_ref2) { 879 | var padding = _ref2.padding, 880 | prefix = _ref2.prefix, 881 | path = _ref2.path; 882 | 883 | 884 | var canvas = this.canvas; 885 | 886 | this.ctx.clearRect(0, 0, canvas.width || DEFAULT_SIZE, canvas.height || DEFAULT_SIZE); 887 | 888 | this.root = { 889 | x: 0, // origin x 890 | y: 0, // origin y 891 | w: DEFAULT_SIZE - padding, // width 892 | h: DEFAULT_SIZE - padding, // height 893 | p: padding 894 | }; 895 | 896 | this.prefix = prefix; 897 | this.path = path; 898 | 899 | return this.pack(); 900 | } 901 | }]); 902 | 903 | return TexturePacker; 904 | }(); 905 | 906 | exports.default = TexturePacker; 907 | 908 | /***/ }), 909 | /* 7 */ 910 | /***/ (function(module, exports, __webpack_require__) { 911 | 912 | "use strict"; 913 | 914 | 915 | Object.defineProperty(exports, "__esModule", { 916 | value: true 917 | }); 918 | 919 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 920 | 921 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 922 | 923 | var Template = function () { 924 | function Template() { 925 | _classCallCheck(this, Template); 926 | } 927 | 928 | _createClass(Template, [{ 929 | key: 'listItem', 930 | value: function listItem(item) { 931 | 932 | var li = document.createElement('li'); 933 | var img = document.createElement('img'); 934 | var info = document.createElement('span'); 935 | var remove = document.createElement('div'); 936 | 937 | li.setAttribute('data-id', item.id); 938 | 939 | img.src = item.src; 940 | img.height = 60; 941 | img.onload = function () { 942 | window.URL.revokeObjectURL(item.src); 943 | item.onLoadSuccess({ 944 | img: this, 945 | w: this.naturalWidth, 946 | h: this.naturalHeight, 947 | name: item.name, 948 | id: item.id 949 | }); 950 | }; 951 | 952 | li.appendChild(img); 953 | info.innerHTML = item.name; 954 | li.appendChild(info); 955 | 956 | remove.classList.add('remove'); 957 | li.appendChild(remove); 958 | 959 | return li; 960 | } 961 | }]); 962 | 963 | return Template; 964 | }(); 965 | 966 | exports.default = Template; 967 | 968 | /***/ }), 969 | /* 8 */ 970 | /***/ (function(module, exports, __webpack_require__) { 971 | 972 | var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) { 973 | if (true) { 974 | !(__WEBPACK_AMD_DEFINE_ARRAY__ = [module, __webpack_require__(9), __webpack_require__(11), __webpack_require__(12)], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), 975 | __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? 976 | (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), 977 | __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); 978 | } else if (typeof exports !== "undefined") { 979 | factory(module, require('./clipboard-action'), require('tiny-emitter'), require('good-listener')); 980 | } else { 981 | var mod = { 982 | exports: {} 983 | }; 984 | factory(mod, global.clipboardAction, global.tinyEmitter, global.goodListener); 985 | global.clipboard = mod.exports; 986 | } 987 | })(this, function (module, _clipboardAction, _tinyEmitter, _goodListener) { 988 | 'use strict'; 989 | 990 | var _clipboardAction2 = _interopRequireDefault(_clipboardAction); 991 | 992 | var _tinyEmitter2 = _interopRequireDefault(_tinyEmitter); 993 | 994 | var _goodListener2 = _interopRequireDefault(_goodListener); 995 | 996 | function _interopRequireDefault(obj) { 997 | return obj && obj.__esModule ? obj : { 998 | default: obj 999 | }; 1000 | } 1001 | 1002 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { 1003 | return typeof obj; 1004 | } : function (obj) { 1005 | return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; 1006 | }; 1007 | 1008 | function _classCallCheck(instance, Constructor) { 1009 | if (!(instance instanceof Constructor)) { 1010 | throw new TypeError("Cannot call a class as a function"); 1011 | } 1012 | } 1013 | 1014 | var _createClass = function () { 1015 | function defineProperties(target, props) { 1016 | for (var i = 0; i < props.length; i++) { 1017 | var descriptor = props[i]; 1018 | descriptor.enumerable = descriptor.enumerable || false; 1019 | descriptor.configurable = true; 1020 | if ("value" in descriptor) descriptor.writable = true; 1021 | Object.defineProperty(target, descriptor.key, descriptor); 1022 | } 1023 | } 1024 | 1025 | return function (Constructor, protoProps, staticProps) { 1026 | if (protoProps) defineProperties(Constructor.prototype, protoProps); 1027 | if (staticProps) defineProperties(Constructor, staticProps); 1028 | return Constructor; 1029 | }; 1030 | }(); 1031 | 1032 | function _possibleConstructorReturn(self, call) { 1033 | if (!self) { 1034 | throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); 1035 | } 1036 | 1037 | return call && (typeof call === "object" || typeof call === "function") ? call : self; 1038 | } 1039 | 1040 | function _inherits(subClass, superClass) { 1041 | if (typeof superClass !== "function" && superClass !== null) { 1042 | throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); 1043 | } 1044 | 1045 | subClass.prototype = Object.create(superClass && superClass.prototype, { 1046 | constructor: { 1047 | value: subClass, 1048 | enumerable: false, 1049 | writable: true, 1050 | configurable: true 1051 | } 1052 | }); 1053 | if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; 1054 | } 1055 | 1056 | var Clipboard = function (_Emitter) { 1057 | _inherits(Clipboard, _Emitter); 1058 | 1059 | /** 1060 | * @param {String|HTMLElement|HTMLCollection|NodeList} trigger 1061 | * @param {Object} options 1062 | */ 1063 | function Clipboard(trigger, options) { 1064 | _classCallCheck(this, Clipboard); 1065 | 1066 | var _this = _possibleConstructorReturn(this, (Clipboard.__proto__ || Object.getPrototypeOf(Clipboard)).call(this)); 1067 | 1068 | _this.resolveOptions(options); 1069 | _this.listenClick(trigger); 1070 | return _this; 1071 | } 1072 | 1073 | /** 1074 | * Defines if attributes would be resolved using internal setter functions 1075 | * or custom functions that were passed in the constructor. 1076 | * @param {Object} options 1077 | */ 1078 | 1079 | 1080 | _createClass(Clipboard, [{ 1081 | key: 'resolveOptions', 1082 | value: function resolveOptions() { 1083 | var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 1084 | 1085 | this.action = typeof options.action === 'function' ? options.action : this.defaultAction; 1086 | this.target = typeof options.target === 'function' ? options.target : this.defaultTarget; 1087 | this.text = typeof options.text === 'function' ? options.text : this.defaultText; 1088 | this.container = _typeof(options.container) === 'object' ? options.container : document.body; 1089 | } 1090 | }, { 1091 | key: 'listenClick', 1092 | value: function listenClick(trigger) { 1093 | var _this2 = this; 1094 | 1095 | this.listener = (0, _goodListener2.default)(trigger, 'click', function (e) { 1096 | return _this2.onClick(e); 1097 | }); 1098 | } 1099 | }, { 1100 | key: 'onClick', 1101 | value: function onClick(e) { 1102 | var trigger = e.delegateTarget || e.currentTarget; 1103 | 1104 | if (this.clipboardAction) { 1105 | this.clipboardAction = null; 1106 | } 1107 | 1108 | this.clipboardAction = new _clipboardAction2.default({ 1109 | action: this.action(trigger), 1110 | target: this.target(trigger), 1111 | text: this.text(trigger), 1112 | container: this.container, 1113 | trigger: trigger, 1114 | emitter: this 1115 | }); 1116 | } 1117 | }, { 1118 | key: 'defaultAction', 1119 | value: function defaultAction(trigger) { 1120 | return getAttributeValue('action', trigger); 1121 | } 1122 | }, { 1123 | key: 'defaultTarget', 1124 | value: function defaultTarget(trigger) { 1125 | var selector = getAttributeValue('target', trigger); 1126 | 1127 | if (selector) { 1128 | return document.querySelector(selector); 1129 | } 1130 | } 1131 | }, { 1132 | key: 'defaultText', 1133 | value: function defaultText(trigger) { 1134 | return getAttributeValue('text', trigger); 1135 | } 1136 | }, { 1137 | key: 'destroy', 1138 | value: function destroy() { 1139 | this.listener.destroy(); 1140 | 1141 | if (this.clipboardAction) { 1142 | this.clipboardAction.destroy(); 1143 | this.clipboardAction = null; 1144 | } 1145 | } 1146 | }], [{ 1147 | key: 'isSupported', 1148 | value: function isSupported() { 1149 | var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut']; 1150 | 1151 | var actions = typeof action === 'string' ? [action] : action; 1152 | var support = !!document.queryCommandSupported; 1153 | 1154 | actions.forEach(function (action) { 1155 | support = support && !!document.queryCommandSupported(action); 1156 | }); 1157 | 1158 | return support; 1159 | } 1160 | }]); 1161 | 1162 | return Clipboard; 1163 | }(_tinyEmitter2.default); 1164 | 1165 | /** 1166 | * Helper function to retrieve attribute value. 1167 | * @param {String} suffix 1168 | * @param {Element} element 1169 | */ 1170 | function getAttributeValue(suffix, element) { 1171 | var attribute = 'data-clipboard-' + suffix; 1172 | 1173 | if (!element.hasAttribute(attribute)) { 1174 | return; 1175 | } 1176 | 1177 | return element.getAttribute(attribute); 1178 | } 1179 | 1180 | module.exports = Clipboard; 1181 | }); 1182 | 1183 | /***/ }), 1184 | /* 9 */ 1185 | /***/ (function(module, exports, __webpack_require__) { 1186 | 1187 | var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) { 1188 | if (true) { 1189 | !(__WEBPACK_AMD_DEFINE_ARRAY__ = [module, __webpack_require__(10)], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), 1190 | __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? 1191 | (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), 1192 | __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); 1193 | } else if (typeof exports !== "undefined") { 1194 | factory(module, require('select')); 1195 | } else { 1196 | var mod = { 1197 | exports: {} 1198 | }; 1199 | factory(mod, global.select); 1200 | global.clipboardAction = mod.exports; 1201 | } 1202 | })(this, function (module, _select) { 1203 | 'use strict'; 1204 | 1205 | var _select2 = _interopRequireDefault(_select); 1206 | 1207 | function _interopRequireDefault(obj) { 1208 | return obj && obj.__esModule ? obj : { 1209 | default: obj 1210 | }; 1211 | } 1212 | 1213 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { 1214 | return typeof obj; 1215 | } : function (obj) { 1216 | return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; 1217 | }; 1218 | 1219 | function _classCallCheck(instance, Constructor) { 1220 | if (!(instance instanceof Constructor)) { 1221 | throw new TypeError("Cannot call a class as a function"); 1222 | } 1223 | } 1224 | 1225 | var _createClass = function () { 1226 | function defineProperties(target, props) { 1227 | for (var i = 0; i < props.length; i++) { 1228 | var descriptor = props[i]; 1229 | descriptor.enumerable = descriptor.enumerable || false; 1230 | descriptor.configurable = true; 1231 | if ("value" in descriptor) descriptor.writable = true; 1232 | Object.defineProperty(target, descriptor.key, descriptor); 1233 | } 1234 | } 1235 | 1236 | return function (Constructor, protoProps, staticProps) { 1237 | if (protoProps) defineProperties(Constructor.prototype, protoProps); 1238 | if (staticProps) defineProperties(Constructor, staticProps); 1239 | return Constructor; 1240 | }; 1241 | }(); 1242 | 1243 | var ClipboardAction = function () { 1244 | /** 1245 | * @param {Object} options 1246 | */ 1247 | function ClipboardAction(options) { 1248 | _classCallCheck(this, ClipboardAction); 1249 | 1250 | this.resolveOptions(options); 1251 | this.initSelection(); 1252 | } 1253 | 1254 | /** 1255 | * Defines base properties passed from constructor. 1256 | * @param {Object} options 1257 | */ 1258 | 1259 | 1260 | _createClass(ClipboardAction, [{ 1261 | key: 'resolveOptions', 1262 | value: function resolveOptions() { 1263 | var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 1264 | 1265 | this.action = options.action; 1266 | this.container = options.container; 1267 | this.emitter = options.emitter; 1268 | this.target = options.target; 1269 | this.text = options.text; 1270 | this.trigger = options.trigger; 1271 | 1272 | this.selectedText = ''; 1273 | } 1274 | }, { 1275 | key: 'initSelection', 1276 | value: function initSelection() { 1277 | if (this.text) { 1278 | this.selectFake(); 1279 | } else if (this.target) { 1280 | this.selectTarget(); 1281 | } 1282 | } 1283 | }, { 1284 | key: 'selectFake', 1285 | value: function selectFake() { 1286 | var _this = this; 1287 | 1288 | var isRTL = document.documentElement.getAttribute('dir') == 'rtl'; 1289 | 1290 | this.removeFake(); 1291 | 1292 | this.fakeHandlerCallback = function () { 1293 | return _this.removeFake(); 1294 | }; 1295 | this.fakeHandler = this.container.addEventListener('click', this.fakeHandlerCallback) || true; 1296 | 1297 | this.fakeElem = document.createElement('textarea'); 1298 | // Prevent zooming on iOS 1299 | this.fakeElem.style.fontSize = '12pt'; 1300 | // Reset box model 1301 | this.fakeElem.style.border = '0'; 1302 | this.fakeElem.style.padding = '0'; 1303 | this.fakeElem.style.margin = '0'; 1304 | // Move element out of screen horizontally 1305 | this.fakeElem.style.position = 'absolute'; 1306 | this.fakeElem.style[isRTL ? 'right' : 'left'] = '-9999px'; 1307 | // Move element to the same position vertically 1308 | var yPosition = window.pageYOffset || document.documentElement.scrollTop; 1309 | this.fakeElem.style.top = yPosition + 'px'; 1310 | 1311 | this.fakeElem.setAttribute('readonly', ''); 1312 | this.fakeElem.value = this.text; 1313 | 1314 | this.container.appendChild(this.fakeElem); 1315 | 1316 | this.selectedText = (0, _select2.default)(this.fakeElem); 1317 | this.copyText(); 1318 | } 1319 | }, { 1320 | key: 'removeFake', 1321 | value: function removeFake() { 1322 | if (this.fakeHandler) { 1323 | this.container.removeEventListener('click', this.fakeHandlerCallback); 1324 | this.fakeHandler = null; 1325 | this.fakeHandlerCallback = null; 1326 | } 1327 | 1328 | if (this.fakeElem) { 1329 | this.container.removeChild(this.fakeElem); 1330 | this.fakeElem = null; 1331 | } 1332 | } 1333 | }, { 1334 | key: 'selectTarget', 1335 | value: function selectTarget() { 1336 | this.selectedText = (0, _select2.default)(this.target); 1337 | this.copyText(); 1338 | } 1339 | }, { 1340 | key: 'copyText', 1341 | value: function copyText() { 1342 | var succeeded = void 0; 1343 | 1344 | try { 1345 | succeeded = document.execCommand(this.action); 1346 | } catch (err) { 1347 | succeeded = false; 1348 | } 1349 | 1350 | this.handleResult(succeeded); 1351 | } 1352 | }, { 1353 | key: 'handleResult', 1354 | value: function handleResult(succeeded) { 1355 | this.emitter.emit(succeeded ? 'success' : 'error', { 1356 | action: this.action, 1357 | text: this.selectedText, 1358 | trigger: this.trigger, 1359 | clearSelection: this.clearSelection.bind(this) 1360 | }); 1361 | } 1362 | }, { 1363 | key: 'clearSelection', 1364 | value: function clearSelection() { 1365 | if (this.trigger) { 1366 | this.trigger.focus(); 1367 | } 1368 | 1369 | window.getSelection().removeAllRanges(); 1370 | } 1371 | }, { 1372 | key: 'destroy', 1373 | value: function destroy() { 1374 | this.removeFake(); 1375 | } 1376 | }, { 1377 | key: 'action', 1378 | set: function set() { 1379 | var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'copy'; 1380 | 1381 | this._action = action; 1382 | 1383 | if (this._action !== 'copy' && this._action !== 'cut') { 1384 | throw new Error('Invalid "action" value, use either "copy" or "cut"'); 1385 | } 1386 | }, 1387 | get: function get() { 1388 | return this._action; 1389 | } 1390 | }, { 1391 | key: 'target', 1392 | set: function set(target) { 1393 | if (target !== undefined) { 1394 | if (target && (typeof target === 'undefined' ? 'undefined' : _typeof(target)) === 'object' && target.nodeType === 1) { 1395 | if (this.action === 'copy' && target.hasAttribute('disabled')) { 1396 | throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute'); 1397 | } 1398 | 1399 | if (this.action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) { 1400 | throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes'); 1401 | } 1402 | 1403 | this._target = target; 1404 | } else { 1405 | throw new Error('Invalid "target" value, use a valid Element'); 1406 | } 1407 | } 1408 | }, 1409 | get: function get() { 1410 | return this._target; 1411 | } 1412 | }]); 1413 | 1414 | return ClipboardAction; 1415 | }(); 1416 | 1417 | module.exports = ClipboardAction; 1418 | }); 1419 | 1420 | /***/ }), 1421 | /* 10 */ 1422 | /***/ (function(module, exports) { 1423 | 1424 | function select(element) { 1425 | var selectedText; 1426 | 1427 | if (element.nodeName === 'SELECT') { 1428 | element.focus(); 1429 | 1430 | selectedText = element.value; 1431 | } 1432 | else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') { 1433 | var isReadOnly = element.hasAttribute('readonly'); 1434 | 1435 | if (!isReadOnly) { 1436 | element.setAttribute('readonly', ''); 1437 | } 1438 | 1439 | element.select(); 1440 | element.setSelectionRange(0, element.value.length); 1441 | 1442 | if (!isReadOnly) { 1443 | element.removeAttribute('readonly'); 1444 | } 1445 | 1446 | selectedText = element.value; 1447 | } 1448 | else { 1449 | if (element.hasAttribute('contenteditable')) { 1450 | element.focus(); 1451 | } 1452 | 1453 | var selection = window.getSelection(); 1454 | var range = document.createRange(); 1455 | 1456 | range.selectNodeContents(element); 1457 | selection.removeAllRanges(); 1458 | selection.addRange(range); 1459 | 1460 | selectedText = selection.toString(); 1461 | } 1462 | 1463 | return selectedText; 1464 | } 1465 | 1466 | module.exports = select; 1467 | 1468 | 1469 | /***/ }), 1470 | /* 11 */ 1471 | /***/ (function(module, exports) { 1472 | 1473 | function E () { 1474 | // Keep this empty so it's easier to inherit from 1475 | // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3) 1476 | } 1477 | 1478 | E.prototype = { 1479 | on: function (name, callback, ctx) { 1480 | var e = this.e || (this.e = {}); 1481 | 1482 | (e[name] || (e[name] = [])).push({ 1483 | fn: callback, 1484 | ctx: ctx 1485 | }); 1486 | 1487 | return this; 1488 | }, 1489 | 1490 | once: function (name, callback, ctx) { 1491 | var self = this; 1492 | function listener () { 1493 | self.off(name, listener); 1494 | callback.apply(ctx, arguments); 1495 | }; 1496 | 1497 | listener._ = callback 1498 | return this.on(name, listener, ctx); 1499 | }, 1500 | 1501 | emit: function (name) { 1502 | var data = [].slice.call(arguments, 1); 1503 | var evtArr = ((this.e || (this.e = {}))[name] || []).slice(); 1504 | var i = 0; 1505 | var len = evtArr.length; 1506 | 1507 | for (i; i < len; i++) { 1508 | evtArr[i].fn.apply(evtArr[i].ctx, data); 1509 | } 1510 | 1511 | return this; 1512 | }, 1513 | 1514 | off: function (name, callback) { 1515 | var e = this.e || (this.e = {}); 1516 | var evts = e[name]; 1517 | var liveEvents = []; 1518 | 1519 | if (evts && callback) { 1520 | for (var i = 0, len = evts.length; i < len; i++) { 1521 | if (evts[i].fn !== callback && evts[i].fn._ !== callback) 1522 | liveEvents.push(evts[i]); 1523 | } 1524 | } 1525 | 1526 | // Remove event from queue to prevent memory leak 1527 | // Suggested by https://github.com/lazd 1528 | // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910 1529 | 1530 | (liveEvents.length) 1531 | ? e[name] = liveEvents 1532 | : delete e[name]; 1533 | 1534 | return this; 1535 | } 1536 | }; 1537 | 1538 | module.exports = E; 1539 | 1540 | 1541 | /***/ }), 1542 | /* 12 */ 1543 | /***/ (function(module, exports, __webpack_require__) { 1544 | 1545 | var is = __webpack_require__(13); 1546 | var delegate = __webpack_require__(14); 1547 | 1548 | /** 1549 | * Validates all params and calls the right 1550 | * listener function based on its target type. 1551 | * 1552 | * @param {String|HTMLElement|HTMLCollection|NodeList} target 1553 | * @param {String} type 1554 | * @param {Function} callback 1555 | * @return {Object} 1556 | */ 1557 | function listen(target, type, callback) { 1558 | if (!target && !type && !callback) { 1559 | throw new Error('Missing required arguments'); 1560 | } 1561 | 1562 | if (!is.string(type)) { 1563 | throw new TypeError('Second argument must be a String'); 1564 | } 1565 | 1566 | if (!is.fn(callback)) { 1567 | throw new TypeError('Third argument must be a Function'); 1568 | } 1569 | 1570 | if (is.node(target)) { 1571 | return listenNode(target, type, callback); 1572 | } 1573 | else if (is.nodeList(target)) { 1574 | return listenNodeList(target, type, callback); 1575 | } 1576 | else if (is.string(target)) { 1577 | return listenSelector(target, type, callback); 1578 | } 1579 | else { 1580 | throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList'); 1581 | } 1582 | } 1583 | 1584 | /** 1585 | * Adds an event listener to a HTML element 1586 | * and returns a remove listener function. 1587 | * 1588 | * @param {HTMLElement} node 1589 | * @param {String} type 1590 | * @param {Function} callback 1591 | * @return {Object} 1592 | */ 1593 | function listenNode(node, type, callback) { 1594 | node.addEventListener(type, callback); 1595 | 1596 | return { 1597 | destroy: function() { 1598 | node.removeEventListener(type, callback); 1599 | } 1600 | } 1601 | } 1602 | 1603 | /** 1604 | * Add an event listener to a list of HTML elements 1605 | * and returns a remove listener function. 1606 | * 1607 | * @param {NodeList|HTMLCollection} nodeList 1608 | * @param {String} type 1609 | * @param {Function} callback 1610 | * @return {Object} 1611 | */ 1612 | function listenNodeList(nodeList, type, callback) { 1613 | Array.prototype.forEach.call(nodeList, function(node) { 1614 | node.addEventListener(type, callback); 1615 | }); 1616 | 1617 | return { 1618 | destroy: function() { 1619 | Array.prototype.forEach.call(nodeList, function(node) { 1620 | node.removeEventListener(type, callback); 1621 | }); 1622 | } 1623 | } 1624 | } 1625 | 1626 | /** 1627 | * Add an event listener to a selector 1628 | * and returns a remove listener function. 1629 | * 1630 | * @param {String} selector 1631 | * @param {String} type 1632 | * @param {Function} callback 1633 | * @return {Object} 1634 | */ 1635 | function listenSelector(selector, type, callback) { 1636 | return delegate(document.body, selector, type, callback); 1637 | } 1638 | 1639 | module.exports = listen; 1640 | 1641 | 1642 | /***/ }), 1643 | /* 13 */ 1644 | /***/ (function(module, exports) { 1645 | 1646 | /** 1647 | * Check if argument is a HTML element. 1648 | * 1649 | * @param {Object} value 1650 | * @return {Boolean} 1651 | */ 1652 | exports.node = function(value) { 1653 | return value !== undefined 1654 | && value instanceof HTMLElement 1655 | && value.nodeType === 1; 1656 | }; 1657 | 1658 | /** 1659 | * Check if argument is a list of HTML elements. 1660 | * 1661 | * @param {Object} value 1662 | * @return {Boolean} 1663 | */ 1664 | exports.nodeList = function(value) { 1665 | var type = Object.prototype.toString.call(value); 1666 | 1667 | return value !== undefined 1668 | && (type === '[object NodeList]' || type === '[object HTMLCollection]') 1669 | && ('length' in value) 1670 | && (value.length === 0 || exports.node(value[0])); 1671 | }; 1672 | 1673 | /** 1674 | * Check if argument is a string. 1675 | * 1676 | * @param {Object} value 1677 | * @return {Boolean} 1678 | */ 1679 | exports.string = function(value) { 1680 | return typeof value === 'string' 1681 | || value instanceof String; 1682 | }; 1683 | 1684 | /** 1685 | * Check if argument is a function. 1686 | * 1687 | * @param {Object} value 1688 | * @return {Boolean} 1689 | */ 1690 | exports.fn = function(value) { 1691 | var type = Object.prototype.toString.call(value); 1692 | 1693 | return type === '[object Function]'; 1694 | }; 1695 | 1696 | 1697 | /***/ }), 1698 | /* 14 */ 1699 | /***/ (function(module, exports, __webpack_require__) { 1700 | 1701 | var closest = __webpack_require__(15); 1702 | 1703 | /** 1704 | * Delegates event to a selector. 1705 | * 1706 | * @param {Element} element 1707 | * @param {String} selector 1708 | * @param {String} type 1709 | * @param {Function} callback 1710 | * @param {Boolean} useCapture 1711 | * @return {Object} 1712 | */ 1713 | function _delegate(element, selector, type, callback, useCapture) { 1714 | var listenerFn = listener.apply(this, arguments); 1715 | 1716 | element.addEventListener(type, listenerFn, useCapture); 1717 | 1718 | return { 1719 | destroy: function() { 1720 | element.removeEventListener(type, listenerFn, useCapture); 1721 | } 1722 | } 1723 | } 1724 | 1725 | /** 1726 | * Delegates event to a selector. 1727 | * 1728 | * @param {Element|String|Array} [elements] 1729 | * @param {String} selector 1730 | * @param {String} type 1731 | * @param {Function} callback 1732 | * @param {Boolean} useCapture 1733 | * @return {Object} 1734 | */ 1735 | function delegate(elements, selector, type, callback, useCapture) { 1736 | // Handle the regular Element usage 1737 | if (typeof elements.addEventListener === 'function') { 1738 | return _delegate.apply(null, arguments); 1739 | } 1740 | 1741 | // Handle Element-less usage, it defaults to global delegation 1742 | if (typeof type === 'function') { 1743 | // Use `document` as the first parameter, then apply arguments 1744 | // This is a short way to .unshift `arguments` without running into deoptimizations 1745 | return _delegate.bind(null, document).apply(null, arguments); 1746 | } 1747 | 1748 | // Handle Selector-based usage 1749 | if (typeof elements === 'string') { 1750 | elements = document.querySelectorAll(elements); 1751 | } 1752 | 1753 | // Handle Array-like based usage 1754 | return Array.prototype.map.call(elements, function (element) { 1755 | return _delegate(element, selector, type, callback, useCapture); 1756 | }); 1757 | } 1758 | 1759 | /** 1760 | * Finds closest match and invokes callback. 1761 | * 1762 | * @param {Element} element 1763 | * @param {String} selector 1764 | * @param {String} type 1765 | * @param {Function} callback 1766 | * @return {Function} 1767 | */ 1768 | function listener(element, selector, type, callback) { 1769 | return function(e) { 1770 | e.delegateTarget = closest(e.target, selector); 1771 | 1772 | if (e.delegateTarget) { 1773 | callback.call(element, e); 1774 | } 1775 | } 1776 | } 1777 | 1778 | module.exports = delegate; 1779 | 1780 | 1781 | /***/ }), 1782 | /* 15 */ 1783 | /***/ (function(module, exports) { 1784 | 1785 | var DOCUMENT_NODE_TYPE = 9; 1786 | 1787 | /** 1788 | * A polyfill for Element.matches() 1789 | */ 1790 | if (typeof Element !== 'undefined' && !Element.prototype.matches) { 1791 | var proto = Element.prototype; 1792 | 1793 | proto.matches = proto.matchesSelector || 1794 | proto.mozMatchesSelector || 1795 | proto.msMatchesSelector || 1796 | proto.oMatchesSelector || 1797 | proto.webkitMatchesSelector; 1798 | } 1799 | 1800 | /** 1801 | * Finds the closest parent that matches a selector. 1802 | * 1803 | * @param {Element} element 1804 | * @param {String} selector 1805 | * @return {Function} 1806 | */ 1807 | function closest (element, selector) { 1808 | while (element && element.nodeType !== DOCUMENT_NODE_TYPE) { 1809 | if (typeof element.matches === 'function' && 1810 | element.matches(selector)) { 1811 | return element; 1812 | } 1813 | element = element.parentNode; 1814 | } 1815 | } 1816 | 1817 | module.exports = closest; 1818 | 1819 | 1820 | /***/ }), 1821 | /* 16 */ 1822 | /***/ (function(module, exports, __webpack_require__) { 1823 | 1824 | var __WEBPACK_AMD_DEFINE_RESULT__;/* Web Font Loader v1.6.28 - (c) Adobe Systems, Google. License: Apache 2.0 */(function(){function aa(a,b,c){return a.call.apply(a.bind,arguments)}function ba(a,b,c){if(!a)throw Error();if(2=b.f?e():a.fonts.load(fa(b.a),b.h).then(function(a){1<=a.length?d():setTimeout(f,25)},function(){e()})}f()}),e=null,f=new Promise(function(a,d){e=setTimeout(d,b.f)});Promise.race([f,d]).then(function(){e&&(clearTimeout(e),e=null);b.g(b.a)},function(){b.j(b.a)})};function Q(a,b,c,d,e,f,g){this.v=a;this.B=b;this.c=c;this.a=d;this.s=g||"BESbswy";this.f={};this.w=e||3E3;this.u=f||null;this.m=this.j=this.h=this.g=null;this.g=new M(this.c,this.s);this.h=new M(this.c,this.s);this.j=new M(this.c,this.s);this.m=new M(this.c,this.s);a=new G(this.a.c+",serif",J(this.a));a=O(a);this.g.a.style.cssText=a;a=new G(this.a.c+",sans-serif",J(this.a));a=O(a);this.h.a.style.cssText=a;a=new G("serif",J(this.a));a=O(a);this.j.a.style.cssText=a;a=new G("sans-serif",J(this.a));a= 1829 | O(a);this.m.a.style.cssText=a;N(this.g);N(this.h);N(this.j);N(this.m)}var R={D:"serif",C:"sans-serif"},S=null;function T(){if(null===S){var a=/AppleWebKit\/([0-9]+)(?:\.([0-9]+))/.exec(window.navigator.userAgent);S=!!a&&(536>parseInt(a[1],10)||536===parseInt(a[1],10)&&11>=parseInt(a[2],10))}return S}Q.prototype.start=function(){this.f.serif=this.j.a.offsetWidth;this.f["sans-serif"]=this.m.a.offsetWidth;this.A=q();U(this)}; 1830 | function la(a,b,c){for(var d in R)if(R.hasOwnProperty(d)&&b===a.f[R[d]]&&c===a.f[R[d]])return!0;return!1}function U(a){var b=a.g.a.offsetWidth,c=a.h.a.offsetWidth,d;(d=b===a.f.serif&&c===a.f["sans-serif"])||(d=T()&&la(a,b,c));d?q()-a.A>=a.w?T()&&la(a,b,c)&&(null===a.u||a.u.hasOwnProperty(a.a.c))?V(a,a.v):V(a,a.B):ma(a):V(a,a.v)}function ma(a){setTimeout(p(function(){U(this)},a),50)}function V(a,b){setTimeout(p(function(){v(this.g.a);v(this.h.a);v(this.j.a);v(this.m.a);b(this.a)},a),0)};function W(a,b,c){this.c=a;this.a=b;this.f=0;this.m=this.j=!1;this.s=c}var X=null;W.prototype.g=function(a){var b=this.a;b.g&&w(b.f,[b.a.c("wf",a.c,J(a).toString(),"active")],[b.a.c("wf",a.c,J(a).toString(),"loading"),b.a.c("wf",a.c,J(a).toString(),"inactive")]);K(b,"fontactive",a);this.m=!0;na(this)}; 1831 | W.prototype.h=function(a){var b=this.a;if(b.g){var c=y(b.f,b.a.c("wf",a.c,J(a).toString(),"active")),d=[],e=[b.a.c("wf",a.c,J(a).toString(),"loading")];c||d.push(b.a.c("wf",a.c,J(a).toString(),"inactive"));w(b.f,d,e)}K(b,"fontinactive",a);na(this)};function na(a){0==--a.f&&a.j&&(a.m?(a=a.a,a.g&&w(a.f,[a.a.c("wf","active")],[a.a.c("wf","loading"),a.a.c("wf","inactive")]),K(a,"active")):L(a.a))};function oa(a){this.j=a;this.a=new ja;this.h=0;this.f=this.g=!0}oa.prototype.load=function(a){this.c=new ca(this.j,a.context||this.j);this.g=!1!==a.events;this.f=!1!==a.classes;pa(this,new ha(this.c,a),a)}; 1832 | function qa(a,b,c,d,e){var f=0==--a.h;(a.f||a.g)&&setTimeout(function(){var a=e||null,m=d||null||{};if(0===c.length&&f)L(b.a);else{b.f+=c.length;f&&(b.j=f);var h,l=[];for(h=0;h 0) { 78 | var packer = new Packer(padding, prefix, path); 79 | packer.sort(blocks); 80 | packer.fit(blocks); 81 | packer.draw( 82 | blocks, 83 | canvas, 84 | css 85 | ); 86 | dimensionsElem.innerHTML = '(' + canvas.width + 'px by ' + canvas.height + 'px)'; 87 | ga('send', { 88 | hitType: 'event', 89 | eventCategory: 'Update Values', 90 | eventAction: 'keyup' 91 | }); 92 | } 93 | } 94 | 95 | var dropbox; 96 | 97 | dropbox = document.getElementById("dropbox"); 98 | dropbox.addEventListener("dragenter", dragenter, false); 99 | dropbox.addEventListener("dragover", dragover, false); 100 | dropbox.addEventListener("drop", drop, false); 101 | dropbox.addEventListener("click", function(e){ 102 | if (fileElem) { 103 | fileElem.click(); 104 | } 105 | e.preventDefault(); // prevent navigation to "#" 106 | ga('send', { 107 | hitType: 'event', 108 | eventCategory: 'File Explorer', 109 | eventAction: 'click' 110 | }); 111 | }, false); 112 | 113 | function dragenter(e) { 114 | e.stopPropagation(); 115 | e.preventDefault(); 116 | } 117 | 118 | function dragover(e) { 119 | e.stopPropagation(); 120 | e.preventDefault(); 121 | } 122 | 123 | function drop(e) { 124 | e.stopPropagation(); 125 | e.preventDefault(); 126 | 127 | var dt = e.dataTransfer; 128 | var files = dt.files; 129 | 130 | handleFiles(files); 131 | 132 | ga('send', { 133 | hitType: 'event', 134 | eventCategory: 'File Drop', 135 | eventAction: 'drop' 136 | }); 137 | 138 | } 139 | 140 | /* 141 | * Handle Files 142 | * */ 143 | 144 | function handleFiles(files) { 145 | if (!files.length) { 146 | fileList.innerHTML = "

No files selected!

"; 147 | } else { 148 | loadInProgress = true; 149 | //fileList.innerHTML = ""; 150 | //var list = document.createElement("ul"); 151 | //fileList.appendChild(list); 152 | for (var i = 0; i < files.length; i++) { 153 | id++; 154 | var li = document.createElement("li"); 155 | var img = document.createElement("img"); 156 | var info = document.createElement("span"); 157 | var remove = document.createElement("div"); 158 | 159 | li.setAttribute('data-id', id); 160 | list.appendChild(li); 161 | 162 | img.src = window.URL.createObjectURL(files[i]); 163 | img.height = 60; 164 | img.onload = onload(id, files[i], files.length + blocks.length); 165 | li.appendChild(img); 166 | 167 | info.innerHTML = files[i].name.substring(0, files[i].name.indexOf('.')); 168 | li.appendChild(info); 169 | 170 | remove.classList.add('remove'); 171 | li.appendChild(remove); 172 | 173 | dropbox.classList.remove('is-empty'); 174 | } 175 | } 176 | fileElem.value = ''; 177 | } 178 | 179 | function loadComplete(){ 180 | var packer = new Packer(padding, prefix, path); 181 | packer.sort(blocks); 182 | packer.fit(blocks); 183 | packer.draw( 184 | blocks, 185 | canvas, 186 | css 187 | ); 188 | dimensionsElem.innerHTML = '(' + canvas.width + 'px by ' + canvas.height + 'px)'; 189 | loadInProgress = false; 190 | } 191 | 192 | function onload(id, file, queue){ 193 | return function(){ 194 | window.URL.revokeObjectURL(this.src); 195 | blocks.push({ 196 | w:this.naturalWidth, 197 | h:this.naturalHeight, 198 | img:this, 199 | name:file.name.substring(0, file.name.indexOf('.')), 200 | id:id 201 | }); 202 | loaded++; 203 | if(loaded === queue){ 204 | loadComplete(); 205 | } 206 | } 207 | } 208 | 209 | document.getElementById('download').addEventListener('click', function(){ 210 | var a = document.createElement('a'); 211 | a.href = canvas.toDataURL(); 212 | a.download = 'sprite.png'; 213 | document.body.appendChild(a); 214 | //console.log(a); 215 | a.click(); 216 | document.body.removeChild(a); 217 | 218 | ga('send', { 219 | hitType: 'event', 220 | eventCategory: 'Sprite Download', 221 | eventAction: 'click' 222 | }); 223 | 224 | }, false); 225 | 226 | var clipboard = new Clipboard('#copy'); 227 | 228 | document.getElementById('copy').addEventListener('click', function(){ 229 | ga('send', { 230 | hitType: 'event', 231 | eventCategory: 'Copy CSS', 232 | eventAction: 'click' 233 | }); 234 | }); -------------------------------------------------------------------------------- /assets/js/legacy/packer.js: -------------------------------------------------------------------------------- 1 | 2 | function Packer(pad, pre, path) { 3 | this.init(pad, pre, path) 4 | } 5 | 6 | Packer.prototype = { 7 | 8 | init: function (pad, pre, path) { 9 | var padding = isNumeric(pad) ? pad : 2; 10 | padding = Math.round(Math.abs(padding)); 11 | this.root = { 12 | x: 0, // origin x 13 | y: 0, // origin y 14 | w: 256 - padding, // width 15 | h: 256 - padding, // height 16 | p: padding 17 | }; 18 | this.prefix = pre; 19 | //this.prefix = this.prefix.replace(/ /g, ''); 20 | this.path = path; 21 | }, 22 | 23 | sort: function (blocks) { 24 | blocks.sort(function (a, b) { 25 | // should this be sorted by height? 26 | if (a.h < b.h) { 27 | return 1; 28 | } 29 | if (a.h > b.h) { 30 | return -1; 31 | } 32 | return 0; 33 | }); 34 | }, 35 | 36 | fit: function (blocks) { 37 | var n, node, block, p = this.root.p; 38 | for (n = 0; n < blocks.length; n++) { 39 | block = blocks[n]; 40 | block.fit = false; // reset 41 | if (node = this.findNode(this.root, block.w + p, block.h + p)) { 42 | block.fit = this.splitNode(node, block.w + p, block.h + p); 43 | } 44 | if(!block.fit){ 45 | this.resize(blocks); 46 | break; 47 | } 48 | } 49 | }, 50 | 51 | resize: function(blocks){ 52 | var w, h, p = this.root.p; 53 | if(this.root.w > this.root.h){ 54 | w = (this.root.w + p); 55 | h = (this.root.h + p) * 2; 56 | } else { 57 | w = (this.root.w + p) * 2; 58 | h = (this.root.h + p); 59 | } 60 | this.root = { 61 | x: 0, // origin x 62 | y: 0, // origin y 63 | w: w - p, // width 64 | h: h - p, // height 65 | p: p 66 | }; 67 | this.fit(blocks); 68 | }, 69 | 70 | findNode: function (root, w, h) { 71 | if (root.used) 72 | return this.findNode(root.right, w, h) || this.findNode(root.down, w, h); 73 | else if ((w <= root.w) && (h <= root.h)) 74 | return root; 75 | else 76 | return null; 77 | }, 78 | 79 | splitNode: function (node, w, h) { 80 | node.used = true; 81 | node.down = {x: node.x, y: node.y + h, w: node.w, h: node.h - h}; 82 | node.right = {x: node.x + w, y: node.y, w: node.w - w, h: h}; 83 | return node; 84 | }, 85 | 86 | draw: function (blocks, canvas, output) { 87 | var ctx = canvas.getContext('2d'); 88 | var gitubUrl = '/*\nResponsive CSS Sprite created using: ' + 89 | 'https://responsive-css.us/\n' + 90 | '*/\n\n'; 91 | var groupSelectors = ''; 92 | var globalStyle = '\n{display:inline-block; overflow:hidden; ' + 93 | 'background-repeat: no-repeat;\n' + 94 | 'background-image:url(' + this.path + ');}\n\n'; 95 | var spriteStyle = ''; 96 | var p = this.root.p; // padding 97 | var width = this.root.w + p; 98 | var height = this.root.h + p; 99 | var b; // block 100 | canvas.width = width; 101 | canvas.height = height; 102 | ctx.clearRect(0, 0, canvas.width, canvas.height); 103 | 104 | // sort blocks alphabetically 105 | blocks.sort(function(a,b){ 106 | var nameA = a.name.toUpperCase(); 107 | var nameB = b.name.toUpperCase(); 108 | return (nameA < nameB) ? -1 : (nameA > nameB) ? 1 : 0; 109 | }); 110 | 111 | for (var n = 0; n < blocks.length; n++) { 112 | b = blocks[n]; 113 | if (b.fit) { 114 | // turn on for testing 115 | // ctx.fillRect(b.fit.x + p, b.fit.y + p, b.w, b.h); 116 | // ctx.stroke(); 117 | ctx.drawImage(b.img, b.fit.x + p, b.fit.y + p); 118 | // add comma if not the last style 119 | groupSelectors += '.' + this.prefix + b.name + (n === blocks.length - 1 ? ' ' : ', '); 120 | // individual sprite style 121 | spriteStyle += '.' + this.prefix + b.name + 122 | ' {width:' + (b.w) + 'px; ' + 123 | 'height:' + (b.h) + 'px; ' + 124 | 'background-position:' + (((b.fit.x + p) / (width - (b.w))) * 100).toPrecision(6) + '% ' + 125 | (((b.fit.y + p) / (height - (b.h))) * 100).toPrecision(6) + '%; ' + 126 | 'background-size:' + ((width / (b.w)) * 100).toPrecision(6) + '%; ' + 127 | '}\n'; 128 | } 129 | } 130 | output.value = gitubUrl + groupSelectors + globalStyle + spriteStyle; 131 | } 132 | 133 | }; 134 | 135 | function isNumeric(n) { 136 | return !isNaN(parseFloat(n)) && isFinite(n); 137 | } 138 | 139 | module.exports = Packer; 140 | -------------------------------------------------------------------------------- /assets/styles/main.css: -------------------------------------------------------------------------------- 1 | html { 2 | -webkit-box-sizing: border-box; 3 | box-sizing: border-box; 4 | font-family: 'Roboto Condensed', sans-serif; 5 | color: #24292e; } 6 | 7 | *, *:before, *:after { 8 | -webkit-box-sizing: inherit; 9 | box-sizing: inherit; } 10 | 11 | html, body { 12 | height: 100%; } 13 | 14 | canvas { 15 | vertical-align: middle; } 16 | 17 | .page-wrap { 18 | padding-top: 120px; 19 | position: relative; 20 | height: 100%; } 21 | 22 | header { 23 | height: 120px; 24 | width: 100%; 25 | min-width: 1024px; 26 | background-color: #24292e; 27 | position: fixed; 28 | z-index: 3; 29 | padding: 8px 10px 0; } 30 | header h1 { 31 | font-size: 2em; 32 | margin: 0 0 2px; 33 | color: #ffffff; 34 | font-weight: normal; } 35 | header h2 { 36 | font-size: 0.85em; 37 | color: rgba(255, 255, 255, 0.75); 38 | font-weight: normal; 39 | margin: 0 0 15px; } 40 | header input { 41 | font-size: 1em; 42 | padding: 6px; 43 | font-family: 'Roboto Condensed', sans-serif; 44 | background-color: rgba(255, 255, 255, 0.125); 45 | color: #fff; 46 | border: none; 47 | border-radius: 2px; } 48 | header input:last-child { 49 | width: 30px; 50 | text-align: center; } 51 | header input.path-input { 52 | width: 400px; } 53 | header label { 54 | color: rgba(255, 255, 255, 0.75); 55 | font-size: 1em; } 56 | 57 | .content { 58 | position: relative; 59 | min-height: 100%; } 60 | 61 | .file-list { 62 | width: 200px; 63 | min-height: 100%; 64 | position: absolute; 65 | top: 0; 66 | left: 0; 67 | border-right: solid 8px #24292e; } 68 | .file-list p { 69 | color: #fafbfc; } 70 | .file-list ul { 71 | list-style: none; 72 | margin: 0; 73 | padding: 5px 5px 0; } 74 | .file-list ul li { 75 | position: relative; 76 | padding: 5px; 77 | margin: 0 0 5px; 78 | min-height: 29px; 79 | border: solid 1px transparent; } 80 | .file-list ul li img { 81 | max-width: 100%; 82 | height: auto; 83 | display: block; } 84 | .file-list ul li span { 85 | position: absolute; 86 | bottom: 5px; 87 | left: 5px; 88 | background-color: #333333; 89 | color: #ffffff; 90 | padding: 3px; 91 | font-size: 0.8em; 92 | border-radius: 5px; 93 | opacity: 0; } 94 | .file-list ul li:hover { 95 | border: dashed 1px #FFFFFF; } 96 | .file-list ul li:hover span { 97 | opacity: 1; } 98 | .file-list ul li:hover .remove { 99 | opacity: 1; } 100 | .file-list .remove { 101 | position: absolute; 102 | top: 3px; 103 | right: 3px; 104 | width: 24px; 105 | height: 24px; 106 | background: transparent url("/assets/img/ic_clear_black_24px.svg") 0 0 no-repeat; 107 | cursor: pointer; 108 | opacity: 0.5; 109 | -webkit-filter: invert(1); 110 | filter: invert(1); } 111 | .file-list .remove:hover { 112 | -webkit-transform: scale(1.1); 113 | -ms-transform: scale(1.1); 114 | transform: scale(1.1); } 115 | 116 | .output { 117 | padding: 10px 10px 10px 210px; } 118 | .output b { 119 | font-size: 1.1em; } 120 | 121 | .add-files-btn { 122 | position: absolute; 123 | top: 0; 124 | left: 0; 125 | right: 0; 126 | bottom: 0; 127 | margin: auto; 128 | width: 48px; 129 | height: 48px; 130 | background: transparent url("/assets/img/ic_add_circle_black_48px.svg") center center no-repeat; 131 | cursor: pointer; 132 | display: none; } 133 | 134 | .add-files-text { 135 | position: absolute; 136 | top: 76px; 137 | left: 0; 138 | right: 0; 139 | bottom: 0; 140 | margin: auto; 141 | width: 175px; 142 | height: 20px; 143 | text-align: center; 144 | cursor: pointer; 145 | display: none; } 146 | 147 | .dropbox { 148 | border: solid 1px #E0E0E0; 149 | margin: 0 auto; 150 | float: left; 151 | position: relative; 152 | min-width: 100%; 153 | min-height: 512px; 154 | cursor: pointer; } 155 | .dropbox.is-empty .add-files-btn, .dropbox.is-empty .add-files-text { 156 | display: block; } 157 | 158 | .fileSelect { 159 | display: block; } 160 | 161 | .clear { 162 | width: 100%; 163 | clear: both; } 164 | 165 | .result { 166 | padding: 10px 0 0 0; } 167 | 168 | textarea { 169 | width: 1024px; 170 | font-family: 'Roboto Condensed', sans-serif; 171 | padding: 10px 8px; 172 | background-color: #F5F5F5; 173 | -webkit-box-shadow: none; 174 | box-shadow: none; 175 | border: solid 1px #E0E0E0; } 176 | 177 | .button { 178 | width: 65px; 179 | height: 65px; 180 | border-radius: 5px; 181 | cursor: pointer; 182 | -webkit-transition: background-color 300ms ease; 183 | -o-transition: background-color 300ms ease; 184 | transition: background-color 300ms ease; } 185 | .button:hover { 186 | background-color: #00E5FF; } 187 | .button span { 188 | display: block; 189 | position: absolute; 190 | left: 0; 191 | bottom: 100%; 192 | width: 100%; 193 | font-size: 0.8em; 194 | color: rgba(255, 255, 255, 0.75); 195 | text-align: center; 196 | padding-bottom: 5px; } 197 | 198 | .download { 199 | position: absolute; 200 | bottom: 10px; 201 | right: 10px; 202 | background: rgba(255, 255, 255, 0.125) url("/assets/img/ic_file_download_white_48px.svg") center center no-repeat; } 203 | 204 | .download-info { 205 | font-size: 0.9em; 206 | margin-bottom: 2px; } 207 | 208 | .copy { 209 | position: absolute; 210 | bottom: 10px; 211 | right: 85px; 212 | background: rgba(255, 255, 255, 0.125) url("/assets/img/ic_content_copy_white_48px.svg") center center no-repeat; } 213 | 214 | .github { 215 | position: relative; 216 | left: 10px; 217 | top: 5px; 218 | display: inline-block; 219 | background: transparent url("/assets/img/GitHub-Mark-Light-32px.png") 0 0 no-repeat; 220 | width: 32px; 221 | height: 32px; } 222 | 223 | .dimensions { 224 | font-size: 0.9em; } 225 | 226 | .dark-checkered { 227 | background-color: #757575; 228 | background-image: -o-linear-gradient(45deg, #424242 25%, transparent 25%), -o-linear-gradient(135deg, #424242 25%, transparent 25%), -o-linear-gradient(45deg, transparent 75%, #424242 75%), -o-linear-gradient(135deg, transparent 75%, #424242 75%); 229 | background-image: linear-gradient(45deg, #424242 25%, transparent 25%), linear-gradient(-45deg, #424242 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #424242 75%), linear-gradient(-45deg, transparent 75%, #424242 75%); 230 | background-size: 20px 20px; 231 | background-position: 0 0, 0 10px, 10px -10px, -10px 0px; } 232 | 233 | .light-checkered { 234 | background-color: #f5f5f5; 235 | background-image: -o-linear-gradient(45deg, #e0e0e0 25%, transparent 25%), -o-linear-gradient(135deg, #e0e0e0 25%, transparent 25%), -o-linear-gradient(45deg, transparent 75%, #e0e0e0 75%), -o-linear-gradient(135deg, transparent 75%, #e0e0e0 75%); 236 | background-image: linear-gradient(45deg, #e0e0e0 25%, transparent 25%), linear-gradient(-45deg, #e0e0e0 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #e0e0e0 75%), linear-gradient(-45deg, transparent 75%, #e0e0e0 75%); 237 | background-size: 20px 20px; 238 | background-position: 0 0, 0 10px, 10px -10px, -10px 0px; } 239 | 240 | 241 | 242 | /*# sourceMappingURL=main.css.map */ 243 | -------------------------------------------------------------------------------- /assets/styles/main.scss: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | font-family: 'Roboto Condensed', sans-serif; 4 | color: #24292e; 5 | } 6 | 7 | *, *:before, *:after { 8 | box-sizing: inherit; 9 | } 10 | 11 | html, body { 12 | height: 100%; 13 | } 14 | 15 | canvas { 16 | vertical-align: middle; 17 | } 18 | 19 | .page-wrap { 20 | padding-top: 120px; 21 | position: relative; 22 | height: 100%; 23 | } 24 | header { 25 | height: 120px; 26 | width: 100%; 27 | min-width: 1024px; 28 | background-color: #24292e; 29 | position: fixed; 30 | z-index: 3; 31 | padding: 8px 10px 0; 32 | 33 | h1 { 34 | font-size: 2em; 35 | margin: 0 0 2px; 36 | color: #ffffff; 37 | font-weight: normal; 38 | } 39 | h2 { 40 | font-size: 0.85em; 41 | color: rgba(255,255,255,0.75); 42 | font-weight: normal; 43 | margin: 0 0 15px; 44 | } 45 | input { 46 | font-size: 1em; 47 | padding: 6px; 48 | font-family: 'Roboto Condensed', sans-serif; 49 | background-color: rgba(255,255,255,0.125); 50 | color: #fff; 51 | border: none; 52 | border-radius: 2px; 53 | &:last-child { 54 | width: 30px; 55 | text-align: center; 56 | } 57 | &.path-input { 58 | width: 400px; 59 | } 60 | } 61 | label { 62 | color: rgba(255,255,255,0.75); 63 | font-size: 1em; 64 | } 65 | } 66 | .content { 67 | position: relative; 68 | min-height: 100%; 69 | 70 | } 71 | .file-list { 72 | width: 200px; 73 | min-height: 100%; 74 | position: absolute; 75 | top: 0; 76 | left: 0; 77 | //background: transparent url('/assets/img/tile-dark.jpg') 0 0 repeat; 78 | //background-size: 20px; 79 | border-right: solid 8px #24292e; 80 | p { 81 | color: #fafbfc; 82 | } 83 | ul { 84 | list-style: none; 85 | margin: 0; 86 | padding: 5px 5px 0; 87 | li { 88 | position: relative; 89 | padding: 5px; 90 | margin: 0 0 5px; 91 | min-height: 29px; 92 | border: solid 1px transparent; 93 | img { 94 | max-width: 100%; 95 | height: auto; 96 | display: block; 97 | } 98 | span { 99 | position: absolute; 100 | bottom: 5px; 101 | left: 5px; 102 | background-color: #333333; 103 | color: #ffffff; 104 | padding: 3px; 105 | font-size: 0.8em; 106 | border-radius: 5px; 107 | opacity: 0; 108 | } 109 | &:hover { 110 | border: dashed 1px #FFFFFF; 111 | span { 112 | opacity: 1; 113 | } 114 | .remove { 115 | opacity: 1; 116 | } 117 | } 118 | } 119 | } 120 | .remove { 121 | position: absolute; 122 | top: 3px; 123 | right: 3px; 124 | width: 24px; 125 | height: 24px; 126 | background: transparent url('/assets/img/ic_clear_black_24px.svg') 0 0 no-repeat; 127 | cursor: pointer; 128 | opacity: 0.5; 129 | filter: invert(1); 130 | &:hover { 131 | transform: scale(1.1); 132 | } 133 | } 134 | } 135 | 136 | .output { 137 | padding: 10px 10px 10px 210px; 138 | b { 139 | font-size: 1.1em; 140 | } 141 | } 142 | 143 | .add-files-btn { 144 | position: absolute; 145 | top: 0; 146 | left: 0; 147 | right: 0; 148 | bottom: 0; 149 | margin: auto; 150 | width: 48px; 151 | height: 48px; 152 | background: transparent url('/assets/img/ic_add_circle_black_48px.svg') center center no-repeat; 153 | cursor: pointer; 154 | display: none; 155 | } 156 | .add-files-text { 157 | position: absolute; 158 | top: 76px; 159 | left: 0; 160 | right: 0; 161 | bottom: 0; 162 | margin: auto; 163 | width: 175px; 164 | height: 20px; 165 | text-align: center; 166 | cursor: pointer; 167 | display: none; 168 | } 169 | 170 | .dropbox { 171 | border: solid 1px #E0E0E0; 172 | //background: transparent url('/assets/img/tile-light.jpg') 0 0 repeat; 173 | //background-size: 20px; 174 | margin: 0 auto; 175 | float: left; 176 | position: relative; 177 | min-width: 100%; 178 | min-height: 512px; 179 | cursor: pointer; 180 | &.is-empty { 181 | .add-files-btn, .add-files-text { 182 | display: block; 183 | } 184 | } 185 | } 186 | 187 | .fileSelect { 188 | display: block; 189 | } 190 | 191 | .clear { 192 | width: 100%; 193 | clear: both; 194 | } 195 | 196 | .result { 197 | padding: 10px 0 0 0; 198 | } 199 | 200 | textarea { 201 | width: 1024px; 202 | font-family: 'Roboto Condensed', sans-serif; 203 | padding: 10px 8px; 204 | background-color: #F5F5F5; 205 | box-shadow: none; 206 | border: solid 1px #E0E0E0; 207 | } 208 | .button { 209 | width: 65px; 210 | height: 65px; 211 | border-radius: 5px; 212 | cursor: pointer; 213 | transition: background-color 300ms ease; 214 | &:hover { 215 | background-color: #00E5FF; 216 | } 217 | span { 218 | display: block; 219 | position: absolute; 220 | left: 0; 221 | bottom: 100%; 222 | width: 100%; 223 | font-size: 0.8em; 224 | color: rgba(255, 255, 255, 0.75); 225 | text-align: center; 226 | padding-bottom: 5px; 227 | } 228 | } 229 | .download { 230 | position: absolute; 231 | bottom: 10px; 232 | right: 10px; 233 | background: rgba(255,255,255,0.125) url('/assets/img/ic_file_download_white_48px.svg') center center no-repeat; 234 | } 235 | .download-info { 236 | font-size: 0.9em; 237 | margin-bottom: 2px; 238 | } 239 | .copy { 240 | position: absolute; 241 | bottom: 10px; 242 | right: 85px; 243 | background: rgba(255,255,255,0.125) url('/assets/img/ic_content_copy_white_48px.svg') center center no-repeat; 244 | } 245 | .github { 246 | position: relative; 247 | left: 10px; 248 | top: 5px; 249 | display: inline-block; 250 | background: transparent url('/assets/img/GitHub-Mark-Light-32px.png') 0 0 no-repeat; 251 | width: 32px; 252 | height: 32px; 253 | } 254 | .dimensions { 255 | font-size: 0.9em; 256 | } 257 | 258 | .dark-checkered { 259 | background-color: #757575; 260 | background-image: 261 | linear-gradient(45deg, #424242 25%, transparent 25%), 262 | linear-gradient(-45deg, #424242 25%, transparent 25%), 263 | linear-gradient(45deg, transparent 75%, #424242 75%), 264 | linear-gradient(-45deg, transparent 75%, #424242 75%); 265 | background-size: 20px 20px; 266 | background-position: 0 0, 0 10px, 10px -10px, -10px 0px; 267 | } 268 | 269 | .light-checkered { 270 | background-color: #f5f5f5; 271 | background-image: 272 | linear-gradient(45deg, #e0e0e0 25%, transparent 25%), 273 | linear-gradient(-45deg, #e0e0e0 25%, transparent 25%), 274 | linear-gradient(45deg, transparent 75%, #e0e0e0 75%), 275 | linear-gradient(-45deg, transparent 75%, #e0e0e0 75%); 276 | background-size: 20px 20px; 277 | background-position: 0 0, 0 10px, 10px -10px, -10px 0px; 278 | } 279 | -------------------------------------------------------------------------------- /assets/styles/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /* Document 4 | ========================================================================== */ 5 | 6 | /** 7 | * 1. Correct the line height in all browsers. 8 | * 2. Prevent adjustments of font size after orientation changes in 9 | * IE on Windows Phone and in iOS. 10 | */ 11 | 12 | html { 13 | line-height: 1.15; /* 1 */ 14 | -ms-text-size-adjust: 100%; /* 2 */ 15 | -webkit-text-size-adjust: 100%; /* 2 */ 16 | } 17 | 18 | /* Sections 19 | ========================================================================== */ 20 | 21 | /** 22 | * Remove the margin in all browsers (opinionated). 23 | */ 24 | 25 | body { 26 | margin: 0; 27 | } 28 | 29 | /** 30 | * Add the correct display in IE 9-. 31 | */ 32 | 33 | article, 34 | aside, 35 | footer, 36 | header, 37 | nav, 38 | section { 39 | display: block; 40 | } 41 | 42 | /** 43 | * Correct the font size and margin on `h1` elements within `section` and 44 | * `article` contexts in Chrome, Firefox, and Safari. 45 | */ 46 | 47 | h1 { 48 | font-size: 2em; 49 | margin: 0.67em 0; 50 | } 51 | 52 | /* Grouping content 53 | ========================================================================== */ 54 | 55 | /** 56 | * Add the correct display in IE 9-. 57 | * 1. Add the correct display in IE. 58 | */ 59 | 60 | figcaption, 61 | figure, 62 | main { /* 1 */ 63 | display: block; 64 | } 65 | 66 | /** 67 | * Add the correct margin in IE 8. 68 | */ 69 | 70 | figure { 71 | margin: 1em 40px; 72 | } 73 | 74 | /** 75 | * 1. Add the correct box sizing in Firefox. 76 | * 2. Show the overflow in Edge and IE. 77 | */ 78 | 79 | hr { 80 | box-sizing: content-box; /* 1 */ 81 | height: 0; /* 1 */ 82 | overflow: visible; /* 2 */ 83 | } 84 | 85 | /** 86 | * 1. Correct the inheritance and scaling of font size in all browsers. 87 | * 2. Correct the odd `em` font sizing in all browsers. 88 | */ 89 | 90 | pre { 91 | font-family: monospace, monospace; /* 1 */ 92 | font-size: 1em; /* 2 */ 93 | } 94 | 95 | /* Text-level semantics 96 | ========================================================================== */ 97 | 98 | /** 99 | * 1. Remove the gray background on active links in IE 10. 100 | * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. 101 | */ 102 | 103 | a { 104 | background-color: transparent; /* 1 */ 105 | -webkit-text-decoration-skip: objects; /* 2 */ 106 | } 107 | 108 | /** 109 | * 1. Remove the bottom border in Chrome 57- and Firefox 39-. 110 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 111 | */ 112 | 113 | abbr[title] { 114 | border-bottom: none; /* 1 */ 115 | text-decoration: underline; /* 2 */ 116 | text-decoration: underline dotted; /* 2 */ 117 | } 118 | 119 | /** 120 | * Prevent the duplicate application of `bolder` by the next rule in Safari 6. 121 | */ 122 | 123 | b, 124 | strong { 125 | font-weight: inherit; 126 | } 127 | 128 | /** 129 | * Add the correct font weight in Chrome, Edge, and Safari. 130 | */ 131 | 132 | b, 133 | strong { 134 | font-weight: bolder; 135 | } 136 | 137 | /** 138 | * 1. Correct the inheritance and scaling of font size in all browsers. 139 | * 2. Correct the odd `em` font sizing in all browsers. 140 | */ 141 | 142 | code, 143 | kbd, 144 | samp { 145 | font-family: monospace, monospace; /* 1 */ 146 | font-size: 1em; /* 2 */ 147 | } 148 | 149 | /** 150 | * Add the correct font style in Android 4.3-. 151 | */ 152 | 153 | dfn { 154 | font-style: italic; 155 | } 156 | 157 | /** 158 | * Add the correct background and color in IE 9-. 159 | */ 160 | 161 | mark { 162 | background-color: #ff0; 163 | color: #000; 164 | } 165 | 166 | /** 167 | * Add the correct font size in all browsers. 168 | */ 169 | 170 | small { 171 | font-size: 80%; 172 | } 173 | 174 | /** 175 | * Prevent `sub` and `sup` elements from affecting the line height in 176 | * all browsers. 177 | */ 178 | 179 | sub, 180 | sup { 181 | font-size: 75%; 182 | line-height: 0; 183 | position: relative; 184 | vertical-align: baseline; 185 | } 186 | 187 | sub { 188 | bottom: -0.25em; 189 | } 190 | 191 | sup { 192 | top: -0.5em; 193 | } 194 | 195 | /* Embedded content 196 | ========================================================================== */ 197 | 198 | /** 199 | * Add the correct display in IE 9-. 200 | */ 201 | 202 | audio, 203 | video { 204 | display: inline-block; 205 | } 206 | 207 | /** 208 | * Add the correct display in iOS 4-7. 209 | */ 210 | 211 | audio:not([controls]) { 212 | display: none; 213 | height: 0; 214 | } 215 | 216 | /** 217 | * Remove the border on images inside links in IE 10-. 218 | */ 219 | 220 | img { 221 | border-style: none; 222 | } 223 | 224 | /** 225 | * Hide the overflow in IE. 226 | */ 227 | 228 | svg:not(:root) { 229 | overflow: hidden; 230 | } 231 | 232 | /* Forms 233 | ========================================================================== */ 234 | 235 | /** 236 | * 1. Change the font styles in all browsers (opinionated). 237 | * 2. Remove the margin in Firefox and Safari. 238 | */ 239 | 240 | button, 241 | input, 242 | optgroup, 243 | select, 244 | textarea { 245 | font-family: sans-serif; /* 1 */ 246 | font-size: 100%; /* 1 */ 247 | line-height: 1.15; /* 1 */ 248 | margin: 0; /* 2 */ 249 | } 250 | 251 | /** 252 | * Show the overflow in IE. 253 | * 1. Show the overflow in Edge. 254 | */ 255 | 256 | button, 257 | input { /* 1 */ 258 | overflow: visible; 259 | } 260 | 261 | /** 262 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 263 | * 1. Remove the inheritance of text transform in Firefox. 264 | */ 265 | 266 | button, 267 | select { /* 1 */ 268 | text-transform: none; 269 | } 270 | 271 | /** 272 | * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` 273 | * controls in Android 4. 274 | * 2. Correct the inability to style clickable types in iOS and Safari. 275 | */ 276 | 277 | button, 278 | html [type="button"], /* 1 */ 279 | [type="reset"], 280 | [type="submit"] { 281 | -webkit-appearance: button; /* 2 */ 282 | } 283 | 284 | /** 285 | * Remove the inner border and padding in Firefox. 286 | */ 287 | 288 | button::-moz-focus-inner, 289 | [type="button"]::-moz-focus-inner, 290 | [type="reset"]::-moz-focus-inner, 291 | [type="submit"]::-moz-focus-inner { 292 | border-style: none; 293 | padding: 0; 294 | } 295 | 296 | /** 297 | * Restore the focus styles unset by the previous rule. 298 | */ 299 | 300 | button:-moz-focusring, 301 | [type="button"]:-moz-focusring, 302 | [type="reset"]:-moz-focusring, 303 | [type="submit"]:-moz-focusring { 304 | outline: 1px dotted ButtonText; 305 | } 306 | 307 | /** 308 | * Correct the padding in Firefox. 309 | */ 310 | 311 | fieldset { 312 | padding: 0.35em 0.75em 0.625em; 313 | } 314 | 315 | /** 316 | * 1. Correct the text wrapping in Edge and IE. 317 | * 2. Correct the color inheritance from `fieldset` elements in IE. 318 | * 3. Remove the padding so developers are not caught out when they zero out 319 | * `fieldset` elements in all browsers. 320 | */ 321 | 322 | legend { 323 | box-sizing: border-box; /* 1 */ 324 | color: inherit; /* 2 */ 325 | display: table; /* 1 */ 326 | max-width: 100%; /* 1 */ 327 | padding: 0; /* 3 */ 328 | white-space: normal; /* 1 */ 329 | } 330 | 331 | /** 332 | * 1. Add the correct display in IE 9-. 333 | * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera. 334 | */ 335 | 336 | progress { 337 | display: inline-block; /* 1 */ 338 | vertical-align: baseline; /* 2 */ 339 | } 340 | 341 | /** 342 | * Remove the default vertical scrollbar in IE. 343 | */ 344 | 345 | textarea { 346 | overflow: auto; 347 | } 348 | 349 | /** 350 | * 1. Add the correct box sizing in IE 10-. 351 | * 2. Remove the padding in IE 10-. 352 | */ 353 | 354 | [type="checkbox"], 355 | [type="radio"] { 356 | box-sizing: border-box; /* 1 */ 357 | padding: 0; /* 2 */ 358 | } 359 | 360 | /** 361 | * Correct the cursor style of increment and decrement buttons in Chrome. 362 | */ 363 | 364 | [type="number"]::-webkit-inner-spin-button, 365 | [type="number"]::-webkit-outer-spin-button { 366 | height: auto; 367 | } 368 | 369 | /** 370 | * 1. Correct the odd appearance in Chrome and Safari. 371 | * 2. Correct the outline style in Safari. 372 | */ 373 | 374 | [type="search"] { 375 | -webkit-appearance: textfield; /* 1 */ 376 | outline-offset: -2px; /* 2 */ 377 | } 378 | 379 | /** 380 | * Remove the inner padding and cancel buttons in Chrome and Safari on macOS. 381 | */ 382 | 383 | [type="search"]::-webkit-search-cancel-button, 384 | [type="search"]::-webkit-search-decoration { 385 | -webkit-appearance: none; 386 | } 387 | 388 | /** 389 | * 1. Correct the inability to style clickable types in iOS and Safari. 390 | * 2. Change font properties to `inherit` in Safari. 391 | */ 392 | 393 | ::-webkit-file-upload-button { 394 | -webkit-appearance: button; /* 1 */ 395 | font: inherit; /* 2 */ 396 | } 397 | 398 | /* Interactive 399 | ========================================================================== */ 400 | 401 | /* 402 | * Add the correct display in IE 9-. 403 | * 1. Add the correct display in Edge, IE, and Firefox. 404 | */ 405 | 406 | details, /* 1 */ 407 | menu { 408 | display: block; 409 | } 410 | 411 | /* 412 | * Add the correct display in all browsers. 413 | */ 414 | 415 | summary { 416 | display: list-item; 417 | } 418 | 419 | /* Scripting 420 | ========================================================================== */ 421 | 422 | /** 423 | * Add the correct display in IE 9-. 424 | */ 425 | 426 | canvas { 427 | display: inline-block; 428 | } 429 | 430 | /** 431 | * Add the correct display in IE. 432 | */ 433 | 434 | template { 435 | display: none; 436 | } 437 | 438 | /* Hidden 439 | ========================================================================== */ 440 | 441 | /** 442 | * Add the correct display in IE 10-. 443 | */ 444 | 445 | [hidden] { 446 | display: none; 447 | } -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eivers88/responsive-css-sprite-generator/f463d5509529407705c48470e6f5612068742396/favicon.ico -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** GULP DEPENDENCIES 4 | ========================================================================== */ 5 | 6 | let gulp = require('gulp'); 7 | let sass = require('gulp-ruby-sass'); 8 | let autoprefixer = require('gulp-autoprefixer'); 9 | let sourcemaps = require('gulp-sourcemaps'); 10 | let browserSync = require('browser-sync').create(); 11 | let usemin = require('gulp-usemin'); 12 | let uglify = require('gulp-uglify'); 13 | let htmlmin = require('gulp-htmlmin'); 14 | let cleanCss = require('gulp-clean-css'); 15 | let rev = require('gulp-rev'); 16 | 17 | /** DEFAULT 18 | ========================================================================== */ 19 | 20 | gulp.task('default', ['usemin']); 21 | 22 | /** STYLESHEETS 23 | ========================================================================== */ 24 | 25 | gulp.task('clear:cache', function () { 26 | sass.clearCache(); 27 | }); 28 | 29 | gulp.task('clean:sass', ['clear:cache'], sassTask); 30 | 31 | gulp.task('sass', sassTask); 32 | 33 | function sassTask() { 34 | return sass('./assets/styles/**/*.scss', {sourcemap: true}) 35 | .on('error', sass.logError) 36 | .pipe(autoprefixer({ 37 | browsers: ['last 3 versions'], 38 | cascade: false 39 | })) 40 | .pipe(sourcemaps.write('.')) 41 | .pipe(gulp.dest('./assets/styles')) 42 | .pipe(browserSync.stream({match: '**/*.css'})); 43 | } 44 | 45 | /** USEMIN 46 | ========================================================================== */ 47 | 48 | gulp.task('usemin', function () { 49 | return gulp.src('./src/index.html') 50 | .pipe(usemin({ 51 | html: [htmlmin({collapseWhitespace: true})], 52 | jsAttributes: { 53 | async: true 54 | }, 55 | js: [uglify(), rev()], 56 | inlinecss: [cleanCss(), 'concat'] 57 | })) 58 | .pipe(gulp.dest('./')); 59 | }); 60 | 61 | /** WATCH 62 | ========================================================================== */ 63 | 64 | gulp.task('watch', ['clean:sass'], function () { 65 | 66 | browserSync.init({ 67 | server: { 68 | baseDir: './', 69 | index: './src/index.html' 70 | } 71 | }); 72 | 73 | gulp.watch('./assets/js/bundle.js').on('change', browserSync.reload); 74 | gulp.watch('./src/*.html').on('change', browserSync.reload); 75 | gulp.watch('./assets/styles/**/*.scss', ['sass']); 76 | 77 | }); 78 | 79 | 80 | /** BUILD SERVER 81 | ========================================================================== */ 82 | 83 | gulp.task('build-server', ['clean:sass'], function () { 84 | 85 | browserSync.init({ 86 | server: { 87 | baseDir: './', 88 | index: './index.html' 89 | }, 90 | port: 8080 91 | }); 92 | 93 | }); 94 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | Responsive CSS Sprite Generator
Generated Sprite 
If the download button does not work, right click the sprite and select "Save Image As..."
Add some images!
Generated CSS
-------------------------------------------------------------------------------- /manifest.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Responsive CSS Sprite Generator", 3 | "short_name": "rCSSsg", 4 | "description": "A tool for generating responsive CSS sprites." 5 | } 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "responsive-css-sprite-generator", 3 | "version": "2.0.0", 4 | "description": "http://responsive-css.us/", 5 | "main": "gulpfile.js", 6 | "scripts": { 7 | "test": "eslint ./assets/js/app/**/*.js", 8 | "build": "eslint ./assets/js/app/**/*.js && gulp", 9 | "build-server": "gulp build-server", 10 | "dev": "concurrently \"gulp watch\" \"webpack --watch\"" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/eivers88/responsive-css-sprite-generator.git" 15 | }, 16 | "author": "Aaron Eivers", 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/eivers88/responsive-css-sprite-generator/issues" 20 | }, 21 | "homepage": "https://github.com/eivers88/responsive-css-sprite-generator#readme", 22 | "devDependencies": { 23 | "babel-core": "^6.26.0", 24 | "babel-loader": "^7.1.2", 25 | "babel-preset-es2015": "^6.24.1", 26 | "browser-sync": "^2.19.0", 27 | "concurrently": "^3.5.1", 28 | "eslint": "^4.13.1", 29 | "expose-loader": "^0.7.4", 30 | "gulp": "^3.9.1", 31 | "gulp-autoprefixer": "^4.0.0", 32 | "gulp-clean-css": "^3.9.0", 33 | "gulp-htmlmin": "^3.0.0", 34 | "gulp-rev": "^8.1.0", 35 | "gulp-ruby-sass": "^2.1.1", 36 | "gulp-sourcemaps": "^2.6.1", 37 | "gulp-uglify": "^3.0.0", 38 | "gulp-usemin": "^0.3.28", 39 | "webpack": "^3.10.0" 40 | }, 41 | "dependencies": { 42 | "clipboard": "^1.7.1", 43 | "webfontloader": "^1.6.28" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Responsive CSS Sprite Generator 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 39 | 40 |
41 | 42 | 43 | 44 |
45 |
46 |
47 |
48 |
Generated Sprite 
49 |
If the download button does not work, right click the sprite and select "Save Image 50 | As..." 51 |
52 |
53 | 54 |
55 |
Add some images!
56 |
57 |
58 |
59 |
Generated CSS
60 | 61 |
62 |
63 |
64 | 65 |
66 | 67 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | 2 | var config = { 3 | context: __dirname + "/assets", 4 | entry: "./js/index.js", 5 | devtool: "cheap-source-map", 6 | // devtool: "cheap-eval-source-map", 7 | // devtool: "cheap-module-eval-source-map", 8 | output: { 9 | filename: "bundle.js", 10 | path: __dirname + "/assets/js" 11 | }, 12 | module: { 13 | rules: [ 14 | { 15 | test: /\.js$/, 16 | exclude: /node_modules/, 17 | use: [ 18 | { 19 | loader: 'babel-loader', 20 | options: { 21 | presets: ['es2015'] 22 | } 23 | } 24 | ] 25 | } 26 | ] 27 | } 28 | }; 29 | 30 | module.exports = config; --------------------------------------------------------------------------------