diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..ad8abdd Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f0fa30c --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.bundle +db/*.sqlite3 +log/*.log +tmp/ diff --git a/.sass-cache/742ad55b4b9424c9a95e324ff03114fb3e26b9e1/chatbox.scssc b/.sass-cache/742ad55b4b9424c9a95e324ff03114fb3e26b9e1/chatbox.scssc new file mode 100644 index 0000000..8e75016 Binary files /dev/null and b/.sass-cache/742ad55b4b9424c9a95e324ff03114fb3e26b9e1/chatbox.scssc differ diff --git a/.sass-cache/e5914d15ed8f94df020df183f2d9dc49fb5ec2f6/chatbox.scssc b/.sass-cache/e5914d15ed8f94df020df183f2d9dc49fb5ec2f6/chatbox.scssc new file mode 100644 index 0000000..591fdae Binary files /dev/null and b/.sass-cache/e5914d15ed8f94df020df183f2d9dc49fb5ec2f6/chatbox.scssc differ diff --git a/.sass-cache/e5914d15ed8f94df020df183f2d9dc49fb5ec2f6/style.scssc b/.sass-cache/e5914d15ed8f94df020df183f2d9dc49fb5ec2f6/style.scssc new file mode 100644 index 0000000..5eef1ec Binary files /dev/null and b/.sass-cache/e5914d15ed8f94df020df183f2d9dc49fb5ec2f6/style.scssc differ diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..ed43898 --- /dev/null +++ b/Gemfile @@ -0,0 +1,35 @@ +source 'http://rubygems.org' + +gem 'rails', '3.0.5' + +# Bundle edge Rails instead: +# gem 'rails', :git => 'git://github.com/rails/rails.git' + +gem 'rake' +gem 'passenger' + +gem 'rspec' +gem 'json' + +gem 'omniauth' +gem 'haml' +gem "sass" +gem "compass", ">= 0.11.5" +gem 'bundler' + +gem 'mysql2', "0.2.9" +gem 'thin' + +gem 'yaml_db' +gem 'pusher' + +# Geolocation-related functionality. +gem 'geokit-rails3' + +# Jquery :) +gem 'jquery-rails', '>= 1.0.12' + + +# group :development, :test do +# gem 'wirble' +# end diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..7bcaf05 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,193 @@ +GEM + remote: http://rubygems.org/ + specs: + abstract (1.0.0) + actionmailer (3.0.5) + actionpack (= 3.0.5) + mail (~> 2.2.15) + actionpack (3.0.5) + activemodel (= 3.0.5) + activesupport (= 3.0.5) + builder (~> 2.1.2) + erubis (~> 2.6.6) + i18n (~> 0.4) + rack (~> 1.2.1) + rack-mount (~> 0.6.13) + rack-test (~> 0.5.7) + tzinfo (~> 0.3.23) + activemodel (3.0.5) + activesupport (= 3.0.5) + builder (~> 2.1.2) + i18n (~> 0.4) + activerecord (3.0.5) + activemodel (= 3.0.5) + activesupport (= 3.0.5) + arel (~> 2.0.2) + tzinfo (~> 0.3.23) + activeresource (3.0.5) + activemodel (= 3.0.5) + activesupport (= 3.0.5) + activesupport (3.0.5) + addressable (2.2.4) + arel (2.0.10) + builder (2.1.2) + chunky_png (1.2.0) + compass (0.11.5) + chunky_png (~> 1.2) + fssm (>= 0.2.7) + sass (~> 3.1) + crack (0.1.8) + daemon_controller (0.2.6) + daemons (1.1.4) + diff-lcs (1.1.2) + erubis (2.6.6) + abstract (>= 1.0.0) + eventmachine (0.12.10) + faraday (0.6.1) + addressable (~> 2.2.4) + multipart-post (~> 1.1.0) + rack (< 2, >= 1.1.0) + fastthread (1.0.7) + fssm (0.2.7) + geokit (1.5.0) + geokit-rails3 (0.1.3) + geokit (~> 1.5.0) + rails (~> 3.0.4) + haml (3.1.2) + i18n (0.6.0) + jquery-rails (1.0.12) + railties (~> 3.0) + thor (~> 0.14) + json (1.5.3) + mail (2.2.19) + activesupport (>= 2.3.6) + i18n (>= 0.4.0) + mime-types (~> 1.16) + treetop (~> 1.4.8) + mime-types (1.16) + multi_json (1.0.3) + multi_xml (0.2.2) + multipart-post (1.1.3) + mysql2 (0.2.9) + net-ldap (0.2.2) + nokogiri (1.4.7) + oa-basic (0.2.6) + oa-core (= 0.2.6) + rest-client (~> 1.6.0) + oa-core (0.2.6) + oa-enterprise (0.2.6) + addressable (= 2.2.4) + net-ldap (~> 0.2.2) + nokogiri (~> 1.4.2) + oa-core (= 0.2.6) + pyu-ruby-sasl (~> 0.0.3.1) + rubyntlm (~> 0.1.1) + oa-more (0.2.6) + multi_json (~> 1.0.0) + oa-core (= 0.2.6) + rest-client (~> 1.6.0) + oa-oauth (0.2.6) + faraday (~> 0.6.1) + multi_json (~> 1.0.0) + multi_xml (~> 0.2.2) + oa-core (= 0.2.6) + oauth (~> 0.4.0) + oauth2 (~> 0.4.1) + oa-openid (0.2.6) + oa-core (= 0.2.6) + rack-openid (~> 1.3.1) + ruby-openid-apps-discovery (~> 1.2.0) + oauth (0.4.5) + oauth2 (0.4.1) + faraday (~> 0.6.1) + multi_json (>= 0.0.5) + omniauth (0.2.6) + oa-basic (= 0.2.6) + oa-core (= 0.2.6) + oa-enterprise (= 0.2.6) + oa-more (= 0.2.6) + oa-oauth (= 0.2.6) + oa-openid (= 0.2.6) + passenger (3.0.7) + daemon_controller (>= 0.2.5) + fastthread (>= 1.0.1) + rack + rake (>= 0.8.1) + polyglot (0.3.2) + pusher (0.8.2) + crack (~> 0.1.0) + multi_json (~> 1.0) + ruby-hmac (~> 0.4.0) + signature (~> 0.1.2) + pyu-ruby-sasl (0.0.3.3) + rack (1.2.3) + rack-mount (0.6.14) + rack (>= 1.0.0) + rack-openid (1.3.1) + rack (>= 1.1.0) + ruby-openid (>= 2.1.8) + rack-test (0.5.7) + rack (>= 1.0) + rails (3.0.5) + actionmailer (= 3.0.5) + actionpack (= 3.0.5) + activerecord (= 3.0.5) + activeresource (= 3.0.5) + activesupport (= 3.0.5) + bundler (~> 1.0) + railties (= 3.0.5) + railties (3.0.5) + actionpack (= 3.0.5) + activesupport (= 3.0.5) + rake (>= 0.8.7) + thor (~> 0.14.4) + rake (0.9.2) + rest-client (1.6.3) + mime-types (>= 1.16) + rspec (2.6.0) + rspec-core (~> 2.6.0) + rspec-expectations (~> 2.6.0) + rspec-mocks (~> 2.6.0) + rspec-core (2.6.4) + rspec-expectations (2.6.0) + diff-lcs (~> 1.1.2) + rspec-mocks (2.6.0) + ruby-hmac (0.4.0) + ruby-openid (2.1.8) + ruby-openid-apps-discovery (1.2.0) + ruby-openid (>= 2.1.7) + rubyntlm (0.1.1) + sass (3.1.5) + signature (0.1.2) + ruby-hmac + thin (1.2.11) + daemons (>= 1.0.9) + eventmachine (>= 0.12.6) + rack (>= 1.0.0) + thor (0.14.6) + treetop (1.4.10) + polyglot + polyglot (>= 0.3.1) + tzinfo (0.3.29) + yaml_db (0.2.1) + +PLATFORMS + ruby + +DEPENDENCIES + bundler + compass (>= 0.11.5) + geokit-rails3 + haml + jquery-rails (>= 1.0.12) + json + mysql2 (= 0.2.9) + omniauth + passenger + pusher + rails (= 3.0.5) + rake + rspec + sass + thin + yaml_db diff --git a/README.md b/README.md new file mode 100644 index 0000000..e6b532a --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +### Sirkel + +Sirkel is a geolocation based chat application that won VMware's Flings Contest among all interns in the Americas. It was build in 3 weeks using Rails, Cloud Foundry & Pushr App. + +Because it was built in 3 weeks, there are _bugs_, so please just use this as a (very)rough reference. diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..78075ca --- /dev/null +++ b/Rakefile @@ -0,0 +1,7 @@ +# Add your own tasks in files placed in lib/tasks ending in .rake, +# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. + +require File.expand_path('../config/application', __FILE__) +require 'rake' + +SirkelApp::Application.load_tasks \ No newline at end of file diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb new file mode 100644 index 0000000..3a194ac --- /dev/null +++ b/app/controllers/application_controller.rb @@ -0,0 +1,74 @@ +class ApplicationController < ActionController::Base + protected + helper_method( :current_user, :signed_in?, :get_active_users, :have_location, :set_user_inactive, :chat_id_hash, :poll_interval ) + + # HELPERS + def current_user + @current_user ||= User.find_by_id(session[:user_id]) + end + + def signed_in? + !!current_user + end + + def get_active_users(id) + user = User.find( id ) + lat = user.latitude + lng = user.longitude + return User.geo_scope(:origin => [lat, lng]) + #return User.where(:is_active => true) + end + + def have_location?(id) + user = User.find(id) + return user.latitude || user.longitude + end + + # Caller when logout and when user navigates away. + def set_user_inactive(user) + user.update_attributes(:last_seen_at => 0.0 ) + user.save + end + + + + def chat_id_hash( user_id, p_user_hash ) + chat_ids = [] + + if p_user_hash + p_user_hash.each do |p_user| + + if (p_user['id'] > user_id ) + chat_id = "sirkel-" + user_id.to_s() + "-" + p_user['id'].to_s() + else + chat_id = "sirkel-" + p_user['id'].to_s() + "-" + user_id.to_s() + end + chat_ids << chat_id + end + end + + return chat_ids + + end + + + def poll_interval + # Is the user still alive after ___ sec.? + return 8 + end + + + + + + + + + + # NON HELPERS + def current_user=(user) + @current_user = user + session[:user_id] = user.id + end + +end diff --git a/app/controllers/env_controller.rb b/app/controllers/env_controller.rb new file mode 100644 index 0000000..7cdccfa --- /dev/null +++ b/app/controllers/env_controller.rb @@ -0,0 +1,8 @@ +class EnvController < ApplicationController + + def env + @env = ENV + @page_title = 'Environment Variables' + end + +end diff --git a/app/controllers/geolocate_controller.rb b/app/controllers/geolocate_controller.rb new file mode 100644 index 0000000..2a39123 --- /dev/null +++ b/app/controllers/geolocate_controller.rb @@ -0,0 +1,52 @@ +require 'json' + +class GeolocateController < ApplicationController + + # + # GETS ACTIVE USERS WITH A CERTAIN RADIUS AND SET SELF ACTIVE. + # + def get_active_users_within_radius_and_set_current_user_active() + + lat = params[:lat] + lng = params[:lng] + loc = { :lat => lat, :lng => lng } + radius = params[:radius] + + # First, update the user since now we know there geolocation. + user = User.find( session[:user_id] ) + user.update_attributes( :latitude => lat, :longitude => lng) + user.save + + # Grab proxime users from db. + # proxime_users = User.geo_scope( :within => radius, :origin => [lat, lng]) + # + all_users = User.where("id != ?", session[:user_id]) + all_near_users = User.geo_scope( :within => radius, :origin => [lat, lng]).where("id != ?", session[:user_id]) + proxime_users = [] + + all_near_users.each do |user| + proxime_users << user unless !( Time.now.to_f - user.last_seen_at < poll_interval ) # We're polling every 5, so if a user has + end # been gone for this interval, we're ok. + + # Grab all users from db. + # all_active_users = User.where(:is_active => true).where("id != ?", session[:user_id]) + # Pusher['userActive'].trigger('update-users', {:data => all_active_users}) + + # Get the chat id hash that we'll use on the client side to check which user are within the radius. + chat_ids = chat_id_hash( session[:user_id], proxime_users ) + all_chat_ids = chat_id_hash( session[:user_id], all_users ) + + respond_to do |type| + type.json { render :json => { proxime_users: proxime_users, + self_id: session[:user_id], + params: params, + chat_ids: chat_ids, + all_chat_ids: all_chat_ids, + image_url: user.image_url, + name: user.name }.to_json } + end + + end + + +end diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb new file mode 100644 index 0000000..7716c74 --- /dev/null +++ b/app/controllers/home_controller.rb @@ -0,0 +1,14 @@ +class HomeController < ApplicationController + #layout 'application', :except => :index # Rule to ignore application layout (application.html.) + + def home + reset_session + @login_url = "auth/facebook" + @page_title = 'Login' + @active_users = User.find_active_users + + @tag_line = "Only your closest friends." + + @just_logged_out = params[:just_logged_out] + end +end diff --git a/app/controllers/map_controller.rb b/app/controllers/map_controller.rb new file mode 100644 index 0000000..b0a4370 --- /dev/null +++ b/app/controllers/map_controller.rb @@ -0,0 +1,71 @@ +require 'json' + +class MapController < ApplicationController + helper_method :login_info + + def map + @page_title = 'Map' + end + + def set_all_users_inactive + # Are you Brian, if not... + logger.info( '[Sirkel] Setting all users inactive.' ) + users = User.find(:all) + + users.each do |user| + user.update_attributes(:interval => 80.00) + user.save + end + render :text => '[Sirkel] Set all users inactive.' + end + + + def heartbeat + # Update this user's interval. We'll be getting one of these + # requests every 8 or so seconds. + + user = User.find(session[:user_id] ) + user.update_attributes( :latitude => params[:lat], :longitude => params[:lng]) + user.save + + now = Time.now.to_f + + if( user.last_seen_at ) + + #interval = BigDecimal.new(now.to_s) - BigDecimal.new(user.last_seen_at.to_s) + interval = now - user.last_seen_at + + logger.info( "******************************************************************************" ) + logger.info( "[Sirkel] User ##{session[:user_id]} is alive. We saw them #{interval} seconds ago." ) + logger.info( "******************************************************************************" ) + + user.update_attributes( :last_seen_at => now, + :interval => interval ) + user.save + + + else + user.update_attributes( :last_seen_at => now, + :interval => 5 ) + user.save + end + + + # Let's get a list of users who have a small interval. + # + users = User.where(:interval => 0..poll_interval) + active_users = chat_id_hash( session[:user_id], users ) + + + render :json => { is_alive: "I hear you're alive User ##{params[:id]}", + active_users: active_users, + last_seen_at: user.last_seen_at + }.to_json + + end + + def quicky + render :json => { }.to_json + end + +end diff --git a/app/controllers/pusher_controller.rb b/app/controllers/pusher_controller.rb new file mode 100644 index 0000000..57617e5 --- /dev/null +++ b/app/controllers/pusher_controller.rb @@ -0,0 +1,26 @@ +require 'pusher' +require 'json' + +Pusher.app_id = '7047' +Pusher.key = '67b3874b87eccb84c688' +Pusher.secret = '9136f26c7b6b54e0400f' + +class PusherController < ApplicationController + + protect_from_forgery :except => :auth + + def auth + Logger.new(STDOUT).info("Current User is: " + current_user.to_s) + current_user = User.find( session[:user_id] ) + + if current_user + response = Pusher[params[:channel_name]].authenticate(params[:socket_id], { + :user_id => current_user.id + }) + render :json => response.to_json + else + render :text => "Not authorized", :status => '403' + end + end + +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb new file mode 100644 index 0000000..63c84c1 --- /dev/null +++ b/app/controllers/sessions_controller.rb @@ -0,0 +1,63 @@ +class SessionsController < ApplicationController + + def create + reset_session # see http://guides.rubyonrails.org/security.html#session-fixation + auth = request.env['omniauth.auth'] + + unless @auth = Authorization.find_from_hash(auth) + # Create a new user or add an auth to existing user, depending on + # whether there is already a user signed in. + @auth = Authorization.create_from_hash(auth, current_user) + end + + # @have_location = have_location?(@auth.user_id) + # if( @have_location ) + # @active_users = get_active_users(@auth.user_id) + # end + + @page_title = 'Logged In' + + user = User.find( @auth.user_id ) + @user_img_url = user.image_url + @user_name = user.name + @user_lat = user.latitude + @user_lng = user.longitude + + # We'll use this to display a message to new users. + if (user.last_seen_at) + @new_user_alert = true + end + + # Log the authorizing user in. + self.current_user = @auth + + # Give the user some tips after they login. + tips = [ 'Alt + Drag = Zoom.', 'Click \'n Drag markers.' ] + @tip = tips[rand(tips.size)] + + redirect_to :template => 'home/home', :please_sign_in => true unless session[:user_id] + render :template => "map/map" + end + + + def index + render :text => "Logged in as.." + end + + def log_out + flash[:notice] = "Logged out." + + user = User.find( session[:user_id] ) + set_user_inactive(user) + + reset_session + @just_logged_out = true + redirect_to :controller => 'home', :action => 'home', :just_logged_out => 'true' + end + + def failure + redirect_to root_url, :alert => 'Sorry, there was something wrong with your login attempt. Please try again.' + end + + +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb new file mode 100644 index 0000000..498a623 --- /dev/null +++ b/app/helpers/application_helper.rb @@ -0,0 +1,13 @@ +module ApplicationHelper + + # CONSTANTS + def site_title + return 'SirkelApp' + end + + # URLS + def logout_url + return '/sessions/log_out' + end + +end diff --git a/app/helpers/env_helper.rb b/app/helpers/env_helper.rb new file mode 100644 index 0000000..7cbd64a --- /dev/null +++ b/app/helpers/env_helper.rb @@ -0,0 +1,2 @@ +module EnvHelper +end diff --git a/app/helpers/geolocate_helper.rb b/app/helpers/geolocate_helper.rb new file mode 100644 index 0000000..5d967cd --- /dev/null +++ b/app/helpers/geolocate_helper.rb @@ -0,0 +1,2 @@ +module GeolocateHelper +end diff --git a/app/helpers/home_helper.rb b/app/helpers/home_helper.rb new file mode 100644 index 0000000..23de56a --- /dev/null +++ b/app/helpers/home_helper.rb @@ -0,0 +1,2 @@ +module HomeHelper +end diff --git a/app/helpers/map_helper.rb b/app/helpers/map_helper.rb new file mode 100644 index 0000000..65a9561 --- /dev/null +++ b/app/helpers/map_helper.rb @@ -0,0 +1,2 @@ +module MapHelper +end diff --git a/app/helpers/pusher_helper.rb b/app/helpers/pusher_helper.rb new file mode 100644 index 0000000..404708b --- /dev/null +++ b/app/helpers/pusher_helper.rb @@ -0,0 +1,2 @@ +module PusherHelper +end diff --git a/app/helpers/sessions_helper.rb b/app/helpers/sessions_helper.rb new file mode 100644 index 0000000..309f8b2 --- /dev/null +++ b/app/helpers/sessions_helper.rb @@ -0,0 +1,2 @@ +module SessionsHelper +end diff --git a/app/models/authorization.rb b/app/models/authorization.rb new file mode 100644 index 0000000..4032017 --- /dev/null +++ b/app/models/authorization.rb @@ -0,0 +1,15 @@ +class Authorization < ActiveRecord::Base + belongs_to :user + validates_presence_of :user_id, :uid, :provider + validates_uniqueness_of :uid, :scope => :provider + + def self.find_from_hash(hash) + find_by_provider_and_uid(hash['provider'], hash['uid']) + end + + def self.create_from_hash(hash, user = nil) + user ||= User.create_from_hash!(hash) + Authorization.create(:user => user, :uid => hash['uid'], :provider => hash['provider']) + end + +end diff --git a/app/models/session.rb b/app/models/session.rb new file mode 100644 index 0000000..54fee45 --- /dev/null +++ b/app/models/session.rb @@ -0,0 +1,2 @@ +class Session < ActiveRecord::Base +end diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 0000000..6b85a57 --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,17 @@ +class User < ActiveRecord::Base + has_one :authorization + acts_as_mappable :default_units => :miles, + :default_formula => :sphere, + :lat_column_name => :latitude, + :lng_column_name => :longitude + + + def self.create_from_hash!(hash) + create( :name => hash['user_info']['name'], :image_url => hash['user_info']['image'] ) + end + + def self.find_active_users + find_all_by_is_active(true) + end + +end diff --git a/app/stylesheets/chatbox.scss b/app/stylesheets/chatbox.scss new file mode 100644 index 0000000..74887e9 --- /dev/null +++ b/app/stylesheets/chatbox.scss @@ -0,0 +1,323 @@ +// MIXINS *****************************************************************************/ + +@mixin round{ +-moz-border-radius: 10px; +-webkit-border-radius: 10px; +border-radius: 10px; /* future proofing */ +-khtml-border-radius: 10px; /* for old Konqueror browsers */ +} + +@mixin vinyl-font{ + font-family: "Vinyl", "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif; +} + +@mixin dark-shadow{ + text-shadow: #333 0px 1px 0px; +} + +@mixin shadow{ + text-shadow: #fff 0px 1px 0px; +} + +@mixin round-3{ +-moz-border-radius: 3px; +-webkit-border-radius: 3px; +border-radius: 3px; /* future proofing */ +-khtml-border-radius: 3px; /* for old Konqueror browsers */ +} + +.chat-container { + position: absolute; + bottom: -30px; + left: -34px; + z-index: 9999999999999999999; + display: none; } + +// CHATBOX *****************************************************************************/ + +.chatbox { + height: 0px; + width: 0px; + position: relative; + + .close { + background-image: url('../images/chat_close.png'); + width: 16px; + height: 21px; + position: absolute; + top: -2px; + right: -2px; + cursor: pointer; } + .slidy { + -webkit-transform: rotate(0deg); + background: rgba(243, 239, 233, 0.9); + width: 310px; + max-height: 300px; + display: none; + margin: auto; + position: absolute; + bottom: 110px; + left: 21px; + padding: 10px; + border-radius: 10px 10px 1px 0px; + -moz-border-radius: 10px 10px 1px 0px; + -webkit-border-bottom-right-radius: 1px; + -webkit-border-bottom-left-radius: 0px; } } + +.slidy .header h1 { + font-family: 'vino'; + color: #444444; + font-size: 50px; + margin: 0 40px 0 0; + padding: 0; + float: right; + text-align: right; + @include shadow; } + +.header .status { + width: 200px; + float: right; } + +.status { + + ul{ + list-style: none; + margin: 10px; + padding: 2px; + text-indent: 10px; + } + + li{ + float:left; + } + + ul.h3 { + font-size: 12px; + color: #bc6f0f; + width: 50%; + display: inline; + padding: 0px 3px 0px 3px; + @include shadow; } + p { + font-size: 10px; + color: #444; + width: 50%; + display: inline; } } + +.slidy { + h1#slidy-name{ + font-size: 25px; + float:right; + padding-right: 10px; + text-align:left; + text-transform: uppercase; + width: 78%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .im-roller { + background: rgba(242,239,233,0.8); + width: 95%; + height: 150px; + overflow: scroll; + overflow-y: scroll; + overflow-x: hidden; + padding: 5px 0 5px 5px; + margin-top: 55px; + } + .im { + width: 100%; + float: left; + padding: 10px 0; + border-bottom: solid 1px #bbb; } } + +.im h2 { + @include vinyl-font; + float: left; + padding: 0; + font-size: 14px; + line-height: 12px; + text-align: left; + display: block; + margin: 10px 10px 10px 0px; + padding: 3px; + @include round-3; + } + +.me h2 { + background: #e2ba55; + color: red !important; + @include shadow;} + +.them h2 { + background: #e2ba55; + color: white !important; + @include dark-shadow;} + +.im p { + float: left; + margin: 0; + padding: 0; + font-size: 11px; + line-height: 14px; + text-align: left; + color: #333; } + +.chatbox .input-container { + background: url('../images/chat_input.png') no-repeat; + width: 360px; + height: 90px; + position: absolute; + bottom: 30px; + left: 11px; + display: none; } + +.header-icon { + width: 50px; + height: 50px; + float: left; } + +.pointy-icon { + width: 50px; + height: 50px; + margin-left: 9px; + cursor: pointer; + position: absolute; + bottom: 50px; + left: 138px; + + .new-message-indicator{ + background:url('../images/new_message.png') no-repeat; + width: 24px; + height: 25px; + position: relative; + top:-115px; + right: -40px; + display:none; + } + +} + +.icon { + border: #9e9e9e 1px solid; + border-radius: 2px 2px 2px 2px; + -moz-border-radius: 2px 2px 2px 2px; + -webkit-border-radius: 2px; + background: url('../images/user_icon.jpg'); } + +.pointy-icon .pointy { + background: url('../images/chat_icon.png') no-repeat 4px 55px; + width: 56px; + height: 83px; + margin-top: -5px; + margin-left: -5px;} + +.input-container { + .input { + position: absolute; + height: 20px; + width: 178px; + left: 26px; + top: 22px; + background: transparent; + border: none; + padding: 2px; + color: #333; + font-size: 12px; } + + .send-button { + background: url('../images/chat_send.png'); + width: 118px; + height: 47px; + position: absolute; + top: 9px; + left: 221px; + cursor: pointer; } } + +.chatbox textarea:focus, input:focus { + outline: none; } + + + +// SLIDER *****************************************************************************/ + +#radius-slider { + background-image: url('../images/radius_slider_bg.png'); + width: 40px; + position: absolute; + top: 20px; + right: 20px; + height:100%; + display: none; + + .slider-button { + background: no-repeat url('../images/radius_slider.png'); + width: 40px; + height: 53px; + margin-top: 16px; + position: relative; + top: 400px; + cursor: pointer; } + #slider-container { + height: 100%; } + .radius-size { + width: 60px; + height: 30px; + position: relative; + top: 10px; + left: -10px; + text-align: center; } } + +.radius-size { + position: absolute; + bottom: 30px; + right: 70px; + text-align: center; + display:none; + + p { + font-size: 9px; + @include shadow; } + + span#sirkel{ + @include vinyl-font; + } + } + +// ZOOM *****************************************************************************/ + +#zoom-buttons { + width: 38px; + height: 79px; + position: absolute; + top: 5px; + right: 50px; + .zoom-in { + background: no-repeat url('../images/zoom_in.png'); + width: 38px; + height: 42px; } + .zoom-out { + background: no-repeat url('../images/zoom_out.png'); + width: 38px; + height: 44px; + margin-top: -7px; } } + +// LOCATE *****************/ + +#locate-button { + width: 30px; + height: 30px; + position: absolute; + top: 97px; + right: 53px; + background: no-repeat url('../images/locator_button_inactive.gif');} + +#revert-button { + width: 50px; + height: 56px; + position: absolute; + top: 15px; + right: 110px; + background: no-repeat url('../images/revert_button_inactive.png');} \ No newline at end of file diff --git a/app/stylesheets/ie.scss b/app/stylesheets/ie.scss new file mode 100644 index 0000000..53b1c8a --- /dev/null +++ b/app/stylesheets/ie.scss @@ -0,0 +1,16 @@ +@import "blueprint"; + +// To generate css equivalent to the blueprint css but with your configuration applied, uncomment: +// @include blueprint-ie + +//Recommended Blueprint configuration with scoping and semantic layout: +body.bp { + @include blueprint-ie(true); + // Note: Blueprint centers text to fix IE6 container centering. + // This means all your texts will be centered under all version of IE by default. + // If your container does not have the .container class, don't forget to restore + // the correct behavior to your main container (but not the body tag!) + // Example: + // .my-container + // text-align: left +} diff --git a/app/stylesheets/partials/_base.scss b/app/stylesheets/partials/_base.scss new file mode 100644 index 0000000..fb77f7b --- /dev/null +++ b/app/stylesheets/partials/_base.scss @@ -0,0 +1,10 @@ +// Here is where you can define your constants for your application and to configure the blueprint framework. +// Feel free to delete these if you want keep the defaults: + +$blueprint-grid-columns: 24; +$blueprint-container-size: 950px; +$blueprint-grid-margin: 10px; + +// Use this to calculate the width based on the total width. +// Or you can set $blueprint-grid-width to a fixed value and unset $blueprint-container-size -- it will be calculated for you. +$blueprint-grid-width: ($blueprint-container-size + $blueprint-grid-margin) / $blueprint-grid-columns - $blueprint-grid-margin; diff --git a/app/stylesheets/print.scss b/app/stylesheets/print.scss new file mode 100644 index 0000000..0c0e0d0 --- /dev/null +++ b/app/stylesheets/print.scss @@ -0,0 +1,8 @@ +@import "blueprint"; + +// To generate css equivalent to the blueprint css but with your configuration applied, uncomment: +// @include blueprint-print + +//Recommended Blueprint configuration with scoping and semantic layout: +body.bp { + @include blueprint-print(true); } diff --git a/app/stylesheets/screen.scss b/app/stylesheets/screen.scss new file mode 100644 index 0000000..9c0765b --- /dev/null +++ b/app/stylesheets/screen.scss @@ -0,0 +1,506 @@ +@mixin vinyl{ + font-family: "Vinyl", "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif; +} + +@mixin dark-shadow{ + text-shadow: #333 0px 1px 0px; +} + +@mixin shadow{ + text-shadow: #fff 0px 1px 0px; +} + + +@mixin round{ +-moz-border-radius: 10px; +-webkit-border-radius: 10px; +border-radius: 10px; /* future proofing */ +-khtml-border-radius: 10px; /* for old Konqueror browsers */ +} + +@mixin round-bottom{ +-moz-border-radius: 0px 0px 10px 10px ; +-webkit-border-radius: 0px 0px 10px 10px ; +border-radius: 0px 0px 10px 10px ; /* future proofing */ +-khtml-border-radius: 0px 0px 10px 10px ; /* for old Konqueror browsers */ +} + +@mixin sirkel-box{ + -moz-box-shadow: 1px 1px 0px #333; + -webkit-box-shadow: 1px 1px 0px #333; + box-shadow: 1px 1px 0px #333; +} + + +@mixin sirkel-box-inset{ + -moz-box-shadow: inset 1px 1px 0px #ccc; + -webkit-box-shadow: inset 1px 1px 0px #ccc; + box-shadow: inset 1px 1px 0px #ccc; +} + +// This import applies a global reset to any page that imports this stylesheet. +@import "blueprint/reset"; + +// To configure blueprint, edit the partials/base.sass file. +@import "partials/base"; + +// Import all the default blueprint modules so that we can access their mixins. +@import "blueprint"; + +// Import the non-default scaffolding module. +@import "blueprint/scaffolding"; + +// To generate css equivalent to the blueprint css but with your +// configuration applied, uncomment: +// @include blueprint + +// If you are doing a lot of stylesheet concatenation, it is suggested +// that you scope your blueprint styles, so that you can better control +// what pages use blueprint when stylesheets are concatenated together. +body.bp { + //@include blueprint-typography(true); + @include blueprint-utilities; + @include blueprint-debug; + @include blueprint-interaction; + // Remove the scaffolding when you're ready to start doing visual design. + // Or leave it in if you're happy with how blueprint looks out-of-the-box +} + +form.bp { + @include blueprint-form; + // You'll probably want to remove the scaffolding once you start styling your site. + @include blueprint-scaffolding; } + +// Page layout can be done using mixins applied to your semantic classes and IDs: +body.two-col { + + + #container { + @include container; + text-align: center;} + + #header, #footer { @include column($blueprint-grid-columns); } + + #sidebar { + + // One third of the grid columns, rounding down. With 24 cols, this is 8. + $sidebar-columns: floor($blueprint-grid-columns / 3); + @include column($sidebar-columns); } + + #content { + // Two thirds of the grid columns, rounding up. + // With 24 cols, this is 16. + $content-columns: ceil(2 * $blueprint-grid-columns / 3); + // true means it's the last column in the row + @include column($content-columns, true); } +} + + +// ------------------------------------------------------- +// UNIVERSAL +// ------------------------------------------------------- + +p { + font-family: "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif; + color: #333; + font-weight: lighter; + font-size: 14px; + @include shadow; +} + +p { + a:link, a:visited, a:active{ + text-decoration: none; + color: #333; + border-bottom: 1px solid #ffb92e; + } + a:hover{ + border-bottom: none; + } +} + + +// ------------------------------------------------------- +// LOGIN PAGE +// ------------------------------------------------------- + +@import "compass/css3"; +@include font-face("Vinyl", font-files("vino-webfont.ttf", truetype, "vino-webfont.woff", woff)); + +body.login{ + background: url('../images/login_bg.jpg') no-repeat center center fixed; + -webkit-background-size: cover; + -moz-background-size: cover; + -o-background-size: cover; + background-size: cover; + + footer{ + position: fixed; + bottom: 10px; + right: 10px; + } + + header{ + position: fixed; + top: 10px; + right: 10px; + height: 100px; + + p#about, p#contact{ + @include sirkel-box-inset; + @include round; + background: rgba(255, 255, 255, .5); + font-size: 12px; + padding: 5px; + width: 80px; + display: inline; + margin: 0 2px; + float:right; + } + + #contact-div{ + @include sirkel-box-inset; + @include round; + position: relative; + width: 150px; + height: 100px; + top:30px; + right: 0px; + background: rgba(255, 255, 255, .9); + padding: 5px; + display:none; + } + + #about-div{ + @include sirkel-box-inset; + @include round; + position: relative; + width: 150px; + top:30px; + right: 0px; + background: rgba(255, 255, 255, .9); + padding: 5px; + display:none; + margin:10px; + + p{ + margin: 15px; + text-align:left; + line-height:18px; + font-size: 12px; + } + + } + + } + + p.tagline{ + margin-top: -50px; + margin-bottom: 50px; + text-align: right; + padding-right: 245px; + } + + h2{ + color: #ffb92e; + text-shadow: #fcf46a 1px 0px 0px, #7b5e24 3px 2px 0px, rgba(90, 90, 90, .2) 10px 10px 20px; + margin-top: 30px; + @include vinyl; + font-size: 250px; + } + h2:hover{ + color: #f8c000; + } + + h3{ + font-size: 20px; + margin: 10px; + @include vinyl; + @include shadow; + } + + .sirkel-tut{ + background: white !important; + width: 425px; + height: 350px; + padding: 10px; + margin: 0 auto; + overflow:hidden; + display:none; + @include round; + @include box-shadow; + } + + p.watch{ + background: white; + padding:5px; + @include round; + width: 420px; + margin: 10px auto; + font-size: 14px; + @include sirkel-box; + } + + #login-buttons{ + width: 520px; + margin: 30px auto; + padding: 10px; + + p{ + margin:10px; + color: #666; + font-size: 11px; + } + + } + + #fb-login{ + background: url('../images/login_fb_sprite.png') no-repeat; + width: 131px; + height: 49px; + margin: 0px auto; + } + #fb-login:hover{ background: url('../images/login_fb_sprite.png') no-repeat 1px -49px; cursor: pointer;} + #fb-login:active{ background: url('../images/login_fb_sprite.png') no-repeat 1px -99px;cursor: pointer;} + + #twitter-login{ + background: url('../images/login_twitter_sprite.png') no-repeat; + width: 131px; + height: 49px; + float:right; + } + #twitter-login:hover{background: url('../images/login_twitter_sprite.png') no-repeat 1px -49px;cursor: pointer;} + #twitter-login:active{background: url('../images/login_twitter_sprite.png') no-repeat 1px -100px;cursor: pointer;} +} + +// ------------------------------------------------------- +// MAP PAGE. +// ------------------------------------------------------- + +html{ + height:100%; +} + +body.map{ + height: 100%; + + p{ + font-size: 12px; + } + + p, h1, h2, h3,{ + color: #333; + } + + p.online{ + font-size: 10px; + } + + .wrap{ + width: 270px; + word-wrap: break-word; + overflow: hidden; + } + + h1{ + font-family: "Vinyl", "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif; + font-size: 40px; + } + + h1.sirkel{ + font-size: 100px; + color: #ffd842; + text-shadow: #422c06 0px 1px 0px; + } + + li.h3{ + font-family: "Vinyl", "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif; + font-size: 10px; + } + + li.p{ + font-family: "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif; + font-size: 8px; + margin-bottom: 2px; + } + + #map_canvas { + height: 100%; + background: #a5bfdd;} + + #logged-inout{ + position: absolute; + top: -1000px; + left: 10px; + } + + #new-user-alert{ + position: absolute; + top: 100px; + left: -10px; + width: 500px; + border: 10px solid red; + padding: 60px; + background: rgba(242,239,233,0.9); + @include round; + @include sirkel-box; + + h1{ + text-transform: uppercase; + + } + + p{ + font-size: 20px !important; + color: red; + margin: 20px; + } + + } + + #user_info{ + background: rgba(242,239,233,0.8); + @include round; + @include sirkel-box; + padding: 10px; + width: 300px; + margin-bottom:15px; + + #user_image_container{ + height: 100px; + float: left; + + + #user_image{ + @include sirkel-box; + margin: 10px; + } + } + + p.hide{ + font-family: "Vinyl", "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif; + @include round-bottom; + background: #fff; + width: 102%; + margin: 10px -9px -9px -9px; + padding: 5% 2% 2% 2%; + font-size: 10px; + cursor: pointer; + } + + h1{ + margin-top: 10px; + margin-bottom: 5px; + @include shadow; + text-transform: uppercase; + font-size: 20px; + } + + } + + p.show{ + @include round-bottom; + @include sirkel-box; + position: absolute; + top:0px; + left:10px; + font-family: "Vinyl", "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif; + background: #fff; + width: 300px; + height: 10px; + padding: 10px; + font-size: 10px; + cursor: pointer; + } + + #slider{ + position:absolute; + top:10px; + right:10px; + display:block; + height: 95%; + margin: 3px; + } + + #pusher{ + @include round; + padding: 10px; + position: absolute; + bottom: 10px; + left: 10px; + width: 300px; + height: 200px; + background: rgba(242,239,233,0.8); + + } + + #logo{ + background: white; + position: absolute; + bottom: 3px; + left: 3px; + width: 200px; + height: 35px; + padding-left: 10px; + @include round; + @include sirkel-box; + + h2, h2 a:link, h2 a:visited, h2 a:active, h2 a:hover{ + color: #ffb92e; + width: 75px; + text-shadow: #fcf46a 1px 0px 0px, #7b5e24 2px 1px 0px, rgba(90, 90, 90, .2) 10px 10px 20px; + @include vinyl; + font-size: 30px; + text-decoration: none; + display:inline; + float:left; + } + + p{ + font-size: 10px; + float:left; + display:inline; + padding: 0px 0px 0px 0px; + line-height: 15px; + } + } + + + // WEBKIT STYLED SCROLLBARS *************************************************************/ + //@import "compass/css3"; + + ::-webkit-scrollbar { + width: 5px; } + ::-webkit-scrollbar-track { + -webkit-box-shadow: inset 0 0 6px #bc6f0f;; + border-radius: 3px; } + ::-webkit-scrollbar-thumb { + border-radius: 3px; + -webkit-box-shadow: inset 0 0 1px #bc0000; + } + +} + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/views/env/env.html.haml b/app/views/env/env.html.haml new file mode 100644 index 0000000..b18fcf1 --- /dev/null +++ b/app/views/env/env.html.haml @@ -0,0 +1,2 @@ +- if(@env) + = debug(@env.inspect) \ No newline at end of file diff --git a/app/views/home/home.html.haml b/app/views/home/home.html.haml new file mode 100644 index 0000000..40791eb --- /dev/null +++ b/app/views/home/home.html.haml @@ -0,0 +1,34 @@ += javascript_include_tag 'http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js' += javascript_include_tag 'home.script.js' + +%body.bp.two-col.login + + #container + =render :partial => "shared/header" + + %h2 Sirkel + - if(@just_logged_out) + %p.tagline= raw "But you're still logged in to #{link_to "Facebook", 'http://facebook.com', :target => 'blank'} ☺" + -elsif(@please_sign_in) + %p.tagline Please sign in below to use Sirkel. + -elsif(params[:geo_error]) + %p.tagline → After 60 seconds, we weren't able to determine your location ☹ + %p.tagline You may have clicked "Don't Allow This Site to Track My Location" + %p.tagline= raw "To fix this issue, #{link_to "please reset", 'http://news.cnet.com/8301-30685_3-20003731-264.html', :target => 'blank'} your browser settings." + - else + %p.tagline= @tag_line + + %p.watch= raw "New to Sirkel? #{link_to "Watch", '#'} our getting started tutorial." + .sirkel-tut + :preserve + + + #login-buttons + %h3.loggy LOGIN VIA FACEBOOK + #fb-login + %p + We authenticate you and use your image & name from Facebook. + %br We'll never post to your wall or store your data. + /#twitter-login + + = render :partial => "shared/footer" \ No newline at end of file diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml new file mode 100644 index 0000000..9e53d9b --- /dev/null +++ b/app/views/layouts/application.html.haml @@ -0,0 +1,46 @@ +!!! 5 + +:plain + + +%html{ :lang => "en"} + %head + %title= site_title + " | " + @page_title + + =csrf_meta_tag + %meta{ :content => "", :name => "description" } + %meta{ :content => "", :name => "author" } + %meta{ :content => "3 days", :name => "revisit-after" } + %meta{ :name => "viewport", :content=> "width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" } + + %link{ :href => "http://creativecommons.org/licenses/by/3.0/", :rel => "license", :title => "Creative Commons Attribution 3.0 Unported License" } + %link{ :href => "/feed", :rel => "alternate", :title => "Atom", :type => "application/atom+xml" } + + = stylesheet_link_tag 'screen.css?2.0', :media => 'screen, projection' + = stylesheet_link_tag 'print.css?2.0', :media => 'print' + = stylesheet_link_tag 'master.css?2.0', :media => 'screen, projection' + :javascript + var _gaq = _gaq || []; + _gaq.push(['_setAccount', 'UA-5991707-4']); + _gaq.push(['_trackPageview']); + + (function() { + var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; + ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; + var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); + })(); + + // Javascripts + = javascript_include_tag 'jquery.min.js' + + /[if lt IE 8] = stylesheet_link_tag 'ie.css', :media => 'screen, projection' + + = yield diff --git a/app/views/map/map.html.haml b/app/views/map/map.html.haml new file mode 100644 index 0000000..895db74 --- /dev/null +++ b/app/views/map/map.html.haml @@ -0,0 +1,99 @@ +%body.map + #map_canvas + + // THE NEW USER ALERT + -if( !@new_user_alert ) + #new-user-alert + %h1= "↑ Hey " + @user_name + ' ↑' + %p Welcome to Sirkel! + %p= "You have about 30 seconds to tell your browser that it's OK for us to track you. You should see a dialog box. Please do so now." + %p#close-new-user-alert= raw "#{link_to "Close", "#"}" + + // THE BOTTOM LEFT LOGO + #logo + %h2= raw "#{link_to "Sirkel", root_url, :target => "_blank"}" + %p= @tip + %p= link_to 'Log out now', logout_url + /%p= raw "#{mail_to "brian@gnzlz.com", 'Submit a bug to Sirkel'}" + + // THE SLIDER + #slider + - if(signed_in?) + .radius-size + %span#sirkel YOUR SIRKEL → + %h1.sirkel 3 + %span#sirkel MILES + + // THE ZOOM + #zoom-buttons + .zoom-in + .zoom-out + + // THE LOCATE & REVERT + #locate-button + #revert-button + + // THE CHAT CONTAINER + .chat-container + .chatbox + .slidy + .header + .header-icon.icon + %h1#slidy-name ZACH G. + .status + %ul + %li.h3 LATITUDE + %li.p#latstat - + %li.h3 LONGITUDE + %li.p#longstat - + %li.h3 DISTANCE + %li.p#diststat - + + .close + .im-roller + .input-container + .send-button + %input{:type => "text", :class => "input", :name => "message", :maxlength => "140"} + + .pointy-icon.icon + .pointy + .new-message-indicator + + // THE LOGGED IN/OUT + %p.show ↓ SHOW INFO + #logged-inout + - if(signed_in?) + #user_info + #user_image_container + %img{:src => @user_img_url, :id => 'user_image' , :alt => "User image"} + %h1= @user_name + - if @user_lat + %p= "Latitude: " + @user_lat.to_s() + %p= "Longitude: " + @user_lng.to_s() + %p= link_to 'Log out.', logout_url + %p.hide ↑ HIDE INFO + - else + #user_info + %p= raw "Well, seems as if you're logged out ☹
" + %p= raw "Please sign in #{link_to "here.", root_url}" + + + +// Javascripts at the bottom. += stylesheet_link_tag 'jquery-ui-1.8.14.custom.css' + += javascript_include_tag 'http://js.pusherapp.com/1.8/pusher.min.js' += javascript_include_tag 'libs/jquery-ui-1.8.14.custom.min.js' += javascript_include_tag 'http://maps.google.com/maps/api/js?sensor=true' += javascript_include_tag 'libs/dragzoom.js' += javascript_include_tag 'libs/jquery.timeout.min.js' += javascript_include_tag 'map.pusher.js' + += javascript_include_tag 'libs/infobox.js' += javascript_include_tag 'libs/locate.js' + += javascript_include_tag 'map.script.js' += javascript_include_tag 'map.util.js' += javascript_include_tag 'libs/jquery.ui.touch-punch.min.js' += javascript_include_tag 'libs/heartbeat.js' += javascript_include_tag 'map.heartbeat.js' \ No newline at end of file diff --git a/app/views/pusher/auth.html.erb b/app/views/pusher/auth.html.erb new file mode 100644 index 0000000..c480aa3 --- /dev/null +++ b/app/views/pusher/auth.html.erb @@ -0,0 +1,2 @@ +Find me in app/views/pusher/auth.html.erb
diff --git a/app/views/sessions/index.html.haml b/app/views/sessions/index.html.haml new file mode 100644 index 0000000..f63a16d --- /dev/null +++ b/app/views/sessions/index.html.haml @@ -0,0 +1,2 @@ +%h1 Welcome. +%p= "You are logged in as " + self.current_user.name \ No newline at end of file diff --git a/app/views/shared/_footer.html.haml b/app/views/shared/_footer.html.haml new file mode 100644 index 0000000..6878c0b --- /dev/null +++ b/app/views/shared/_footer.html.haml @@ -0,0 +1,4 @@ +%footer + %nav + %ul + %p.li= raw "#{link_to "gnzlz.com", 'http://gnzlz.com', :target => '_blank'} ★ Copyright © 2011 ★ #{link_to( "Github","https://github.com/briangonzalez/sirkel" )}" diff --git a/app/views/shared/_header.html.haml b/app/views/shared/_header.html.haml new file mode 100644 index 0000000..aaae8f8 --- /dev/null +++ b/app/views/shared/_header.html.haml @@ -0,0 +1,17 @@ +%header + %p#contact= raw "#{link_to "Contact", '#'}" + %p#about= raw "#{link_to "About Sirkel", '#'}" + + #contact-div + %h3 Contact + %p= raw "Drop #{mail_to "brian@gnzlz.com", 'a line.'}" + + #about-div + %h3 About Sirkel + %p= raw "#{link_to "Sirkel", 'http://en.wiktionary.org/wiki/sirkel'} is an app entered into VMWare's Summer '11 Flings Contest by interns Brian Gonzalez and Rahul Mathew." + %p= raw "Sirkel is built on #{link_to "VMWare's Cloud ☁ Foundry Service", 'http://www.cloudfoundry.com/', :target => '_blank'}, + powered by #{link_to "Pusher", 'http://pusher.com', :target => '_blank'}, with the help of the #{link_to "Google Maps v3 API", 'http://code.google.com/apis/maps/index.html', :target => '_blank'}" + %p Sirkel has already been used is over 15 countries ✯. + %p= raw "Please use Sirkel in an HTML5 ready browser such as #{link_to "Chrome.", 'http://google.com/chrome', :target => '_blank'}" + %p Sirkel took home VMware's Flings '11 Intern Contest for best app -- woot! + %img{:src => '/images/vmware_logo.png', :alt => "VMWare Logo", :style => 'margin:10px'} diff --git a/config.ru b/config.ru new file mode 100644 index 0000000..68f3481 --- /dev/null +++ b/config.ru @@ -0,0 +1,4 @@ +# This file is used by Rack-based servers to start the application. + +require ::File.expand_path('../config/environment', __FILE__) +run SirkelApp::Application diff --git a/config/application.rb b/config/application.rb new file mode 100644 index 0000000..51828e6 --- /dev/null +++ b/config/application.rb @@ -0,0 +1,24 @@ +require File.expand_path('../boot', __FILE__) + +require 'rails/all' + +# If you have a Gemfile, require the gems listed there, including any gems +# you've limited to :test, :development, or :production. +Bundler.require(:default, Rails.env) if defined?(Bundler) + +module SirkelApp + class Application < Rails::Application + + #Geokit + config.gem "geokit" + + # Lets use Jquery instead of prototype. + config.action_view.javascript_expansions[:defaults] = %w(jquery rails) + + # Configure the default encoding used in templates for Ruby 1.9. + config.encoding = "utf-8" + + # Configure sensitive parameters which will be filtered from the log file. + config.filter_parameters += [:password] + end +end diff --git a/config/boot.rb b/config/boot.rb new file mode 100644 index 0000000..4489e58 --- /dev/null +++ b/config/boot.rb @@ -0,0 +1,6 @@ +require 'rubygems' + +# Set up gems listed in the Gemfile. +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) + +require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE']) diff --git a/config/compass.rb b/config/compass.rb new file mode 100644 index 0000000..2b5e6c7 --- /dev/null +++ b/config/compass.rb @@ -0,0 +1,22 @@ +# This configuration file works with both the Compass command line tool and within Rails. +# Require any additional compass plugins here. +project_type = :rails + +# Set this to the root of your project when deployed: +http_path = "/" + +# You can select your preferred output style here (can be overridden via the command line): +# output_style = :expanded or :nested or :compact or :compressed + +# To enable relative paths to assets via compass helper functions. Uncomment: +# relative_assets = true + +# To disable debugging comments that display the original location of your selectors. Uncomment: +# line_comments = false + + +# If you prefer the indented syntax, you might want to regenerate this +# project again passing --syntax sass, or you can uncomment this: +# preferred_syntax = :sass +# and then run: +# sass-convert -R --from scss --to sass app/stylesheets scss && rm -rf sass && mv scss sass diff --git a/config/data_loader.rb b/config/data_loader.rb new file mode 100644 index 0000000..e69de29 diff --git a/config/database.yml b/config/database.yml new file mode 100644 index 0000000..3dc2697 --- /dev/null +++ b/config/database.yml @@ -0,0 +1,38 @@ +# MySQL +development: + adapter: mysql2 + encoding: utf8 + reconnect: false + database: sirkeldev + pool: 5 + username: root + password: + host: localhost + socket: /private/tmp/mysql.sock + + + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + adapter: mysql2 + encoding: utf8 + reconnect: false + database: sirkeltest + pool: 5 + username: root + password: + host: localhost + socket: /private/tmp/mysql.sock + + +production: + adapter: mysql2 + encoding: utf8 + reconnect: false + database: sirkelprod + pool: 5 + username: root + password: + host: localhost diff --git a/config/database.yml.old2 b/config/database.yml.old2 new file mode 100644 index 0000000..90d87cc --- /dev/null +++ b/config/database.yml.old2 @@ -0,0 +1,22 @@ +# SQLite version 3.x +# gem install sqlite3 +development: + adapter: sqlite3 + database: db/development.sqlite3 + pool: 5 + timeout: 5000 + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + adapter: sqlite3 + database: db/test.sqlite3 + pool: 5 + timeout: 5000 + +production: + adapter: sqlite3 + database: db/production.sqlite3 + pool: 5 + timeout: 5000 diff --git a/config/environment.rb b/config/environment.rb new file mode 100644 index 0000000..5044b85 --- /dev/null +++ b/config/environment.rb @@ -0,0 +1,7 @@ +# Load the rails application +require File.expand_path('../application', __FILE__) + +# Initialize the rails application +SirkelApp::Application.initialize! + +Haml::Template.options[:format] = :html5 diff --git a/config/environments/development.rb b/config/environments/development.rb new file mode 100644 index 0000000..d498c73 --- /dev/null +++ b/config/environments/development.rb @@ -0,0 +1,26 @@ +SirkelApp::Application.configure do + # Settings specified here will take precedence over those in config/application.rb + + # In the development environment your application's code is reloaded on + # every request. This slows down response time but is perfect for development + # since you don't have to restart the webserver when you make code changes. + config.cache_classes = false + + # Log error messages when you accidentally call methods on nil. + config.whiny_nils = true + + # Show full error reports and disable caching + config.consider_all_requests_local = true + config.action_view.debug_rjs = true + config.action_controller.perform_caching = false + + # Don't care if the mailer can't send + config.action_mailer.raise_delivery_errors = false + + # Print deprecation notices to the Rails logger + config.active_support.deprecation = :log + + # Only use best-standards-support built into browsers + config.action_dispatch.best_standards_support = :builtin +end + diff --git a/config/environments/production.rb b/config/environments/production.rb new file mode 100644 index 0000000..f167525 --- /dev/null +++ b/config/environments/production.rb @@ -0,0 +1,49 @@ +SirkelApp::Application.configure do + # Settings specified here will take precedence over those in config/application.rb + + # The production environment is meant for finished, "live" apps. + # Code is not reloaded between requests + config.cache_classes = true + + # Full error reports are disabled and caching is turned on + config.consider_all_requests_local = false + config.action_controller.perform_caching = true + + # Specifies the header that your server uses for sending files + config.action_dispatch.x_sendfile_header = "X-Sendfile" + + # For nginx: + # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' + + # If you have no front-end server that supports something like X-Sendfile, + # just comment this out and Rails will serve the files + + # See everything in the log (default is :info) + # config.log_level = :debug + + # Use a different logger for distributed setups + # config.logger = SyslogLogger.new + + # Use a different cache store in production + # config.cache_store = :mem_cache_store + + # Disable Rails's static asset server + # In production, Apache or nginx will already do this + config.serve_static_assets = false + + # Enable serving of images, stylesheets, and javascripts from an asset server + # config.action_controller.asset_host = "http://assets.example.com" + + # Disable delivery errors, bad email addresses will be ignored + # config.action_mailer.raise_delivery_errors = false + + # Enable threaded mode + # config.threadsafe! + + # Enable locale fallbacks for I18n (makes lookups for any locale fall back to + # the I18n.default_locale when a translation can not be found) + config.i18n.fallbacks = true + + # Send deprecation notices to registered listeners + config.active_support.deprecation = :notify +end diff --git a/config/environments/test.rb b/config/environments/test.rb new file mode 100644 index 0000000..da5eab4 --- /dev/null +++ b/config/environments/test.rb @@ -0,0 +1,35 @@ +SirkelApp::Application.configure do + # Settings specified here will take precedence over those in config/application.rb + + # The test environment is used exclusively to run your application's + # test suite. You never need to work with it otherwise. Remember that + # your test database is "scratch space" for the test suite and is wiped + # and recreated between test runs. Don't rely on the data there! + config.cache_classes = true + + # Log error messages when you accidentally call methods on nil. + config.whiny_nils = true + + # Show full error reports and disable caching + config.consider_all_requests_local = true + config.action_controller.perform_caching = false + + # Raise exceptions instead of rendering exception templates + config.action_dispatch.show_exceptions = false + + # Disable request forgery protection in test environment + config.action_controller.allow_forgery_protection = false + + # Tell Action Mailer not to deliver emails to the real world. + # The :test delivery method accumulates sent emails in the + # ActionMailer::Base.deliveries array. + config.action_mailer.delivery_method = :test + + # Use SQL instead of Active Record's schema dumper when creating the test database. + # This is necessary if your schema can't be completely dumped by the schema dumper, + # like if you have constraints or database-specific column types + # config.active_record.schema_format = :sql + + # Print deprecation notices to the stderr + config.active_support.deprecation = :stderr +end diff --git a/config/initializers/backtrace_silencers.rb b/config/initializers/backtrace_silencers.rb new file mode 100644 index 0000000..59385cd --- /dev/null +++ b/config/initializers/backtrace_silencers.rb @@ -0,0 +1,7 @@ +# Be sure to restart your server when you modify this file. + +# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. +# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } + +# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. +# Rails.backtrace_cleaner.remove_silencers! diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb new file mode 100644 index 0000000..9e8b013 --- /dev/null +++ b/config/initializers/inflections.rb @@ -0,0 +1,10 @@ +# Be sure to restart your server when you modify this file. + +# Add new inflection rules using the following format +# (all these examples are active by default): +# ActiveSupport::Inflector.inflections do |inflect| +# inflect.plural /^(ox)$/i, '\1en' +# inflect.singular /^(ox)en/i, '\1' +# inflect.irregular 'person', 'people' +# inflect.uncountable %w( fish sheep ) +# end diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb new file mode 100644 index 0000000..72aca7e --- /dev/null +++ b/config/initializers/mime_types.rb @@ -0,0 +1,5 @@ +# Be sure to restart your server when you modify this file. + +# Add new mime types for use in respond_to blocks: +# Mime::Type.register "text/richtext", :rtf +# Mime::Type.register_alias "text/html", :iphone diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb new file mode 100644 index 0000000..2049e02 --- /dev/null +++ b/config/initializers/omniauth.rb @@ -0,0 +1,13 @@ +Rails.application.config.middleware.use OmniAuth::Builder do + + if Rails.env.development? + provider :facebook, '208356915880593', '39f25e85529aae23d87c77bfca6aae9d', + :iframe => true, + :scope => "publish_stream,email,user_education_history" + else + provider :facebook, '242295279129203', '2fc600c92bf11b26cdaf598dde5bc095', + :iframe => true, + :scope => "publish_stream,email,user_education_history" + end + +end \ No newline at end of file diff --git a/config/initializers/secret_token.rb b/config/initializers/secret_token.rb new file mode 100644 index 0000000..5f5e719 --- /dev/null +++ b/config/initializers/secret_token.rb @@ -0,0 +1,7 @@ +# Be sure to restart your server when you modify this file. + +# Your secret key for verifying the integrity of signed cookies. +# If you change this key, all old signed cookies will become invalid! +# Make sure the secret is at least 30 characters and all random, +# no regular words or you'll be exposed to dictionary attacks. +SirkelApp::Application.config.secret_token = '7fd57409d1ca771a25d1150b93411e94832410f954b026d71938952b4ad388ac4107a170bafc8204384b3cb728ebeedf363812b7249a6b99364bde881f2b5a23' diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb new file mode 100644 index 0000000..6653c5b --- /dev/null +++ b/config/initializers/session_store.rb @@ -0,0 +1,8 @@ +# Be sure to restart your server when you modify this file. + +#SirkelApp::Application.config.session_store :cookie_store, :key => '_sirkel_app_session' + +# Use the database for sessions instead of the cookie-based default, +# which shouldn't be used to store highly confidential information +# (create the session table with "rails generate session_migration") +SirkelApp::Application.config.session_store :active_record_store diff --git a/config/locales/en.yml b/config/locales/en.yml new file mode 100644 index 0000000..a747bfa --- /dev/null +++ b/config/locales/en.yml @@ -0,0 +1,5 @@ +# Sample localization file for English. Add more files in this directory for other locales. +# See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. + +en: + hello: "Hello world" diff --git a/config/routes.rb b/config/routes.rb new file mode 100644 index 0000000..21e5743 --- /dev/null +++ b/config/routes.rb @@ -0,0 +1,85 @@ +SirkelApp::Application.routes.draw do + + get "pusher/auth" + + # Facebook oauth + match '/auth/:provider/callback', :to => 'sessions#create' + match '/auth/failure', :to => 'sessions#failure' + + # Other + match '/map', :to => 'map#map' + match '/map/sweep', :to => 'map#set_all_users_inactive' + match '/map/heartbeat.json', :to => 'map#heartbeat' + match '/map/quicky.json', :to => 'map#quicky' + + match '/sessions', :to => 'sessions#index' + match '/sessions/log_out', :to => 'sessions#log_out' + match '/home', :to => 'home#home' + match '/pusher/auth', :to => 'pusher#auth' + + match '/env', :to => 'env#env' + + # Geolocate routes + match '/geolocate/locate_update_get.json', + :to => 'geolocate#get_active_users_within_radius_and_set_current_user_active' + + + # The priority is based upon order of creation: + # first created -> highest priority. + + # Sample of regular route: + # match 'products/:id' => 'catalog#view' + # Keep in mind you can assign values other than :controller and :action + + # Sample of named route: + # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase + # This route can be invoked with purchase_url(:id => product.id) + + # Sample resource route (maps HTTP verbs to controller actions automatically): + # resources :products + + # Sample resource route with options: + # resources :products do + # member do + # get 'short' + # post 'toggle' + # end + # + # collection do + # get 'sold' + # end + # end + + # Sample resource route with sub-resources: + # resources :products do + # resources :comments, :sales + # resource :seller + # end + + # Sample resource route with more complex sub-resources + # resources :products do + # resources :comments + # resources :sales do + # get 'recent', :on => :collection + # end + # end + + # Sample resource route within a namespace: + # namespace :admin do + # # Directs /admin/products/* to Admin::ProductsController + # # (app/controllers/admin/products_controller.rb) + # resources :products + # end + + # You can have the root of your site routed with "root" + # just remember to delete public/index.html. + root :to => "home#home" + + # See how all your routes lay out with "rake routes" + + + + # This is a legacy wild controller route that's not recommended for RESTful applications. + # Note: This route will make all actions in every controller accessible via GET requests. + # match ':controller(/:action(/:id(.:format)))' +end diff --git a/db/data.yml b/db/data.yml new file mode 100644 index 0000000..5e165c6 --- /dev/null +++ b/db/data.yml @@ -0,0 +1,88 @@ + +--- +authorizations: + columns: + - id + - provider + - uid + - user_id + - created_at + - updated_at + records: +--- + - - 3 + - facebook + - '6411802' + - 3 + - '2011-07-15 21:14:19.290108' + - '2011-07-15 21:14:19.290108' + +--- +users: + columns: + - id + - name + - created_at + - updated_at + - image_url + - latitude + - longitude + - is_active + records: +--- + - - 3 + - Brian Gonzalez + - '2011-07-15 21:14:19.265707' + - '2011-07-15 21:14:19.265707' + - http://graph.facebook.com/6411802/picture?type=square + - !!null + - !!null + - false + - - 4 + - !!null + - '2011-07-16 02:07:05.291133' + - '2011-07-16 02:07:05.291133' + - !!null + - !!null + - !!null + - false + - - 5 + - DoDo + - '2011-07-16 02:08:11.756589' + - '2011-07-16 02:08:11.756589' + - !!null + - 102.0 + - 30.0 + - true + - - 6 + - BeeGee + - '2011-07-16 02:08:37.499551' + - '2011-07-16 02:08:37.499551' + - !!null + - 102.0 + - 30.0 + - true + - - 7 + - BeeGee + - '2011-07-16 02:16:17.598159' + - '2011-07-16 02:16:17.598159' + - !!null + - 102.0 + - 30.0 + - true + - - 8 + - BeeGee + - '2011-07-16 02:16:30.933270' + - '2011-07-16 02:16:30.933270' + - !!null + - 102.0 + - 30.0 + - true + - - 9 + - BeeGee + - '2011-07-16 02:16:38.813769' + - '2011-07-16 02:16:38.813769' + - !!null + - 102.0 + - 30.0 + - true diff --git a/db/development.sqlite3.old b/db/development.sqlite3.old new file mode 100644 index 0000000..d877a81 Binary files /dev/null and b/db/development.sqlite3.old differ diff --git a/db/migrate/20110711224012_create_authorizations.rb b/db/migrate/20110711224012_create_authorizations.rb new file mode 100644 index 0000000..0692625 --- /dev/null +++ b/db/migrate/20110711224012_create_authorizations.rb @@ -0,0 +1,15 @@ +class CreateAuthorizations < ActiveRecord::Migration + def self.up + create_table :authorizations do |t| + t.string :provider + t.string :uid + t.integer :user_id + + t.timestamps + end + end + + def self.down + drop_table :authorizations + end +end diff --git a/db/migrate/20110711224032_create_users.rb b/db/migrate/20110711224032_create_users.rb new file mode 100644 index 0000000..d3fe73e --- /dev/null +++ b/db/migrate/20110711224032_create_users.rb @@ -0,0 +1,13 @@ +class CreateUsers < ActiveRecord::Migration + def self.up + create_table :users do |t| + t.string :name + + t.timestamps + end + end + + def self.down + drop_table :users + end +end diff --git a/db/migrate/20110714200853_create_sessions.rb.zip b/db/migrate/20110714200853_create_sessions.rb.zip new file mode 100644 index 0000000..21b8b3d Binary files /dev/null and b/db/migrate/20110714200853_create_sessions.rb.zip differ diff --git a/db/migrate/20110715193416_add_image_url_to_user.rb b/db/migrate/20110715193416_add_image_url_to_user.rb new file mode 100644 index 0000000..0573ab0 --- /dev/null +++ b/db/migrate/20110715193416_add_image_url_to_user.rb @@ -0,0 +1,9 @@ +class AddImageUrlToUser < ActiveRecord::Migration + def self.up + add_column :users, :image_url, :string + end + + def self.down + remove_column :users, :image_url + end +end diff --git a/db/migrate/20110716015914_add_latitude_to_user.rb b/db/migrate/20110716015914_add_latitude_to_user.rb new file mode 100644 index 0000000..2ee5d8d --- /dev/null +++ b/db/migrate/20110716015914_add_latitude_to_user.rb @@ -0,0 +1,9 @@ +class AddLatitudeToUser < ActiveRecord::Migration + def self.up + add_column :users, :latitude, :float + end + + def self.down + remove_column :users, :latitude + end +end diff --git a/db/migrate/20110716015932_add_longitude_to_user.rb b/db/migrate/20110716015932_add_longitude_to_user.rb new file mode 100644 index 0000000..2b275ca --- /dev/null +++ b/db/migrate/20110716015932_add_longitude_to_user.rb @@ -0,0 +1,9 @@ +class AddLongitudeToUser < ActiveRecord::Migration + def self.up + add_column :users, :longitude, :float + end + + def self.down + remove_column :users, :longitude + end +end diff --git a/db/migrate/20110716020021_add_is_active_to_user.rb b/db/migrate/20110716020021_add_is_active_to_user.rb new file mode 100644 index 0000000..e3df18e --- /dev/null +++ b/db/migrate/20110716020021_add_is_active_to_user.rb @@ -0,0 +1,9 @@ +class AddIsActiveToUser < ActiveRecord::Migration + def self.up + add_column :users, :is_active, :boolean + end + + def self.down + remove_column :users, :is_active + end +end diff --git a/db/migrate/20110721040527_add_sessions_table.rb b/db/migrate/20110721040527_add_sessions_table.rb new file mode 100644 index 0000000..8e644d2 --- /dev/null +++ b/db/migrate/20110721040527_add_sessions_table.rb @@ -0,0 +1,16 @@ +class AddSessionsTable < ActiveRecord::Migration + def self.up + create_table :sessions do |t| + t.string :session_id, :null => false + t.text :data + t.timestamps + end + + add_index :sessions, :session_id + add_index :sessions, :updated_at + end + + def self.down + drop_table :sessions + end +end diff --git a/db/migrate/20110728222036_add_last_seen_at_and_interval_to_user.rb b/db/migrate/20110728222036_add_last_seen_at_and_interval_to_user.rb new file mode 100644 index 0000000..5438caa --- /dev/null +++ b/db/migrate/20110728222036_add_last_seen_at_and_interval_to_user.rb @@ -0,0 +1,11 @@ +class AddLastSeenAtAndIntervalToUser < ActiveRecord::Migration + def self.up + add_column :users, :last_seen_at, :decimal, :precision => 20, :scale => 6 + add_column :users, :interval, :decimal, :precision => 20, :scale => 6 + end + + def self.down + remove_column :users, :interval + remove_column :users, :last_seen_at + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 0000000..763befd --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,45 @@ +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended to check this file into your version control system. + +ActiveRecord::Schema.define(:version => 20110728222036) do + + create_table "authorizations", :force => true do |t| + t.string "provider" + t.string "uid" + t.integer "user_id" + t.datetime "created_at" + t.datetime "updated_at" + end + + create_table "sessions", :force => true do |t| + t.string "session_id", :null => false + t.text "data" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "sessions", ["session_id"], :name => "index_sessions_on_session_id" + add_index "sessions", ["updated_at"], :name => "index_sessions_on_updated_at" + + create_table "users", :force => true do |t| + t.string "name" + t.datetime "created_at" + t.datetime "updated_at" + t.string "image_url" + t.float "latitude" + t.float "longitude" + t.boolean "is_active" + t.decimal "last_seen_at", :precision => 20, :scale => 6 + t.decimal "interval", :precision => 20, :scale => 6 + end + +end diff --git a/db/seeds.rb b/db/seeds.rb new file mode 100644 index 0000000..664d8c7 --- /dev/null +++ b/db/seeds.rb @@ -0,0 +1,7 @@ +# This file should contain all the record creation needed to seed the database with its default values. +# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). +# +# Examples: +# +# cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }]) +# Mayor.create(:name => 'Daley', :city => cities.first) diff --git a/doc/README_FOR_APP b/doc/README_FOR_APP new file mode 100644 index 0000000..fe41f5c --- /dev/null +++ b/doc/README_FOR_APP @@ -0,0 +1,2 @@ +Use this README file to introduce your application and point to useful places in the API for learning more. +Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries. diff --git a/lib/tasks/.gitkeep b/lib/tasks/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/public/css/chatbox.css b/public/css/chatbox.css new file mode 100644 index 0000000..7c0b897 --- /dev/null +++ b/public/css/chatbox.css @@ -0,0 +1,293 @@ +@font-face { + font-family: 'vino'; + src: url('font/vino-webfont.eot'); + src: url('font/vino-webfont.eot?#iefix') format('embedded-opentype'), + url('font/vino-webfont.woff') format('woff'), + url('font/vino-webfont.ttf') format('truetype'), + url('font/vino-webfont.svg#VinylRegular') format('svg'); + font-weight: normal; + font-style: normal; + +} + +.chat-container{ + position: absolute; + bottom:-22px; + left: -36px; + z-index: 9999999999999999999; + display:none; +} + +/* CHATBOX *****************************************************************************/ + +.chatbox{ + height: 500px; + width: 380px; + position: relative; +} + +.chatbox .close{ + background-image: url(../img/chat_close.png); + width: 16px; + height: 21px; + position: absolute; + top: -2px; + right: -2px; + cursor: pointer; +} + +.chatbox .slidy{ +-webkit-transform: rotate(0deg); + background: rgba(243,239,233,0.9); + width: 310px; + max-height: 300px; + display: none; + margin: auto; + position: absolute; + bottom: 110px; + left: 21px; + padding: 10px; + border-radius: 10px 10px 1px 0px; + -moz-border-radius: 10px 10px 1px 0px; + -webkit-border-bottom-right-radius: 1px; + -webkit-border-bottom-left-radius: 0px; +} + +.slidy .header h1{ + font-family: 'vino'; + color: #444444; + font-size: 50px; + margin: 0 40px 0 0; + padding: 0; + float: right; + text-align: right; + text-shadow: 1px 1px white; +} + +.header .status{ + width: 200px; + float: right; +} + +.status h3{ + font-family: 'vino'; + font-size: 12px; + color: #bc6f0f; + width:50%; + display: inline; + padding: 0px 3px 0px 3px; + text-shadow: 1px 1px white; +} + +.status p{ + font-size: 10px; + color: #444; + width: 50%; + display: inline; +} + +.slidy .im-roller{ + width: 95%; + height: 150px; + overflow:scroll; + overflow-y:scroll; + overflow-x:hidden; + padding-right: 5%; +} + +.slidy .im{ + width: 100%; + float: left; + padding: 10px 0; + border-bottom: solid 1px #bbb; +} + +.im h2{ + float: left; + padding: 0; + font-size: 14px; + text-transform: capitalize; + line-height: 12px; + margin: 5px; + text-align: left; + display: block; + width: 100%; + text-shadow: 1px 1px white; + +} + +.me h2{ + color: #3885f8; +} + +.them h2{ + color: #22bbc7; +} + +.im p{ + float: left; + margin: 0; + padding: 0; + font-size: 11px; + line-height: 14px; + text-align: left; + color: #333; +} + + +.chatbox .input-container{ + background: url(../img/chat_input.png) no-repeat; + width: 379px; + height: 111px; + position: absolute; + bottom: 0px; + left: 0px; + display: none; +} + +.header-icon{ + width: 65px; + height: 65px; + float: left; + +} + +.pointy-icon{ + width: 62px; + height: 56px; + margin-left: 9px; + cursor: pointer; + + position: absolute; + bottom: 50px; + left: 135px; +} + +.icon{ + border: #9e9e9e 1px solid; + border-radius: 6px 10px 10px 10px; + -moz-border-radius: 10px 10px 10px 10px; + -webkit-border-radius: 6px; + background: url(../img/user_icon.png); +} + +.pointy-icon .pointy{ + background: url(../img/chat_icon.png) no-repeat -11px -1px; + width: 95px; + height: 108px; +} + +.input-container .input{ + position: absolute; + height: 20px; + width: 187px; + left: 26px; + top: 22px; + background: transparent; + border: none; + padding: 2px; + color: #333; + font-size: 12px; +} + +.input-container .send-button{ + background: url(../img/chat_send.png); + width: 118px; + height: 47px; + position: absolute; + top: 9px; + left: 231px; + cursor: pointer; +} + +.chatbox textarea:focus, input:focus{ + outline:none +} + + +/* SLIDER *****************************************************************************/ + + +#radius-slider{ + background-image: url(../img/radius_slider_bg.png); + width: 40px; + height: 223px; + position: absolute; + top: 20px; + right: 20px; +} + +#radius-slider .slider-button{ + background: no-repeat url(../img/radius_slider.png); + width: 40px; + height: 53px; + margin-top: 16px; + position: relative; + top: 150px; + cursor: pointer; +} + +#radius-slider #slider-container{ + height: 219px; +} + +#radius-slider .radius-size{ + width: 60px; + height: 30px; + position: relative; + top: 10px; + left: -10px; + text-align:center; +} + +.radius-size h1{ + font-family: 'vino'; + font-size: 30px; + text-shadow: 1px 1px white; +} + +.radius-size p{ + font-size: 9px; + text-shadow: 1px 1px white; +} + + +/* ZOOM *****************************************************************************/ + + +#zoom-buttons{ + width: 38px; + height: 79px; + position: absolute; + top: 35px; + right: 70px; +} + +#zoom-buttons .zoom-in{ + background: no-repeat url(../img/zoom_in.png); + width: 38px; + height: 42px; +} +#zoom-buttons .zoom-out{ + background: no-repeat url(../img/zoom_out.png); + width: 38px; + height: 44px; + margin-top: -7px; +} + + +/* WEBKIT STYLED SCROLLBARS *************************************************************/ + +::-webkit-scrollbar { + width: 2px; +} + +::-webkit-scrollbar-track { + -webkit-box-shadow: inset 0 0 3px rgba(255, 0, 0, 0.1); + border-radius: 10px; +} + +::-webkit-scrollbar-thumb { + border-radius: 10px; + -webkit-box-shadow: inset 0 0 20px #aaa; +} \ No newline at end of file diff --git a/public/css/style.css b/public/css/style.css new file mode 100755 index 0000000..cfe5572 --- /dev/null +++ b/public/css/style.css @@ -0,0 +1,171 @@ + +/* ==== Scroll down to find where to put your styles :) ==== */ + +/* HTML5 ✰ Boilerplate */ + +html, body, div, span, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, +small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, figcaption, figure, +footer, header, hgroup, menu, nav, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} + +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} + +blockquote, q { quotes: none; } +blockquote:before, blockquote:after, +q:before, q:after { content: ''; content: none; } +ins { background-color: #ff9; color: #000; text-decoration: none; } +mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; } +del { text-decoration: line-through; } +abbr[title], dfn[title] { border-bottom: 1px dotted; cursor: help; } +table { border-collapse: collapse; border-spacing: 0; } +hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; } +input, select { vertical-align: middle; } + +body { font:13px/1.231 sans-serif; *font-size:small; } +select, input, textarea, button { font:99% sans-serif; } +pre, code, kbd, samp { font-family: monospace, sans-serif; } + +html { overflow-y: scroll; } +a:hover, a:active { outline: none; } +ul, ol { margin-left: 2em; } +ol { list-style-type: decimal; } +nav ul, nav li { margin: 0; list-style:none; list-style-image: none; } +small { font-size: 85%; } +strong, th { font-weight: bold; } +td { vertical-align: top; } + +sub, sup { font-size: 75%; line-height: 0; position: relative; } +sup { top: -0.5em; } +sub { bottom: -0.25em; } + +pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; padding: 15px; } +textarea { overflow: auto; } +.ie6 legend, .ie7 legend { margin-left: -7px; } +input[type="radio"] { vertical-align: text-bottom; } +input[type="checkbox"] { vertical-align: bottom; } +.ie7 input[type="checkbox"] { vertical-align: baseline; } +.ie6 input { vertical-align: text-bottom; } +label, input[type="button"], input[type="submit"], input[type="image"], button { cursor: pointer; } +button, input, select, textarea { margin: 0; } +input:valid, textarea:valid { } +input:invalid, textarea:invalid { border-radius: 1px; -moz-box-shadow: 0px 0px 5px red; -webkit-box-shadow: 0px 0px 5px red; box-shadow: 0px 0px 5px red; } +.no-boxshadow input:invalid, .no-boxshadow textarea:invalid { background-color: #f0dddd; } + +::-moz-selection{ background: #FF5E99; color:#fff; text-shadow: none; } +::selection { background:#FF5E99; color:#fff; text-shadow: none; } +a:link { -webkit-tap-highlight-color: #FF5E99; } + +button { width: auto; overflow: visible; } +.ie7 img { -ms-interpolation-mode: bicubic; } + +body, select, input, textarea { color: #444; } +h1, h2, h3, h4, h5, h6 { font-weight: bold; } +a, a:active, a:visited { color: #607890; } +a:hover { color: #036; } + +/* + // ========================================== \\ + || || + || Your styles ! || + || || + \\ ========================================== // +*/ + +body{ + font-family: "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif; +} + +h2, h3{ + margin:30px 0; +} + +h2{ + font-size: 55px; +} + +h3{ + font-size: 40px; +} + +/* Map Canvas */ +html, body { + height: 100%; +} + +#map_canvas { + height: 100%; +} + + + + + + + + + + + + + + + + + +/************************************/ +/* OTHER +*************************************/ +.ir { display: block; text-indent: -999em; overflow: hidden; background-repeat: no-repeat; text-align: left; direction: ltr; } +.hidden { display: none; visibility: hidden; } +.visuallyhidden { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; } +.visuallyhidden.focusable:active, +.visuallyhidden.focusable:focus { clip: auto; height: auto; margin: 0; overflow: visible; position: static; width: auto; } +.invisible { visibility: hidden; } +.clearfix:before, .clearfix:after { content: "\0020"; display: block; height: 0; overflow: hidden; } +.clearfix:after { clear: both; } +.clearfix { zoom: 1; } + + +@media all and (orientation:portrait) { + +} + +@media all and (orientation:landscape) { + +} + +@media screen and (max-device-width: 480px) { + + /* html { -webkit-text-size-adjust:none; -ms-text-size-adjust:none; } */ +} + + +@media print { + * { background: transparent !important; color: black !important; text-shadow: none !important; filter:none !important; + -ms-filter: none !important; } + a, a:visited { color: #444 !important; text-decoration: underline; } + a[href]:after { content: " (" attr(href) ")"; } + abbr[title]:after { content: " (" attr(title) ")"; } + .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } + pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } + thead { display: table-header-group; } + tr, img { page-break-inside: avoid; } + @page { margin: 0.5cm; } + p, h2, h3 { orphans: 3; widows: 3; } + h2, h3{ page-break-after: avoid; } +} \ No newline at end of file diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100755 index 0000000..4ec0d29 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/fonts/vino-webfont.eot b/public/fonts/vino-webfont.eot new file mode 100755 index 0000000..1476b34 Binary files /dev/null and b/public/fonts/vino-webfont.eot differ diff --git a/public/fonts/vino-webfont.svg b/public/fonts/vino-webfont.svg new file mode 100755 index 0000000..d0a0b38 --- /dev/null +++ b/public/fonts/vino-webfont.svg @@ -0,0 +1,224 @@ + + + \ No newline at end of file diff --git a/public/fonts/vino-webfont.ttf b/public/fonts/vino-webfont.ttf new file mode 100755 index 0000000..d6aa64d Binary files /dev/null and b/public/fonts/vino-webfont.ttf differ diff --git a/public/fonts/vino-webfont.woff b/public/fonts/vino-webfont.woff new file mode 100755 index 0000000..9040950 Binary files /dev/null and b/public/fonts/vino-webfont.woff differ diff --git a/public/fonts/vino.ttf b/public/fonts/vino.ttf new file mode 100644 index 0000000..807b0a8 Binary files /dev/null and b/public/fonts/vino.ttf differ diff --git a/public/images/anim.gif b/public/images/anim.gif new file mode 100644 index 0000000..4d065d3 Binary files /dev/null and b/public/images/anim.gif differ diff --git a/public/images/chat_close.png b/public/images/chat_close.png new file mode 100644 index 0000000..429cb07 Binary files /dev/null and b/public/images/chat_close.png differ diff --git a/public/images/chat_icon.png b/public/images/chat_icon.png new file mode 100644 index 0000000..fbbf80c Binary files /dev/null and b/public/images/chat_icon.png differ diff --git a/public/images/chat_input.png b/public/images/chat_input.png new file mode 100644 index 0000000..9daad94 Binary files /dev/null and b/public/images/chat_input.png differ diff --git a/public/images/chat_send.png b/public/images/chat_send.png new file mode 100644 index 0000000..4a768ed Binary files /dev/null and b/public/images/chat_send.png differ diff --git a/public/images/chat_send_hover.png b/public/images/chat_send_hover.png new file mode 100644 index 0000000..3d86be7 Binary files /dev/null and b/public/images/chat_send_hover.png differ diff --git a/public/images/chat_send_pressed.png b/public/images/chat_send_pressed.png new file mode 100644 index 0000000..b2c3423 Binary files /dev/null and b/public/images/chat_send_pressed.png differ diff --git a/public/images/chrome.png b/public/images/chrome.png new file mode 100644 index 0000000..311ff06 Binary files /dev/null and b/public/images/chrome.png differ diff --git a/public/images/grid.png b/public/images/grid.png new file mode 100644 index 0000000..129d4a2 Binary files /dev/null and b/public/images/grid.png differ diff --git a/public/images/locator.gif b/public/images/locator.gif new file mode 100644 index 0000000..aa83e16 Binary files /dev/null and b/public/images/locator.gif differ diff --git a/public/images/locator.png b/public/images/locator.png new file mode 100644 index 0000000..0d5461b Binary files /dev/null and b/public/images/locator.png differ diff --git a/public/images/locator_button.gif b/public/images/locator_button.gif new file mode 100644 index 0000000..573a866 Binary files /dev/null and b/public/images/locator_button.gif differ diff --git a/public/images/locator_button_inactive.gif b/public/images/locator_button_inactive.gif new file mode 100644 index 0000000..9995647 Binary files /dev/null and b/public/images/locator_button_inactive.gif differ diff --git a/public/images/login.jpg b/public/images/login.jpg new file mode 100644 index 0000000..be79630 Binary files /dev/null and b/public/images/login.jpg differ diff --git a/public/images/login_bg.jpg b/public/images/login_bg.jpg new file mode 100644 index 0000000..e74cfc9 Binary files /dev/null and b/public/images/login_bg.jpg differ diff --git a/public/images/login_fb_sprite.png b/public/images/login_fb_sprite.png new file mode 100644 index 0000000..0d8d2e2 Binary files /dev/null and b/public/images/login_fb_sprite.png differ diff --git a/public/images/login_twitter_sprite.png b/public/images/login_twitter_sprite.png new file mode 100644 index 0000000..7b172a6 Binary files /dev/null and b/public/images/login_twitter_sprite.png differ diff --git a/public/images/marker_drag.png b/public/images/marker_drag.png new file mode 100644 index 0000000..4ae8d37 Binary files /dev/null and b/public/images/marker_drag.png differ diff --git a/public/images/marker_drag_hover.png b/public/images/marker_drag_hover.png new file mode 100644 index 0000000..0234774 Binary files /dev/null and b/public/images/marker_drag_hover.png differ diff --git a/public/images/new_message.png b/public/images/new_message.png new file mode 100644 index 0000000..ccea7bb Binary files /dev/null and b/public/images/new_message.png differ diff --git a/public/images/radius_slider.png b/public/images/radius_slider.png new file mode 100644 index 0000000..0c4b016 Binary files /dev/null and b/public/images/radius_slider.png differ diff --git a/public/images/radius_slider_bg.png b/public/images/radius_slider_bg.png new file mode 100644 index 0000000..b43f4f8 Binary files /dev/null and b/public/images/radius_slider_bg.png differ diff --git a/public/images/revert_button.png b/public/images/revert_button.png new file mode 100644 index 0000000..3ae3e31 Binary files /dev/null and b/public/images/revert_button.png differ diff --git a/public/images/revert_button_inactive.png b/public/images/revert_button_inactive.png new file mode 100644 index 0000000..a89fb50 Binary files /dev/null and b/public/images/revert_button_inactive.png differ diff --git a/public/images/user_icon.jpg b/public/images/user_icon.jpg new file mode 100644 index 0000000..56dae9b Binary files /dev/null and b/public/images/user_icon.jpg differ diff --git a/public/images/vmware_logo.png b/public/images/vmware_logo.png new file mode 100644 index 0000000..1d56b98 Binary files /dev/null and b/public/images/vmware_logo.png differ diff --git a/public/images/zoom_in.png b/public/images/zoom_in.png new file mode 100644 index 0000000..ba4bc39 Binary files /dev/null and b/public/images/zoom_in.png differ diff --git a/public/images/zoom_out.png b/public/images/zoom_out.png new file mode 100644 index 0000000..d237dae Binary files /dev/null and b/public/images/zoom_out.png differ diff --git a/public/images/zoom_out_pressed.png b/public/images/zoom_out_pressed.png new file mode 100644 index 0000000..7bc7213 Binary files /dev/null and b/public/images/zoom_out_pressed.png differ diff --git a/public/javascripts/.DS_Store b/public/javascripts/.DS_Store new file mode 100644 index 0000000..15c9b5a Binary files /dev/null and b/public/javascripts/.DS_Store differ diff --git a/public/javascripts/heartbeat/default.asp b/public/javascripts/heartbeat/default.asp new file mode 100755 index 0000000..64b17e6 --- /dev/null +++ b/public/javascripts/heartbeat/default.asp @@ -0,0 +1,70 @@ + + + + + ++ Download links: +
+ + +JHeartbeat is a plugin for JQuery 1.0.1 that allows a web page to periodically poll the server. This polling can be used to keep the user's session alive or to download updated information.
+ +To use JHeartbeat, add the following to the HEAD section of your website.
+ ++
++ + ++ <script type="text/javascript" src="heartbeat.js"></script>+
Next, initialize the JTicker plugin by adding the following code:
+ ++
++ + ++ $(document).ready(function() { + $.jheartbeat.set({ + url: "heartbeat.asp", + delay: 3000 + }, function () { + // Callback Function + }); + });+
The options (Bolded) are recommended. You can omit either of them, but then defaults will be used. The callback function (Italics) is optional. When the JHeartbeat object is set, a hidden div with the id of HeartBeatDIV is created. The contents of the "heartbeat" page will be loaded into this div. You can use the callback function to manipulate this data if need be. A sample "heartbeat" page is included in the Zip file.
+ + + + \ No newline at end of file diff --git a/public/javascripts/heartbeat/heartbeat.asp b/public/javascripts/heartbeat/heartbeat.asp new file mode 100755 index 0000000..4fd7837 --- /dev/null +++ b/public/javascripts/heartbeat/heartbeat.asp @@ -0,0 +1,11 @@ +<% +Response.Expires = 3 +Response.Expiresabsolute = Now() - 1 +Response.AddHeader "pragma","no-cache" +Response.AddHeader "cache-control","private" +Response.CacheControl = "no-cache" + +Response.Clear +Response.Write "1" +Response.End +%> \ No newline at end of file diff --git a/public/javascripts/heartbeat/heartbeat.js b/public/javascripts/heartbeat/heartbeat.js new file mode 100755 index 0000000..cf97b32 --- /dev/null +++ b/public/javascripts/heartbeat/heartbeat.js @@ -0,0 +1,43 @@ +/* + * JHeartbeat 0.1.1 Beta + * By Jason Levine (http://www.jasons-toolbox.com) + * A heartbeat plugin for the jquery library to help keep sessions alive. + */ + + $.jheartbeat = { + + options: { + url: "heartbeat_default.asp", + delay: 10000 + }, + + beatfunction: function(){ + + }, + + timeoutobj: { + id: -1 + }, + + set: function(options, onbeatfunction) { + if (this.timeoutobj.id > -1) { + clearTimeout(this.timeoutobj); + } + if (options) { + $.extend(this.options, options); + } + if (onbeatfunction) { + this.beatfunction = onbeatfunction; + } + + // Add the HeartBeatDIV to the page + $("body").append(""); + this.timeoutobj.id = setTimeout("$.jheartbeat.beat();", this.options.delay); + }, + + beat: function() { + $("#HeartBeatDIV").load(this.options.url); + this.timeoutobj.id = setTimeout("$.jheartbeat.beat();", this.options.delay); + this.beatfunction(); + } +}; \ No newline at end of file diff --git a/public/javascripts/home.script.js b/public/javascripts/home.script.js new file mode 100644 index 0000000..91c841f --- /dev/null +++ b/public/javascripts/home.script.js @@ -0,0 +1,37 @@ +$(document).ready(function(){ + + // Login via facebook... + $("div#fb-login").bind('click', function(){ + window.location = 'auth/facebook'; + }); + + // ...or twitter? + $("div#twitter-login").bind('click', function(){ + //window.location = 'auth/twitter'; + }); + + // Show/hide our vid. + $('p.watch').bind('click', function(){ + $('.sirkel-tut').slideToggle(); + + if( $(this).text() == 'New to Sirkel? Watch our getting started tutorial.'){ + $(this).html("Done watching the video?"); + } + else if( $(this).text() == 'Done watching the video?' ){ + $(this).text('Login below.'); + $('p.watch').unbind(); + } + + }); + + $('header p#contact').bind('click', function(){ + //$('html, body').animate({ scrollTop: $('#fb-login').offset().top }, 'slow'); + $("#contact-div").fadeIn(500).delay(5000).fadeOut(500) + }); + + $('header p#about').bind('click', function(){ + //$('html, body').animate({ scrollTop: $('#fb-login').offset().top }, 'slow'); + $("#about-div").fadeIn(500).delay(10000).fadeOut(500) + }); + +}); \ No newline at end of file diff --git a/public/javascripts/jquery.js b/public/javascripts/jquery.js new file mode 100644 index 0000000..5d5a1d5 --- /dev/null +++ b/public/javascripts/jquery.js @@ -0,0 +1,8936 @@ +/*! + * jQuery JavaScript Library v1.6.1 + * http://jquery.com/ + * + * Copyright 2011, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Thu May 12 15:04:36 2011 -0400 + */ +(function( window, undefined ) { + +// Use the correct document accordingly with window argument (sandbox) +var document = window.document, + navigator = window.navigator, + location = window.location; +var jQuery = (function() { + +// Define a local copy of jQuery +var jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // A central reference to the root jQuery(document) + rootjQuery, + + // A simple way to check for HTML strings or ID strings + // (both of which we optimize for) + quickExpr = /^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, + + // Check if a string has a non-whitespace character in it + rnotwhite = /\S/, + + // Used for trimming whitespace + trimLeft = /^\s+/, + trimRight = /\s+$/, + + // Check for digits + rdigit = /\d/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, + rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + + // Useragent RegExp + rwebkit = /(webkit)[ \/]([\w.]+)/, + ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/, + rmsie = /(msie) ([\w.]+)/, + rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/, + + // Keep a UserAgent string for use with jQuery.browser + userAgent = navigator.userAgent, + + // For matching the engine and version of the browser + browserMatch, + + // The deferred used on DOM ready + readyList, + + // The ready event handler + DOMContentLoaded, + + // Save a reference to some core methods + toString = Object.prototype.toString, + hasOwn = Object.prototype.hasOwnProperty, + push = Array.prototype.push, + slice = Array.prototype.slice, + trim = String.prototype.trim, + indexOf = Array.prototype.indexOf, + + // [[Class]] -> type pairs + class2type = {}; + +jQuery.fn = jQuery.prototype = { + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem, ret, doc; + + // Handle $(""), $(null), or $(undefined) + if ( !selector ) { + return this; + } + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + } + + // The body element only exists once, optimize finding it + if ( selector === "body" && !context && document.body ) { + this.context = document; + this[0] = document.body; + this.selector = selector; + this.length = 1; + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + // Are we dealing with HTML string or an ID? + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = quickExpr.exec( selector ); + } + + // Verify a match, and that no context was specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + doc = (context ? context.ownerDocument || context : document); + + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + ret = rsingleTag.exec( selector ); + + if ( ret ) { + if ( jQuery.isPlainObject( context ) ) { + selector = [ document.createElement( ret[1] ) ]; + jQuery.fn.attr.call( selector, context, true ); + + } else { + selector = [ doc.createElement( ret[1] ) ]; + } + + } else { + ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); + selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes; + } + + return jQuery.merge( this, selector ); + + // HANDLE: $("#id") + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return (context || rootjQuery).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if (selector.selector !== undefined) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.6.1", + + // The default length of a jQuery object is 0 + length: 0, + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + toArray: function() { + return slice.call( this, 0 ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + // Build a new jQuery matched element set + var ret = this.constructor(); + + if ( jQuery.isArray( elems ) ) { + push.apply( ret, elems ); + + } else { + jQuery.merge( ret, elems ); + } + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) { + ret.selector = this.selector + (this.selector ? " " : "") + selector; + } else if ( name ) { + ret.selector = this.selector + "." + name + "(" + selector + ")"; + } + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Attach the listeners + jQuery.bindReady(); + + // Add the callback + readyList.done( fn ); + + return this; + }, + + eq: function( i ) { + return i === -1 ? + this.slice( i ) : + this.slice( i, +i + 1 ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ), + "slice", slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + // Either a released hold or an DOMready/load event and not yet ready + if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready, 1 ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger( "ready" ).unbind( "ready" ); + } + } + }, + + bindReady: function() { + if ( readyList ) { + return; + } + + readyList = jQuery._Deferred(); + + // Catch cases where $(document).ready() is called after the + // browser event has already occurred. + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + return setTimeout( jQuery.ready, 1 ); + } + + // Mozilla, Opera and webkit nightlies currently support this event + if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", jQuery.ready, false ); + + // If IE event model is used + } else if ( document.attachEvent ) { + // ensure firing before onload, + // maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", DOMContentLoaded ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", jQuery.ready ); + + // If IE and not a frame + // continually check to see if the document is ready + var toplevel = false; + + try { + toplevel = window.frameElement == null; + } catch(e) {} + + if ( document.documentElement.doScroll && toplevel ) { + doScrollCheck(); + } + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + // A crude way of determining if an object is a window + isWindow: function( obj ) { + return obj && typeof obj === "object" && "setInterval" in obj; + }, + + isNaN: function( obj ) { + return obj == null || !rdigit.test( obj ) || isNaN( obj ); + }, + + type: function( obj ) { + return obj == null ? + String( obj ) : + class2type[ toString.call(obj) ] || "object"; + }, + + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call(obj, "constructor") && + !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + for ( var name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw msg; + }, + + parseJSON: function( data ) { + if ( typeof data !== "string" || !data ) { + return null; + } + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "")) ) { + + return (new Function( "return " + data ))(); + + } + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + // (xml & tmp used internally) + parseXML: function( data , xml , tmp ) { + + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + + tmp = xml.documentElement; + + if ( ! tmp || ! tmp.nodeName || tmp.nodeName === "parsererror" ) { + jQuery.error( "Invalid XML: " + data ); + } + + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && rnotwhite.test( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); + }, + + // args is for internal usage only + each: function( object, callback, args ) { + var name, i = 0, + length = object.length, + isObj = length === undefined || jQuery.isFunction( object ); + + if ( args ) { + if ( isObj ) { + for ( name in object ) { + if ( callback.apply( object[ name ], args ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.apply( object[ i++ ], args ) === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isObj ) { + for ( name in object ) { + if ( callback.call( object[ name ], name, object[ name ] ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) { + break; + } + } + } + } + + return object; + }, + + // Use native String.trim function wherever possible + trim: trim ? + function( text ) { + return text == null ? + "" : + trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + text.toString().replace( trimLeft, "" ).replace( trimRight, "" ); + }, + + // results is for internal usage only + makeArray: function( array, results ) { + var ret = results || []; + + if ( array != null ) { + // The window, strings (and functions) also have 'length' + // The extra typeof function check is to prevent crashes + // in Safari 2 (See: #3039) + // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 + var type = jQuery.type( array ); + + if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) { + push.call( ret, array ); + } else { + jQuery.merge( ret, array ); + } + } + + return ret; + }, + + inArray: function( elem, array ) { + + if ( indexOf ) { + return indexOf.call( array, elem ); + } + + for ( var i = 0, length = array.length; i < length; i++ ) { + if ( array[ i ] === elem ) { + return i; + } + } + + return -1; + }, + + merge: function( first, second ) { + var i = first.length, + j = 0; + + if ( typeof second.length === "number" ) { + for ( var l = second.length; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var ret = [], retVal; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( var i = 0, length = elems.length; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, key, ret = [], + i = 0, + length = elems.length, + // jquery objects are treated as arrays + isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( key in elems ) { + value = callback( elems[ key ], key, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return ret.concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + if ( typeof context === "string" ) { + var tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + var args = slice.call( arguments, 2 ), + proxy = function() { + return fn.apply( context, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; + + return proxy; + }, + + // Mutifunctional method to get and set values to a collection + // The value/s can be optionally by executed if its a function + access: function( elems, key, value, exec, fn, pass ) { + var length = elems.length; + + // Setting many attributes + if ( typeof key === "object" ) { + for ( var k in key ) { + jQuery.access( elems, k, key[k], exec, fn, value ); + } + return elems; + } + + // Setting one attribute + if ( value !== undefined ) { + // Optionally, function values get executed if exec is true + exec = !pass && exec && jQuery.isFunction(value); + + for ( var i = 0; i < length; i++ ) { + fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); + } + + return elems; + } + + // Getting an attribute + return length ? fn( elems[0], key ) : undefined; + }, + + now: function() { + return (new Date()).getTime(); + }, + + // Use of jQuery.browser is frowned upon. + // More details: http://docs.jquery.com/Utilities/jQuery.browser + uaMatch: function( ua ) { + ua = ua.toLowerCase(); + + var match = rwebkit.exec( ua ) || + ropera.exec( ua ) || + rmsie.exec( ua ) || + ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) || + []; + + return { browser: match[1] || "", version: match[2] || "0" }; + }, + + sub: function() { + function jQuerySub( selector, context ) { + return new jQuerySub.fn.init( selector, context ); + } + jQuery.extend( true, jQuerySub, this ); + jQuerySub.superclass = this; + jQuerySub.fn = jQuerySub.prototype = this(); + jQuerySub.fn.constructor = jQuerySub; + jQuerySub.sub = this.sub; + jQuerySub.fn.init = function init( selector, context ) { + if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { + context = jQuerySub( context ); + } + + return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); + }; + jQuerySub.fn.init.prototype = jQuerySub.fn; + var rootjQuerySub = jQuerySub(document); + return jQuerySub; + }, + + browser: {} +}); + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +browserMatch = jQuery.uaMatch( userAgent ); +if ( browserMatch.browser ) { + jQuery.browser[ browserMatch.browser ] = true; + jQuery.browser.version = browserMatch.version; +} + +// Deprecated, use jQuery.browser.webkit instead +if ( jQuery.browser.webkit ) { + jQuery.browser.safari = true; +} + +// IE doesn't match non-breaking spaces with \s +if ( rnotwhite.test( "\xA0" ) ) { + trimLeft = /^[\s\xA0]+/; + trimRight = /[\s\xA0]+$/; +} + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); + +// Cleanup functions for the document ready method +if ( document.addEventListener ) { + DOMContentLoaded = function() { + document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + jQuery.ready(); + }; + +} else if ( document.attachEvent ) { + DOMContentLoaded = function() { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( document.readyState === "complete" ) { + document.detachEvent( "onreadystatechange", DOMContentLoaded ); + jQuery.ready(); + } + }; +} + +// The DOM ready check for Internet Explorer +function doScrollCheck() { + if ( jQuery.isReady ) { + return; + } + + try { + // If IE is used, use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + document.documentElement.doScroll("left"); + } catch(e) { + setTimeout( doScrollCheck, 1 ); + return; + } + + // and execute any waiting functions + jQuery.ready(); +} + +// Expose jQuery to the global object +return jQuery; + +})(); + + +var // Promise methods + promiseMethods = "done fail isResolved isRejected promise then always pipe".split( " " ), + // Static reference to slice + sliceDeferred = [].slice; + +jQuery.extend({ + // Create a simple deferred (one callbacks list) + _Deferred: function() { + var // callbacks list + callbacks = [], + // stored [ context , args ] + fired, + // to avoid firing when already doing so + firing, + // flag to know if the deferred has been cancelled + cancelled, + // the deferred itself + deferred = { + + // done( f1, f2, ...) + done: function() { + if ( !cancelled ) { + var args = arguments, + i, + length, + elem, + type, + _fired; + if ( fired ) { + _fired = fired; + fired = 0; + } + for ( i = 0, length = args.length; i < length; i++ ) { + elem = args[ i ]; + type = jQuery.type( elem ); + if ( type === "array" ) { + deferred.done.apply( deferred, elem ); + } else if ( type === "function" ) { + callbacks.push( elem ); + } + } + if ( _fired ) { + deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] ); + } + } + return this; + }, + + // resolve with given context and args + resolveWith: function( context, args ) { + if ( !cancelled && !fired && !firing ) { + // make sure args are available (#8421) + args = args || []; + firing = 1; + try { + while( callbacks[ 0 ] ) { + callbacks.shift().apply( context, args ); + } + } + finally { + fired = [ context, args ]; + firing = 0; + } + } + return this; + }, + + // resolve with this as context and given arguments + resolve: function() { + deferred.resolveWith( this, arguments ); + return this; + }, + + // Has this deferred been resolved? + isResolved: function() { + return !!( firing || fired ); + }, + + // Cancel + cancel: function() { + cancelled = 1; + callbacks = []; + return this; + } + }; + + return deferred; + }, + + // Full fledged deferred (two callbacks list) + Deferred: function( func ) { + var deferred = jQuery._Deferred(), + failDeferred = jQuery._Deferred(), + promise; + // Add errorDeferred methods, then and promise + jQuery.extend( deferred, { + then: function( doneCallbacks, failCallbacks ) { + deferred.done( doneCallbacks ).fail( failCallbacks ); + return this; + }, + always: function() { + return deferred.done.apply( deferred, arguments ).fail.apply( this, arguments ); + }, + fail: failDeferred.done, + rejectWith: failDeferred.resolveWith, + reject: failDeferred.resolve, + isRejected: failDeferred.isResolved, + pipe: function( fnDone, fnFail ) { + return jQuery.Deferred(function( newDefer ) { + jQuery.each( { + done: [ fnDone, "resolve" ], + fail: [ fnFail, "reject" ] + }, function( handler, data ) { + var fn = data[ 0 ], + action = data[ 1 ], + returned; + if ( jQuery.isFunction( fn ) ) { + deferred[ handler ](function() { + returned = fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise().then( newDefer.resolve, newDefer.reject ); + } else { + newDefer[ action ]( returned ); + } + }); + } else { + deferred[ handler ]( newDefer[ action ] ); + } + }); + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + if ( obj == null ) { + if ( promise ) { + return promise; + } + promise = obj = {}; + } + var i = promiseMethods.length; + while( i-- ) { + obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ]; + } + return obj; + } + }); + // Make sure only one callback list will be used + deferred.done( failDeferred.cancel ).fail( deferred.cancel ); + // Unexpose cancel + delete deferred.cancel; + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + return deferred; + }, + + // Deferred helper + when: function( firstParam ) { + var args = arguments, + i = 0, + length = args.length, + count = length, + deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ? + firstParam : + jQuery.Deferred(); + function resolveFunc( i ) { + return function( value ) { + args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; + if ( !( --count ) ) { + // Strange bug in FF4: + // Values changed onto the arguments object sometimes end up as undefined values + // outside the $.when method. Cloning the object into a fresh array solves the issue + deferred.resolveWith( deferred, sliceDeferred.call( args, 0 ) ); + } + }; + } + if ( length > 1 ) { + for( ; i < length; i++ ) { + if ( args[ i ] && jQuery.isFunction( args[ i ].promise ) ) { + args[ i ].promise().then( resolveFunc(i), deferred.reject ); + } else { + --count; + } + } + if ( !count ) { + deferred.resolveWith( deferred, args ); + } + } else if ( deferred !== firstParam ) { + deferred.resolveWith( deferred, length ? [ firstParam ] : [] ); + } + return deferred.promise(); + } +}); + + + +jQuery.support = (function() { + + var div = document.createElement( "div" ), + documentElement = document.documentElement, + all, + a, + select, + opt, + input, + marginDiv, + support, + fragment, + body, + bodyStyle, + tds, + events, + eventName, + i, + isSupported; + + // Preliminary tests + div.setAttribute("className", "t"); + div.innerHTML = "t |