├── Procfile ├── Procfile_dev ├── routes ├── email_confirmed.js ├── upload.js ├── index.js └── signup.js ├── public ├── images │ ├── favicon │ │ ├── 64.png │ │ ├── favicon.ico │ │ ├── mstile-70x70.png │ │ ├── favicon-160x160.png │ │ ├── favicon-16x16.png │ │ ├── favicon-196x196.png │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.png │ │ ├── mstile-144x144.png │ │ ├── mstile-150x150.png │ │ ├── mstile-310x150.png │ │ ├── mstile-310x310.png │ │ ├── apple-touch-icon.png │ │ ├── apple-touch-icon-57x57.png │ │ ├── apple-touch-icon-60x60.png │ │ ├── apple-touch-icon-72x72.png │ │ ├── apple-touch-icon-76x76.png │ │ ├── apple-touch-icon-114x114.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-144x144.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-precomposed.png │ │ └── browserconfig.xml │ ├── logo │ │ ├── dark.png │ │ ├── icon.png │ │ └── light.png │ ├── default │ │ └── user.gif │ ├── profile │ │ └── pic_userh.png │ ├── wallpapers │ │ ├── default.jpg │ │ └── original.jpg │ └── svg │ │ ├── views.svg │ │ └── comments.svg ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.svg ├── stylesheets │ └── style.css └── bootstrap │ └── bootstrap.min.js ├── run ├── .gitignore ├── README.md ├── views ├── category.jade ├── about.jade ├── email_confirmation.jade ├── password_reset_request.jade ├── email_confirmed.jade ├── error.jade ├── passwordchange_confirmation.jade ├── user │ ├── profile.jade │ └── upload.jade ├── index.jade ├── explore.jade ├── components │ ├── footer.jade │ ├── header.jade │ └── mixins.jade ├── categories.jade ├── password_change.jade ├── password_reset.jade ├── signin.jade ├── account │ ├── settingsv2.jade │ └── settings.jade ├── signup.jade ├── layout.jade └── details.jade ├── deplist.txt ├── run.bat ├── newrelic.js ├── scripts ├── rackspaceIO.js ├── flickrIO.js ├── auth.js ├── resources.js ├── data.js └── flickrUtils.js ├── package.json ├── LICENSE └── server.js /Procfile: -------------------------------------------------------------------------------- 1 | web: node app.js -------------------------------------------------------------------------------- /Procfile_dev: -------------------------------------------------------------------------------- 1 | web: nodemon app.js -------------------------------------------------------------------------------- /routes/email_confirmed.js: -------------------------------------------------------------------------------- 1 | var url = require('url'); -------------------------------------------------------------------------------- /public/images/favicon/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/favicon/64.png -------------------------------------------------------------------------------- /public/images/logo/dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/logo/dark.png -------------------------------------------------------------------------------- /public/images/logo/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/logo/icon.png -------------------------------------------------------------------------------- /public/images/logo/light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/logo/light.png -------------------------------------------------------------------------------- /public/images/default/user.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/default/user.gif -------------------------------------------------------------------------------- /public/images/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/favicon/favicon.ico -------------------------------------------------------------------------------- /public/images/profile/pic_userh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/profile/pic_userh.png -------------------------------------------------------------------------------- /public/images/wallpapers/default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/wallpapers/default.jpg -------------------------------------------------------------------------------- /public/images/favicon/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/favicon/mstile-70x70.png -------------------------------------------------------------------------------- /public/images/wallpapers/original.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/wallpapers/original.jpg -------------------------------------------------------------------------------- /public/images/favicon/favicon-160x160.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/favicon/favicon-160x160.png -------------------------------------------------------------------------------- /public/images/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /public/images/favicon/favicon-196x196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/favicon/favicon-196x196.png -------------------------------------------------------------------------------- /public/images/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /public/images/favicon/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/favicon/favicon-96x96.png -------------------------------------------------------------------------------- /public/images/favicon/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/favicon/mstile-144x144.png -------------------------------------------------------------------------------- /public/images/favicon/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/favicon/mstile-150x150.png -------------------------------------------------------------------------------- /public/images/favicon/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/favicon/mstile-310x150.png -------------------------------------------------------------------------------- /public/images/favicon/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/favicon/mstile-310x310.png -------------------------------------------------------------------------------- /public/images/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /run: -------------------------------------------------------------------------------- 1 | echo "Installing dependencies..." 2 | npm install 3 | echo "Running server..." 4 | foreman start -f Procfile_dev --env=app.env -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /public/images/favicon/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/favicon/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /public/images/favicon/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/favicon/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /public/images/favicon/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/favicon/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /public/images/favicon/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/favicon/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /public/images/favicon/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/favicon/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /public/images/favicon/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/favicon/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /public/images/favicon/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/favicon/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /public/images/favicon/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/favicon/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /routes/upload.js: -------------------------------------------------------------------------------- 1 | /* 2 | * GET upload page. 3 | */ 4 | 5 | exports.upload = function(req, res){ 6 | // Code to get post and upload to flickr 7 | }; -------------------------------------------------------------------------------- /public/images/favicon/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hr/pictroid/dev/public/images/favicon/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * GET home page. 4 | */ 5 | 6 | exports.index = function(req, res){ 7 | res.render('index', { title: 'Pictroid' }); 8 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | data/ 2 | .sass-cache/ 3 | node_modules/ 4 | private/ 5 | out/ 6 | *.env 7 | *.log 8 | *.codio 9 | .DS_Store 10 | ._.DS_Store 11 | .db 12 | thumbs.db 13 | _MACOSX 14 | __MACOSX 15 | *.exe 16 | *.dll 17 | *.conf 18 | *.dat 19 | *.sublime-workspace -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Pictroid 2 | ======== 3 | A space imagery sharing service for researchers, astronomists and everyone who is awed by the beauty of our universe. 4 | 5 | Made as part of the [NASA Space Apps Challenge](https://2014.spaceappschallenge.org/project/pictroid/). 6 | Designs and screens can be found at https://dribbble.com/HR/projects/389129-Pictroid and https://www.behance.net/gallery/19848395/Pictroid 7 | -------------------------------------------------------------------------------- /views/category.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block head 4 | title Categories | Pictroid 5 | 6 | extends layout 7 | - var title = field.replace(/-/g, ' ') 8 | block head 9 | title #{title} | Pictroid 10 | 11 | block variables 12 | - var headerClasses = 'navbar-absolute' 13 | 14 | block content 15 | +chickenLittle() 16 | .page 17 | br 18 | .container 19 | h3 20 | a(href="/explore/category/#{title}").active= title 21 | hr -------------------------------------------------------------------------------- /public/images/favicon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | #da532c 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /deplist.txt: -------------------------------------------------------------------------------- 1 | # 2 | # *************************************************************************** 3 | # 4 | # Note: This file has been deprecated and exists for backward compatibility. 5 | # Please use package.json to add dependencies to the Node modules 6 | # your application requires. 7 | # 8 | # *************************************************************************** 9 | # 10 | 11 | # 12 | # For a list of globally installed modules - see file: npm_global_module_list. 13 | # 14 | -------------------------------------------------------------------------------- /views/about.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block head 4 | title About | Pictroid 5 | 6 | block content 7 | .page 8 | .container 9 | h1 About Pictroid 10 | hr(style="height:1px;") 11 | p Pictroid is a app that enables users to tag images of NEOs (Near-Earth Objects) with astronomical data. It provides a chatting and sharing system, as well as aggregating the data into larger graphs of asteroid activity. 12 | p The source code for Pictroid can be found on 13 | a(href="https://github.com/codexa/pictroid") GitHub -------------------------------------------------------------------------------- /run.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | FOR /F %%A IN (app.env) DO set %%A 3 | if not exist data mkdir data 4 | start cmd /k mongod --dbpath "data" 5 | IF /I "%1"=="" GOTO DEFAULT 6 | IF /I "%1"=="-p" GOTO PROD 7 | IF /I "%1"=="-d" GOTO DEV 8 | @echo on 9 | :DEFAULT 10 | GOTO DEV 11 | :PROD 12 | echo "Production mode" 13 | SET NODE_ENV=production 14 | start cmd /k mongo ds037508.mongolab.com:37508/heroku_app23982462 -u %DbUser% -p %DbPass% 15 | nodemon server.js 16 | :DEV 17 | SET NODE_ENV=development 18 | start cmd /k mongo cache 19 | nodemon server.js -------------------------------------------------------------------------------- /views/email_confirmation.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block head 4 | title About | Pictroid 5 | 6 | block content 7 | .page 8 | .container 9 | h1 Confirm your email address 10 | hr(style="height:1px;") 11 | p A confirmation email has been sent to #{email}. Please click on the confirmation link in the email to activate your account. 12 | p 13 | | You'll be redirected to the homepage in 5 seconds ... 14 | script. 15 | setTimeout(function(){ 16 | window.location.replace("/"); 17 | }, 18 | 5000 19 | ); -------------------------------------------------------------------------------- /views/password_reset_request.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block head 4 | title Password reset request | Pictroid 5 | 6 | block content 7 | .page 8 | .container 9 | h1 Your password change request has been submitted for the #{email} associated account 10 | hr(style="height:1px;") 11 | p Please check your #{email} inbox for a reset email and follow the instructions 12 | script. 13 | setTimeout(function(){ 14 | window.location.replace("/"); 15 | }, 16 | 5000 17 | ); -------------------------------------------------------------------------------- /views/email_confirmed.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block head 4 | title Email confirmed | Pictroid 5 | 6 | block content 7 | .page 8 | .container 9 | h1 Thanks for confirming your email #{username} 10 | hr(style="height:1px;") 11 | p You will be now redirected to Sign In automatically... 12 | p If this doesn't work then please follow this Sign In Link 13 | script. 14 | setTimeout(function(){ 15 | window.location.replace("/signin"); 16 | }, 17 | 3000 18 | ); -------------------------------------------------------------------------------- /views/error.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block head 4 | title= error+" Error | Pictroid" 5 | 6 | block variables 7 | - var bodyClasses = "background-wallpaper" 8 | - var headerClasses = 'navbar-fixed-top navbar-transparent' 9 | 10 | block content 11 | .jumbotron.transparent.center 12 | h1(style="font-size: 100px;") 13 | | 4 14 | img(src="/images/logo/icon.png", alt="0", height="100", style="vertical-align: -15px; margin: 0 5px;") 15 | | 4 16 | if error == 404 17 | p The universe does not contain the page you were looking for. Please 18 | a(href="/") return to our home base 19 | | . -------------------------------------------------------------------------------- /views/passwordchange_confirmation.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block head 4 | title Email confirmed | Pictroid 5 | 6 | block content 7 | .page 8 | .container 9 | h1 Your password has been successfully changed #{username} 10 | hr(style="height:1px;") 11 | p To finish you have to Sign in again. You'll automatically be redirected to Sign In 12 | p If this doesn't work then please follow this Sign In Link 13 | script. 14 | setTimeout(function(){ 15 | window.location.replace("/signout?return_to=/signin"); 16 | }, 17 | 3000 18 | ); -------------------------------------------------------------------------------- /views/user/profile.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | 3 | block head 4 | title= profile_username+'\'s Profile | Pictroid' 5 | 6 | block content 7 | +chickenLittle() 8 | .page 9 | .container.profile-container 10 | .profile-info 11 | img.profile-info-picture(src="#{profile_image}", alt="profile_image") 12 | .profile-info-text 13 | .profile-name #{profile_username} 14 | p #{profile_status} 15 | p #{profile_picsCount} Pictroids 16 | p #{profile_picsTotalCount} Views 17 | //- img.profile-header-picture(src="/images/profile/pic_userh.png", alt="profile_image") 18 | .profile-pics 19 | h3 20 | a(href="#") Pics 21 | hr 22 | +feed() 23 | -------------------------------------------------------------------------------- /newrelic.js: -------------------------------------------------------------------------------- 1 | /** 2 | * New Relic agent configuration. 3 | * 4 | * See lib/config.defaults.js in the agent distribution for a more complete 5 | * description of configuration variables and their potential values. 6 | */ 7 | exports.config = { 8 | /** 9 | * Array of application names. 10 | */ 11 | app_name : ['Pictroid'], 12 | /** 13 | * Your New Relic license key. 14 | */ 15 | license_key : process.env.newRelicKey, 16 | logging : { 17 | /** 18 | * Level at which to log. 'trace' is most useful to New Relic when diagnosing 19 | * issues with the agent, 'info' and higher will impose the least overhead on 20 | * production applications. 21 | */ 22 | level : 'trace' 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /public/images/svg/views.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /views/index.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block head 4 | title Socially exploring the universe | Pictroid 5 | 6 | block variables 7 | - var bodyClasses = 'background-wallpaper' 8 | - var headerClasses = 'navbar-absolute navbar-transparent' 9 | 10 | block content 11 | .jumbotron.transparent.center 12 | .container 13 | img(src="/images/logo/icon.png", alt="Pictroid", height="200") 14 | h2 Socially exploring the universe 15 | if authed 16 | a.btn.clear(href="/explore") Explore 17 | a.btn.clear(href="/upload") Upload 18 | else 19 | a.btn.clear(href="/explore") Explore 20 | a.btn.clear(href="/signin") Sign In 21 | .page 22 | br 23 | .container 24 | h3 25 | a(href="/explore/popular") Popular 26 | hr 27 | +feed() 28 | -------------------------------------------------------------------------------- /routes/signup.js: -------------------------------------------------------------------------------- 1 | /** 2 | * POST 3 | */ 4 | var Parse = require('parse').Parse; 5 | Parse.initialize(process.env.parseID, process.env.parseJavascriptKey, process.env.parseMasterKey); 6 | 7 | var user = new Parse.User(); 8 | 9 | user.set("username", req.body.username); 10 | user.set("password", req.body.password); 11 | user.set("email", req.body.email); 12 | 13 | user.signUp(null, { 14 | success: function(user) { 15 | // Redirect to email confirmation page 16 | res.render('email_confirmation', { email: req.body.email}); 17 | }, 18 | error: function(user, error) { 19 | // Show the error message somewhere and let the user try again. 20 | console.log("Error: " + error.code + " " + error.message); 21 | if(error == 202) { 22 | window.document.getElementById('emsg').innerHTML=error.message; 23 | } 24 | } 25 | }); -------------------------------------------------------------------------------- /views/explore.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block head 4 | title Explore | Pictroid 5 | 6 | block variables 7 | - var headerClasses = 'navbar-absolute' 8 | 9 | block content 10 | +chickenLittle() 11 | .page 12 | br 13 | .container 14 | h3 15 | a(href="/explore/popular") Popular 16 | h3 17 | a(href="/explore/rated") Highest Rated 18 | h3 19 | a(href="/explore/latest") Latest 20 | hr 21 | +feed() 22 | script. 23 | window.onload = function() { 24 | var current = '#{filter}', 25 | a = $('h3 > a'); 26 | 27 | if(current === "popular") { 28 | a[0].className = "active"; 29 | a[1].className = a[2].className = ""; 30 | } else if(current === "rated") { 31 | a[1].className = "active"; 32 | a[0].className = a[2].className = ""; 33 | } else if(current === "latest") { 34 | a[2].className = "active"; 35 | a[0].className = a[1].className = ""; 36 | } 37 | }; -------------------------------------------------------------------------------- /scripts/rackspaceIO.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"); 2 | var blockStorage = require("pkgcloud").storage.createClient({ 3 | provider: "rackspace", 4 | username: process.env.rackspaceUsername, 5 | apiKey: process.env.rackspaceApiKey, 6 | region: "IAD" 7 | }); 8 | 9 | function findByRegExName(obj, regex) { 10 | var i = 0; 11 | while(!obj[i].name.match(regex)) { 12 | i++; 13 | } 14 | return obj[i] 15 | } 16 | 17 | exports.upload = function (file, name, callback) { 18 | blockStorage.getContainers(function (err, containers) { 19 | blockStorage.getCdnContainers(function (err, cdns) { 20 | var container = findByRegExName(containers, /^Images-\d{5}$/); 21 | var cdn = findByRegExName(cdns, container.name); 22 | console.log(cdn); 23 | file.pipe(blockStorage.upload({ 24 | container: container, 25 | remote: name 26 | }, function (err, result) { 27 | callback(err, result, cdn.cdnSslUri + "/" + name); 28 | })); 29 | }); 30 | }); 31 | } -------------------------------------------------------------------------------- /views/components/footer.jade: -------------------------------------------------------------------------------- 1 | footer 2 | .container 3 | .column-container 4 | .column.top 5 | img.footer-logo(src="/images/logo/light.png", alt="Pictroid") 6 | .column 7 | h2 About 8 | ul 9 | li 10 | a(href="/about") About us 11 | .column 12 | h2 Account 13 | ul 14 | if authed && user 15 | li 16 | a(href='/'+user.name) Profile 17 | li 18 | a(href='/'+user.name+'/settings') Settings 19 | li 20 | a(href="/signout") Sign Out 21 | li 22 | a(href="/upload") Upload 23 | else 24 | li 25 | a(href="/signin") Sign In 26 | li 27 | a(href="/signup") Sign Up 28 | .column 29 | h2 Explore 30 | ul 31 | li 32 | a(href="/explore/popular") Popular pics 33 | li 34 | a(href="/explore/rated") Highest rated 35 | li 36 | a(href="/explore/latest") Latest feed 37 | li 38 | a(href="/explore/categories") Categories 39 | div.text-muted 40 | | Pictroid is in α (Alpha) development stage. 41 | br 42 | | A Project by the   43 | a(href="https://codexa.org") Codexa Organization -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Pictroid", 3 | "description": "The open-source NEO (Near Earth Objects) sharing system.", 4 | "version": "0.0.1", 5 | "private": true 6 | , "main": "server.js", 7 | "scripts": { 8 | "start": "node server.js", 9 | "dev":"nodemon app.js", 10 | "test": "make test" 11 | }, 12 | "engines": { 13 | "node": ">= 0.6.0", 14 | "npm": ">= 1.0.0" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/codexa/pictroid" 19 | }, 20 | "dependencies": { 21 | "base64-js": "*", 22 | "bower": "*", 23 | "express": "3.5.1", 24 | "flickrapi": "~0.3.14", 25 | "grunt-cli": "*", 26 | "helmet": "*", 27 | "jade": "*", 28 | "moment": "^2.6.0", 29 | "connect-mongo": "*", 30 | "mongodb": "*", 31 | "mongoose": "~3.8.8", 32 | "monk": "*", 33 | "multiparty": "*", 34 | "newrelic": "*", 35 | "node-uuid": "*", 36 | "nodemon": "*", 37 | "parse": "*", 38 | "redis": "*", 39 | "request": "*", 40 | "form-data": "~0.1.2", 41 | "sass": "*", 42 | "pkgcloud": "~0.9.4", 43 | "universal-analytics": "*", 44 | "mongojs": "*" 45 | }, 46 | "devDependencies": { 47 | "mocha": "0.3.x" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Codexa and its individual contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | Please note that Codexa has full rights to change the licence of Pictroid at anytime and by using the product you're automatically subject to comply with the changes. 24 | -------------------------------------------------------------------------------- /views/categories.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block head 4 | title Categories | Pictroid 5 | 6 | block variables 7 | - var headerClasses = 'navbar-absolute' 8 | 9 | block content 10 | +chickenLittle() 11 | .page 12 | br 13 | .container 14 | h3 15 | a(href="/explore/categories").active Categories 16 | hr 17 | .col-md-5 18 | h4 19 | a(href="/explore/popular") Popular 20 | h4 21 | a(href="/explore/rated") Highest Rated 22 | h4 23 | a(href="/explore/latest") Latest 24 | h4 25 | a(href="/explore/category/Solar-System") Solar System 26 | h4 27 | a(href="/explore/category/Exoplanets") Exoplanets 28 | .col-md-5 29 | h4 30 | a(href="/explore/category/Stars") Stars 31 | h4 32 | a(href="/explore/category/Star-Clusters") Star Clusters 33 | h4 34 | a(href="/explore/category/Nebulae") Nebulae 35 | h4 36 | a(href="/explore/category/Galaxies") Galaxies 37 | h4 38 | a(href="/explore/category/Quasars") Black Holes and Quasars 39 | hr 40 | +feed() 41 | script. 42 | window.onload = function() { 43 | var current = '#{filter}', 44 | a = $('h3 > a'); 45 | 46 | if(current === "popular") { 47 | a[0].className = "active"; 48 | a[1].className = a[2].className = ""; 49 | } else if(current === "rated") { 50 | a[1].className = "active"; 51 | a[0].className = a[2].className = ""; 52 | } else if(current === "latest") { 53 | a[2].className = "active"; 54 | a[0].className = a[1].className = ""; 55 | } 56 | }; -------------------------------------------------------------------------------- /views/components/header.jade: -------------------------------------------------------------------------------- 1 | div.navbar.navbar-default(role='navigation', class=headerClasses) 2 | div.container 3 | div.navbar-header 4 | button.navbar-toggle(type="button", data-toggle="collapse", data-target=".navbar-collapse") 5 | span.sr-only Toggle navigation 6 | span.icon-bar 7 | span.icon-bar 8 | span.icon-bar 9 | a.navbar-brand(href="/") 10 | div.navbar-collapse.collapse 11 | ul.nav.navbar-nav.navbar-right 12 | li 13 | a(href="/") Home 14 | li 15 | a.dropdown-toggle(href="#", data-toggle="dropdown") Explore 16 | b.caret 17 | ul.dropdown-menu 18 | li 19 | a(href="/explore/popular") Popular 20 | li 21 | a(href="/explore/rated") Highest Rated 22 | li 23 | a(href="/explore/latest") Latest feed 24 | li 25 | a(href="/explore/categories") Categories 26 | if authed 27 | li 28 | a(href="/upload") Upload 29 | li 30 | a(href="/about") About 31 | if authed && username 32 | li 33 | != '' 34 | != username 35 | != '' 36 | ul.dropdown-menu 37 | li 38 | a(href='/user/'+username) Profile 39 | li 40 | a(href='/account/settings') Settings 41 | li 42 | if (route) 43 | a(href='/signout?return_to=#{route}') Sign Out 44 | else 45 | a(href='/signout') Sign Out 46 | else 47 | li 48 | if (route) 49 | a.btn.clear(href="/signin?return_to=#{route}") Sign In 50 | else 51 | a.btn.clear(href="/signin") Sign In 52 | .navbar-fix -------------------------------------------------------------------------------- /views/components/mixins.jade: -------------------------------------------------------------------------------- 1 | mixin chickenLittle 2 | .banner.background-wallpaper 3 | .container 4 | | The sky is falling, but you can help! Post a pic to help us track Near-Earth Objects and save the chickens from destruction.   5 | if authed 6 | a.btn.clear(href="/upload") Upload 7 | else 8 | a.btn.clear(href="/signin") Sign In 9 | |   or 10 | a.btn.clear(href="/signup") Sign Up 11 | 12 | mixin feed() 13 | .feed 14 | each val in results 15 | .feed-item 16 | a(href= "/pic/"+val.image.id) 17 | img(src=val.src.get("src"), alt='') 18 | .stats 19 | ul(class="details") 20 | li(class="user") 21 | a(href="/user/"+val.username)= val.username 22 | li(class="views") 23 | if(val.views) 24 | #{val.views} 25 | else 26 | | 0 27 | li(class="comments") 28 | a(href="/pic/"+val.image.id+"#disqus_thread", data-disqus-identifier=val.image.id) 0 29 | .text 30 | h4=val.image.get("name") 31 | p=val.image.get("description").substr(0, 150) 32 | a(href= "/pic/"+val.image.id)="..." 33 | script. 34 | /* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */ 35 | var disqus_shortname = 'codexa'; // required: replace example with your forum shortname 36 | 37 | /* * * DON'T EDIT BELOW THIS LINE * * */ 38 | (function () { 39 | var s = document.createElement('script'); s.async = true; 40 | s.type = 'text/javascript'; 41 | s.src = '//' + disqus_shortname + '.disqus.com/count.js'; 42 | (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); 43 | }()); -------------------------------------------------------------------------------- /views/password_change.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block head 4 | title Password reset | Pictroid 5 | 6 | block variables 7 | - var bodyClasses = "background-wallpaper" 8 | - var headerClasses = 'navbar-fixed-top navbar-transparent' 9 | 10 | block content 11 | form.form-signin.form-transparent(role="form", action="/password_reset", method="POST", onsubmit="return(validate());") 12 | img(src="/images/logo/icon.png", alt="Pictroid", height="75") 13 | hr 14 | input.form-control(type="hidden", name="_csrf", value="#{csrftoken}") 15 | input.form-control(type="password", name="password", placeholder="Password") 16 | if error 17 | style. 18 | .emsg { 19 | display: block !important; 20 | } 21 | div.emsg 22 | h5 Errors: 23 | ul.emul 24 | li #{error} 25 | button.btn.btn-lg.btn-primary.btn-block(type="submit") Reset password 26 | script. 27 | console.log(document.referrer); 28 | console.log(document.referrer.substr(document.referrer.indexOf("com")+3, document.referrer.length)); 29 | //- if(document.referrer.substr((document.referrer.indexOf("com")+3), document.referrer.length) !== "/signin") { 30 | //- window.location.replace("/signin"); 31 | //- } 32 | function validate(){ 33 | var valid = true, 34 | ermsg = "", 35 | form = window.document.forms[0], 36 | emsg = window.document.querySelectorAll(".emsg")[0], 37 | emul = window.document.querySelectorAll(".emul")[0]; 38 | if(!(/^[a-z0-9_-]{6,14}$/ig).test(form.password.value)){ 39 | ermsg = errorLi("Invalid password, needs to be 6-14 alphanumeric and - _ characters"); 40 | valid = false; 41 | } 42 | if (valid) { 43 | emsg.style.display = "none"; 44 | return valid; 45 | } else { 46 | emul.innerHTML = ermsg; 47 | emsg.style.display = "block"; 48 | return valid; 49 | } 50 | } 51 | function errorLi(text) { 52 | return ('
  • '+text+'
  • '); 53 | } -------------------------------------------------------------------------------- /views/password_reset.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block head 4 | title Password reset | Pictroid 5 | 6 | block variables 7 | - var bodyClasses = "background-wallpaper" 8 | - var headerClasses = 'navbar-fixed-top navbar-transparent' 9 | 10 | block content 11 | form.form-signin.form-transparent(role="form", action="/password_reset", method="POST", onsubmit="return(validate());") 12 | img(src="/images/logo/icon.png", alt="Pictroid", height="75") 13 | hr 14 | input.form-control(type="hidden", name="_csrf", value="#{csrftoken}") 15 | input.form-control(type="email", name="email", placeholder="Email") 16 | if error 17 | style. 18 | .emsg { 19 | display: block !important; 20 | } 21 | div.emsg 22 | h5 Errors: 23 | ul.emul 24 | li #{error} 25 | button.btn.btn-lg.btn-primary.btn-block(type="submit") Reset password 26 | script. 27 | console.log(document.referrer); 28 | console.log(document.referrer.substr(document.referrer.indexOf("com")+3, document.referrer.length)); 29 | //- if(document.referrer.substr((document.referrer.indexOf("com")+3), document.referrer.length) !== "/signin") { 30 | //- window.location.replace("/signin"); 31 | //- } 32 | function validate(){ 33 | var valid = true, 34 | ermsg = "", 35 | form = window.document.forms[0], 36 | emsg = window.document.querySelectorAll(".emsg")[0], 37 | emul = window.document.querySelectorAll(".emul")[0]; 38 | if(!(/^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/g).test(form.email.value)) { 39 | ermsg = errorLi("Invalid email, enter a valid email like john.n-doe@gmail.com"); 40 | valid = false; 41 | } 42 | if (valid) { 43 | emsg.style.display = "none"; 44 | return valid; 45 | } else { 46 | emul.innerHTML = ermsg; 47 | emsg.style.display = "block"; 48 | return valid; 49 | } 50 | } 51 | function errorLi(text) { 52 | return ('
  • '+text+'
  • '); 53 | } -------------------------------------------------------------------------------- /public/images/svg/comments.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 22 | 24 | image/svg+xml 25 | 27 | 28 | 29 | 30 | 32 | 52 | 54 | 59 | 60 | -------------------------------------------------------------------------------- /views/signin.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block head 4 | title Sign In | Pictroid 5 | 6 | block variables 7 | - var bodyClasses = "background-wallpaper" 8 | - var headerClasses = 'navbar-fixed-top navbar-transparent' 9 | 10 | block content 11 | form.form-signin.form-transparent(role="form", action="/signin", method="POST", onsubmit="return(validate());") 12 | img(src="/images/logo/icon.png", alt="Pictroid", height="75") 13 | hr 14 | input.form-control(type="hidden", name="_csrf", value="#{csrftoken}") 15 | input.form-control(type="hidden", name="return_to", value="#{route}") 16 | input.form-control(type="text", name="username", placeholder="Username") 17 | input.form-control(type="password", name="password", placeholder="Password") 18 | if error 19 | style. 20 | .emsg { 21 | display: block !important; 22 | } 23 | div.emsg 24 | h5 Errors: 25 | ul.emul 26 | li #{error} 27 | button.btn.btn-lg.btn-primary.btn-block(type="submit") Sign In 28 | br 29 | p(style="text-align: center;") 30 | | Forgotten your password? Then Reset Password 31 | p(style="text-align: center;") 32 | | Don't have an account? Then Sign Up 33 | script. 34 | function validate(){ 35 | var valid = true, 36 | ermsg = "", 37 | form = window.document.forms[0], 38 | emsg = window.document.querySelectorAll(".emsg")[0], 39 | emul = window.document.querySelectorAll(".emul")[0], 40 | qstring = getParameterByName('return_to'); 41 | alert("qs: "+qstring); 42 | if(!(/^[a-z0-9_-]{6,10}$/i).test(form.username.value)){ 43 | ermsg += errorLi("Invalid username, needs to be 6-14 alphanumeric and - _ characters"); 44 | valid = false; 45 | } 46 | if(!(/^[a-z0-9_-]{6,14}$/ig).test(form.password.value)){ 47 | ermsg += errorLi("Invalid password, needs to be 6-14 alphanumeric and - _ characters"); 48 | valid = false; 49 | } 50 | if (valid) { 51 | emsg.style.display = "none"; 52 | return valid; 53 | // $.post( "/signin", { 'return_to': qstring } ); 54 | } else { 55 | emul.innerHTML = ermsg; 56 | emsg.style.display = "block"; 57 | return valid; 58 | } 59 | } 60 | function errorLi(text) { 61 | return ('
  • '+text+'
  • '); 62 | } -------------------------------------------------------------------------------- /views/account/settingsv2.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | 3 | block head 4 | title Settings | Pictroid 5 | 6 | block variables 7 | - var bodyClasses = "background-wallpaper" 8 | - var headerClasses = 'navbar-fixed-top navbar-transparent' 9 | 10 | block content 11 | form.form-settings.form-transparent(role="form", enctype="multipart/form-data", action="/account/settings", method="POST", onsubmit="return(validate());") 12 | img(src="/images/logo/icon.png", alt="Pictroid", height="75") 13 | hr 14 | .settings-combo 15 | img.settings-picture(src="", alt="") 16 | .settings-sidebar 17 | p.form-control(id="username") #{username} 18 | p.form-control(id="status") #{status} 19 | input.form-control(type="email", name="email", placeholder='#{email}') 20 | hr 21 | input.form-control(type="password", name="password", placeholder="New Password") 22 | input.form-control(type="password", name="passwordconfirm", placeholder="Confirm Password") 23 | p(id="emsg" style="color:#DD7674;display:none;") 24 | if (error) 25 | p(class="emsg") #{error} 26 | button.btn.btn-lg.btn-block(type="submit") Update 27 | script. 28 | function validate(){ 29 | var valid = true, 30 | form = window.document.forms[0], 31 | ermsg, 32 | emsg = window.document.querySelectorAll(".emsg")[0]; 33 | if(form.email.value || form.password.value){ 34 | if(form.email.value) { 35 | if(!(/^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/g).test(form.email.value)){ 36 | ermsg = "Invalid email, a valid email Go-hub.be@email.com"; 37 | valid = false; 38 | } 39 | } 40 | if(form.password.value) { 41 | if(form.password.value !== form.passwordconfirm.value){ 42 | ermsg = "Passwords do not match"; 43 | valid = false; 44 | } 45 | if(!(/^[a-z0-9_-]{6,14}$/g).test(form.password.value)) { 46 | ermsg = "Invalid password, needs to be 6-14 alphanumeric and - _ characters"; 47 | valid = false; 48 | } 49 | } 50 | //- console.log('flag: '+flag); 51 | //- console.log(Boolean(valid && (form.email.value || flag))); 52 | //- return false; 53 | if(valid){ 54 | emsg.style.display = "none"; 55 | return valid; 56 | } else { 57 | emsg.innerHTML = ermsg; 58 | emsg.style.display = "block"; 59 | return valid; 60 | } 61 | } else { 62 | return false; 63 | } 64 | } -------------------------------------------------------------------------------- /views/signup.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block head 4 | title Sign Up | Pictroid 5 | 6 | block variables 7 | - var bodyClasses = "background-wallpaper" 8 | - var headerClasses = 'navbar-fixed-top navbar-transparent' 9 | 10 | 11 | block content 12 | form.form-signin.form-transparent(role="form", action="/signup", method="POST", onsubmit="return(validate());") 13 | img(src="/images/logo/icon.png", alt="Pictroid", height="75") 14 | hr 15 | input.form-control(type="hidden", name="_csrf", value="#{csrftoken}") 16 | input.form-control(type="text", name="username", placeholder="Username") 17 | input.form-control(type="email", name="email", placeholder="Email") 18 | input.form-control(type="password", name="password", placeholder="Password") 19 | input.form-control(type="password", name="passwordconfirm", placeholder="Confirm password") 20 | if error 21 | style. 22 | .emsg { 23 | display: block !important; 24 | } 25 | div.emsg 26 | h5 Errors: 27 | ul.emul 28 | li #{error} 29 | button.btn.btn-lg.btn-primary.btn-block(type="submit") Sign Up 30 | br 31 | p(style="text-align: center;") 32 | | Already have an account? Then Sign In 33 | script. 34 | function validate(){ 35 | var valid = true, 36 | form = window.document.forms[0], 37 | ermsg = "", 38 | emsg = window.document.querySelectorAll(".emsg")[0], 39 | emul = window.document.querySelectorAll(".emul")[0]; 40 | if(!form.username.value || !(/^[a-z0-9_-]{6,14}$/gi).test(form.username.value)){ 41 | ermsg += errorLi("Invalid username, needs to be 6-14 alphanumeric and - _ characters"); 42 | valid = false; 43 | } 44 | if(!form.email.value || !(/^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/gi).test(form.email.value)) { 45 | ermsg += errorLi("Invalid email, enter a valid email like john.n-doe@gmail.com"); 46 | valid = false; 47 | } 48 | if(!form.password.value || !(/^[a-z0-9_-]{6,14}$/gi).test(form.password.value)){ 49 | ermsg += errorLi("Invalid password, needs to be 6-14 alphanumeric and - _ characters."); 50 | valid = false; 51 | } 52 | if(!form.passwordconfirm.value || !(form.password.value === form.passwordconfirm.value)) { 53 | ermsg += errorLi("Passwords do not match."); 54 | valid = false; 55 | } 56 | if (valid) { 57 | emsg.style.display = "none"; 58 | return valid; 59 | } else { 60 | emul.innerHTML = ermsg; 61 | emsg.style.display = "block"; 62 | return valid; 63 | } 64 | } 65 | function errorLi(text) { 66 | return ('
  • '+text+'
  • '); 67 | } -------------------------------------------------------------------------------- /scripts/flickrIO.js: -------------------------------------------------------------------------------- 1 | var Flickr = require("flickrapi"); 2 | var http = require("http"); 3 | var https = require("https"); 4 | var FormData = require("form-data"); 5 | var db = require("./data"); 6 | var flickrUtils = require("./flickrUtils"); 7 | var request = require("request"); 8 | 9 | exports.quickTest = function() { 10 | Flickr.authenticate({ 11 | api_key: process.env.flickrKey, 12 | secret: process.env.flickrSecret, 13 | user_id: process.env.FLICKR_USER_ID, 14 | access_token: process.env.FLICKR_ACCESS_TOKEN, 15 | access_token_secret: process.env.FLICKR_ACCESS_TOKEN_SECRET 16 | }, function(err, flickr) { 17 | exports.uploadFromDb("bh17LSc6Ai", flickr); 18 | }); 19 | } 20 | exports.uploadFromDb = function(id, flickr) { 21 | return db.asteroids.query.getPic(id).then(function (result){ 22 | flickr.options = flickrUtils.setAuthVals(flickr.options); 23 | var params = { 24 | title: result.image.id, 25 | description: result.image.get("description"), 26 | is_public: 0, 27 | api_key: flickr.options.api_key, 28 | user_id: flickr.options.user_id, 29 | oauth_consumer_key: flickr.options.api_key, 30 | oauth_nonce: flickr.options.oauth_nonce, 31 | oauth_timestamp: flickr.options.oauth_timestamp, 32 | oauth_signature_method: "HMAC-SHA1", 33 | oauth_token: flickr.options.access_token 34 | }; 35 | var queryString = flickrUtils.formQueryString(params); 36 | var data = flickrUtils.formBaseString("https://up.flickr.com/services/upload", queryString).replace("GET", "POST"); 37 | var signature = flickrUtils.sign(data, flickr.options.secret, flickr.options.access_token_secret); 38 | params.oauth_signature = decodeURIComponent(signature); 39 | 40 | var data = new FormData(); 41 | Object.keys(params).sort().forEach(function (key) { 42 | data.append(key, params[key]); 43 | }); 44 | data.append("photo", request(result.src.get("src")), {filename: "file.jpg",contentType: "image/jpeg"}); 45 | 46 | var req = data.submit("https://up.flickr.com/services/upload", function(error, res) { 47 | var output = ''; 48 | res.setEncoding('utf8'); 49 | 50 | res.on('data', function (chunk) { 51 | output += chunk; 52 | }); 53 | 54 | res.on('end', function() { 55 | //var obj = JSON.parse(output); 56 | if(res.statusCode === 200) { 57 | /*exports.updateKimono(obj).then(function() { 58 | console.log("success:"); 59 | console.log(arguments); 60 | }, function() { 61 | console.log("error:"); 62 | console.log(arguments); 63 | });*/ 64 | } 65 | console.log(output) 66 | }); 67 | }); 68 | }); 69 | } -------------------------------------------------------------------------------- /views/account/settings.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | 3 | block head 4 | title Settings | Pictroid 5 | 6 | block variables 7 | - var bodyClasses = "background-wallpaper" 8 | - var headerClasses = 'navbar-fixed-top navbar-transparent' 9 | 10 | block content 11 | form.form-settings.form-transparent(role="form", action="/account/settings", method="POST", enctype="multipart/form-data", onsubmit="return(validate());") 12 | img(src="/images/logo/icon.png", alt="Pictroid", height="75") 13 | hr 14 | .settings-combo 15 | img.settings-picture(src="#{profileImgSrc}", alt="#{username}_profile_image") 16 | input.form-file(type="file", id="profilePhotoFileUpload", name="profileImgFile", accept="image/*") 17 | .settings-sidebar 18 | p.form-control(id="username") #{username} 19 | p.form-control(class="formp") #{status} 20 | p.form-control(class="formp") #{email} 21 | hr 22 | input.form-control(type="password", name="password", placeholder="New Password") 23 | input.form-control(type="password", name="passwordconfirm", placeholder="Confirm Password") 24 | if error 25 | style. 26 | .emsg { 27 | display: block !important; 28 | } 29 | div.emsg 30 | h5 Errors: 31 | ul.emul 32 | li #{error} 33 | button.btn.btn-lg.btn-block(type="submit") Update 34 | script. 35 | function validate(){ 36 | var valid = true, 37 | ermsg = "", 38 | form = window.document.forms[0], 39 | emsg = window.document.querySelectorAll(".emsg")[0], 40 | emul = window.document.querySelectorAll(".emul")[0]; 41 | if((!form.profileImgFile || !form.profileImgFile.files[0]) && 42 | !form.password.value) { 43 | ermsg = errorLi("Nothing to change..."); 44 | valid = false; 45 | } 46 | if(form.profileImgFile && form.profileImgFile.files[0] && form.profileImgFile.files[0].size > 30000){ 47 | ermsg += errorLi("File must be below 30kb in size."); 48 | valid = false; 49 | } 50 | if(form.password.value && !(/^[a-z0-9_-]{6,14}$/gi).test(form.password.value)){ 51 | ermsg += errorLi("Invalid password, needs to be 6-14 alphanumeric and - _ characters."); 52 | valid = false; 53 | } 54 | if(form.password.value && !(form.password.value === form.passwordconfirm.value)) { 55 | ermsg += errorLi("Passwords do not match."); 56 | valid = false; 57 | } 58 | if (valid) { 59 | emsg.style.display = "none"; 60 | return valid; 61 | } else { 62 | emul.innerHTML = ermsg; 63 | emsg.style.display = "block"; 64 | return valid; 65 | } 66 | } 67 | function errorLi(text) { 68 | return ('
  • '+text+'
  • '); 69 | } -------------------------------------------------------------------------------- /views/layout.jade: -------------------------------------------------------------------------------- 1 | block variables 2 | - var bodyStyle = "" 3 | - var headerClasses = "" 4 | - var user = { } 5 | include components/mixins 6 | doctype html 7 | html 8 | head 9 | block head 10 | title= title 11 | 12 | // Meta 13 | meta(charset='utf-8') 14 | meta(name='viewport', content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no') 15 | if secure 16 | meta(http-equiv='cache-control', content='no-cache') 17 | meta(http-equiv='expires', content='0') 18 | meta(http-equiv='pragma', content='no-cache') 19 | // Favicon 20 | link(rel="icon", type="image/png", href="/images/favicon/64.png") 21 | 22 | // Stylesheets 23 | link(rel='stylesheet', href='//fonts.googleapis.com/css?family=Open+Sans:400,300') 24 | link(rel='stylesheet', href='/bootstrap/bootstrap.min.css') 25 | link(rel='stylesheet', href='/stylesheets/style.css') 26 | link(rel='/images/favicon/apple-touch-icon', sizes='57x57', href='/images/favicon/apple-touch-icon-57x57.png') 27 | link(rel='apple-touch-icon', sizes='114x114', href='/images/favicon/apple-touch-icon-114x114.png') 28 | link(rel='apple-touch-icon', sizes='72x72', href='/images/favicon/apple-touch-icon-72x72.png') 29 | link(rel='apple-touch-icon', sizes='144x144', href='/images/favicon/apple-touch-icon-144x144.png') 30 | link(rel='apple-touch-icon', sizes='60x60', href='/images/favicon/apple-touch-icon-60x60.png') 31 | link(rel='apple-touch-icon', sizes='120x120', href='/images/favicon/apple-touch-icon-120x120.png') 32 | link(rel='apple-touch-icon', sizes='76x76', href='/images/favicon/apple-touch-icon-76x76.png') 33 | link(rel='apple-touch-icon', sizes='152x152', href='/images/favicon/apple-touch-icon-152x152.png') 34 | link(rel='icon', type='image/png', href='/images/favicon/favicon-196x196.png', sizes='196x196') 35 | link(rel='icon', type='image/png', href='/images/favicon/favicon-160x160.png', sizes='160x160') 36 | link(rel='icon', type='image/png', href='/images/favicon/favicon-96x96.png', sizes='96x96') 37 | link(rel='icon', type='image/png', href='/images/favicon/favicon-16x16.png', sizes='16x16') 38 | link(rel='icon', type='image/png', href='/images/favicon/favicon-32x32.png', sizes='32x32') 39 | meta(name='Pictroid', content='#eee') 40 | meta(name='Pictroid', content='/images/favicon/mstile-144x144.png') 41 | body 42 | div(id="wrapper", class=bodyClasses) 43 | include components/header 44 | block content 45 | include components/footer 46 | 47 | // Bootstrap core JavaScript 48 | // Placed at the end of the document so the pages load faster 49 | script(src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js?n=1") 50 | script(src="/bootstrap/bootstrap.min.js?n=1") 51 | script(src="//www.parsecdn.com/js/parse-1.2.18.min.js?n=1") -------------------------------------------------------------------------------- /scripts/auth.js: -------------------------------------------------------------------------------- 1 | // Sign up 2 | exports.signup = function (username, password, email, res) { 3 | var Parse = require('parse').Parse; 4 | Parse.initialize(process.env.parseID, process.env.parseJavascriptKey, process.env.parseMasterKey); 5 | 6 | var user = new Parse.User(); 7 | 8 | user.set("username", username); 9 | user.set("password", password); 10 | user.set("email", email); 11 | 12 | user.signUp(null, { 13 | success: function(user) { 14 | // Redirect to profile page 15 | res.setHeader('Location', '/user/'+user.username); 16 | }, 17 | error: function(user, error) { 18 | // Show the error message somewhere and let the user try again. 19 | console.log("Error: " + error.code + " " + error.message); 20 | } 21 | }); 22 | }; 23 | 24 | exports.signin = function (username, password, res) { 25 | var Parse = require('parse').Parse; 26 | Parse.initialize(process.env.parseID, process.env.parseJavascriptKey, process.env.parseMasterKey); 27 | Parse.User.logIn(username, password, { 28 | success: function(user) { 29 | // Do stuff after successful login. 30 | res.setHeader('Location', '/'); 31 | }, 32 | error: function(user, error) { 33 | // The login failed. Check error to see why. 34 | console.log("Error: " + error.code + " " + error.message); 35 | } 36 | }); 37 | } 38 | 39 | module.exports = function csrf(options) { 40 | options = options || {}; 41 | var value = options.value || defaultValue; 42 | 43 | return function(req, res, next){ 44 | 45 | // already have one 46 | var secret = req.session._csrfSecret; 47 | if (secret) return createToken(secret); 48 | 49 | // generate secret 50 | uid(24, function(err, secret){ 51 | if (err) return next(err); 52 | req.session._csrfSecret = secret; 53 | createToken(secret); 54 | }); 55 | 56 | // generate the token 57 | function createToken(secret) { 58 | var token; 59 | 60 | // lazy-load token 61 | req.csrfToken = function csrfToken() { 62 | return token || (token = saltedToken(secret)); 63 | }; 64 | 65 | // compatibility with old middleware 66 | Object.defineProperty(req.session, '_csrf', { 67 | configurable: true, 68 | get: function() { 69 | console.warn('req.session._csrf is deprecated, use req.csrfToken() instead'); 70 | return req.csrfToken(); 71 | } 72 | }); 73 | 74 | // ignore these methods 75 | if ('GET' == req.method || 'HEAD' == req.method || 'OPTIONS' == req.method) return next(); 76 | 77 | // determine user-submitted value 78 | var val = value(req); 79 | 80 | // check 81 | if (!checkToken(val, secret)) return next(utils.error(403)); 82 | 83 | next(); 84 | } 85 | } 86 | }; -------------------------------------------------------------------------------- /views/user/upload.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | 3 | block head 4 | title Upload | Pictroid 5 | 6 | block variables 7 | - var bodyClasses = "background-wallpaper" 8 | - var headerClasses = 'navbar-fixed-top navbar-transparent' 9 | - var charCount = 1000 10 | 11 | block content 12 | form.form-transparent(role="form", action="/upload", enctype="multipart/form-data", method="POST", onsubmit="return(validate());") 13 | img(src="/images/logo/icon.png", alt="Pictroid", height="75") 14 | hr 15 | #dropbox 16 | span.message 17 | | Drop images here to upload. 18 | br 19 | i (they will only be visible to you) 20 | p(id="or") 21 | | OR 22 | input.form-control(type="file", id="image", name="image") 23 | hr 24 | input.form-control(type="text", id="title", name="title", placeholder="Title") 25 | textarea.form-control(type="text", id="description", name="description", placeholder="Description" row="20") 26 | p(style="text-align:right; padding: 0px;") 27 | | You have 1000 characters left 28 | input.form-control(type="text", name="tags", placeholder="#Tags") 29 | if error 30 | style. 31 | .emsg { 32 | display: block !important; 33 | } 34 | div.emsg 35 | h5 Errors: 36 | ul.emul 37 | li #{error} 38 | button.btn.btn-lg.btn-primary.btn-block(type="submit") Upload 39 | script. 40 | window.addEventListener('DOMContentLoaded', function() { descriptionCount(); }); 41 | document.getElementById("description").addEventListener('input', function() { descriptionCount(); }); 42 | function descriptionCount() { 43 | document.getElementById("charCount").textContent = (1000 - document.getElementById("description").value.length); 44 | } 45 | function validate(){ 46 | var valid = true, 47 | form = window.document.forms[0], 48 | ermsg = "", 49 | emsg = window.document.querySelectorAll(".emsg")[0], 50 | emul = window.document.querySelectorAll(".emul")[0], 51 | fileName = $(form.image).val(), 52 | imgSize = (imgSize) ? readImage($("#image")[0].files[0]) : false; 53 | switch(fileName.length-3) { 54 | case fileName.lastIndexOf("gif"): 55 | case fileName.lastIndexOf("png"): 56 | case fileName.lastIndexOf("jpeg"): 57 | case fileName.lastIndexOf("jpg"): 58 | break; 59 | default: 60 | ermsg += errorLi("We do not support this file type. Please upload a file with the extension .gif, .png, .jpeg, or .jpg"); 61 | valid = false; 62 | break; 63 | } 64 | if(!(/^.{0,1000}$/g).test(form.description.value)){ 65 | ermsg += errorLi("Please enter a description that is less than 1000 characters long."); 66 | valid = false; 67 | } 68 | if (valid) { 69 | emsg.style.display = "none"; 70 | return valid; 71 | } else { 72 | emul.innerHTML = ermsg; 73 | emsg.style.display = "block"; 74 | return valid; 75 | } 76 | } 77 | function errorLi(text) { 78 | return ('
  • '+text+'
  • '); 79 | } -------------------------------------------------------------------------------- /views/details.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block head 4 | title= picObject.image.get("name")+' | Pictroid' 5 | 6 | block variables 7 | - var headerClasses = 'navbar-absolute' 8 | 9 | block content 10 | +chickenLittle() 11 | .page 12 | .container.image-page 13 | if m 14 | div.notice 15 | h1= m.title 16 | = m.message 17 | h1.image-title= picObject.image.get("name") 18 | hr 19 | .image-container 20 | img.image-image(src=picObject.src.get("src"), alt=picObject.image.get("name")) 21 | .image-share 22 | .a2a_kit.a2a_default_style.a2a_kit_size_32 23 | a.a2a_dd(href="http://www.addtoany.com/share_save") 24 | a.a2a_button_facebook 25 | a.a2a_button_twitter 26 | a.a2a_button_google_plus 27 | a.a2a_button_email 28 | a.a2a_button_wordpress 29 | script(type="text/javascript", src="//static.addtoany.com/menu/page.js") 30 | .image-stats 31 | .image-by 32 | i by   33 | a(href="/user/"+picObject.username)= picObject.username 34 | .image-tags 35 | .tag #foo 36 | .tag #bar 37 | .image-social 38 | ul(class="details") 39 | li(class="views") 40 | if views 41 | span= views 42 | else 43 | | 0 44 | li(class="comments") 45 | a(href="/pic/"+picObject.image.id+"#disqus_thread", data-disqus-identifier=picObject.image.id) 0 46 | .image-details 47 | .image-description 48 | p= picObject.image.get("description") 49 | // 50 | .image-comments 51 | h2 Comment on this pic 52 | if !authed 53 | p.image-comments-notice Please Sign In or 54 | Sign Up to comment. 55 | .comment 56 | a.comment-user(href="#") Foo User 57 | .comment-text Bar baz qux 58 | .comment 59 | a.comment-user(href="#") Foo User 60 | .comment-text Bar baz qux 61 | .comment 62 | a.comment-user(href="#") Foo User 63 | .comment-text Bar baz qux 64 | .comment 65 | a.comment-user(href="#") Foo User 66 | .comment-text Bar baz qux 67 | .comment 68 | a.comment-user(href="#") Foo User 69 | .comment-text Bar baz qux 70 | != '' 71 | #disqus_thread 72 | script. 73 | /* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */ 74 | var disqus_shortname = 'codexa'; // required: replace example with your forum shortname 75 | var disqus_identifier = thisID; 76 | 77 | /* * * DON'T EDIT BELOW THIS LINE * * */ 78 | (function() { 79 | var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; 80 | dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; 81 | (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); 82 | })(); 83 | (function () { 84 | var s = document.createElement('script'); s.async = true; 85 | s.type = 'text/javascript'; 86 | s.src = '//' + disqus_shortname + '.disqus.com/count.js'; 87 | (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); 88 | }()); 89 | noscript 90 | | Please enable JavaScript to view the 91 | a(href='http://disqus.com/?ref_noscript') comments powered by Disqus. 92 | a.dsq-brlink(href='http://disqus.com') 93 | | comments powered by 94 | span.logo-disqus Disqus 95 | -------------------------------------------------------------------------------- /scripts/resources.js: -------------------------------------------------------------------------------- 1 | var db = require('./data'); 2 | var http = require("http"); 3 | var Flickr = require("flickrapi"); 4 | var Parse = require("parse").Parse; 5 | var moment = require("moment"); 6 | 7 | function updateDB(results, api) { 8 | var finished = []; 9 | var burstFinished; 10 | var prevFunc; 11 | // Deal with request limit 12 | for(var i = results.length; i > 0; i -= 10) { 13 | burstFinished = new Parse.Promise(); 14 | (function(burstFinished, nextFunc, i) { 15 | var currentFunc = function() { 16 | if(nextFunc) { 17 | setTimeout(nextFunc, 1000); 18 | } 19 | console.log("uploading burst " + Math.ceil(i/10) + "/" + Math.ceil(results.length/10)); 20 | var uploaded = []; 21 | var start = 0 > i - 10 ? 0 : i - 10; 22 | for(var y = start; y < i; y++) { 23 | uploaded.push(db.asteroids.upload(results[y].title, results[y].src, results[y].desc, results[y].date, api).fail(function(err) { 24 | if(err === "Image already exists") { 25 | return Parse.Promise.as("Image already exists"); 26 | } else { 27 | return err; 28 | } 29 | })); 30 | } 31 | Parse.Promise.when(uploaded).then(function () { 32 | burstFinished.resolve.apply(burstFinished, arguments); 33 | }, function () { 34 | burstFinished.reject.apply(burstFinished, arguments); 35 | }); 36 | }; 37 | if(i - 10 < 0) { 38 | currentFunc(); 39 | } 40 | prevFunc = currentFunc; 41 | })(burstFinished, prevFunc, i); 42 | finished.push(burstFinished); 43 | } 44 | return Parse.Promise.when(finished) 45 | } 46 | 47 | exports.updateSpitzer = function(obj) { 48 | var results = obj.results 49 | var finalResults = []; 50 | var counter = 0; 51 | for(var i = 0; i < results.collection1.length; i++) { 52 | var files = []; 53 | var resolution; 54 | var urlRegex = /^.+?(_[a-zA-Z]+)?\.jpg$/ 55 | var next = false; 56 | do { 57 | resolution = results.collection2[counter].resolution; 58 | var size = resolution.text.match(/^(\d+) x (\d+) • (\d+(?:\.\d+)?) ([KM])B$/); 59 | size[3] = size[4] === "M" ? 1024 * size[3] : size[3] 60 | files.push({ 61 | src: resolution.href, 62 | resolution: { 63 | width: parseFloat(size[1]), 64 | height: parseFloat(size[2]), 65 | size: parseFloat(size[3]) 66 | } 67 | }); 68 | counter++; 69 | } while(resolution.href.match(urlRegex)[1]); 70 | finalResults.push({ 71 | title: results["collection" + (4 + i)][0].Title.text, 72 | src: files, 73 | desc: results.collection3[i].text 74 | }); 75 | } 76 | return updateDB(finalResults, "spitzer"); 77 | } 78 | 79 | exports.updateAPOD = function (obj) { 80 | var results = obj.results.Apod_Detail; 81 | var finalResults = []; 82 | for(var i = 0; i < results.length; i++) { 83 | if (results[i].Title !== undefined && results[i].Date !== undefined && results[i].Image.href !== undefined) { 84 | finalResults.push({ 85 | title: results[i].Title, 86 | src: [{ 87 | src: results[i].Image.href, 88 | resolution: {} 89 | }], 90 | desc: results[i].Description.text, 91 | date: moment(results[i].Date, "YYYY MMM DD").toDate() 92 | }); 93 | } 94 | } 95 | return updateDB(finalResults, "APOD"); 96 | } 97 | 98 | // flickr 99 | /*function flickrOAuth(callback) { 100 | Flickr.authenticate({ 101 | api_key: process.env.flickrKey, 102 | secret: process.env.flickrSecret, 103 | user_id: process.env.FLICKR_USER_ID, 104 | access_token: process.env.FLICKR_ACCESS_TOKEN, 105 | access_token_secret: process.env.FLICKR_ACCESS_TOKEN_SECRET 106 | }, callback); 107 | } 108 | 109 | flickrOAuth(function(err, flickr) { 110 | if(err) { 111 | console.log("Flickr authentication error: " + err); 112 | return; 113 | } 114 | flickr.people.getPublicPhotos({ 115 | user_id: flickr.options.user_id 116 | }, function(err, result) { 117 | if(err) { 118 | console.log("Error getting photos from user: " + err); 119 | return; 120 | } 121 | photos = result.photos.photo; 122 | var finalResults = []; 123 | sizes = [ 124 | { 125 | name: "t", 126 | size: 100 127 | }, 128 | { 129 | name: "n", 130 | size: 320 131 | }, 132 | { 133 | name: "z", 134 | size: 640 135 | }, 136 | { 137 | name: "b", 138 | size: 1024 139 | } 140 | ]; 141 | for(var i = 0; i < photos.length; i++) { 142 | var files = []; 143 | for(var y = 0; y < sizes.length; y++) { 144 | files.push({ 145 | src: "http://farm"+ photos[i].farm +".staticflickr.com/" + photos[i].server + "/" + photos[i].id + "_" + photos[i].secret + "_" + sizes[y].name + ".jpg", 146 | resolution: { 147 | width: sizes[y].size 148 | } 149 | }); 150 | } 151 | finalResults.push({ 152 | title: photos[i].title, 153 | src: files, 154 | desc: "" 155 | }); 156 | } 157 | updateDB(finalResults, "flickr"); 158 | }); 159 | });*/ 160 | 161 | // kimono 162 | var req = http.request({ 163 | host: "www.kimonolabs.com", 164 | port: 80, 165 | path: "/api/51mcm5qm?apikey="+process.env.kimonoKey, 166 | method: "GET", 167 | headers: { 168 | "Content-Type": "application/json" 169 | } 170 | }, function(res) { 171 | var output = ''; 172 | res.setEncoding('utf8'); 173 | 174 | res.on('data', function (chunk) { 175 | output += chunk; 176 | }); 177 | 178 | res.on('end', function() { 179 | var obj = JSON.parse(output); 180 | if(res.statusCode === 200) { 181 | /*exports.updateKimono(obj).then(function() { 182 | console.log("success:"); 183 | console.log(arguments); 184 | }, function() { 185 | console.log("error:"); 186 | console.log(arguments); 187 | });*/ 188 | } 189 | }); 190 | }); 191 | req.on('error', function(err) { 192 | //res.send('error: ' + err.message); 193 | }); 194 | req.end(); 195 | -------------------------------------------------------------------------------- /scripts/data.js: -------------------------------------------------------------------------------- 1 | /** 2 | * asteroid image data manipulation 3 | * @namespace asteroids 4 | */ 5 | var asteroids = {}, 6 | Parse = require('parse').Parse, 7 | Image = Parse.Object.extend("Image"), 8 | ImageSrc = Parse.Object.extend("ImageSrc"); 9 | 10 | Parse.initialize(process.env.parseID, process.env.parseJavascriptKey, process.env.parseMasterKey); 11 | 12 | /** 13 | * upload an image 14 | * @memberof asteroids 15 | * @param {String} name The title of the image. 16 | * @param {Object[]} src Array of src image files. 17 | * @param {Boolean} [src.isFile=false] Whether the file is file or url. 18 | * @param {String|Array} src.src The source of the file, can be url, base64, or byte array 19 | * @param {string} [src.contentType="image/gif"] The content type if it is a file. 20 | * @param {Object} src.resolution Resolution information of the image 21 | * @param {Number} src.resolution.x The width of the image in pixels 22 | * @param {Number} src.resolution.y The height of the image in pixels 23 | * @param {Number} src.resolution.size The size of the image in kilobytes 24 | * @param {String} desc A description of the image 25 | */ 26 | 27 | /*var kimReq = http.request({ 28 | host: "www.kimonolabs.com", 29 | port: 80, 30 | path: "/api/5zeipr38?apikey="+process.env.kimonoKey, 31 | method: "GET", 32 | headers: { 33 | "Content-Type": "application/json" 34 | } 35 | }, function(kimRes) { 36 | var output = ''; 37 | kimRes.setEncoding('utf8'); 38 | 39 | kimRes.on('data', function (chunk) { 40 | output += chunk; 41 | }); 42 | 43 | kimRes.on('end', function() { 44 | var obj = JSON.parse(output); 45 | if(kimRes.statusCode === 200) { 46 | resources.updateAPOD(obj).then(function(){}, function() { 47 | console.error(arguments); 48 | }); 49 | } 50 | }); 51 | }); 52 | kimReq.on('error', function(err) { 53 | //res.send('error: ' + err.message); 54 | }); 55 | kimReq.end();*/ 56 | 57 | asteroids.upload = function(name, src, desc, date, api) { 58 | var imgQuery = new Parse.Query(Image); 59 | imgQuery.equalTo("name", name); 60 | return imgQuery.find().then(function(results) { 61 | if(results.length) { 62 | return Parse.Promise.error("Image already exists"); 63 | } 64 | 65 | // create new image 66 | var image = new Image(); 67 | 68 | image.set("name", name); 69 | image.set("description", desc); 70 | if(date) { 71 | image.set("actualDate", date); 72 | } 73 | if(api) { 74 | image.set("api", api); 75 | } 76 | 77 | // set permissions 78 | var imageACL = new Parse.ACL(Parse.User.current()); 79 | imageACL.setPublicReadAccess(true); 80 | image.setACL(imageACL); 81 | 82 | var finished = []; 83 | for(var i = 0; i < src.length; i++) { 84 | var srcReady, 85 | imgSrc = new ImageSrc(); 86 | imgSrc.set("width", src[i].resolution.width); 87 | imgSrc.set("height", src[i].resolution.height); 88 | imgSrc.set("size", src[i].resolution.size); 89 | imgSrc.setACL(imageACL); 90 | 91 | if(src[i].isFile) { 92 | if(typeof src[i].src === "string") { 93 | src[i].src = {base64: src[i].src}; 94 | } else if(!Array.isArray(src[i].src)) { 95 | // Convert data to array 96 | src[i].src = Array.prototype.map.call(src[i].src, function (val){ 97 | return val; 98 | }); 99 | } 100 | 101 | var file = new Parse.File(name, src[i].src, src[i].contentType || "image/gif"); 102 | 103 | srcReady = file.save(); 104 | } else { 105 | srcReady = Parse.Promise.as(src[i].src); 106 | } 107 | 108 | finished.push(srcReady.then(function(file) { 109 | if(typeof file === "string") { 110 | imgSrc.set("src", file); 111 | } else { 112 | imgSrc.set("file", file); 113 | imgSrc.set("src", file.url()); 114 | } 115 | 116 | return imgSrc.save() 117 | }).then(function(imgSrc) { 118 | image.relation("src").add(imgSrc); 119 | })); 120 | } 121 | return Parse.Promise.when(finished).then(function(result) { 122 | image.set("owner", Parse.User.current()); 123 | return image.save(); 124 | }).then(function (result) { 125 | if(Parse.User.current()) { 126 | Parse.User.current().relation("uploads").add(image); 127 | return Parse.User.current().save(); 128 | } 129 | }).then(function(user) { 130 | return image; 131 | }) 132 | }); 133 | } 134 | 135 | 136 | asteroids.parseSyncViews = function(id, amount) { 137 | var imgQuery = new Parse.Query(Image); 138 | imgQuery.equalTo("objectId", id); 139 | imgQuery.first({ 140 | success: function(pic) { 141 | pic.increment("views", amount); 142 | pic.save(null, { 143 | success: function(pic) { 144 | // Execute any logic that should take place after the object is saved. 145 | console.log('incremented views '+pic); 146 | }, 147 | error: function(pic, error) { 148 | // Execute any logic that should take place if the save fails. 149 | // error is a Parse.Error with an error code and description. 150 | console.log("Failed to increment: " + error.code + " " + error.message); 151 | } 152 | }); 153 | // Execute any logic that should take place after the object is saved. 154 | }, 155 | error: function(error) { 156 | console.log("Error: " + error.code + " " + error.message); 157 | } 158 | }); 159 | } 160 | asteroids.query = {} 161 | asteroids.query.getPic = function(id){ 162 | var imgQuery = new Parse.Query(Image).include("owner"), 163 | image = {}; 164 | return imgQuery.get(id).then(function(result){ 165 | // Add image table 166 | image.image = result; 167 | var owner = result.get("owner"); 168 | if (owner) { 169 | image.username = owner.get('username'); 170 | } else { 171 | image.username = 'pictroid'; 172 | } 173 | 174 | asteroids.parseSyncViews(id, 15); 175 | // Get src 176 | return result.relation("src").query().first(); 177 | }).then(function(src){ 178 | image.src = src; 179 | return image; 180 | }); 181 | } 182 | 183 | asteroids.query.getViews = function(id, callback) { 184 | global.mdb.pics.findOne(id, function(err, pic) { 185 | if (callback) { 186 | callback(err, pic); 187 | } else { 188 | return [err, pic]; 189 | } 190 | }); 191 | } 192 | 193 | asteroids.query.getUser = function(username){ 194 | var userQuery = new Parse.Query(Parse.User); 195 | return userQuery.get(username).then(function(result){ 196 | console.log(result); 197 | }); 198 | } 199 | asteroids.query.getLatest = function(width) { 200 | var imgQuery = new Parse.Query(Image).include("owner"); 201 | imgQuery.descending("createdAt"); 202 | imgQuery.limit(15); 203 | return imgQuery.find().then(function(results){ 204 | var fileQuery, 205 | images = []; 206 | for (var i = 0; i < results.length; i++) { 207 | (function() { 208 | var owner = results[i].get("owner"), 209 | imgOwner, 210 | views; 211 | if (owner) { 212 | imgOwner = owner.get('username'); 213 | } else { 214 | imgOwner = 'pictroid'; 215 | } 216 | asteroids.query.getViews(results[i].id, function(e,p) { 217 | if (!e) { 218 | views = p.views; 219 | console.log("getViews success: "+p.views); 220 | } else { 221 | console.log("getViews error: "+e); 222 | } 223 | }); 224 | fileQuery = results[i].relation("src").query(); 225 | fileQuery.lessThanOrEqualTo("width", width); 226 | fileQuery.descending("width"); 227 | (function(image) { 228 | images.push(fileQuery.first().then(function(result){ 229 | if(!result) { 230 | var fileQuery = image.relation("src").query(); 231 | fileQuery.ascending("width"); 232 | return fileQuery.first(); 233 | } 234 | return result; 235 | }).then(function(result) { 236 | return { 237 | image: image, 238 | src: result, 239 | username: imgOwner, 240 | views: views 241 | } 242 | })); 243 | })(results[i]); 244 | })(results[i]); 245 | }; 246 | return Parse.Promise.when(images); 247 | }); 248 | } 249 | 250 | exports.user = {}; 251 | exports.asteroids = asteroids; 252 | -------------------------------------------------------------------------------- /scripts/flickrUtils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a utility object for universal oauth 3 | * signing as well as querying Flickr once a query 4 | * object has been constructed to set to the Flickr 5 | * API endpoint. 6 | * 7 | * Response are in JSON format. 8 | */ 9 | module.exports = (function() { 10 | "use strict"; 11 | var crypto = require("crypto"), 12 | fs = require("fs"), 13 | request = require("request"), 14 | statusCodes = { 15 | 400: "Bad Request", 16 | 401: "Unauthorized", 17 | 402: "Payment Required", 18 | 403: "Forbidden", 19 | 404: "Not Found", 20 | 405: "Method Not Allowed", 21 | 406: "Not Acceptable", 22 | 407: "Proxy Authentication Required", 23 | 408: "Request Timeout", 24 | 409: "Conflict", 25 | 410: "Gone", 26 | 411: "Length Required", 27 | 412: "Precondition Failed", 28 | 413: "Request Entity Too Large", 29 | 414: "Request-URI Too Long", 30 | 415: "Unsupported Media Type", 31 | 416: "Requested Range Not Satisfiable", 32 | 417: "Expectation Failed", 33 | 428: "Precondition Required", 34 | 429: "Too Many Requests", 35 | 431: "Request Header Fields Too Large", 36 | 450: "Blocked By Windows Parental Controls", 37 | 499: "Client Closed Request", 38 | 500: "Internal Server Error", 39 | 501: "Not Implemented", 40 | 502: "Bad Gateway", 41 | 503: "Service Unavailable", 42 | 504: "Gateway Timeout", 43 | 505: "HTTP Version Not Supported", 44 | 506: "Variant Also Negotiates", 45 | 507: "Insufficient Storage", 46 | 508: "Loop Detected", 47 | 509: "Bandwidth Limit Exceeded", 48 | 510: "Not Extended", 49 | 511: "Network Authentication Required" 50 | }; 51 | 52 | /** 53 | * Pretty-print JSON files, because we will want 54 | * to inspect them manually, as good humans. 55 | */ 56 | module.exports = (function() { 57 | if (!JSON.prettyprint) { 58 | JSON.prettyprint = function prettyprint(data) { 59 | return this.stringify(data, undefined, 2); 60 | }; 61 | } 62 | return JSON; 63 | }()); 64 | 65 | var callErrors = {}; 66 | 67 | return { 68 | /** 69 | * shorthand function 70 | */ 71 | mkdir: function(dir) { 72 | var trymkdir = function(dir) { 73 | try { 74 | fs.mkdirSync(dir); 75 | //console.log("creating " + dir); 76 | } 77 | catch (e) { 78 | /* we really don't care if it already exists */ 79 | } 80 | }; 81 | var f = ""; 82 | dir.replace("./",'').split("/").forEach(function(d) { 83 | f += d + "/"; 84 | trymkdir(f); 85 | }); 86 | return dir; 87 | }, 88 | 89 | /** 90 | * extend the known generic Flickr errors 91 | */ 92 | extendErrors: function(errors, errCap) { 93 | errors.forEach(function(err) { 94 | if (+(err.code) >= errCap) { 95 | callErrors[err.code] = err; 96 | } 97 | }); 98 | }, 99 | 100 | getCallErrors: function() { 101 | return callErrors; 102 | }, 103 | 104 | /** 105 | * Update an options object for use with Flickr oauth 106 | * so that it has a new timestampe and nonce. 107 | */ 108 | setAuthVals: function(options) { 109 | var timestamp = "" + Date.now(), 110 | md5 = crypto.createHash('md5').update(timestamp).digest("hex"), 111 | nonce = md5.substring(0,32); 112 | options.oauth_timestamp = timestamp; 113 | options.oauth_nonce = nonce; 114 | return options; 115 | }, 116 | 117 | /** 118 | * Collapse a number of oauth query arguments into an 119 | * alphabetically sorted, URI-safe concatenated string. 120 | */ 121 | formQueryString: function(queryArguments) { 122 | var args = [], 123 | append = function(key) { 124 | args.push(key + "=" + encodeURIComponent(queryArguments[key]).replace(/'/g,"%27")); 125 | }; 126 | Object.keys(queryArguments).sort().forEach(append); 127 | return args.join("&"); 128 | }, 129 | 130 | /** 131 | * Turn a url + query string into a Flickr API "base string". 132 | */ 133 | formBaseString: function(url, queryString) { 134 | return ["GET", encodeURIComponent(url), encodeURIComponent(queryString)].join("&"); 135 | }, 136 | 137 | /** 138 | * Parse a Flickr API response. 139 | */ 140 | parseRestResponse: function(body) { 141 | var constituents = body.split("&"), 142 | response = {}, 143 | keyval; 144 | constituents.forEach(function(pair) { 145 | keyval = pair.split("="); 146 | response[keyval[0]] = keyval[1]; 147 | }); 148 | return response; 149 | }, 150 | 151 | /** 152 | * HMAC-SHA1 data signing 153 | */ 154 | sign: function(data, key, secret) { 155 | var hmacKey = key + "&" + (secret ? secret : ''), 156 | hmac = crypto.createHmac("SHA1", hmacKey); 157 | hmac.update(data); 158 | var digest = hmac.digest("base64"); 159 | return encodeURIComponent(digest); 160 | }, 161 | 162 | /** 163 | * Validate an api call 164 | */ 165 | checkRequirements: function(method_name, required, callOptions, callback) { 166 | for(var r=0, last=required.length, arg; r a { 173 | border: none; 174 | } 175 | 176 | .navbar-nav .open ul a { 177 | border-left: 2px solid transparent; 178 | border-bottom: none; 179 | } 180 | 181 | .navbar-nav .open ul a:hover, 182 | .navbar-nav .open ul a:active { 183 | background-color: #000000 !important; 184 | border-color: #a67c52; 185 | } 186 | 187 | .navbar-nav .open > a, 188 | .navbar-nav .open a.active, 189 | .navbar-nav .open a:hover { 190 | background-color: #111111 !important; 191 | border-color: transparent; 192 | } 193 | 194 | .dropdown-menu { 195 | background-color: #111111 !important; 196 | border: none; 197 | } 198 | 199 | .navbar-nav .btn { 200 | border-color: inherit; 201 | padding: 5px 10px 7px; 202 | margin: 9px; 203 | } 204 | 205 | .navbar-toggle { 206 | margin-right: 0; 207 | } 208 | 209 | .navbar-fixed-top + .navbar-fix, 210 | .navbar-absolute + .navbar-fix { 211 | display: block; 212 | height: 50px; 213 | } 214 | 215 | @media (max-width: 767px) { 216 | .navbar-header { 217 | margin: 0 !important; 218 | } 219 | .navbar-collapse { 220 | background-color: #252525; 221 | position: absolute; 222 | left: 0; 223 | right: 0; 224 | margin: 0 !important; 225 | border: none; 226 | padding: 0; 227 | } 228 | .navbar-nav { 229 | margin: 0; 230 | } 231 | .navbar-nav a { 232 | border-bottom: none; 233 | border-left: 2px solid transparent; 234 | } 235 | .navbar-nav .open > a { 236 | border-left: 2px solid transparent; 237 | } 238 | .navbar-transparent { 239 | background: rgba(0,0,0,0.75) !important; 240 | } 241 | .navbar-transparent .navbar-collapse { 242 | background-color: rgba(0,0,0,0.75); 243 | } 244 | } 245 | 246 | /* Absolute header */ 247 | .navbar-absolute { 248 | position: absolute; 249 | top: 0; 250 | right: 0px; 251 | left: 0px; 252 | z-index: 1030; 253 | } 254 | 255 | /* Transparent header */ 256 | .navbar-transparent { 257 | background-color: transparent; 258 | color: #ffffff; 259 | border-color: transparent; 260 | text-shadow: 0 0 2px #000; 261 | border: none; 262 | background: -moz-linear-gradient(top, rgba(0,0,0,0.5) 75%, rgba(0,0,0,0) 100%); /* FF3.6+ */ 263 | background: -webkit-gradient(linear, left top, left bottom, color-stop(75%,rgba(0,0,0,0.5)), color-stop(100%,rgba(0,0,0,0))); /* Chrome,Safari4+ */ 264 | background: -webkit-linear-gradient(top, rgba(0,0,0,0.5) 75%,rgba(0,0,0,0) 100%); /* Chrome10+,Safari5.1+ */ 265 | background: -o-linear-gradient(top, rgba(0,0,0,0.5) 75%,rgba(0,0,0,0) 100%); /* Opera 11.10+ */ 266 | background: -ms-linear-gradient(top, rgba(0,0,0,0.5) 75%,rgba(0,0,0,0) 100%); /* IE10+ */ 267 | background: linear-gradient(to bottom, rgba(0,0,0,0.5) 75%,rgba(0,0,0,0) 100%); /* W3C */ 268 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#80000000', endColorstr='#00000000',GradientType=0 ); /* IE6-9 */ 269 | } 270 | 271 | .navbar-transparent .navbar-brand { 272 | background-image: url(/images/logo/light.png); 273 | } 274 | 275 | .navbar-transparent .navbar-nav .open > a, 276 | .navbar-transparent .navbar-nav .open a.active, 277 | .navbar-transparent .navbar-nav .open a:hover { 278 | background-color: rgba(0, 0, 0, 0.7) !important; 279 | } 280 | 281 | .navbar-transparent .dropdown-menu { 282 | background-color: rgba(0, 0, 0, 0.7) !important; 283 | } 284 | 285 | .navbar-transparent + .jumbotron { 286 | padding-top: 68px; 287 | } 288 | 289 | /* Page */ 290 | .page { 291 | background-color: #eeeeee; 292 | color: #000000; 293 | } 294 | 295 | .page .container { 296 | padding-bottom: 20px; 297 | } 298 | 299 | .page hr { 300 | border-color: #B7B7B7; 301 | margin: 0px; 302 | } 303 | 304 | /* Jumbotron */ 305 | .jumbotron.transparent { 306 | color: #fefefe; 307 | text-shadow: 0 0 5px #000000; 308 | margin: 0; 309 | background-color: transparent; 310 | } 311 | 312 | .jumbotron.center { 313 | text-align: center; 314 | } 315 | 316 | /* Profile */ 317 | .profile-info { 318 | background-color: #fafafa; 319 | display: block; 320 | height: 170px; 321 | padding: 10px; 322 | margin: 20px 0; 323 | } 324 | 325 | .profile-info-picture { 326 | width: 150px; 327 | height: 150px; 328 | float: left; 329 | margin-right: 20px; 330 | background-color: #999999; 331 | } 332 | 333 | .profile-header-picture { 334 | 335 | } 336 | 337 | .profile-name { 338 | text-transform:capitalize; 339 | font-size: 24px; 340 | color: #a67c52; 341 | margin-top: 10px; 342 | } 343 | 344 | .profile-info-text p { 345 | color: #666666; 346 | margin: 0; 347 | } 348 | 349 | /* Forms */ 350 | form.form-transparent { 351 | max-width: 400px; 352 | padding: 30px; 353 | margin: 5% auto; 354 | background-color: rgba(255, 255, 255, 0.2); 355 | border-radius: 5px; 356 | box-shadow: 0 0 50px #000000; 357 | font-size: 12px; 358 | text-shadow: none; 359 | color: #ffffff; 360 | text-align: center; 361 | } 362 | 363 | form.form-transparent hr { 364 | border: 1px solid rgba(255, 255, 255, 0.2); 365 | margin: 15px 30px; 366 | } 367 | 368 | form.form-transparent p { 369 | font-size: 12px; 370 | text-align: left; 371 | padding: 10px 0 0; 372 | } 373 | 374 | form.form-transparent a { 375 | text-shadow: 0 0 5px #000000; 376 | } 377 | 378 | form.form-transparent input, form.form-transparent textarea, #username { 379 | background-color: rgba(255, 255, 255, 0.2); 380 | margin: 5px 0; 381 | box-shadow: none; 382 | border: 2px solid transparent; 383 | color: #ffffff; 384 | border-radius: 2px; 385 | } 386 | 387 | form.form-transparent input:focus, form.form-transparent textarea:focus { 388 | box-shadow: none; 389 | border-color: #a67c52; 390 | } 391 | 392 | form.form-transparent button, 393 | form.form-transparent button:hover, 394 | form.form-transparent button:focus { 395 | background-color: #a67c52; 396 | border: none; 397 | margin: 0; 398 | } 399 | 400 | form.form-transparent button:active { 401 | box-shadow: 0 0 15px #000000 inner; 402 | background-color: #a67c52; 403 | border: none; 404 | } 405 | 406 | form #or { 407 | text-align:center; 408 | margin: 0px; 409 | padding: 0; 410 | font-size: 1.2rem; 411 | color: #a67c52; 412 | text-shadow: 1px 0px 0px #a67c52; 413 | } 414 | 415 | .emsg { 416 | background-color: rgba(221, 118, 116, 0.4); 417 | color: #fff !important; 418 | line-height: auto; 419 | font-size: 13px !important; 420 | padding: 10px; 421 | text-align: left; 422 | display: none; 423 | margin: 5px 0; 424 | } 425 | 426 | .emsg h5 { 427 | margin: 0 0 10px; 428 | } 429 | 430 | @media (max-width: 450px) { 431 | form.form-transparent { 432 | width: 90%; 433 | } 434 | } 435 | 436 | /* Dropbox Element */ 437 | #dropbox{ 438 | background-color: rgba(255, 255, 255, 0.1); 439 | border: 2px dashed rgba(255, 255, 255, 0.2); 440 | width: 100%; 441 | border-radius:2px; 442 | position: relative; 443 | min-height: 60px; 444 | overflow: hidden; 445 | } 446 | 447 | #dropbox .message{ 448 | font-size: 11px; 449 | text-align: center; 450 | display: block; 451 | } 452 | 453 | #dropbox .message i{ 454 | vertical-align: center; 455 | color:#ccc; 456 | font-size:10px; 457 | } 458 | 459 | @media (max-width: 510px) { 460 | #dropbox, form #or { 461 | display: none; 462 | } 463 | } 464 | 465 | /* footer */ 466 | footer { 467 | height: 250px; 468 | width: 100%; 469 | background-color: #252525; 470 | color: #ffffff; 471 | text-align: center; 472 | margin: 0; 473 | font-size: 12px; 474 | bottom: 0px; 475 | left: 0px; 476 | } 477 | 478 | footer .column-container { 479 | height: 200px; 480 | margin: 0 auto; 481 | padding-top: 20px; 482 | display: inline-block; 483 | bottom: 0px; 484 | 485 | } 486 | 487 | footer .column { 488 | float: left; 489 | margin: 0; 490 | padding: 0 30px; 491 | max-width: 100%; 492 | text-align: left; 493 | -moz-box-sizing: border-box; 494 | -webkit-box-sizing: border-box; 495 | box-sizing: border-box; 496 | } 497 | 498 | footer h2 { 499 | font-size: 16px; 500 | margin: 0 0 10px; 501 | } 502 | 503 | footer ul { 504 | list-style: none; 505 | font-size: 13px; 506 | margin: 0; 507 | padding: 0; 508 | } 509 | 510 | footer ul li { 511 | margin: 7px 0; 512 | line-height: 0.7rem; 513 | } 514 | 515 | footer .footer-logo { 516 | width: 14rem; 517 | } 518 | 519 | @media (max-width: 850px) { 520 | footer { 521 | height: 300px; 522 | } 523 | footer .column-container { 524 | height: 250px; 525 | } 526 | footer .top { 527 | display: block; 528 | width: 100%; 529 | margin: 0 0 20px; 530 | float: none; 531 | text-align: center; 532 | } 533 | } 534 | 535 | @media (max-width: 450px) { 536 | footer { 537 | height: auto; 538 | } 539 | footer .column-container { 540 | margin: 0; 541 | display: block; 542 | height: auto; 543 | } 544 | footer .footer-logo { 545 | width: 60%; 546 | } 547 | footer .column { 548 | float: none; 549 | width: 100%; 550 | display: block; 551 | margin: 0 0 18px; 552 | } 553 | footer .top { 554 | text-align: center; 555 | } 556 | } 557 | 558 | /* Settings */ 559 | .settings-combo { 560 | position: relative; 561 | } 562 | 563 | .settings-picture { 564 | height: 110px; 565 | width: 110px; 566 | position: relative; 567 | float: left; 568 | left: 0; 569 | background-color: #999999; 570 | } 571 | 572 | .form-file { 573 | position: relative; 574 | position: relative; 575 | float: left; 576 | width: 100%; 577 | } 578 | 579 | .settings-picture:hover { 580 | 581 | } 582 | 583 | .settings-sidebar { 584 | margin-left: 115px; 585 | min-height: 100px; 586 | } 587 | 588 | .formp { 589 | padding-top: 0px !important; 590 | vertical-align: middle; 591 | font-size: 1.2rem !important; 592 | background-color: rgba(255, 255, 255, 0.2); 593 | box-shadow: none; 594 | border: 2px solid transparent; 595 | height: 28px; 596 | color: #ffffff; 597 | border-radius: 2px; 598 | margin-bottom: 5px; 599 | } 600 | #username { 601 | padding-top: 0px !important; 602 | text-shadow: 0px 0px 20px #000; 603 | vertical-align: middle; 604 | font-size: 1.8rem; 605 | text-transform:capitalize; 606 | background-color: rgba(255, 255, 255, 0) !important; 607 | } 608 | 609 | /* Details */ 610 | .image-page { 611 | margin-bottom: 0; 612 | padding-bottom: 0 !important; 613 | } 614 | 615 | .image-title { 616 | color: #a67c52; 617 | font-size: 20px; 618 | } 619 | 620 | .image-container { 621 | position: relative; 622 | } 623 | 624 | .image-image { 625 | width: 100%; 626 | } 627 | 628 | .image-share { 629 | position: absolute; 630 | bottom: 10px; 631 | right: 10px; 632 | } 633 | 634 | .image-share .a2a_default_style .a2a_svg { 635 | border-radius: 4px; 636 | } 637 | 638 | .image-stats { 639 | padding: 10px 15px; 640 | } 641 | 642 | .image-stats div { 643 | display: inline-block; 644 | } 645 | 646 | .image-tags { 647 | margin-left: 20px; 648 | } 649 | 650 | .image-tags div + div:before { 651 | content: ', '; 652 | } 653 | 654 | .image-social { 655 | float: right; 656 | padding-top: 3px; 657 | } 658 | 659 | .image-social div + div { 660 | margin-left: 15px; 661 | } 662 | 663 | .image-details { 664 | padding: 10px 15px; 665 | background-color: #ffffff; 666 | } 667 | 668 | .image-description { 669 | font-size: 16px; 670 | font-weight: 200; 671 | } 672 | 673 | .image-details h2 { 674 | border-bottom: 1px solid #b7b7b7; 675 | font-size: 18px; 676 | padding-bottom: 5px; 677 | } 678 | 679 | .image-comments { 680 | padding: 60px 30px; 681 | } 682 | 683 | .image-comments-notice { 684 | padding: 10px 10px 50px; 685 | } 686 | 687 | .comment { 688 | border-top: 1px solid #b7b7b7; 689 | padding: 10px 0 2px; 690 | margin: 10px; 691 | } 692 | 693 | .comment-user { 694 | font-size: 16px; 695 | } 696 | 697 | .comment-text { 698 | margin-top: 5px; 699 | } 700 | 701 | /* Feed */ 702 | .feed { 703 | margin: 0px auto; 704 | font-family: 'Open Sans', sans-serif; 705 | font-weight: 200; 706 | display: flex; 707 | flex-flow: row wrap; 708 | margin: 0px !important; 709 | } 710 | 711 | .feed-item { 712 | margin: 10px 1.5%; 713 | width: 30%; 714 | background-color: #fff; 715 | column-rule: #333; 716 | border-radius: 3px; 717 | } 718 | 719 | .feed-item:hover { 720 | box-shadow: 0px 2px 3px rgba(0,0,0,0.3); 721 | } 722 | 723 | .feed img { 724 | width: 100%; 725 | margin: auto; 726 | } 727 | 728 | .feed h4 { 729 | margin: 0 0 5px; 730 | font-size: 115%; 731 | font-weight: 400; 732 | } 733 | 734 | .feed hr { 735 | clear: both; 736 | margin-top: 2px; 737 | height: 2px; 738 | border: 0; 739 | border-top: 2px solid #c2c2c2; 740 | } 741 | 742 | .feed p { 743 | font-size: 85%; 744 | } 745 | 746 | .feed-item .text { 747 | padding: 2% 5% 3% 5%; 748 | margin: 0px; 749 | } 750 | 751 | div.stats { 752 | padding: 4% 5% 0% 5%; 753 | margin: 0px; 754 | } 755 | 756 | ul.details { 757 | list-style: none; 758 | display: inline; 759 | padding: 0px; 760 | } 761 | 762 | li.user > a:hover { 763 | text-decoration: none !important; 764 | font-weight: 500; 765 | } 766 | 767 | li.user { 768 | display: inline; 769 | } 770 | 771 | li.views, li.comments { 772 | height: auto; 773 | float: right; 774 | position: relative; 775 | width: auto; 776 | margin-left: 12px; 777 | display: inline; 778 | line-height: 1; 779 | color: #666; 780 | background-position: left center; 781 | padding-left: 20px; 782 | background-repeat: no-repeat; 783 | background-size: 16px auto; 784 | } 785 | 786 | li.views { 787 | background-image: url(../images/svg/views.svg); 788 | } 789 | 790 | li.comments { 791 | background-image: url(../images/svg/comments.svg); 792 | } 793 | 794 | li.comments a { 795 | color: inherit; 796 | } 797 | 798 | @media all and (max-width: 800px) { 799 | .feed-item { 800 | width: 100%; 801 | } 802 | } 803 | 804 | /* headings */ 805 | .container h3 { 806 | margin-left: 2rem; 807 | display: inline; 808 | font-size: 1.35rem; 809 | } 810 | 811 | .container h3:first-child { 812 | margin-left: 0rem !important; 813 | } 814 | 815 | .container h3 a { 816 | text-decoration: none; 817 | color: #666; 818 | } 819 | 820 | .container h3 > a.active { 821 | color: #a67c52; 822 | } 823 | 824 | .col-md-5 h4 { 825 | display: block; 826 | font-size: 1rem; 827 | } 828 | 829 | .col-md-5 h4 a { 830 | text-decoration: none; 831 | color: #666; 832 | } 833 | 834 | .col-md-5 h4 > a:hover { 835 | color: #a67c52 !important; 836 | } 837 | 838 | .container hr { 839 | clear: both; 840 | margin-top: 2px; 841 | height: 2px; 842 | border: 0; 843 | border-top: 1px solid #c2c2c2; 844 | } 845 | 846 | @media (max-width: 450px) { 847 | .container h3 { 848 | margin-left: 0rem; 849 | display: block; 850 | } 851 | } 852 | -------------------------------------------------------------------------------- /public/bootstrap/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.1.1 (http://getbootstrap.com) 3 | * Copyright 2011-2014 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.isLoading=!1};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",f.resetText||d.data("resetText",d[e]()),d[e](f[b]||this.options[b]),setTimeout(a.proxy(function(){"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},b.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}a&&this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}if(e.hasClass("active"))return this.sliding=!1;var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});return this.$element.trigger(j),j.isDefaultPrevented()?void 0:(this.sliding=!0,f&&this.pause(),this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid.bs.carousel",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")?(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid.bs.carousel")},0)}).emulateTransitionEnd(1e3*d.css("transition-duration").slice(0,-1))):(d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid.bs.carousel")),f&&this.cycle(),this)};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("collapse in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?void this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);!e&&f.toggle&&"show"==c&&(c=!c),e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(jQuery),+function(a){"use strict";function b(b){a(d).remove(),a(e).each(function(){var d=c(a(this)),e={relatedTarget:this};d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown",e)),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown",e))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(''}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;(e||"destroy"!=c)&&(e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]())})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(a(c).is("body")?window:c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);{var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})}},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);if(g&&b<=e[0])return g!=(a=f[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parentsUntil(this.options.target,".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=this.pinnedOffset=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(b.RESET).addClass("affix");var a=this.$window.scrollTop(),c=this.$element.offset();return this.pinnedOffset=c.top-a},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"top"==this.affixed&&(e.top+=d),"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top(this.$element)),"function"==typeof h&&(h=f.bottom(this.$element));var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;if(this.affixed!==i){this.unpin&&this.$element.css("top","");var j="affix"+(i?"-"+i:""),k=a.Event(j+".bs.affix");this.$element.trigger(k),k.isDefaultPrevented()||(this.affixed=i,this.unpin="bottom"==i?this.getPinnedOffset():null,this.$element.removeClass(b.RESET).addClass(j).trigger(a.Event(j.replace("affix","affixed"))),"bottom"==i&&this.$element.offset({top:c-h-this.$element.height()}))}}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(jQuery); -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies 4 | * (C) by Codexa 2014 5 | */ 6 | 7 | var express = require('express'), 8 | http = require('http'), 9 | mongojs = require('mongojs'), 10 | mongo = require('mongodb'), 11 | MongoClient = require('mongodb').MongoClient, 12 | mongoose = require('mongoose'), 13 | MongoStore = require('connect-mongo')(express); 14 | monk = require('monk'), 15 | path = require('path'), 16 | routes = require('./routes'), 17 | app = express(), 18 | auth = require('./scripts/auth'), 19 | db = require('./scripts/data'), 20 | resources = require('./scripts/resources'), 21 | newrelic = require('newrelic'), 22 | helmet = require('helmet'), 23 | moment = require('moment'), 24 | base64 = require('base64-js'), 25 | multiparty = require('multiparty'), 26 | fs = require("fs"), 27 | uuid = require('node-uuid'), 28 | url = require('url'), 29 | ua = require('universal-analytics'), 30 | rackspaceIO = require('./scripts/rackspaceIO'), 31 | Parse = require('parse').Parse; 32 | 33 | // Setup vars 34 | var sid = uuid.v4(), mdb, URI; 35 | // visitor = ua(process.env.gTrackID, sid); 36 | // Parse initialization 37 | Parse.initialize(process.env.parseID, process.env.parseJavascriptKey, process.env.parseMasterKey); 38 | 39 | // Environment configs 40 | app.configure('development', function(){ 41 | app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); 42 | mdb = mongojs.connect('localhost/cache', ['pics']); 43 | env = false; 44 | mdbName = 'cache'; 45 | mhost = '127.0.0.1'; 46 | mport = 27017; 47 | mongoose.connect('mongodb://127.0.0.1:27017/cache'); 48 | }); 49 | 50 | app.configure('production', function(){ 51 | app.use(express.errorHandler()); 52 | env = false; 53 | URI = process.env.OPENSHIFT_MONGODB_DB_URL+process.env.DbName; 54 | mdb = mongojs(URI, ['pics']); 55 | // mongoose.connect(URI); 56 | }); 57 | 58 | global.mdb = mdb; 59 | 60 | // all environments 61 | app.set('port', process.env.OPENSHIFT_NODEJS_PORT || 8080); 62 | app.use(express.static(path.join(__dirname, 'public'))); 63 | app.set('view engine', 'jade'); 64 | app.use(express.logger('dev')); 65 | app.use(express.json()); 66 | app.use(express.urlencoded()); 67 | app.use(express.methodOverride()); 68 | app.use(express.bodyParser()); 69 | app.use(helmet.xframe()); 70 | app.use(helmet.iexss()); 71 | app.use(helmet.contentTypeOptions()); 72 | app.use(helmet.cacheControl()); 73 | app.use(express.cookieParser(sid)); 74 | app.use(express.session({ 75 | secret: sid, 76 | // TO DO change to use just "url" param to allow dev env. 77 | store: new MongoStore({ 78 | db: process.env.DbName, 79 | host: process.env.OPENSHIFT_MONGODB_DB_HOST, 80 | port: process.env.OPENSHIFT_MONGODB_DB_PORT, 81 | username: process.env.OPENSHIFT_MONGODB_DB_USERNAME, 82 | password: process.env.OPENSHIFT_MONGODB__DB_PASSWORD 83 | 84 | }), 85 | cookie: { 86 | httpOnly: (!env), 87 | secure: env, 88 | maxAge: 7200000 // 2 hours 89 | } 90 | })); 91 | app.use(app.router); 92 | app.use(express.csrf()); 93 | app.use(function (req, res, next) { 94 | res.locals.csrftoken = req.csrfToken(); 95 | next(); 96 | }); 97 | 98 | // Get 99 | // app.get('*',function(req,res){ 100 | // if (!env) { 101 | // // res.redirect('https://pictroid.herokuapp.com'+req.url) 102 | // res.redirect('http://localhost:3000'+req.url); 103 | // } 104 | // }); 105 | 106 | app.get('/', function(req, res) { 107 | if (req.session.auth){ 108 | res.render('index', { title: 'Pictroid', username:req.session.user.username, authed:true, route:req.url}); 109 | } else { 110 | res.render('index', {route:req.url}); 111 | } 112 | }); 113 | 114 | // categories 115 | app.get('/explore/categories', function(req, res) { 116 | if (req.session.auth){ 117 | res.render('categories', {username:req.session.user.username, authed:true, route:req.url}); 118 | } else { 119 | res.render('categories', { route:req.url}); 120 | } 121 | }); 122 | 123 | app.get('/explore/category/:field?', function(req, res) { 124 | if (req.session.auth){ 125 | res.render('category', {field:req.params.field, username:req.session.user.username, authed:true, route:req.url}); 126 | } else { 127 | res.render('category', {field:req.params.field, route:req.url}); 128 | } 129 | }); 130 | 131 | app.get('/explore/:filter?', function(req, res) { 132 | if (req.session.auth){ 133 | res.render('explore', { filter: req.params.filter, username:req.session.user.username, authed:true, route:req.url}); 134 | } else { 135 | res.render('explore', { filter: req.params.filter, route:req.url}); 136 | } 137 | }); 138 | 139 | // Profile page 140 | app.get('/pic/:id', function(req, res) { 141 | var message = req.query.m, 142 | views; 143 | if(message == "u"){ 144 | message = []; 145 | message.title = "Success!"; 146 | message.message = "Your pic has been successfully uploaded."; 147 | } 148 | mdb.pics.count({picID:req.params.id}, function(err, count){ 149 | if (!err) { 150 | if (count) { 151 | mdb.pics.update({picID:req.params.id}, {$inc:{views:1}}, {multi:true}, function(err, val) { 152 | // the update is complete 153 | if (err) console.log("error incrementing: "+err) 154 | else console.log("view count incremented "+val); 155 | }); 156 | } else { 157 | mdb.pics.insert({picID:req.params.id, views:1}, function(err, val){ 158 | if (err) console.log("error creating: "+err) 159 | else console.log("view count created " + val); 160 | }); 161 | } 162 | } else { 163 | console.log(err) 164 | }; 165 | }); 166 | if(req.session.auth){ 167 | db.asteroids.query.getPic(req.params.id).then(function(result) { 168 | db.asteroids.query.getViews({picID:req.params.id}, function(err, pic) { 169 | if (!err) { 170 | res.render('details', { m:message, picObject: result, imgOwner:result.username, username:req.session.user.username, views:pic.views, authed:true, route:req.url}); 171 | } else { 172 | res.render('details', { m:message, picObject:result, imgOwner:result.username, username:req.session.user.username, authed:true, route:req.url}); 173 | console.log("error retrieving view count: "+err); 174 | } 175 | }); 176 | }, function() { 177 | res.render('error', { error: '404' }); 178 | }); 179 | } else { 180 | db.asteroids.query.getPic(req.params.id).then(function(result) { 181 | db.asteroids.query.getViews({picID:req.params.id}, function(err, pic) { 182 | // Sync Pic views with Parse 183 | // if (pic.views%15 === 0) { 184 | // db.asteroids.parseSyncViews(pic, 15); 185 | // } 186 | if (!err) { 187 | res.render('details', { m:message, picObject:result, imgOwner:result.username, views:pic.views, route:req.url}); 188 | return pic; 189 | } else { 190 | res.render('details', { m:message, picObject:result, imgOwner:result.username, route:req.url}); 191 | console.log("error retrieving view count: "+err); 192 | } 193 | }); 194 | }, function() { 195 | res.render('error', { error: '404' }); 196 | }); 197 | } 198 | }); 199 | app.get('/user/:name', function(req, res) { 200 | var query = new Parse.Query(Parse.User); 201 | query.equalTo("username", req.params.name); 202 | query.first({ 203 | success: function(user) { 204 | if (user !== undefined) { 205 | var imgQ = user.relation("uploads").query(); 206 | var image = {}; 207 | imgQ.find({ 208 | success : function (result) { 209 | user.picsCount = result.length; 210 | for (var i = 0; i < result.length; i++) { 211 | result[i]; 212 | user.picsTotalCount += result[i].get("views") || 0; 213 | } 214 | console.log(user.picsTotalCount); 215 | return; 216 | }, 217 | error : function(error) { 218 | alert("Error: " + error.code + " " + error.message); 219 | } 220 | }).then(function (pics) { 221 | if (req.session.auth) { 222 | res.render('user/profile', { profile_username:req.params.name, profile_picsCount:user.picsCount, profile_picsTotalCount:user.picsTotalCount, profile_image:user.get("profileImg").url(), profile_status:user.get("status"), username:req.session.user.username, authed:true, route:req.url}); 223 | } else { 224 | res.render('user/profile', { profile_username:req.params.name, profile_picsCount:user.picsCount, profile_picsTotalCount:user.picsTotalCount, results:image ,profile_image:user.get("profileImg").url(), profile_status:user.get("status"), route:req.url}); 225 | } 226 | }); 227 | } else { 228 | res.render('error', { error: '404', secure:true}); 229 | } 230 | }, 231 | error: function(user, error) { 232 | console.log("Error: " + error.code + " " + error.message); 233 | res.render('error', { error: '404' }); 234 | } 235 | }); 236 | 237 | }); 238 | app.get('/upload', function(req, res) { 239 | // Code to auth 240 | if (req.session.auth) { 241 | res.render('user/upload', { username:req.session.user.username, authed:true}); 242 | } else { 243 | res.redirect('/signin'+'?er=SignInRequired'); 244 | } 245 | }); 246 | app.get('/about', function(req, res) { 247 | console.log(req.session); 248 | if (req.session.auth){ 249 | 250 | res.render('about', { username:req.session.user.username, authed:true, route:req.url}); 251 | } else { 252 | res.render('about', { route:req.url}); 253 | } 254 | }); 255 | app.get('/signin', function(req, res) { 256 | if (!req.session.auth){ 257 | if (req.query.er === "SignInRequired") { 258 | res.render('signin', { error : "You have to be signed in to access the page", secure:true}); 259 | } else if (req.query.return_to) { 260 | res.render('signin', { route:req.query.return_to, secure:true}); 261 | } else { 262 | res.render('signin'); 263 | } 264 | } else { 265 | res.redirect('/'); 266 | } 267 | }); 268 | app.get('/signout', function(req, res) { 269 | if (req.session.auth){ 270 | Parse.User.logOut(); 271 | req.session.destroy(function(){ 272 | req.session = null; 273 | if (req.query.return_to) { 274 | res.redirect(req.query.return_to); 275 | } else { 276 | res.redirect('/'); 277 | } 278 | }); 279 | } else { 280 | res.redirect('/signin'); 281 | } 282 | }); 283 | app.get('/signup', function(req, res) { 284 | if (!req.session.auth){ 285 | res.render('signup'); 286 | } else { 287 | res.redirect('/'); 288 | } 289 | }); 290 | app.get('/account/settings', function(req, res) { 291 | // Code to auth 292 | if (req.session.auth) { 293 | res.render('account/settings', { username:req.session.user.username, email:req.session.user.email, status:req.session.user.status, profileImgSrc:req.session.user.profileImg.url, authed:true, secure:true}); 294 | } else { 295 | res.redirect('/signin'); 296 | } 297 | }); 298 | app.get('/password_reset', function(req, res) { 299 | if (!req.session.auth) { 300 | res.render('password_reset', { secure:true}); 301 | } else { 302 | res.redirect('/account/settings'); 303 | } 304 | }); 305 | app.get('/password_reset_request', function(req, res) { 306 | if(req.query.email){ 307 | res.render('password_reset_request', { email : req.query.email, secure:true}); 308 | } else { 309 | res.redirect('/'); 310 | } 311 | }); 312 | app.get('/passwordchange_confirmation', function(req, res) { 313 | if (req.query.username) { 314 | res.render('passwordchange_confirmation', { username:req.query.username, secure:true}); 315 | } else { 316 | res.redirect('/'); 317 | } 318 | }); 319 | app.get('/email_confirmed', function(req, res) { 320 | if (req.query.username){ 321 | res.render('email_confirmed', { username : req.query.username, secure:true}); 322 | } else { 323 | res.redirect('/'); 324 | } 325 | }); 326 | app.get('/email_confirmation', function(req, res) { 327 | if (req.query.email){ 328 | res.render('email_confirmed', { email : req.query.email, secure:true}); 329 | } else { 330 | res.redirect('/'); 331 | } 332 | }); 333 | app.get('/invalid_link', function(req, res) { 334 | // Code to auth 335 | res.redirect('/404'); 336 | }); 337 | app.get('/*', function(req, res) { 338 | res.render('error', { error: '404' }); 339 | }); 340 | 341 | // Post 342 | app.post('/kimono_spitzer', function(req, res) { 343 | resources.updateSpitzer(req.body).then(function(){ 344 | res.send(arguments); 345 | }, function() { 346 | console.error(arguments); 347 | res.send(500, arguments); 348 | }); 349 | }); 350 | app.post('/kimono_APOD', function(req, res) { 351 | resources.updateAPOD(req.body).then(function(){ 352 | res.send(arguments); 353 | }, function() { 354 | console.error(arguments); 355 | res.send(500, arguments); 356 | }); 357 | }); 358 | app.post('/password_reset', function(req, res) { 359 | Parse.User.requestPasswordReset(req.body.email.toLowerCase(), { 360 | success: function() { 361 | // Password reset request was sent successfully 362 | res.redirect("/password_reset_request?email="+req.body.email.toLowerCase()); 363 | }, 364 | error: function(error) { 365 | // Show the error message somewhere 366 | alert("Error: " + error.code + " " + error.message); 367 | } 368 | }); 369 | }); 370 | app.post('/account/settings', function(req, res) { 371 | var form = new multiparty.Form(); 372 | form.parse(req, function(err, fields, files) { 373 | var imagef = fs.readFile(files.profileImgFile[0].path, function (err, data) { 374 | if (err) throw err; 375 | // convert to array 376 | data = Array.prototype.map.call(data, function (val){ 377 | return val; 378 | }); 379 | var file = new Parse.File("user_profile", data, files.profileImgFile[0].headers["content-type"]); 380 | file.save().then(function (file) { 381 | Parse.User.current().set("profileImg", file); 382 | return Parse.User.current().save(); 383 | }).then(function(user) { 384 | console.log('success'); 385 | res.redirect('/account/settings'); 386 | }, 387 | function(error) { 388 | console.log('error', error); 389 | res.render('signin', { error : error.message, secure:true}); 390 | }); 391 | }); 392 | Parse.User.current().set("password", fields.password[0]); 393 | Parse.User.current().save() 394 | .then( 395 | function(user) { 396 | return user.fetch(); 397 | } 398 | ) 399 | .then( 400 | function(user) { 401 | console.log('Password changed', user); 402 | res.redirect('/passwordchange_confirmation?username='+req.session.username); 403 | }, 404 | function(error) { 405 | console.log('Something went wrong', error); 406 | } 407 | ); 408 | }); 409 | }); 410 | 411 | app.post('/signup', function(req, res) { 412 | // var SignUp = new authed.signup(req.body.username, req.body.password, req.body.email, res); 413 | var user = new Parse.User(); 414 | user.set("username", req.body.username.toLowerCase()); 415 | user.set("password", req.body.password); 416 | user.set("email", req.body.email.toLowerCase()); 417 | user.set("status", "Explorer"); 418 | user.signUp(null, { 419 | success: function(user) { 420 | // Redirect to email confirmation page 421 | res.redirect('email_confirmation?email='+req.body.email); 422 | }, 423 | error: function(user, error) { 424 | // Show the error message somewhere and let the user try again. 425 | console.log("Error: " + error.code + " " + error.message); 426 | res.render('signup', { error : error.message, secure:true}); 427 | } 428 | }); 429 | }); 430 | 431 | app.post('/signin', function(req, res) { 432 | Parse.User.logIn(req.body.username.toLowerCase(), req.body.password, { 433 | success: function(user) { 434 | // Do stuff after successful login. 435 | if (user.attributes.emailVerified === true) { 436 | if (user.attributes.lastSignIn == undefined) { 437 | var profileImg = "data:image/gif;base64,R0lGODlhlgCWAOfpABYWFhcXFxgYGBkZGRoaGhsbGxwcHB0dHR4eHh8fHyAgICEhISIiIiMjIyQkJCUlJSYmJicnJygoKCkpKSoqKisrKywsLC0tLS4uLi8vLzAwMDExMTIyMjMzMzQ0NDU1NTY2Njc3Nzg4ODk5OTo6Ojs7Ozw8PD09PT4+Pj8/P0BAQEFBQUJCQkNDQ0REREVFRUZGRkdHR0hISElJSUpKSktLS0xMTE1NTU5OTk9PT1BQUFFRUVJSUlNTU1RUVFVVVVZWVldXV1hYWFlZWVpaWltbW1xcXF1dXV5eXl9fX2BgYGFhYWJiYmNjY2RkZGVlZWZmZmdnZ2hoaGlpaWpqamtra2xsbG1tbW5ubm9vb3BwcHFxcXJycnNzc3R0dHV1dXZ2dnd3d3h4eHl5eXp6ent7e3x8fH19fX5+fn9/f4CAgIGBgYKCgoODg4SEhIWFhYaGhoeHh4iIiImJiYqKiouLi4yMjI2NjY6Ojo+Pj5CQkJGRkZKSkpOTk5SUlJWVlZaWlpeXl5iYmJmZmZqampubm5ycnJ2dnZ6enp+fn6CgoKGhoaKioqOjo6SkpKWlpaampqenp6ioqKmpqaqqqqurq6ysrK2tra6urq+vr7CwsLGxsbKysrOzs7S0tLW1tba2tre3t7i4uLm5ubq6uru7u7y8vL29vb6+vr+/v8DAwMHBwcLCwsPDw8TExMXFxcbGxsfHx8jIyMnJycrKysvLy8zMzM3Nzc7Ozs/Pz9DQ0NHR0dLS0tPT09TU1NXV1dbW1tfX19jY2NnZ2dra2tvb29zc3N3d3d7e3t/f3+Dg4OHh4eLi4uPj4+Tk5OXl5ebm5ufn5+jo6Onp6erq6uvr6+zs7O3t7e7u7u/v7/Dw8PHx8fLy8vPz8/T09PX19fb29vf39/j4+Pn5+fr6+vv7+/z8/P39/f7+/v///////////////////////////////////////////////////////////////////////////////////////////yH+IENvcHlyaWdodCBieSBDb2RleGEgT3JnYW5pc2F0aW9uACH5BAEKAP8ALAAAAACWAJYAAAj+ALEJHEiwoMGDCBMqXMiwocOHECNKnEixosWLGDNq3Mixo8ePIEOKHEmypMmTKFOqXMmypcuXMGPKnEmzps2b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", 438 | parseFile = new Parse.File("user_profile", { base64: profileImg}); 439 | user.set("lastSignIn", new Date()); 440 | user.set("profileImg", parseFile); 441 | user.save().then( 442 | function(user) { 443 | req.session.regenerate(function(){ 444 | // Store the user's primary key 445 | // in the session store to be retrieved, 446 | // or in this case the entire user object 447 | req.session.user = Parse.User.current(); 448 | req.session.auth = true; 449 | res.redirect('/account/settings'); 450 | }); 451 | }, 452 | function(error) { 453 | res.render('signin', { error : error.message, secure:true}); 454 | } 455 | ); 456 | } else { 457 | user.set("lastSignIn", new Date()); 458 | user.save().then( 459 | function(user) { 460 | req.session.regenerate(function(){ 461 | // Store the user's primary key 462 | // in the session store to be retrieved, 463 | // or in this case the entire user object 464 | req.session.user = Parse.User.current(); 465 | req.session.auth = true; 466 | console.log(req.query.return_to); 467 | if (req.body.return_to) { 468 | res.redirect(req.body.return_to); 469 | } else { 470 | res.redirect('/'); 471 | } 472 | }); 473 | }, 474 | function(error) { 475 | console.log('error', error); 476 | if (req.body.return_to) { 477 | res.render('signin', { error : error.message, secure:true, route:req.body.return_to}); 478 | } else { 479 | res.render('signin', { error : error.message, secure:true}); 480 | } 481 | } 482 | ); 483 | } 484 | } else { 485 | res.render('signin', { error : "You have to confirm your email before you can Sign In", secure:true}); 486 | } 487 | }, 488 | error: function(user, error) { 489 | console.log("Error: " + error.code + " " + error.message); 490 | res.render('signin', { error : error.message, secure:true}); 491 | } 492 | }); 493 | }); 494 | 495 | app.post('/upload', function(req, res) { 496 | // Code to handle upload 497 | var form = new multiparty.Form(); 498 | form.parse(req, function(err, fields, files) { 499 | var imagef = fs.createReadStream(files.image[0].path); 500 | rackspaceIO.upload(imagef, files.image[0].originalFilename, function(err, result, url) { 501 | db.asteroids.upload(fields.title[0], [{ 502 | src: url, 503 | contentType: files.image[0].headers["content-type"], 504 | resolution: {} 505 | }], fields.description[0]).then(function (result) { 506 | res.redirect("/pic/"+result.id+"?m=u"); 507 | }, function (err) { 508 | console.log(err); 509 | res.send("Error: " + JSON.stringify(err), 500); 510 | }); 511 | }); 512 | }); 513 | }); 514 | 515 | db.asteroids.query.getLatest(600).then(function() { 516 | global.results = arguments; 517 | http.createServer(app).listen(app.get('port'), function(){ 518 | console.log('Express server listening on port ' + app.get('port')); 519 | }); 520 | }); 521 | -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 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 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | --------------------------------------------------------------------------------