From 04bc1c81da02d39233087eff70e3afbe8a06f5aa Mon Sep 17 00:00:00 2001 From: Andreas Savvides Date: Sun, 15 Sep 2013 18:41:46 +0100 Subject: [PATCH 01/40] Replacing tabs with whitespace for codebase consistency --- coder-base/apps/wifi/app.js | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/coder-base/apps/wifi/app.js b/coder-base/apps/wifi/app.js index 80c871f6..30f4faf6 100644 --- a/coder-base/apps/wifi/app.js +++ b/coder-base/apps/wifi/app.js @@ -90,26 +90,26 @@ exports.api_wifi_configure_handler = function( req, res ) { var wpatemplate = "network={\n" + - "\tssid=\"[ssid]\"\n" + - "\tpsk=\"[password]\"\n" + - "\tscan_ssid=1\n" + - "\tpriority=10\n" + - "}\n"; + "\tssid=\"[ssid]\"\n" + + "\tpsk=\"[password]\"\n" + + "\tscan_ssid=1\n" + + "\tpriority=10\n" + + "}\n"; var weptemplate = "network={\n" + - "\tssid=\"[ssid]\"\n" + - "\twep_key0=\"[password]\"\n" + - "\tscan_ssid=1\n" + - "\tkey_mgmt=NONE\n" + - "\tpriority=10\n" + - "}\n"; + "\tssid=\"[ssid]\"\n" + + "\twep_key0=\"[password]\"\n" + + "\tscan_ssid=1\n" + + "\tkey_mgmt=NONE\n" + + "\tpriority=10\n" + + "}\n"; var opentemplate = "network={\n" + - "\tssid=\"[ssid]\"\n" + - "\tscan_ssid=1\n" + - "\tkey_mgmt=NONE\n" + - "\tpriority=10\n" + - "}\n"; + "\tssid=\"[ssid]\"\n" + + "\tscan_ssid=1\n" + + "\tkey_mgmt=NONE\n" + + "\tpriority=10\n" + + "}\n"; var confentry=""; @@ -198,7 +198,7 @@ exports.api_wifi_list_handler = function( req, res ) { var access_points = {}; var debug=""; - var addHighestSignal = function( ssid, type, signal ) { + var addHighestSignal = function( ssid, type, signal ) { if ( !access_points[ssid] ) { access_points[ssid] = { ssid: ssid, type: type, signal: signal }; } else if ( access_points[ssid].signal < signal ) { From 730e1e659365cd3dddd1a85115f3da6a9be2af37 Mon Sep 17 00:00:00 2001 From: Andreas Savvides Date: Sun, 15 Sep 2013 18:43:30 +0100 Subject: [PATCH 02/40] Using proper Python commenting --- findcoder-appengine/api/coder.py | 36 +++++++++++++++----------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/findcoder-appengine/api/coder.py b/findcoder-appengine/api/coder.py index c37bba4c..3c752499 100644 --- a/findcoder-appengine/api/coder.py +++ b/findcoder-appengine/api/coder.py @@ -1,22 +1,20 @@ -/** - * Coder for Raspberry Pi - * A simple platform for experimenting with web stuff. - * http://goo.gl/coder - * - * Copyright 2013 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +# Coder for Raspberry Pi +# A simple platform for experimenting with web stuff. +# http://goo.gl/coder + +# Copyright 2013 Google Inc. All Rights Reserved. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import webapp2 import os From 81ade68def27e2f72edbc120cf2703ba463327b7 Mon Sep 17 00:00:00 2001 From: Andreas Savvides Date: Sun, 15 Sep 2013 18:44:10 +0100 Subject: [PATCH 03/40] Removing unused imports --- findcoder-appengine/api/coder.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/findcoder-appengine/api/coder.py b/findcoder-appengine/api/coder.py index 3c752499..c3dec7ef 100644 --- a/findcoder-appengine/api/coder.py +++ b/findcoder-appengine/api/coder.py @@ -17,12 +17,9 @@ # limitations under the License. import webapp2 -import os import time -import logging import re import json -import math from google.appengine.api import memcache CACHETIME = 120 From 44afb433582d521267643ecc24392956205ced84 Mon Sep 17 00:00:00 2001 From: Chris Matta Date: Sun, 15 Sep 2013 15:23:07 -0400 Subject: [PATCH 04/40] missing close

in hello_coder index --- coder-base/views/apps/hello_coder/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coder-base/views/apps/hello_coder/index.html b/coder-base/views/apps/hello_coder/index.html index 26984c73..573af7d5 100644 --- a/coder-base/views/apps/hello_coder/index.html +++ b/coder-base/views/apps/hello_coder/index.html @@ -27,7 +27,7 @@

Hello Coder

-

Get started with Coder by exploring this app. +

Get started with Coder by exploring this app.

Click the edit button "</>" to dive in.

It's at the top right.

From 209ca86a44c7f73ac86baf6a97a09bd4fb30db07 Mon Sep 17 00:00:00 2001 From: Ricky Pike Date: Thu, 3 Oct 2013 18:07:22 -0500 Subject: [PATCH 05/40] Use vars instead of multiple ssh cert file strings --- coder-base/server.js | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/coder-base/server.js b/coder-base/server.js index f7ec4f97..160663f7 100644 --- a/coder-base/server.js +++ b/coder-base/server.js @@ -32,6 +32,7 @@ var util = require('util'); var cons = require('consolidate'); var params = require('express-params'); var querystring = require('querystring'); +var path = require('path'); var loadApp = function( loadpath ) { @@ -136,11 +137,13 @@ var startSSLRedirect = function() { var startSSL = function() { + privateKeyFile=path.normalize('certs/server.key'); + certificateFile=path.normalize('certs/server.cert'); var privateKey=""; var certificate=""; try { - privateKey = fs.readFileSync('certs/server.key').toString(); - certificate = fs.readFileSync('certs/server.cert').toString(); + privateKey = fs.readFileSync(privateKeyFile).toString(); + certificate = fs.readFileSync(certificateFile).toString(); } catch ( e ) { util.print( "no certificate found. generating self signed cert.\n" ); } @@ -150,30 +153,30 @@ var startSSL = function() { } else { var spawn = require('child_process').spawn; - var genSelfSignedCert = function() { + var genSelfSignedCert = function(keyFile, certFile) { var genkey = spawn( 'openssl', [ 'req', '-x509', '-nodes', '-days', '365', '-newkey', 'rsa:2048', - '-keyout', 'certs/server.key', - '-out', 'certs/server.cert', + '-keyout', keyFile, + '-out', certFile, '-subj', '/C=' + config.country + '/ST=' + config.state + "/L=" + config.locale + "/CN=" + config.commonName + "/subjectAltName=" + config.subjectAltName ]); genkey.stdout.on('data', function(d) { util.print(d) } ); genkey.stderr.on('data', function(d) { util.print(d) } ); genkey.addListener( 'exit', function( code, signal ) { - fs.chmodSync('certs/server.key', '600'); + fs.chmodSync(privateKeyFile, '600'); loadServer(); }); }; var loadServer = function() { - privateKey = fs.readFileSync('certs/server.key').toString(); - certificate = fs.readFileSync('certs/server.cert').toString(); + privateKey = fs.readFileSync(privateKeyFile).toString(); + certificate = fs.readFileSync(certificateFile).toString(); https.createServer({ key: privateKey, cert: certificate }, sslapp).listen( config.listenPort, config.listenIP ); }; - genSelfSignedCert(); + genSelfSignedCert(privateKeyFile, certificateFile); } }; From 00a9c7fc5fa68b96761bc6f1b778efecc141848e Mon Sep 17 00:00:00 2001 From: Jason Striegel Date: Tue, 22 Oct 2013 19:28:10 -0700 Subject: [PATCH 06/40] Restructure: separate pi-specific apps and added localhost server. All apps have moved to /coder-apps/platform/[appname] and some crude scripts have been added to make moving files between coder-base and the coder-apps structure a bit easier. --- INSTALL | 61 ++ coder-apps/archive_app.sh | 35 + coder-apps/common/auth/app/app.js | 628 ++++++++++++++++++ .../common/auth/app}/meta.json | 0 .../common/auth/static}/css/index.css | 0 .../common/auth/static}/js/index.js | 0 .../common/auth/static/media/.gitignore | 0 coder-apps/common/auth/views/index.html | 108 +++ .../common/boilerplate/app}/app.js | 0 .../common/boilerplate/app}/meta.json | 0 .../common/boilerplate/static}/css/index.css | 0 .../common/boilerplate/static}/js/index.js | 0 .../boilerplate/static/media/.gitignore | 0 .../common/boilerplate/views}/index.html | 0 .../common/coder/app}/app.js | 0 .../common/coder/app}/meta.json | 0 .../common/coder/static}/css/index.css | 0 coder-apps/common/coder/static/js/index.js | 454 +++++++++++++ .../common/coder/static/media/.gitignore | 0 .../common/coder/static}/media/myapps_tip.png | Bin .../common/coder/static}/media/newapp_tip.png | Bin .../coder/static}/media/settings_tip.png | Bin coder-apps/common/coder/views/index.html | 138 ++++ .../common/coderlib/app}/app.js | 0 .../common/coderlib/app}/meta.json | 0 .../common/coderlib/static}/css/index.css | 0 .../common/coderlib/static}/js/index.js | 0 .../common/coderlib/static/media/.gitignore | 0 coder-apps/common/coderlib/views/index.html | 0 .../common/editor/app}/app.js | 2 +- .../common/editor/app}/meta.json | 2 +- .../common/editor/static}/css/index.css | 0 .../common/editor/static}/js/index.js | 0 .../common/editor/static/media/.gitignore | 0 .../common/editor/views}/index.html | 0 .../common/eyeball/app}/app.js | 0 .../common/eyeball/app}/meta.json | 2 +- .../common/eyeball/static}/css/index.css | 2 +- .../common/eyeball/static}/js/index.js | 0 .../common/eyeball/static/media/.gitignore | 0 .../common/eyeball/views}/index.html | 0 .../common/game2d/app}/app.js | 0 .../common/game2d/app}/meta.json | 0 .../common/game2d/static}/css/index.css | 0 .../common/game2d/static}/js/index.js | 0 .../common/game2d/static/media/.gitignore | 0 .../common/game2d/views}/index.html | 0 .../common/hello_coder/app}/app.js | 0 .../common/hello_coder/app}/meta.json | 0 .../common/hello_coder/static}/css/index.css | 0 .../common/hello_coder/static}/js/index.js | 0 .../hello_coder/static/media/.gitignore | 0 .../common/hello_coder/views}/index.html | 0 .../common/space_rocks_/app}/app.js | 0 .../common/space_rocks_/app}/meta.json | 0 .../common/space_rocks_/static}/css/index.css | 0 .../common/space_rocks_/static}/js/index.js | 0 .../space_rocks_/static/media/.gitignore | 0 .../space_rocks_/static}/media/die_ship.mp3 | Bin .../space_rocks_/static}/media/die_ship.ogg | Bin .../space_rocks_/static}/media/die_ship.wav | Bin .../static}/media/die_spacerock.mp3 | Bin .../static}/media/die_spacerock.ogg | Bin .../static}/media/die_spacerock.wav | Bin .../static}/media/spacerocks_shoot.mp3 | Bin .../static}/media/spacerocks_shoot.ogg | Bin .../static}/media/spacerocks_shoot.wav | Bin .../space_rocks_/static}/media/thrust.mp3 | Bin .../space_rocks_/static}/media/thrust.ogg | Bin .../space_rocks_/static}/media/thrust.wav | Bin .../common/space_rocks_/views}/index.html | 0 coder-apps/install_app.sh | 34 + coder-apps/install_common.sh | 29 + coder-apps/install_pi.sh | 25 + .../auth => coder-apps/pi/auth/app}/app.js | 0 coder-apps/pi/auth/app/meta.json | 8 + coder-apps/pi/auth/static/css/index.css | 133 ++++ coder-apps/pi/auth/static/js/index.js | 474 +++++++++++++ coder-apps/pi/auth/static/media/.gitignore | 0 .../pi/auth/views}/index.html | 0 coder-apps/pi/coder/app/app.js | 495 ++++++++++++++ coder-apps/pi/coder/app/meta.json | 8 + coder-apps/pi/coder/static/css/index.css | 489 ++++++++++++++ .../pi/coder/static}/js/index.js | 0 coder-apps/pi/coder/static/media/.gitignore | 0 .../pi/coder/static/media/myapps_tip.png | Bin 0 -> 9585 bytes .../pi/coder/static/media/newapp_tip.png | Bin 0 -> 10125 bytes .../pi/coder/static/media/settings_tip.png | Bin 0 -> 7218 bytes .../pi/coder/views}/index.html | 0 .../wifi => coder-apps/pi/wifi/app}/app.js | 0 .../wifi => coder-apps/pi/wifi/app}/meta.json | 0 .../pi/wifi/static}/css/index.css | 0 .../pi/wifi/static}/js/index.js | 0 .../pi/wifi/views}/index.html | 0 coder-base/apps/.gitignore | 0 coder-base/config.js.default | 28 + coder-base/config.js.localhost | 28 + coder-base/localserver.js | 177 +++++ coder-base/package.json | 3 +- coder-base/server.js | 8 +- coder-base/static/apps/.gitignore | 0 .../coder-base}/sudo_scripts/reboot | 0 .../coder-base}/sudo_scripts/setpipass | 0 .../coder-base}/sudo_scripts/wpa_cli_apscan | 0 .../coder-base}/sudo_scripts/wpa_cli_scan | 0 .../sudo_scripts/wpa_cli_scanresults | 0 106 files changed, 3364 insertions(+), 7 deletions(-) create mode 100644 INSTALL create mode 100755 coder-apps/archive_app.sh create mode 100644 coder-apps/common/auth/app/app.js rename {coder-base/apps/auth => coder-apps/common/auth/app}/meta.json (100%) rename {coder-base/static/apps/auth => coder-apps/common/auth/static}/css/index.css (100%) rename {coder-base/static/apps/auth => coder-apps/common/auth/static}/js/index.js (100%) rename coder-base/views/apps/coderlib/index.html => coder-apps/common/auth/static/media/.gitignore (100%) create mode 100644 coder-apps/common/auth/views/index.html rename {coder-base/apps/boilerplate => coder-apps/common/boilerplate/app}/app.js (100%) rename {coder-base/apps/boilerplate => coder-apps/common/boilerplate/app}/meta.json (100%) rename {coder-base/static/apps/boilerplate => coder-apps/common/boilerplate/static}/css/index.css (100%) rename {coder-base/static/apps/boilerplate => coder-apps/common/boilerplate/static}/js/index.js (100%) create mode 100644 coder-apps/common/boilerplate/static/media/.gitignore rename {coder-base/views/apps/boilerplate => coder-apps/common/boilerplate/views}/index.html (100%) rename {coder-base/apps/coder => coder-apps/common/coder/app}/app.js (100%) rename {coder-base/apps/coder => coder-apps/common/coder/app}/meta.json (100%) rename {coder-base/static/apps/coder => coder-apps/common/coder/static}/css/index.css (100%) create mode 100644 coder-apps/common/coder/static/js/index.js create mode 100644 coder-apps/common/coder/static/media/.gitignore rename {coder-base/static/apps/coder => coder-apps/common/coder/static}/media/myapps_tip.png (100%) rename {coder-base/static/apps/coder => coder-apps/common/coder/static}/media/newapp_tip.png (100%) rename {coder-base/static/apps/coder => coder-apps/common/coder/static}/media/settings_tip.png (100%) create mode 100644 coder-apps/common/coder/views/index.html rename {coder-base/apps/coderlib => coder-apps/common/coderlib/app}/app.js (100%) rename {coder-base/apps/coderlib => coder-apps/common/coderlib/app}/meta.json (100%) rename {coder-base/static/apps/coderlib => coder-apps/common/coderlib/static}/css/index.css (100%) rename {coder-base/static/apps/coderlib => coder-apps/common/coderlib/static}/js/index.js (100%) create mode 100644 coder-apps/common/coderlib/static/media/.gitignore create mode 100644 coder-apps/common/coderlib/views/index.html rename {coder-base/apps/editor => coder-apps/common/editor/app}/app.js (99%) rename {coder-base/apps/editor => coder-apps/common/editor/app}/meta.json (81%) rename {coder-base/static/apps/editor => coder-apps/common/editor/static}/css/index.css (100%) rename {coder-base/static/apps/editor => coder-apps/common/editor/static}/js/index.js (100%) create mode 100644 coder-apps/common/editor/static/media/.gitignore rename {coder-base/views/apps/editor => coder-apps/common/editor/views}/index.html (100%) rename {coder-base/apps/eyeball => coder-apps/common/eyeball/app}/app.js (100%) rename {coder-base/apps/eyeball => coder-apps/common/eyeball/app}/meta.json (81%) rename {coder-base/static/apps/eyeball => coder-apps/common/eyeball/static}/css/index.css (97%) rename {coder-base/static/apps/eyeball => coder-apps/common/eyeball/static}/js/index.js (100%) create mode 100644 coder-apps/common/eyeball/static/media/.gitignore rename {coder-base/views/apps/eyeball => coder-apps/common/eyeball/views}/index.html (100%) rename {coder-base/apps/game2d => coder-apps/common/game2d/app}/app.js (100%) rename {coder-base/apps/game2d => coder-apps/common/game2d/app}/meta.json (100%) rename {coder-base/static/apps/game2d => coder-apps/common/game2d/static}/css/index.css (100%) rename {coder-base/static/apps/game2d => coder-apps/common/game2d/static}/js/index.js (100%) create mode 100644 coder-apps/common/game2d/static/media/.gitignore rename {coder-base/views/apps/game2d => coder-apps/common/game2d/views}/index.html (100%) rename {coder-base/apps/hello_coder => coder-apps/common/hello_coder/app}/app.js (100%) rename {coder-base/apps/hello_coder => coder-apps/common/hello_coder/app}/meta.json (100%) rename {coder-base/static/apps/hello_coder => coder-apps/common/hello_coder/static}/css/index.css (100%) rename {coder-base/static/apps/hello_coder => coder-apps/common/hello_coder/static}/js/index.js (100%) create mode 100644 coder-apps/common/hello_coder/static/media/.gitignore rename {coder-base/views/apps/hello_coder => coder-apps/common/hello_coder/views}/index.html (100%) rename {coder-base/apps/space_rocks_ => coder-apps/common/space_rocks_/app}/app.js (100%) rename {coder-base/apps/space_rocks_ => coder-apps/common/space_rocks_/app}/meta.json (100%) rename {coder-base/static/apps/space_rocks_ => coder-apps/common/space_rocks_/static}/css/index.css (100%) rename {coder-base/static/apps/space_rocks_ => coder-apps/common/space_rocks_/static}/js/index.js (100%) create mode 100644 coder-apps/common/space_rocks_/static/media/.gitignore rename {coder-base/static/apps/space_rocks_ => coder-apps/common/space_rocks_/static}/media/die_ship.mp3 (100%) rename {coder-base/static/apps/space_rocks_ => coder-apps/common/space_rocks_/static}/media/die_ship.ogg (100%) rename {coder-base/static/apps/space_rocks_ => coder-apps/common/space_rocks_/static}/media/die_ship.wav (100%) rename {coder-base/static/apps/space_rocks_ => coder-apps/common/space_rocks_/static}/media/die_spacerock.mp3 (100%) rename {coder-base/static/apps/space_rocks_ => coder-apps/common/space_rocks_/static}/media/die_spacerock.ogg (100%) rename {coder-base/static/apps/space_rocks_ => coder-apps/common/space_rocks_/static}/media/die_spacerock.wav (100%) rename {coder-base/static/apps/space_rocks_ => coder-apps/common/space_rocks_/static}/media/spacerocks_shoot.mp3 (100%) rename {coder-base/static/apps/space_rocks_ => coder-apps/common/space_rocks_/static}/media/spacerocks_shoot.ogg (100%) rename {coder-base/static/apps/space_rocks_ => coder-apps/common/space_rocks_/static}/media/spacerocks_shoot.wav (100%) rename {coder-base/static/apps/space_rocks_ => coder-apps/common/space_rocks_/static}/media/thrust.mp3 (100%) rename {coder-base/static/apps/space_rocks_ => coder-apps/common/space_rocks_/static}/media/thrust.ogg (100%) rename {coder-base/static/apps/space_rocks_ => coder-apps/common/space_rocks_/static}/media/thrust.wav (100%) rename {coder-base/views/apps/space_rocks_ => coder-apps/common/space_rocks_/views}/index.html (100%) create mode 100755 coder-apps/install_app.sh create mode 100755 coder-apps/install_common.sh create mode 100755 coder-apps/install_pi.sh rename {coder-base/apps/auth => coder-apps/pi/auth/app}/app.js (100%) create mode 100644 coder-apps/pi/auth/app/meta.json create mode 100644 coder-apps/pi/auth/static/css/index.css create mode 100644 coder-apps/pi/auth/static/js/index.js create mode 100644 coder-apps/pi/auth/static/media/.gitignore rename {coder-base/views/apps/auth => coder-apps/pi/auth/views}/index.html (100%) create mode 100644 coder-apps/pi/coder/app/app.js create mode 100644 coder-apps/pi/coder/app/meta.json create mode 100644 coder-apps/pi/coder/static/css/index.css rename {coder-base/static/apps/coder => coder-apps/pi/coder/static}/js/index.js (100%) create mode 100644 coder-apps/pi/coder/static/media/.gitignore create mode 100644 coder-apps/pi/coder/static/media/myapps_tip.png create mode 100644 coder-apps/pi/coder/static/media/newapp_tip.png create mode 100644 coder-apps/pi/coder/static/media/settings_tip.png rename {coder-base/views/apps/coder => coder-apps/pi/coder/views}/index.html (100%) rename {coder-base/apps/wifi => coder-apps/pi/wifi/app}/app.js (100%) rename {coder-base/apps/wifi => coder-apps/pi/wifi/app}/meta.json (100%) rename {coder-base/static/apps/wifi => coder-apps/pi/wifi/static}/css/index.css (100%) rename {coder-base/static/apps/wifi => coder-apps/pi/wifi/static}/js/index.js (100%) rename {coder-base/views/apps/wifi => coder-apps/pi/wifi/views}/index.html (100%) create mode 100644 coder-base/apps/.gitignore create mode 100644 coder-base/config.js.default create mode 100644 coder-base/config.js.localhost create mode 100644 coder-base/localserver.js create mode 100644 coder-base/static/apps/.gitignore rename {coder-base => raspbian-addons/home/coder/coder-dist/coder-base}/sudo_scripts/reboot (100%) rename {coder-base => raspbian-addons/home/coder/coder-dist/coder-base}/sudo_scripts/setpipass (100%) rename {coder-base => raspbian-addons/home/coder/coder-dist/coder-base}/sudo_scripts/wpa_cli_apscan (100%) rename {coder-base => raspbian-addons/home/coder/coder-dist/coder-base}/sudo_scripts/wpa_cli_scan (100%) rename {coder-base => raspbian-addons/home/coder/coder-dist/coder-base}/sudo_scripts/wpa_cli_scanresults (100%) diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..67959a96 --- /dev/null +++ b/INSTALL @@ -0,0 +1,61 @@ + +CODER FOR RASPBERRY PI +The easy way... + +Coder for Raspberry Pi is distributed as a Pi SD Card image. +If you want to go the easy route, please check out the +documentation at http://goo.gl/coder + + + +EVERYONE ELSE + +If you want to install Coder on something else, or would +like to install it on an existing Raspberry Pi, you can +manually install it as well. + +BEFORE YOU START: + I recommend you do this as a normal user and not root. + The official pi distro has a "coder" user that Coder runs + under, and ports 80 and 433 are forwarded to 8080/8081. + +MANUAL INSTALL: + +1. You need to have node.js and npm installed + +2. Download Coder from git. + # git clone https://github.com/googlecreativelab/coder.git + +3. In coder-base run "npm install" to download all the + needed modules. + +4. Install the basic Coder apps. + # cd coder-apps + # ./install_common.sh + +5. Edit config.js to your liking. I recommend starting + with the settings in config.js.localhost and running + the localhost server. + +6. Start Coder + # cd coder-base + # node localserver.js + + +If you want to run Coder on an external port, you'll need +to run server.js instead of localserver.js. This requires +a bit of port forwarding setup in iptables. Look in +the raspbian-addons directory to see the customizations that +were made to the stock raspbian distro. + +The raspberry pi version of Coder has some other +tweaks that allow you to change your wifi settings +and keep your Pi password in sync with your Coder password. +There's some convoluted system configuration involved, which is +probably why you'd want to start with the Coder disk image, +but the modified apps are available by running ./install_pi.sh +after step 4. Modifications to the stock raspbian configuration +can be found in raspbian-addons. + + + diff --git a/coder-apps/archive_app.sh b/coder-apps/archive_app.sh new file mode 100755 index 00000000..0012ca5a --- /dev/null +++ b/coder-apps/archive_app.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +## +## Copies an application from the coder-base working directory to +## the coder-apps directory. +## +## sh archive_app appname base_path apps_path +## +## Eg. +## sh archive_app hello_coder ../coder-base/ ./common/ + +if [ $# != 3 ] + then + echo -e "\nUse:\narchive_app appname base_path apps_path\n" + exit +fi + +app=$1 +base=$2 +dest=$3 + +mkdir "$dest/$app" +mkdir "$dest/$app/app" +mkdir "$dest/$app/static" +mkdir "$dest/$app/static/js" +mkdir "$dest/$app/static/css" +mkdir "$dest/$app/static/media" +mkdir "$dest/$app/views" +touch "$dest/$app/static/media/.gitignore" + +cp $base/apps/$app/* $dest/$app/app/ +cp $base/views/apps/$app/* $dest/$app/views/ +cp $base/static/apps/$app/js/* $dest/$app/static/js/ +cp $base/static/apps/$app/css/* $dest/$app/static/css/ +cp $base/static/apps/$app/media/* $dest/$app/static/media/ diff --git a/coder-apps/common/auth/app/app.js b/coder-apps/common/auth/app/app.js new file mode 100644 index 00000000..dacd9034 --- /dev/null +++ b/coder-apps/common/auth/app/app.js @@ -0,0 +1,628 @@ +/** + * Coder for Raspberry Pi + * A simple platform for experimenting with web stuff. + * http://goo.gl/coder + * + * Copyright 2013 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var mustache = require('mustache'); +var util = require('util'); +var fs = require('fs'); +var bcrypt = require('bcrypt'); + +//stores cache of password hash and device name +var device_settings = { + password_hash: '', + device_name: '', + hostname: '', + coder_owner: '', + coder_color: '#3e3e3e' +}; + + +exports.settings={}; +//These are dynamically updated by the runtime +//settings.appname - the app id (folder) where your app is installed +//settings.viewpath - prefix to where your view html files are located +//settings.staticurl - base url path to static assets /static/apps/appname +//settings.appurl - base url path to this app /app/appname +//settings.device_name - name the user gave to their coder "Susie's Coder" + + +exports.get_routes = [ + { path:'/', handler:'index_handler'}, + { path:'/login', handler:'login_handler'}, + { path:'/logout', handler:'logout_handler'}, + { path:'/configure', handler:'configure_handler'}, + { path:'/addpassword', handler:'addpassword_handler'}, + { path:'/changepassword', handler:'changepassword_handler'}, + { path: '/api/devicename/get', handler: 'api_devicename_get_handler' }, + { path: '/api/codercolor/get', handler: 'api_codercolor_get_handler' }, + { path: '/api/coderowner/get', handler: 'api_coderowner_get_handler' } +]; + + +exports.post_routes = [ + { path: '/api/login', handler: 'api_login_handler' }, + { path: '/api/logout', handler: 'api_logout_handler' }, + { path: '/api/devicename/set', handler: 'api_devicename_set_handler' }, + { path: '/api/codercolor/set', handler: 'api_codercolor_set_handler' }, + { path: '/api/coderowner/set', handler: 'api_coderowner_set_handler' }, + { path: '/api/addpassword', handler: 'api_addpassword_handler' }, + { path: '/api/changepassword', handler: 'api_changepassword_handler' } +]; + +exports.on_destroy = function() { +}; + + +exports.isAuthenticated = function( req ) { + if ( typeof req.session !== 'undefined' && typeof req.session.authenticated !== 'undefined' ) { + return req.session.authenticated === true; + } + return false; +}; + +exports.isConfigured = function() { + if ( typeof device_settings.device_name !== 'undefined' && device_settings.device_name !== '' && + typeof device_settings.hostname !== 'undefined' && device_settings.hostname !== '' ) { + return true; + } else { + return false; + } +}; + +exports.hasPassword = function() { + if ( typeof device_settings.password_hash !== 'undefined' && device_settings.password_hash !== '' ) { + return true; + } else { + return false; + } +}; + +exports.getDeviceName = function() { + return device_settings.device_name; +}; +exports.getCoderOwner = function() { + return device_settings.coder_owner; +}; +exports.getCoderColor = function() { + return device_settings.coder_color; +}; + +exports.authenticate = function( req, password ) { + + var authenticated = bcrypt.compareSync( password, device_settings.password_hash ); + if ( authenticated ) { + req.session.authenticated = true; + } + + return authenticated; +}; + +exports.logout = function( req ) { + + req.session.authenticated = false; +}; + + +exports.index_handler = function( req, res ) { + + var firstuse = "?firstuse"; + if ( typeof( req.param('firstuse') ) === 'undefined' ) { + firstuse = ""; + } + + if ( !exports.isConfigured() ) { + res.redirect('/app/auth/configure?firstuse'); + } else if ( !exports.hasPassword() ) { + res.redirect('/app/auth/addpassword?firstuse'); + } else if ( !exports.isAuthenticated(req) ) { + res.redirect('/app/auth/login' + firstuse); + } else { + res.redirect('/app/coder' + firstuse); + } +}; + +exports.addpassword_handler = function( req, res ) { + var tmplvars = {}; + tmplvars['static_url'] = exports.settings.staticurl; + tmplvars['app_name'] = exports.settings.appname; + tmplvars['app_url'] = exports.settings.appurl; + tmplvars['device_name'] = exports.settings.device_name; + tmplvars['page_mode'] = "addpassword"; + + //only allow this step if they have not yet set a password + if ( !exports.hasPassword() ) { + res.render( exports.settings.viewpath + '/index', tmplvars ); + } else { + res.redirect('/app/auth/login'); + } +}; + +exports.changepassword_handler = function( req, res ) { + var tmplvars = {}; + tmplvars['static_url'] = exports.settings.staticurl; + tmplvars['app_name'] = exports.settings.appname; + tmplvars['app_url'] = exports.settings.appurl; + tmplvars['device_name'] = exports.settings.device_name; + tmplvars['page_mode'] = "changepassword"; + + //only allow this step if they are authenticated + if ( exports.isAuthenticated(req) ) { + res.render( exports.settings.viewpath + '/index', tmplvars ); + } else { + res.redirect('/app/auth/login'); + } +}; + +exports.configure_handler = function( req, res ) { + var tmplvars = {}; + tmplvars['static_url'] = exports.settings.staticurl; + tmplvars['app_name'] = exports.settings.appname; + tmplvars['app_url'] = exports.settings.appurl; + tmplvars['device_name'] = exports.settings.device_name; + tmplvars['page_mode'] = "configure"; + + //only allow this step if they are authenticated or have not yet set a password + if ( exports.isAuthenticated(req) || !exports.hasPassword() ) { + res.render( exports.settings.viewpath + '/index', tmplvars ); + } else { + res.redirect('/app/auth/login'); + } +}; + +exports.api_devicename_get_handler = function( req, res ) { + res.json({ + device_name: exports.getDeviceName() + }); +}; +exports.api_codercolor_get_handler = function( req, res ) { + res.json({ + coder_color: exports.getCoderColor() + }); +}; +exports.api_coderowner_get_handler = function( req, res ) { + //only allow this step if they are authenticated or have not yet set a password + if ( !exports.isAuthenticated(req) && exports.hasPassword() ) { + res.json({ + status: "error", + error: "not authenticated" + }); + return; + } + res.json({ + coder_owner: exports.getCoderOwner() + }); +}; + +exports.api_devicename_set_handler = function( req, res ) { + + //only allow this step if they are authenticated or have not yet set a password + if ( !exports.isAuthenticated(req) && exports.hasPassword() ) { + res.json({ + status: "error", + error: "not authenticated" + }); + return; + } + + var devicename = req.param('device_name'); + if ( !devicename || devicename === "" || !isValidDeviceName( devicename ) ) { + res.json({ + status: 'error', + error: "invalid device name" + }); + return; + } + + device_settings.device_name = devicename; + device_settings.hostname = hostnameFromDeviceName( devicename ); + + err = saveDeviceSettings(); + + if ( !err ) { + res.json({ + status: "success", + device_name: device_settings.device_name, + hostname: device_settings.hostname + }); + } else { + res.json({ + status: "error", + error: "could not save device settings" + }); + } + +}; + + +exports.api_coderowner_set_handler = function( req, res ) { + + //only allow this step if they are authenticated or have not yet set a password + if ( !exports.isAuthenticated(req) && exports.hasPassword() ) { + res.json({ + status: "error", + error: "not authenticated" + }); + return; + } + + var owner = req.param('coder_owner'); + if ( typeof owner === 'undefined' ) { + res.json({ + status: 'error', + error: "invalid owner name" + }); + return; + } + + device_settings.coder_owner = owner; + + err = saveDeviceSettings(); + + if ( !err ) { + res.json({ + status: "success", + coder_owner: device_settings.coder_owner + }); + } else { + res.json({ + status: "error", + error: "could not save device settings" + }); + } + +}; + +exports.api_codercolor_set_handler = function( req, res ) { + + //only allow this step if they are authenticated or have not yet set a password + if ( !exports.isAuthenticated(req) && exports.hasPassword() ) { + res.json({ + status: "error", + error: "not authenticated" + }); + return; + } + + var color = req.param('coder_color'); + if ( typeof color === 'undefined' || !isValidColor( color ) ) { + res.json({ + status: 'error', + error: "invalid color" + }); + return; + } + + device_settings.coder_color = color; + + err = saveDeviceSettings(); + + if ( !err ) { + res.json({ + status: "success", + coder_color: device_settings.coder_color + }); + } else { + res.json({ + status: "error", + error: "could not save device settings" + }); + } + +}; + +exports.api_addpassword_handler = function( req, res ) { + + //only allow this step if they have not yet set a password + if ( exports.hasPassword() ) { + res.json({ + status: "error", + error: "not authenticated" + }); + return; + } + + var pass = req.param('password'); + if ( !pass || pass === "" || !isValidPassword( pass ) ) { + res.json({ + status: 'error', + error: getPasswordProblem( pass ) + }); + return; + } + + var spawn = require('child_process').spawn; + var err=0; + //device_settings.device_name = devicename; + var erroutput = ""; + var output = ""; + //var setpipass = process.cwd() + '/sudo_scripts/setpipass'; + //var setpass = spawn( '/usr/bin/sudo', [setpipass] ); + //setpass.stdout.on( 'data', function( d ) { + // output += d; + //}); + //setpass.stderr.on( 'data', function( d ) { + // erroutput += d; + //}); + + //setpass.addListener( 'exit', function( code, signal ) { + var completed = function( code, signal ) { + err = code; + + + if ( err ) { + res.json({ + status: "error", + error: erroutput + }); + return; + } + + //TODO - Load hashed password + var s = bcrypt.genSaltSync(10); + var h = bcrypt.hashSync( pass, s ); + util.log("PASSWORD INITIALIZED"); + device_settings.password_hash = h; + err = saveDeviceSettings(); + + if ( !err ) { + res.json({ + status: "success" + }); + } else { + res.json({ + status: "error", + error: "Could not save device settings." + }); + } + + }; + + completed(); + + //setpass.stdin.write(pass + '\n'); + //setpass.stdin.write(pass + '\n'); + //setpass.stdin.end(); + +}; + + + +exports.api_changepassword_handler = function( req, res ) { + + //only allow this step if they are authenticated + if ( !exports.isAuthenticated(req) ) { + res.json({ + status: "error", + error: "not authenticated" + }); + return; + } + + var oldpass = req.param('oldpassword'); + var pass = req.param('password'); + + //Make sure old pass is set and matches + if ( typeof oldpass === 'undefined' || oldpass === "" + || !bcrypt.compareSync( oldpass, device_settings.password_hash ) ) { + res.json({ + status: 'error', + error: "old password was incorrect" + }); + return; + } + + if ( !pass || pass === "" || !isValidPassword( pass ) ) { + res.json({ + status: 'error', + error: getPasswordProblem( pass ) + }); + return; + } + + var spawn = require('child_process').spawn; + var err=0; + //device_settings.device_name = devicename; + var erroutput = ""; + var output = ""; + var setpipass = process.cwd() + '/sudo_scripts/setpipass'; + var setpass = spawn( '/usr/bin/sudo', [setpipass] ); + setpass.stdout.on( 'data', function( d ) { + output += d; + }); + setpass.stderr.on( 'data', function( d ) { + erroutput += d; + }); + + setpass.addListener( 'exit', function( code, signal ) { + err = code; + + + if ( err ) { + res.json({ + status: "error", + error: erroutput + }); + return; + } + + //TODO - Load hashed password + var s = bcrypt.genSaltSync(10); + var h = bcrypt.hashSync( pass, s ); + util.log("PASSWORD INITIALIZED"); + device_settings.password_hash = h; + err = saveDeviceSettings(); + + if ( !err ) { + res.json({ + status: "success" + }); + } else { + res.json({ + status: "error", + error: "Could not save device settings." + }); + } + + }); + setpass.stdin.write(pass + '\n'); + setpass.stdin.write(pass + '\n'); + setpass.stdin.end(); + +}; + + +exports.login_handler = function( req, res ) { + var tmplvars = {}; + tmplvars['static_url'] = exports.settings.staticurl; + tmplvars['app_name'] = exports.settings.appname; + tmplvars['app_url'] = exports.settings.appurl; + tmplvars['device_name'] = exports.settings.device_name; + tmplvars['page_mode'] = "login"; + + + //TODO - should this log you out automatically? + req.session.authenticated = false; + res.render( exports.settings.viewpath + '/index', tmplvars ); +}; + +exports.logout_handler = function( req, res ) { + var tmplvars = {}; + tmplvars['static_url'] = exports.settings.staticurl; + tmplvars['app_name'] = exports.settings.appname; + tmplvars['app_url'] = exports.settings.appurl; + tmplvars['device_name'] = exports.settings.device_name; + tmplvars['page_mode'] = "logout"; + + req.session.authenticated = false; + res.render( exports.settings.viewpath + '/index', tmplvars ); +}; + +exports.api_login_handler = function( req, res ) { + if ( typeof req.body.password !== 'undefined' && req.body.password !== "" ) { + var authenticated = exports.authenticate( req, req.body.password ); + if ( authenticated === true ) { + res.json( { status: 'success'} ); + return; + } + } + res.json( { + status: 'error', + error: 'invalid password' + } ); +}; +exports.api_logout_handler = function( req, res ) { + req.session.authenticated = false; + + res.json( { status: 'success'} ); +}; + +var saveDeviceSettings = function() { + err = fs.writeFileSync( process.cwd() + "/device.json", JSON.stringify(device_settings, null, 4), 'utf8' ); + return err; +}; + +var reloadDeviceSettings = function() { + var settings = { + password_hash: '', + device_name: '', + hostname: '', + coder_owner: '', + coder_color: '' + }; + + var loadedsettings = JSON.parse(fs.readFileSync( process.cwd() + "/device.json", 'utf-8' )); + settings.password_hash = ( typeof loadedsettings.password_hash !== 'undefined' && loadedsettings.password_hash !== '' ) ? loadedsettings.password_hash : settings.password_hash; + settings.device_name = ( typeof loadedsettings.device_name !== 'undefined' && loadedsettings.device_name !== '' ) ? loadedsettings.device_name : settings.device_name; + settings.hostname = ( typeof loadedsettings.hostname !== 'undefined' && loadedsettings.hostname !== '' ) ? loadedsettings.hostname : settings.hostname; + settings.coder_owner = ( typeof loadedsettings.coder_owner !== 'undefined' && loadedsettings.coder_owner !== '' ) ? loadedsettings.coder_owner : settings.coder_owner; + settings.coder_color = ( typeof loadedsettings.coder_color !== 'undefined' && loadedsettings.coder_color !== '' ) ? loadedsettings.coder_color : settings.coder_color; + + device_settings = settings; +} +reloadDeviceSettings(); + + +var isValidDeviceName = function( name ) { + if ( !name || name === '' ) { + return false; + } + //starts with an ascii word char. can contain word char's spaces and ' + if ( !name.match(/^[a-zA-Z0-9][\w ']*$/) ) { + return false; + } + //ends in an ascii word char + if ( !name.match(/[a-zA-Z0-9]$/) ) { + return false; + } + return true; +}; +var hostnameFromDeviceName = function( name ) { + var hostname = name; + hostname = hostname.toLowerCase(); + hostname = hostname.replace(/[^a-z0-9\- ]/g, ''); + hostname = hostname.replace(/[\- ]+/g,'-'); + return hostname; +}; + +var getPasswordProblem = function( pass ) { + if ( !pass || pass === '' ) { + return "the password is empty"; + } + if ( pass.length < 6 ) { + return "the password should contain at least 6 characters"; + } + if ( !pass.match(/[a-z]/) || + !pass.match(/[A-Z0-9\-\_\.\,\;\:\'\"\[\]\{\}\!\@\#\$\%\^\&\*\(\)\\].*[A-Z0-9\-\_\.\,\;\:\'\"\[\]\{\}\!\@\#\$\%\^\&\*\(\)\\]/) ) { + return "your password must contain a lower case letter and at least two upper case letters or numbers"; + } +}; + +var isValidPassword = function( pass ) { + if ( !pass || pass === '' ) { + return false; + } + //at least 6 characters + if ( pass.length < 6 ) { + return false; + } + //contains lower case + if ( !pass.match(/[a-z]/) ) { + return false; + } + //contains two upper case or numbers + if ( !pass.match(/[A-Z0-9\-\_\.\,\;\:\'\"\[\]\{\}\!\@\#\$\%\^\&\*\(\)\\].*[A-Z0-9\-\_\.\,\;\:\'\"\[\]\{\}\!\@\#\$\%\^\&\*\(\)\\]/) ) { + return false; + } + return true; +}; + +var isValidColor = function( color ) { + if ( !color || color === '' ) { + return false; + } + color = color.toLowerCase(); + if ( !color.match(/^\#[a-f0-9]{6}$/) ) { + return false; + } + return true; +} + + + + diff --git a/coder-base/apps/auth/meta.json b/coder-apps/common/auth/app/meta.json similarity index 100% rename from coder-base/apps/auth/meta.json rename to coder-apps/common/auth/app/meta.json diff --git a/coder-base/static/apps/auth/css/index.css b/coder-apps/common/auth/static/css/index.css similarity index 100% rename from coder-base/static/apps/auth/css/index.css rename to coder-apps/common/auth/static/css/index.css diff --git a/coder-base/static/apps/auth/js/index.js b/coder-apps/common/auth/static/js/index.js similarity index 100% rename from coder-base/static/apps/auth/js/index.js rename to coder-apps/common/auth/static/js/index.js diff --git a/coder-base/views/apps/coderlib/index.html b/coder-apps/common/auth/static/media/.gitignore similarity index 100% rename from coder-base/views/apps/coderlib/index.html rename to coder-apps/common/auth/static/media/.gitignore diff --git a/coder-apps/common/auth/views/index.html b/coder-apps/common/auth/views/index.html new file mode 100644 index 00000000..d2da0a2a --- /dev/null +++ b/coder-apps/common/auth/views/index.html @@ -0,0 +1,108 @@ + + + + Coder + + + + + + + + + + + + + + + + + +
+ +
+
+
+ + + + + + + + + + + +
+
+ + + + + + diff --git a/coder-base/apps/boilerplate/app.js b/coder-apps/common/boilerplate/app/app.js similarity index 100% rename from coder-base/apps/boilerplate/app.js rename to coder-apps/common/boilerplate/app/app.js diff --git a/coder-base/apps/boilerplate/meta.json b/coder-apps/common/boilerplate/app/meta.json similarity index 100% rename from coder-base/apps/boilerplate/meta.json rename to coder-apps/common/boilerplate/app/meta.json diff --git a/coder-base/static/apps/boilerplate/css/index.css b/coder-apps/common/boilerplate/static/css/index.css similarity index 100% rename from coder-base/static/apps/boilerplate/css/index.css rename to coder-apps/common/boilerplate/static/css/index.css diff --git a/coder-base/static/apps/boilerplate/js/index.js b/coder-apps/common/boilerplate/static/js/index.js similarity index 100% rename from coder-base/static/apps/boilerplate/js/index.js rename to coder-apps/common/boilerplate/static/js/index.js diff --git a/coder-apps/common/boilerplate/static/media/.gitignore b/coder-apps/common/boilerplate/static/media/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/coder-base/views/apps/boilerplate/index.html b/coder-apps/common/boilerplate/views/index.html similarity index 100% rename from coder-base/views/apps/boilerplate/index.html rename to coder-apps/common/boilerplate/views/index.html diff --git a/coder-base/apps/coder/app.js b/coder-apps/common/coder/app/app.js similarity index 100% rename from coder-base/apps/coder/app.js rename to coder-apps/common/coder/app/app.js diff --git a/coder-base/apps/coder/meta.json b/coder-apps/common/coder/app/meta.json similarity index 100% rename from coder-base/apps/coder/meta.json rename to coder-apps/common/coder/app/meta.json diff --git a/coder-base/static/apps/coder/css/index.css b/coder-apps/common/coder/static/css/index.css similarity index 100% rename from coder-base/static/apps/coder/css/index.css rename to coder-apps/common/coder/static/css/index.css diff --git a/coder-apps/common/coder/static/js/index.js b/coder-apps/common/coder/static/js/index.js new file mode 100644 index 00000000..c6ac1a67 --- /dev/null +++ b/coder-apps/common/coder/static/js/index.js @@ -0,0 +1,454 @@ +/** + * Coder for Raspberry Pi + * A simple platform for experimenting with web stuff. + * http://goo.gl/coder + * + * Copyright 2013 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var metadata; + +$(document).ready( function() { + + Coder.listApps( buildAppList ); + + $('#addapp_button').click( function(e){ + $(this).hide(); + $("#createform").show(); + }); + $('#createform .cancel').click( function(e){ + $("#createform").hide(); + $("#addapp_button").show(); + }); + $('#createform .submit').click( createAppClicked ); + + $('#import_file').on('change', handleFileImport ); + + $('#createform .formfield.textinput .label').click( focusTextInput ); + $('#createform .formfield.textinput input').click( focusTextInput ); + $('#createform .formfield.textinput input').focus( hideTextLabel ); + $('#createform .formfield.textinput input').blur( onBlurTextInput ); + + $("#createform .colorchit").click( selectAppColor ); + activateCurrentColor(); + + + $("#settings_button").click( toggleSettings ); + $("#settingscontainer .changepass").click( function() { + window.location.href="http://github.com/app/auth/changepassword"; + }); + + $("#settingscontainer .colorchit").click( selectCoderColor ); + activateCurrentCoderColor(); + updateSettingsData(); + $("#settingscontainer input").on('change', checkChangedSettings ); + $("#settingscontainer input").on('keydown', function() { setTimeout( checkChangedSettings, 0); } ); + $("#settingscontainer .cancel").click( revertSettings ); + $("#settingscontainer .save").click( saveSettings ); + + $("#settingscontainer .logout").click( function() { + window.location.href="http://github.com/app/auth/logout"; + }); + + if ( typeof getParams['firstuse'] !== 'undefined' ) { + setTimeout( function(){ + buildIntroduction(); + }, 400 ); + } else { + $('#introduction').css('display','none'); + } +}); + + +var buildIntroduction = function() { + $('#introduction').css({ + 'display': 'none', + 'visibility': 'visible' + }).fadeIn( 'slow', function() { + setTimeout( function() { + $('#myapps_tip').css({'visibility':'visible'}).hide().fadeIn(); + }, 1000); + setTimeout( function() { + $('#newapp_tip').css({'visibility':'visible'}).hide().fadeIn(); + }, 2000); + setTimeout( function() { + $('#settings_tip').css({'visibility':'visible'}).hide().fadeIn(); + }, 3000); + }); + $('.gotit').click( function() { + $('#introduction').fadeOut(function() { + $(this).hide(); + }); + }); +}; + + + + + +var revertSettings = function() { + activateCurrentCoderColor(); + updateSettingsData(); + checkChangedSettings(); +}; + +var updateSettingsData = function( ) { + $('#coder_name').val( device_name ); + $('#coder_ownername').val( coder_owner ); +}; + +var hideTextLabel = function() { + $(this).parent().find('.label').hide(); +}; +var focusTextInput = function() { + $(this).parent().find('input').focus(); +}; +var onBlurTextInput = function() { + if ( $(this).val() == "" ) { + $(this).parent().find('.label').show(); + } +}; +var thefile = null; +var handleFileImport = function( ev ) { + var files = ev.target.files; + + if ( files && files.length > 0 ) { + var importfile = files[0]; + + //console.log( importfile ); + if (!importfile.type.match('application/zip')) { + alert('This doesn\'t appear to be a Coder project zip file'); + return false; + } + thefile = importfile; + + var fdata = new FormData(); + fdata.append( 'import_file', thefile ); + fdata.append( 'test', 'foo' ); + + $.ajax({ + url: '/app/coder/api/app/import', + type: 'POST', + contentType: false, + processData: false, + cache: false, + data: fdata, + success: function( data ) { + //console.log('upload returned'); + //console.log(data); + if ( data.status === 'success' ) { + var newappid = data.appname; + window.location.href = '/app/editor/edit/' + encodeURIComponent(newappid); + } else if ( typeof data.error !== 'undefined' ) { + alert( data.error ); + } + } + }); + + + /* + var reader = new FileReader(); + // Closure to capture the file information. + reader.onload = (function(theFile) { + return function(e) { + // Render thumbnail. + var span = document.createElement('span'); + span.innerHTML = [''].join(''); + document.getElementById('list').insertBefore(span, null); + }; + })(f); + + reader.readAsDataURL(f); + */ + } +}; + + +var settingson = false; +var toggleSettings = function() { + settingson = !settingson; + enableSettings( settingson ); +}; +var enableSettings = function( on ) { + settingson = on; + if ( settingson ) { + $("body").addClass('settingsEnabled'); + } else { + $("body").removeClass('settingsEnabled'); + } +}; + + +var activateCurrentCoderColor = function() { + var current = coder_color; + $("#coder_nav").css('background-color', current); + $("#settingscontainer .colorchit").each( function() { + $this = $(this); + $this.removeClass('active'); + + if ( rgb2hex($this.css('background-color')) === current ) { + $this.addClass('active'); + } + }); + +}; + +var selectCoderColor = function() { + $this = $(this); + $("#settingscontainer .colorchit").removeClass('active'); + $this.addClass('active'); + checkChangedSettings(); +}; + +var device_changed = false; +var owner_changed = false; +var color_changed = false; +var checkChangedSettings = function() { + var changed = false; + device_changed = false; + owner_changed = false; + color_changed = false; + + if ( $('#coder_name').val() !== device_name ) { + changed = device_changed = true; + } + if ( $('#coder_ownername').val() !== coder_owner ) { + changed = owner_changed = true; + } + var $selectedcolor = $("#settingscontainer .colorchit.active").first(); + if ( $selectedcolor.get(0) && rgb2hex($selectedcolor.css('background-color')) !== coder_color.toLowerCase() ) { + changed = color_changed = true; + } + + if ( changed ) { + $('#settingscontainer').addClass('changed'); + } else { + $('#settingscontainer').removeClass('changed'); + } +}; + +var saveSettings = function() { + + var saveDeviceName = function(callback) { + if ( !device_changed ) { + callback(); + return; + } + $.post('/app/auth/api/devicename/set', + { 'device_name': $('#coder_name').val() }, + function(d) { + //console.log( d ); + if ( d.status === 'success' ) { + device_name = d.device_name; + $("#coder_logo").text( device_name ); + } + callback(); + } + ); + }; + + var saveOwnerName = function(callback) { + if ( !owner_changed ) { + callback(); + return; + } + $.post('/app/auth/api/coderowner/set', + { 'coder_owner': $('#coder_ownername').val() }, + function(d) { + //console.log( d ); + if ( d.status === 'success' ) { + coder_owner = d.coder_owner; + } + callback(); + } + ); + }; + + var saveCoderColor = function(callback) { + if ( !color_changed ) { + callback(); + return; + } + var $selectedcolor = $("#settingscontainer .colorchit.active").first(); + if ( !$selectedcolor.get(0) ) { + callback(); + return; + } + var hexcolor = rgb2hex($selectedcolor.css('background-color')); + + + $.post('/app/auth/api/codercolor/set', + { 'coder_color': hexcolor }, + function(d) { + //console.log( d ); + if ( d.status === 'success' ) { + coder_color = d.coder_color; + $("#coder_nav").css('background-color', coder_color); + } + callback(); + } + ); + }; + + saveDeviceName(function() { + saveOwnerName(function() { + saveCoderColor(function() { + checkChangedSettings(); + }); + }); + }); + +}; + +var buildAppList = function(apps){ + + + //get the app color from our own app (appname is set globally in template) + metadata = apps[appname].metadata; + $('.userbgcolor').css('background-color', metadata.color); + + var $apptmpl = $('#appitem_template').clone(); + $apptmpl.attr('id', '').css('display',''); + + var launchApp = function( appname ) { + return function(e) { + e.preventDefault(); + e.stopPropagation(); + window.location.href = '/app/' + encodeURIComponent(appname); + }; + }; + var editApp = function( appname ) { + return function(e) { + e.preventDefault(); + e.stopPropagation(); + window.location.href = '/app/editor/edit/' + encodeURIComponent(appname); + }; + }; + + + + //Sort the apps by more recently modified + var sortedapps = []; + for ( var k in apps ) { + sortedapps.push( apps[k] ); + } + sortedapps.sort( function(a,b) { + if ( a.ctime < b.ctime ) { + return 1; + } else if ( b.ctime < a.ctime ) { + return -1; + } else { + return 0; + } + }); + + for ( var x=0; x