├── .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 | [](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 |
--------------------------------------------------------------------------------