├── 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 |
--------------------------------------------------------------------------------
/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 |
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('').insertAfter(a(this)).on("click",b);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;f.toggleClass("open").trigger("shown.bs.dropdown",h),e.focus()}return!1}},f.prototype.keydown=function(b){if(/(38|40|27)/.test(b.keyCode)){var d=a(this);if(b.preventDefault(),b.stopPropagation(),!d.is(".disabled, :disabled")){var f=c(d),g=f.hasClass("open");if(!g||g&&27==b.keyCode)return 27==b.which&&f.find(e).focus(),d.click();var h=" li:not(.divider):visible a",i=f.find("[role=menu]"+h+", [role=listbox]"+h);if(i.length){var j=i.index(i.filter(":focus"));38==b.keyCode&&j>0&&j--,40==b.keyCode&&j').appendTo(document.body),this.$element.on("click.dismiss.bs.modal",a.proxy(function(a){a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus.call(this.$element[0]):this.hide.call(this))},this)),d&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;d?this.$backdrop.one(a.support.transition.end,b).emulateTransitionEnd(150):b()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(a.support.transition.end,b).emulateTransitionEnd(150):b()):b&&b()};var c=a.fn.modal;a.fn.modal=function(c,d){return this.each(function(){var e=a(this),f=e.data("bs.modal"),g=a.extend({},b.DEFAULTS,e.data(),"object"==typeof c&&c);f||e.data("bs.modal",f=new b(this,g)),"string"==typeof c?f[c](d):g.show&&f.show(d)})},a.fn.modal.Constructor=b,a.fn.modal.noConflict=function(){return a.fn.modal=c,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(b){var c=a(this),d=c.attr("href"),e=a(c.attr("data-target")||d&&d.replace(/.*(?=#[^\s]+$)/,"")),f=e.data("bs.modal")?"toggle":a.extend({remote:!/#/.test(d)&&d},e.data(),c.data());c.is("a")&&b.preventDefault(),e.modal(f,this).one("hide",function(){c.is(":visible")&&c.focus()})}),a(document).on("show.bs.modal",".modal",function(){a(document.body).addClass("modal-open")}).on("hidden.bs.modal",".modal",function(){a(document.body).removeClass("modal-open")})}(jQuery),+function(a){"use strict";var b=function(a,b){this.type=this.options=this.enabled=this.timeout=this.hoverState=this.$element=null,this.init("tooltip",a,b)};b.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'',trigger:"hover focus",title:"",delay:0,html:!1,container:!1},b.prototype.init=function(b,c,d){this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d);for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},b.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},b.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget)[this.type](this.getDelegateOptions()).data("bs."+this.type);return clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show()},b.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget)[this.type](this.getDelegateOptions()).data("bs."+this.type);return clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},b.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){if(this.$element.trigger(b),b.isDefaultPrevented())return;var c=this,d=this.tip();this.setContent(),this.options.animation&&d.addClass("fade");var e="function"==typeof this.options.placement?this.options.placement.call(this,d[0],this.$element[0]):this.options.placement,f=/\s?auto?\s?/i,g=f.test(e);g&&(e=e.replace(f,"")||"top"),d.detach().css({top:0,left:0,display:"block"}).addClass(e),this.options.container?d.appendTo(this.options.container):d.insertAfter(this.$element);var h=this.getPosition(),i=d[0].offsetWidth,j=d[0].offsetHeight;if(g){var k=this.$element.parent(),l=e,m=document.documentElement.scrollTop||document.body.scrollTop,n="body"==this.options.container?window.innerWidth:k.outerWidth(),o="body"==this.options.container?window.innerHeight:k.outerHeight(),p="body"==this.options.container?0:k.offset().left;e="bottom"==e&&h.top+h.height+j-m>o?"top":"top"==e&&h.top-m-j<0?"bottom":"right"==e&&h.right+i>n?"left":"left"==e&&h.left-i'}),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 |
--------------------------------------------------------------------------------