From 236cf4059e41ec5fa5b7b7206afe4b62e3a57f20 Mon Sep 17 00:00:00 2001 From: Shahab Lashkari Date: Sun, 28 Feb 2016 21:18:00 -0800 Subject: [PATCH] Initial release --- .gitignore | 33 ++++ Procfile | 1 + README.md | 29 +++ app.js | 61 ++++++ bin/www | 90 +++++++++ package.json | 31 +++ public/config.xml | 30 +++ public/favicon.ico | Bin 0 -> 99678 bytes public/images/logo.png | Bin 0 -> 2263 bytes public/images/thumbnail.png | Bin 0 -> 3505 bytes public/index.html | 282 ++++++++++++++++++++++++++++ public/javascripts/gadgetlib.min.js | 1 + public/stylesheets/style.css | 8 + routes/jshint.js | 13 ++ routes/less.js | 13 ++ routes/sass.js | 14 ++ routes/uglify.js | 31 +++ 17 files changed, 637 insertions(+) create mode 100644 .gitignore create mode 100644 Procfile create mode 100644 README.md create mode 100644 app.js create mode 100755 bin/www create mode 100644 package.json create mode 100644 public/config.xml create mode 100644 public/favicon.ico create mode 100644 public/images/logo.png create mode 100644 public/images/thumbnail.png create mode 100644 public/index.html create mode 100644 public/javascripts/gadgetlib.min.js create mode 100644 public/stylesheets/style.css create mode 100644 routes/jshint.js create mode 100644 routes/less.js create mode 100644 routes/sass.js create mode 100644 routes/uglify.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e920c16 --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +# Logs +logs +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +node_modules + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..a553d73 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: node ./bin/www diff --git a/README.md b/README.md new file mode 100644 index 0000000..e976735 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# Developer Tools Gadget +An [OU Campus](https://omniupdate.com/products/oucampus) gadget designed for web developers. + +## Features +- Minify/uglify CSS +- Minify/uglify JavaScript +- Compile SCSS +- Compile LESS + +## OU Campus Installation +Follow the OU Campus [gadget installation instructions](http://support.omniupdate.com/oucampus10/setup/gadgets/new-gadget.html) to install this gadget. When prompted for the gadget URL, enter `https://devtools.gadget.host` + +## Installing and running locally +This gadget requires a NodeJS back-end to handle the core functionality. To install, run +``` +git clone https://github.com/lashkari/gadget-devtools.git + +cd gadget-devtools + +npm install + +npm run dev +``` + +## Reporting issues +This is NOT an official gadget. Please report any issues you find here on GitHub, **do not contact OmniUpdate customer support about this gadget**. + +## Contributing +Contributions (by way of pull requests and reporting issues) are very welcome. I've still got some code cleanup to do and then I'll add some more info on how to contribute code. diff --git a/app.js b/app.js new file mode 100644 index 0000000..a9db3f8 --- /dev/null +++ b/app.js @@ -0,0 +1,61 @@ +var express = require('express'); +var path = require('path'); +var favicon = require('serve-favicon'); +var logger = require('morgan'); +var cookieParser = require('cookie-parser'); +var bodyParser = require('body-parser'); + +var jshint = require('./routes/jshint.js'); +var uglify = require('./routes/uglify.js'); +var sass = require('./routes/sass.js'); +var less = require('./routes/less.js'); + +var app = express(); + +app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); +app.use(logger('dev')); +app.use(bodyParser.json({ limit: '1mb' })); +app.use(bodyParser.urlencoded({ extended: false })); +app.use(bodyParser.text()); +app.use(cookieParser()); +app.use(express.static(path.join(__dirname, 'public'))); + +app.use('/', express.static(path.join(__dirname, 'public'))); +app.use('/jshint', jshint); +app.use('/uglify', uglify); +app.use('/sass', sass); +app.use('/less', less); + +// catch 404 and forward to error handler +app.use(function(req, res, next) { + var err = new Error('Not Found'); + err.status = 404; + next(err); +}); + +// error handlers + +// development error handler +// will print stacktrace +if (app.get('env') === 'development') { + app.use(function(err, req, res, next) { + res.status(err.status || 500); + res.render('error', { + message: err.message, + error: err + }); + }); +} + +// production error handler +// no stacktraces leaked to user +app.use(function(err, req, res, next) { + res.status(err.status || 500); + res.render('error', { + message: err.message, + error: {} + }); +}); + + +module.exports = app; diff --git a/bin/www b/bin/www new file mode 100755 index 0000000..a2c8f8d --- /dev/null +++ b/bin/www @@ -0,0 +1,90 @@ +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var app = require('../app'); +var debug = require('debug')('devtoolsGadget:server'); +var http = require('http'); + +/** + * Get port from environment and store in Express. + */ + +var port = normalizePort(process.env.PORT || '3000'); +app.set('port', port); + +/** + * Create HTTP server. + */ + +var server = http.createServer(app); + +/** + * Listen on provided port, on all network interfaces. + */ + +server.listen(port); +server.on('error', onError); +server.on('listening', onListening); + +/** + * Normalize a port into a number, string, or false. + */ + +function normalizePort(val) { + var port = parseInt(val, 10); + + if (isNaN(port)) { + // named pipe + return val; + } + + if (port >= 0) { + // port number + return port; + } + + return false; +} + +/** + * Event listener for HTTP server "error" event. + */ + +function onError(error) { + if (error.syscall !== 'listen') { + throw error; + } + + var bind = typeof port === 'string' + ? 'Pipe ' + port + : 'Port ' + port; + + // handle specific listen errors with friendly messages + switch (error.code) { + case 'EACCES': + console.error(bind + ' requires elevated privileges'); + process.exit(1); + break; + case 'EADDRINUSE': + console.error(bind + ' is already in use'); + process.exit(1); + break; + default: + throw error; + } +} + +/** + * Event listener for HTTP server "listening" event. + */ + +function onListening() { + var addr = server.address(); + var bind = typeof addr === 'string' + ? 'pipe ' + addr + : 'port ' + addr.port; + debug('Listening on ' + bind); +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..b2821c1 --- /dev/null +++ b/package.json @@ -0,0 +1,31 @@ +{ + "name": "devtoolsGadget", + "version": "1.0.0", + "author": "Shahab Lashkari", + "description":"An OU Campus gadget for web developers", + "license": "ISC", + "scripts": { + "start": "node ./bin/www", + "dev": "nodemon ./bin/www" + }, + "engines": { + "node": "5.4.0" + }, + "dependencies": { + "body-parser": "^1.13.3", + "cookie-parser": "~1.3.5", + "debug": "~2.2.0", + "express": "~4.13.1", + "imagemin": "^4.0.0", + "jshint": "^2.9.1", + "less": "^2.6.0", + "morgan": "~1.6.1", + "node-sass": "^3.4.2", + "serve-favicon": "~2.3.0", + "uglify-js": "^2.6.1", + "uglifycss": "0.0.20" + }, + "devDependencies": { + "nodemon": "^1.9.1" + } +} diff --git a/public/config.xml b/public/config.xml new file mode 100644 index 0000000..d8c1036 --- /dev/null +++ b/public/config.xml @@ -0,0 +1,30 @@ + + + + + Developer Tools + + + https://devtools.gadget.host/favicon.ico + + + Provides useful tools for web developers. Currently supports minification of CSS/JavaScript and CSS pre-processing of SASS and LESS. + + + https://devtools.gadget.host/images/thumbnail.png + + + sidebar + + + view_changed + + + page + + + 80 + + \ No newline at end of file diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a448e4f26d373498a2c5e6fa2168c2b208561064 GIT binary patch literal 99678 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 literal 0 HcmV?d00001 diff --git a/public/images/logo.png b/public/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..002fc7b442e298cf131e1c9ff217820673702b2a GIT binary patch literal 2263 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 + + + Developer Tools Gadget + + + + + + + +
+ + + + +
+ + + + + + + diff --git a/public/javascripts/gadgetlib.min.js b/public/javascripts/gadgetlib.min.js new file mode 100644 index 0000000..1b86a91 --- /dev/null +++ b/public/javascripts/gadgetlib.min.js @@ -0,0 +1 @@ +!function(){function t(){return a("get-environment")}function e(){var t={},e=location.href.split(/[\?&]/),a=e.splice(1);t.url=e[0];for(var i,o,r,s=0;s