├── .gitignore ├── AUTHORS ├── LICENSE ├── README.md ├── api.js ├── bin └── nodify ├── deployer.js ├── domain ├── handler.js ├── project.js └── user.js ├── package.json ├── public ├── 404.html ├── BespinEmbedded.css ├── BespinEmbedded.js ├── BespinMain.js ├── BespinWorker.js ├── apple-touch-icon.png ├── btn-forum.png ├── btn-twitter.png ├── crossdomain.xml ├── css │ ├── BespinEmbedded.css │ ├── csscolors.css │ ├── docs.css │ ├── handheld.css │ ├── jscolors.css │ ├── people.jpg │ ├── sparqlcolors.css │ ├── style.css │ ├── ui-lightness │ │ ├── _jquery-ui-1.8.4.custom.css │ │ ├── images │ │ │ ├── ui-bg_diagonals-thick_18_b81900_40x40.png │ │ │ ├── ui-bg_diagonals-thick_20_666666_40x40.png │ │ │ ├── ui-bg_flat_10_000000_40x100.png │ │ │ ├── ui-bg_glass_100_f6f6f6_1x400.png │ │ │ ├── ui-bg_glass_100_fdf5ce_1x400.png │ │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ │ ├── ui-bg_gloss-wave_35_f6a828_500x100.png │ │ │ ├── ui-bg_highlight-soft_100_eeeeee_1x100.png │ │ │ ├── ui-bg_highlight-soft_75_ffe45c_1x100.png │ │ │ ├── ui-icons_222222_256x240.png │ │ │ ├── ui-icons_228ef1_256x240.png │ │ │ ├── ui-icons_ef8c08_256x240.png │ │ │ ├── ui-icons_ffd27a_256x240.png │ │ │ └── ui-icons_ffffff_256x240.png │ │ └── jquery-ui-1.8.4.custom.css │ └── xmlcolors.css ├── favicon.png ├── index.html ├── js │ ├── client.js │ ├── codemirror.js │ ├── dd_belatedpng.js │ ├── editor.js │ ├── highlight.js │ ├── jquery-1.4.2.min.js │ ├── jquery-ui-1.8.4.custom.min.js │ ├── jquery.loading.min.js │ ├── jquery.slidingmessage.min.js │ ├── mirrorframe.js │ ├── modernizr-1.5.min.js │ ├── parsecss.js │ ├── parsedummy.js │ ├── parsehtmlmixed.js │ ├── parsejavascript.js │ ├── parsesparql.js │ ├── parsexml.js │ ├── plugins.js │ ├── select.js │ ├── stringstream.js │ ├── tokenize.js │ ├── tokenizejavascript.js │ ├── undo.js │ └── util.js ├── nodify-logo.png ├── resources │ ├── screen_theme │ │ ├── images │ │ │ ├── bespin-s.png │ │ │ ├── check-selected.png │ │ │ ├── check-unselected.png │ │ │ ├── lines.png │ │ │ ├── radio-selected.png │ │ │ ├── radio-unselected.png │ │ │ ├── scroll-down.png │ │ │ ├── scroll-left.png │ │ │ ├── scroll-right.png │ │ │ └── scroll-up.png │ │ └── theme.less │ └── whitetheme │ │ └── theme.less ├── robots.txt └── temp.css ├── server.js └── tmp └── README /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.swp 3 | public/.idea 4 | tmp/*.js 5 | .idea 6 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # Authors ordered by first contribution. 2 | 3 | Panagiotis Astithas 4 | Christos Stathis 5 | Dionysios Synodinos 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2010 Panagiotis Astithas, Christos Stathis, Dionysios Synodinos 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to 5 | deal in the Software without restriction, including without limitation the 6 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | sell copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Nodify 2 | ====== 3 | 4 | Nodify is a web-based IDE for creating and testing JavaScript programs on 5 | [NodeJS](http://nodejs.org). You can already edit and save your programming projects, using the 6 | on-screen Bespin editor that provides syntax highlighting and natural keyboard 7 | shortcuts. Executing your project is performed by clicking 'Run'. After the 8 | execution is finished, the console will be displayed containing the program's 9 | output. If the program is a server or enters an infinite loop, it will be 10 | killed after 5 minutes, but you can terminate its execution by clicking 11 | 'Stop'. You may also start a debugging session by clicking 'Debug', though do 12 | note that it requires using Google Chrome as your browser. Currently projects are single-file, but soon you will be able to 13 | have multiple files per project. You may create multiple projects, rename or 14 | delete them using the links on top of the screen. You don't need to create an 15 | account to use Nodify, a unique user account is created and stored in a cookie 16 | in your browser. Currently, if you browse the service from a different browser 17 | you will create a new user account. 18 | 19 | Installation 20 | ------------ 21 | 22 | You may install nodify either using git or [npm](http://npmjs.org). 23 | 24 | ###Via npm: 25 | 26 | $ npm install nodify 27 | 28 | The command "nodify" can then be used to launch the IDE. 29 | The only requirements are node and npm. 30 | 31 | ###Via git: 32 | 33 | $ git clone http://github.com/past/nodify.git 34 | 35 | Then you can run nodify like this: 36 | 37 | $ bin/nodify 38 | 39 | You must have already downladed and installed node, connect, nStore and node-inspector. 40 | 41 | Debugging 42 | --------- 43 | 44 | In order to debug your node programs you need to use Google Chrome. This is a 45 | requirement from node-inspector that provides the debugging functionality. 46 | Also, for a better user experience you should enable popups from 127.0.0.1 in 47 | the browser's preferences. Furthermore, node itself must be compiled with the 48 | --debug configuration option. 49 | 50 | Mailing list 51 | ------------ 52 | 53 | For any questions, suggestions or general brainstorming, use the [discussion group](http://groups.google.com/group/nodify). 54 | 55 | License 56 | ------- 57 | 58 | (the MIT License) 59 | 60 | Copyright 2010 Panagiotis Astithas, Christos Stathis, Dionysios Synodinos 61 | 62 | Permission is hereby granted, free of charge, to any person obtaining a copy 63 | of this software and associated documentation files (the "Software"), to 64 | deal in the Software without restriction, including without limitation the 65 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 66 | sell copies of the Software, and to permit persons to whom the Software is 67 | furnished to do so, subject to the following conditions: 68 | 69 | The above copyright notice and this permission notice shall be included in 70 | all copies or substantial portions of the Software. 71 | 72 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 73 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 74 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 75 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 76 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 77 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 78 | IN THE SOFTWARE. 79 | -------------------------------------------------------------------------------- /api.js: -------------------------------------------------------------------------------- 1 | var User = require('./domain/user').User, 2 | Handler = require('./domain/handler').Handler, 3 | Project = require('./domain/project').Project, 4 | nStore = require('nStore'), 5 | sys = require('sys'), 6 | deployer = require('./deployer'); 7 | 8 | var users = nStore(process.env.HOME + '/.nodify.users.db'); 9 | 10 | // The router for the api requests. 11 | var router = exports.router = function (app) { 12 | // Request for bootstrapping actions. 13 | app.get('/init', function (req, res, next) { 14 | var project, body, authToken, handler, user; 15 | authToken = req.cookies['_auth_nodify']; 16 | if (!authToken) { 17 | console.log("No token, Creating new user..."); 18 | createNewUser(res); 19 | } 20 | else { 21 | console.log("Token found, loading user..."); 22 | users.get(authToken, function(err, doc, meta) { 23 | if (err && err.errno == 2) { 24 | console.log("User not found, Creating new user..."); 25 | createNewUser(res); 26 | } 27 | else if (err) { 28 | sendError(res, 500); 29 | throw err; 30 | } 31 | else { 32 | user = createUser(doc); 33 | if (user.lastProject) { 34 | project = user.lastProject; 35 | body = JSON.stringify({'user': user, 'project': project.id}); 36 | sendResult(res, body); 37 | } else { 38 | project = new Project('MyProject', user.username); 39 | project.addHandler(new Handler('GET', '/', 'var a=1;\nconsole.log(a);', user.username)); 40 | user.addProject(project); 41 | users.save(user.username, user, function(err) { 42 | if (err) { 43 | sendError(res, 500); 44 | throw err; 45 | } 46 | body = JSON.stringify({'user': user, 'project': project.id}); 47 | sendResult(res, body); 48 | }); 49 | } 50 | } 51 | }); 52 | } 53 | }); 54 | // Create new project. 55 | app.post('/init', function(req, res, next) { 56 | var authToken = req.cookies['_auth_nodify']; 57 | if (!authToken) 58 | sendError(res, 403); 59 | else { 60 | users.get(authToken, function(err, doc, meta) { 61 | var user, rename, create, project, p; 62 | if (err && err.errno == 2) { 63 | sendError(res, 404); 64 | return; 65 | } 66 | else if (err) { 67 | sendError(res, 500); 68 | throw err; 69 | } 70 | user = createUser(doc); 71 | req.body = req.body || {}; 72 | rename = req.body.rename; 73 | create = req.body.create; 74 | project = req.body.project; 75 | if (!create && !(project && rename)) { 76 | console.log("ERROR: project=" + project + ",create=" + create + ",rename=" + rename); 77 | sendError(res, 400); 78 | return; 79 | } 80 | if (rename && !user.projects[project]) { 81 | sendError(res, 404); 82 | return; 83 | } 84 | if (create) { 85 | p = new Project(decodeURIComponent(create), user.username); 86 | p.addHandler(new Handler('GET', '/', '', user.username)); 87 | user.addProject(p); 88 | } else { 89 | user.projects[project].name = decodeURIComponent(rename); 90 | user.lastProject = user.projects[project]; 91 | } 92 | users.save(user.username, user, function(err) { 93 | if (err) { 94 | sendError(res, 500); 95 | throw err; 96 | } 97 | sendResult(res); 98 | }); 99 | }); 100 | } 101 | }); 102 | // Request to update the project contents. 103 | app.put('/init', function(req, res, next) { 104 | var authToken = req.cookies['_auth_nodify']; 105 | if (!authToken) 106 | sendError(res, 403); 107 | else { 108 | users.get(authToken, function(err, doc, meta) { 109 | var user; 110 | if (err && err.errno == 2) { 111 | sendError(res, 404); 112 | return; 113 | } 114 | else if (err) { 115 | sendError(res, 500); 116 | throw err; 117 | } 118 | user = createUser(doc); 119 | req.body = req.body || {}; 120 | var code = req.body.code; 121 | var uri = req.body.uri; 122 | var method = req.body.method; 123 | var project = req.body.project; 124 | if (!code || !method || !uri || !project) { 125 | console.log("ERROR: project=" + project + ",uri=" + uri + ",method=" + method + ",code=" + code); 126 | sendError(res, 400); 127 | return; 128 | } 129 | if (!user.projects[project] || !user.projects[project].handlers[method + " " + uri]) { 130 | sendError(res, 404); 131 | return; 132 | } 133 | user.projects[project].handlers[method + " " + uri].code = decodeURIComponent(code); 134 | user.projects[project].lastHandler = user.projects[project].handlers[method + " " + uri]; 135 | user.lastProject = user.projects[project]; 136 | users.save(user.username, user, function(err) { 137 | if (err) { 138 | sendError(res, 500); 139 | throw err; 140 | } 141 | console.log(sys.inspect(user.lastProject)); 142 | sendResult(res); 143 | }); 144 | }); 145 | } 146 | }); 147 | // Request to delete the project. 148 | app.del('/init', function(req, res, next) { 149 | var authToken = req.cookies['_auth_nodify']; 150 | if (!authToken) 151 | sendError(res, 403); 152 | else { 153 | users.get(authToken, function(err, doc, meta) { 154 | var user; 155 | if (err && err.errno == 2) { 156 | sendError(res, 404); 157 | return; 158 | } 159 | else if (err) { 160 | sendError(res, 500); 161 | throw err; 162 | } 163 | user = createUser(doc); 164 | req.body = req.body || {}; 165 | var project = req.body.project; 166 | if (!project) { 167 | console.log("ERROR: project=" + project); 168 | sendError(res, 400); 169 | return; 170 | } 171 | if (!user.projects[project]) { 172 | sendError(res, 404); 173 | return; 174 | } 175 | user.removeProject(user.projects[project]); 176 | users.save(user.username, user, function(err) { 177 | if (err) { 178 | sendError(res, 500); 179 | throw err; 180 | } 181 | sendResult(res); 182 | }); 183 | }); 184 | } 185 | }); 186 | 187 | app.post('/deploy', function(req, res, next) { 188 | var authToken = req.cookies['_auth_nodify']; 189 | if (!authToken) 190 | sendError(res, 403); 191 | else { 192 | users.get(authToken, function(err, doc, meta) { 193 | var user; 194 | if (err && err.errno == 2) { 195 | sendError(res, 404); 196 | return; 197 | } 198 | else if (err) { 199 | sendError(res, 500); 200 | throw err; 201 | } 202 | user = createUser(doc); 203 | req.body = req.body || {}; 204 | var project = req.body.project; 205 | if (!project) { 206 | console.log("ERROR: project=" + project); 207 | sendError(res, 400); 208 | return; 209 | } 210 | if (!user.projects[project] || !user.projects[project].handlers["GET /"]) { 211 | sendError(res, 404); 212 | return; 213 | } 214 | deployer.start(user.projects[project], function (pids) { 215 | pids.forEach(function(pid) { 216 | console.log("Spawned proccess " + pid); 217 | }); 218 | user.projects[project].pids = pids; 219 | users.save(user.id, user, function (err) { 220 | if (err) { 221 | throw err; 222 | } 223 | }); 224 | }, function(stdout, stderr) { 225 | sendResult(res, stdout+'\n'+stderr); 226 | }); 227 | }); 228 | } 229 | }); 230 | 231 | app.post('/terminate', function(req, res, next) { 232 | var authToken = req.cookies['_auth_nodify']; 233 | if (!authToken) 234 | sendError(res, 403); 235 | else { 236 | users.get(authToken, function(err, doc, meta) { 237 | var user; 238 | if (err && err.errno == 2) { 239 | sendError(res, 404); 240 | return; 241 | } 242 | else if (err) { 243 | sendError(res, 500); 244 | throw err; 245 | } 246 | user = createUser(doc); 247 | req.body = req.body || {}; 248 | var project = req.body.project; 249 | if (!project) { 250 | console.log("ERROR: project=" + project); 251 | sendError(res, 400); 252 | return; 253 | } 254 | if (!user.projects[project] || !user.projects[project].handlers["GET /"]) { 255 | sendError(res, 404); 256 | return; 257 | } 258 | if (user.projects[project].pids) { 259 | deployer.stop(user.projects[project].pids); 260 | delete user.projects[project].pids; 261 | users.save(user.id, user, function (err) { 262 | if (err) throw err; 263 | sendResult(res); 264 | }); 265 | } 266 | }); 267 | } 268 | }); 269 | 270 | app.post('/debug', function(req, res, next) { 271 | var authToken = req.cookies['_auth_nodify']; 272 | if (!authToken) 273 | sendError(res, 403); 274 | else { 275 | users.get(authToken, function(err, doc, meta) { 276 | var user; 277 | if (err && err.errno == 2) { 278 | sendError(res, 404); 279 | return; 280 | } 281 | else if (err) { 282 | sendError(res, 500); 283 | throw err; 284 | } 285 | user = createUser(doc); 286 | req.body = req.body || {}; 287 | var project = req.body.project; 288 | if (!project) { 289 | console.log("ERROR: project=" + project); 290 | sendError(res, 400); 291 | return; 292 | } 293 | if (!user.projects[project] || !user.projects[project].handlers["GET /"]) { 294 | sendError(res, 404); 295 | return; 296 | } 297 | deployer.debug(user.projects[project], function (pids) { 298 | pids.forEach(function(pid) { 299 | console.log("Spawned proccess " + pid); 300 | }); 301 | user.projects[project].pids = pids; 302 | users.save(user.id, user, function (err) { 303 | if (err) throw err; 304 | sendResult(res); 305 | }); 306 | }, function(stdout, stderr) { 307 | sendResult(res, stdout+'\n'+stderr); 308 | }); 309 | }); 310 | } 311 | }); 312 | }; 313 | 314 | // Helper function to send the result. 315 | var sendResult = function (res, data, extraHeaders) { 316 | var headers = {'Content-Type': 'application/json'}; 317 | if (extraHeaders) 318 | for (var h in extraHeaders) 319 | headers[h] = extraHeaders[h]; 320 | res.writeHead(200, headers); 321 | if (data) 322 | res.end(data); 323 | else 324 | res.end(); 325 | }; 326 | 327 | // Helper function to send the result in error cases. 328 | var sendError = function (res, status, data, extraHeaders) { 329 | var headers = {'Content-Type': 'text/plain'}; 330 | if (extraHeaders) 331 | for (var h in extraHeaders) 332 | headers[h] = extraHeaders[h]; 333 | res.writeHead(status, headers); 334 | if (data) 335 | res.end(data); 336 | else 337 | res.end(); 338 | }; 339 | 340 | var createUser = function(dbUser) { 341 | var lastP = dbUser.lastProject; 342 | var user = new User(dbUser.username); 343 | for (var p in dbUser.projects) { 344 | user.addProject(createProject(dbUser.projects[p])); 345 | } 346 | user.lastProject = lastP; 347 | return user; 348 | }; 349 | 350 | var createProject = function(dbProject) { 351 | var lastH = dbProject.lastHandler; 352 | var proj = new Project(dbProject.name, dbProject.author); 353 | proj.pids = dbProject.pids; 354 | for (var h in dbProject.handlers) { 355 | proj.addHandler(createHandler(dbProject.handlers[h])); 356 | } 357 | proj.lastHandler = lastH; 358 | return proj; 359 | }; 360 | 361 | var createHandler = function(dbHandler) { 362 | return new Handler(dbHandler.method, dbHandler.uri, dbHandler.code, dbHandler.author); 363 | } 364 | 365 | var generateAuthToken = function (callback) { 366 | var authToken = new Date().getTime(); 367 | tokenExists(authToken, callback) 368 | } 369 | 370 | var tokenExists = function (token, callback) { 371 | users.get(token, function (err, doc, meta) { 372 | if (err && err.errno == 2) { 373 | callback(token); 374 | } 375 | else if (err) 376 | throw err; 377 | else { 378 | console.log('!!!! Unbelievably token ' + token + ' already exists. Try another one !!!!'); 379 | generateAuthToken(callback); 380 | } 381 | }); 382 | } 383 | 384 | var createNewUser = function (res) { 385 | generateAuthToken(function (token) { 386 | var authToken = token; 387 | var user = new User(authToken); 388 | if (user.projectsLength === 0) { 389 | var project = new Project('MyProject', user.id); 390 | user.addProject(project); 391 | var handler = new Handler('GET', '/', 'var a = 1;\nconsole.log(a);', user.id); 392 | project.addHandler(handler); 393 | var headers = {'Set-Cookie':'_auth_nodify=' + authToken}; 394 | } 395 | users.save(authToken, user, function(err) { 396 | if (err) { 397 | sendError(res, 500); 398 | throw err; 399 | } 400 | var body = JSON.stringify({'user': user, 'project': project.id}); 401 | sendResult(res, body, headers); 402 | }); 403 | }); 404 | } 405 | -------------------------------------------------------------------------------- /bin/nodify: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var connect = require('connect'), 4 | api = require('../api'), 5 | child_process = require('child_process'), 6 | port = parseInt(process.env.PORT) || 8000; 7 | 8 | var server = connect.createServer( 9 | connect.logger(), 10 | connect.errorHandler({ dumpExceptions: true, showStack: true }) 11 | ); 12 | // Serve static resources. 13 | server.use("/", 14 | connect.conditionalGet(), 15 | connect.cache(), 16 | connect.gzip(), 17 | connect.staticProvider(require('path').dirname(__dirname + '..') + '/public') 18 | ); 19 | // Serve the API responses. 20 | server.use("/api", 21 | connect.bodyDecoder(), 22 | connect.cookieDecoder(), 23 | connect.router(api.router) 24 | ); 25 | server.listen(port); 26 | console.log('Nodify server running at http://127.0.0.1:' + port); 27 | 28 | process.addListener('uncaughtException', function (err) { 29 | console.log(err.stack); 30 | }); 31 | 32 | // Point the default browser to nodify. 33 | if (process.platform === 'darwin') 34 | child_process.exec('open http://127.0.0.1:' + port, function (error, stdout, stderr) { 35 | if (error) 36 | console.log('Opening the default browser failed: ' + error + '\nYou will to have to do it yourself.'); 37 | }); 38 | else if (process.platform === 'linux') { 39 | child_process.exec('gnome-open http://127.0.0.1:' + port, function (error, stdout, stderr) { 40 | if (error) 41 | child_process.exec('xdg-open http://127.0.0.1:' + port, function (error, stdout, stderr) { 42 | if (error) 43 | console.log('Opening the default browser failed: ' + error + '\nYou will to have to do it yourself.'); 44 | }); 45 | }); 46 | } 47 | 48 | -------------------------------------------------------------------------------- /deployer.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), 2 | sys = require('sys'), 3 | child_process = require('child_process'); 4 | 5 | var start = exports.start = function (project, createCallback, exitCallback) { 6 | var tempFile = __dirname + '/tmp/' + Math.random() + '.js'; 7 | fs.writeFile(tempFile, project.lastHandler.code, function(err) { 8 | if (err) 9 | throw err; 10 | var aggrOut = ''; 11 | var aggrErr = ''; 12 | var node = child_process.spawn('node', [tempFile]); 13 | createCallback([node.pid]); 14 | node.stdout.on('data', function (data) { 15 | console.log('Stdout data: ' + data); 16 | aggrOut += data; 17 | }); 18 | node.stderr.on('data', function (data) { 19 | console.log('Stderr data: ' + data); 20 | aggrErr += data; 21 | }); 22 | node.on('exit', function (code) { 23 | aggrOut += '\nExit code: ' + code; 24 | fs.unlink(tempFile, function (err) { 25 | if (err) 26 | console.log("Unable to delete " + tempFile + "."); 27 | exitCallback(aggrOut, aggrErr); 28 | }); 29 | }); 30 | }); 31 | } 32 | 33 | var stop = exports.stop = function (pids) { 34 | pids.forEach(function(pid) { 35 | console.log("Killing process " + pid); 36 | try { 37 | process.kill(pid, 'SIGKILL'); 38 | } catch (err) { 39 | console.log(err); 40 | } 41 | }); 42 | } 43 | 44 | var debug = exports.debug = function (project, createCallback, exitCallback) { 45 | var tempFile = __dirname + '/tmp/' + Math.random() + '.js'; 46 | fs.writeFile(tempFile, project.lastHandler.code, function(err) { 47 | if (err) throw err; 48 | var aggrOut = ''; 49 | var aggrErr = ''; 50 | var inspector = child_process.spawn('node-inspector'); 51 | inspector.stdout.on('data', function (data) { 52 | console.log('Stdout data: ' + data); 53 | aggrOut += data; 54 | var node = child_process.spawn('node', ['--debug', tempFile]); 55 | createCallback([node.pid, inspector.pid]); 56 | node.stdout.on('data', function (data) { 57 | console.log('Stdout data: ' + data); 58 | aggrOut += data; 59 | }); 60 | node.stderr.on('data', function (data) { 61 | console.log('Stderr data: ' + data); 62 | aggrErr += data; 63 | }); 64 | node.on('exit', function (code) { 65 | aggrOut += '\nExit code: ' + code; 66 | fs.unlink(tempFile, function (err) { 67 | if (err) 68 | console.log("Unable to delete " + tempFile + "."); 69 | }); 70 | exitCallback(aggrOut, aggrErr); 71 | }); 72 | }); 73 | inspector.stderr.on('data', function (data) { 74 | console.log('Stderr data: ' + data); 75 | aggrErr += data; 76 | }); 77 | inspector.on('exit', function (code) { 78 | aggrOut += '\nExit code: ' + code; 79 | }); 80 | }); 81 | } 82 | 83 | -------------------------------------------------------------------------------- /domain/handler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A handler is the code that handles a particular uri for a particula method (GET, PUT, etc) 3 | **/ 4 | var Handler = exports.Handler = function(method, uri, code, author) { 5 | this.author = author; 6 | this.method = method; 7 | this.code = code; 8 | this.uri = uri; 9 | }; 10 | 11 | Handler.prototype.toString = function() { 12 | return this.method + " " + this.uri; 13 | }; 14 | 15 | Object.defineProperty(Handler.prototype, "id", { 16 | get: function() { 17 | return this.toString(); 18 | } 19 | }); 20 | -------------------------------------------------------------------------------- /domain/project.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A project is a collection of handlers 3 | **/ 4 | var Project = exports.Project = function(name, author) { 5 | this.name = name; 6 | this.author = author; 7 | this.handlers = {}; 8 | this.handlersLength = 0; 9 | }; 10 | 11 | Project.prototype.addHandler = function(handler) { 12 | this.handlers[handler.id] = handler; 13 | this.handlersLength++; 14 | this.lastHandler = handler; 15 | }; 16 | 17 | Project.prototype.removeHandler = function(handler) { 18 | delete this.handlers[handler.id]; 19 | this.handlersLength--; 20 | self = this; 21 | Object.keys(this.handlers).forEach(function (h) { 22 | self.lastHandler = self.handlers[h]; 23 | }); 24 | }; 25 | 26 | Project.prototype.toString = function() { 27 | return this.name; 28 | }; 29 | 30 | Object.defineProperty(Project.prototype, "id", { 31 | get: function() { 32 | return this.toString(); 33 | } 34 | }); 35 | -------------------------------------------------------------------------------- /domain/user.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A user of the application. A user contains projects 3 | **/ 4 | var User = exports.User = function(username) { 5 | this.username = username; 6 | this.projects = {}; 7 | this.projectsLength = 0; 8 | }; 9 | 10 | User.prototype.addProject = function(project) { 11 | this.projects[project.id] = project; 12 | this.projectsLength++; 13 | this.lastProject = project; 14 | }; 15 | 16 | User.prototype.removeProject = function(project) { 17 | delete this.projects[project.id]; 18 | this.projectsLength--; 19 | self = this; 20 | Object.keys(this.projects).forEach(function (p) { 21 | self.lastProject = self.projects[p]; 22 | }); 23 | }; 24 | 25 | User.prototype.toString = function() { 26 | return this.username; 27 | }; 28 | 29 | Object.defineProperty(User.prototype, "id", { 30 | get: function() { 31 | return this.toString(); 32 | } 33 | }); 34 | 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodify", 3 | "version": "0.1.2", 4 | "description": "A web-based IDE for NodeJS applications", 5 | "homepage": "http://nodifyjs.com", 6 | "author": "Panagiotis Astithas ", 7 | "contributors": [ 8 | { "name": "Panagiotis Astithas", "email": "pastith@gmail.com" }, 9 | { "name": "Christos Stathis", "email": "chstath@gmail.com" }, 10 | { "name": "Dionysios Synodinos", "email": "synodinos@gmail.com" } 11 | ], 12 | "keywords": ["ide", "programming", "editor"], 13 | "engines": { "node": ">= 0.2.0" }, 14 | "bin": { "nodify" : "./bin/nodify" }, 15 | "repository": { 16 | "type": "git", 17 | "url": "http://github.com/past/nodify.git" 18 | }, 19 | "licenses": [ 20 | { 21 | "type": "MIT", 22 | "url": "http://github.com/past/nodify/raw/master/LICENSE" 23 | } 24 | ], 25 | 26 | "dependencies": { 27 | "connect": ">= 0.2.4", 28 | "node-inspector": ">= 0.0.2", 29 | "nStore": ">= 0.2.1" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | not found 3 | 4 | 12 | 13 | 14 | 15 | 16 |
17 |

Not found

18 |

:(

19 |
-------------------------------------------------------------------------------- /public/BespinEmbedded.css: -------------------------------------------------------------------------------- 1 | .bespin-completion-panel { 2 | font-family: Helvetica, Arial, sans-serif; 3 | position: absolute; 4 | cursor: default; 5 | line-height: normal; 6 | -moz-user-select: none; 7 | -webkit-user-select: none; 8 | } 9 | 10 | .bespin-completion-pointer { 11 | position: absolute; 12 | z-index: 2; 13 | height: 21px; 14 | width: 21px; 15 | } 16 | 17 | .bespin-completion-pointer-up { 18 | top: 1px; 19 | border-top: solid #555 1px; 20 | border-left: solid #555 1px; 21 | background-image: -moz-linear-gradient(top left, #333333, #333333 50%, transparent 50%, transparent); 22 | background-image: -webkit-gradient(linear, left top, right bottom, from(#333333), color-stop(0.5, #333333), color-stop(0.5, transparent), to(transparent)); 23 | -moz-transform: rotate(45deg); 24 | -webkit-transform: rotate(45deg); 25 | } 26 | 27 | .bespin-completion-pointer-down { 28 | bottom: 1px; 29 | border-top: solid #000 1px; 30 | border-left: solid #000 1px; 31 | background-image: -moz-linear-gradient(top left, #000, #000 50%, transparent 50%, transparent); 32 | background-image: -webkit-gradient(linear, left top, right bottom, from(#000), color-stop(0.5, #000), color-stop(0.5, transparent), to(transparent)); 33 | -moz-transform: rotate(225deg); 34 | -webkit-transform: rotate(225deg); 35 | } 36 | 37 | .bespin-completion-bubble-outer { 38 | position: relative; 39 | z-index: 1; 40 | margin: 11px 0px 11px 0px; 41 | border-top: solid #555 1px; 42 | -moz-border-radius: 8px; 43 | -webkit-border-radius: 8px; 44 | } 45 | 46 | .bespin-completion-bubble-inner { 47 | position: relative; 48 | z-index: 3; 49 | padding: 6px; 50 | background: -moz-linear-gradient(top, #333333, #000000); 51 | background: -webkit-gradient(linear, center top, center bottom, from(#333333), to(#000000)); 52 | color: #ffffff; 53 | font-size: 10.5pt; 54 | -moz-border-radius: 8px; 55 | -webkit-border-radius: 8px; 56 | -moz-box-shadow: 0px 6px 16px 2px rgba(0, 0, 0, 0.5); 57 | -webkit-box-shadow: 0px 6px 16px 2px rgba(0, 0, 0, 0.5); 58 | } 59 | 60 | .bespin-completion-panel ul { 61 | list-style: none; 62 | margin: 0px; 63 | padding: 0px; 64 | } 65 | 66 | .bespin-completion-panel li { 67 | text-indent: 0px; 68 | margin: 0px; 69 | padding: 6px 16px; 70 | } 71 | 72 | .bespin-completion-highlight { 73 | position: absolute; 74 | z-index: -1; 75 | background-image: -moz-linear-gradient(top, #3e59be, #312d80); 76 | background-image: -webkit-gradient(linear, center top, center bottom, from(#3e59be), to(#312d80)); 77 | border: solid rgba(37, 34, 91, 1.0) 1px; 78 | -moz-border-radius: 6px; 79 | -webkit-border-radius: 6px; 80 | } 81 | 82 | .bespin-completion-kind { 83 | display: block; 84 | float: left; 85 | top: 0px; 86 | left: 0px; 87 | width: 8px; 88 | height: 8px; 89 | padding: 2px; 90 | margin: 0px 5px 0px 0px; 91 | font-size: 6.5pt; 92 | font-weight: bold; 93 | text-transform: uppercase; 94 | text-align: center; 95 | -moz-border-radius: 3px; 96 | -webkit-border-radius: 3px; 97 | } 98 | 99 | .bespin-completion-kind-m { 100 | background-color: maroon; 101 | } 102 | 103 | .bespin-completion-kind-f { 104 | background-color: green; 105 | } 106 | 107 | .bespin-completion-top-row { 108 | position: relative; 109 | } 110 | 111 | .bespin-completion-second-row { 112 | margin: 6px 0px 0px 17px; 113 | display: none; 114 | } 115 | 116 | .bespin-completion-ident { 117 | font-weight: bold; 118 | } 119 | 120 | .bespin-completion-container { 121 | color: #a0a0a0; 122 | } 123 | 124 | -------------------------------------------------------------------------------- /public/BespinWorker.js: -------------------------------------------------------------------------------- 1 | bespin.tiki.register("::syntax_worker",{name:"syntax_worker",dependencies:{syntax_directory:"0.0.0",underscore:"0.0.0"}}); 2 | bespin.tiki.module("syntax_worker:index",function(b,f){var c=b("bespin:promise"),d=b("underscore")._;b("bespin:console");var a=b("syntax_directory").syntaxDirectory;f.syntaxWorker={engines:{},settings:{},annotate:function(e,j){function p(k){return k.split(":")}function n(){o.push(d(h).invoke("join",":").join(" "))}var m=this.engines,o=[],g=[],q=[],h=d(e.split(" ")).map(p);d(j).each(function(k){n();for(var r=[],t={},l=0;l/,tag:"operator",then:e},{regex:/^./,tag:"keyword",then:a+"_attrName"}];c[a+"_attrName"]=[{regex:/^\s+/,tag:"plain",then:a+"_afterAttrName"},{regex:/^\//,tag:"operator",then:a+"_selfClosingStartTag"},{regex:/^=/,tag:"operator",then:a+"_beforeAttrValue"},{regex:/^>/,tag:"operator", 8 | then:e},{regex:/^["'<]+/,tag:"error"},{regex:/^[^ \t\n\/=>"'<]+/,tag:"keyword"}];c[a+"_afterAttrName"]=[{regex:/^\s+/,tag:"plain"},{regex:/^\//,tag:"operator",then:a+"_selfClosingStartTag"},{regex:/^=/,tag:"operator",then:a+"_beforeAttrValue"},{regex:/^>/,tag:"operator",then:e},{regex:/^./,tag:"keyword",then:a+"_attrName"}];c[a+"_beforeAttrValue"]=[{regex:/^\s+/,tag:"plain"},{regex:/^"/,tag:"string",then:a+"_attrValueQQ"},{regex:/^(?=&)/,tag:"plain",then:a+"_attrValueU"},{regex:/^'/,tag:"string", 9 | then:a+"_attrValueQ"},{regex:/^>/,tag:"error",then:e},{regex:/^./,tag:"string",then:a+"_attrValueU"}];c[a+"_attrValueQQ"]=[{regex:/^"/,tag:"string",then:a+"_afterAttrValueQ"},{regex:/^[^"]+/,tag:"string"}];c[a+"_attrValueQ"]=[{regex:/^'/,tag:"string",then:a+"_afterAttrValueQ"},{regex:/^[^']+/,tag:"string"}];c[a+"_attrValueU"]=[{regex:/^\s/,tag:"string",then:a+"_beforeAttrName"},{regex:/^>/,tag:"operator",then:e},{regex:/[^ \t\n>]+/,tag:"string"}];c[a+"_afterAttrValueQ"]=[{regex:/^\s/,tag:"plain", 10 | then:a+"_beforeAttrName"},{regex:/^\//,tag:"operator",then:a+"_selfClosingStartTag"},{regex:/^>/,tag:"operator",then:e},{regex:/^(?=.)/,tag:"operator",then:a+"_beforeAttrName"}];c[a+"_selfClosingStartTag"]=[{regex:/^>/,tag:"operator",then:"start"},{regex:/^./,tag:"error",then:a+"_beforeAttrName"}]};c={start:[{regex:/^[^<]+/,tag:"plain"},{regex:/^ 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /public/css/BespinEmbedded.css: -------------------------------------------------------------------------------- 1 | .bespin-completion-panel { 2 | font-family: Helvetica, Arial, sans-serif; 3 | position: absolute; 4 | cursor: default; 5 | line-height: normal; 6 | -moz-user-select: none; 7 | -webkit-user-select: none; 8 | } 9 | 10 | .bespin-completion-pointer { 11 | position: absolute; 12 | z-index: 2; 13 | height: 21px; 14 | width: 21px; 15 | } 16 | 17 | .bespin-completion-pointer-up { 18 | top: 1px; 19 | border-top: solid #555 1px; 20 | border-left: solid #555 1px; 21 | background-image: -moz-linear-gradient(top left, #333333, #333333 50%, transparent 50%, transparent); 22 | background-image: -webkit-gradient(linear, left top, right bottom, from(#333333), color-stop(0.5, #333333), color-stop(0.5, transparent), to(transparent)); 23 | -moz-transform: rotate(45deg); 24 | -webkit-transform: rotate(45deg); 25 | } 26 | 27 | .bespin-completion-pointer-down { 28 | bottom: 1px; 29 | border-top: solid #000 1px; 30 | border-left: solid #000 1px; 31 | background-image: -moz-linear-gradient(top left, #000, #000 50%, transparent 50%, transparent); 32 | background-image: -webkit-gradient(linear, left top, right bottom, from(#000), color-stop(0.5, #000), color-stop(0.5, transparent), to(transparent)); 33 | -moz-transform: rotate(225deg); 34 | -webkit-transform: rotate(225deg); 35 | } 36 | 37 | .bespin-completion-bubble-outer { 38 | position: relative; 39 | z-index: 1; 40 | margin: 11px 0px 11px 0px; 41 | border-top: solid #555 1px; 42 | -moz-border-radius: 8px; 43 | -webkit-border-radius: 8px; 44 | } 45 | 46 | .bespin-completion-bubble-inner { 47 | position: relative; 48 | z-index: 3; 49 | padding: 6px; 50 | background: -moz-linear-gradient(top, #333333, #000000); 51 | background: -webkit-gradient(linear, center top, center bottom, from(#333333), to(#000000)); 52 | color: #ffffff; 53 | font-size: 10.5pt; 54 | -moz-border-radius: 8px; 55 | -webkit-border-radius: 8px; 56 | -moz-box-shadow: 0px 6px 16px 2px rgba(0, 0, 0, 0.5); 57 | -webkit-box-shadow: 0px 6px 16px 2px rgba(0, 0, 0, 0.5); 58 | } 59 | 60 | .bespin-completion-panel ul { 61 | list-style: none; 62 | margin: 0px; 63 | padding: 0px; 64 | } 65 | 66 | .bespin-completion-panel li { 67 | text-indent: 0px; 68 | margin: 0px; 69 | padding: 6px 16px; 70 | } 71 | 72 | .bespin-completion-highlight { 73 | position: absolute; 74 | z-index: -1; 75 | background-image: -moz-linear-gradient(top, #3e59be, #312d80); 76 | background-image: -webkit-gradient(linear, center top, center bottom, from(#3e59be), to(#312d80)); 77 | border: solid rgba(37, 34, 91, 1.0) 1px; 78 | -moz-border-radius: 6px; 79 | -webkit-border-radius: 6px; 80 | } 81 | 82 | .bespin-completion-kind { 83 | display: block; 84 | float: left; 85 | top: 0px; 86 | left: 0px; 87 | width: 8px; 88 | height: 8px; 89 | padding: 2px; 90 | margin: 0px 5px 0px 0px; 91 | font-size: 6.5pt; 92 | font-weight: bold; 93 | text-transform: uppercase; 94 | text-align: center; 95 | -moz-border-radius: 3px; 96 | -webkit-border-radius: 3px; 97 | } 98 | 99 | .bespin-completion-kind-m { 100 | background-color: maroon; 101 | } 102 | 103 | .bespin-completion-kind-f { 104 | background-color: green; 105 | } 106 | 107 | .bespin-completion-top-row { 108 | position: relative; 109 | } 110 | 111 | .bespin-completion-second-row { 112 | margin: 6px 0px 0px 17px; 113 | display: none; 114 | } 115 | 116 | .bespin-completion-ident { 117 | font-weight: bold; 118 | } 119 | 120 | .bespin-completion-container { 121 | color: #a0a0a0; 122 | } 123 | 124 | -------------------------------------------------------------------------------- /public/css/csscolors.css: -------------------------------------------------------------------------------- 1 | html { 2 | cursor: text; 3 | } 4 | 5 | .editbox { 6 | margin: .4em; 7 | padding: 0; 8 | font-family: monospace; 9 | font-size: 10pt; 10 | color: black; 11 | } 12 | 13 | pre.code, .editbox { 14 | color: #666; 15 | } 16 | 17 | .editbox p { 18 | margin: 0; 19 | } 20 | 21 | span.css-at { 22 | color: #708; 23 | } 24 | 25 | span.css-unit { 26 | color: #281; 27 | } 28 | 29 | span.css-value { 30 | color: #708; 31 | } 32 | 33 | span.css-identifier { 34 | color: black; 35 | } 36 | 37 | span.css-selector { 38 | color: #11B; 39 | } 40 | 41 | span.css-important { 42 | color: #00F; 43 | } 44 | 45 | span.css-colorcode { 46 | color: #299; 47 | } 48 | 49 | span.css-comment { 50 | color: #A70; 51 | } 52 | 53 | span.css-string { 54 | color: #A22; 55 | } 56 | -------------------------------------------------------------------------------- /public/css/docs.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 3em 6em; 4 | color: black; 5 | /*max-width: 50em;*/ 6 | } 7 | 8 | h1 { 9 | font-size: 22pt; 10 | } 11 | 12 | .underline { 13 | border-bottom: 3px solid #C44; 14 | } 15 | 16 | h2 { 17 | font-size: 14pt; 18 | } 19 | 20 | h3 { 21 | font-size: 12pt; 22 | } 23 | 24 | p.rel { 25 | padding-left: 2em; 26 | text-indent: -2em; 27 | } 28 | 29 | div.border { 30 | border: 1px solid black; 31 | } 32 | 33 | code { 34 | font-family: courier, monospace; 35 | font-size: 90%; 36 | color: #144; 37 | } 38 | 39 | pre.code { 40 | margin: 1.1em 12px; 41 | border: 1px solid #CCCCCC; 42 | color: black; 43 | padding: .4em; 44 | font-family: courier, monospace; 45 | } 46 | 47 | .warn { 48 | color: #C00; 49 | } 50 | 51 | dl dl { 52 | margin: 0; 53 | } -------------------------------------------------------------------------------- /public/css/handheld.css: -------------------------------------------------------------------------------- 1 | 2 | * { 3 | float: none; /* Screens are not big enough to account for floats */ 4 | font-size: 80%; /* Slightly reducing font size to reduce need to scroll */ 5 | background: #fff; /* As much contrast as possible */ 6 | color: #000; 7 | } -------------------------------------------------------------------------------- /public/css/jscolors.css: -------------------------------------------------------------------------------- 1 | html { 2 | cursor: text; 3 | } 4 | 5 | .editbox { 6 | margin: .4em; 7 | padding: 0; 8 | font-family: monospace; 9 | font-size: 10pt; 10 | color: black; 11 | } 12 | 13 | pre.code, .editbox { 14 | color: #666666; 15 | } 16 | 17 | .editbox p { 18 | margin: 0; 19 | } 20 | 21 | span.js-punctuation { 22 | color: #666666; 23 | } 24 | 25 | span.js-operator { 26 | color: #666666; 27 | } 28 | 29 | span.js-keyword { 30 | color: #770088; 31 | } 32 | 33 | span.js-atom { 34 | color: #228811; 35 | } 36 | 37 | span.js-variable { 38 | color: black; 39 | } 40 | 41 | span.js-variabledef { 42 | color: #0000FF; 43 | } 44 | 45 | span.js-localvariable { 46 | color: #004499; 47 | } 48 | 49 | span.js-property { 50 | color: black; 51 | } 52 | 53 | span.js-comment { 54 | color: #AA7700; 55 | } 56 | 57 | span.js-string { 58 | color: #AA2222; 59 | } 60 | -------------------------------------------------------------------------------- /public/css/people.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/css/people.jpg -------------------------------------------------------------------------------- /public/css/sparqlcolors.css: -------------------------------------------------------------------------------- 1 | html { 2 | cursor: text; 3 | } 4 | 5 | .editbox { 6 | margin: .4em; 7 | padding: 0; 8 | font-family: monospace; 9 | font-size: 10pt; 10 | color: black; 11 | } 12 | 13 | .editbox p { 14 | margin: 0; 15 | } 16 | 17 | span.sp-keyword { 18 | color: #708; 19 | } 20 | 21 | span.sp-prefixed { 22 | color: #5d1; 23 | } 24 | 25 | span.sp-var { 26 | color: #00c; 27 | } 28 | 29 | span.sp-comment { 30 | color: #a70; 31 | } 32 | 33 | span.sp-literal { 34 | color: #a22; 35 | } 36 | 37 | span.sp-uri { 38 | color: #292; 39 | } 40 | 41 | span.sp-operator { 42 | color: #088; 43 | } 44 | -------------------------------------------------------------------------------- /public/css/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | style.css contains a reset, font normalization and some base styles. 3 | 4 | credit is left where credit is due. 5 | additionally, much inspiration was taken from these projects: 6 | yui.yahooapis.com/2.8.1/build/base/base.css 7 | camendesign.com/design/ 8 | praegnanz.de/weblog/htmlcssjs-kickstart 9 | */ 10 | 11 | /* 12 | html5doctor.com Reset Stylesheet (Eric Meyer's Reset Reloaded + HTML5 baseline) 13 | v1.4 2009-07-27 | Authors: Eric Meyer & Richard Clark 14 | html5doctor.com/html-5-reset-stylesheet/ 15 | */ 16 | 17 | html, body, div, span, object, iframe, 18 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 19 | abbr, address, cite, code, 20 | del, dfn, em, img, ins, kbd, q, samp, 21 | small, strong, sub, sup, var, 22 | b, i, 23 | dl, dt, dd, ol, ul, li, 24 | fieldset, form, label, legend, 25 | table, caption, tbody, tfoot, thead, tr, th, td, 26 | article, aside, figure, footer, header, 27 | hgroup, menu, nav, section, menu, 28 | time, mark, audio, video { 29 | margin:0; 30 | padding:0; 31 | border:0; 32 | outline:0; 33 | font-size:100%; 34 | vertical-align:baseline; 35 | background:transparent; 36 | } 37 | 38 | article, aside, figure, footer, header, 39 | hgroup, nav, section { display:block; } 40 | 41 | nav ul { list-style:none; } 42 | 43 | blockquote, q { quotes:none; } 44 | 45 | blockquote:before, blockquote:after, 46 | q:before, q:after { content:''; content:none; } 47 | 48 | a { margin:0; padding:0; font-size:100%; vertical-align:baseline; background:transparent; } 49 | 50 | ins { background-color:#ff9; color:#000; text-decoration:none; } 51 | 52 | mark { background-color:#ff9; color:#000; font-style:italic; font-weight:bold; } 53 | 54 | del { text-decoration: line-through; } 55 | 56 | abbr[title], dfn[title] { border-bottom:1px dotted #000; cursor:help; } 57 | 58 | /* tables still need cellspacing="0" in the markup */ 59 | table { border-collapse:collapse; border-spacing:0; } 60 | 61 | hr { display:block; height:1px; border:0; border-top:1px solid #ccc; margin:1em 0; padding:0; } 62 | 63 | input, select { vertical-align:middle; } 64 | /* END RESET CSS */ 65 | 66 | 67 | /* 68 | fonts.css from the YUI Library: developer.yahoo.com/yui/ 69 | Please refer to developer.yahoo.com/yui/fonts/ for font sizing percentages 70 | 71 | There are three custom edits: 72 | * remove arial, helvetica from explicit font stack 73 | * make the line-height relative and unit-less 74 | * remove the pre, code styles 75 | */ 76 | body { font:13px sans-serif; *font-size:small; *font:x-small; line-height:1.22; } 77 | 78 | table { font-size:inherit; font:100%; } 79 | 80 | select, input, textarea { font:99% sans-serif; } 81 | 82 | 83 | /* normalize monospace sizing 84 | * en.wikipedia.org/wiki/MediaWiki_talk:Common.css/Archive_11#Teletype_style_fix_for_Chrome 85 | */ 86 | pre, code, kbd, samp { font-family: monospace, sans-serif; } 87 | 88 | 89 | 90 | /* 91 | * minimal base styles 92 | */ 93 | 94 | 95 | /* #444 looks better than black: twitter.com/H_FJ/statuses/11800719859 */ 96 | body, select, input, textarea { color:#444; } 97 | 98 | /* Headers (h1,h2,etc) have no default font-size or margin, 99 | you'll want to define those yourself. */ 100 | 101 | /* www.aestheticallyloyal.com/public/optimize-legibility/ */ 102 | h1,h2,h3,h4,h5,h6 { font-weight: bold; text-rendering: optimizeLegibility; } 103 | 104 | /* maxvoltar.com/archive/-webkit-font-smoothing */ 105 | html { -webkit-font-smoothing: antialiased; } 106 | 107 | 108 | /* Accessible focus treatment: people.opera.com/patrickl/experiments/keyboard/test */ 109 | a:hover, a:active { outline: none; } 110 | 111 | a, a:active, a:visited { color:#607890; } 112 | a:hover { color:#036; } 113 | 114 | 115 | ul { margin-left:30px; } 116 | ol { margin-left:30px; list-style-type: decimal; } 117 | 118 | small { font-size:85%; } 119 | strong, th { font-weight: bold; } 120 | 121 | td, td img { vertical-align:top; } 122 | 123 | sub { vertical-align: sub; font-size: smaller; } 124 | sup { vertical-align: super; font-size: smaller; } 125 | 126 | pre { 127 | padding: 15px; 128 | 129 | /* www.pathf.com/blogs/2008/05/formatting-quoted-code-in-blog-posts-css21-white-space-pre-wrap/ */ 130 | white-space: pre; /* CSS2 */ 131 | white-space: pre-wrap; /* CSS 2.1 */ 132 | white-space: pre-line; /* CSS 3 (and 2.1 as well, actually) */ 133 | word-wrap: break-word; /* IE */ 134 | } 135 | 136 | /* align checkboxes, radios, text inputs with their label 137 | by: Thierry Koblentz tjkdesign.com/ez-css/css/base.css */ 138 | input[type="radio"] { vertical-align: text-bottom; } 139 | input[type="checkbox"] { vertical-align: bottom; *vertical-align: baseline; } 140 | .ie6 input { vertical-align: text-bottom; } 141 | 142 | /* hand cursor on clickable input elements */ 143 | label, input[type=button], input[type=submit], button { cursor: pointer; } 144 | 145 | 146 | /* These selection declarations have to be separate. 147 | No text-shadow: twitter.com/miketaylr/status/12228805301 148 | Also: hot pink. */ 149 | ::-moz-selection{ background: #FF5E99; color:#fff; text-shadow: none; } 150 | ::selection { background:#FF5E99; color:#fff; text-shadow: none; } 151 | 152 | /* j.mp/webkit-tap-highlight-color */ 153 | a:link { -webkit-tap-highlight-color: #FF5E99; } 154 | 155 | 156 | /* always force a scrollbar in non-IE */ 157 | html { overflow-y: scroll; } 158 | 159 | /* make buttons play nice in IE: 160 | www.viget.com/inspire/styling-the-button-element-in-internet-explorer/ */ 161 | button { width: auto; overflow: visible; } 162 | 163 | /* bicubic resizing for non-native sized IMG: 164 | code.flickr.com/blog/2008/11/12/on-ui-quality-the-little-things-client-side-image-resizing/ */ 165 | .ie7 img { -ms-interpolation-mode: bicubic; } 166 | 167 | 168 | 169 | /* 170 | * Non-semantic helper classes 171 | */ 172 | 173 | /* for image replacement */ 174 | .ir { display:block; text-indent:-999em; overflow:hidden; background-repeat: no-repeat; } 175 | 176 | /* Hide for both screenreaders and browsers 177 | css-discuss.incutio.com/wiki/Screenreader_Visibility */ 178 | .hidden { display:none; visibility:hidden; } 179 | 180 | /* Hide only visually, but have it available for screenreaders 181 | www.webaim.org/techniques/css/invisiblecontent/ 182 | Solution from: j.mp/visuallyhidden - Thanks Jonathan Neal! */ 183 | .visuallyhidden { position:absolute !important; 184 | clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ 185 | clip: rect(1px, 1px, 1px, 1px); } 186 | 187 | /* Hide visually and from screenreaders, but maintain layout */ 188 | .invisible { visibility: hidden; } 189 | 190 | /* >> The Magnificent CLEARFIX << */ 191 | .clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } 192 | .clearfix { display: inline-block; } 193 | * html .clearfix { height: 1%; } /* Hides from IE-mac \*/ 194 | .clearfix { display: block; } 195 | 196 | 197 | 198 | 199 | 200 | /* Primary Styles 201 | Author: 202 | */ 203 | .bespin { 204 | width: 98.5%; 205 | height: 520px; 206 | margin: 0; 207 | padding: 0; 208 | border: 0; 209 | border: 10px solid #ddd; 210 | -moz-border-radius: 10px; 211 | -webkit-border-radius: 10px; 212 | } 213 | 214 | .btns { 215 | -webkit-box-shadow: rgba(255, 255, 255, 0.199219) 0px 1px 0px inset, rgba(0, 0, 0, 0.199219) 0px -1px 0px inset, rgba(0, 0, 0, 0.0976562) 0px 1px 2px; 216 | -moz-border-radius: 0.25em 0.25em 0.25em 0.25em; 217 | -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 -1px 0 rgba(0, 0, 0, 0.2) inset, 0 1px 2px rgba(0, 0, 0, 0.1); 218 | background: -moz-linear-gradient(center top, #3E6CB9, #383292) repeat scroll 0 0 transparent; 219 | background: transparent -webkit-gradient(linear, 50% 0%, 50% 100%, from(#3E6CB9), to(#383292)); 220 | border: 1px solid #25225B; 221 | border-bottom-left-radius: 0.25em 0.25em; 222 | border-bottom-right-radius: 0.25em 0.25em; 223 | border-top-left-radius: 0.25em 0.25em; 224 | border-top-right-radius: 0.25em 0.25em; 225 | color: white; 226 | font-family: arial, lucida, helvetica, sans-serif; 227 | font-size: 14px; 228 | margin-left: 8px; /*padding: 8px 12px;*/ 229 | text-shadow: rgba(0, 0, 0, 0.398438); 230 | 231 | } 232 | 233 | #message_from_top { 234 | color: #E2BE38; 235 | 236 | } 237 | 238 | .loading { 239 | background: #FFC129; 240 | color: black; 241 | font-weight: bold; 242 | padding: 3px; 243 | -moz-border-radius: 5px; 244 | -webkit-border-radius: 5px; 245 | } 246 | 247 | .loading-img, .loading-element { 248 | background: transparent; 249 | padding: 0px; 250 | } 251 | 252 | .loading.top { 253 | -moz-border-radius-topleft: 0px; 254 | -moz-border-radius-topright: 0px; 255 | -webkit-border-top-left-radius: 0px; 256 | -webkit-border-top-right-radius: 0px; 257 | } 258 | 259 | .loading.left { 260 | -moz-border-radius-topleft: 0px; 261 | -moz-border-radius-bottomleft: 0px; 262 | -webkit-border-top-left-radius: 0px; 263 | -webkit-border-bottom-left-radius: 0px; 264 | } 265 | 266 | .loading.bottom { 267 | -moz-border-radius-bottomleft: 0px; 268 | -moz-border-radius-bottomright: 0px; 269 | -webkit-border-bottom-left-radius: 0px; 270 | -webkit-border-bottom-right-radius: 0px; 271 | } 272 | 273 | .loading.right { 274 | -moz-border-radius-topright: 0px; 275 | -moz-border-radius-bottomright: 0px; 276 | -webkit-border-top-right-radius: 0px; 277 | -webkit-border-bottom-right-radius: 0px; 278 | } 279 | 280 | .loading-masked { overflow: hidden; } 281 | 282 | .loading-error { 283 | color: #FFF; 284 | background: red; 285 | } 286 | /*nodify page css*/ 287 | body { 288 | font: 62.5% "Trebuchet MS", sans-serif; 289 | margin: 50px; 290 | background-color:#1E1916; 291 | } 292 | 293 | input.store-btn { 294 | width: 70px; 295 | display: inline; 296 | } 297 | 298 | form.store-form { 299 | display: inline; 300 | } 301 | 302 | a#more, a#console-clear { 303 | color: #ffffff; 304 | } 305 | 306 | #console { 307 | display: none; 308 | background-color: black; 309 | font-size: 14px; 310 | font-family:Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace; 311 | color: #0c0; 312 | width: 98.5%; 313 | height: 140px; 314 | margin: 0; 315 | margin-top: 10px; 316 | padding: 0; 317 | border: 0; 318 | border: 10px solid #ddd; 319 | -moz-border-radius: 10px; 320 | -webkit-border-radius: 10px; 321 | } 322 | 323 | #console-label { 324 | display: none; 325 | margin: 0; 326 | margin-top: 10px; 327 | font-size: 18px; 328 | color: white; 329 | } 330 | 331 | #div-lnk-new, #div-lnk-rn, #div-lnk-del, #div-lnk-start, #div-lnk-debug, #div-lnk-stop { 332 | display:inline; 333 | border-right-width: .2em; 334 | border-right-style: solid; 335 | border-right-color: #4C4A41; 336 | padding-left:14px; 337 | padding-right:14px; 338 | padding-top:5px; 339 | } 340 | 341 | a#lnk-new, a#lnk-rn, a#lnk-del, a#lnk-start, a#lnk-debug, a#lnk-stop { 342 | color: #ffffff; 343 | } 344 | 345 | #div-lnk-new { 346 | margin-left:55px; 347 | } 348 | 349 | img#btn-forum, img#btn-twitter { 350 | top: 10px; border: 0px; 351 | } 352 | 353 | img#btn-twitter { 354 | right: 131px; 355 | } 356 | 357 | img#btn-forum { 358 | right: 257px; 359 | } 360 | 361 | #blurb { 362 | display: none; 363 | font-size: 14px; 364 | } 365 | 366 | #subtitle { 367 | font-size: 14px; 368 | color: white; 369 | } 370 | 371 | /* 372 | * print styles 373 | * inlined to avoid required HTTP connection www.phpied.com/delay-loading-your-print-css/ 374 | */ 375 | @media print { 376 | * { background: transparent !important; color: #444 !important; text-shadow: none; } 377 | 378 | a, a:visited { color: #444 !important; text-decoration: underline; } 379 | 380 | a:after { content: " (" attr(href) ")"; } 381 | 382 | abbr:after { content: " (" attr(title) ")"; } 383 | 384 | .ir a:after { content: ""; } /* Don't show links for images */ 385 | 386 | pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } 387 | 388 | img { page-break-inside: avoid; } 389 | 390 | @page { margin: 0.5cm; } 391 | 392 | p, h2, h3 { orphans: 3; widows: 3; } 393 | 394 | h2, h3{ page-break-after: avoid; } 395 | } 396 | 397 | 398 | 399 | /* 400 | * Media queries for responsive design 401 | */ 402 | 403 | @media all and (orientation:portrait) { 404 | /* Style adjustments for portrait mode goes here */ 405 | 406 | } 407 | 408 | @media all and (orientation:landscape) { 409 | /* Style adjustments for landscape mode goes here */ 410 | 411 | } 412 | 413 | /* Grade-A Mobile Browsers (Opera Mobile, iPhone Safari, Android Chrome) 414 | Consider this: www.cloudfour.com/css-media-query-for-mobile-is-fools-gold/ */ 415 | @media screen and (max-device-width: 480px) { 416 | 417 | 418 | /* Prevent iOS, WinMobile from adjusting font size */ 419 | html { -webkit-text-size-adjust:none; -ms-text-size-adjust:none; } 420 | } 421 | 422 | -------------------------------------------------------------------------------- /public/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png -------------------------------------------------------------------------------- /public/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png -------------------------------------------------------------------------------- /public/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png -------------------------------------------------------------------------------- /public/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png -------------------------------------------------------------------------------- /public/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png -------------------------------------------------------------------------------- /public/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /public/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png -------------------------------------------------------------------------------- /public/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png -------------------------------------------------------------------------------- /public/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png -------------------------------------------------------------------------------- /public/css/ui-lightness/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/css/ui-lightness/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /public/css/ui-lightness/images/ui-icons_228ef1_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/css/ui-lightness/images/ui-icons_228ef1_256x240.png -------------------------------------------------------------------------------- /public/css/ui-lightness/images/ui-icons_ef8c08_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/css/ui-lightness/images/ui-icons_ef8c08_256x240.png -------------------------------------------------------------------------------- /public/css/ui-lightness/images/ui-icons_ffd27a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/css/ui-lightness/images/ui-icons_ffd27a_256x240.png -------------------------------------------------------------------------------- /public/css/ui-lightness/images/ui-icons_ffffff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/css/ui-lightness/images/ui-icons_ffffff_256x240.png -------------------------------------------------------------------------------- /public/css/xmlcolors.css: -------------------------------------------------------------------------------- 1 | html { 2 | cursor: text; 3 | } 4 | 5 | .editbox { 6 | margin: .4em; 7 | padding: 0; 8 | font-family: monospace; 9 | font-size: 10pt; 10 | color: black; 11 | } 12 | 13 | .editbox p { 14 | margin: 0; 15 | } 16 | 17 | span.xml-tagname { 18 | color: #A0B; 19 | } 20 | 21 | span.xml-attribute { 22 | color: #281; 23 | } 24 | 25 | span.xml-punctuation { 26 | color: black; 27 | } 28 | 29 | span.xml-attname { 30 | color: #00F; 31 | } 32 | 33 | span.xml-comment { 34 | color: #A70; 35 | } 36 | 37 | span.xml-cdata { 38 | color: #48A; 39 | } 40 | 41 | span.xml-processing { 42 | color: #999; 43 | } 44 | 45 | span.xml-entity { 46 | color: #A22; 47 | } 48 | 49 | span.xml-error { 50 | color: #F00 !important; 51 | } 52 | 53 | span.xml-text { 54 | color: black; 55 | } 56 | -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/favicon.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | 12 | 13 | 14 | 15 | 21 | 22 | 23 | Nodify 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
62 |
63 |
64 |
65 |
a web-based IDE for NodeJS applications (more)
66 |
67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 |
75 |
76 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 |
87 |
88 |
89 | 90 | 91 |
92 | 93 | 94 | 95 |


96 | 97 | 98 |
99 |
100 | 101 |
Console (clear)
102 | 103 | 104 |
Nodify is a web-based IDE for creating and testing JavaScript programs on NodeJS.

You can edit and save your programming projects, using the on-screen Bespin editor that provides syntax highlighting and natural keyboard shortcuts. Executing your project is performed by clicking 'Execute'. After the execution is finished, the console will be displayed containing the program's output. If the program is a server or enters an infinite loop, it will be killed after 5 minutes, but you can terminate its execution by clicking 'Terminate'. Currently projects are single-file, but soon you will be able to have multiple files per project. You may create multiple projects, rename or delete them using the links on top of the screen. You don't need to create an account to use nodify, a unique user account is created and stored in a cookie in your browser. Currently, if you browse the service from a different browser you will create a new user account.

There is also an option to install nodify locally using npm:
npm install nodify
Then run "nodify" to launch the IDE and use your browser to access it. 105 |

106 | 107 |
108 |

 

109 |
110 | 111 |
112 |

 

113 |
114 | 115 |
116 |

Do you want to permanently delete this project?

117 |
118 | 119 | 120 | 121 | 124 | 125 | 126 | 128 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /public/js/client.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | // Register the loading indicator on ajax events. 3 | $.loading({onAjax:true, text: 'Working...', effect: 'fade', delay: 100}); 4 | // Fetch the user data. 5 | function getUserData() { 6 | $.get('/api/init', function (data) { 7 | var projects, project, handlers, handler, h, p, edit, env; 8 | window.data = data; 9 | $('#projects').empty(); 10 | projects = data.user.projects; 11 | for (p in projects) { 12 | if (projects.hasOwnProperty(p)) { 13 | project = projects[p]; 14 | log('project=' + project.name); 15 | if (data.user.lastProject && project.name === data.user.lastProject.name) 16 | $('#projects').append(''); 17 | else 18 | $('#projects').append(''); 19 | } 20 | } 21 | if (!data.user.lastProject && project) 22 | data.user.lastProject = project; 23 | try { 24 | handlers = data.user.lastProject.handlers; 25 | } catch (e) { 26 | handlers = {}; 27 | } 28 | for (h in handlers) { 29 | if (handlers.hasOwnProperty(h)) { 30 | handler = handlers[h]; 31 | log('method=' + handler.method + ',uri=' + handler.uri); 32 | // Get the DOM node with the Bespin instance inside 33 | edit = document.getElementById("editor1"); 34 | // Get the environment variable. 35 | env = edit.bespin; 36 | // Get the editor. 37 | if (env && env.editor) { 38 | env.editor.value = data.user.lastProject.handlers['GET /'].code; 39 | env.editor.focus = true; 40 | } 41 | } 42 | }; 43 | }); 44 | } 45 | // Retrieve the user data. 46 | getUserData(); 47 | 48 | function nodifyMsg(msg, type) { 49 | var backgroundColor = ""; 50 | if (type === "error") { 51 | backgroundColor = "red"; 52 | // This will not work 53 | //$("#message_from_top").css("color","black"); 54 | } else { 55 | backgroundColor = "#4C4A41"; 56 | // This will not work 57 | // $("#message_from_top").css("color","#E2BE38"); 58 | } 59 | var options = {id: 'message_from_top', 60 | position: 'top', 61 | size: 20, 62 | backgroundColor: backgroundColor, 63 | delay: 3500, 64 | speed: 500, 65 | fontSize: '16px' 66 | }; 67 | 68 | $.showMessage(msg, options) 69 | } 70 | 71 | function consoleAppend (msg, type) { 72 | if (type === 'error') { 73 | msg = 'Error:\n------\n' + msg; 74 | } else { 75 | msg = 'Output:\n-------\n' + msg; 76 | } 77 | var c = $("#console"); 78 | var contents = c.val(); 79 | c.val(contents + '\n' + msg); 80 | c.css("display", "block"); 81 | c.attr("scrollTop", c.attr("scrollHeight")); 82 | var cl = $("#console-label"); 83 | cl.css("display", "block"); 84 | // Get the DOM node with the Bespin instance inside 85 | var edit = document.getElementById("editor1"); 86 | // Get the environment variable. 87 | var env = edit.bespin; 88 | // Get the editor. 89 | if (env && env.editor) 90 | env.editor.dimensionsChanged(); 91 | } 92 | 93 | $('#projects').change(function() { 94 | var project = window.data.user.projects[$(this).val()]; 95 | // Get the DOM node with the Bespin instance inside 96 | var edit = document.getElementById("editor1"); 97 | // Get the environment variable. 98 | var env = edit.bespin; 99 | // Get the editor. 100 | if (env && env.editor) { 101 | var editor = env.editor; 102 | editor.value = project.lastHandler.code; 103 | editor.focus = true; 104 | } 105 | }); 106 | 107 | $('#save-btn').click(function() { 108 | // Get the DOM node with the Bespin instance inside 109 | var edit = document.getElementById("editor1"); 110 | // Get the environment variable. 111 | var env = edit.bespin; 112 | // Get the editor. 113 | if (env && env.editor) 114 | var editor = env.editor; 115 | $.ajax({ 116 | url: '/api/init', 117 | type: 'PUT', 118 | data: { 119 | method: 'GET', 120 | project: $('#projects').val(), 121 | code: encodeURIComponent(editor.value), 122 | uri: '/' 123 | }, 124 | success: function () { 125 | nodifyMsg("The contents were saved"); 126 | getUserData(); 127 | editor.focus = true; 128 | }, 129 | dataType: "text", 130 | error: function(request, status, error) { 131 | nodifyMsg("Error while saving file: " + error, "error"); 132 | } 133 | }); 134 | }); 135 | 136 | $('#revert-btn').click(function() { 137 | $.get('/api/init', function (data) { 138 | window.data = data; 139 | // Get the DOM node with the Bespin instance inside 140 | var edit = document.getElementById("editor1"); 141 | // Get the environment variable. 142 | var env = edit.bespin; 143 | // Get the editor. 144 | if (env && env.editor) 145 | var editor = env.editor; 146 | editor.value = data.user.projects[$('#projects').val()].handlers['GET /'].code; 147 | nodifyMsg("The contents were reverted"); 148 | editor.focus = true; 149 | }); 150 | }); 151 | 152 | $('#lnk-start').click(function() { 153 | $.ajax({ 154 | url: '/api/deploy', 155 | type: 'POST', 156 | data: {'project': $('#projects').val()}, 157 | success: function (data) { 158 | // Get the DOM node with the Bespin instance inside 159 | var edit = document.getElementById("editor1"); 160 | // Get the environment variable. 161 | var env = edit.bespin; 162 | // Get the editor. 163 | if (env && env.editor) 164 | var editor = env.editor; 165 | consoleAppend(data); 166 | editor.focus = true; 167 | }, 168 | error: function(request, status, error) { 169 | consoleAppend(error, "error"); 170 | }, 171 | dataType: 'text' 172 | }); 173 | }); 174 | 175 | $('#lnk-stop').click(function() { 176 | $.ajax({ 177 | url: '/api/terminate', 178 | type: 'POST', 179 | data: {'project': $('#projects').val()}, 180 | success: function (data) { 181 | // Get the DOM node with the Bespin instance inside 182 | var edit = document.getElementById("editor1"); 183 | // Get the environment variable. 184 | var env = edit.bespin; 185 | // Get the editor. 186 | if (env && env.editor) 187 | var editor = env.editor; 188 | nodifyMsg("The program was terminated"); 189 | editor.focus = true; 190 | }, 191 | error: function(request, status, error) { 192 | consoleAppend(error, "error"); 193 | }, 194 | dataType: 'text' 195 | }); 196 | }); 197 | 198 | $('#dialog-project-delete').dialog({ 199 | resizable: false, 200 | height: 140, 201 | modal: true, 202 | autoOpen: false, 203 | buttons: { 204 | 'Delete project': function() { 205 | $(this).dialog('close'); 206 | $.ajax({ 207 | url: '/api/init', 208 | type: 'DELETE', 209 | data: {'project': $('#projects').val()}, 210 | success: function (data) { 211 | nodifyMsg("The project was deleted"); 212 | getUserData(); 213 | }, 214 | error: function(request, status, error) { 215 | nodifyMsg(error, "error"); 216 | }, 217 | dataType: 'text' 218 | }); 219 | }, 220 | 'Cancel': function() { 221 | $(this).dialog('close'); 222 | } 223 | } 224 | }); 225 | 226 | $('#lnk-rn').click(function() { 227 | $('#dialog-project-rename').dialog('open'); 228 | return false; // prevent default action 229 | }); 230 | $('#lnk-del').click(function() { 231 | $('#dialog-project-delete').dialog('open'); 232 | return false; // prevent default action 233 | }); 234 | 235 | $('#lnk-new').click(function() { 236 | $("#dialog-project-new").dialog('open'); 237 | return false; // prevent default action 238 | }); 239 | 240 | $("#dialog-project-new").dialog({ 241 | autoOpen: false, 242 | resizable: false, 243 | height: 140, 244 | modal: true, 245 | buttons: { 246 | 'Save': function() { 247 | $(this).dialog('close'); 248 | var newProjectName = $("#btn-project-new-name").val(); 249 | $.ajax({ 250 | url: '/api/init', 251 | type: 'POST', 252 | data: { 253 | create: encodeURIComponent(newProjectName) 254 | }, 255 | success: function () { 256 | $("#dialog-project-new").dialog('close'); 257 | nodifyMsg("Project " + newProjectName + " was created"); 258 | getUserData(); 259 | }, 260 | dataType: "text", 261 | error: function(request, status, error) { 262 | $("#dialog-project-new").dialog('close'); 263 | nodifyMsg("Error while creating project: " + error, "error"); 264 | } 265 | }); 266 | }, 267 | 'Cancel': function() { 268 | $(this).dialog('close'); 269 | } 270 | } 271 | }); 272 | 273 | $("#dialog-project-rename").dialog({ 274 | autoOpen: false, 275 | resizable: false, 276 | height: 140, 277 | modal: true, 278 | buttons: { 279 | 'Rename': function() { 280 | $(this).dialog('close'); 281 | var newProjectName = $("#btn-project-rn-name").val(); 282 | $.ajax({ 283 | url: '/api/init', 284 | type: 'POST', 285 | data: { 286 | project: $('#projects').val(), 287 | rename: encodeURIComponent(newProjectName) 288 | }, 289 | success: function () { 290 | $("#dialog-project-rename").dialog('close'); 291 | nodifyMsg("Project was renamed to " + newProjectName); 292 | getUserData(); 293 | }, 294 | dataType: "text", 295 | error: function(request, status, error) { 296 | $("#dialog-project-rename").dialog('close'); 297 | nodifyMsg("Error while renaming project: " + error, "error"); 298 | } 299 | }); 300 | }, 301 | 'Cancel': function() { 302 | $(this).dialog('close'); 303 | } 304 | } 305 | }); 306 | 307 | $("#blurb").dialog({ 308 | autoOpen: false, 309 | resizable: false, 310 | height: 440, 311 | width: 440, 312 | modal: true, 313 | }); 314 | 315 | $("#more").click(function() { 316 | $("#blurb").dialog('open'); 317 | return false; // prevent default action 318 | }); 319 | 320 | $("#console-clear").click(function() { 321 | $("#console").val(""); 322 | return false; // prevent default action 323 | }); 324 | 325 | $('#lnk-debug').click(function() { 326 | $.ajax({ 327 | url: '/api/debug', 328 | type: 'POST', 329 | data: {'project': $('#projects').val()}, 330 | success: function (data) { 331 | nodifyMsg("Started debugging"); 332 | setTimeout(function () { 333 | window.open('http://127.0.0.1:8080/', 'nodify-debugger'); 334 | }, 2000); 335 | }, 336 | error: function(request, status, error) { 337 | consoleAppend(error, "error"); 338 | }, 339 | dataType: 'text' 340 | }); 341 | }); 342 | }); 343 | 344 | window.onBespinLoad = function() { 345 | // Get the DOM node with the Bespin instance inside 346 | var edit = document.getElementById("editor1"); 347 | // Get the environment variable. 348 | var env = edit.bespin; 349 | // Get the editor. 350 | var editor = env.editor; 351 | env.settings.set("tabstop", 4); 352 | editor.syntax = "js"; 353 | editor.focus = true; 354 | if (window.data) 355 | editor.value = window.data.user.lastProject.handlers['GET /'].code; 356 | } 357 | -------------------------------------------------------------------------------- /public/js/dd_belatedpng.js: -------------------------------------------------------------------------------- 1 | /** 2 | * DD_belatedPNG: Adds IE6 support: PNG images for CSS background-image and HTML . 3 | * Author: Drew Diller 4 | * Email: drew.diller@gmail.com 5 | * URL: http://www.dillerdesign.com/experiment/DD_belatedPNG/ 6 | * Version: 0.0.8a 7 | * Licensed under the MIT License: http://dillerdesign.com/experiment/DD_belatedPNG/#license 8 | * 9 | * Example usage: 10 | * DD_belatedPNG.fix('.png_bg'); // argument is a CSS selector 11 | * DD_belatedPNG.fixPng( someNode ); // argument is an HTMLDomElement 12 | **/ 13 | 14 | /* 15 | PLEASE READ: 16 | Absolutely everything in this script is SILLY. I know this. IE's rendering of certain pixels doesn't make sense, so neither does this code! 17 | */ 18 | 19 | var DD_belatedPNG = { 20 | ns: 'DD_belatedPNG', 21 | imgSize: {}, 22 | delay: 10, 23 | nodesFixed: 0, 24 | createVmlNameSpace: function () { /* enable VML */ 25 | if (document.namespaces && !document.namespaces[this.ns]) { 26 | document.namespaces.add(this.ns, 'urn:schemas-microsoft-com:vml'); 27 | } 28 | }, 29 | createVmlStyleSheet: function () { /* style VML, enable behaviors */ 30 | /* 31 | Just in case lots of other developers have added 32 | lots of other stylesheets using document.createStyleSheet 33 | and hit the 31-limit mark, let's not use that method! 34 | further reading: http://msdn.microsoft.com/en-us/library/ms531194(VS.85).aspx 35 | */ 36 | var screenStyleSheet, printStyleSheet; 37 | screenStyleSheet = document.createElement('style'); 38 | screenStyleSheet.setAttribute('media', 'screen'); 39 | document.documentElement.firstChild.insertBefore(screenStyleSheet, document.documentElement.firstChild.firstChild); 40 | if (screenStyleSheet.styleSheet) { 41 | screenStyleSheet = screenStyleSheet.styleSheet; 42 | screenStyleSheet.addRule(this.ns + '\\:*', '{behavior:url(#default#VML)}'); 43 | screenStyleSheet.addRule(this.ns + '\\:shape', 'position:absolute;'); 44 | screenStyleSheet.addRule('img.' + this.ns + '_sizeFinder', 'behavior:none; border:none; position:absolute; z-index:-1; top:-10000px; visibility:hidden;'); /* large negative top value for avoiding vertical scrollbars for large images, suggested by James O'Brien, http://www.thanatopsic.org/hendrik/ */ 45 | this.screenStyleSheet = screenStyleSheet; 46 | 47 | /* Add a print-media stylesheet, for preventing VML artifacts from showing up in print (including preview). */ 48 | /* Thanks to Rémi Prévost for automating this! */ 49 | printStyleSheet = document.createElement('style'); 50 | printStyleSheet.setAttribute('media', 'print'); 51 | document.documentElement.firstChild.insertBefore(printStyleSheet, document.documentElement.firstChild.firstChild); 52 | printStyleSheet = printStyleSheet.styleSheet; 53 | printStyleSheet.addRule(this.ns + '\\:*', '{display: none !important;}'); 54 | printStyleSheet.addRule('img.' + this.ns + '_sizeFinder', '{display: none !important;}'); 55 | } 56 | }, 57 | readPropertyChange: function () { 58 | var el, display, v; 59 | el = event.srcElement; 60 | if (!el.vmlInitiated) { 61 | return; 62 | } 63 | if (event.propertyName.search('background') != -1 || event.propertyName.search('border') != -1) { 64 | DD_belatedPNG.applyVML(el); 65 | } 66 | if (event.propertyName == 'style.display') { 67 | display = (el.currentStyle.display == 'none') ? 'none' : 'block'; 68 | for (v in el.vml) { 69 | if (el.vml.hasOwnProperty(v)) { 70 | el.vml[v].shape.style.display = display; 71 | } 72 | } 73 | } 74 | if (event.propertyName.search('filter') != -1) { 75 | DD_belatedPNG.vmlOpacity(el); 76 | } 77 | }, 78 | vmlOpacity: function (el) { 79 | if (el.currentStyle.filter.search('lpha') != -1) { 80 | var trans = el.currentStyle.filter; 81 | trans = parseInt(trans.substring(trans.lastIndexOf('=')+1, trans.lastIndexOf(')')), 10)/100; 82 | el.vml.color.shape.style.filter = el.currentStyle.filter; /* complete guesswork */ 83 | el.vml.image.fill.opacity = trans; /* complete guesswork */ 84 | } 85 | }, 86 | handlePseudoHover: function (el) { 87 | setTimeout(function () { /* wouldn't work as intended without setTimeout */ 88 | DD_belatedPNG.applyVML(el); 89 | }, 1); 90 | }, 91 | /** 92 | * This is the method to use in a document. 93 | * @param {String} selector - REQUIRED - a CSS selector, such as '#doc .container' 94 | **/ 95 | fix: function (selector) { 96 | if (this.screenStyleSheet) { 97 | var selectors, i; 98 | selectors = selector.split(','); /* multiple selectors supported, no need for multiple calls to this anymore */ 99 | for (i=0; i size.H) { 241 | c.B = size.H; 242 | } 243 | el.vml.image.shape.style.clip = 'rect('+c.T+'px '+(c.R+fudge)+'px '+c.B+'px '+(c.L+fudge)+'px)'; 244 | } 245 | else { 246 | el.vml.image.shape.style.clip = 'rect('+dC.T+'px '+dC.R+'px '+dC.B+'px '+dC.L+'px)'; 247 | } 248 | }, 249 | figurePercentage: function (bg, size, axis, position) { 250 | var horizontal, fraction; 251 | fraction = true; 252 | horizontal = (axis == 'X'); 253 | switch(position) { 254 | case 'left': 255 | case 'top': 256 | bg[axis] = 0; 257 | break; 258 | case 'center': 259 | bg[axis] = 0.5; 260 | break; 261 | case 'right': 262 | case 'bottom': 263 | bg[axis] = 1; 264 | break; 265 | default: 266 | if (position.search('%') != -1) { 267 | bg[axis] = parseInt(position, 10) / 100; 268 | } 269 | else { 270 | fraction = false; 271 | } 272 | } 273 | bg[axis] = Math.ceil( fraction ? ( (size[horizontal?'W': 'H'] * bg[axis]) - (size[horizontal?'w': 'h'] * bg[axis]) ) : parseInt(position, 10) ); 274 | if (bg[axis] % 2 === 0) { 275 | bg[axis]++; 276 | } 277 | return bg[axis]; 278 | }, 279 | fixPng: function (el) { 280 | el.style.behavior = 'none'; 281 | var lib, els, nodeStr, v, e; 282 | if (el.nodeName == 'BODY' || el.nodeName == 'TD' || el.nodeName == 'TR') { /* elements not supported yet */ 283 | return; 284 | } 285 | el.isImg = false; 286 | if (el.nodeName == 'IMG') { 287 | if(el.src.toLowerCase().search(/\.png$/) != -1) { 288 | el.isImg = true; 289 | el.style.visibility = 'hidden'; 290 | } 291 | else { 292 | return; 293 | } 294 | } 295 | else if (el.currentStyle.backgroundImage.toLowerCase().search('.png') == -1) { 296 | return; 297 | } 298 | lib = DD_belatedPNG; 299 | el.vml = {color: {}, image: {}}; 300 | els = {shape: {}, fill: {}}; 301 | for (v in el.vml) { 302 | if (el.vml.hasOwnProperty(v)) { 303 | for (e in els) { 304 | if (els.hasOwnProperty(e)) { 305 | nodeStr = lib.ns + ':' + e; 306 | el.vml[v][e] = document.createElement(nodeStr); 307 | } 308 | } 309 | el.vml[v].shape.stroked = false; 310 | el.vml[v].shape.appendChild(el.vml[v].fill); 311 | el.parentNode.insertBefore(el.vml[v].shape, el); 312 | } 313 | } 314 | el.vml.image.shape.fillcolor = 'none'; /* Don't show blank white shapeangle when waiting for image to load. */ 315 | el.vml.image.fill.type = 'tile'; /* Makes image show up. */ 316 | el.vml.color.fill.on = false; /* Actually going to apply vml element's style.backgroundColor, so hide the whiteness. */ 317 | lib.attachHandlers(el); 318 | lib.giveLayout(el); 319 | lib.giveLayout(el.offsetParent); 320 | el.vmlInitiated = true; 321 | lib.applyVML(el); /* Render! */ 322 | } 323 | }; 324 | try { 325 | document.execCommand("BackgroundImageCache", false, true); /* TredoSoft Multiple IE doesn't like this, so try{} it */ 326 | } catch(r) {} 327 | DD_belatedPNG.createVmlNameSpace(); 328 | DD_belatedPNG.createVmlStyleSheet(); -------------------------------------------------------------------------------- /public/js/highlight.js: -------------------------------------------------------------------------------- 1 | // Minimal framing needed to use CodeMirror-style parsers to highlight 2 | // code. Load this along with tokenize.js, stringstream.js, and your 3 | // parser. Then call highlightText, passing a string as the first 4 | // argument, and as the second argument either a callback function 5 | // that will be called with an array of SPAN nodes for every line in 6 | // the code, or a DOM node to which to append these spans, and 7 | // optionally (not needed if you only loaded one parser) a parser 8 | // object. 9 | 10 | // Stuff from util.js that the parsers are using. 11 | var StopIteration = {toString: function() {return "StopIteration"}}; 12 | 13 | var Editor = {}; 14 | var indentUnit = 2; 15 | 16 | (function(){ 17 | function normaliseString(string) { 18 | var tab = ""; 19 | for (var i = 0; i < indentUnit; i++) tab += " "; 20 | 21 | string = string.replace(/\t/g, tab).replace(/\u00a0/g, " ").replace(/\r\n?/g, "\n"); 22 | var pos = 0, parts = [], lines = string.split("\n"); 23 | for (var line = 0; line < lines.length; line++) { 24 | if (line != 0) parts.push("\n"); 25 | parts.push(lines[line]); 26 | } 27 | 28 | return { 29 | next: function() { 30 | if (pos < parts.length) return parts[pos++]; 31 | else throw StopIteration; 32 | } 33 | }; 34 | } 35 | 36 | window.highlightText = function(string, callback, parser) { 37 | parser = (parser || Editor.Parser).make(stringStream(normaliseString(string))); 38 | var line = []; 39 | if (callback.nodeType == 1) { 40 | var node = callback; 41 | callback = function(line) { 42 | for (var i = 0; i < line.length; i++) 43 | node.appendChild(line[i]); 44 | node.appendChild(document.createElement("BR")); 45 | }; 46 | } 47 | 48 | try { 49 | while (true) { 50 | var token = parser.next(); 51 | if (token.value == "\n") { 52 | callback(line); 53 | line = []; 54 | } 55 | else { 56 | var span = document.createElement("SPAN"); 57 | span.className = token.style; 58 | span.appendChild(document.createTextNode(token.value)); 59 | line.push(span); 60 | } 61 | } 62 | } 63 | catch (e) { 64 | if (e != StopIteration) throw e; 65 | } 66 | if (line.length) callback(line); 67 | } 68 | })(); 69 | -------------------------------------------------------------------------------- /public/js/jquery.loading.min.js: -------------------------------------------------------------------------------- 1 | ;(function($){var L=$.loading=function(show,opts){return $('body').loading(show,opts,true);};$.fn.loading=function(show,opts,page){opts=toOpts(show,opts);var base=page?$.extend(true,{},L,L.pageOptions):L;return this.each(function(){var $el=$(this),l=$.extend(true,{},base,$.metadata?$el.metadata():null,opts);if(typeof l.onAjax=="boolean"){L.setAjax.call($el,l);}else{L.toggle.call($el,l);}});};var fixed={position:$.browser.msie?'absolute':'fixed'};$.extend(L,{version:"1.6.1",align:'top-left',pulse:'working error',mask:false,img:null,element:null,text:'Loading...',onAjax:undefined,delay:0,max:0,classname:'loading',imgClass:'loading-img',elementClass:'loading-element',maskClass:'loading-mask',css:{position:'absolute',whiteSpace:'nowrap',zIndex:1001},maskCss:{position:'absolute',opacity:.15,background:'#333',zIndex:101,display:'block',cursor:'wait'},cloneEvents:true,pageOptions:{page:true,align:'top-center',css:fixed,maskCss:fixed},html:'
',maskHtml:'
',maskedClass:'loading-masked',maskEvents:'mousedown mouseup keydown keypress',resizeEvents:'resize',working:{time:10000,text:'Still working...',run:function(l){var w=l.working,self=this;w.timeout=setTimeout(function(){self.height('auto').width('auto').text(l.text=w.text);l.place.call(self,l);},w.time);}},error:{time:100000,text:'Task may have failed...',classname:'loading-error',run:function(l){var e=l.error,self=this;e.timeout=setTimeout(function(){self.height('auto').width('auto').text(l.text=e.text).addClass(e.classname);l.place.call(self,l);},e.time);}},fade:{time:800,speed:'slow',run:function(l){var f=l.fade,s=f.speed,self=this;f.interval=setInterval(function(){self.fadeOut(s).fadeIn(s);},f.time);}},ellipsis:{time:300,run:function(l){var e=l.ellipsis,self=this;e.interval=setInterval(function(){var et=self.text(),t=l.text,i=dotIndex(t);self.text((et.length-i)<3?et+'.':t.substring(0,i));},e.time);function dotIndex(t){var x=t.indexOf('.');return x<0?t.length:x;}}},type:{time:100,run:function(l){var t=l.type,self=this;t.interval=setInterval(function(){var e=self.text(),el=e.length,txt=l.text;self.text(el==txt.length?txt.charAt(0):txt.substring(0,el+1));},t.time);}},toggle:function(l){var old=this.data('loading');if(old){if(l.show!==true)old.off.call(this,old,l);}else{if(l.show!==false)l.on.call(this,l);}},setAjax:function(l){if(l.onAjax){var self=this,A=l.ajax={start:function(){l.on.call(self,l);},stop:function(){l.off.call(self,l,l);}};this.bind('ajaxStart.loading',A.start).bind('ajaxStop.loading',A.stop);}else{this.unbind('ajaxStart.loading ajaxStop.loading');}},on:function(l,force){var p=l.parent=this.data('loading',l);if(l.max)l.maxout=setTimeout(function(){l.off.call(p,l,l);},l.max);if(l.delay&&!force){return l.timeout=setTimeout(function(){delete l.timeout;l.on.call(p,l,true);},l.delay);} 2 | if(l.mask)l.mask=l.createMask.call(p,l);l.display=l.create.call(p,l);if(l.img){l.initImg.call(p,l);}else if(l.element){l.initElement.call(p,l);}else{l.init.call(p,l);} 3 | p.trigger('loadingStart',[l]);},initImg:function(l){var self=this;l.img=$('').bind('load',function(){l.init.call(self,l);});l.display.addClass(l.imgClass).append(l.img);},initElement:function(l){l.element=$(l.element).clone(l.cloneEvents).show();l.display.addClass(l.elementClass).append(l.element);l.init.call(this,l);},init:function(l){l.place.call(l.display,l);if(l.pulse)l.initPulse.call(this,l);},initPulse:function(l){$.each(l.pulse.split(' '),function(){l[this].run.call(l.display,l);});},create:function(l){var el=$(l.html).addClass(l.classname).css(l.css).appendTo(this);if(l.text&&!l.img&&!l.element)el.text(l.text);$(window).bind(l.resizeEvents,l.resizer=function(){l.resize(l);});return el;},resize:function(l){l.parent.box=null;if(l.mask)l.mask.hide();l.place.call(l.display.hide(),l);if(l.mask)l.mask.show().css(l.parent.box);},createMask:function(l){var box=l.measure.call(this.addClass(l.maskedClass),l);l.handler=function(e){return l.maskHandler(e,l);};$(document).bind(l.maskEvents,l.handler);return $(l.maskHtml).addClass(l.maskClass).css(box).css(l.maskCss).appendTo(this);},maskHandler:function(e,l){var $els=$(e.target).parents().andSelf();if($els.filter('.'+l.classname).length!=0)return true;return!l.page&&$els.filter('.'+l.maskedClass).length==0;},place:function(l){var box=l.align,v='top',h='left';if(typeof box=="object"){box=$.extend(l.calc.call(this,v,h,l),box);}else{if(box!='top-left'){var s=box.split('-');if(s.length==1){v=h=s[0];}else{v=s[0];h=s[1];}} 4 | if(!this.hasClass(v))this.addClass(v);if(!this.hasClass(h))this.addClass(h);box=l.calc.call(this,v,h,l);} 5 | this.show().css(l.box=box);},calc:function(v,h,l){var box=$.extend({},l.measure.call(l.parent,l)),H=$.boxModel?this.height():this.innerHeight(),W=$.boxModel?this.width():this.innerWidth();if(v!='top'){var d=box.height-H;if(v=='center'){d/=2;}else if(v!='bottom'){d=0;}else if($.boxModel){d-=css(this,'paddingTop')+css(this,'paddingBottom');} 6 | box.top+=d;} 7 | if(h!='left'){var d=box.width-W;if(h=='center'){d/=2;}else if(h!='right'){d=0;}else if($.boxModel){d-=css(this,'paddingLeft')+css(this,'paddingRight');} 8 | box.left+=d;} 9 | box.height=H;box.width=W;return box;},measure:function(l){return this.box||(this.box=l.page?l.pageBox(l):l.elementBox(this,l));},elementBox:function(e,l){var box=e.position();box.top+=css(e,'marginTop');box.left+=css(e,'marginLeft');box.height=e.outerHeight();box.width=e.outerWidth();return box;},pageBox:function(l){var full=$.boxModel&&l.css.position!='fixed';return{top:0,left:0,height:get(full,'Height'),width:get(full,'Width')};function get(full,side){var doc=document;if(full){var s=side.toLowerCase(),d=$(doc)[s](),w=$(window)[s]();return d-css($(doc.body),'marginTop')>w?d:w;} 10 | var c='client'+side;return Math.max(doc.documentElement[c],doc.body[c]);}},off:function(old,l){this.data('loading',null);if(old.maxout)clearTimeout(old.maxout);if(old.timeout)return clearTimeout(old.timeout);if(old.pulse)old.stopPulse.call(this,old,l);if(old.mask)old.stopMask.call(this,old,l);$(window).unbind(old.resizeEvents,old.resizer);if(old.display)old.display.remove();if(old.parent)old.parent.trigger('loadingEnd',[old]);},stopPulse:function(old,l){$.each(old.pulse.split(' '),function(){var p=old[this];if(p.end)p.end.call(l.display,old,l);if(p.interval)clearInterval(p.interval);if(p.timeout)clearTimeout(p.timeout);});},stopMask:function(old,l){this.removeClass(l.maskedClass);$(document).unbind(old.maskEvents,old.handler);old.mask.remove();}});function toOpts(s,l){if(l===undefined){l=(typeof s=="boolean")?{show:s}:s;}else{l.show=s;} 11 | if(l&&(l.img||l.element)&&!l.pulse)l.pulse=false;if(l&&l.onAjax!==undefined&&l.show===undefined)l.show=false;return l;} 12 | function css(el,prop){var val=el.css(prop);return val=='auto'?0:parseFloat(val,10);}})(jQuery); -------------------------------------------------------------------------------- /public/js/jquery.slidingmessage.min.js: -------------------------------------------------------------------------------- 1 | (function(){jQuery.showMessage=function(b,a){settings=jQuery.extend({id:"sliding_message_box",position:"bottom",size:"90",backgroundColor:"rgb(143, 177, 240)",delay:1500,speed:500,fontSize:"30px"},a);a=$("#"+settings.id);if(a.length==0){a=$("
").attr("id",settings.id);a.css({"z-index":"999","background-color":settings.backgroundColor,"text-align":"center",position:"absolute",position:"fixed",left:"0",width:"100%","line-height":settings.size+"px","font-size":settings.fontSize});$("body").append(a)}a.html(b); 2 | if(settings.position=="bottom"){a.css("bottom","-"+settings.size+"px");a.animate({bottom:"0"},settings.speed);b='$("#'+settings.id+'").animate({bottom:"-'+settings.size+'px"}, '+settings.speed+");";setTimeout(b,settings.delay)}else if(settings.position=="top"){a.css("top","-"+settings.size+"px");a.animate({top:"0"},settings.speed);b='$("#'+settings.id+'").animate({top:"-'+settings.size+'px"}, '+settings.speed+");";setTimeout(b,settings.delay)}}})(jQuery); -------------------------------------------------------------------------------- /public/js/mirrorframe.js: -------------------------------------------------------------------------------- 1 | /* Demonstration of embedding CodeMirror in a bigger application. The 2 | * interface defined here is a mess of prompts and confirms, and 3 | * should probably not be used in a real project. 4 | */ 5 | 6 | function MirrorFrame(place, options) { 7 | this.home = document.createElement("DIV"); 8 | if (place.appendChild) 9 | place.appendChild(this.home); 10 | else 11 | place(this.home); 12 | 13 | var self = this; 14 | function makeButton(name, action) { 15 | var button = document.createElement("INPUT"); 16 | button.type = "button"; 17 | button.value = name; 18 | self.home.appendChild(button); 19 | button.onclick = function(){self[action].call(self);}; 20 | } 21 | 22 | makeButton("Search", "search"); 23 | makeButton("Replace", "replace"); 24 | makeButton("Current line", "line"); 25 | makeButton("Jump to line", "jump"); 26 | makeButton("Insert constructor", "macro"); 27 | makeButton("Indent all", "reindent"); 28 | 29 | this.mirror = new CodeMirror(this.home, options); 30 | } 31 | 32 | MirrorFrame.prototype = { 33 | search: function() { 34 | var text = prompt("Enter search term:", ""); 35 | if (!text) return; 36 | 37 | var first = true; 38 | do { 39 | var cursor = this.mirror.getSearchCursor(text, first); 40 | first = false; 41 | while (cursor.findNext()) { 42 | cursor.select(); 43 | if (!confirm("Search again?")) 44 | return; 45 | } 46 | } while (confirm("End of document reached. Start over?")); 47 | }, 48 | 49 | replace: function() { 50 | // This is a replace-all, but it is possible to implement a 51 | // prompting replace. 52 | var from = prompt("Enter search string:", ""), to; 53 | if (from) to = prompt("What should it be replaced with?", ""); 54 | if (to == null) return; 55 | 56 | var cursor = this.mirror.getSearchCursor(from, false); 57 | while (cursor.findNext()) 58 | cursor.replace(to); 59 | }, 60 | 61 | jump: function() { 62 | var line = prompt("Jump to line:", ""); 63 | if (line && !isNaN(Number(line))) 64 | this.mirror.jumpToLine(Number(line)); 65 | }, 66 | 67 | line: function() { 68 | alert("The cursor is currently at line " + this.mirror.currentLine()); 69 | this.mirror.focus(); 70 | }, 71 | 72 | macro: function() { 73 | var name = prompt("Name your constructor:", ""); 74 | if (name) 75 | this.mirror.replaceSelection("function " + name + "() {\n \n}\n\n" + name + ".prototype = {\n \n};\n"); 76 | }, 77 | 78 | reindent: function() { 79 | this.mirror.reindent(); 80 | } 81 | }; 82 | -------------------------------------------------------------------------------- /public/js/modernizr-1.5.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Modernizr JavaScript library 1.5 3 | * http://www.modernizr.com/ 4 | * 5 | * Copyright (c) 2009-2010 Faruk Ates - http://farukat.es/ 6 | * Dual-licensed under the BSD and MIT licenses. 7 | * http://www.modernizr.com/license/ 8 | * 9 | * Featuring major contributions by 10 | * Paul Irish - http://paulirish.com 11 | */ 12 | window.Modernizr=function(i,e,I){function C(a,b){for(var c in a)if(m[a[c]]!==I&&(!b||b(a[c],D)))return true}function r(a,b){var c=a.charAt(0).toUpperCase()+a.substr(1);return!!C([a,"Webkit"+c,"Moz"+c,"O"+c,"ms"+c,"Khtml"+c],b)}function P(){j[E]=function(a){for(var b=0,c=a.length;b7)};d.historymanagement=function(){return!!(i.history&&history.pushState)};d.draganddrop=function(){return u("drag")&&u("dragstart")&&u("dragenter")&&u("dragover")&&u("dragleave")&&u("dragend")&&u("drop")};d.websockets=function(){return"WebSocket"in i};d.rgba=function(){m.cssText="background-color:rgba(150,255,150,.5)";return(""+m.backgroundColor).indexOf("rgba")!==-1};d.hsla=function(){m.cssText="background-color:hsla(120,40%,100%,.5)";return(""+ 17 | m.backgroundColor).indexOf("rgba")!==-1};d.multiplebgs=function(){m.cssText="background:url(//:),url(//:),red url(//:)";return/(url\s*\(.*?){3}/.test(m.background)};d.backgroundsize=function(){return r("backgroundSize")};d.borderimage=function(){return r("borderImage")};d.borderradius=function(){return r("borderRadius","",function(a){return(""+a).indexOf("orderRadius")!==-1})};d.boxshadow=function(){return r("boxShadow")};d.opacity=function(){var a=y.join("opacity:.5;")+"";m.cssText=a;return(""+m.opacity).indexOf("0.5")!== 18 | -1};d.cssanimations=function(){return r("animationName")};d.csscolumns=function(){return r("columnCount")};d.cssgradients=function(){var a=("background-image:"+y.join("gradient(linear,left top,right bottom,from(#9f9),to(white));background-image:")+y.join("linear-gradient(left top,#9f9, white);background-image:")).slice(0,-17);m.cssText=a;return(""+m.backgroundImage).indexOf("gradient")!==-1};d.cssreflections=function(){return r("boxReflect")};d.csstransforms=function(){return!!C(["transformProperty", 19 | "WebkitTransform","MozTransform","OTransform","msTransform"])};d.csstransforms3d=function(){var a=!!C(["perspectiveProperty","WebkitPerspective","MozPerspective","OPerspective","msPerspective"]);if(a){var b=document.createElement("style"),c=e.createElement("div");b.textContent="@media ("+y.join("transform-3d),(")+"modernizr){#modernizr{height:3px}}";e.getElementsByTagName("head")[0].appendChild(b);c.id="modernizr";s.appendChild(c);a=c.offsetHeight===3;b.parentNode.removeChild(b);c.parentNode.removeChild(c)}return a}; 20 | d.csstransitions=function(){return r("transitionProperty")};d.fontface=function(){var a;if(/*@cc_on@if(@_jscript_version>=5)!@end@*/0)a=true;else{var b=e.createElement("style"),c=e.createElement("span"),h,t=false,g=e.body,o,w;b.textContent="@font-face{font-family:testfont;src:url('data:font/ttf;base64,AAEAAAAMAIAAAwBAT1MvMliohmwAAADMAAAAVmNtYXCp5qrBAAABJAAAANhjdnQgACICiAAAAfwAAAAEZ2FzcP//AAMAAAIAAAAACGdseWYv5OZoAAACCAAAANxoZWFk69bnvwAAAuQAAAA2aGhlYQUJAt8AAAMcAAAAJGhtdHgGDgC4AAADQAAAABRsb2NhAIQAwgAAA1QAAAAMbWF4cABVANgAAANgAAAAIG5hbWUgXduAAAADgAAABPVwb3N03NkzmgAACHgAAAA4AAECBAEsAAUAAAKZAswAAACPApkCzAAAAesAMwEJAAACAAMDAAAAAAAAgAACbwAAAAoAAAAAAAAAAFBmRWQAAAAgqS8DM/8zAFwDMwDNAAAABQAAAAAAAAAAAAMAAAADAAAAHAABAAAAAABGAAMAAQAAAK4ABAAqAAAABgAEAAEAAgAuqQD//wAAAC6pAP///9ZXAwAAAAAAAAACAAAABgBoAAAAAAAvAAEAAAAAAAAAAAAAAAAAAAABAAIAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEACoAAAAGAAQAAQACAC6pAP//AAAALqkA////1lcDAAAAAAAAAAIAAAAiAogAAAAB//8AAgACACIAAAEyAqoAAwAHAC6xAQAvPLIHBADtMrEGBdw8sgMCAO0yALEDAC88sgUEAO0ysgcGAfw8sgECAO0yMxEhESczESMiARDuzMwCqv1WIgJmAAACAFUAAAIRAc0ADwAfAAATFRQWOwEyNj0BNCYrASIGARQGKwEiJj0BNDY7ATIWFX8aIvAiGhoi8CIaAZIoN/43KCg3/jcoAWD0JB4eJPQkHh7++EY2NkbVRjY2RgAAAAABAEH/+QCdAEEACQAANjQ2MzIWFAYjIkEeEA8fHw8QDxwWFhwWAAAAAQAAAAIAAIuYbWpfDzz1AAsEAAAAAADFn9IuAAAAAMWf0i797/8zA4gDMwAAAAgAAgAAAAAAAAABAAADM/8zAFwDx/3v/98DiAABAAAAAAAAAAAAAAAAAAAABQF2ACIAAAAAAVUAAAJmAFUA3QBBAAAAKgAqACoAWgBuAAEAAAAFAFAABwBUAAQAAgAAAAEAAQAAAEAALgADAAMAAAAQAMYAAQAAAAAAAACLAAAAAQAAAAAAAQAhAIsAAQAAAAAAAgAFAKwAAQAAAAAAAwBDALEAAQAAAAAABAAnAPQAAQAAAAAABQAKARsAAQAAAAAABgAmASUAAQAAAAAADgAaAUsAAwABBAkAAAEWAWUAAwABBAkAAQBCAnsAAwABBAkAAgAKAr0AAwABBAkAAwCGAscAAwABBAkABABOA00AAwABBAkABQAUA5sAAwABBAkABgBMA68AAwABBAkADgA0A/tDb3B5cmlnaHQgMjAwOSBieSBEYW5pZWwgSm9obnNvbi4gIFJlbGVhc2VkIHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgT3BlbiBGb250IExpY2Vuc2UuIEtheWFoIExpIGdseXBocyBhcmUgcmVsZWFzZWQgdW5kZXIgdGhlIEdQTCB2ZXJzaW9uIDMuYmFlYzJhOTJiZmZlNTAzMiAtIHN1YnNldCBvZiBKdXJhTGlnaHRiYWVjMmE5MmJmZmU1MDMyIC0gc3Vic2V0IG9mIEZvbnRGb3JnZSAyLjAgOiBKdXJhIExpZ2h0IDogMjMtMS0yMDA5YmFlYzJhOTJiZmZlNTAzMiAtIHN1YnNldCBvZiBKdXJhIExpZ2h0VmVyc2lvbiAyIGJhZWMyYTkyYmZmZTUwMzIgLSBzdWJzZXQgb2YgSnVyYUxpZ2h0aHR0cDovL3NjcmlwdHMuc2lsLm9yZy9PRkwAQwBvAHAAeQByAGkAZwBoAHQAIAAyADAAMAA5ACAAYgB5ACAARABhAG4AaQBlAGwAIABKAG8AaABuAHMAbwBuAC4AIAAgAFIAZQBsAGUAYQBzAGUAZAAgAHUAbgBkAGUAcgAgAHQAaABlACAAdABlAHIAbQBzACAAbwBmACAAdABoAGUAIABPAHAAZQBuACAARgBvAG4AdAAgAEwAaQBjAGUAbgBzAGUALgAgAEsAYQB5AGEAaAAgAEwAaQAgAGcAbAB5AHAAaABzACAAYQByAGUAIAByAGUAbABlAGEAcwBlAGQAIAB1AG4AZABlAHIAIAB0AGgAZQAgAEcAUABMACAAdgBlAHIAcwBpAG8AbgAgADMALgBiAGEAZQBjADIAYQA5ADIAYgBmAGYAZQA1ADAAMwAyACAALQAgAHMAdQBiAHMAZQB0ACAAbwBmACAASgB1AHIAYQBMAGkAZwBoAHQAYgBhAGUAYwAyAGEAOQAyAGIAZgBmAGUANQAwADMAMgAgAC0AIABzAHUAYgBzAGUAdAAgAG8AZgAgAEYAbwBuAHQARgBvAHIAZwBlACAAMgAuADAAIAA6ACAASgB1AHIAYQAgAEwAaQBnAGgAdAAgADoAIAAyADMALQAxAC0AMgAwADAAOQBiAGEAZQBjADIAYQA5ADIAYgBmAGYAZQA1ADAAMwAyACAALQAgAHMAdQBiAHMAZQB0ACAAbwBmACAASgB1AHIAYQAgAEwAaQBnAGgAdABWAGUAcgBzAGkAbwBuACAAMgAgAGIAYQBlAGMAMgBhADkAMgBiAGYAZgBlADUAMAAzADIAIAAtACAAcwB1AGIAcwBlAHQAIABvAGYAIABKAHUAcgBhAEwAaQBnAGgAdABoAHQAdABwADoALwAvAHMAYwByAGkAcAB0AHMALgBzAGkAbAAuAG8AcgBnAC8ATwBGAEwAAAAAAgAAAAAAAP+BADMAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAQACAQIAEQt6ZXJva2F5YWhsaQ==')}"; 21 | e.getElementsByTagName("head")[0].appendChild(b);c.setAttribute("style","font:99px _,arial,helvetica;position:absolute;visibility:hidden");if(!g){g=s.appendChild(e.createElement("fontface"));t=true}c.innerHTML="........";c.id="fonttest";g.appendChild(c);h=c.offsetWidth*c.offsetHeight;c.style.font="99px testfont,_,arial,helvetica";a=h!==c.offsetWidth*c.offsetHeight;var v=function(){if(g.parentNode){a=j.fontface=h!==c.offsetWidth*c.offsetHeight;s.className=s.className.replace(/(no-)?fontface\b/,"")+ 22 | (a?" ":" no-")+"fontface"}};setTimeout(v,75);setTimeout(v,150);addEventListener("load",function(){v();(w=true)&&o&&o(a);setTimeout(function(){t||(g=c);g.parentNode.removeChild(g);b.parentNode.removeChild(b)},50)},false)}j._fontfaceready=function(p){w||a?p(a):(o=p)};return a||h!==c.offsetWidth};d.video=function(){var a=e.createElement("video"),b=!!a.canPlayType;if(b){b=new Boolean(b);b.ogg=a.canPlayType('video/ogg; codecs="theora"');b.h264=a.canPlayType('video/mp4; codecs="avc1.42E01E"');b.webm=a.canPlayType('video/webm; codecs="vp8, vorbis"')}return b}; 23 | d.audio=function(){var a=e.createElement("audio"),b=!!a.canPlayType;if(b){b=new Boolean(b);b.ogg=a.canPlayType('audio/ogg; codecs="vorbis"');b.mp3=a.canPlayType("audio/mpeg;");b.wav=a.canPlayType('audio/wav; codecs="1"');b.m4a=a.canPlayType("audio/x-m4a;")||a.canPlayType("audio/aac;")}return b};d.localStorage=function(){return"localStorage"in i&&i.localStorage!==null};d.sessionStorage=function(){try{return"sessionStorage"in i&&i.sessionStorage!==null}catch(a){return false}};d.webworkers=function(){return!!i.Worker}; 24 | d.applicationCache=function(){var a=i.applicationCache;return!!(a&&typeof a.status!="undefined"&&typeof a.update=="function"&&typeof a.swapCache=="function")};d.svg=function(){return!!e.createElementNS&&!!e.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect};d.smil=function(){return!!e.createElementNS&&/SVG/.test(M.call(e.createElementNS("http://www.w3.org/2000/svg","animate")))};d.svgclippaths=function(){return!!e.createElementNS&&/SVG/.test(M.call(e.createElementNS("http://www.w3.org/2000/svg", 25 | "clipPath")))};for(var z in d)if(O(d,z))N.push(((j[z.toLowerCase()]=d[z]())?"":"no-")+z.toLowerCase());j[E]||P();j.addTest=function(a,b){a=a.toLowerCase();if(!j[a]){b=!!b();s.className+=" "+(b?"":"no-")+a;j[a]=b;return j}};m.cssText="";D=n=null;(function(){var a=e.createElement("div");a.innerHTML="";return a.childNodes.length!==1})()&&function(a,b){function c(f,k){if(o[f])o[f].styleSheet.cssText+=k;else{var l=t[G],q=b[A]("style");q.media=f;l.insertBefore(q,l[G]);o[f]=q;c(f,k)}}function h(f, 26 | k){for(var l=new RegExp("\\b("+w+")\\b(?!.*[;}])","gi"),q=function(B){return".iepp_"+B},x=-1;++x\\s*$","i");g.innerHTML=f.outerHTML.replace(/\r|\n/g," ").replace(l,f.currentStyle.display=="block"?"":"");l=g.childNodes[0];l.className+=" iepp_"+q;l=p[p.length]=[f,l];f.parentNode.replaceChild(l[1],l[0])}h(b.styleSheets,"all")});a.attachEvent("onafterprint", 28 | function(){for(var f=-1,k;++f*\/]/.test(ch)) { 44 | return "css-select-op"; 45 | } 46 | else if (/[;{}:\[\]]/.test(ch)) { 47 | return "css-punctuation"; 48 | } 49 | else { 50 | source.nextWhileMatches(/[\w\\\-_]/); 51 | return "css-identifier"; 52 | } 53 | } 54 | 55 | function inCComment(source, setState) { 56 | var maybeEnd = false; 57 | while (!source.endOfLine()) { 58 | var ch = source.next(); 59 | if (maybeEnd && ch == "/") { 60 | setState(normal); 61 | break; 62 | } 63 | maybeEnd = (ch == "*"); 64 | } 65 | return "css-comment"; 66 | } 67 | 68 | function inSGMLComment(source, setState) { 69 | var dashes = 0; 70 | while (!source.endOfLine()) { 71 | var ch = source.next(); 72 | if (dashes >= 2 && ch == ">") { 73 | setState(normal); 74 | break; 75 | } 76 | dashes = (ch == "-") ? dashes + 1 : 0; 77 | } 78 | return "css-comment"; 79 | } 80 | 81 | function inString(quote) { 82 | return function(source, setState) { 83 | var escaped = false; 84 | while (!source.endOfLine()) { 85 | var ch = source.next(); 86 | if (ch == quote && !escaped) 87 | break; 88 | escaped = !escaped && ch == "\\"; 89 | } 90 | if (!escaped) 91 | setState(normal); 92 | return "css-string"; 93 | }; 94 | } 95 | 96 | return function(source, startState) { 97 | return tokenizer(source, startState || normal); 98 | }; 99 | })(); 100 | 101 | function indentCSS(inBraces, inRule, base) { 102 | return function(nextChars) { 103 | if (!inBraces || /^\}/.test(nextChars)) return base; 104 | else if (inRule) return base + indentUnit * 2; 105 | else return base + indentUnit; 106 | }; 107 | } 108 | 109 | // This is a very simplistic parser -- since CSS does not really 110 | // nest, it works acceptably well, but some nicer colouroing could 111 | // be provided with a more complicated parser. 112 | function parseCSS(source, basecolumn) { 113 | basecolumn = basecolumn || 0; 114 | var tokens = tokenizeCSS(source); 115 | var inBraces = false, inRule = false, inDecl = false;; 116 | 117 | var iter = { 118 | next: function() { 119 | var token = tokens.next(), style = token.style, content = token.content; 120 | 121 | if (style == "css-hash") 122 | style = token.style = inRule ? "css-colorcode" : "css-identifier"; 123 | if (style == "css-identifier") { 124 | if (inRule) token.style = "css-value"; 125 | else if (!inBraces && !inDecl) token.style = "css-selector"; 126 | } 127 | 128 | if (content == "\n") 129 | token.indentation = indentCSS(inBraces, inRule, basecolumn); 130 | 131 | if (content == "{") 132 | inBraces = true; 133 | else if (content == "}") 134 | inBraces = inRule = inDecl = false; 135 | else if (content == ";") 136 | inRule = inDecl = false; 137 | else if (inBraces && style != "css-comment" && style != "whitespace") 138 | inRule = true; 139 | else if (!inBraces && style == "css-at") 140 | inDecl = true; 141 | 142 | return token; 143 | }, 144 | 145 | copy: function() { 146 | var _inBraces = inBraces, _inRule = inRule, _tokenState = tokens.state; 147 | return function(source) { 148 | tokens = tokenizeCSS(source, _tokenState); 149 | inBraces = _inBraces; 150 | inRule = _inRule; 151 | return iter; 152 | }; 153 | } 154 | }; 155 | return iter; 156 | } 157 | 158 | return {make: parseCSS, electricChars: "}"}; 159 | })(); 160 | -------------------------------------------------------------------------------- /public/js/parsedummy.js: -------------------------------------------------------------------------------- 1 | var DummyParser = Editor.Parser = (function() { 2 | function tokenizeDummy(source) { 3 | while (!source.endOfLine()) source.next(); 4 | return "text"; 5 | } 6 | function parseDummy(source) { 7 | function indentTo(n) {return function() {return n;}} 8 | source = tokenizer(source, tokenizeDummy); 9 | var space = 0; 10 | 11 | var iter = { 12 | next: function() { 13 | var tok = source.next(); 14 | if (tok.type == "whitespace") { 15 | if (tok.value == "\n") tok.indentation = indentTo(space); 16 | else space = tok.value.length; 17 | } 18 | return tok; 19 | }, 20 | copy: function() { 21 | var _space = space; 22 | return function(_source) { 23 | space = _space; 24 | source = tokenizer(_source, tokenizeDummy); 25 | return iter; 26 | }; 27 | } 28 | }; 29 | return iter; 30 | } 31 | return {make: parseDummy}; 32 | })(); 33 | -------------------------------------------------------------------------------- /public/js/parsehtmlmixed.js: -------------------------------------------------------------------------------- 1 | var HTMLMixedParser = Editor.Parser = (function() { 2 | if (!(CSSParser && JSParser && XMLParser)) 3 | throw new Error("CSS, JS, and XML parsers must be loaded for HTML mixed mode to work."); 4 | XMLParser.configure({useHTMLKludges: true}); 5 | 6 | function parseMixed(stream) { 7 | var htmlParser = XMLParser.make(stream), localParser = null, inTag = false; 8 | var iter = {next: top, copy: copy}; 9 | 10 | function top() { 11 | var token = htmlParser.next(); 12 | if (token.content == "<") 13 | inTag = true; 14 | else if (token.style == "xml-tagname" && inTag === true) 15 | inTag = token.content.toLowerCase(); 16 | else if (token.content == ">") { 17 | if (inTag == "script") 18 | iter.next = local(JSParser, "= 0; i--) 155 | cc.push(fs[i]); 156 | } 157 | // cont and pass are used by the action functions to add other 158 | // actions to the stack. cont will cause the current token to be 159 | // consumed, pass will leave it for the next action. 160 | function cont(){ 161 | push(arguments); 162 | consume = true; 163 | } 164 | function pass(){ 165 | push(arguments); 166 | consume = false; 167 | } 168 | // Used to change the style of the current token. 169 | function mark(style){ 170 | marked = style; 171 | } 172 | 173 | // Push a new scope. Will automatically link the current scope. 174 | function pushcontext(){ 175 | context = {prev: context, vars: {"this": true, "arguments": true}}; 176 | } 177 | // Pop off the current scope. 178 | function popcontext(){ 179 | context = context.prev; 180 | } 181 | // Register a variable in the current scope. 182 | function register(varname){ 183 | if (context){ 184 | mark("js-variabledef"); 185 | context.vars[varname] = true; 186 | } 187 | } 188 | // Check whether a variable is defined in the current scope. 189 | function inScope(varname){ 190 | var cursor = context; 191 | while (cursor) { 192 | if (cursor.vars[varname]) 193 | return true; 194 | cursor = cursor.prev; 195 | } 196 | return false; 197 | } 198 | 199 | // Push a new lexical context of the given type. 200 | function pushlex(type, info) { 201 | var result = function(){ 202 | lexical = new JSLexical(indented, column, type, null, lexical, info) 203 | }; 204 | result.lex = true; 205 | return result; 206 | } 207 | // Pop off the current lexical context. 208 | function poplex(){ 209 | lexical = lexical.prev; 210 | } 211 | poplex.lex = true; 212 | // The 'lex' flag on these actions is used by the 'next' function 213 | // to know they can (and have to) be ran before moving on to the 214 | // next token. 215 | 216 | // Creates an action that discards tokens until it finds one of 217 | // the given type. 218 | function expect(wanted){ 219 | return function expecting(type){ 220 | if (type == wanted) cont(); 221 | else if (wanted == ";") pass(); 222 | else cont(arguments.callee); 223 | }; 224 | } 225 | 226 | // Looks for a statement, and then calls itself. 227 | function statements(type){ 228 | return pass(statement, statements); 229 | } 230 | function expressions(type){ 231 | return pass(expression, expressions); 232 | } 233 | // Dispatches various types of statements based on the type of the 234 | // current token. 235 | function statement(type){ 236 | if (type == "var") cont(pushlex("vardef"), vardef1, expect(";"), poplex); 237 | else if (type == "keyword a") cont(pushlex("form"), expression, statement, poplex); 238 | else if (type == "keyword b") cont(pushlex("form"), statement, poplex); 239 | else if (type == "{") cont(pushlex("}"), block, poplex); 240 | else if (type == ";") cont(); 241 | else if (type == "function") cont(functiondef); 242 | else if (type == "for") cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"), poplex, statement, poplex); 243 | else if (type == "variable") cont(pushlex("stat"), maybelabel); 244 | else if (type == "switch") cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"), block, poplex, poplex); 245 | else if (type == "case") cont(expression, expect(":")); 246 | else if (type == "default") cont(expect(":")); 247 | else if (type == "catch") cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"), statement, poplex, popcontext); 248 | else pass(pushlex("stat"), expression, expect(";"), poplex); 249 | } 250 | // Dispatch expression types. 251 | function expression(type){ 252 | if (atomicTypes.hasOwnProperty(type)) cont(maybeoperator); 253 | else if (type == "function") cont(functiondef); 254 | else if (type == "keyword c") cont(expression); 255 | else if (type == "(") cont(pushlex(")"), expression, expect(")"), poplex, maybeoperator); 256 | else if (type == "operator") cont(expression); 257 | else if (type == "[") cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator); 258 | else if (type == "{") cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator); 259 | else cont(); 260 | } 261 | // Called for places where operators, function calls, or 262 | // subscripts are valid. Will skip on to the next action if none 263 | // is found. 264 | function maybeoperator(type){ 265 | if (type == "operator") cont(expression); 266 | else if (type == ";") pass(); 267 | else if (type == "(") cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator); 268 | else if (type == ".") cont(property, maybeoperator); 269 | else if (type == "[") cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator); 270 | } 271 | // When a statement starts with a variable name, it might be a 272 | // label. If no colon follows, it's a regular statement. 273 | function maybelabel(type){ 274 | if (type == ":") cont(poplex, statement); 275 | else pass(maybeoperator, expect(";"), poplex); 276 | } 277 | // Property names need to have their style adjusted -- the 278 | // tokenizer thinks they are variables. 279 | function property(type){ 280 | if (type == "variable") {mark("js-property"); cont();} 281 | } 282 | // This parses a property and its value in an object literal. 283 | function objprop(type){ 284 | if (type == "variable") mark("js-property"); 285 | if (atomicTypes.hasOwnProperty(type)) cont(expect(":"), expression); 286 | } 287 | // Parses a comma-separated list of the things that are recognized 288 | // by the 'what' argument. 289 | function commasep(what, end){ 290 | function proceed(type) { 291 | if (type == ",") cont(what, proceed); 292 | else if (type == end) cont(); 293 | else cont(expect(end)); 294 | } 295 | return function commaSeparated(type) { 296 | if (type == end) cont(); 297 | else pass(what, proceed); 298 | }; 299 | } 300 | // Look for statements until a closing brace is found. 301 | function block(type){ 302 | if (type == "}") cont(); 303 | else pass(statement, block); 304 | } 305 | // Variable definitions are split into two actions -- 1 looks for 306 | // a name or the end of the definition, 2 looks for an '=' sign or 307 | // a comma. 308 | function vardef1(type, value){ 309 | if (type == "variable"){register(value); cont(vardef2);} 310 | else cont(); 311 | } 312 | function vardef2(type, value){ 313 | if (value == "=") cont(expression, vardef2); 314 | else if (type == ",") cont(vardef1); 315 | } 316 | // For loops. 317 | function forspec1(type){ 318 | if (type == "var") cont(vardef1, forspec2); 319 | else if (type == ";") pass(forspec2); 320 | else if (type == "variable") cont(formaybein); 321 | else pass(forspec2); 322 | } 323 | function formaybein(type, value){ 324 | if (value == "in") cont(expression); 325 | else cont(maybeoperator, forspec2); 326 | } 327 | function forspec2(type, value){ 328 | if (type == ";") cont(forspec3); 329 | else if (value == "in") cont(expression); 330 | else cont(expression, expect(";"), forspec3); 331 | } 332 | function forspec3(type) { 333 | if (type == ")") pass(); 334 | else cont(expression); 335 | } 336 | // A function definition creates a new context, and the variables 337 | // in its argument list have to be added to this context. 338 | function functiondef(type, value){ 339 | if (type == "variable"){register(value); cont(functiondef);} 340 | else if (type == "(") cont(pushcontext, commasep(funarg, ")"), statement, popcontext); 341 | } 342 | function funarg(type, value){ 343 | if (type == "variable"){register(value); cont();} 344 | } 345 | 346 | return parser; 347 | } 348 | 349 | return { 350 | make: parseJS, 351 | electricChars: "{}:", 352 | configure: function(obj) { 353 | if (obj.json != null) json = obj.json; 354 | } 355 | }; 356 | })(); 357 | -------------------------------------------------------------------------------- /public/js/parsesparql.js: -------------------------------------------------------------------------------- 1 | var SparqlParser = Editor.Parser = (function() { 2 | function wordRegexp(words) { 3 | return new RegExp("^(?:" + words.join("|") + ")$", "i"); 4 | } 5 | var ops = wordRegexp(["str", "lang", "langmatches", "datatype", "bound", "sameterm", "isiri", "isuri", 6 | "isblank", "isliteral", "union", "a"]); 7 | var keywords = wordRegexp(["base", "prefix", "select", "distinct", "reduced", "construct", "describe", 8 | "ask", "from", "named", "where", "order", "limit", "offset", "filter", "optional", 9 | "graph", "by", "asc", "desc"]); 10 | var operatorChars = /[*+\-<>=&|]/; 11 | 12 | var tokenizeSparql = (function() { 13 | function normal(source, setState) { 14 | var ch = source.next(); 15 | if (ch == "$" || ch == "?") { 16 | source.nextWhileMatches(/[\w\d]/); 17 | return "sp-var"; 18 | } 19 | else if (ch == "<" && !source.matches(/[\s\u00a0=]/)) { 20 | source.nextWhileMatches(/[^\s\u00a0>]/); 21 | if (source.equals(">")) source.next(); 22 | return "sp-uri"; 23 | } 24 | else if (ch == "\"" || ch == "'") { 25 | setState(inLiteral(ch)); 26 | return null; 27 | } 28 | else if (/[{}\(\),\.;\[\]]/.test(ch)) { 29 | return "sp-punc"; 30 | } 31 | else if (ch == "#") { 32 | while (!source.endOfLine()) source.next(); 33 | return "sp-comment"; 34 | } 35 | else if (operatorChars.test(ch)) { 36 | source.nextWhileMatches(operatorChars); 37 | return "sp-operator"; 38 | } 39 | else if (ch == ":") { 40 | source.nextWhileMatches(/[\w\d\._\-]/); 41 | return "sp-prefixed"; 42 | } 43 | else { 44 | source.nextWhileMatches(/[_\w\d]/); 45 | if (source.equals(":")) { 46 | source.next(); 47 | source.nextWhileMatches(/[\w\d_\-]/); 48 | return "sp-prefixed"; 49 | } 50 | var word = source.get(), type; 51 | if (ops.test(word)) 52 | type = "sp-operator"; 53 | else if (keywords.test(word)) 54 | type = "sp-keyword"; 55 | else 56 | type = "sp-word"; 57 | return {style: type, content: word}; 58 | } 59 | } 60 | 61 | function inLiteral(quote) { 62 | return function(source, setState) { 63 | var escaped = false; 64 | while (!source.endOfLine()) { 65 | var ch = source.next(); 66 | if (ch == quote && !escaped) { 67 | setState(normal); 68 | break; 69 | } 70 | escaped = !escaped && ch == "\\"; 71 | } 72 | return "sp-literal"; 73 | }; 74 | } 75 | 76 | return function(source, startState) { 77 | return tokenizer(source, startState || normal); 78 | }; 79 | })(); 80 | 81 | function indentSparql(context) { 82 | return function(nextChars) { 83 | var firstChar = nextChars && nextChars.charAt(0); 84 | if (/[\]\}]/.test(firstChar)) 85 | while (context && context.type == "pattern") context = context.prev; 86 | 87 | var closing = context && firstChar == matching[context.type]; 88 | if (!context) 89 | return 0; 90 | else if (context.type == "pattern") 91 | return context.col; 92 | else if (context.align) 93 | return context.col - (closing ? context.width : 0); 94 | else 95 | return context.indent + (closing ? 0 : indentUnit); 96 | } 97 | } 98 | 99 | function parseSparql(source) { 100 | var tokens = tokenizeSparql(source); 101 | var context = null, indent = 0, col = 0; 102 | function pushContext(type, width) { 103 | context = {prev: context, indent: indent, col: col, type: type, width: width}; 104 | } 105 | function popContext() { 106 | context = context.prev; 107 | } 108 | 109 | var iter = { 110 | next: function() { 111 | var token = tokens.next(), type = token.style, content = token.content, width = token.value.length; 112 | 113 | if (content == "\n") { 114 | token.indentation = indentSparql(context); 115 | indent = col = 0; 116 | if (context && context.align == null) context.align = false; 117 | } 118 | else if (type == "whitespace" && col == 0) { 119 | indent = width; 120 | } 121 | else if (type != "sp-comment" && context && context.align == null) { 122 | context.align = true; 123 | } 124 | 125 | if (content != "\n") col += width; 126 | 127 | if (/[\[\{\(]/.test(content)) { 128 | pushContext(content, width); 129 | } 130 | else if (/[\]\}\)]/.test(content)) { 131 | while (context && context.type == "pattern") 132 | popContext(); 133 | if (context && content == matching[context.type]) 134 | popContext(); 135 | } 136 | else if (content == "." && context && context.type == "pattern") { 137 | popContext(); 138 | } 139 | else if ((type == "sp-word" || type == "sp-prefixed" || type == "sp-uri" || type == "sp-var" || type == "sp-literal") && 140 | context && /[\{\[]/.test(context.type)) { 141 | pushContext("pattern", width); 142 | } 143 | 144 | return token; 145 | }, 146 | 147 | copy: function() { 148 | var _context = context, _indent = indent, _col = col, _tokenState = tokens.state; 149 | return function(source) { 150 | tokens = tokenizeSparql(source, _tokenState); 151 | context = _context; 152 | indent = _indent; 153 | col = _col; 154 | return iter; 155 | }; 156 | } 157 | }; 158 | return iter; 159 | } 160 | 161 | return {make: parseSparql, electricChars: "}]"}; 162 | })(); 163 | -------------------------------------------------------------------------------- /public/js/parsexml.js: -------------------------------------------------------------------------------- 1 | /* This file defines an XML parser, with a few kludges to make it 2 | * useable for HTML. autoSelfClosers defines a set of tag names that 3 | * are expected to not have a closing tag, and doNotIndent specifies 4 | * the tags inside of which no indentation should happen (see Config 5 | * object). These can be disabled by passing the editor an object like 6 | * {useHTMLKludges: false} as parserConfig option. 7 | */ 8 | 9 | var XMLParser = Editor.Parser = (function() { 10 | var Kludges = { 11 | autoSelfClosers: {"br": true, "img": true, "hr": true, "link": true, "input": true, 12 | "meta": true, "col": true, "frame": true, "base": true, "area": true}, 13 | doNotIndent: {"pre": true, "!cdata": true} 14 | }; 15 | var NoKludges = {autoSelfClosers: {}, doNotIndent: {"!cdata": true}}; 16 | var UseKludges = Kludges; 17 | var alignCDATA = false; 18 | 19 | // Simple stateful tokenizer for XML documents. Returns a 20 | // MochiKit-style iterator, with a state property that contains a 21 | // function encapsulating the current state. See tokenize.js. 22 | var tokenizeXML = (function() { 23 | function inText(source, setState) { 24 | var ch = source.next(); 25 | if (ch == "<") { 26 | if (source.equals("!")) { 27 | source.next(); 28 | if (source.equals("[")) { 29 | if (source.lookAhead("[CDATA[", true)) { 30 | setState(inBlock("xml-cdata", "]]>")); 31 | return null; 32 | } 33 | else { 34 | return "xml-text"; 35 | } 36 | } 37 | else if (source.lookAhead("--", true)) { 38 | setState(inBlock("xml-comment", "-->")); 39 | return null; 40 | } 41 | else { 42 | return "xml-text"; 43 | } 44 | } 45 | else if (source.equals("?")) { 46 | source.next(); 47 | source.nextWhileMatches(/[\w\._\-]/); 48 | setState(inBlock("xml-processing", "?>")); 49 | return "xml-processing"; 50 | } 51 | else { 52 | if (source.equals("/")) source.next(); 53 | setState(inTag); 54 | return "xml-punctuation"; 55 | } 56 | } 57 | else if (ch == "&") { 58 | while (!source.endOfLine()) { 59 | if (source.next() == ";") 60 | break; 61 | } 62 | return "xml-entity"; 63 | } 64 | else { 65 | source.nextWhileMatches(/[^&<\n]/); 66 | return "xml-text"; 67 | } 68 | } 69 | 70 | function inTag(source, setState) { 71 | var ch = source.next(); 72 | if (ch == ">") { 73 | setState(inText); 74 | return "xml-punctuation"; 75 | } 76 | else if (/[?\/]/.test(ch) && source.equals(">")) { 77 | source.next(); 78 | setState(inText); 79 | return "xml-punctuation"; 80 | } 81 | else if (ch == "=") { 82 | return "xml-punctuation"; 83 | } 84 | else if (/[\'\"]/.test(ch)) { 85 | setState(inAttribute(ch)); 86 | return null; 87 | } 88 | else { 89 | source.nextWhileMatches(/[^\s\u00a0=<>\"\'\/?]/); 90 | return "xml-name"; 91 | } 92 | } 93 | 94 | function inAttribute(quote) { 95 | return function(source, setState) { 96 | while (!source.endOfLine()) { 97 | if (source.next() == quote) { 98 | setState(inTag); 99 | break; 100 | } 101 | } 102 | return "xml-attribute"; 103 | }; 104 | } 105 | 106 | function inBlock(style, terminator) { 107 | return function(source, setState) { 108 | while (!source.endOfLine()) { 109 | if (source.lookAhead(terminator, true)) { 110 | setState(inText); 111 | break; 112 | } 113 | source.next(); 114 | } 115 | return style; 116 | }; 117 | } 118 | 119 | return function(source, startState) { 120 | return tokenizer(source, startState || inText); 121 | }; 122 | })(); 123 | 124 | // The parser. The structure of this function largely follows that of 125 | // parseJavaScript in parsejavascript.js (there is actually a bit more 126 | // shared code than I'd like), but it is quite a bit simpler. 127 | function parseXML(source) { 128 | var tokens = tokenizeXML(source), token; 129 | var cc = [base]; 130 | var tokenNr = 0, indented = 0; 131 | var currentTag = null, context = null; 132 | var consume; 133 | 134 | function push(fs) { 135 | for (var i = fs.length - 1; i >= 0; i--) 136 | cc.push(fs[i]); 137 | } 138 | function cont() { 139 | push(arguments); 140 | consume = true; 141 | } 142 | function pass() { 143 | push(arguments); 144 | consume = false; 145 | } 146 | 147 | function markErr() { 148 | token.style += " xml-error"; 149 | } 150 | function expect(text) { 151 | return function(style, content) { 152 | if (content == text) cont(); 153 | else {markErr(); cont(arguments.callee);} 154 | }; 155 | } 156 | 157 | function pushContext(tagname, startOfLine) { 158 | var noIndent = UseKludges.doNotIndent.hasOwnProperty(tagname) || (context && context.noIndent); 159 | context = {prev: context, name: tagname, indent: indented, startOfLine: startOfLine, noIndent: noIndent}; 160 | } 161 | function popContext() { 162 | context = context.prev; 163 | } 164 | function computeIndentation(baseContext) { 165 | return function(nextChars, current) { 166 | var context = baseContext; 167 | if (context && context.noIndent) 168 | return current; 169 | if (alignCDATA && /")); 189 | else if (style == "xml-cdata") { 190 | if (!context || context.name != "!cdata") pushContext("!cdata"); 191 | if (/\]\]>$/.test(content)) popContext(); 192 | cont(); 193 | } 194 | else if (harmlessTokens.hasOwnProperty(style)) cont(); 195 | else {markErr(); cont();} 196 | } 197 | function tagname(style, content) { 198 | if (style == "xml-name") { 199 | currentTag = content.toLowerCase(); 200 | token.style = "xml-tagname"; 201 | cont(); 202 | } 203 | else { 204 | currentTag = null; 205 | pass(); 206 | } 207 | } 208 | function closetagname(style, content) { 209 | if (style == "xml-name") { 210 | token.style = "xml-tagname"; 211 | if (context && content.toLowerCase() == context.name) popContext(); 212 | else markErr(); 213 | } 214 | cont(); 215 | } 216 | function endtag(startOfLine) { 217 | return function(style, content) { 218 | if (content == "/>" || (content == ">" && UseKludges.autoSelfClosers.hasOwnProperty(currentTag))) cont(); 219 | else if (content == ">") {pushContext(currentTag, startOfLine); cont();} 220 | else {markErr(); cont(arguments.callee);} 221 | }; 222 | } 223 | function attributes(style) { 224 | if (style == "xml-name") {token.style = "xml-attname"; cont(attribute, attributes);} 225 | else pass(); 226 | } 227 | function attribute(style, content) { 228 | if (content == "=") cont(value); 229 | else if (content == ">" || content == "/>") pass(endtag); 230 | else pass(); 231 | } 232 | function value(style) { 233 | if (style == "xml-attribute") cont(value); 234 | else pass(); 235 | } 236 | 237 | return { 238 | indentation: function() {return indented;}, 239 | 240 | next: function(){ 241 | token = tokens.next(); 242 | if (token.style == "whitespace" && tokenNr == 0) 243 | indented = token.value.length; 244 | else 245 | tokenNr++; 246 | if (token.content == "\n") { 247 | indented = tokenNr = 0; 248 | token.indentation = computeIndentation(context); 249 | } 250 | 251 | if (token.style == "whitespace" || token.type == "xml-comment") 252 | return token; 253 | 254 | while(true){ 255 | consume = false; 256 | cc.pop()(token.style, token.content); 257 | if (consume) return token; 258 | } 259 | }, 260 | 261 | copy: function(){ 262 | var _cc = cc.concat([]), _tokenState = tokens.state, _context = context; 263 | var parser = this; 264 | 265 | return function(input){ 266 | cc = _cc.concat([]); 267 | tokenNr = indented = 0; 268 | context = _context; 269 | tokens = tokenizeXML(input, _tokenState); 270 | return parser; 271 | }; 272 | } 273 | }; 274 | } 275 | 276 | return { 277 | make: parseXML, 278 | electricChars: "/", 279 | configure: function(config) { 280 | if (config.useHTMLKludges != null) 281 | UseKludges = config.useHTMLKludges ? Kludges : NoKludges; 282 | if (config.alignCDATA) 283 | alignCDATA = config.alignCDATA; 284 | } 285 | }; 286 | })(); 287 | -------------------------------------------------------------------------------- /public/js/plugins.js: -------------------------------------------------------------------------------- 1 | 2 | // remap jQuery to $ 3 | (function($){ 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | })(window.jQuery); 16 | 17 | 18 | 19 | // usage: log('inside coolFunc',this,arguments); 20 | // paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/ 21 | window.log = function(){ 22 | log.history = log.history || []; // store logs to an array for reference 23 | log.history.push(arguments); 24 | if(this.console){ 25 | console.log( Array.prototype.slice.call(arguments) ); 26 | } 27 | }; 28 | 29 | 30 | 31 | // catch all document.write() calls 32 | (function(doc){ 33 | var write = doc.write; 34 | doc.write = function(q){ 35 | log('document.write(): ',arguments); 36 | if (/docwriteregexwhitelist/.test(q)) write.apply(doc,arguments); 37 | }; 38 | })(document); 39 | 40 | 41 | -------------------------------------------------------------------------------- /public/js/stringstream.js: -------------------------------------------------------------------------------- 1 | /* String streams are the things fed to parsers (which can feed them 2 | * to a tokenizer if they want). They provide peek and next methods 3 | * for looking at the current character (next 'consumes' this 4 | * character, peek does not), and a get method for retrieving all the 5 | * text that was consumed since the last time get was called. 6 | * 7 | * An easy mistake to make is to let a StopIteration exception finish 8 | * the token stream while there are still characters pending in the 9 | * string stream (hitting the end of the buffer while parsing a 10 | * token). To make it easier to detect such errors, the stringstreams 11 | * throw an exception when this happens. 12 | */ 13 | 14 | // Make a stringstream stream out of an iterator that returns strings. 15 | // This is applied to the result of traverseDOM (see codemirror.js), 16 | // and the resulting stream is fed to the parser. 17 | var stringStream = function(source){ 18 | // String that's currently being iterated over. 19 | var current = ""; 20 | // Position in that string. 21 | var pos = 0; 22 | // Accumulator for strings that have been iterated over but not 23 | // get()-ed yet. 24 | var accum = ""; 25 | // Make sure there are more characters ready, or throw 26 | // StopIteration. 27 | function ensureChars() { 28 | while (pos == current.length) { 29 | accum += current; 30 | current = ""; // In case source.next() throws 31 | pos = 0; 32 | try {current = source.next();} 33 | catch (e) { 34 | if (e != StopIteration) throw e; 35 | else return false; 36 | } 37 | } 38 | return true; 39 | } 40 | 41 | return { 42 | // peek: -> character 43 | // Return the next character in the stream. 44 | peek: function() { 45 | if (!ensureChars()) return null; 46 | return current.charAt(pos); 47 | }, 48 | // next: -> character 49 | // Get the next character, throw StopIteration if at end, check 50 | // for unused content. 51 | next: function() { 52 | if (!ensureChars()) { 53 | if (accum.length > 0) 54 | throw "End of stringstream reached without emptying buffer ('" + accum + "')."; 55 | else 56 | throw StopIteration; 57 | } 58 | return current.charAt(pos++); 59 | }, 60 | // get(): -> string 61 | // Return the characters iterated over since the last call to 62 | // .get(). 63 | get: function() { 64 | var temp = accum; 65 | accum = ""; 66 | if (pos > 0){ 67 | temp += current.slice(0, pos); 68 | current = current.slice(pos); 69 | pos = 0; 70 | } 71 | return temp; 72 | }, 73 | // Push a string back into the stream. 74 | push: function(str) { 75 | current = current.slice(0, pos) + str + current.slice(pos); 76 | }, 77 | lookAhead: function(str, consume, skipSpaces, caseInsensitive) { 78 | function cased(str) {return caseInsensitive ? str.toLowerCase() : str;} 79 | str = cased(str); 80 | var found = false; 81 | 82 | var _accum = accum, _pos = pos; 83 | if (skipSpaces) this.nextWhileMatches(/[\s\u00a0]/); 84 | 85 | while (true) { 86 | var end = pos + str.length, left = current.length - pos; 87 | if (end <= current.length) { 88 | found = str == cased(current.slice(pos, end)); 89 | pos = end; 90 | break; 91 | } 92 | else if (str.slice(0, left) == cased(current.slice(pos))) { 93 | accum += current; current = ""; 94 | try {current = source.next();} 95 | catch (e) {break;} 96 | pos = 0; 97 | str = str.slice(left); 98 | } 99 | else { 100 | break; 101 | } 102 | } 103 | 104 | if (!(found && consume)) { 105 | current = accum.slice(_accum.length) + current; 106 | pos = _pos; 107 | accum = _accum; 108 | } 109 | 110 | return found; 111 | }, 112 | 113 | // Utils built on top of the above 114 | // more: -> boolean 115 | // Produce true if the stream isn't empty. 116 | more: function() { 117 | return this.peek() !== null; 118 | }, 119 | applies: function(test) { 120 | var next = this.peek(); 121 | return (next !== null && test(next)); 122 | }, 123 | nextWhile: function(test) { 124 | var next; 125 | while ((next = this.peek()) !== null && test(next)) 126 | this.next(); 127 | }, 128 | matches: function(re) { 129 | var next = this.peek(); 130 | return (next !== null && re.test(next)); 131 | }, 132 | nextWhileMatches: function(re) { 133 | var next; 134 | while ((next = this.peek()) !== null && re.test(next)) 135 | this.next(); 136 | }, 137 | equals: function(ch) { 138 | return ch === this.peek(); 139 | }, 140 | endOfLine: function() { 141 | var next = this.peek(); 142 | return next == null || next == "\n"; 143 | } 144 | }; 145 | }; 146 | -------------------------------------------------------------------------------- /public/js/tokenize.js: -------------------------------------------------------------------------------- 1 | // A framework for simple tokenizers. Takes care of newlines and 2 | // white-space, and of getting the text from the source stream into 3 | // the token object. A state is a function of two arguments -- a 4 | // string stream and a setState function. The second can be used to 5 | // change the tokenizer's state, and can be ignored for stateless 6 | // tokenizers. This function should advance the stream over a token 7 | // and return a string or object containing information about the next 8 | // token, or null to pass and have the (new) state be called to finish 9 | // the token. When a string is given, it is wrapped in a {style, type} 10 | // object. In the resulting object, the characters consumed are stored 11 | // under the content property. Any whitespace following them is also 12 | // automatically consumed, and added to the value property. (Thus, 13 | // content is the actual meaningful part of the token, while value 14 | // contains all the text it spans.) 15 | 16 | function tokenizer(source, state) { 17 | // Newlines are always a separate token. 18 | function isWhiteSpace(ch) { 19 | // The messy regexp is because IE's regexp matcher is of the 20 | // opinion that non-breaking spaces are no whitespace. 21 | return ch != "\n" && /^[\s\u00a0]*$/.test(ch); 22 | } 23 | 24 | var tokenizer = { 25 | state: state, 26 | 27 | take: function(type) { 28 | if (typeof(type) == "string") 29 | type = {style: type, type: type}; 30 | 31 | type.content = (type.content || "") + source.get(); 32 | if (!/\n$/.test(type.content)) 33 | source.nextWhile(isWhiteSpace); 34 | type.value = type.content + source.get(); 35 | return type; 36 | }, 37 | 38 | next: function () { 39 | if (!source.more()) throw StopIteration; 40 | 41 | var type; 42 | if (source.equals("\n")) { 43 | source.next(); 44 | return this.take("whitespace"); 45 | } 46 | 47 | if (source.applies(isWhiteSpace)) 48 | type = "whitespace"; 49 | else 50 | while (!type) 51 | type = this.state(source, function(s) {tokenizer.state = s;}); 52 | 53 | return this.take(type); 54 | } 55 | }; 56 | return tokenizer; 57 | } 58 | -------------------------------------------------------------------------------- /public/js/tokenizejavascript.js: -------------------------------------------------------------------------------- 1 | /* Tokenizer for JavaScript code */ 2 | 3 | var tokenizeJavaScript = (function() { 4 | // Advance the stream until the given character (not preceded by a 5 | // backslash) is encountered, or the end of the line is reached. 6 | function nextUntilUnescaped(source, end) { 7 | var escaped = false; 8 | while (!source.endOfLine()) { 9 | var next = source.next(); 10 | if (next == end && !escaped) 11 | return false; 12 | escaped = !escaped && next == "\\"; 13 | } 14 | return escaped; 15 | } 16 | 17 | // A map of JavaScript's keywords. The a/b/c keyword distinction is 18 | // very rough, but it gives the parser enough information to parse 19 | // correct code correctly (we don't care that much how we parse 20 | // incorrect code). The style information included in these objects 21 | // is used by the highlighter to pick the correct CSS style for a 22 | // token. 23 | var keywords = function(){ 24 | function result(type, style){ 25 | return {type: type, style: "js-" + style}; 26 | } 27 | // keywords that take a parenthised expression, and then a 28 | // statement (if) 29 | var keywordA = result("keyword a", "keyword"); 30 | // keywords that take just a statement (else) 31 | var keywordB = result("keyword b", "keyword"); 32 | // keywords that optionally take an expression, and form a 33 | // statement (return) 34 | var keywordC = result("keyword c", "keyword"); 35 | var operator = result("operator", "keyword"); 36 | var atom = result("atom", "atom"); 37 | return { 38 | "if": keywordA, "while": keywordA, "with": keywordA, 39 | "else": keywordB, "do": keywordB, "try": keywordB, "finally": keywordB, 40 | "return": keywordC, "break": keywordC, "continue": keywordC, "new": keywordC, "delete": keywordC, "throw": keywordC, 41 | "in": operator, "typeof": operator, "instanceof": operator, 42 | "var": result("var", "keyword"), "function": result("function", "keyword"), "catch": result("catch", "keyword"), 43 | "for": result("for", "keyword"), "switch": result("switch", "keyword"), 44 | "case": result("case", "keyword"), "default": result("default", "keyword"), 45 | "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom 46 | }; 47 | }(); 48 | 49 | // Some helper regexps 50 | var isOperatorChar = /[+\-*&%=<>!?|]/; 51 | var isHexDigit = /[0-9A-Fa-f]/; 52 | var isWordChar = /[\w\$_]/; 53 | 54 | // Wrapper around jsToken that helps maintain parser state (whether 55 | // we are inside of a multi-line comment and whether the next token 56 | // could be a regular expression). 57 | function jsTokenState(inside, regexp) { 58 | return function(source, setState) { 59 | var newInside = inside; 60 | var type = jsToken(inside, regexp, source, function(c) {newInside = c;}); 61 | var newRegexp = type.type == "operator" || type.type == "keyword c" || type.type.match(/^[\[{}\(,;:]$/); 62 | if (newRegexp != regexp || newInside != inside) 63 | setState(jsTokenState(newInside, newRegexp)); 64 | return type; 65 | }; 66 | } 67 | 68 | // The token reader, intended to be used by the tokenizer from 69 | // tokenize.js (through jsTokenState). Advances the source stream 70 | // over a token, and returns an object containing the type and style 71 | // of that token. 72 | function jsToken(inside, regexp, source, setInside) { 73 | function readHexNumber(){ 74 | source.next(); // skip the 'x' 75 | source.nextWhileMatches(isHexDigit); 76 | return {type: "number", style: "js-atom"}; 77 | } 78 | 79 | function readNumber() { 80 | source.nextWhileMatches(/[0-9]/); 81 | if (source.equals(".")){ 82 | source.next(); 83 | source.nextWhileMatches(/[0-9]/); 84 | } 85 | if (source.equals("e") || source.equals("E")){ 86 | source.next(); 87 | if (source.equals("-")) 88 | source.next(); 89 | source.nextWhileMatches(/[0-9]/); 90 | } 91 | return {type: "number", style: "js-atom"}; 92 | } 93 | // Read a word, look it up in keywords. If not found, it is a 94 | // variable, otherwise it is a keyword of the type found. 95 | function readWord() { 96 | source.nextWhileMatches(isWordChar); 97 | var word = source.get(); 98 | var known = keywords.hasOwnProperty(word) && keywords.propertyIsEnumerable(word) && keywords[word]; 99 | return known ? {type: known.type, style: known.style, content: word} : 100 | {type: "variable", style: "js-variable", content: word}; 101 | } 102 | function readRegexp() { 103 | nextUntilUnescaped(source, "/"); 104 | source.nextWhileMatches(/[gi]/); 105 | return {type: "regexp", style: "js-string"}; 106 | } 107 | // Mutli-line comments are tricky. We want to return the newlines 108 | // embedded in them as regular newline tokens, and then continue 109 | // returning a comment token for every line of the comment. So 110 | // some state has to be saved (inside) to indicate whether we are 111 | // inside a /* */ sequence. 112 | function readMultilineComment(start){ 113 | var newInside = "/*"; 114 | var maybeEnd = (start == "*"); 115 | while (true) { 116 | if (source.endOfLine()) 117 | break; 118 | var next = source.next(); 119 | if (next == "/" && maybeEnd){ 120 | newInside = null; 121 | break; 122 | } 123 | maybeEnd = (next == "*"); 124 | } 125 | setInside(newInside); 126 | return {type: "comment", style: "js-comment"}; 127 | } 128 | function readOperator() { 129 | source.nextWhileMatches(isOperatorChar); 130 | return {type: "operator", style: "js-operator"}; 131 | } 132 | function readString(quote) { 133 | var endBackSlash = nextUntilUnescaped(source, quote); 134 | setInside(endBackSlash ? quote : null); 135 | return {type: "string", style: "js-string"}; 136 | } 137 | 138 | // Fetch the next token. Dispatches on first character in the 139 | // stream, or first two characters when the first is a slash. 140 | if (inside == "\"" || inside == "'") 141 | return readString(inside); 142 | var ch = source.next(); 143 | if (inside == "/*") 144 | return readMultilineComment(ch); 145 | else if (ch == "\"" || ch == "'") 146 | return readString(ch); 147 | // with punctuation, the type of the token is the symbol itself 148 | else if (/[\[\]{}\(\),;\:\.]/.test(ch)) 149 | return {type: ch, style: "js-punctuation"}; 150 | else if (ch == "0" && (source.equals("x") || source.equals("X"))) 151 | return readHexNumber(); 152 | else if (/[0-9]/.test(ch)) 153 | return readNumber(); 154 | else if (ch == "/"){ 155 | if (source.equals("*")) 156 | { source.next(); return readMultilineComment(ch); } 157 | else if (source.equals("/")) 158 | { nextUntilUnescaped(source, null); return {type: "comment", style: "js-comment"};} 159 | else if (regexp) 160 | return readRegexp(); 161 | else 162 | return readOperator(); 163 | } 164 | else if (isOperatorChar.test(ch)) 165 | return readOperator(); 166 | else 167 | return readWord(); 168 | } 169 | 170 | // The external interface to the tokenizer. 171 | return function(source, startState) { 172 | return tokenizer(source, startState || jsTokenState(false, true)); 173 | }; 174 | })(); 175 | -------------------------------------------------------------------------------- /public/js/undo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Storage and control for undo information within a CodeMirror 3 | * editor. 'Why on earth is such a complicated mess required for 4 | * that?', I hear you ask. The goal, in implementing this, was to make 5 | * the complexity of storing and reverting undo information depend 6 | * only on the size of the edited or restored content, not on the size 7 | * of the whole document. This makes it necessary to use a kind of 8 | * 'diff' system, which, when applied to a DOM tree, causes some 9 | * complexity and hackery. 10 | * 11 | * In short, the editor 'touches' BR elements as it parses them, and 12 | * the UndoHistory stores these. When nothing is touched in commitDelay 13 | * milliseconds, the changes are committed: It goes over all touched 14 | * nodes, throws out the ones that did not change since last commit or 15 | * are no longer in the document, and assembles the rest into zero or 16 | * more 'chains' -- arrays of adjacent lines. Links back to these 17 | * chains are added to the BR nodes, while the chain that previously 18 | * spanned these nodes is added to the undo history. Undoing a change 19 | * means taking such a chain off the undo history, restoring its 20 | * content (text is saved per line) and linking it back into the 21 | * document. 22 | */ 23 | 24 | // A history object needs to know about the DOM container holding the 25 | // document, the maximum amount of undo levels it should store, the 26 | // delay (of no input) after which it commits a set of changes, and, 27 | // unfortunately, the 'parent' window -- a window that is not in 28 | // designMode, and on which setTimeout works in every browser. 29 | function UndoHistory(container, maxDepth, commitDelay, editor) { 30 | this.container = container; 31 | this.maxDepth = maxDepth; this.commitDelay = commitDelay; 32 | this.editor = editor; this.parent = editor.parent; 33 | // This line object represents the initial, empty editor. 34 | var initial = {text: "", from: null, to: null}; 35 | // As the borders between lines are represented by BR elements, the 36 | // start of the first line and the end of the last one are 37 | // represented by null. Since you can not store any properties 38 | // (links to line objects) in null, these properties are used in 39 | // those cases. 40 | this.first = initial; this.last = initial; 41 | // Similarly, a 'historyTouched' property is added to the BR in 42 | // front of lines that have already been touched, and 'firstTouched' 43 | // is used for the first line. 44 | this.firstTouched = false; 45 | // History is the set of committed changes, touched is the set of 46 | // nodes touched since the last commit. 47 | this.history = []; this.redoHistory = []; this.touched = []; 48 | } 49 | 50 | UndoHistory.prototype = { 51 | // Schedule a commit (if no other touches come in for commitDelay 52 | // milliseconds). 53 | scheduleCommit: function() { 54 | var self = this; 55 | this.parent.clearTimeout(this.commitTimeout); 56 | this.commitTimeout = this.parent.setTimeout(function(){self.tryCommit();}, this.commitDelay); 57 | }, 58 | 59 | // Mark a node as touched. Null is a valid argument. 60 | touch: function(node) { 61 | this.setTouched(node); 62 | this.scheduleCommit(); 63 | }, 64 | 65 | // Undo the last change. 66 | undo: function() { 67 | // Make sure pending changes have been committed. 68 | this.commit(); 69 | 70 | if (this.history.length) { 71 | // Take the top diff from the history, apply it, and store its 72 | // shadow in the redo history. 73 | var item = this.history.pop(); 74 | this.redoHistory.push(this.updateTo(item, "applyChain")); 75 | this.notifyEnvironment(); 76 | return this.chainNode(item); 77 | } 78 | }, 79 | 80 | // Redo the last undone change. 81 | redo: function() { 82 | this.commit(); 83 | if (this.redoHistory.length) { 84 | // The inverse of undo, basically. 85 | var item = this.redoHistory.pop(); 86 | this.addUndoLevel(this.updateTo(item, "applyChain")); 87 | this.notifyEnvironment(); 88 | return this.chainNode(item); 89 | } 90 | }, 91 | 92 | clear: function() { 93 | this.history = []; 94 | this.redoHistory = []; 95 | }, 96 | 97 | // Ask for the size of the un/redo histories. 98 | historySize: function() { 99 | return {undo: this.history.length, redo: this.redoHistory.length}; 100 | }, 101 | 102 | // Push a changeset into the document. 103 | push: function(from, to, lines) { 104 | var chain = []; 105 | for (var i = 0; i < lines.length; i++) { 106 | var end = (i == lines.length - 1) ? to : this.container.ownerDocument.createElement("BR"); 107 | chain.push({from: from, to: end, text: cleanText(lines[i])}); 108 | from = end; 109 | } 110 | this.pushChains([chain], from == null && to == null); 111 | this.notifyEnvironment(); 112 | }, 113 | 114 | pushChains: function(chains, doNotHighlight) { 115 | this.commit(doNotHighlight); 116 | this.addUndoLevel(this.updateTo(chains, "applyChain")); 117 | this.redoHistory = []; 118 | }, 119 | 120 | // Retrieve a DOM node from a chain (for scrolling to it after undo/redo). 121 | chainNode: function(chains) { 122 | for (var i = 0; i < chains.length; i++) { 123 | var start = chains[i][0], node = start && (start.from || start.to); 124 | if (node) return node; 125 | } 126 | }, 127 | 128 | // Clear the undo history, make the current document the start 129 | // position. 130 | reset: function() { 131 | this.history = []; this.redoHistory = []; 132 | }, 133 | 134 | textAfter: function(br) { 135 | return this.after(br).text; 136 | }, 137 | 138 | nodeAfter: function(br) { 139 | return this.after(br).to; 140 | }, 141 | 142 | nodeBefore: function(br) { 143 | return this.before(br).from; 144 | }, 145 | 146 | // Commit unless there are pending dirty nodes. 147 | tryCommit: function() { 148 | if (!window.UndoHistory) return; // Stop when frame has been unloaded 149 | if (this.editor.highlightDirty()) this.commit(true); 150 | else this.scheduleCommit(); 151 | }, 152 | 153 | // Check whether the touched nodes hold any changes, if so, commit 154 | // them. 155 | commit: function(doNotHighlight) { 156 | this.parent.clearTimeout(this.commitTimeout); 157 | // Make sure there are no pending dirty nodes. 158 | if (!doNotHighlight) this.editor.highlightDirty(true); 159 | // Build set of chains. 160 | var chains = this.touchedChains(), self = this; 161 | 162 | if (chains.length) { 163 | this.addUndoLevel(this.updateTo(chains, "linkChain")); 164 | this.redoHistory = []; 165 | this.notifyEnvironment(); 166 | } 167 | }, 168 | 169 | // [ end of public interface ] 170 | 171 | // Update the document with a given set of chains, return its 172 | // shadow. updateFunc should be "applyChain" or "linkChain". In the 173 | // second case, the chains are taken to correspond the the current 174 | // document, and only the state of the line data is updated. In the 175 | // first case, the content of the chains is also pushed iinto the 176 | // document. 177 | updateTo: function(chains, updateFunc) { 178 | var shadows = [], dirty = []; 179 | for (var i = 0; i < chains.length; i++) { 180 | shadows.push(this.shadowChain(chains[i])); 181 | dirty.push(this[updateFunc](chains[i])); 182 | } 183 | if (updateFunc == "applyChain") 184 | this.notifyDirty(dirty); 185 | return shadows; 186 | }, 187 | 188 | // Notify the editor that some nodes have changed. 189 | notifyDirty: function(nodes) { 190 | forEach(nodes, method(this.editor, "addDirtyNode")) 191 | this.editor.scheduleHighlight(); 192 | }, 193 | 194 | notifyEnvironment: function() { 195 | if (this.onChange) this.onChange(); 196 | // Used by the line-wrapping line-numbering code. 197 | if (window.frameElement && window.frameElement.CodeMirror.updateNumbers) 198 | window.frameElement.CodeMirror.updateNumbers(); 199 | }, 200 | 201 | // Link a chain into the DOM nodes (or the first/last links for null 202 | // nodes). 203 | linkChain: function(chain) { 204 | for (var i = 0; i < chain.length; i++) { 205 | var line = chain[i]; 206 | if (line.from) line.from.historyAfter = line; 207 | else this.first = line; 208 | if (line.to) line.to.historyBefore = line; 209 | else this.last = line; 210 | } 211 | }, 212 | 213 | // Get the line object after/before a given node. 214 | after: function(node) { 215 | return node ? node.historyAfter : this.first; 216 | }, 217 | before: function(node) { 218 | return node ? node.historyBefore : this.last; 219 | }, 220 | 221 | // Mark a node as touched if it has not already been marked. 222 | setTouched: function(node) { 223 | if (node) { 224 | if (!node.historyTouched) { 225 | this.touched.push(node); 226 | node.historyTouched = true; 227 | } 228 | } 229 | else { 230 | this.firstTouched = true; 231 | } 232 | }, 233 | 234 | // Store a new set of undo info, throw away info if there is more of 235 | // it than allowed. 236 | addUndoLevel: function(diffs) { 237 | this.history.push(diffs); 238 | if (this.history.length > this.maxDepth) 239 | this.history.shift(); 240 | }, 241 | 242 | // Build chains from a set of touched nodes. 243 | touchedChains: function() { 244 | var self = this; 245 | 246 | // The temp system is a crummy hack to speed up determining 247 | // whether a (currently touched) node has a line object associated 248 | // with it. nullTemp is used to store the object for the first 249 | // line, other nodes get it stored in their historyTemp property. 250 | var nullTemp = null; 251 | function temp(node) {return node ? node.historyTemp : nullTemp;} 252 | function setTemp(node, line) { 253 | if (node) node.historyTemp = line; 254 | else nullTemp = line; 255 | } 256 | 257 | function buildLine(node) { 258 | var text = []; 259 | for (var cur = node ? node.nextSibling : self.container.firstChild; 260 | cur && !isBR(cur); cur = cur.nextSibling) 261 | if (cur.currentText) text.push(cur.currentText); 262 | return {from: node, to: cur, text: cleanText(text.join(""))}; 263 | } 264 | 265 | // Filter out unchanged lines and nodes that are no longer in the 266 | // document. Build up line objects for remaining nodes. 267 | var lines = []; 268 | if (self.firstTouched) self.touched.push(null); 269 | forEach(self.touched, function(node) { 270 | if (node && node.parentNode != self.container) return; 271 | 272 | if (node) node.historyTouched = false; 273 | else self.firstTouched = false; 274 | 275 | var line = buildLine(node), shadow = self.after(node); 276 | if (!shadow || shadow.text != line.text || shadow.to != line.to) { 277 | lines.push(line); 278 | setTemp(node, line); 279 | } 280 | }); 281 | 282 | // Get the BR element after/before the given node. 283 | function nextBR(node, dir) { 284 | var link = dir + "Sibling", search = node[link]; 285 | while (search && !isBR(search)) 286 | search = search[link]; 287 | return search; 288 | } 289 | 290 | // Assemble line objects into chains by scanning the DOM tree 291 | // around them. 292 | var chains = []; self.touched = []; 293 | forEach(lines, function(line) { 294 | // Note that this makes the loop skip line objects that have 295 | // been pulled into chains by lines before them. 296 | if (!temp(line.from)) return; 297 | 298 | var chain = [], curNode = line.from, safe = true; 299 | // Put any line objects (referred to by temp info) before this 300 | // one on the front of the array. 301 | while (true) { 302 | var curLine = temp(curNode); 303 | if (!curLine) { 304 | if (safe) break; 305 | else curLine = buildLine(curNode); 306 | } 307 | chain.unshift(curLine); 308 | setTemp(curNode, null); 309 | if (!curNode) break; 310 | safe = self.after(curNode); 311 | curNode = nextBR(curNode, "previous"); 312 | } 313 | curNode = line.to; safe = self.before(line.from); 314 | // Add lines after this one at end of array. 315 | while (true) { 316 | if (!curNode) break; 317 | var curLine = temp(curNode); 318 | if (!curLine) { 319 | if (safe) break; 320 | else curLine = buildLine(curNode); 321 | } 322 | chain.push(curLine); 323 | setTemp(curNode, null); 324 | safe = self.before(curNode); 325 | curNode = nextBR(curNode, "next"); 326 | } 327 | chains.push(chain); 328 | }); 329 | 330 | return chains; 331 | }, 332 | 333 | // Find the 'shadow' of a given chain by following the links in the 334 | // DOM nodes at its start and end. 335 | shadowChain: function(chain) { 336 | var shadows = [], next = this.after(chain[0].from), end = chain[chain.length - 1].to; 337 | while (true) { 338 | shadows.push(next); 339 | var nextNode = next.to; 340 | if (!nextNode || nextNode == end) 341 | break; 342 | else 343 | next = nextNode.historyAfter || this.before(end); 344 | // (The this.before(end) is a hack -- FF sometimes removes 345 | // properties from BR nodes, in which case the best we can hope 346 | // for is to not break.) 347 | } 348 | return shadows; 349 | }, 350 | 351 | // Update the DOM tree to contain the lines specified in a given 352 | // chain, link this chain into the DOM nodes. 353 | applyChain: function(chain) { 354 | // Some attempt is made to prevent the cursor from jumping 355 | // randomly when an undo or redo happens. It still behaves a bit 356 | // strange sometimes. 357 | var cursor = select.cursorPos(this.container, false), self = this; 358 | 359 | // Remove all nodes in the DOM tree between from and to (null for 360 | // start/end of container). 361 | function removeRange(from, to) { 362 | var pos = from ? from.nextSibling : self.container.firstChild; 363 | while (pos != to) { 364 | var temp = pos.nextSibling; 365 | removeElement(pos); 366 | pos = temp; 367 | } 368 | } 369 | 370 | var start = chain[0].from, end = chain[chain.length - 1].to; 371 | // Clear the space where this change has to be made. 372 | removeRange(start, end); 373 | 374 | // Insert the content specified by the chain into the DOM tree. 375 | for (var i = 0; i < chain.length; i++) { 376 | var line = chain[i]; 377 | // The start and end of the space are already correct, but BR 378 | // tags inside it have to be put back. 379 | if (i > 0) 380 | self.container.insertBefore(line.from, end); 381 | 382 | // Add the text. 383 | var node = makePartSpan(fixSpaces(line.text), this.container.ownerDocument); 384 | self.container.insertBefore(node, end); 385 | // See if the cursor was on this line. Put it back, adjusting 386 | // for changed line length, if it was. 387 | if (cursor && cursor.node == line.from) { 388 | var cursordiff = 0; 389 | var prev = this.after(line.from); 390 | if (prev && i == chain.length - 1) { 391 | // Only adjust if the cursor is after the unchanged part of 392 | // the line. 393 | for (var match = 0; match < cursor.offset && 394 | line.text.charAt(match) == prev.text.charAt(match); match++); 395 | if (cursor.offset > match) 396 | cursordiff = line.text.length - prev.text.length; 397 | } 398 | select.setCursorPos(this.container, {node: line.from, offset: Math.max(0, cursor.offset + cursordiff)}); 399 | } 400 | // Cursor was in removed line, this is last new line. 401 | else if (cursor && (i == chain.length - 1) && cursor.node && cursor.node.parentNode != this.container) { 402 | select.setCursorPos(this.container, {node: line.from, offset: line.text.length}); 403 | } 404 | } 405 | 406 | // Anchor the chain in the DOM tree. 407 | this.linkChain(chain); 408 | return start; 409 | } 410 | }; 411 | -------------------------------------------------------------------------------- /public/js/util.js: -------------------------------------------------------------------------------- 1 | /* A few useful utility functions. */ 2 | 3 | // Capture a method on an object. 4 | function method(obj, name) { 5 | return function() {obj[name].apply(obj, arguments);}; 6 | } 7 | 8 | // The value used to signal the end of a sequence in iterators. 9 | var StopIteration = {toString: function() {return "StopIteration"}}; 10 | 11 | // Apply a function to each element in a sequence. 12 | function forEach(iter, f) { 13 | if (iter.next) { 14 | try {while (true) f(iter.next());} 15 | catch (e) {if (e != StopIteration) throw e;} 16 | } 17 | else { 18 | for (var i = 0; i < iter.length; i++) 19 | f(iter[i]); 20 | } 21 | } 22 | 23 | // Map a function over a sequence, producing an array of results. 24 | function map(iter, f) { 25 | var accum = []; 26 | forEach(iter, function(val) {accum.push(f(val));}); 27 | return accum; 28 | } 29 | 30 | // Create a predicate function that tests a string againsts a given 31 | // regular expression. No longer used but might be used by 3rd party 32 | // parsers. 33 | function matcher(regexp){ 34 | return function(value){return regexp.test(value);}; 35 | } 36 | 37 | // Test whether a DOM node has a certain CSS class. Much faster than 38 | // the MochiKit equivalent, for some reason. 39 | function hasClass(element, className){ 40 | var classes = element.className; 41 | return classes && new RegExp("(^| )" + className + "($| )").test(classes); 42 | } 43 | 44 | // Insert a DOM node after another node. 45 | function insertAfter(newNode, oldNode) { 46 | var parent = oldNode.parentNode; 47 | parent.insertBefore(newNode, oldNode.nextSibling); 48 | return newNode; 49 | } 50 | 51 | function removeElement(node) { 52 | if (node.parentNode) 53 | node.parentNode.removeChild(node); 54 | } 55 | 56 | function clearElement(node) { 57 | while (node.firstChild) 58 | node.removeChild(node.firstChild); 59 | } 60 | 61 | // Check whether a node is contained in another one. 62 | function isAncestor(node, child) { 63 | while (child = child.parentNode) { 64 | if (node == child) 65 | return true; 66 | } 67 | return false; 68 | } 69 | 70 | // The non-breaking space character. 71 | var nbsp = "\u00a0"; 72 | var matching = {"{": "}", "[": "]", "(": ")", 73 | "}": "{", "]": "[", ")": "("}; 74 | 75 | // Standardize a few unportable event properties. 76 | function normalizeEvent(event) { 77 | if (!event.stopPropagation) { 78 | event.stopPropagation = function() {this.cancelBubble = true;}; 79 | event.preventDefault = function() {this.returnValue = false;}; 80 | } 81 | if (!event.stop) { 82 | event.stop = function() { 83 | this.stopPropagation(); 84 | this.preventDefault(); 85 | }; 86 | } 87 | 88 | if (event.type == "keypress") { 89 | event.code = (event.charCode == null) ? event.keyCode : event.charCode; 90 | event.character = String.fromCharCode(event.code); 91 | } 92 | return event; 93 | } 94 | 95 | // Portably register event handlers. 96 | function addEventHandler(node, type, handler, removeFunc) { 97 | function wrapHandler(event) { 98 | handler(normalizeEvent(event || window.event)); 99 | } 100 | if (typeof node.addEventListener == "function") { 101 | node.addEventListener(type, wrapHandler, false); 102 | if (removeFunc) return function() {node.removeEventListener(type, wrapHandler, false);}; 103 | } 104 | else { 105 | node.attachEvent("on" + type, wrapHandler); 106 | if (removeFunc) return function() {node.detachEvent("on" + type, wrapHandler);}; 107 | } 108 | } 109 | 110 | function nodeText(node) { 111 | return node.textContent || node.innerText || node.nodeValue || ""; 112 | } 113 | 114 | function nodeTop(node) { 115 | var top = 0; 116 | while (node.offsetParent) { 117 | top += node.offsetTop; 118 | node = node.offsetParent; 119 | } 120 | return top; 121 | } 122 | 123 | function isBR(node) { 124 | var nn = node.nodeName; 125 | return nn == "BR" || nn == "br"; 126 | } 127 | function isSpan(node) { 128 | var nn = node.nodeName; 129 | return nn == "SPAN" || nn == "span"; 130 | } 131 | -------------------------------------------------------------------------------- /public/nodify-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/nodify-logo.png -------------------------------------------------------------------------------- /public/resources/screen_theme/images/bespin-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/resources/screen_theme/images/bespin-s.png -------------------------------------------------------------------------------- /public/resources/screen_theme/images/check-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/resources/screen_theme/images/check-selected.png -------------------------------------------------------------------------------- /public/resources/screen_theme/images/check-unselected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/resources/screen_theme/images/check-unselected.png -------------------------------------------------------------------------------- /public/resources/screen_theme/images/lines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/resources/screen_theme/images/lines.png -------------------------------------------------------------------------------- /public/resources/screen_theme/images/radio-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/resources/screen_theme/images/radio-selected.png -------------------------------------------------------------------------------- /public/resources/screen_theme/images/radio-unselected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/resources/screen_theme/images/radio-unselected.png -------------------------------------------------------------------------------- /public/resources/screen_theme/images/scroll-down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/resources/screen_theme/images/scroll-down.png -------------------------------------------------------------------------------- /public/resources/screen_theme/images/scroll-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/resources/screen_theme/images/scroll-left.png -------------------------------------------------------------------------------- /public/resources/screen_theme/images/scroll-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/resources/screen_theme/images/scroll-right.png -------------------------------------------------------------------------------- /public/resources/screen_theme/images/scroll-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/past/nodify/31cb5d603b1deb4ebcd3b2bd7350a6a84f8a446f/public/resources/screen_theme/images/scroll-up.png -------------------------------------------------------------------------------- /public/resources/screen_theme/theme.less: -------------------------------------------------------------------------------- 1 | /* ***** BEGIN LICENSE BLOCK ***** 2 | * Version: MPL 1.1/GPL 2.0/LGPL 2.1 3 | * 4 | * The contents of this file are subject to the Mozilla Public License Version 5 | * 1.1 (the "License"); you may not use this file except in compliance with 6 | * the License. You may obtain a copy of the License at 7 | * http://www.mozilla.org/MPL/ 8 | * 9 | * Software distributed under the License is distributed on an "AS IS" basis, 10 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing rights and limitations under the 12 | * License. 13 | * 14 | * The Original Code is Bespin. 15 | * 16 | * The Initial Developer of the Original Code is 17 | * Mozilla. 18 | * Portions created by the Initial Developer are Copyright (C) 2009 19 | * the Initial Developer. All Rights Reserved. 20 | * 21 | * Contributor(s): 22 | * Bespin Team (bespin@mozilla.com) 23 | * 24 | * Alternatively, the contents of this file may be used under the terms of 25 | * either the GNU General Public License Version 2 or later (the "GPL"), or 26 | * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 27 | * in which case the provisions of the GPL or the LGPL are applicable instead 28 | * of those above. If you wish to allow use of your version of this file only 29 | * under the terms of either the GPL or the LGPL, and not to allow others to 30 | * use your version of this file under the terms of the MPL, indicate your 31 | * decision by deleting the provisions above and replace them with the notice 32 | * and other provisions required by the GPL or the LGPL. If you do not delete 33 | * the provisions above, a recipient may use your version of this file under 34 | * the terms of any one of the MPL, the GPL or the LGPL. 35 | * 36 | * ***** END LICENSE BLOCK ***** */ 37 | 38 | .bespin { 39 | .container { 40 | font-family: @container_font; 41 | font-size: @container_font_size; 42 | 43 | color: @container_color; 44 | background: @container_bg; 45 | line-height: @container_line_height; 46 | 47 | display: -moz-box; 48 | -moz-box-orient: vertical; 49 | display: -webkit-box; 50 | -webkit-box-orient: vertical; 51 | width: 100%; 52 | height: 100%; 53 | margin: 0; 54 | } 55 | 56 | .container .north { 57 | -moz-box-ordinal-group: 1; 58 | -webkit-box-ordinal-group: 1; 59 | } 60 | 61 | .container .center-container { 62 | display: -moz-box; 63 | -moz-box-orient: horizontal; 64 | -moz-box-ordinal-group: 2; 65 | -moz-box-flex: 1; 66 | display: -webkit-box; 67 | -webkit-box-orient: horizontal; 68 | -webkit-box-ordinal-group: 2; 69 | -webkit-box-flex: 1; 70 | } 71 | 72 | .container .south { 73 | -moz-box-ordinal-group: 3; 74 | -webkit-box-ordinal-group: 3; 75 | } 76 | 77 | .container .center-container .west { 78 | -moz-box-ordinal-group: 1; 79 | -webkit-box-ordinal-group: 1; 80 | } 81 | 82 | .container .center-container .center { 83 | -moz-box-flex: 1; 84 | -moz-box-ordinal-group: 2; 85 | -webkit-box-flex: 1; 86 | -webkit-box-ordinal-group: 2; 87 | } 88 | 89 | .container .center-container .east { 90 | -moz-box-ordinal-group: 3; 91 | -webkit-box-ordinal-group: 3; 92 | } 93 | 94 | input, textarea { 95 | display: block; 96 | border: 0px; 97 | width: 200px; 98 | padding: 4px; 99 | color: @control_color; 100 | background: @control_bg; 101 | border: @control_border; 102 | font-size: 8pt; 103 | -moz-border-radius: @control_border_radius; 104 | -webkit-border-radius: @control_border_radius; 105 | box-shadow: 106 | rgba(255,255,255,0.3) 0px 0px 2px, 107 | inset rgba(0,0,0,0.3) 0px 0px 4px; 108 | -moz-box-shadow: 109 | rgba(255,255,255,0.3) 0px 0px 2px, 110 | inset rgba(0,0,0,0.3) 0px 0px 4px; 111 | -webkit-box-shadow: 112 | rgba(255,255,255,0.3) 0px 0px 2px, 113 | inset rgba(0,0,0,0.3) 0px 0px 4px; 114 | } 115 | 116 | input:focus, textarea:focus { 117 | color: @control_active_color; 118 | border: @control_active_border; 119 | background: @control_active_bg; 120 | outline: none; 121 | box-shadow: opacity(@control_active_inset_color, 0.6) 0px 0px 2px, 122 | inset opacity(@control_active_inset_color, 0.3) 0px 0px 6px; 123 | -moz-box-shadow: opacity(@control_active_inset_color, 0.6) 0px 0px 2px, 124 | inset opacity(@control_active_inset_color, 0.3) 0px 0px 6px; 125 | -webkit-box-shadow: opacity(@control_active_inset_color, 0.6) 0px 0px 2px, 126 | inset opacity(@control_active_inset_color, 0.3) 0px 0px 6px; 127 | } 128 | 129 | input[type=submit], input[type=button] { 130 | font-family: @button_font; 131 | font-size: @button_font_size; 132 | text-shadow: 1px 1px rgba(0, 0, 0, 0.4); 133 | 134 | padding: 8px 12px; 135 | margin-left: 8px; 136 | color: @button_color; 137 | background: transparent -moz-linear-gradient(top, @button_bg, @button_bg2); 138 | background: transparent -webkit-gradient(linear, 50% 0%, 50% 100%, from(@button_bg), to(@button_bg2)); 139 | 140 | border: 1px solid @button_border; 141 | -moz-border-radius: @control_border_radius; 142 | -webkit-border-radius: @control_border_radius; 143 | -moz-box-shadow: 144 | inset rgba(255, 255, 255, 0.2) 0 1px 0px, 145 | inset rgba(0, 0, 0, 0.2) 0 -1px 0px, 146 | rgba(0, 0, 0, 0.1) 0px 1px 2px; 147 | -webkit-box-shadow: 148 | inset rgba(255, 255, 255, 0.2) 0 1px 0px, 149 | inset rgba(0, 0, 0, 0.2) 0 -1px 0px, 150 | rgba(0, 0, 0, 0.1) 0px 1px 2px; 151 | box-shadow: 152 | inset rgba(255, 255, 255, 0.2) 0 1px 0px, 153 | inset rgba(0, 0, 0, 0.2) 0 -1px 0px, 154 | rgba(0, 0, 0, 0.1) 0px 1px 2px; 155 | } 156 | 157 | .pane { 158 | font-size: @pane_font_size; 159 | font-family: @pane_font; 160 | border-top: 1px solid rgba(255,255,255,0.1); 161 | border-left: 1px solid rgba(0, 0, 0, 0.1); 162 | border-right: 1px solid rgba(0, 0, 0, 0.1); 163 | border-bottom: 2px solid rgba(0, 0, 0, 0.1); 164 | background-color: @pane_bg; 165 | overflow: visible; 166 | padding: 15px; 167 | 168 | color: @pane_color; 169 | line-height: @pane_line_height; 170 | 171 | margin-bottom: 6px; 172 | margin-top: 6px; 173 | text-shadow: @pane_text_shadow; 174 | 175 | -moz-border-radius: @pane_border_radius; 176 | -webkit-border-radius: @pane_border_radius; 177 | -moz-box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 2px; 178 | -webkit-box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 2px; 179 | 180 | a { 181 | color: @pane_a_color; 182 | } 183 | 184 | h1 { 185 | font-family: @pane_h1_font; 186 | font-size: @pane_h1_font_size; 187 | color: @pane_h1_color; 188 | 189 | letter-spacing: -1.25px; 190 | line-height: 1.0; 191 | margin-top: 0px; 192 | margin-left: 10px; 193 | margin-bottom: 25px; 194 | margin-top: 10px; 195 | text-shadow: @pane_text_shadow; 196 | } 197 | 198 | p { 199 | margin-left: 10px; 200 | } 201 | } 202 | 203 | 204 | .form { 205 | font-family: @form_font; 206 | font-size: @form_font_size; 207 | line-height: @form_line_height; 208 | color: @form_color; 209 | text-shadow: @form_text_shadow; 210 | 211 | fieldset, p { 212 | color: @form_color; 213 | margin-bottom: 6px; 214 | margin-top: 6px; 215 | text-shadow: form_text_shadow; 216 | } 217 | } 218 | 219 | /* 220 | * The state classes are a little bit complex, because of the doubble class bug in IE6 221 | * The state class looks like this: 222 | * 223 | * .ui-radio-state[-checked][-disabled][-hover] or .ui-checkbox-state[-checked][-disabled][-hover] 224 | * 225 | * Examples: 226 | * 227 | * .ui-radio-state-checked (simply checked) 228 | * .ui-radio-state-checked-hover (checked and hovered/focused) 229 | * .ui-radio-state-hover (unchecked and hovered/focused) 230 | * 231 | * If you don´t have to deal with the doubble class bug of IE6 you can also use the simple ui-checkbox-checked, ui-checkbox-disabled, ui-checkbox-hover state-classnames (or: ui-radio-checked...) 232 | * and the ui-radio/ui-checkbox role-classnames. 233 | * 234 | */ 235 | 236 | span.ui-checkbox, 237 | span.ui-radio { 238 | display: block; 239 | float: left; 240 | width: 24px; 241 | height: 24px; 242 | background: url(images/radio-unselected.png) no-repeat; 243 | } 244 | 245 | span.ui-checkbox { 246 | background: url(images/check-unselected.png) no-repeat; 247 | } 248 | 249 | span.ui-helper-hidden { 250 | display: none; 251 | } 252 | 253 | span.ui-checkbox-state-checked, 254 | span.ui-checkbox-state-checked-hover { 255 | background: url(images/check-selected.png) no-repeat; 256 | } 257 | 258 | span.ui-radio-state-checked, 259 | span.ui-radio-state-checked-hover { 260 | background: url(images/radio-selected.png) no-repeat; 261 | } 262 | 263 | .ui-helper-hidden-accessible { 264 | position: absolute; 265 | left: -999em; 266 | } 267 | } 268 | 269 | -------------------------------------------------------------------------------- /public/resources/whitetheme/theme.less: -------------------------------------------------------------------------------- 1 | /* ***** BEGIN LICENSE BLOCK ***** 2 | * Version: MPL 1.1/GPL 2.0/LGPL 2.1 3 | * 4 | * The contents of this file are subject to the Mozilla Public License Version 5 | * 1.1 (the "License"); you may not use this file except in compliance with 6 | * the License. You may obtain a copy of the License at 7 | * http://www.mozilla.org/MPL/ 8 | * 9 | * Software distributed under the License is distributed on an "AS IS" basis, 10 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing rights and limitations under the 12 | * License. 13 | * 14 | * The Original Code is Bespin. 15 | * 16 | * The Initial Developer of the Original Code is 17 | * Mozilla. 18 | * Portions created by the Initial Developer are Copyright (C) 2009 19 | * the Initial Developer. All Rights Reserved. 20 | * 21 | * Contributor(s): 22 | * Bespin Team (bespin@mozilla.com) 23 | * 24 | * Alternatively, the contents of this file may be used under the terms of 25 | * either the GNU General Public License Version 2 or later (the "GPL"), or 26 | * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 27 | * in which case the provisions of the GPL or the LGPL are applicable instead 28 | * of those above. If you wish to allow use of your version of this file only 29 | * under the terms of either the GPL or the LGPL, and not to allow others to 30 | * use your version of this file under the terms of the MPL, indicate your 31 | * decision by deleting the provisions above and replace them with the notice 32 | * and other provisions required by the GPL or the LGPL. If you do not delete 33 | * the provisions above, a recipient may use your version of this file under 34 | * the terms of any one of the MPL, the GPL or the LGPL. 35 | * 36 | * ***** END LICENSE BLOCK ***** */ 37 | 38 | .bespin .cmd_typed { 39 | background: transparent -webkit-gradient( 40 | linear, 41 | left top, 42 | left bottom, 43 | color-stop(0.07, rgb(235,235,235)), 44 | color-stop(1, rgb(209,209,209)) 45 | ); 46 | background: transparent -moz-linear-gradient( 47 | center top, 48 | rgb(235,235,235) 7%, 49 | rgb(209,209,209) 100% 50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # www.robotstxt.org/ 2 | # www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156449 3 | 4 | User-agent: * 5 | 6 | -------------------------------------------------------------------------------- /public/temp.css: -------------------------------------------------------------------------------- 1 | #gritter-notice-wrapper { 2 | position: fixed; 3 | top: 20px; 4 | right: 20px; 5 | width: 301px; 6 | z-index: 9999; 7 | } 8 | 9 | .gritter-item-wrapper { 10 | position: relative; 11 | margin: 0 0 10px 0; 12 | background: url('.'); 13 | } 14 | 15 | .gritter-top { 16 | background: url(gritter.png) no-repeat left -30px; 17 | height: 10px; 18 | } 19 | 20 | .hover .gritter-top { 21 | background-position: right -30px; 22 | } 23 | 24 | .gritter-bottom { 25 | background: url(gritter.png) no-repeat left bottom; 26 | height: 8px; 27 | margin: 0; 28 | } 29 | 30 | .hover .gritter-bottom { 31 | background-position: bottom right; 32 | } 33 | 34 | .gritter-item { 35 | display: block; 36 | background: url(gritter.png) no-repeat left -40px; 37 | color: #eee; 38 | padding: 2px 11px 8px 11px; 39 | font-size: 11px; 40 | font-family: verdana; 41 | } 42 | 43 | .hover .gritter-item { 44 | background-position: right -40px; 45 | } 46 | 47 | .gritter-item p { 48 | padding: 0; 49 | margin: 0; 50 | } 51 | 52 | .gritter-close { 53 | position: absolute; 54 | top: 5px; 55 | left: 3px; 56 | background: url(gritter.png) no-repeat left top; 57 | cursor: pointer; 58 | width: 30px; 59 | height: 30px; 60 | } 61 | 62 | .gritter-title { 63 | font-size: 14px; 64 | font-weight: bold; 65 | padding: 0 0 7px 0; 66 | display: block; 67 | text-shadow: 1px 1px #000; 68 | } 69 | 70 | .gritter-image { 71 | width: 48px; 72 | height: 48px; 73 | float: left; 74 | } 75 | 76 | .gritter-with-image, .gritter-without-image { 77 | padding: 0 0 5px 0; 78 | } 79 | 80 | .gritter-with-image { 81 | width: 220px; 82 | float: right; 83 | } 84 | 85 | #testresult { 86 | position: absolute; 87 | top: 19px; 88 | right: 10px; 89 | text-align: right; 90 | font-weight: bold; 91 | } 92 | 93 | .core-test { 94 | font: 13px/16px "Helvetica", Verdana, Arial, sans-serif; 95 | margin: 0; 96 | padding: 0; 97 | position: absolute; 98 | top: 0; 99 | left: 0; 100 | right: 0; 101 | bottom: 0; 102 | background-color: #f0f0f0; 103 | overflow: auto; 104 | } 105 | 106 | .core-test .useragent { 107 | border-bottom: 1px solid #000; 108 | font: 11px/21px "Helvetica", Verdana, Arial, sans-serif; 109 | height: 20px; 110 | padding: 0 8px; 111 | } 112 | 113 | .core-test ul.detail { 114 | list-style-type: none; 115 | padding: 0; 116 | margin: 0; 117 | background-color: white; 118 | top: 24px; 119 | position: relative; 120 | } 121 | 122 | .core-test ul.detail ul { 123 | margin: 0; 124 | padding: 0; 125 | margin-left: 20px; 126 | position: relative; 127 | } 128 | 129 | .core-test ul.detail li { 130 | line-height: 24px; 131 | list-style-type: none; 132 | } 133 | 134 | .core-test ul.detail li.plan { 135 | margin: 4px 0; 136 | margin-left: -20px; 137 | padding-bottom: 4px; 138 | border-bottom: 1px #ccc solid; 139 | } 140 | 141 | .core-test ul.detail li.plan>.name { 142 | display: none; 143 | } 144 | 145 | .core-test ul.detail li.module>.name { 146 | display: block; 147 | background-color: #323453; 148 | padding: 0 4px; 149 | color: white; 150 | font-weight: bold; 151 | margin: 2px 0; 152 | } 153 | 154 | .core-test ul.detail li.test>.name { 155 | display: block; 156 | font-weight: bold; 157 | } 158 | 159 | .core-test ul.detail li.test.passed>.name { 160 | color: #888; 161 | } 162 | 163 | .core-test ul.detail li.test>.status { 164 | display: block; 165 | position: absolute; 166 | right: 0; 167 | top: 0; 168 | } 169 | 170 | .core-test ul.detail li.test.passed>.status { 171 | display: none; 172 | } 173 | 174 | .core-test ul.detail li.test>.status span { 175 | display: inline-block; 176 | min-width: 14px; 177 | text-align: center; 178 | padding: 0 4px; 179 | } 180 | 181 | .core-test ul.detail li.assertion { 182 | position: relative; 183 | } 184 | 185 | .core-test ul.detail li.assertion>.name { 186 | display: block; 187 | margin-right: 90px; 188 | } 189 | 190 | .core-test ul.detail li.assertion>.status { 191 | display: block; 192 | position: absolute; 193 | right: 0; 194 | top: 0; 195 | padding: 0 4px; 196 | margin-left: 8px; 197 | width: 80px; 198 | background: #ccc; 199 | text-transform: uppercase; 200 | } 201 | 202 | .core-test ul.detail li.assertion { 203 | margin: 2px 0; 204 | } 205 | 206 | .core-test ul.detail li.assertion.passed { 207 | color: #888; 208 | margin: 2px 0; 209 | } 210 | 211 | .core-test ul.detail li.assertion.passed .status { 212 | background: #bbe9b5; 213 | } 214 | 215 | .core-test ul.detail li.test>.status .passed, .core-test .final-status>.passed { 216 | background: #80D175; 217 | } 218 | 219 | .core-test ul.detail li.test>.status .failed, .core-test ul.detail li.assertion.failed .status, .core-test .final-status>.failed { 220 | background: #ea4d44; 221 | } 222 | 223 | .core-test ul.detail li.test>.status .errors, .core-test ul.detail li.assertion.errors .status, .core-test .final-status>.errors { 224 | background: red; 225 | color: #fff; 226 | } 227 | 228 | .core-test ul.detail li.test>.status .warnings, .core-test ul.detail li.assertion.warnings .status, .core-test .final-status>.warnings { 229 | background: #E49723; 230 | } 231 | 232 | .core-test ul.detail.hide-passed li.passed, .core-test ul.detail.hide-passed li.assertion.passed { 233 | display: none; 234 | } 235 | 236 | .core-test .testresult { 237 | position: relative; 238 | } 239 | 240 | .core-test label.hide-passed { 241 | position: absolute; 242 | display: block; 243 | left: 4px; 244 | top: 0; 245 | line-height: 26px; 246 | } 247 | 248 | .core-test .final-status { 249 | display: block; 250 | right: 4px; 251 | position: absolute; 252 | top: 0; 253 | line-height: 26px; 254 | font-weight: bold; 255 | } 256 | 257 | .core-test .final-status>span { 258 | margin-left: 2px; 259 | padding: 0 3px; 260 | } -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var connect = require('connect'), 2 | api = require('./api'), 3 | port = parseInt(process.env.PORT) || 8000; 4 | 5 | var server = connect.createServer( 6 | connect.logger(), 7 | connect.errorHandler({ dumpExceptions: true, showStack: true }) 8 | ); 9 | // Serve static resources. 10 | server.use("/", 11 | connect.conditionalGet(), 12 | connect.cache(), 13 | connect.gzip(), 14 | connect.staticProvider(__dirname + '/public') 15 | ); 16 | // Serve the API responses. 17 | server.use("/api", 18 | connect.bodyDecoder(), 19 | connect.cookieDecoder(), 20 | connect.router(api.router) 21 | ); 22 | server.listen(port); 23 | console.log('Nodify server running at http://127.0.0.1:' + port); 24 | 25 | process.addListener('uncaughtException', function (err) { 26 | console.log(err.stack); 27 | }); 28 | 29 | -------------------------------------------------------------------------------- /tmp/README: -------------------------------------------------------------------------------- 1 | This is the deployment directory. 2 | --------------------------------------------------------------------------------