├── .gitignore ├── package.json ├── README.md ├── LICENSE ├── template.html └── gulpfile.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Generated index.html 6 | index.html 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | 13 | # Directory for instrumented libs generated by jscoverage/JSCover 14 | lib-cov 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | 19 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 20 | .grunt 21 | 22 | # node-waf configuration 23 | .lock-wscript 24 | 25 | # Compiled binary addons (http://nodejs.org/api/addons.html) 26 | build/Release 27 | 28 | # Dependency directory 29 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 30 | node_modules 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CodepenOffline", 3 | "version": "1.0.0", 4 | "description": "Codepen's pen offline coding with livereload", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/icebob/codepen_offline.git" 8 | }, 9 | "author": { 10 | "name": "Icebob", 11 | "email": "mereg.norbert@gmail.com" 12 | }, 13 | "scripts": { 14 | "test": "echo \"Error: no test specified\" && exit 1" 15 | }, 16 | "licenses": [ 17 | { 18 | "type": "MIT" 19 | } 20 | ], 21 | "devDependencies": { 22 | "browser-sync": "^2.5.2", 23 | "glob": "^5.0.3", 24 | "gulp": "^3.8.11", 25 | "gulp-coffee": "^2.3.1", 26 | "gulp-compass": "^2.0.4", 27 | "gulp-jade": "^1.0.0", 28 | "gulp-load-plugins": "^0.9.0", 29 | "gulp-plumber": "^1.0.0", 30 | "gulp-rename": "^1.2.2", 31 | "gulp-replace": "^0.5.3", 32 | "gulp-tap": "^0.1.3", 33 | "gulp-watch": "^4.2.3", 34 | "replacestream": "^2.1.0", 35 | "run-sequence": "^1.0.2", 36 | "unzip": "^0.1.11" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Codepen Offline 2 | Codepen's pen offline coding tool with livereload support 3 | 4 | ## Features 5 | - handles multiple downloaded pens 6 | - livereload support (browser-sync) 7 | - watch, recompile and reload files 8 | - SASS and Compass support 9 | - Coffeescript support 10 | 11 | 12 | ## Dependencies 13 | 14 | - [NodeJS](http://nodejs.org) 15 | - [gulp](http://gulpjs.com) - npm install -g gulp 16 | - Python + compass (for SASS files) 17 | 18 | ## Download 19 | ``` 20 | git clone https://github.com/icebob/codepen_offline.git 21 | ``` 22 | 23 | or download zip 24 | 25 | ``` 26 | https://github.com/icebob/codepen_offline/archive/master.zip 27 | ``` 28 | 29 | ## Install 30 | 31 | 1. Install npm dependencies: 32 | ``` 33 | npm install 34 | ``` 35 | 3. Run gulp: 36 | ``` 37 | gulp 38 | ``` 39 | 40 | ## How it works 41 | [![How it works video](http://img.youtube.com/vi/GSMks-6RXIk/0.jpg)](http://www.youtube.com/watch?v=GSMks-6RXIk) 42 | 43 | ## Contact 44 | 45 | Copyright (C) 2015 Icebob 46 | 47 | [www.github.com/icebob](http://www.github.com/icebob)
48 | [@Icebobcsi](http://www.twitter.com/Icebobcsi) 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 icebob 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 | -------------------------------------------------------------------------------- /template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CodePen Offline 6 | 7 | 8 | 9 | 110 | 111 | 112 | 113 | 114 | 115 |
116 | 117 | %%PENS%% 118 | 119 |
120 | 121 | 122 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var rs = require('replacestream'); 5 | var tap = require('gulp-tap'); 6 | var runSequence = require('run-sequence'); 7 | var glob = require("glob"); 8 | var unzip = require("unzip"); 9 | 10 | var browserSync = require('browser-sync'); 11 | var reload = browserSync.reload; 12 | 13 | var gulp = require('gulp'); 14 | var $ = require('gulp-load-plugins')({ 15 | pattern: ['gulp-*'] 16 | }); 17 | 18 | // Browser-sync task 19 | gulp.task('browser-sync', function() { 20 | browserSync({ 21 | server: { 22 | baseDir: "./" 23 | } 24 | }); 25 | }); 26 | 27 | // Sass task 28 | gulp.task('sass', function () { 29 | 30 | glob('**/scss/*.scss', function(err, files) { 31 | if (err) return; 32 | 33 | files.forEach(function(file) { 34 | 35 | var subDir = file.split("/")[0]; 36 | gulp.src(file) 37 | .pipe($.plumber()) 38 | .pipe($.compass({ 39 | project: path.join(__dirname, subDir), 40 | css: 'css', 41 | sass: 'scss', 42 | style: 'expanded', 43 | cache: false 44 | })); 45 | }); 46 | }) 47 | }); 48 | 49 | // Coffescript task 50 | gulp.task('coffee', function () { 51 | return gulp.src('**/coffeescript/*.coffeescript') 52 | .pipe($.plumber()) 53 | .pipe($.coffee({bare: true })) 54 | .pipe($.rename(function(path) { 55 | path.dirname = path.dirname.replace("coffeescript", "js"); 56 | })) 57 | .pipe(gulp.dest(".")); 58 | }); 59 | 60 | // JADE task (index.jade -> __body.html) 61 | gulp.task('jade-body', function () { 62 | return gulp.src(['**/index.jade', '!./node_modules/**']) 63 | .pipe($.plumber()) 64 | .pipe($.jade({ 65 | pretty: true 66 | })) 67 | .pipe($.rename(function(path) { 68 | path.basename = path.basename.replace("index", "__body"); 69 | })) 70 | .pipe(gulp.dest(".")); 71 | }); 72 | 73 | // HTML concatenate (layout.html + __body.html -> index.html) 74 | gulp.task('html-concat', function() { 75 | return gulp.src(['**/layout.html', '!./node_modules/**']) 76 | .pipe(tap(function(file) { 77 | // Read __body.html 78 | var bodyFile = file.path.replace("layout.html", "__body.html"); 79 | var bodyContent = fs.readFileSync(bodyFile, "UTF-8"); 80 | 81 | // Replace content 82 | if (file.isBuffer()) 83 | file.contents = new Buffer(String(file.contents).replace(/]*>((.|\s)*)<\/body>/im, "" + bodyContent + "")); 84 | 85 | fs.unlinkSync(bodyFile); 86 | })) 87 | .pipe($.rename(function(path) { 88 | // Rename to index.html 89 | path.basename = path.basename.replace("layout", "index"); 90 | })) 91 | .pipe(gulp.dest(".")); 92 | }); 93 | 94 | // Jade compile task 95 | gulp.task('jade', function(cb) { 96 | runSequence('jade-body', 'html-concat', cb); 97 | }); 98 | 99 | 100 | // Unzip task. (unzip downloaded pens to a folder) 101 | gulp.task('unzip', function (done) { 102 | 103 | glob("*.zip", function(err, files) { 104 | 105 | files.forEach(function(f) { 106 | var name = path.basename(f, '.zip'); 107 | console.log("Unzip pen " + name + "..."); 108 | fs.createReadStream(f).pipe(unzip.Extract({ path: './' + name })); 109 | 110 | fs.unlinkSync(f); 111 | }); 112 | 113 | 114 | done(); 115 | }); 116 | 117 | }); 118 | 119 | 120 | // Scan pen directories and make a default index.html 121 | gulp.task('dirs', ['unzip'], function (done) { 122 | 123 | // Pen html template 124 | var penHTML = 125 | '
\r\n'+ 126 | '
\r\n'+ 127 | ' \r\n'+ 128 | 129 | ' \r\n'+ 131 | 132 | '
\r\n'+ 133 | ' %%PENNAME%% \r\n'+ 134 | '
\r\n'+ 135 | '
\r\n'+ 136 | '
\r\n'; 137 | 138 | // Read index template 139 | var indexContent = fs.readFileSync("template.html", "UTF-8"); 140 | var pens = []; 141 | 142 | // Search directories 143 | glob('*/', function(err, dirs) { 144 | if (err) return; 145 | 146 | dirs.forEach(function(dir) { 147 | if (dir != "node_modules/") { 148 | pens.push(penHTML.replace(/%%PENNAME%%/g, dir)); 149 | } 150 | }) 151 | 152 | }).on("end", function() { 153 | 154 | console.log("Found " + pens.length + " pens."); 155 | indexContent = indexContent.replace("%%PENS%%", pens.join("\r\n")); 156 | fs.writeFileSync("index.html", indexContent); 157 | 158 | done(); 159 | }) 160 | 161 | }); 162 | 163 | /** 164 | * Reload all Browsers 165 | */ 166 | gulp.task('bs-reload', function () { 167 | browserSync.reload(); 168 | }); 169 | 170 | // Default full task (compile + watch + reload) 171 | gulp.task('default', ['dirs', 'sass', 'coffee', 'browser-sync'], function () { 172 | $.watch(['**/css/*.css', '!./node_modules/**'], function(file) { 173 | reload(file.path); 174 | }); 175 | 176 | $.watch(['**/*.html', '!./node_modules/**', '!index_template.html'], function() { 177 | gulp.start('bs-reload'); 178 | }); 179 | $.watch(['**/js/*.js', '!./node_modules/**'], function() { 180 | gulp.start('bs-reload'); 181 | }); 182 | 183 | $.watch(['**/*.jade', '!./node_modules/**'], function() { 184 | gulp.start('jade'); 185 | }); 186 | $.watch(['**/coffeescript/*.coffeescript', '!./node_modules/**'], function() { 187 | gulp.start('coffee'); 188 | }); 189 | $.watch(['**/scss/*.scss', '!./node_modules/**'], function() { 190 | gulp.start('sass'); 191 | }); 192 | 193 | $.watch(['index_template.html'], function() { 194 | gulp.start('dirs'); 195 | }); 196 | 197 | $.watch(['*.zip'], function() { 198 | gulp.start('dirs'); 199 | }); 200 | 201 | }); 202 | 203 | --------------------------------------------------------------------------------