├── .gitignore ├── www ├── favicon.ico ├── images │ ├── .DS_Store │ ├── icons │ │ ├── favicon.ico │ │ ├── apple-icon.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.png │ │ ├── ms-icon-70x70.png │ │ ├── apple-icon-57x57.png │ │ ├── apple-icon-60x60.png │ │ ├── apple-icon-72x72.png │ │ ├── apple-icon-76x76.png │ │ ├── ms-icon-144x144.png │ │ ├── ms-icon-150x150.png │ │ ├── ms-icon-310x310.png │ │ ├── android-icon-36x36.png │ │ ├── android-icon-48x48.png │ │ ├── android-icon-72x72.png │ │ ├── android-icon-96x96.png │ │ ├── apple-icon-114x114.png │ │ ├── apple-icon-120x120.png │ │ ├── apple-icon-144x144.png │ │ ├── apple-icon-152x152.png │ │ ├── apple-icon-180x180.png │ │ ├── android-icon-144x144.png │ │ ├── android-icon-192x192.png │ │ ├── apple-icon-precomposed.png │ │ ├── browserconfig.xml │ │ └── manifest.json │ └── placeholder_pics.jpg ├── font │ ├── placeholder.eot │ ├── placeholder.ttf │ ├── placeholder.woff │ ├── placeholder.woff2 │ └── placeholder.svg ├── css │ ├── placeholder-codes.css │ ├── placeholder.css │ ├── site.css │ └── placeholder-embedded.css ├── js │ ├── site.js │ └── jscolor.min.js └── index.html ├── Dockerfile ├── placeholder_test.go ├── README.md ├── package.json ├── Gulpfile.js └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | svg-placeholder 2 | node_modules 3 | www-dist 4 | .DS_Store 5 | www/js/all.js -------------------------------------------------------------------------------- /www/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/favicon.ico -------------------------------------------------------------------------------- /www/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/.DS_Store -------------------------------------------------------------------------------- /www/font/placeholder.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/font/placeholder.eot -------------------------------------------------------------------------------- /www/font/placeholder.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/font/placeholder.ttf -------------------------------------------------------------------------------- /www/font/placeholder.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/font/placeholder.woff -------------------------------------------------------------------------------- /www/font/placeholder.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/font/placeholder.woff2 -------------------------------------------------------------------------------- /www/images/icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/icons/favicon.ico -------------------------------------------------------------------------------- /www/images/icons/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/icons/apple-icon.png -------------------------------------------------------------------------------- /www/images/placeholder_pics.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/placeholder_pics.jpg -------------------------------------------------------------------------------- /www/images/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/icons/favicon-16x16.png -------------------------------------------------------------------------------- /www/images/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/icons/favicon-32x32.png -------------------------------------------------------------------------------- /www/images/icons/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/icons/favicon-96x96.png -------------------------------------------------------------------------------- /www/images/icons/ms-icon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/icons/ms-icon-70x70.png -------------------------------------------------------------------------------- /www/images/icons/apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/icons/apple-icon-57x57.png -------------------------------------------------------------------------------- /www/images/icons/apple-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/icons/apple-icon-60x60.png -------------------------------------------------------------------------------- /www/images/icons/apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/icons/apple-icon-72x72.png -------------------------------------------------------------------------------- /www/images/icons/apple-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/icons/apple-icon-76x76.png -------------------------------------------------------------------------------- /www/images/icons/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/icons/ms-icon-144x144.png -------------------------------------------------------------------------------- /www/images/icons/ms-icon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/icons/ms-icon-150x150.png -------------------------------------------------------------------------------- /www/images/icons/ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/icons/ms-icon-310x310.png -------------------------------------------------------------------------------- /www/images/icons/android-icon-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/icons/android-icon-36x36.png -------------------------------------------------------------------------------- /www/images/icons/android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/icons/android-icon-48x48.png -------------------------------------------------------------------------------- /www/images/icons/android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/icons/android-icon-72x72.png -------------------------------------------------------------------------------- /www/images/icons/android-icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/icons/android-icon-96x96.png -------------------------------------------------------------------------------- /www/images/icons/apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/icons/apple-icon-114x114.png -------------------------------------------------------------------------------- /www/images/icons/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/icons/apple-icon-120x120.png -------------------------------------------------------------------------------- /www/images/icons/apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/icons/apple-icon-144x144.png -------------------------------------------------------------------------------- /www/images/icons/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/icons/apple-icon-152x152.png -------------------------------------------------------------------------------- /www/images/icons/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/icons/apple-icon-180x180.png -------------------------------------------------------------------------------- /www/images/icons/android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/icons/android-icon-144x144.png -------------------------------------------------------------------------------- /www/images/icons/android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/icons/android-icon-192x192.png -------------------------------------------------------------------------------- /www/images/icons/apple-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezzer/svg-placeholder/HEAD/www/images/icons/apple-icon-precomposed.png -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM busybox 2 | ADD www-dist/ /www/ 3 | ADD svg-placeholder /app/svg-placeholder 4 | ENV PORT 5000 5 | EXPOSE 5000 6 | ENTRYPOINT ["/app/svg-placeholder"] 7 | -------------------------------------------------------------------------------- /www/css/placeholder-codes.css: -------------------------------------------------------------------------------- 1 | 2 | .icon-photo:before { content: '\e800'; } /* '' */ 3 | .icon-twitter:before { content: '\e801'; } /* '' */ 4 | .icon-github:before { content: '\e802'; } /* '' */ 5 | .icon-linkedin:before { content: '\e803'; } /* '' */ -------------------------------------------------------------------------------- /www/images/icons/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | #ffffff -------------------------------------------------------------------------------- /placeholder_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "testing" 7 | "net/http" 8 | "net/http/httptest" 9 | ) 10 | 11 | func TestSvg(t *testing.T) { 12 | req, err := http.NewRequest("GET", "http://example.com/svg/100/100", nil) 13 | if err != nil { 14 | log.Fatal(err) 15 | } 16 | w := httptest.NewRecorder() 17 | svg(w, req) 18 | 19 | fmt.Printf("%d - %s", w.Code, w.Body.String()) 20 | } 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SVG Placeholder 2 | 3 | ## About 4 | Small service writen in Go to request svg placeholder images. The Go app is statically compiled and added to an empty docker container for running. 5 | 6 | ## How to build 7 | The file `build.sh` builds the go app as a static binary, then adds it to a scratch docker container along with any app resources in `www-dist/`. 8 | 9 | The build steps are: 10 | 11 | 1. Build the svg-placeholder Go binary 12 | 2. Minify static resources and output to `www-dist/` 13 | 3. Build the docker container (tagged as `bezzer/svg-placeholder`) 14 | 15 | ## License 16 | MIT 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svg-placeholder", 3 | "version": "1.0.0", 4 | "description": "svg-placeholder front end resources scripts", 5 | "main": "Gulpfile.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/bezzer/svg-placeholder.git" 12 | }, 13 | "author": "Chris Berry", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/bezzer/svg-placeholder/issues" 17 | }, 18 | "homepage": "https://github.com/bezzer/svg-placeholder#readme", 19 | "devDependencies": { 20 | "gulp": "^3.9.1", 21 | "gulp-autoprefixer": "^3.1.0", 22 | "gulp-clean-css": "^2.0.7", 23 | "gulp-concat": "^2.6.0", 24 | "gulp-livereload": "^3.8.1", 25 | "gulp-minify": "0.0.11", 26 | "gulp-uglify": "^1.5.3" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /www/images/icons/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "App", 3 | "icons": [ 4 | { 5 | "src": "\/android-icon-36x36.png", 6 | "sizes": "36x36", 7 | "type": "image\/png", 8 | "density": "0.75" 9 | }, 10 | { 11 | "src": "\/android-icon-48x48.png", 12 | "sizes": "48x48", 13 | "type": "image\/png", 14 | "density": "1.0" 15 | }, 16 | { 17 | "src": "\/android-icon-72x72.png", 18 | "sizes": "72x72", 19 | "type": "image\/png", 20 | "density": "1.5" 21 | }, 22 | { 23 | "src": "\/android-icon-96x96.png", 24 | "sizes": "96x96", 25 | "type": "image\/png", 26 | "density": "2.0" 27 | }, 28 | { 29 | "src": "\/android-icon-144x144.png", 30 | "sizes": "144x144", 31 | "type": "image\/png", 32 | "density": "3.0" 33 | }, 34 | { 35 | "src": "\/android-icon-192x192.png", 36 | "sizes": "192x192", 37 | "type": "image\/png", 38 | "density": "4.0" 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /Gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require("gulp"); 2 | 3 | var minifyCSS = require("gulp-clean-css"); 4 | var uglify = require("gulp-uglify"); 5 | var autoprefixer = require('gulp-autoprefixer'); 6 | var concat = require("gulp-concat"); 7 | var livereload = require("gulp-livereload"); 8 | 9 | gulp.task('minify-css', function () { 10 | return gulp.src('www/css/*.css') 11 | .pipe(autoprefixer({ 12 | browsers: ['last 4 versions'], 13 | cascade: false, 14 | flexbox: true 15 | })) 16 | .pipe(minifyCSS()) 17 | .pipe(gulp.dest('www-dist/css')); 18 | }); 19 | 20 | gulp.task('minify-js', function () { 21 | return gulp.src(['www/js/*.js', '!www/js/all.js']) 22 | .pipe(concat('all.js')) 23 | .pipe(uglify()) 24 | .pipe(gulp.dest('www-dist/js')); 25 | }); 26 | 27 | gulp.task('js-dev', function () { 28 | return gulp.src(['www/js/*.js', '!www/js/all.js']) 29 | .pipe(concat('all.js')) 30 | .pipe(gulp.dest('www/js')) 31 | .pipe(livereload()); 32 | }); 33 | 34 | gulp.task('copy-resources', function () { 35 | return gulp.src([ 36 | 'www/**', 37 | '!**/*.js', 38 | '!**/*.css']) 39 | .pipe(gulp.dest('www-dist')); 40 | }); 41 | 42 | gulp.task('watch', function () { 43 | livereload.listen(); 44 | gulp.watch('www/js/*.js', ['js-dev']); 45 | }); 46 | 47 | gulp.task('default', ['minify-js', 'minify-css', 'copy-resources']) -------------------------------------------------------------------------------- /www/css/placeholder.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'placeholder'; 3 | src: url('../font/placeholder.eot?43745002'); 4 | src: url('../font/placeholder.eot?43745002#iefix') format('embedded-opentype'), 5 | url('../font/placeholder.woff2?43745002') format('woff2'), 6 | url('../font/placeholder.woff?43745002') format('woff'), 7 | url('../font/placeholder.ttf?43745002') format('truetype'), 8 | url('../font/placeholder.svg?43745002#placeholder') format('svg'); 9 | font-weight: normal; 10 | font-style: normal; 11 | } 12 | /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ 13 | /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ 14 | /* 15 | @media screen and (-webkit-min-device-pixel-ratio:0) { 16 | @font-face { 17 | font-family: 'placeholder'; 18 | src: url('../font/placeholder.svg?43745002#placeholder') format('svg'); 19 | } 20 | } 21 | */ 22 | 23 | [class^="icon-"]:before, [class*=" icon-"]:before { 24 | font-family: "placeholder"; 25 | font-style: normal; 26 | font-weight: normal; 27 | speak: none; 28 | 29 | display: inline-block; 30 | text-decoration: inherit; 31 | width: 1em; 32 | margin-right: .2em; 33 | text-align: center; 34 | /* opacity: .8; */ 35 | 36 | /* For safety - reset parent styles, that can break glyph codes*/ 37 | font-variant: normal; 38 | text-transform: none; 39 | 40 | /* fix buttons height, for twitter bootstrap */ 41 | line-height: 1em; 42 | 43 | /* Animation center compensation - margins should be symmetric */ 44 | /* remove if not needed */ 45 | margin-left: .2em; 46 | 47 | /* you can be more comfortable with increased icons size */ 48 | /* font-size: 120%; */ 49 | 50 | /* Font smoothing. That was taken from TWBS */ 51 | -webkit-font-smoothing: antialiased; 52 | -moz-osx-font-smoothing: grayscale; 53 | 54 | /* Uncomment for 3D effect */ 55 | /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ 56 | } 57 | 58 | .icon-photo:before { content: '\e800'; } /* '' */ 59 | .icon-twitter:before { content: '\e801'; } /* '' */ 60 | .icon-github:before { content: '\e802'; } /* '' */ 61 | .icon-linkedin:before { content: '\e803'; } /* '' */ -------------------------------------------------------------------------------- /www/font/placeholder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Copyright (C) 2016 by original authors @ fontello.com 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /www/js/site.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | var data = { 3 | width: "300", 4 | height: "", 5 | backgroundStart: "", 6 | backgroundEnd: "", 7 | foregroundText: "", 8 | foregroundBorder: "", 9 | message: "" 10 | }; 11 | var defaultBackground = "DEDEDE"; 12 | var defaultForeground = "555555"; 13 | var location = window.location; 14 | var baseURL = location.protocol + "//" + window.location.hostname + (location.port? ":" + location.port : "") + "/svg/"; 15 | 16 | // Connect elemensts based on ID 17 | var connectElement = function(elemId) { 18 | var element = document.querySelector("#" + elemId); 19 | if (element.type === "text" || element.type === "number") { 20 | element.addEventListener("click", function (event) { 21 | event.target.setSelectionRange(0, event.target.value.length); 22 | }); 23 | } 24 | 25 | var updateValue = function (value) { 26 | var current = data[elemId]; 27 | if (current !== value) { 28 | data[elemId] = value; 29 | render(); 30 | } 31 | } 32 | 33 | // Update on input change 34 | element.addEventListener("change", function (event) { 35 | updateValue(event.target.value); 36 | }); 37 | 38 | element.addEventListener("input", function (event) { 39 | updateValue(event.target.value); 40 | }); 41 | 42 | var clearButton = document.querySelector("." + elemId + " .clear"); 43 | if (clearButton) { 44 | clearButton.addEventListener('click', function (e) { 45 | e.preventDefault(); 46 | e.stopPropagation(); 47 | element.value = ""; 48 | data[elemId] = ""; 49 | var colorPicker = document.querySelector('.' + elemId); 50 | if (colorPicker) { 51 | colorPicker.style.backgroundColor = ""; 52 | colorPicker.style.color = ""; 53 | } 54 | render(); 55 | }); 56 | } 57 | 58 | // Initial update 59 | updateValue(element.value); 60 | }; 61 | 62 | var init = function () { 63 | connectElement("width"); 64 | connectElement("height"); 65 | connectElement("backgroundStart"); 66 | connectElement("backgroundEnd"); 67 | connectElement("foregroundText"); 68 | connectElement("foregroundBorder"); 69 | connectElement("message"); 70 | 71 | // Auto update copyright year 72 | [].slice.call(document.querySelectorAll(".copyright-year")).forEach(function (ele) { 73 | ele.innerText = (new Date()).getFullYear(); 74 | }); 75 | // initial render 76 | render(); 77 | }; 78 | 79 | var render = function () { 80 | var renderEl = document.querySelector("#render"); 81 | var colors = ""; 82 | var urlMessage = ""; 83 | var dimensions = "" + data.width; 84 | var url; 85 | 86 | if (data.height) { 87 | dimensions += "x" + data.height; 88 | } 89 | 90 | if (data.backgroundStart) { 91 | colors = "/" + data.backgroundStart.replace("#", ""); 92 | if (data.backgroundEnd) { 93 | colors += "-" + data.backgroundEnd.replace("#", ""); 94 | } 95 | } 96 | 97 | if (data.foregroundText) { 98 | colors += (data.backgroundStart ? "/" : "/" + defaultBackground + "/") + data.foregroundText.replace("#", ""); 99 | if (data.foregroundBorder) { 100 | colors += "-" + data.foregroundBorder.replace("#",""); 101 | } 102 | } 103 | 104 | if (data.message) { 105 | if (!colors) { 106 | colors = "/" + defaultBackground + "/" + defaultForeground; 107 | } 108 | 109 | urlMessage = "/" + encodeURIComponent(data.message); 110 | } 111 | 112 | // Build the URL 113 | url = baseURL + dimensions + colors + urlMessage; 114 | 115 | renderEl.innerHTML = "
" + 116 | "
Placeholder " + dimensions +"
"; 117 | }; 118 | 119 | init(); 120 | })(); -------------------------------------------------------------------------------- /www/css/site.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | font-family: "Source Sans Pro", sans-serif; 7 | font-size: 18px; 8 | color: #333; 9 | line-height: 1.5; 10 | margin: 0; 11 | } 12 | h1 { 13 | font-family: "Amatic SC", sans-serif; 14 | font-weight: 700; 15 | font-size: 3rem; 16 | font-size: 14vmin; 17 | text-align: center; 18 | text-shadow: 5px 5px 5px #7B1E7A; 19 | } 20 | strong { 21 | font-weight: 700; 22 | } 23 | h2 { 24 | font-family: "Amatic SC", sans-serif; 25 | text-align: center; 26 | font-size: 2rem; 27 | font-weight: 700; 28 | margin-top: 0; 29 | } 30 | .pics-inputs { 31 | flex: 1 1 200px; 32 | display: flex; 33 | flex-direction: column; 34 | align-items: stretch; 35 | min-width: 200px; 36 | } 37 | .pics-input { 38 | width: auto; 39 | margin-bottom: 12px; 40 | } 41 | .pics-input label { 42 | display: block; 43 | } 44 | .pics-input input, .pics-input button { 45 | width: 100%; 46 | height: 3rem; 47 | padding: 0.5rem; 48 | font-size: 1.2rem; 49 | border: none; 50 | color: #333; 51 | border: 1px solid #ddd; 52 | box-shadow: 0 3px 6px rgba(0,0,0,0.1); 53 | } 54 | .pics-input.color button { 55 | width: 50%; 56 | text-align: left; 57 | position: relative; 58 | background: #fff; 59 | } 60 | .pics-input.color button .clear { 61 | position: absolute; 62 | right: 0.2rem; 63 | top: 0.2rem; 64 | bottom: 0; 65 | text-align: center; 66 | line-height: 2.2rem; 67 | color: #777; 68 | background: rgba(255,255,255,0.3); 69 | font-size: 1.8rem; 70 | border: 1px solid rgba(100,100,100,0.3); 71 | border-radius: 2rem; 72 | width: 2.5rem; 73 | height: 2.5rem; 74 | } 75 | .pics-input .optional { 76 | display: block; 77 | font-size: 12px; 78 | color: #555; 79 | opacity: 0.9; 80 | } 81 | .pics-url { 82 | border: 3px solid rgba(123, 31, 122,0.5); 83 | background: rgba(255,255,255,0.3); 84 | box-shadow: 0 3px 6px rgba(0,0,0,0.1); 85 | padding: 0.5rem; 86 | text-align: center; 87 | font-size: 1.2rem; 88 | height: 3rem; 89 | font-weight: normal; 90 | color: #7B1E7A; 91 | margin: 12px 0; 92 | width: 100%; 93 | } 94 | .pics-preview { 95 | margin: 10px; 96 | min-height: 200px; 97 | text-align: center; 98 | } 99 | .pics-info { 100 | font-weight: 200; 101 | } 102 | .pics-preview img { 103 | max-width: 100%; 104 | } 105 | .pics-display { 106 | flex:2 1 300px; 107 | padding: 15px 0; 108 | min-width: 300px; 109 | overflow-x: scroll; 110 | } 111 | .pics-container { 112 | max-width: 750px; 113 | margin: 0 auto; 114 | padding: 0 10px; 115 | } 116 | /* blocks */ 117 | .pics-header { 118 | background-color: #F9564F; 119 | background-image: linear-gradient(0deg, #7B1E7A, #F9564F); 120 | color: #f5f5f5; 121 | padding: 30px 0; 122 | } 123 | 124 | .pics-description { 125 | background-color: #ff5a5f; 126 | color: #f5f5f5; 127 | padding: 15px 0; 128 | font-weight: 200; 129 | } 130 | .pics-try { 131 | background-color: #f5f5f5; 132 | color: #333; 133 | padding: 30px 0; 134 | } 135 | .pics-try-form { 136 | display: flex; 137 | flex-wrap: wrap; 138 | } 139 | .pics-footer { 140 | background-color: #0C0A3E; 141 | color: #f5f5f5; 142 | padding: 20px 0; 143 | } 144 | .pics-social, .pics-disclaimer { 145 | width: 49%; 146 | min-width: 200px; 147 | display:inline-block; 148 | } 149 | .pics-social-list { 150 | list-style: none; 151 | float:right; 152 | margin: 0; 153 | padding: 0; 154 | } 155 | .pics-social-list li { 156 | margin: 5px 0; 157 | font-weight: 200; 158 | } 159 | .pics-footer a { 160 | color: #f5f5f5; 161 | text-decoration: none; 162 | } 163 | .pics-footer a:hover { 164 | color: #ff5a5f; 165 | } 166 | .pics-about { 167 | padding: 30px 10px; 168 | background: #7B1E7A; 169 | color: #f5f5f5; 170 | font-weight: 200; 171 | } 172 | .pics-about li { 173 | margin-bottom: 0.8rem; 174 | } 175 | .pics-disclaimer { 176 | font-weight: 200; 177 | font-size: 1rem; 178 | color: #CCC; 179 | } 180 | .code { 181 | color: #333; 182 | background-color: rgba(255,255,255,0.9); 183 | padding: 0.5rem; 184 | border-radius: 2px; 185 | display: block; 186 | width: auto; 187 | } 188 | .code .highlight { 189 | color: #7B1E7A; 190 | font-weight: bold; 191 | } 192 | .code pre { 193 | margin: 0; 194 | } 195 | 196 | @media only screen and (max-width:480px) { 197 | .pics-social, .pics-disclaimer { 198 | width: auto; 199 | min-width: 200px; 200 | display:block; 201 | } 202 | .pics-social-list { 203 | float: none; 204 | } 205 | } -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "strconv" 7 | "strings" 8 | "regexp" 9 | "html/template" 10 | "net/http" 11 | "github.com/NYTimes/gziphandler" 12 | ) 13 | 14 | // Placeholder Holds the values for building an SVG 15 | type Placeholder struct { 16 | Width int 17 | Height int 18 | BorderWidth int 19 | BorderHeight int 20 | StrokeWidth int 21 | Fill string 22 | FillEnd string 23 | StrokeColor string 24 | TextColor string 25 | Message string 26 | ShowText bool 27 | } 28 | 29 | // SVG placeholder template (string formated to remove newlines and spaces) 30 | const svgTemplate = "" + 31 | "{{if .FillEnd}}" + 32 | ""+ 33 | "" + 34 | "" + 35 | "" + 36 | "{{end}}" + 37 | "" + 38 | ""+ 39 | "{{if .ShowText}}" + 40 | "{{if .Message}}" + 41 | "{{.Message}}" + 42 | "{{else}}" + 43 | "{{.Width}}×{{.Height}}" + 44 | "{{end}}" + 45 | "{{end}}" + 46 | "" + 47 | "" 48 | 49 | // Default strokewidth 50 | const strokeWidth = 2 51 | 52 | // Pre-parse the template 53 | var templates = template.Must(template.New("svg").Parse(svgTemplate)) 54 | 55 | // Patern matcher for SVG URLs 56 | var svgPatern = regexp.MustCompile(`\/(\d+)(?:x(\d+))?(?:\/([\da-f]{6}|[\da-f]{3})(?:-([\da-f]{6}|[\da-f]{3}))?)?(?:\/([\da-f]{6}|[\da-f]{3})(?:-([\da-f]{6}|[\da-f]{3}))?(?:\/(.+))?)?`) 57 | 58 | // Handler for URL paths /svg/WIDTH/HEIGHT/[FILL/STROKE] 59 | func svg(w http.ResponseWriter, r *http.Request) { 60 | var showText bool 61 | var width, height int 62 | var fill, fillEnd, strokeColor, textColor, message string 63 | 64 | // Lowercase the path to simpify the regex 65 | path := strings.ToLower(r.URL.Path) 66 | 67 | fill = "DEDEDE" 68 | textColor = "555555" 69 | strokeColor = "555555" 70 | 71 | if svgPatern.MatchString(path) { 72 | // Output SVG 73 | matches := svgPatern.FindStringSubmatch(path) 74 | 75 | // Width must always be defined 76 | width, _ = strconv.Atoi(matches[1]) 77 | // Height defaults to width (square) if not defined 78 | height = width 79 | if len(matches[2]) > 0 { 80 | height, _ = strconv.Atoi(matches[2]) 81 | } 82 | // Determine whether to show text based on width/height 83 | showText = width >= 75 && height >= 40 84 | // Fill colour 85 | if len(matches[3]) > 0 { 86 | fill = matches[3] 87 | } 88 | // Fill end colour (for gradients) 89 | if len(matches[4]) > 0 { 90 | fillEnd = matches[4] 91 | } 92 | // Stroke colour 93 | if len(matches[5]) > 0 { 94 | textColor = matches[5] 95 | } 96 | // Border color 97 | if len(matches[6]) > 0 { 98 | strokeColor = matches[6] 99 | } else { 100 | strokeColor = textColor; 101 | } 102 | // Text 103 | if len(matches[7]) > 0 { 104 | message = matches[7] 105 | } 106 | } else { 107 | // Show error image 108 | width = 300 109 | height = 100 110 | message = "Unsupported" 111 | } 112 | 113 | values := &Placeholder{ 114 | Height: height, 115 | Width: width, 116 | Fill: fill, 117 | FillEnd: fillEnd, 118 | StrokeColor: strokeColor, 119 | TextColor: textColor, 120 | StrokeWidth: strokeWidth, 121 | Message: message, 122 | ShowText: showText, 123 | BorderWidth: width - strokeWidth * 2, 124 | BorderHeight: height - strokeWidth * 2} 125 | 126 | // Set the content type to image/svg 127 | w.Header().Set("Content-Type", "image/svg+xml") 128 | w.Header().Set("Cache-Control", "max-age=31536000") 129 | w.Header().Set("Content-Disposition", fmt.Sprintf("inline; filename=%dx%d.svg", width, height)) 130 | // Construct the output 131 | rendererr := templates.ExecuteTemplate(w, "svg", values) 132 | 133 | if rendererr != nil { 134 | log.Printf("Error rendering template: %v", rendererr) 135 | } else { 136 | log.Printf("SVG Placeholder of width %d, height %d, fill %s and stroke %s generated. Message: %s", width, height, fill, strokeColor, message) 137 | } 138 | } 139 | 140 | func main() { 141 | fileHandler := http.FileServer(http.Dir("www")) 142 | http.Handle("/", gziphandler.GzipHandler(fileHandler)) 143 | http.Handle("/svg/", gziphandler.GzipHandler(http.StripPrefix("/svg", http.HandlerFunc(svg)))) 144 | http.ListenAndServe(":5000", nil) 145 | } 146 | -------------------------------------------------------------------------------- /www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Placeholder pics 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 31 | 32 | 33 |
34 |

placeholder.pics

35 |
36 |

37 | The lightest way to include placeholder pictures in your designs. 38 | All images are lovingly served up as sub-kilobyte, fully optimized 39 | Scalable Vector Graphics (SVG) in any size or color you need. 40 | You can even add a short label to keep track of what goes 41 | where in your designs and mockups. 42 |

43 |
44 |
45 |
46 |

Try it out

47 |
48 |
49 |
50 | 51 | 52 |
53 |
54 | 55 | 56 | Optional 57 |
58 |
59 | 60 | 61 | 62 | 63 | Optional 64 |
65 |
66 | 67 | 68 | 69 | 70 | Optional 71 |
72 |
73 | 74 | 75 | Optional, replaces image dimensions 76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |

Build your own URL

86 |

The placeholder.pics service uses a simple URL format to define the size, colours, and label used for your image. 87 | The format that image URLs must follow is: 88 |

89 |
    90 |
  1. The URL must start with /svg/
  2. 91 |
  3. URLs that specify only a single dimension will be square, for example a request to /svg/100 will return an svg that is 100 pixels wide and 100 pixels high
  4. 92 |
  5. Width and height is specified via WIDTHxHEIGHT 93 |
    /svg/100x100
  6. 94 |
  7. Background color can be either a single 3 or 6 character hex color code, or a gradient by defining a start color and stop color with START-STOP 95 |
    /svg/100x100/888888
  8. 96 |
  9. Foreground text and border color can be specified as a single 3 or 6 character hex color code or two colors separated by a dash TEXT-BORDER. Background color must be defined in order to set a foreground color 97 |
    /svg/100x100/888888/EEE
  10. 98 |
  11. A label can be defined using the last part of the URL. Labels should be short and take into account the size of the image being requested. Foreground and background colors must be specified first when using a label 99 |
    /svg/100x100/888888/My Label
  12. 100 |
101 |
102 |
103 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /www/css/placeholder-embedded.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'placeholder'; 3 | src: url('../font/placeholder.eot?10492122'); 4 | src: url('../font/placeholder.eot?10492122#iefix') format('embedded-opentype'), 5 | url('../font/placeholder.svg?10492122#placeholder') format('svg'); 6 | font-weight: normal; 7 | font-style: normal; 8 | } 9 | @font-face { 10 | font-family: 'placeholder'; 11 | src: url('data:application/octet-stream;base64,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') format('woff'), 12 | url('data:application/octet-stream;base64,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') format('truetype'); 13 | } 14 | /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ 15 | /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ 16 | /* 17 | @media screen and (-webkit-min-device-pixel-ratio:0) { 18 | @font-face { 19 | font-family: 'placeholder'; 20 | src: url('../font/placeholder.svg?10492122#placeholder') format('svg'); 21 | } 22 | } 23 | */ 24 | 25 | [class^="icon-"]:before, [class*=" icon-"]:before { 26 | font-family: "placeholder"; 27 | font-style: normal; 28 | font-weight: normal; 29 | speak: none; 30 | 31 | display: inline-block; 32 | text-decoration: inherit; 33 | width: 1em; 34 | margin-right: .2em; 35 | text-align: center; 36 | /* opacity: .8; */ 37 | 38 | /* For safety - reset parent styles, that can break glyph codes*/ 39 | font-variant: normal; 40 | text-transform: none; 41 | 42 | /* fix buttons height, for twitter bootstrap */ 43 | line-height: 1em; 44 | 45 | /* Animation center compensation - margins should be symmetric */ 46 | /* remove if not needed */ 47 | margin-left: .2em; 48 | 49 | /* you can be more comfortable with increased icons size */ 50 | /* font-size: 120%; */ 51 | 52 | /* Uncomment for 3D effect */ 53 | /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ 54 | } 55 | .icon-photo:before { content: '\e800'; } /* '' */ 56 | .icon-twitter:before { content: '\e801'; } /* '' */ 57 | .icon-github:before { content: '\e802'; } /* '' */ 58 | .icon-linkedin:before { content: '\e803'; } /* '' */ -------------------------------------------------------------------------------- /www/js/jscolor.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jscolor - JavaScript Color Picker 3 | * 4 | * @link http://jscolor.com 5 | * @license For open source use: GPLv3 6 | * For commercial use: JSColor Commercial License 7 | * @author Jan Odvarko 8 | * 9 | * See usage examples at http://jscolor.com/examples/ 10 | */"use strict";window.jscolor||(window.jscolor=function(){var e={register:function(){e.attachDOMReadyEvent(e.init),e.attachEvent(document,"mousedown",e.onDocumentMouseDown),e.attachEvent(document,"touchstart",e.onDocumentTouchStart),e.attachEvent(window,"resize",e.onWindowResize)},init:function(){e.jscolor.lookupClass&&e.jscolor.installByClassName(e.jscolor.lookupClass)},tryInstallOnElements:function(t,n){var r=new RegExp("(^|\\s)("+n+")(\\s*(\\{[^}]*\\})|\\s|$)","i");for(var i=0;is[u]?-r[u]+n[u]+i[u]/2>s[u]/2&&n[u]+i[u]-o[u]>=0?n[u]+i[u]-o[u]:n[u]:n[u],-r[a]+n[a]+i[a]+o[a]-l+l*f>s[a]?-r[a]+n[a]+i[a]/2>s[a]/2&&n[a]+i[a]-l-l*f>=0?n[a]+i[a]-l-l*f:n[a]+i[a]-l+l*f:n[a]+i[a]-l+l*f>=0?n[a]+i[a]-l+l*f:n[a]+i[a]-l-l*f];var h=c[u],p=c[a],d=t.fixed?"fixed":"absolute",v=(c[0]+o[0]>n[0]||c[0]2)switch(e.mode.charAt(2).toLowerCase()){case"s":return"s";case"v":return"v"}return null},onDocumentMouseDown:function(t){t||(t=window.event);var n=t.target||t.srcElement;n._jscLinkedInstance?n._jscLinkedInstance.showOnClick&&n._jscLinkedInstance.show():n._jscControlName?e.onControlPointerStart(t,n,n._jscControlName,"mouse"):e.picker&&e.picker.owner&&e.picker.owner.hide()},onDocumentTouchStart:function(t){t||(t=window.event);var n=t.target||t.srcElement;n._jscLinkedInstance?n._jscLinkedInstance.showOnClick&&n._jscLinkedInstance.show():n._jscControlName?e.onControlPointerStart(t,n,n._jscControlName,"touch"):e.picker&&e.picker.owner&&e.picker.owner.hide()},onWindowResize:function(t){e.redrawPosition()},onParentScroll:function(t){e.picker&&e.picker.owner&&e.picker.owner.hide()},_pointerMoveEvent:{mouse:"mousemove",touch:"touchmove"},_pointerEndEvent:{mouse:"mouseup",touch:"touchend"},_pointerOrigin:null,_capturedTarget:null,onControlPointerStart:function(t,n,r,i){var s=n._jscInstance;e.preventDefault(t),e.captureTarget(n);var o=function(s,o){e.attachGroupEvent("drag",s,e._pointerMoveEvent[i],e.onDocumentPointerMove(t,n,r,i,o)),e.attachGroupEvent("drag",s,e._pointerEndEvent[i],e.onDocumentPointerEnd(t,n,r,i))};o(document,[0,0]);if(window.parent&&window.frameElement){var u=window.frameElement.getBoundingClientRect(),a=[-u.left,-u.top];o(window.parent.window.document,a)}var f=e.getAbsPointerPos(t),l=e.getRelPointerPos(t);e._pointerOrigin={x:f.x-l.x,y:f.y-l.y};switch(r){case"pad":switch(e.getSliderComponent(s)){case"s":s.hsv[1]===0&&s.fromHSV(null,100,null);break;case"v":s.hsv[2]===0&&s.fromHSV(null,null,100)}e.setPad(s,t,0,0);break;case"sld":e.setSld(s,t,0)}e.dispatchFineChange(s)},onDocumentPointerMove:function(t,n,r,i,s){return function(t){var i=n._jscInstance;switch(r){case"pad":t||(t=window.event),e.setPad(i,t,s[0],s[1]),e.dispatchFineChange(i);break;case"sld":t||(t=window.event),e.setSld(i,t,s[1]),e.dispatchFineChange(i)}}},onDocumentPointerEnd:function(t,n,r,i){return function(t){var r=n._jscInstance;e.detachGroupEvents("drag"),e.releaseTarget(),e.dispatchChange(r)}},dispatchChange:function(t){t.valueElement&&e.isElementType(t.valueElement,"input")&&e.fireEvent(t.valueElement,"change")},dispatchFineChange:function(e){if(e.onFineChange){var t;typeof e.onFineChange=="string"?t=new Function(e.onFineChange):t=e.onFineChange,t.call(e)}},setPad:function(t,n,r,i){var s=e.getAbsPointerPos(n),o=r+s.x-e._pointerOrigin.x-t.padding-t.insetWidth,u=i+s.y-e._pointerOrigin.y-t.padding-t.insetWidth,a=o*(360/(t.width-1)),f=100-u*(100/(t.height-1));switch(e.getPadYComponent(t)){case"s":t.fromHSV(a,f,null,e.leaveSld);break;case"v":t.fromHSV(a,null,f,e.leaveSld)}},setSld:function(t,n,r){var i=e.getAbsPointerPos(n),s=r+i.y-e._pointerOrigin.y-t.padding-t.insetWidth,o=100-s*(100/(t.height-1));switch(e.getSliderComponent(t)){case"s":t.fromHSV(null,o,null,e.leavePad);break;case"v":t.fromHSV(null,null,o,e.leavePad)}},_vmlNS:"jsc_vml_",_vmlCSS:"jsc_vml_css_",_vmlReady:!1,initVML:function(){if(!e._vmlReady){var t=document;t.namespaces[e._vmlNS]||t.namespaces.add(e._vmlNS,"urn:schemas-microsoft-com:vml");if(!t.styleSheets[e._vmlCSS]){var n=["shape","shapetype","group","background","path","formulas","handles","fill","stroke","shadow","textbox","textpath","imagedata","line","polyline","curve","rect","roundrect","oval","arc","image"],r=t.createStyleSheet();r.owningElement.id=e._vmlCSS;for(var i=0;i=3&&(s=r[0].match(i))&&(o=r[1].match(i))&&(u=r[2].match(i))){var a=parseFloat((s[1]||"0")+(s[2]||"")),f=parseFloat((o[1]||"0")+(o[2]||"")),l=parseFloat((u[1]||"0")+(u[2]||""));return this.fromRGB(a,f,l,t),!0}}return!1},this.toString=function(){return(256|Math.round(this.rgb[0])).toString(16).substr(1)+(256|Math.round(this.rgb[1])).toString(16).substr(1)+(256|Math.round(this.rgb[2])).toString(16).substr(1)},this.toHEXString=function(){return"#"+this.toString().toUpperCase()},this.toRGBString=function(){return"rgb("+Math.round(this.rgb[0])+","+Math.round(this.rgb[1])+","+Math.round(this.rgb[2])+")"},this.isLight=function(){return.213*this.rgb[0]+.715*this.rgb[1]+.072*this.rgb[2]>127.5},this._processParentElementsInDOM=function(){if(this._linkedElementsProcessed)return;this._linkedElementsProcessed=!0;var t=this.targetElement;do{var n=e.getStyle(t);n&&n.position.toLowerCase()==="fixed"&&(this.fixed=!0),t!==this.targetElement&&(t._jscEventsAttached||(e.attachEvent(t,"scroll",e.onParentScroll),t._jscEventsAttached=!0))}while((t=t.parentNode)&&!e.isElementType(t,"body"))};if(typeof t=="string"){var h=t,p=document.getElementById(h);p?this.targetElement=p:e.warn("Could not find target element with ID '"+h+"'")}else t?this.targetElement=t:e.warn("Invalid target element: '"+t+"'");if(this.targetElement._jscLinkedInstance){e.warn("Cannot link jscolor twice to the same element. Skipping.");return}this.targetElement._jscLinkedInstance=this,this.valueElement=e.fetchElement(this.valueElement),this.styleElement=e.fetchElement(this.styleElement);var d=this,v=this.container?e.fetchElement(this.container):document.getElementsByTagName("body")[0],m=3;if(e.isElementType(this.targetElement,"button"))if(this.targetElement.onclick){var g=this.targetElement.onclick;this.targetElement.onclick=function(e){return g.call(this,e),!1}}else this.targetElement.onclick=function(){return!1};if(this.valueElement&&e.isElementType(this.valueElement,"input")){var y=function(){d.fromString(d.valueElement.value,e.leaveValue),e.dispatchFineChange(d)};e.attachEvent(this.valueElement,"keyup",y),e.attachEvent(this.valueElement,"input",y),e.attachEvent(this.valueElement,"blur",c),this.valueElement.setAttribute("autocomplete","off")}this.styleElement&&(this.styleElement._jscOrigStyle={backgroundImage:this.styleElement.style.backgroundImage,backgroundColor:this.styleElement.style.backgroundColor,color:this.styleElement.style.color}),this.value?this.fromString(this.value)||this.exportColor():this.importColor()}};return e.jscolor.lookupClass="jscolor",e.jscolor.installByClassName=function(t){var n=document.getElementsByTagName("input"),r=document.getElementsByTagName("button");e.tryInstallOnElements(n,t),e.tryInstallOnElements(r,t)},e.register(),e.jscolor}()); --------------------------------------------------------------------------------