Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

First commit.

  • Loading branch information...
commit 786dba02ec60154ae1dfc5cca9f17e699884f7d1 0 parents
@krunkosaurus krunkosaurus authored
Showing with 25,965 additions and 0 deletions.
  1. +41 −0 .gitignore
  2. +925 −0 Jakefile
  3. +15 −0 README.md
  4. +6 −0 jakelib/aws.jake
  5. +169 −0 node_modules/NodeInterval/README.org
  6. +171 −0 node_modules/NodeInterval/index.js
  7. +186 −0 node_modules/NodeInterval/node_modules/nodewatch/docs/docco.css
  8. +167 −0 node_modules/NodeInterval/node_modules/nodewatch/docs/watch.html
  9. +82 −0 node_modules/NodeInterval/node_modules/nodewatch/lib/watch/watch.js
  10. +20 −0 node_modules/NodeInterval/node_modules/nodewatch/package.json
  11. +2 −0  node_modules/NodeInterval/node_modules/simple-logger/.npmignore
  12. +25 −0 node_modules/NodeInterval/node_modules/simple-logger/README.md
  13. +36 −0 node_modules/NodeInterval/node_modules/simple-logger/index.js
  14. +14 −0 node_modules/NodeInterval/node_modules/simple-logger/package.json
  15. +3 −0  node_modules/NodeInterval/node_modules/underscore/.npmignore
  16. +22 −0 node_modules/NodeInterval/node_modules/underscore/LICENSE
  17. +19 −0 node_modules/NodeInterval/node_modules/underscore/README
  18. +1,698 −0 node_modules/NodeInterval/node_modules/underscore/index.html
  19. +1 −0  node_modules/NodeInterval/node_modules/underscore/index.js
  20. +12 −0 node_modules/NodeInterval/node_modules/underscore/package.json
  21. +29 −0 node_modules/NodeInterval/node_modules/underscore/underscore-min.js
  22. +931 −0 node_modules/NodeInterval/node_modules/underscore/underscore.js
  23. +19 −0 node_modules/NodeInterval/package.json
  24. +25 −0 node_modules/NodeInterval/samples/nodeinterval.js
  25. +27 −0 node_modules/NodeInterval/samples/nodeinterval.sh
  26. +44 −0 node_modules/NodeInterval/tests/assets/index.html
  27. +11 −0 node_modules/NodeInterval/tests/listen.js
  28. +515 −0 node_modules/NodeInterval/tests/node_modules/.bin/vows
  29. +186 −0 node_modules/NodeInterval/tests/node_modules/nodeinterval/node_modules/nodewatch/docs/docco.css
  30. +167 −0 node_modules/NodeInterval/tests/node_modules/nodeinterval/node_modules/nodewatch/docs/watch.html
  31. +74 −0 node_modules/NodeInterval/tests/node_modules/nodeinterval/node_modules/nodewatch/lib/watch/watch.js
  32. +20 −0 node_modules/NodeInterval/tests/node_modules/nodeinterval/node_modules/nodewatch/package.json
  33. +2 −0  node_modules/NodeInterval/tests/node_modules/nodeinterval/node_modules/simple-logger/.npmignore
  34. +25 −0 node_modules/NodeInterval/tests/node_modules/nodeinterval/node_modules/simple-logger/README.md
  35. +36 −0 node_modules/NodeInterval/tests/node_modules/nodeinterval/node_modules/simple-logger/index.js
  36. +14 −0 node_modules/NodeInterval/tests/node_modules/nodeinterval/node_modules/simple-logger/package.json
  37. +3 −0  node_modules/NodeInterval/tests/node_modules/nodeinterval/node_modules/underscore/.npmignore
  38. +22 −0 node_modules/NodeInterval/tests/node_modules/nodeinterval/node_modules/underscore/LICENSE
  39. +19 −0 node_modules/NodeInterval/tests/node_modules/nodeinterval/node_modules/underscore/README
  40. +1,608 −0 node_modules/NodeInterval/tests/node_modules/nodeinterval/node_modules/underscore/index.html
  41. +1 −0  node_modules/NodeInterval/tests/node_modules/nodeinterval/node_modules/underscore/index.js
  42. +12 −0 node_modules/NodeInterval/tests/node_modules/nodeinterval/node_modules/underscore/package.json
  43. +27 −0 node_modules/NodeInterval/tests/node_modules/nodeinterval/node_modules/underscore/underscore-min.js
  44. +839 −0 node_modules/NodeInterval/tests/node_modules/nodeinterval/node_modules/underscore/underscore.js
  45. +1 −0  node_modules/NodeInterval/tests/node_modules/vows/.gitignore
  46. +20 −0 node_modules/NodeInterval/tests/node_modules/vows/LICENSE
  47. +7 −0 node_modules/NodeInterval/tests/node_modules/vows/Makefile
  48. +39 −0 node_modules/NodeInterval/tests/node_modules/vows/README.md
  49. +515 −0 node_modules/NodeInterval/tests/node_modules/vows/bin/vows
  50. +27 −0 node_modules/NodeInterval/tests/node_modules/vows/lib/assert/error.js
  51. +215 −0 node_modules/NodeInterval/tests/node_modules/vows/lib/assert/macros.js
  52. +77 −0 node_modules/NodeInterval/tests/node_modules/vows/lib/assert/utils.js
  53. +193 −0 node_modules/NodeInterval/tests/node_modules/vows/lib/vows.js
  54. +131 −0 node_modules/NodeInterval/tests/node_modules/vows/lib/vows/console.js
  55. +55 −0 node_modules/NodeInterval/tests/node_modules/vows/lib/vows/context.js
  56. +29 −0 node_modules/NodeInterval/tests/node_modules/vows/lib/vows/coverage/file.js
  57. +2 −0  node_modules/NodeInterval/tests/node_modules/vows/lib/vows/coverage/fragments/coverage-foot.html
  58. +61 −0 node_modules/NodeInterval/tests/node_modules/vows/lib/vows/coverage/fragments/coverage-head.html
  59. +54 −0 node_modules/NodeInterval/tests/node_modules/vows/lib/vows/coverage/report-html.js
  60. +54 −0 node_modules/NodeInterval/tests/node_modules/vows/lib/vows/coverage/report-json.js
  61. +38 −0 node_modules/NodeInterval/tests/node_modules/vows/lib/vows/coverage/report-plain.js
  62. +28 −0 node_modules/NodeInterval/tests/node_modules/vows/lib/vows/extras.js
  63. +67 −0 node_modules/NodeInterval/tests/node_modules/vows/lib/vows/reporters/dot-matrix.js
  64. +16 −0 node_modules/NodeInterval/tests/node_modules/vows/lib/vows/reporters/json.js
  65. +8 −0 node_modules/NodeInterval/tests/node_modules/vows/lib/vows/reporters/silent.js
  66. +44 −0 node_modules/NodeInterval/tests/node_modules/vows/lib/vows/reporters/spec.js
  67. +39 −0 node_modules/NodeInterval/tests/node_modules/vows/lib/vows/reporters/watch.js
  68. +90 −0 node_modules/NodeInterval/tests/node_modules/vows/lib/vows/reporters/xunit.js
  69. +319 −0 node_modules/NodeInterval/tests/node_modules/vows/lib/vows/suite.js
  70. +14 −0 node_modules/NodeInterval/tests/node_modules/vows/package.json
  71. +135 −0 node_modules/NodeInterval/tests/node_modules/vows/test/assert-test.js
  72. +18 −0 node_modules/NodeInterval/tests/node_modules/vows/test/fixtures/isolate/failing.js
  73. +18 −0 node_modules/NodeInterval/tests/node_modules/vows/test/fixtures/isolate/log.js
  74. +17 −0 node_modules/NodeInterval/tests/node_modules/vows/test/fixtures/isolate/passing.js
  75. +18 −0 node_modules/NodeInterval/tests/node_modules/vows/test/fixtures/isolate/stderr.js
  76. +140 −0 node_modules/NodeInterval/tests/node_modules/vows/test/isolate-test.js
  77. +133 −0 node_modules/NodeInterval/tests/node_modules/vows/test/testInherit.js
  78. +51 −0 node_modules/NodeInterval/tests/node_modules/vows/test/vows-error-test.js
  79. +374 −0 node_modules/NodeInterval/tests/node_modules/vows/test/vows-test.js
  80. +97 −0 node_modules/NodeInterval/tests/run-tests.js
  81. +12 −0 node_modules/NodeInterval/tests/src/index.html
  82. +1 −0  node_modules/NodeInterval/tests/src/templates/.bob
  83. +1 −0  node_modules/NodeInterval/tests/src/templates/dir_level_2/.doggies/file1
  84. +4 −0 node_modules/NodeInterval/tests/src/templates/dir_level_2/dir_level_3/template09.tmpl
  85. +4 −0 node_modules/NodeInterval/tests/src/templates/dir_level_2/template07.tmpl
  86. +4 −0 node_modules/NodeInterval/tests/src/templates/dir_level_2/template08.tmpl
  87. +3 −0  node_modules/NodeInterval/tests/src/templates/template01.tmpl
  88. +4 −0 node_modules/NodeInterval/tests/src/templates/template02.tmpl
  89. +4 −0 node_modules/NodeInterval/tests/src/templates/template03.tmpl
  90. +4 −0 node_modules/NodeInterval/tests/src/templates/template04.tmpl
  91. +4 −0 node_modules/NodeInterval/tests/src/templates/template05.tmpl
  92. +4 −0 node_modules/NodeInterval/tests/src/templates/template06.tmpl
  93. +21 −0 node_modules/NodeInterval/todo.org
  94. +9 −0 node_modules/async/.gitmodules
  95. +19 −0 node_modules/async/LICENSE
  96. +21 −0 node_modules/async/Makefile
  97. +1,009 −0 node_modules/async/README.md
  98. +70 −0 node_modules/async/deps/nodeunit.css
  99. +1,966 −0 node_modules/async/deps/nodeunit.js
  100. +1 −0  node_modules/async/dist/async.min.js
  101. +3 −0  node_modules/async/index.js
  102. +690 −0 node_modules/async/lib/async.js
  103. +4 −0 node_modules/async/nodelint.cfg
  104. +16 −0 node_modules/async/package.json
  105. +1,577 −0 node_modules/async/test/test-async.js
  106. +24 −0 node_modules/async/test/test.html
  107. +9 −0 node_modules/compress/.lock-wscript
  108. +18 −0 node_modules/compress/LICENSE
  109. +37 −0 node_modules/compress/README
  110. +377 −0 node_modules/compress/compress.cc
  111. +41 −0 node_modules/compress/filetest.js
  112. +15 −0 node_modules/compress/package.json
  113. +38 −0 node_modules/compress/test.js
  114. +33 −0 node_modules/compress/wscript
  115. +3 −0  node_modules/knox/.gitmodules
  116. +3 −0  node_modules/knox/.npmignore
  117. +41 −0 node_modules/knox/History.md
  118. +21 −0 node_modules/knox/Makefile
  119. +144 −0 node_modules/knox/Readme.md
  120. +483 −0 node_modules/knox/index.html
  121. +2 −0  node_modules/knox/index.js
  122. +190 −0 node_modules/knox/lib/knox/auth.js
  123. +355 −0 node_modules/knox/lib/knox/client.js
  124. +36 −0 node_modules/knox/lib/knox/index.js
  125. +19 −0 node_modules/knox/lib/knox/mime/README.md
  126. +1 −0  node_modules/knox/lib/knox/mime/index.js
  127. +308 −0 node_modules/knox/lib/knox/mime/mime.js
  128. +12 −0 node_modules/knox/lib/knox/mime/package.json
  129. +59 −0 node_modules/knox/lib/knox/mime/test.js
  130. +55 −0 node_modules/knox/lib/knox/utils.js
  131. +13 −0 node_modules/knox/package.json
  132. +3 −0  node_modules/knox/support/expresso/.gitmodules
  133. +3 −0  node_modules/knox/support/expresso/.npmignore
  134. +87 −0 node_modules/knox/support/expresso/History.md
  135. +53 −0 node_modules/knox/support/expresso/Makefile
  136. +39 −0 node_modules/knox/support/expresso/Readme.md
  137. +835 −0 node_modules/knox/support/expresso/bin/expresso
  138. +1,048 −0 node_modules/knox/support/expresso/docs/api.html
  139. +373 −0 node_modules/knox/support/expresso/docs/index.html
  140. +290 −0 node_modules/knox/support/expresso/docs/index.md
  141. +3 −0  node_modules/knox/support/expresso/docs/layout/foot.html
  142. +42 −0 node_modules/knox/support/expresso/docs/layout/head.html
  143. +4 −0 node_modules/knox/support/expresso/lib/bar.js
  144. +16 −0 node_modules/knox/support/expresso/lib/foo.js
  145. +9 −0 node_modules/knox/support/expresso/package.json
  146. +84 −0 node_modules/knox/support/expresso/test/assert.test.js
  147. +6 −0 node_modules/knox/support/expresso/test/async.test.js
  148. +12 −0 node_modules/knox/support/expresso/test/bar.test.js
  149. +13 −0 node_modules/knox/support/expresso/test/foo.test.js
  150. +76 −0 node_modules/knox/support/expresso/test/http.test.js
  151. +38 −0 node_modules/knox/support/expresso/test/serial/async.test.js
  152. +47 −0 node_modules/knox/support/expresso/test/serial/http.test.js
  153. +59 −0 node_modules/knox/test/auth.test.js
  154. +1 −0  node_modules/knox/test/fixtures/user.json
  155. +188 −0 node_modules/knox/test/knox.test.js
  156. +17 −0 node_modules/knox/test/utils.test.js
  157. +186 −0 node_modules/nodewatch/docs/docco.css
  158. +167 −0 node_modules/nodewatch/docs/watch.html
  159. +82 −0 node_modules/nodewatch/lib/watch/watch.js
  160. +20 −0 node_modules/nodewatch/package.json
  161. +4 −0 node_modules/uglify-js/.gitignore
  162. +825 −0 node_modules/uglify-js/README.html
  163. +431 −0 node_modules/uglify-js/README.org
  164. +305 −0 node_modules/uglify-js/bin/uglifyjs
  165. +75 −0 node_modules/uglify-js/docstyle.css
  166. +1,318 −0 node_modules/uglify-js/lib/parse-js.js
Sorry, we could not display the entire diff because too many files (365) changed.
41 .gitignore
@@ -0,0 +1,41 @@
+# Numerous always-ignore extensions
+*.diff
+*.err
+*.orig
+*.log
+*.rej
+*.swo
+*.swp
+*.vi
+*~
+*.sass-cache
+
+# OS or Editor folders
+.DS_Store
+Thumbs.db
+.cache
+.project
+.settings
+.tmproj
+.esproj
+nbproject
+
+# Dreamweaver added files
+_notes
+dwsync.xml
+
+# Komodo
+*.komodoproject
+.komodotools
+
+# Folders to ignore
+.hg
+.svn
+.CVS
+intermediate
+publish
+.idea
+deploy-debug/
+deploy-prod/
+
+# build script local files
925 Jakefile
@@ -0,0 +1,925 @@
+var fs = require('fs'),
+path = require('path'),
+ jsp = require('./node_modules/uglify-js').parser,
+ pro = require('./node_modules/uglify-js').uglify,
+NodeWatch = require('./node_modules/nodewatch'),
+WatchTree = require('./node_modules/watch-tree'),
+ knox = require('knox'),
+exec = require('child_process').exec,
+util = require('util'),
+ ISWIN = !!process.platform.match(/^win/);
+
+// Increase limit of EventImitters
+// http://stackoverflow.com/questions/8313628/node-js-request-how-to-emitter-setmaxlisteners
+NodeWatch.setMaxListeners(0);
+
+/************************ TO MODIFY ************************/
+
+var APP_NAMESPACE = 'dontbreak',
+ APP_DOMAIN = 'dontbreak.me',
+ APP_QA_DOMAIN = 'qa.dontbreak.me',
+ APP_STAGING_DOMAIN = '';
+
+// Deploy variables
+var PROD_SERVER_USER = 'deploy',
+ PROD_SERVER_IP = 'brainswap', // Also accept SSH alias.
+ PROD_DEPLOY_SCRIPT = 'deploy-prod.sh', // Looks in bin folder.
+ QA_SERVER_USER = 'deploy',
+ QA_SERVER_IP = 'brainswap', // Also accept SSH alias.
+ QA_DEPLOY_SCRIPT = 'deploy-qa.sh', // Looks in bin folder.
+ STAGING_SERVER_USER = '',
+ STAGING_SERVER_IP = '',
+ STAGING_DEPLOY_SCRIPT = '';
+
+// Amazon S3 Variables
+var S3_MEDIA_FOLDERS = [ // Used in deciding what goes to S3
+ '/js/',
+ '/css/',
+ '/images/',
+ '/sounds/',
+ '/polyfill/'
+ // '/webfonts/' // Host web fonts locally as they won't work in FF.
+ ],
+ S3_PROD_BUCKET = 'https://pr-dontbreak.s3.amazonaws.com',
+ S3_QA_BUCKET = 'https://qa-dontbreak.s3.amazonaws.com',
+ S3_STAGING_BUCKET = '';
+
+/************************ NOT TO MODIFY ************************/
+
+// Development folder paths
+var PROD_FOLDER = 'deploy-prod',
+ DEBUG_FOLDER = 'deploy-debug';
+
+// JavaScript variables
+var JS_FILES = [], // JS file paths parsed from src/index.html between @js concat start@ and @js concat end@)
+ JS_START_MARKER = '@javascript concat start@',
+ JS_END_MARKER = '@javascript concat end@';
+
+// CSS Variables
+var CSS_FILES = [], // parsed from src/index.html between @css concat start@ and @css concat end@
+ TEMPLATES_DIRTY = true,
+ CONCATED_TEMPLATES,
+ CSS_START_MARKER = '@css concat start@',
+ CSS_END_MARKER = '@css concat end@';
+
+/********************/
+/* Utiliy functions */
+/********************/
+// Shortcut to run exec command with logging and error support
+function execLog(stringCommand, callback){
+ exec(stringCommand, function(error, stdout, stderr){
+ if (callback){
+ callback();
+ }
+
+ if (stdout){
+ console.log(stdout);
+ }
+ if (stderr){
+ console.log('stderr: ' + stderr);
+ }
+ if (error !== null) {
+ console.log('exec error: ' + error);
+ }
+ });
+}
+
+function getDate(){
+ var today = new Date();
+ var DD = today.getDate();
+ var MM = today.getMonth() + 1; //January is 0!
+ var YYYY = today.getFullYear();
+ var hh = today.getHours();
+ var mm = today.getMinutes();
+ var ss = today.getSeconds();
+
+ if (DD<10){DD ='0' + DD}
+ if (MM<10){MM = '0' + MM}
+
+ return [MM,DD,YYYY,hh,mm,ss].join('_');
+}
+
+function gzipFile(file){
+ execLog('gzip ' + file, function(){
+ fs.renameSync(file + '.gz', file);
+ });
+}
+
+/* Called by css:<env> to compress css */
+function compressCSS(domain){
+ //combine all css into a single scss index file
+ parseCSSFileList();
+
+ var allCSSText = '';
+ for (i = 0, len = CSS_FILES.length; i < len; i++){
+ allCSSText += fs.readFileSync('src/' + CSS_FILES[i], 'utf8');
+ }
+
+ allCSSText = namespaceWebfonts(domain, allCSSText);
+ fs.writeFileSync(PROD_FOLDER + '/css/index.scss', allCSSText, 'utf8');
+
+ execLog('sass --style=compressed --load-path src/css/ --scss ' + PROD_FOLDER + '/css/index.scss ' + PROD_FOLDER + '/css/index.css');
+ //todo: remove the temporary index.scss file in the prod folder. Can't just remove with unlinkSync as sass call is asynch
+ //fs.unlinkSync(PROD_FOLDER+'/css/index.scss');
+}
+
+function namespaceWebfonts(domain, text){
+ return text.replace(/\/webfonts\//g, 'http://' + domain + '/webfonts/');
+}
+
+function uploadMediaFoldersToS3(bucket){
+ console.log('Uploading media folders to %s...', bucket);
+ var bucketDomain = bucket.match(/\/\/(.*)\.s3/)[1];
+ S3_MEDIA_FOLDERS.forEach(function(folder){
+ uploadFolderToS3(bucketDomain, PROD_FOLDER + folder);
+ });
+}
+
+function uploadFolderToS3(bucket, folder){
+ var files = listFilesRecursive(folder);
+ var uploadFile = function(from){
+ var ext = from.match(/\w*$/)[0];
+ var contentType;
+ var to = '/' + from.replace(PROD_FOLDER + '/', '');
+ var meta = {
+ 'Cache-Control': 'max-age=1209600' // 2 weeks
+ }
+ console.log('s3: %s -> %s', from, to);
+
+ switch(ext){
+ case 'jpg':
+ contentType = 'image/jpeg';
+ break;
+ case 'png':
+ contentType = 'image/png';
+ break;
+ case 'gif':
+ contentType = 'image/gif';
+ break;
+ case 'html':
+ contentType = 'text/html';
+ break;
+ case 'txt':
+ contentType = 'text/plain';
+ break;
+ case 'mobileprovision':
+ contentType = 'application/octet-stream';
+ break;
+ case 'ipa':
+ contentType = 'application/octet-stream';
+ break;
+ case 'htc':
+ contentType = 'text/x-component';
+ break;
+ case 'css':
+ gzipFile(from);
+ contentType = 'text/css';
+ break;
+ case 'js':
+ gzipFile(from);
+ contentType = 'application/javascript; charset=utf-8';
+ break;
+ case 'mobileprovision':
+ contentType = 'application/xml';
+ break;
+ case 'ttf':
+ gzipFile(from);
+ contentType = 'application/x-font-ttf';
+ break;
+ case 'otf':
+ gzipFile(from);
+ contentType = 'font/opentype';
+ break;
+ case 'woff':
+ contentType = 'application/x-font-woff';
+ break;
+ case 'svg':
+ contentType = 'image/svg+xml';
+ break;
+ case 'eot':
+ gzipFile(from);
+ contentType = 'application/vnd.ms-fontobject';
+ break;
+ default:
+ contentType = 'uknown';
+ }
+
+ // Note: Setting small timeout here as gziping files happens with exec
+ // which is a child process so we must make sure we wait long enough.
+ setTimeout(function(){
+ fs.readFile(from, function(err, buf){
+ meta['Content-Length'] = buf.length;
+ meta['Content-Type'] = contentType;
+
+ if (ext == 'js' || ext == 'css' || ext == 'ttf' || ext == 'otf' || ext == 'eot'){
+ meta['Content-Encoding'] = 'gzip';
+ }
+
+ var req = knoxClient.put(to, meta);
+ req.on('response', function(res){
+ if (200 == res.statusCode) {
+ console.log('saved to %s', req.url);
+ }else{
+ console.error('ERROR %s %s:', from, res.statusCode);
+ }
+ });
+ req.end(buf);
+ });
+ },500);
+ }
+
+ if (!bucket){
+ bucket = '';
+ }
+
+ if (!global.knoxClient){
+ knoxClient = knox.createClient({
+ key: S3.KEY,
+ secret: S3.SECRET,
+ bucket: bucket
+ });
+ }
+
+ files.forEach(function(file){
+ // If the file isn't a decendent of a folder that begins with _ upload it.
+ if (!file.match('\/_')){
+ uploadFile(file);
+ }
+ });
+}
+
+function parseJavascriptFileList(){
+ //generate css list
+ var templateText = /@javascript concat start@([\s\S]*?)@javascript concat end@/.exec( fs.readFileSync('src/index.html', 'utf8') )[1];
+ //parse out comments to catch any tags that are commented out
+ templateText = templateText.replace(/<!--([\s\S]*?)-->/g, '');
+ JS_FILES = [];
+ var matches = templateText.match(/src="([\s\S]*?)"/g);
+ for (var i = 0; i<matches.length; i++){
+ //ugly but works for now since i can't easily get the capture group
+ JS_FILES.push(matches[i].split('"')[1]);
+ }
+}
+function parseCSSFileList(){
+ //generate css list
+ var templateText = /@css concat start@([\s\S]*?)@css concat end@/.exec( fs.readFileSync('src/index.html', 'utf8') )[1];
+ //parse out comments to catch any tags that are commented out
+ templateText = templateText.replace(/<!--([\s\S]*?)-->/g, '');
+ CSS_FILES = [];
+ var matches = templateText.match(/href="([\s\S]*?)"/g);
+ for (var i = 0; i<matches.length; i++){
+ //ugly but works for now since i can't easily get the capture group
+ CSS_FILES.push(matches[i].split('"')[1]);
+ }
+}
+function generateConcatedTemplates(){
+ CONCATED_TEMPLATES = ''
+ var files = listFilesRecursive('src/templates/');
+ files.forEach(function(file){
+ if (file.indexOf('.tmpl')!=-1){
+ CONCATED_TEMPLATES += fs.readFileSync(file, 'utf8') + '\n ';
+ }
+ });
+}
+
+/* convert and copy all scss and css from src to dev */
+function convertAndCopyCSSDev(path){
+ var filename = path.split('src/css/').join('').split('.scss').join('.css');
+
+ if (path.indexOf('.css')!=-1){
+ console.log('copy: ' + path + ' to dev /css/' + filename);
+ exec('cp ' + path + ' ' + DEBUG_FOLDER + '/css/' + filename, function(error, stdout, stderr){
+ util.puts(stdout);
+ });
+ } else {
+ console.log('path: ' + path + ' filename: ' + filename);
+ //special case for font pathing in main _base.css file
+ if (filename=='_base.css'){
+ var cssText = fs.readFileSync(path, 'utf8');
+ console.log('cssText: ' + cssText);
+ path = DEBUG_FOLDER + '/css/' + filename.split('.css').join('.scss');
+ cssText = cssText.split('@cdnhost@').join('');
+ fs.writeFileSync(path, cssText)
+ }
+ exec('sass --style=expanded --load-path src/css/ --update ' + path + ':' + DEBUG_FOLDER + '/css/' + filename, function(error, stdout, stderr){
+ util.puts(stdout);
+ });
+
+ }
+}
+
+/* Concats all JS files together as one long string. Adds line breaks between files. */
+function concatJS(){
+ parseJavascriptFileList();
+ var concatFiles = [], i;
+ for (i = 0, len = JS_FILES.length; i < len; i++){
+ var file = 'src/'+JS_FILES[i];
+ concatFiles.push(fs.readFileSync(file, 'utf8'));
+ }
+ return concatFiles.join('\n');
+}
+
+/* Takes a string of JS and returns a compresses version of it. */
+function uglifyJS(str){
+ // parse code and get the initial AST
+ var ast = jsp.parse(str);
+ // get a new AST with mangled names
+ ast = pro.ast_mangle(ast);
+ // get an AST with compression optimizations
+ ast = pro.ast_squeeze(ast);
+ // compressed code here
+ return pro.gen_code(ast);
+}
+/* Checks if directory exists, creates if needed with optional created callback. */
+function directoryCheck(dir, callback){
+ dir = platformProofPath(dir);
+ // path.existsSync is now fs.existsSync in v0.7.6-pre so we must check for it.
+ if (!path.existsSync(dir)){
+ console.log(dir + ' folder doesn\'t exist. Creating...');
+ fs.mkdirSync(dir);
+ if (callback){
+ callback(dir);
+ }
+ }
+}
+
+/* Copy a file synchronously to a destination file.*/
+function copyFileSync(srcFile, destFile) {
+ srcFile = platformProofPath(srcFile);
+ destFile = platformProofPath(destFile);
+ var BUF_LENGTH, buff, bytesRead, fdr, fdw, pos;
+ BUF_LENGTH = 64 * 1024;
+ buff = new Buffer(BUF_LENGTH);
+ fdr = fs.openSync(srcFile, 'r');
+ var lastChar = destFile.charAt(destFile.length-1);
+ var slashType = ISWIN ? '\\' : '/';
+ if (lastChar==slashType){
+ var fileName = srcFile.split(slashType);
+ fileName = fileName[fileName.length-1];
+ destFile = destFile+fileName;
+ }
+ fdw = fs.openSync(destFile, 'w');
+ bytesRead = 1;
+ pos = 0;
+ while (bytesRead > 0) {
+ //WARNING this creates an error on windows on latest node (.6.11), use .6.10 for now https://github.com/joyent/node/issues/2807
+ bytesRead = fs.readSync(fdr, buff, 0, BUF_LENGTH, pos);
+ fs.writeSync(fdw, buff, 0, bytesRead);
+ pos += bytesRead;
+ }
+ fs.closeSync(fdr);
+ return fs.closeSync(fdw);
+}
+
+function removeFolder(folder){
+ folder = platformProofPath(folder);
+ rmdirSyncRecursive(folder, true);
+}
+
+/* synchronously create sym link of */
+function createSymLink(src, dest){
+ src = platformProofPath(src);
+ dest = platformProofPath(dest);
+ if (!ISWIN){
+ //weird on mac the symlinks need this or won't work
+ src = '../' + src;
+ }
+ console.log('creating sym link src: ' + src + ' dest: ' + dest);
+ fs.symlinkSync(src, dest, 'dir');
+}
+
+/* correct the slashes in paths for alternate platforms */
+function platformProofPath(path){
+ if (ISWIN){
+ path = path.split('/').join('\\');
+ } else {
+ path = path.split('\\').join('/');
+ }
+ return path;
+}
+
+var delayedUpdateInt = NaN;
+function updateDebugIndexFile(now){
+ if (!now && isNaN(delayedUpdateInt)){
+ //prevent recompile from being called too often
+ delayedUpdateInt = setTimeout(function(){
+ delayedUpdateInt = NaN;
+ updateDebugIndexFile(true);
+ }, 50);
+ return;
+ }
+ copyFileSync('src/index.html', DEBUG_FOLDER + '/index.html');
+ if (TEMPLATES_DIRTY){
+ generateConcatedTemplates(true);
+ }
+ var indexHTML = fs.readFileSync(DEBUG_FOLDER + '/index.html', 'utf8');
+ indexHTML = indexHTML.replace(JS_START_MARKER, '')
+ .replace(JS_END_MARKER, '')
+ .replace(CSS_START_MARKER, '')
+ .replace(CSS_END_MARKER, '')
+ .split('scss"').join('css"')
+ .replace('@templates@', CONCATED_TEMPLATES)
+ .split('@cdnhost@').join('');
+ fs.writeFile(DEBUG_FOLDER + '/index.html', indexHTML, function(err) {
+ if(err) {
+ console.log('error writing to index template: ' + err);
+ }
+ });
+ console.log(' updated dev index.html');
+}
+
+// Builds prod index file by adding in compressed js, css, and templates.
+function updateProdIndexFile(){
+ console.log('building production index.html');
+ copyFileSync('src/index.html', PROD_FOLDER + '/index.html');
+ var indexHTML = fs.readFileSync(PROD_FOLDER + '/index.html', 'utf8')
+ .replace(/@javascript concat start@([\s\S]*?)@javascript concat end@/, '<script src="js/index.js"></script>')
+ .replace(/@css concat start@([\s\S]*?)@css concat end@/, '<link rel="stylesheet" href="css/index.css" />')
+ .replace('@templates@', CONCATED_TEMPLATES);
+ var writeErr = fs.writeFileSync(PROD_FOLDER + '/index.html', indexHTML);
+ if (writeErr){
+ console.log('error writing to index template: ' + writeErr);
+ }
+}
+
+function versionFilesToBucket(bucket){
+ var date = getDate();
+ var indexHTML = fs.readFileSync(PROD_FOLDER + '/index.html', 'utf8');
+
+ var cssText = fs.readFileSync(PROD_FOLDER + '/css/index.css', 'utf8');
+ cssText = cssText.split('@cdnhost@').join(bucket);
+ fs.writeFileSync(PROD_FOLDER + '/css/index.css', cssText)
+
+ // Add date timestamp to prod index.css and js.
+ fs.renameSync(PROD_FOLDER + '/js/index.js', PROD_FOLDER + '/js/index.' + date + '.js');
+ fs.renameSync(PROD_FOLDER + '/css/index.css', PROD_FOLDER + '/css/index.' + date + '.css');
+
+ // Update prod/index.html with the new renamed files.
+ indexHTML = indexHTML.replace('js/index.js', bucket + '/js/index.' + date + '.js')
+ .replace('css/index.css', bucket + '/css/index.' + date + '.css')
+ .split('@cdnhost@').join(bucket);
+
+ fs.writeFileSync(PROD_FOLDER + '/index.html', indexHTML);
+}
+
+// Recurses through a dir and outputs an array of files.
+function listFilesRecursive(sourceDir){
+ sourceDir = platformProofPath(sourceDir);
+ var files = [];
+ try {
+ var filesToCheck = fs.readdirSync(sourceDir);
+ for(var i = 0; i < filesToCheck.length; i++) {
+ var filePath = platformProofPath(sourceDir + filesToCheck[i]);
+ var currFile = fs.lstatSync(filePath);
+ if(currFile.isDirectory()) {
+ files = files.concat(listFilesRecursive(filePath+'/'));
+ } else {
+ files.push(sourceDir+filesToCheck[i]);
+ }
+ }
+ } catch (err) {
+ throw new Error(err.message);
+ }
+ return files;
+}
+
+//from https://github.com/ryanmcgrath/wrench-js/blob/master/lib/wrench.js
+/* wrench.copyDirSyncRecursive('directory_to_copy', 'new_directory_location', opts);
+ *
+ * Recursively dives through a directory and moves all its files to a new location. This is a
+ * Synchronous function, which blocks things until it's done. If you need/want to do this in
+ * an Asynchronous manner, look at wrench.copyDirRecursively() below.
+ *
+ * Note: Directories should be passed to this function without a trailing slash.
+ */
+copyDirSyncRecursive = function(sourceDir, newDirLocation) {
+ sourceDir = platformProofPath(sourceDir);
+ newDirLocation = platformProofPath(newDirLocation);
+ /* Create the directory where all our junk is moving to; read the mode of the source directory and mirror it */
+ var checkDir = fs.statSync(sourceDir);
+ fs.mkdirSync(newDirLocation, checkDir.mode);
+
+ var files = fs.readdirSync(sourceDir);
+
+ for(var i = 0; i < files.length; i++) {
+ var currFile = fs.lstatSync(sourceDir + '/' + files[i]);
+
+ if(currFile.isDirectory()) {
+ /* ...and then recursion this thing right on back. */
+ copyDirSyncRecursive(sourceDir + '/' + files[i], newDirLocation + '/' + files[i]);
+ } else if(currFile.isSymbolicLink()) {
+ var symlinkFull = fs.readlinkSync(sourceDir + '/' + files[i]);
+ fs.symlinkSync(symlinkFull, newDirLocation + '/' + files[i]);
+ } else {
+ /* At this point, we've hit a file actually worth copying... so copy it on over. */
+ var contents = fs.readFileSync(sourceDir + '/' + files[i]);
+ fs.writeFileSync(newDirLocation + '/' + files[i], contents);
+ }
+ }
+};
+
+// TODO: Use https://github.com/isaacs/rimraf
+//from https://github.com/ryanmcgrath/wrench-js/blob/master/lib/wrench.js
+/* wrench.rmdirSyncRecursive('directory_path', forceDelete, failSilent);
+ *
+ * Recursively dives through directories and obliterates everything about it. This is a
+ * Sync-function, which blocks things until it's done. No idea why anybody would want an
+ * Asynchronous version. :\
+ */
+rmdirSyncRecursive = function(path, failSilent) {
+ path = platformProofPath(path);
+ var files;
+
+ try {
+ files = fs.readdirSync(path);
+ } catch (err) {
+ if(failSilent) return;
+ throw new Error(err.message);
+ }
+
+ /* Loop through and delete everything in the sub-tree after checking it */
+ for(var i = 0; i < files.length; i++) {
+ var currFile = fs.lstatSync(path + '/' + files[i]);
+ console.log('checking: ' + path + '/' + files[i]);
+ if(currFile.isSymbolicLink()) {
+ // Unlink symlinks
+ fs.unlinkSync(path + '/' + files[i]);
+ } else if(currFile.isDirectory()){
+ // Recursive function back to the beginning
+ console.log('curFile is dir: ' + path + '/' + files[i] + ' sym: ' + currFile.isSymbolicLink());
+ rmdirSyncRecursive(path + '/' + files[i]);
+ } else {
+ // Assume it's a file - perhaps a try/catch belongs here?
+ fs.unlinkSync(path + '/' + files[i]);
+ }
+ }
+
+ /* Now that we know everything in the sub-tree has been deleted, we can delete the main
+ directory. Huzzah for the shopkeep. */
+ return fs.rmdirSync(path);
+};
+
+/****************************************/
+/* Tasks */
+/****************************************/
+
+desc('This is the default task which starts the dev watch script.');
+task('default', ['watch-dev'], function(params) {
+ console.log('Done with tasks.');
+});
+
+/****************/
+/* Build */
+/****************/
+namespace('build', function () {
+ desc('Does a dev build (but not deploy).');
+ task('dev', [], function(params) {
+ var cssContent, i;
+
+ // concat templates into the debug index html file
+ updateDebugIndexFile();
+
+ // Build css file list
+ parseCSSFileList();
+ for (i = 0, len = CSS_FILES.length; i < len; i++){
+ convertAndCopyCSSDev('src/' + CSS_FILES[i]);
+ }
+
+ complete();
+ },{
+ async : true
+ });
+
+ desc('Does a production build (but not deploy).');
+ task('prod', ['install:prod', 'templates:prod', 'css:prod', 'js:prod', 'version:prod'], function(params) {
+ console.log('Done with tasks.');
+ });
+
+ desc('Does a staging build (but not deploy).');
+ task('staging', ['install:prod', 'templates:prod', 'css:staging', 'js:prod', 'version:staging'], function(params) {
+ console.log('Done with tasks.');
+ });
+
+ desc('Does a qa build (but not deploy).');
+ task('qa', ['install:prod', 'templates:prod', 'css:qa', 'js:prod', 'version:qa'], function(params) {
+ console.log('Done with tasks.');
+});
+
+});
+
+/****************/
+/* Clean */
+/****************/
+namespace('clean', function () {
+desc('Remove prod folder so you can rebuild it.');
+ task('prod', [], function(params) {
+ console.log('Removing prod folder...');
+ removeFolder(PROD_FOLDER);
+});
+
+desc('Remove dev folder so you can rebuild it.');
+ task('dev', [], function(params) {
+ console.log('Removing dev folder...');
+ //WARNING with windows node.js (even latest .6.11) fs.lstatSync doesn't properly detect symbolic links on windows
+ //only this questionable mention under fs.Stats in official node documentation: stats.isSymbolicLink() (only valid with fs.lstat())
+ //this will cause passing a folder like below that has symlinks in it to a recusive delete function to delete the
+ //content inside of the symlink folders instead of the symlinks themselves. So for now, on windows, don't delete this folder :/
+ if (!ISWIN) removeFolder(DEBUG_FOLDER);
+ });
+});
+
+/****************/
+/* Install */
+/****************/
+namespace('install', function () {
+ desc('Create /deploy-dev and /deploy-prod folders and set up sym links..');
+ task('prod', [], function(params) {
+ directoryCheck(PROD_FOLDER, function(dir){
+ fs.mkdirSync(PROD_FOLDER + '/js');
+ fs.mkdirSync(PROD_FOLDER + '/css');
+ console.log('copying static folders');
+
+ [
+ ['src/images', '/images'],
+ ['src/sounds', '/sounds'],
+ ['src/polyfill', '/polyfill'],
+ ['src/webfonts', '/webfonts']
+ ].forEach(function(fileAr){
+ copyDirSyncRecursive(fileAr[0], PROD_FOLDER + fileAr[1]);
+});
+
+ [
+ 'src/apple-touch-icon-57x57-precomposed.png',
+ 'src/apple-touch-icon-72x72-precomposed.png',
+ 'src/apple-touch-icon-114x114-precomposed.png',
+ 'src/apple-touch-icon-precomposed.png',
+ 'src/apple-touch-icon.png',
+ 'src/favicon.ico',
+ 'src/robots.txt',
+ ].forEach(function(file){
+ copyFileSync(file, PROD_FOLDER + '/');
+ });
+
+ });
+ console.log('Created prod folders as needed. Run jake clean:prod for complete rebuild.');
+});
+
+desc('Create dev and prod folders and set up sym links..');
+ task('dev', [], function(params) {
+ directoryCheck(DEBUG_FOLDER, function(dir){
+
+ [
+ 'src/images',
+ 'src/sounds',
+ 'src/js',
+ 'src/polyfill',
+ 'src/webfonts',
+ ].forEach(function(file){
+ createSymLink(file, DEBUG_FOLDER + file.replace('src',''));
+ });
+
+ directoryCheck(DEBUG_FOLDER+'/css');
+
+ [
+ 'src/apple-touch-icon-57x57-precomposed.png',
+ 'src/apple-touch-icon-72x72-precomposed.png',
+ 'src/apple-touch-icon-114x114-precomposed.png',
+ 'src/apple-touch-icon-precomposed.png',
+ 'src/apple-touch-icon.png',
+ 'src/favicon.ico',
+ 'src/robots.txt',
+ ].forEach(function(file){
+ copyFileSync(file, DEBUG_FOLDER + '/');
+ });
+ });
+ console.log('Created dev folders as needed. Run jake clean:dev for complete rebuild.');
+ });
+});
+
+/****************/
+/* CSS */
+/****************/
+namespace('css', function () {
+ desc('Compress and prep all CSS into prod /css/index.css');
+ task('prod', [], function() {
+ console.log('Compressed CSS for prod');
+ compressCSS(APP_DOMAIN);
+ });
+
+ task('staging', [], function() {
+ console.log('Compressed CSS for staging');
+ compressCSS(APP_STAGING_DOMAIN);
+ });
+
+ task('qa', [], function() {
+ console.log('Compressed CSS for qa');
+ compressCSS(APP_QA_DOMAIN);
+ });
+});
+
+/****************/
+/* JS */
+/****************/
+namespace('js', function () {
+ desc('Compress all JS prod');
+ task('prod', [], function(params) {
+ console.log('Compressing JS for prod');
+ var concatFiles = concatJS();
+ //remove console references
+ concatFiles = concatFiles.replace(/console.(log|debug|info|warn|error|assert)(.apply)?\(.*\);?/g, '');
+ var uglifyFiles = uglifyJS(concatFiles);
+ fs.writeFileSync(PROD_FOLDER + '/js/index.js', uglifyFiles, 'utf8');
+ });
+});
+
+/****************/
+/* Templates */
+/****************/
+namespace('templates', function () {
+ desc('Concat templates into HTML files into prod index.html');
+ task('prod', [], function(params) {
+ console.log('Concating Templates for prod');
+ generateConcatedTemplates();
+ updateProdIndexFile();
+ });
+});
+
+/****************/
+/* Versioning */
+/****************/
+namespace('version', function () {
+ desc('Version timestamp CSS and JS filename for qa, and update html references.');
+ task('qa', [], function() {
+ versionFilesToBucket(S3_QA_BUCKET);
+ });
+
+ desc('Version timestamp CSS and JS filename for staging, and update html references.');
+ task('staging', [], function() {
+ versionFilesToBucket(S3_STAGING_BUCKET);
+ });
+
+ desc('Version timestamp CSS and JS filename for prod, and update html references.');
+ task('prod', [], function() {
+ versionFilesToBucket(S3_PROD_BUCKET);
+ });
+});
+
+/****************/
+/* deploy */
+/****************/
+namespace('deploy', function () {
+ // Runs jake clean:env build:env s3:env on remote server
+
+ desc('Update Staging env to latest codebase in master');
+ task('staging', [], function(params) {
+ console.log('Deploying to ' + APP_STAGING_DOMAIN + '...');
+ execLog("ssh " + STAGING_SERVER_USER + "@" + STAGING_SERVER_IP + " './bin/" + STAGING_DEPLOY_SCRIPT + "'");
+ });
+
+ desc('Update QA env to latest codebase in master');
+ task('qa', [], function(params) {
+ console.log('Deploying to ' + APP_QA_DOMAIN + '...');
+ execLog("ssh " + QA_SERVER_USER + "@" + QA_SERVER_IP + " './bin/" + QA_DEPLOY_SCRIPT + "'");
+ });
+
+ desc('Update QA env to latest codebase in stable');
+ task('qa-stable', [], function(params) {
+ console.log('Deploying stable to ' + APP_QA_DOMAIN + '...');
+ execLog("ssh " + QA_SERVER_USER + "@" + QA_SERVER_IP + " './bin/deploy-qa-from-stable.sh'");
+ });
+
+ desc('Update prod env to latest codebase in master');
+ task('prod', [], function(params) {
+ console.log('Deploying to ' + APP_DOMAIN + '...');
+ execLog("ssh " + PROD_SERVER_USER + "@" + PROD_SERVER_IP + " './bin/" + PROD_DEPLOY_SCRIPT + "'");
+ });
+
+ desc('Update prod env to latest codebase in stable');
+ task('prod-stable', [], function(params) {
+ console.log('Deploying stable to ' + APP_DOMAIN + '...');
+ execLog("ssh " + PROD_SERVER_USER + "@" + PROD_SERVER_IP + " './bin/deploy-prod-from-stable.sh'");
+ });
+});
+
+/****************/
+/* S3 */
+/****************/
+namespace('s3', function () {
+ desc('Upload QA assets to Amazon S3.');
+ task('qa', [], function(){
+ uploadMediaFoldersToS3(S3_QA_BUCKET);
+ });
+
+ desc('Upload Staging assets to Amazon S3.');
+ task('staging', [], function(){
+ uploadMediaFoldersToS3(S3_STAGING_BUCKET);
+ });
+
+ desc('Upload Prod assets to Amazon S3.');
+ task('prod', [], function(){
+ uploadMediaFoldersToS3(S3_PROD_BUCKET);
+ });
+});
+
+/****************/
+/* tests */
+/****************/
+namespace('tests', function () {
+ task('api', [], function(){
+ console.log('running api tests...');
+ exec('cd spec && jasmine-node --verbose samplespec.js', function(error, stdout, stderr){
+ if (stdout){
+ console.log(stdout);
+ complete();
+ }
+ if (stderr){
+ console.log('stderr: ' + stderr);
+ }
+ if (error !== null) {
+ console.log('exec error: ' + error);
+ }
+ });
+ },{
+ async : true
+ });
+});
+
+desc('Watch /src css, templates, and index.html for changes and update dev folder');
+task('watch-dev', ['clean:dev', 'install:dev'], function(params) {
+ console.log('Watching source files for changes to copy to dev');
+
+ var FILE_STATES = ['fileDeleted', 'fileCreated', 'fileModified'];
+
+ // Watch src index template for changes
+ NodeWatch.add('src/index.html').onChange(function(file,prev,curr){
+ console.log('src index.html template changed, updating dev folder');
+ updateDebugIndexFile();
+ });
+
+ // Watch root icons for changes
+ [
+ 'src/apple-touch-icon-57x57-precomposed.png',
+ 'src/apple-touch-icon-114x114-precomposed.png',
+ 'src/apple-touch-icon-114x114-precomposed.png',
+ 'src/apple-touch-icon-precomposed.png',
+ 'src/apple-touch-icon.png',
+ 'src/favicon.ico',
+ 'src/robots.txt',
+ ].forEach(function(file){
+ NodeWatch.add(file).onChange(function(file,prev,curr){
+ copyFileSync(file, DEBUG_FOLDER + '/');
+ });
+ })
+
+ // Watch changes to the templates folder and concat them into the debug index html file
+ var templateFolderWatcher = WatchTree.watchTree('src/templates', {
+ 'sample-rate' : 50,
+ match : '\.tmpl$'
+ });
+
+ FILE_STATES.forEach(function(status){
+ templateFolderWatcher.on(status, function(path) {
+ console.log('template ' + status + ': ' + path + '!');
+ TEMPLATES_DIRTY = true;
+ updateDebugIndexFile();
+ });
+ })
+
+ // Watch for changes to the /src/css directory, convert all css files over to dev css folder
+ //TODO: verify sub folders are being synced correctly
+ var scssFolderWatcher = WatchTree.watchTree('src/css', {
+ 'sample-rate' : 50,
+ match: '\.(css|scss)$'
+ });
+
+ FILE_STATES.concat('filePreexisted').forEach(function(status){
+ if (status == 'fileDeleted'){
+ // Also delete item from debug folder
+ scssFolderWatcher.on(status, function(path) {
+ console.log(' ' + status + ' ' + path + '!');
+ var filename = path.split('src/css/').join('').split('.scss').join('.css');
+ exec('rm ' + DEBUG_FOLDER + '/css/' + filename, function(error, stdout, stderr){
+ console.log(' removed from debug folder!');
+ util.puts(stdout);
+ });
+ });
+ }else{
+ scssFolderWatcher.on(status, function(path) {
+ console.log(status + ' ' + path + '!');
+ convertAndCopyCSSDev(path);
+ });
+ }
+ });
+
+ updateDebugIndexFile();
+});
+
+desc('Run all unit tests.');
+task('test', ['tests:api'], function(){
+ console.log('Finished tests.');
+});
+
+desc('Get date.');
+task('getDate', [], function(){
+ console.log(getDate());
+});
15 README.md
@@ -0,0 +1,15 @@
+# dontbreak.me source code
+
+This is sample project launched during [HTML5 Dev Conf](http://www.html5devconf.com/) to show off the architecture and design of a responsive Backbone application. See it live at [http://www.dontbreak.me](http://www.dontbreak.me). It is based on [this](http://lifehacker.com/5886128/how-seinfelds-productivity-secret-fixed-my-procrastination-problem) concept. When deployed it currently runs entirely on the front end and has no backend requirements.
+
+It is built on top of the [Mandible2](https://github.com/BrainSwap/Mandible2) development framework which allows you to compress and push assets to Amazon S3 among other things. Mandible2 requires some libraries like [Node](http://nodejs.org/) and [Jake](http://howtonode.org/intro-to-jake). Visit the Mandible2 page to learn more.
+
+This app is in under constant enhancement. The latest "edge" version of this app can be found at [http://qa.dontbreak.me](http://qa.dontbreak.me).
+
+# How to Install
+
+Clone this repo. For an explanation of the libraries required and folder architecture see [Mandible2](https://github.com/BrainSwap/Mandible2).
+
+# How to Deploy
+
+In order to deploy this app to production and qa enviornments you must fill out your Amazon S3 information at: `/jakelib/aws.jake`
6 jakelib/aws.jake
@@ -0,0 +1,6 @@
+// This file contains Amazon Web Services private keys. Don't check this into a public repository.
+// git update-index --assume-unchanged aws.jake
+global.S3 = {
+ KEY : '',
+ SECRET : ' '
+}
169 node_modules/NodeInterval/README.org
@@ -0,0 +1,169 @@
+: _ _ _ _
+: _ __ ___ __| | ___(_)_ __ | |_ ___ _ ____ ____ _| |
+: | '_ \ / _ \ / _` |/ _ \ | '_ \| __/ _ \ '__\ \ / / _` | |
+: | | | | (_) | (_| | __/ | | | | || __/ | \ V / (_| | |
+: |_| |_|\___/ \__,_|\___|_|_| |_|\__\___|_| \_/ \__,_|_|
+:
+: Automation for lazy people.
+
+* What it does
+
+NodeInterval allows you to move your JavaScript templates out of your html
+page and into organized files and folders. When ran, it collects all the files
+in your templates folders, orders them alphabetically by script id, and inserts
+them into a document of your choice.
+
+NodeInterval contains a watch option that enables you to leave it running in a
+seperate tab to automatically run the above process anytime a template has been
+modified.
+
+NodeInterval is commonly used in [[http://documentcloud.github.com/backbone/][Backbone]], [[http://maccman.github.com/spine/][Spine]], and similiar web application
+frameworks that rely heavily on embedded templates.
+
+Think of it as a simple version of [[http://sass-lang.com/][Sass]] for your JavaScript templates.
+
+* Features
+
+1. Works with all templating solutions including [[http://documentcloud.github.com/underscore/][underscore.js]] and [[http://api.jquery.com/category/plugins/templates/][jQuery templates]]. (The only requirement is that the template needs to have an id property since they are oganized alphabetically.)
+2. Hidden files inside of template folder (files that begin with "." like .git, .svn, and .DS_Store) are automatically ignored.
+3. Profiles the time it takes to render all templates.
+4. Supports multiple sets of input and output files (see example in =sample/nodeinterval.js=).
+5. Can be used with build tools like [[http://www.gnu.org/software/make/][Gnu Make]] and [[http://ant.apache.org/][Apache Ant]].
+6. Has clean, date-stamped, and color-coded command-line output:
+: $ node nodeinterval.js
+: 18 Aug 01:47:49 - INFO: NodeInterval is watching for changes. Press Ctrl-C to stop.
+: 18 Aug 01:47:49 - INFO: overwrite ../assets/index.html
+: 18 Aug 01:47:49 - INFO: Completed in 0.001 seconds.
+: 18 Aug 01:48:04 - INFO: >>> Change detected to: ~/git/projectName/src/templates/signon/signon.tmpl
+: 18 Aug 01:48:04 - INFO: overwrite ../assets/index.html
+: 18 Aug 01:48:04 - INFO: Completed in 0.002 seconds.
+
+* Requirements
+- You must have [[http://nodejs.org/][nodejs]] installed.
+- [[http://npmjs.org/][NPM]] (Node package Manager) if you want to install this package using it.
+* Installing and using
+
+Note: Your application should have a clean folders architecture differentiating
+compiled vs source files. See the section below on "Sample web application
+layout" for a decent one. It's a lot simpler than it sounds.
+
+There are two ways to install: Cloning this repo or using [[http://npmjs.org/][npm]]. Once you have the
+package installed feel free to modify and use the sample script in the =samples=
+directory.
+
+: cd bin # Your scripts directory.
+: npm install NodeInterval
+: cp node_modules/nodeinterval/samples/nodeinterval.sh .
+: emacs nodeinterval.sh # Setup your variable paths with your favorite editor.
+: chmod u+x nodeinterval.sh
+: ./nodeinterval.sh # Start watching your files
+
+Alternatively, you can use the sample .js file instead of the .sh version and
+run it like so:
+
+: node nodeinterval.js
+
+* Usage
+
+It's best to create a Node or shell script that you can call via command line. A
+copy of this example is included in the =samples= directory.
+
+: var Nodeinterval = require('nodeinterval'),
+: ni = new Nodeinterval.Watcher({
+: inputFile: '../path/to/raw/index.html',
+: outputFile: '../path/to/rendered/index.html',
+: replacementString: '@templates@',
+: watchFolder: '../path/to/templates/'
+: }).startWatch();
+
+Options you can pass to =new Nodeinterval.Watcher= are:
+
+| Option | Description | default |
+|---------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------|
+| =inputFile= | is the main template you want to duplicate. | '../src/index.html' |
+| =outputFile= | is the relative or absolute path to the file you want to create (that's a copy of =inputFile= with the =replacementString= replaced with all your templates. | '../assets/index.html' |
+| =replacementString= | is the string inside of =inputFile= that you want to replace with all your templates. | '@templates@' |
+| =watchFolder= | is the relative or absolute path to the folder you want to monitor. | '../src/templates/' |
+
+Once a new =Nodeinterval.Watcher= is created. It has the additional api methods. Some methods are chainable.
+
+| Method | Description |
+|-------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| startWatch | Turns on the monitoring service. All files in =watchFolder= are now being watched for changes. Chainable. |
+| stopWatch | All files in =watchFolder= are now not being watched. Chainable. |
+| updateIndex | This is called internally anytime a change is detected. Replaces =outputFile= with a version of =inputFile= with =replacementString= replaced with contents of =watchFolder=. |
+| | |
+
+NodeInterval can also watch multiple input and output files. Just use an array
+to specify filenames under =inputFile= and =outputFile=. This is good, for
+example, where you have two sets of html files, one for uncompressed js and css
+and one for compressed css and js, and you want both files to render your
+templates on change.
+
+* Sample web application layout
+
+If you don't have a good web application layout. Here's a good one to follow:
+
+: .
+: ├── assets <== Your compressed assets, ready for production.
+: │   ├── images
+: │   ├── index.html <== "Built" html file with your rendered templates.
+: │   ├── js
+: │   └── css
+: ├── bin <== Shell scripts. "npm install nodeinterval" here.
+: │   ├── node_modules <== This folder will automatically be created.
+: │   │   └── nodeinterval <== nodeinterval and it's dependencies will be
+: │   │  installed here.
+: │   ├── nodeinterval.sh <== This sample file (and the .js) version is inside
+: │   │  of nodeinterval/samples/. Use it if you like.
+: │   └── sasswatch.sh <== I like to create a Sass executable for watching
+: │   my CSS files as well. (not part of this project)
+: └── src <== Raw uncompressed code here, where you should be
+: │ editing your codez.
+: ├── index.html <== Raw index.html files with "replacementString"
+: │ where you want the templates.
+: ├── css <== Uncompressed CSS assets.
+: ├── js <== Uncompressed JS assets.
+: └── templates <== Your .js templates. These can be all in one
+: folder or seperated out into many folder deep,
+: according to section. Incude the <script> part
+: in your templates.
+
+* Running the unit tests
+- NodeInterval's unit tests are written using [[http://vowsjs.org/][Vows]].
+- If you want to run the unit tests, you must clone this repo and not use the NPM version.
+
+: cd tests; node run-tests.js
+
+* Change log
+- 2011-10-05 - 0.0.7
+ - NodeInterval now works with build tools like [[http://www.gnu.org/software/make/][Make]] and [[http://ant.apache.org/][Ant]]. To support this NodeInterval no longer starts the watch process until =NodeInterval.startWatch= is ran.
+ - Update sample scripts to contain a --watch option to opt in on monitoring files. If not specified runs only once and quits.
+ - Update unit-tests to support the above.
+ - Turn off monitoring in unit-tests when the test is done.
+- 2011-09-18 - 0.0.6
+ - Templates are now outputted in alphabetical order by script id. This adds consistency to commit diffs among other things.
+- 2011-09-18 - 0.0.5
+ - Now supports watching multiple set of input and output files (use an array under =inputFile= and =outputFile=.
+- 2011-08-22 - 0.0.4
+ - Added improved way of instantiating (new NodeInterval.Watcher), see docs.
+ - Added init defaults if you don't pass them.
+ - New APIs: .startWatch, .stopWatch
+ - Added [[http://vowsjs.org/][Vows]] unit tests. (=cd tests; node run-tests.js= to run)
+- 2011-08-18 - 0.0.1 - First version
+
+* Known issues:
+- NodeInterval currently doesn't watch for new files or know when an existing file is removed. You should probably stop (ctrl-c) and start NodeInterval again when adding or removing a new template. This feature will be added in a future version.
+
+* Thanks
+
+Internally NodeInterval uses the following (included) node packages:
+[[https://github.com/DelvarWorld/Simple-Node-Logger][Simple-Node-Logger]], [[https://github.com/jorritd/node-watch][node-watch]], and [[https://github.com/documentcloud/underscore][underscore]].
+
+* Contribute
+
+Feedback and contributions (via pull requests) are more than welcome. Please add
+a test to the unit tests if it's a new feature. NodeInterval is really young and
+mostly written in one night. I'll be updating it with features as I use it in my
+daily projects.
+
171 node_modules/NodeInterval/index.js
@@ -0,0 +1,171 @@
+var fs = require('fs'),
+ _ = require('underscore'),
+ watch = require('nodewatch'),
+ log = require('simple-logger');
+
+(function(NodeInterval){
+ NodeInterval.Watcher = function(options){
+ var that = this;
+ this.options = _.extend(this.defaults, options);
+ _(this).extend(this.options);
+
+ this.watchFiles = [];
+
+ // TODO: We should store the current set of files in an array for future
+ // comparing.
+ _.each(this.getFilesFrom(this.watchFolder), function(file){
+ that.watchFiles.push(file);
+ });
+
+ // Also watch the input file for changes.
+ // TODO: If inputFile changes, unwatch removed file, or watch new file.
+ if (options.inputFile instanceof Array){
+ _.each(options.inputFile, function(file){
+ that.watchFiles.push(file);
+ });
+ }else{
+ this.watchFiles.push(options.inputFile);
+ }
+
+ // Render files on start.
+ log.log_level = 'info';
+ this.updateIndex();
+ return this;
+ }
+
+ _.extend(NodeInterval.Watcher.prototype, {
+ defaults: {
+ watchFolder: '../src/templates/',
+ inputFile: '../src/html/index.html',
+ replacementString: '@templates@',
+ outputFile: '../assets/index.html'
+ },
+
+ startWatch: function(){
+ var that = this;
+
+ _.each(this.watchFiles, function(file){
+ watch.add(file);
+ });
+
+ log.info('NodeInterval is watching for changes. Press Ctrl-C to stop.');
+
+ // Start the watch change listener.
+ watch.onChange(function(file, prevTime, currTime){
+ log.info('>>> Change detected to:', file);
+ that.updateIndex();
+ });
+ return this;
+ },
+
+ stopWatch: function(){
+ watch.clearListeners();
+
+ _.each(this.watchFiles, function(file){
+ watch.remove(file);
+ });
+ return this;
+ },
+
+ // Recursively return an array of all files (paths) in a directory.
+ getFilesFrom: function(dir){
+ var filesAr = [];
+ var recurse;
+
+ // Normalize path.
+ if (dir[dir.length-1] !== '/'){
+ dir = dir + '/';
+ }
+
+ recurse = function(dir){
+ var arr = fs.readdirSync(dir);
+ if (arr.length){
+ _.each(arr, function(fileOrDir){
+ if (fileOrDir[0] !== '.'){
+
+ if (fs.lstatSync(dir + fileOrDir).isDirectory()){
+ recurse(dir + fileOrDir + '/');
+ }else{
+ filesAr.push(dir + fileOrDir);
+ }
+ }
+ });
+ }
+ }
+ recurse(dir);
+ return filesAr;
+ },
+
+ // Get the text contents of a file.
+ getFileContents: function(file){
+ return fs.readFileSync(file, 'utf8');
+ },
+
+ // Concat an array of files together into a single string.
+ stringFromFiles: function(filesAr){
+ var that = this;
+ var str = '';
+ _.each(filesAr, function(file){
+ str += that.getFileContents(file);
+ });
+ return str;
+ },
+
+ // Write a string to a file.
+ writeToFile: function(file, str){
+ log.info('overwrite', file);
+ fs.writeFileSync(file, str, 'utf8');
+ },
+
+ // Update target page with new templates.
+ updateIndex: function(){
+ var date = new Date();
+ // Ar of sorted ids from each template.
+ var idsAr = [];
+ // Lookup hash of ids to template content.
+ var fileHash = {};
+ // Final concated text content.
+ var content = '';
+ var template;
+ var f;
+
+ // For each file get its template id so we can sort alphabetically.
+ _.each(this.getFilesFrom(this.watchFolder), function(file, i, list){
+ var contents = this.getFileContents(file);
+ var id = contents.match(/id=['"](.*)['"]/);
+ if (id){
+ id = id[1];
+ // Add to filehash
+ fileHash[id] = contents;
+ idsAr.push(id);
+ }else{
+ throw new Error('Template missing id: ' + file);
+ }
+ }, this);
+
+ idsAr.sort();
+
+ _.each(idsAr, function(id){
+ content += fileHash[id];
+ });
+
+ if (this.outputFile instanceof Array){
+ for (f = 0; f < this.outputFile.length; f++){
+ template = this.getFileContents(this.inputFile[f]);
+ template = template.replace(this.replacementString, content);
+
+ this.writeToFile(this.outputFile[f], template);
+ }
+ }else{
+ template = this.getFileContents(this.inputFile);
+ template = template.replace(this.replacementString, content);
+
+ this.writeToFile(this.outputFile, template);
+ }
+
+ log.info('Completed in ' + ((new Date() - date) / 1000) + ' seconds.');
+ }
+ });
+
+ module.exports = NodeInterval;
+})({});
186 node_modules/NodeInterval/node_modules/nodewatch/docs/docco.css
@@ -0,0 +1,186 @@
+/*--------------------- Layout and Typography ----------------------------*/
+body {
+ font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;
+ font-size: 15px;
+ line-height: 22px;
+ color: #252519;
+ margin: 0; padding: 0;
+}
+a {
+ color: #261a3b;
+}
+ a:visited {
+ color: #261a3b;
+ }
+p {
+ margin: 0 0 15px 0;
+}
+h1, h2, h3, h4, h5, h6 {
+ margin: 0px 0 15px 0;
+}
+ h1 {
+ margin-top: 40px;
+ }
+#container {
+ position: relative;
+}
+#background {
+ position: fixed;
+ top: 0; left: 525px; right: 0; bottom: 0;
+ background: #f5f5ff;
+ border-left: 1px solid #e5e5ee;
+ z-index: -1;
+}
+#jump_to, #jump_page {
+ background: white;
+ -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777;
+ -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px;
+ font: 10px Arial;
+ text-transform: uppercase;
+ cursor: pointer;
+ text-align: right;
+}
+#jump_to, #jump_wrapper {
+ position: fixed;
+ right: 0; top: 0;
+ padding: 5px 10px;
+}
+ #jump_wrapper {
+ padding: 0;
+ display: none;
+ }
+ #jump_to:hover #jump_wrapper {
+ display: block;
+ }
+ #jump_page {
+ padding: 5px 0 3px;
+ margin: 0 0 25px 25px;
+ }
+ #jump_page .source {
+ display: block;
+ padding: 5px 10px;
+ text-decoration: none;
+ border-top: 1px solid #eee;
+ }
+ #jump_page .source:hover {
+ background: #f5f5ff;
+ }
+ #jump_page .source:first-child {
+ }
+table td {
+ border: 0;
+ outline: 0;
+}
+ td.docs, th.docs {
+ max-width: 450px;
+ min-width: 450px;
+ min-height: 5px;
+ padding: 10px 25px 1px 50px;
+ overflow-x: hidden;
+ vertical-align: top;
+ text-align: left;
+ }
+ .docs pre {
+ margin: 15px 0 15px;
+ padding-left: 15px;
+ }
+ .docs p tt, .docs p code {
+ background: #f8f8ff;
+ border: 1px solid #dedede;
+ font-size: 12px;
+ padding: 0 0.2em;
+ }
+ .pilwrap {
+ position: relative;
+ }
+ .pilcrow {
+ font: 12px Arial;
+ text-decoration: none;
+ color: #454545;
+ position: absolute;
+ top: 3px; left: -20px;
+ padding: 1px 2px;
+ opacity: 0;
+ -webkit-transition: opacity 0.2s linear;
+ }
+ td.docs:hover .pilcrow {
+ opacity: 1;
+ }
+ td.code, th.code {
+ padding: 14px 15px 16px 25px;
+ width: 100%;
+ vertical-align: top;
+ background: #f5f5ff;
+ border-left: 1px solid #e5e5ee;
+ }
+ pre, tt, code {
+ font-size: 12px; line-height: 18px;
+ font-family: Monaco, Consolas, "Lucida Console", monospace;
+ margin: 0; padding: 0;
+ }
+
+
+/*---------------------- Syntax Highlighting -----------------------------*/
+td.linenos { background-color: #f0f0f0; padding-right: 10px; }
+span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
+body .hll { background-color: #ffffcc }
+body .c { color: #408080; font-style: italic } /* Comment */
+body .err { border: 1px solid #FF0000 } /* Error */
+body .k { color: #954121 } /* Keyword */
+body .o { color: #666666 } /* Operator */
+body .cm { color: #408080; font-style: italic } /* Comment.Multiline */
+body .cp { color: #BC7A00 } /* Comment.Preproc */
+body .c1 { color: #408080; font-style: italic } /* Comment.Single */
+body .cs { color: #408080; font-style: italic } /* Comment.Special */
+body .gd { color: #A00000 } /* Generic.Deleted */
+body .ge { font-style: italic } /* Generic.Emph */
+body .gr { color: #FF0000 } /* Generic.Error */
+body .gh { color: #000080; font-weight: bold } /* Generic.Heading */
+body .gi { color: #00A000 } /* Generic.Inserted */
+body .go { color: #808080 } /* Generic.Output */
+body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
+body .gs { font-weight: bold } /* Generic.Strong */
+body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+body .gt { color: #0040D0 } /* Generic.Traceback */
+body .kc { color: #954121 } /* Keyword.Constant */
+body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */
+body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */
+body .kp { color: #954121 } /* Keyword.Pseudo */
+body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */
+body .kt { color: #B00040 } /* Keyword.Type */
+body .m { color: #666666 } /* Literal.Number */
+body .s { color: #219161 } /* Literal.String */
+body .na { color: #7D9029 } /* Name.Attribute */
+body .nb { color: #954121 } /* Name.Builtin */
+body .nc { color: #0000FF; font-weight: bold } /* Name.Class */
+body .no { color: #880000 } /* Name.Constant */
+body .nd { color: #AA22FF } /* Name.Decorator */
+body .ni { color: #999999; font-weight: bold } /* Name.Entity */
+body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
+body .nf { color: #0000FF } /* Name.Function */
+body .nl { color: #A0A000 } /* Name.Label */
+body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
+body .nt { color: #954121; font-weight: bold } /* Name.Tag */
+body .nv { color: #19469D } /* Name.Variable */
+body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
+body .w { color: #bbbbbb } /* Text.Whitespace */
+body .mf { color: #666666 } /* Literal.Number.Float */
+body .mh { color: #666666 } /* Literal.Number.Hex */
+body .mi { color: #666666 } /* Literal.Number.Integer */
+body .mo { color: #666666 } /* Literal.Number.Oct */
+body .sb { color: #219161 } /* Literal.String.Backtick */
+body .sc { color: #219161 } /* Literal.String.Char */
+body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */
+body .s2 { color: #219161 } /* Literal.String.Double */
+body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
+body .sh { color: #219161 } /* Literal.String.Heredoc */
+body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
+body .sx { color: #954121 } /* Literal.String.Other */
+body .sr { color: #BB6688 } /* Literal.String.Regex */
+body .s1 { color: #219161 } /* Literal.String.Single */
+body .ss { color: #19469D } /* Literal.String.Symbol */
+body .bp { color: #954121 } /* Name.Builtin.Pseudo */
+body .vc { color: #19469D } /* Name.Variable.Class */
+body .vg { color: #19469D } /* Name.Variable.Global */
+body .vi { color: #19469D } /* Name.Variable.Instance */
+body .il { color: #666666 } /* Literal.Number.Integer.Long */
167 node_modules/NodeInterval/node_modules/nodewatch/docs/watch.html
@@ -0,0 +1,167 @@
+<!DOCTYPE html> <html> <head> <title>watch.js</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <table cellpadding="0" cellspacing="0"> <thead> <tr> <th class="docs"> <h1> watch.js </h1> </th> <th class="code"> </th> </tr> </thead> <tbody> <tr id="section-1"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-1">&#182;</a> </div> <p><a href="https://github.com/jorritd/node-watch">Node-watch</a> Is a small <a href="http://www.nodejs.org/">nodejs</a>
+module/lib to watch for filechanges.
+Filechanges are changes where the mtime (make-time) of file's changes.
+This situation happens when a file is overwritten. (e.g. safed)
+It uses the standard nodejs <a href="http://nodejs.org/docs/v0.4.8/api/events.html#events.EventEmitter">EventEmitter</a> as base and therefor has all the methods
+and properties an EventEmitter has. </p>
+
+<h4>Install:</h4>
+
+<p>Local (in "./node_modules"):</p>
+
+<pre><code>npm install nodewatch
+</code></pre>
+
+<p>Global :</p>
+
+<pre><code>npm install nodewatch -g
+</code></pre>
+
+<h4>Usage:</h4>
+
+<pre><code>var watch = require('nodewatch');
+
+// Adding 2 dirs relative from process.cwd()
+// Adding Abolute paths works as well
+// (Nested dirs are not watched)
+// and add the callback
+watch.add("./spec").add("./lib/watch")
+.onChange(function(file,prev,curr){
+ console.log(file);
+ console.log(prev.mtime);
+ console.log(curr,mtime);
+});
+
+// Clear (remove) the listeners
+watch.clearListeners();
+
+// Remove dirs to watch
+watch.remove("./spec").remove("./lib/watch");
+</code></pre> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-2"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-2">&#182;</a> </div> <p><em>nodejs requirements: EventEmitter, fs, path</em></p> </td> <td class="code"> <div class="highlight"><pre><span class="kd">var</span> <span class="nx">EventEmitter</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&quot;events&quot;</span><span class="p">).</span><span class="nx">EventEmitter</span><span class="p">,</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&quot;fs&quot;</span><span class="p">),</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&quot;path&quot;</span><span class="p">);</span></pre></div> </td> </tr> <tr id="section-3"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-3">&#182;</a> </div> <p><em>private helper function:</em>
+extends child with the prototypes of parent and return the extended child </p> </td> <td class="code"> <div class="highlight"><pre><span class="kd">var</span> <span class="nx">__hasProp</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">,</span> <span class="nx">__extends</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">child</span><span class="p">,</span> <span class="nx">parent</span><span class="p">)</span> <span class="p">{</span>
+ <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">key</span> <span class="k">in</span> <span class="nx">parent</span><span class="p">)</span> <span class="p">{</span>
+ <span class="k">if</span> <span class="p">(</span><span class="nx">__hasProp</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">parent</span><span class="p">,</span> <span class="nx">key</span><span class="p">))</span> <span class="p">{</span>
+ <span class="nx">child</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span> <span class="o">=</span> <span class="nx">parent</span><span class="p">[</span><span class="nx">key</span><span class="p">];</span>
+ <span class="p">}</span>
+ <span class="p">}</span>
+ <span class="kd">function</span> <span class="nx">ctor</span><span class="p">()</span> <span class="p">{</span>
+ <span class="k">this</span><span class="p">.</span><span class="nx">constructor</span> <span class="o">=</span> <span class="nx">child</span><span class="p">;</span>
+ <span class="p">}</span>
+ <span class="nx">ctor</span><span class="p">.</span><span class="nx">prototype</span> <span class="o">=</span> <span class="nx">parent</span><span class="p">.</span><span class="nx">prototype</span><span class="p">;</span>
+ <span class="nx">child</span><span class="p">.</span><span class="nx">prototype</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ctor</span><span class="p">;</span>
+ <span class="nx">child</span><span class="p">.</span><span class="nx">__super__</span> <span class="o">=</span> <span class="nx">parent</span><span class="p">.</span><span class="nx">prototype</span><span class="p">;</span>
+ <span class="k">return</span> <span class="nx">child</span><span class="p">;</span>
+<span class="p">};</span></pre></div> </td> </tr> <tr id="section-4"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-4">&#182;</a> </div> <h2>Watch class declaration</h2>
+
+<p>extends from <a href="http://nodejs.org/docs/v0.4.8/api/events.html#events.EventEmitter">EventEmitter</a></p> </td> <td class="code"> <div class="highlight"><pre><span class="kd">var</span> <span class="nx">WatchClass</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
+ <span class="s2">&quot;use strict&quot;</span><span class="p">;</span>
+ <span class="nx">__extends</span><span class="p">(</span><span class="nx">Watch</span><span class="p">,</span> <span class="nx">EventEmitter</span><span class="p">);</span></pre></div> </td> </tr> <tr id="section-5"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-5">&#182;</a> </div> <h3>PUBLIC METHODS</h3>
+
+<hr /> </td> <td class="code"> <div class="highlight"><pre> </pre></div> </td> </tr> <tr id="section-6"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-6">&#182;</a> </div> <h2>Watch class Constructor</h2> </td> <td class="code"> <div class="highlight"><pre>
+ <span class="kd">function</span> <span class="nx">Watch</span><span class="p">(</span><span class="nx">options</span><span class="p">)</span> <span class="p">{}</span>
+ </pre></div> </td> </tr> <tr id="section-7"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-7">&#182;</a> </div> <h2>Public method: add(string)</h2>
+
+<p>String is an absolute or relative path
+to a file or dir to add (watch),
+returns this object</p> </td> <td class="code"> <div class="highlight"><pre>
+ <span class="nx">Watch</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">add</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">str_file_or_path</span><span class="p">)</span> <span class="p">{</span>
+ <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">__handle</span><span class="p">(</span><span class="kc">true</span><span class="p">,</span> <span class="nx">str_file_or_path</span><span class="p">);</span>
+ <span class="p">};</span>
+ </pre></div> </td> </tr> <tr id="section-8"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-8">&#182;</a> </div> <h2>Public method: remove(string)</h2>
+
+<p>String is a absolute or relative path
+to a file or dir to remove (unwatch),
+returns this object</p> </td> <td class="code"> <div class="highlight"><pre>
+ <span class="nx">Watch</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">remove</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">str_file_or_path</span><span class="p">)</span> <span class="p">{</span>
+ <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">__handle</span><span class="p">(</span><span class="kc">false</span><span class="p">,</span> <span class="nx">str_file_or_path</span><span class="p">);</span>
+ <span class="p">};</span>
+ </pre></div> </td> </tr> <tr id="section-9"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-9">&#182;</a> </div> <h2>Public method: onChange(callback)</h2>
+
+<p>Todo: check if <em>cb</em> is a function</p>
+
+<p>add a callback <em>cb</em> :</p>
+
+<pre><code> function(file,prev,curr){
+ /* do something with file,prev,curr */
+ };
+</code></pre>
+
+<p>When an event triggers 3 arguments are send to the callback listener</p>
+
+<ul>
+<li>file String, absolute filepath</li>
+<li>prev <a href="http://nodejs.org/docs/v0.4.8/api/fs.html#fs.watchFile">stats objects</a></li>
+<li>curr <a href="http://nodejs.org/docs/v0.4.8/api/fs.html#fs.watchFile">stats objects</a></li>
+</ul>
+
+<p>and return <em>this</em> object</p> </td> <td class="code"> <div class="highlight"><pre>
+ <span class="nx">Watch</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">onChange</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">cb</span><span class="p">)</span> <span class="p">{</span>
+ <span class="k">this</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s2">&quot;change&quot;</span><span class="p">,</span> <span class="nx">cb</span><span class="p">);</span>
+ <span class="k">return</span> <span class="k">this</span><span class="p">;</span>
+ <span class="p">};</span>
+ </pre></div> </td> </tr> <tr id="section-10"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-10">&#182;</a> </div> <h3>Public method: clearListeners()</h3> </td> <td class="code"> <div class="highlight"><pre>
+ <span class="nx">Watch</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">clearListeners</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
+ <span class="k">this</span><span class="p">.</span><span class="nx">removeAllListeners</span><span class="p">(</span><span class="s2">&quot;change&quot;</span><span class="p">);</span>
+ <span class="k">return</span> <span class="k">this</span><span class="p">;</span>
+ <span class="p">};</span>
+ </pre></div> </td> </tr> <tr id="section-11"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-11">&#182;</a> </div> <h3>PRIVATE METHODS</h3>
+
+<hr /> </td> <td class="code"> <div class="highlight"><pre> </pre></div> </td> </tr> <tr id="section-12"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-12">&#182;</a> </div> <h2>Private method: __handle(boolean, string)</h2>
+
+<p>String is a absolute or relative path
+to a file or dir. </p>
+
+<p>First <em>str_file_or_path</em> is normalized as a valid path, relative paths
+are made absolute depending on process.cwd()</p>
+
+<p>The boolean <em>add</em> (true == add, false == remove) is passed to
+the <em>_file or _</em>dir method</p>
+
+<p>returns this object</p> </td> <td class="code"> <div class="highlight"><pre>
+ <span class="nx">Watch</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">__handle</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">add</span><span class="p">,</span> <span class="nx">str_file_or_path</span><span class="p">)</span> <span class="p">{</span>
+ <span class="k">if</span> <span class="p">(</span><span class="nx">str_file_or_path</span><span class="p">.</span><span class="nx">substring</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="s2">&quot;.&quot;</span><span class="p">)</span> <span class="p">{</span>
+ <span class="nx">str_file_or_path</span> <span class="o">=</span> <span class="nx">process</span><span class="p">.</span><span class="nx">cwd</span><span class="p">()</span> <span class="o">+</span> <span class="s2">&quot;/&quot;</span> <span class="o">+</span> <span class="nx">str_file_or_path</span><span class="p">;</span>
+ <span class="p">}</span>
+ <span class="nx">str_file_or_path</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">normalize</span><span class="p">(</span><span class="nx">str_file_or_path</span><span class="p">);</span>
+ <span class="k">if</span> <span class="p">(</span><span class="nx">fs</span><span class="p">.</span><span class="nx">statSync</span><span class="p">(</span><span class="nx">str_file_or_path</span><span class="p">).</span><span class="nx">isFile</span><span class="p">())</span> <span class="p">{</span>
+ <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">__file</span><span class="p">(</span><span class="nx">add</span><span class="p">,</span> <span class="nx">str_file_or_path</span><span class="p">);</span>
+ <span class="p">}</span>
+ <span class="k">if</span> <span class="p">(</span><span class="nx">fs</span><span class="p">.</span><span class="nx">statSync</span><span class="p">(</span><span class="nx">str_file_or_path</span><span class="p">).</span><span class="nx">isDirectory</span><span class="p">())</span> <span class="p">{</span>
+ <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">__dir</span><span class="p">(</span><span class="nx">add</span><span class="p">,</span> <span class="nx">str_file_or_path</span><span class="p">);</span>
+ <span class="p">}</span>
+ <span class="p">};</span>
+ </pre></div> </td> </tr> <tr id="section-13"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-13">&#182;</a> </div> <h2>Private method: __dir(boolean, string)</h2>
+
+<p>walk a dir and pass the files with the add boolean</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nx">Watch</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">__dir</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">add</span><span class="p">,</span> <span class="nx">dir</span><span class="p">)</span> <span class="p">{</span>
+ <span class="kd">var</span> <span class="nx">files</span> <span class="o">=</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">readdirSync</span><span class="p">(</span><span class="nx">dir</span><span class="p">);</span>
+ <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">files</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
+ <span class="kd">var</span> <span class="nx">full_path</span> <span class="o">=</span> <span class="nx">dir</span> <span class="o">+</span> <span class="s2">&quot;/&quot;</span> <span class="o">+</span> <span class="nx">files</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>
+ <span class="k">if</span> <span class="p">(</span><span class="nx">fs</span><span class="p">.</span><span class="nx">statSync</span><span class="p">(</span><span class="nx">full_path</span><span class="p">).</span><span class="nx">isFile</span><span class="p">())</span> <span class="p">{</span>
+ <span class="k">this</span><span class="p">.</span><span class="nx">__file</span><span class="p">(</span><span class="nx">add</span><span class="p">,</span> <span class="nx">full_path</span><span class="p">);</span>
+ <span class="p">}</span>
+ <span class="p">}</span>
+ <span class="k">return</span> <span class="k">this</span><span class="p">;</span>
+ <span class="p">};</span>
+ </pre></div> </td> </tr> <tr id="section-14"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-14">&#182;</a> </div> <h2>Private method: __file(boolean, string)</h2>
+
+<p>Finally add (add==true) or remove a
+file from watching</p> </td> <td class="code"> <div class="highlight"><pre>
+ <span class="nx">Watch</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">__file</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">add</span><span class="p">,</span> <span class="nx">file</span><span class="p">)</span> <span class="p">{</span>
+ <span class="kd">var</span> <span class="nx">self</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span>
+ <span class="k">if</span> <span class="p">(</span><span class="nx">add</span><span class="p">)</span> <span class="p">{</span>
+ <span class="nx">fs</span><span class="p">.</span><span class="nx">watchFile</span><span class="p">(</span><span class="nx">file</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">prev</span><span class="p">,</span> <span class="nx">curr</span><span class="p">)</span> <span class="p">{</span>
+ <span class="k">if</span> <span class="p">(</span><span class="nx">prev</span><span class="p">.</span><span class="nx">mtime</span><span class="p">.</span><span class="nx">getTime</span><span class="p">()</span> <span class="o">!=</span> <span class="nx">curr</span><span class="p">.</span><span class="nx">mtime</span><span class="p">.</span><span class="nx">getTime</span><span class="p">())</span> <span class="p">{</span>
+ <span class="nx">self</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="s2">&quot;change&quot;</span><span class="p">,</span> <span class="nx">file</span><span class="p">,</span> <span class="nx">prev</span><span class="p">,</span> <span class="nx">curr</span><span class="p">);</span>
+ <span class="p">}</span>
+ <span class="p">});</span>
+ <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
+ <span class="nx">fs</span><span class="p">.</span><span class="nx">unwatchFile</span><span class="p">(</span><span class="nx">file</span><span class="p">);</span>
+ <span class="p">}</span>
+ <span class="k">return</span> <span class="nx">self</span><span class="p">;</span>
+ <span class="p">};</span>
+ <span class="k">return</span> <span class="nx">Watch</span><span class="p">;</span>
+<span class="p">}();</span>
+
+<span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">WatchClass</span><span class="p">;</span>
+
+</pre></div> </td> </tr> </tbody> </table> </div> </body> </html>
82 node_modules/NodeInterval/node_modules/nodewatch/lib/watch/watch.js
@@ -0,0 +1,82 @@
+var EventEmitter = require("events").EventEmitter, fs = require("fs"), path = require("path");
+
+var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) {
+ for (var key in parent) {
+ if (__hasProp.call(parent, key)) {
+ child[key] = parent[key];
+ }
+ }
+ function ctor() {
+ this.constructor = child;
+ }
+ ctor.prototype = parent.prototype;
+ child.prototype = new ctor;
+ child.__super__ = parent.prototype;
+ return child;
+};
+
+var WatchClass = function() {
+ "use strict";
+ __extends(Watch, EventEmitter);
+ function Watch(options) {}
+ Watch.prototype.add = function(str_file_or_path) {
+ return this.__handle(true, str_file_or_path);
+ };
+ Watch.prototype.remove = function(str_file_or_path) {
+ return this.__handle(false, str_file_or_path);
+ };
+ Watch.prototype.onChange = function(cb) {
+ this.on("change", cb);
+ return this;
+ };
+ Watch.prototype.clearListeners = function() {
+ this.removeAllListeners("change");
+ return this;
+ };
+ Watch.prototype.__handle = function(add, str_file_or_path) {
+ if (str_file_or_path.substring(0, 1) == ".") {
+ str_file_or_path = process.cwd() + "/" + str_file_or_path;
+ }
+ str_file_or_path = path.normalize(str_file_or_path);
+ if (fs.statSync(str_file_or_path).isFile()) {
+ return this.__file(add, str_file_or_path);
+ }
+ if (fs.statSync(str_file_or_path).isDirectory()) {
+ return this.__dir(add, str_file_or_path);
+ }
+ };
+ Watch.prototype.__dir = function(add, dir) {
+ var files = fs.readdirSync(dir);
+ for (var i = 0; i < files.length; i++) {
+ var full_path = dir + "/" + files[i];
+ if (fs.statSync(full_path).isFile()) {
+ this.__file(add, full_path);
+ }
+ }
+ return this;
+ };
+ Watch.prototype.__file = function(add, file) {
+ var self = this;
+ if (add) {
+ // Support for windows as windows doesn't seem to have watchFile.
+ // https://github.com/joyent/node/pull/2049
+ if (fs.watch){
+ fs.watch(file, function(event, filename) {
+ self.emit("change", file, event, filename);