Introduction
Introduction Statistics Contact Development Disclaimer Help
Commit some of the last minute changes for the WarVOX release - warvox - VoIP b…
Log
Files
Refs
README
---
commit 76fc65ae7c8b0a944971cfcdbdf4dae3bb1cd3ec
parent 4c5bff4f8d4cde61e0435e1a96b92fc707b4cd38
Author: HD Moore <[email protected]>
Date: Wed, 4 Mar 2009 00:15:40 +0000
Commit some of the last minute changes for the WarVOX release
Diffstat:
M Makefile | 5 ++++-
A bin/verify_install.rb | 70 +++++++++++++++++++++++++++++…
M lib/warvox/config.rb | 27 ++++++++++++++++++++++++++-
M lib/warvox/jobs/analysis.rb | 11 ++++++++++-
M lib/warvox/jobs/dialer.rb | 13 ++++++++++---
M web/app/controllers/dial_jobs_cont… | 13 ++++++++++++-
M web/app/controllers/providers_cont… | 4 ++++
M web/app/models/dial_job.rb | 12 ++++++++++++
M web/app/models/provider.rb | 2 +-
M web/app/views/analyze/index.html.e… | 2 ++
M web/app/views/analyze/view.html.erb | 10 ++++------
M web/app/views/dial_jobs/index.html… | 8 ++++++++
M web/app/views/dial_jobs/new.html.e… | 4 ++++
M web/app/views/dial_results/analyze… | 2 ++
M web/app/views/dial_results/edit.ht… | 4 ++++
M web/app/views/dial_results/index.h… | 2 ++
M web/app/views/dial_results/show.ht… | 5 +++++
M web/app/views/dial_results/view.ht… | 2 ++
M web/app/views/home/about.html.erb | 1 -
M web/app/views/home/index.html.erb | 11 ++++++++---
M web/app/views/layouts/warvox.html.… | 6 +++---
M web/app/views/providers/edit.html.… | 5 ++++-
M web/app/views/providers/index.html… | 3 ++-
M web/app/views/providers/new.html.e… | 1 -
M web/app/views/providers/show.html.… | 4 ++++
M web/config/environment.rb | 5 ++++-
A web/db/migrate/20090303204859_add_… | 9 +++++++++
A web/db/migrate/20090303204917_add_… | 9 +++++++++
A web/db/migrate/20090303225838_add_… | 9 +++++++++
M web/db/schema.rb | 5 ++++-
A web/public/images/close.gif | 0
A web/public/images/loading.gif | 0
A web/public/images/overlay.png | 0
A web/public/javascripts/lightbox.js | 426 +++++++++++++++++++++++++++++…
A web/public/stylesheets/lightbox.css | 27 +++++++++++++++++++++++++++
35 files changed, 691 insertions(+), 26 deletions(-)
---
diff --git a/Makefile b/Makefile
@@ -1,5 +1,8 @@
-all: install
+all: test
+test: install
+ bin/verify_install.rb
+
install: iaxrecord ruby-kissfft db
cp -a src/iaxrecord/iaxrecord bin/
diff --git a/bin/verify_install.rb b/bin/verify_install.rb
@@ -0,0 +1,70 @@
+#!/usr/bin/env ruby
+###################
+
+#
+# Load the library path
+#
+base = __FILE__
+while File.symlink?(base)
+ base = File.expand_path(File.readlink(base), File.dirname(base))
+end
+$:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib'))
+require 'warvox'
+
+#
+# Verify that WarVOX has been installed properly
+#
+
+puts("**********************************************************************")
+puts("* *")
+puts("* WarVOX Installation Verifier *")
+puts("* *")
+puts("**********************************************************************")
+puts(" ")
+
+begin
+ require 'kissfft'
+ puts "[*] The KissFFT module appears to be available"
+rescue ::LoadError
+ puts "[*] ERROR: The KissFFT module has not been installed"
+ exit
+end
+
+sox_path = WarVOX::Config.tool_path('sox')
+if(not sox_path)
+ puts "[*] ERROR: The 'sox' binary could not be found"
+ exit
+end
+
+sox_data = `#{sox_path} --help 2>&1`
+if(sox_data !~ /SUPPORTED FILE FORMATS.*raw/)
+ puts "[*] ERROR: The 'sox' binary does not have support for RAW audio"
+ exit
+end
+puts "[*] The SOX binary appears to be available with RAW file support"
+
+
+if(not WarVOX::Config.tool_path('gnuplot'))
+ puts "[*] ERROR: The 'gnuplot' binary could not be installed"
+ exit
+end
+puts "[*] The GNUPlot binary appears to be available"
+
+if(not WarVOX::Config.tool_path('lame'))
+ puts "[*] ERROR: The 'lame' binary could not be installed"
+ exit
+end
+puts "[*] The LAME binary appears to be available"
+
+
+if(not WarVOX::Config.tool_path('iaxrecord'))
+ puts "[*] ERROR: The 'iaxrecord' binary could not be installed"
+ exit
+end
+puts "[*] The IAXRECORD binary appears to be available"
+
+
+puts " "
+puts "[*] Congratulations! You are now ready to run WarVOX"
+puts "[*] Start WarVOX with bin/warvox.rb"
+puts " "
diff --git a/lib/warvox/config.rb b/lib/warvox/config.rb
@@ -62,7 +62,32 @@ module Config
}
end
return nil
- end
+ end
+
+ # This method prevents two installations of WarVOX from using the same
+ # rails session key. The first time this method is called, it generates
+ # a new key and stores it in the rails directory, afterwards this key
+ # will be used every time.
+ def self.load_session_key
+ kfile = File.join(WarVOX::Base, 'web', 'config', 'session.key')
+ if(not File.exists?(kfile))
+ # XXX: assume /dev/urandom exists
+ kdata = File.read('/dev/urandom', 64).unpack("H*")[0]
+
+ # Create the new session key file
+ fd = File.new(kfile, 'w')
+
+ # Make this file mode 0600
+ File.chmod(0600, kfile)
+
+ # Write it and close
+ fd.write(kdata)
+ fd.close
+ return kdata
+ end
+ File.read(kfile)
+ end
+
end
end
diff --git a/lib/warvox/jobs/analysis.rb b/lib/warvox/jobs/analysis.rb
@@ -3,7 +3,13 @@ module Jobs
class Analysis < Base
require 'fileutils'
- require 'kissfft'
+
+ @@kissfft_loaded = false
+ begin
+ require 'kissfft'
+ @kissfft_loaded = true
+ rescue ::LoadError
+ end
def type
'analysis'
@@ -11,6 +17,9 @@ class Analysis < Base
def initialize(job_id)
@name = job_id
+ if(not @@kissfft_loaded)
+ raise RuntimeError, "The KissFFT module is not availab…
+ end
end
def get_job
diff --git a/lib/warvox/jobs/dialer.rb b/lib/warvox/jobs/dialer.rb
@@ -15,7 +15,12 @@ class Dialer < Base
@seconds = model.seconds
@lines = model.lines
@nums = shuffle_a(WarVOX::Phone.crack_mask(@range))
- @cid = '8005551212' # XXX: Read from job
+
+ # CallerID modes (SELF or a mask)
+ @cid_self = model.cid_mask == 'SELF'
+ if(not @cid_self)
+ @cid_range = WarVOX::Phone.crack_mask(model.cid_mask)
+ end
end
#
@@ -39,7 +44,7 @@ class Dialer < Base
def get_providers
res = []
- ::Provider.find(:all).each do |prov|
+ ::Provider.find_all_by_enabled(true).each do |prov|
info = {
:name => prov.name,
:id => prov.id,
@@ -116,6 +121,7 @@ class Dialer < Base
fail = 1
byte = 0
path = ''
+ cid = @cid_self ? num : @cid_range[ r…
IO.popen(
[
@@ -123,7 +129,7 @@ class Dialer < Base
prov[:host],
prov[:user],
prov[:pass],
- @cid,
+ cid,
out,
num,
@seconds
@@ -144,6 +150,7 @@ class Dialer < Base
res = ::DialResult.new
res.number = num
+ res.cid = cid
res.dial_job_id = @name
res.provider_id = prov[:id]
res.completed = (fail == 0) ? true : f…
diff --git a/web/app/controllers/dial_jobs_controller.rb b/web/app/controllers/…
@@ -55,13 +55,24 @@ class DialJobsController < ApplicationController
# POST /dial_jobs
# POST /dial_jobs.xml
def create
- @dial_job = DialJob.new(params[:dial_job])
+
+ @dial_job = DialJob.new(params[:dial_job])
+
+ if(Provider.find_all_by_enabled(true).length == 0)
+ @dial_job.errors.add("No providers have been configured or ena…
+ respond_to do |format|
+ format.html { render :action => "new" }
+ format.xml { render :xml => @dial_job.errors, :status…
+ end
+ return
+ end
@dial_job.status = 'submitted'
@dial_job.progress = 0
@dial_job.started_at = nil
@dial_job.completed_at = nil
@dial_job.range.gsub!(/[^0-9X]/, '')
+ @dial_job.cid_mask.gsub!(/[^0-9X]/, '') if @dial_job.cid_mask != "SELF"
respond_to do |format|
if @dial_job.save
diff --git a/web/app/controllers/providers_controller.rb b/web/app/controllers/…
@@ -6,6 +6,8 @@ class ProvidersController < ApplicationController
def index
@providers = Provider.find(:all)
@new_provider = Provider.new
+ @new_provider.enabled = true
+
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @providers }
@@ -27,6 +29,7 @@ class ProvidersController < ApplicationController
# GET /providers/new.xml
def new
@provider = Provider.new
+ @provider.enabled = true
respond_to do |format|
format.html # new.html.erb
@@ -43,6 +46,7 @@ class ProvidersController < ApplicationController
# POST /providers.xml
def create
@provider = Provider.new(params[:provider])
+ @provider.enabled = true
respond_to do |format|
if @provider.save
diff --git a/web/app/models/dial_job.rb b/web/app/models/dial_job.rb
@@ -9,5 +9,17 @@ class DialJob < ActiveRecord::Base
if(range.gsub(/[^0-9X]/, '').length != 10)
errors.add(:range, "The range must be exactly 10 chara…
end
+
+ if(range.scan(/X/).length > 5)
+ errors.add(:range, "The range must contain no more tha…
+ end
+
+ if(cid_mask != "SELF" and cid_mask.gsub(/[^0-9X]/, '').length …
+ errors.add(:range, "The Caller ID must be exactly 10 c…
+ end
+
+ if(cid_mask != "SELF" and cid_mask.scan(/X/).length > 5)
+ errors.add(:range, "The Caller ID must contain no more…
+ end
end
end
diff --git a/web/app/models/provider.rb b/web/app/models/provider.rb
@@ -3,5 +3,5 @@ class Provider < ActiveRecord::Base
validates_presence_of :name, :host, :port, :user, :pass, :lines
validates_numericality_of :port, :less_than => 65536, :greater_than =>…
- validates_numericality_of :lines, :less_than => 255, :greater_than => 1
+ validates_numericality_of :lines, :less_than => 255, :greater_than => 0
end
diff --git a/web/app/views/analyze/index.html.erb b/web/app/views/analyze/index…
@@ -6,6 +6,7 @@
<tr>
<th>ID</th>
<th>Range</th>
+ <th>CallerID</th>
<th>Connected</th>
<th>Date</th>
</tr>
@@ -14,6 +15,7 @@
<tr>
<td><%=h dial_job.id %></td>
<td><%=h dial_job.range %></td>
+ <td><%=h dial_job.cid_mask %></td>
<td><%=h DialResult.find(:all, :conditions => ['dial_job_id = ? and proces…
<td><%=h dial_job.started_at.localtime.strftime("%Y-%m-%d %H:%M:%S %Z") %>…
<td><%= link_to 'View', view_analyze_path(dial_job) %></td>
diff --git a/web/app/views/analyze/view.html.erb b/web/app/views/analyze/view.h…
@@ -15,6 +15,7 @@
<th>Signal</th>
<th>Spectrum</th>
<th>Number</th>
+ <th>CallerID</th>
<th>Provider</th>
<th>Call Time</th>
<th>Ring Time</th>
@@ -24,18 +25,15 @@
<tr>
<td><%=h dial_result.id %></td>
<td>
- <a href="<%=resource_analyze_path(@job_id)%>?result_id=<%= dia…
- <img src="<%=resource_analyze_path(@job_id)%>?result_id=<%= di…
- </a>
+ <a href="<%=resource_analyze_path(@job_id)%>?result_id=<%= dia…
</td>
<td>
- <a href="<%=resource_analyze_path(@job_id)%>?result_id=<%= dia…
- <img src="<%=resource_analyze_path(@job_id)%>?result_id=<%= di…
- </a>
+ <a href="<%=resource_analyze_path(@job_id)%>?result_id=<%= dia…
</td>
<td>
<a href="<%=resource_analyze_path(@job_id)%>?result_id=<%= dia…
</td>
+ <td><%=h dial_result.cid %></td>
<td><%=h dial_result.provider.name %></td>
<td><%=h dial_result.seconds %></td>
<td><%=h dial_result.ringtime %></td>
diff --git a/web/app/views/dial_jobs/index.html.erb b/web/app/views/dial_jobs/i…
@@ -6,6 +6,7 @@
<tr>
<th>ID</th>
<th>Range</th>
+ <th>CallerID</th>
<th>Seconds</th>
<th>Lines</th>
<th>Submitted Time</th>
@@ -15,6 +16,7 @@
<tr>
<td><%=h dial_job.id %></td>
<td><%=h dial_job.range %></td>
+ <td><%=h dial_job.cid_mask %></td>
<td><%=h dial_job.seconds %></td>
<td><%=h dial_job.lines %></td>
<td><%=h dial_job.created_at.localtime.strftime("%Y-%m-%d %H:%M:%S %Z") %>…
@@ -35,6 +37,7 @@
<tr>
<th>ID</th>
<th>Range</th>
+ <th>CallerID</th>
<th>Seconds</th>
<th>Lines</th>
<th>Status</th>
@@ -46,6 +49,7 @@
<tr class='active_job_row'>
<td><%=h dial_job.id %></td>
<td><%=h dial_job.range %></td>
+ <td><%=h dial_job.cid_mask %></td>
<td><%=h dial_job.seconds %></td>
<td><%=h dial_job.lines %></td>
<td><%=h dial_job.status %></td>
@@ -85,6 +89,10 @@
<%= f.text_field :lines, :value => 10 %>
</p>
<p>
+ <%= f.label :lines, 'The source Caller ID range (555-555-55XX)' %><br />
+ <%= f.text_field :cid_mask, :value => '000-000-XXXX' %>
+ </p>
+ <p>
<%= f.submit "Create" %>
</p>
<% end %>
diff --git a/web/app/views/dial_jobs/new.html.erb b/web/app/views/dial_jobs/new…
@@ -15,6 +15,10 @@
<%= f.text_field :lines, :value => 10 %>
</p>
<p>
+ <%= f.label :lines, 'The source Caller ID range (555-555-55XX or SELF)' %>…
+ <%= f.text_field :cid_mask, :value => '000-000-XXXX' %>
+ </p>
+ <p>
<%= f.submit "Create" %>
</p>
<% end %>
diff --git a/web/app/views/dial_results/analyze.html.rb b/web/app/views/dial_re…
@@ -12,6 +12,7 @@
<table class='table_scaffold' width='100%'>
<tr>
<th>Number</th>
+ <th>CallerID</th>
<th>Provider</th>
<th>Call Time</th>
<th>Ring Time</th>
@@ -20,6 +21,7 @@
<% for dial_result in @dial_data_todo.sort{|a,b| a.number <=> b.number } %>
<tr>
<td><%=h dial_result.number %></td>
+ <td><%=h dial_result.cid %></td>
<td><%=h dial_result.provider.name %></td>
<td><%=h dial_result.seconds %></td>
<td><%=h dial_result.ringtime %></td>
diff --git a/web/app/views/dial_results/edit.html.erb b/web/app/views/dial_resu…
@@ -8,6 +8,10 @@
<%= f.text_field :number %>
</p>
<p>
+ <%= f.label :cid %><br />
+ <%= f.text_field :cid %>
+ </p>
+ <p>
<%= f.label :dial_job_id %><br />
<%= f.text_field :dial_job_id %>
</p>
diff --git a/web/app/views/dial_results/index.html.erb b/web/app/views/dial_res…
@@ -5,6 +5,7 @@
<tr>
<th>ID</th>
<th>Range</th>
+ <th>CallerID</th>
<th>Seconds</th>
<th>Lines</th>
<th>Started</th>
@@ -14,6 +15,7 @@
<tr>
<td><%=h dial_job.id %></td>
<td><%=h dial_job.range %></td>
+ <td><%=h dial_job.cid_mask %></td>
<td><%=h dial_job.seconds %></td>
<td><%=h dial_job.lines %></td>
<td><%=h dial_job.started_at.localtime.strftime("%Y-%m-%d %H:%M:%S %Z") %>…
diff --git a/web/app/views/dial_results/show.html.erb b/web/app/views/dial_resu…
@@ -4,6 +4,11 @@
</p>
<p>
+ <b>CallerID:</b>
+ <%=h @dial_result.cid %>
+</p>
+
+<p>
<b>Dial job:</b>
<%=h @dial_result.dial_job_id %>
</p>
diff --git a/web/app/views/dial_results/view.html.erb b/web/app/views/dial_resu…
@@ -14,6 +14,7 @@
<table class='table_scaffold' width='100%'>
<tr>
<th>Number</th>
+ <th>CallerID</th>
<th>Provider</th>
<th>Completed</th>
<th>Busy</th>
@@ -25,6 +26,7 @@
<% for dial_result in @dial_results.sort{|a,b| a.number <=> b.number } %>
<tr>
<td><%=h dial_result.number %></td>
+ <td><%=h dial_result.cid %></td>
<td><%=h dial_result.provider.name %></td>
<td><%=h dial_result.completed %></td>
<td><%=h dial_result.busy %></td>
diff --git a/web/app/views/home/about.html.erb b/web/app/views/home/about.html.…
@@ -1 +0,0 @@
-WarVOX is a project created by H D Moore for Metasploit LLC
diff --git a/web/app/views/home/index.html.erb b/web/app/views/home/index.html.…
@@ -4,9 +4,14 @@
<h1 class='title'>Introduction</h1>
<b>WarVOX</b> is a suite of tools for exploring, classifying, and auditing the
-telephone system. Unlike normal wardialing tools, WarVOX can be used to assess
-voice lines as well as data. To get started, configure one or more <b>Provider…
-and submit a new <b>Job</b>.
+telephone system. Unlike normal wardialing tools, WarVOX works with the actual
+audio from each call and does not use a modem directly. This model allows
+WarVOX to find and classify a wide range of devices, including modems, through
+direct data analysis. To get started, configure an IAX-capable VoIP provider
+in the <b>Providers</b> section, then submit a new <b>Job</b>. Keep in mind
+that the laws regulating automated dialing can vary by location, it is your
+responsibility to ensure that your local laws and the laws governing the
+target telephone range are respected.
</td><td valign='top' align='center'>
diff --git a/web/app/views/layouts/warvox.html.erb b/web/app/views/layouts/warv…
@@ -5,16 +5,16 @@
<title><%= @title || "WarVOX" %></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Language" content="en-US" />
- <%= stylesheet_link_tag 'global' %>
+ <%= stylesheet_link_tag 'global', 'lightbox' %>
<!--[if IE 7]><%= stylesheet_link_tag 'ie7' %><![endif]-->
- <%= javascript_include_tag 'custom', 'prototype', 'effects', 'FusionCharts…
+ <%= javascript_include_tag 'custom', 'prototype', 'effects', 'FusionCharts…
</head>
<body>
<div id="header">
<%= render :partial => 'shared/header' %>
-</div>
+</div>
<div id="content">
<div class="box_full">
diff --git a/web/app/views/providers/edit.html.erb b/web/app/views/providers/ed…
@@ -2,7 +2,10 @@
<% form_for(@provider) do |f| %>
<%= f.error_messages %>
-
+ <p>
+ <%= f.label :enabled %><br />
+ <%= f.check_box :enabled %>
+ </p>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
diff --git a/web/app/views/providers/index.html.erb b/web/app/views/providers/i…
@@ -2,6 +2,7 @@
<h1 class='title'>Providers</h1>
<table class='table_scaffold' width='100%'>
<tr>
+ <th>Enabled</th>
<th>Name</th>
<th>Host</th>
<th>Port</th>
@@ -12,6 +13,7 @@
<% for provider in @providers %>
<tr>
+ <td><%=h provider.enabled %></td>
<td><%=h provider.name %></td>
<td><%=h provider.host %></td>
<td><%=h provider.port %></td>
@@ -37,7 +39,6 @@
<% form_for(@new_provider) do |f| %>
<%= f.error_messages %>
-
<p>
<%= f.label :name, 'The nickname for this provider' %><br />
<%= f.text_field :name %>
diff --git a/web/app/views/providers/new.html.erb b/web/app/views/providers/new…
@@ -2,7 +2,6 @@
<% form_for(@provider) do |f| %>
<%= f.error_messages %>
-
<p>
<%= f.label :name, 'The nickname for this provider' %><br />
<%= f.text_field :name %>
diff --git a/web/app/views/providers/show.html.erb b/web/app/views/providers/sh…
@@ -1,5 +1,9 @@
<h1 class='title'>View Provider</h1>
<p>
+ <b>Enabled:</b>
+ <%=h @provider.enabled %>
+</p>
+<p>
<b>Name:</b>
<%=h @provider.name %>
</p>
diff --git a/web/config/environment.rb b/web/config/environment.rb
@@ -58,9 +58,12 @@ Rails::Initializer.run do |config|
# If you change this key, all old sessions 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.
+ #
+ # WarVOX generates a new key for each installation
+ # This file is stored under web/config/session.key
config.action_controller.session = {
:session_key => '_warvox',
- :secret => 'feada588709ac0fd51987c264ad6fba018be0e3a579547b9c9978098a…
+ :secret => WarVOX::Config.load_session_key
}
# Use the database for sessions instead of the cookie-based default,
diff --git a/web/db/migrate/20090303204859_add_cid_mask_to_dial_jobs.rb b/web/d…
@@ -0,0 +1,9 @@
+class AddCidMaskToDialJobs < ActiveRecord::Migration
+ def self.up
+ add_column :dial_jobs, :cid_mask, :string
+ end
+
+ def self.down
+ remove_column :dial_jobs, :cid_mask
+ end
+end
diff --git a/web/db/migrate/20090303204917_add_cid_to_dial_results.rb b/web/db/…
@@ -0,0 +1,9 @@
+class AddCidToDialResults < ActiveRecord::Migration
+ def self.up
+ add_column :dial_results, :cid, :string
+ end
+
+ def self.down
+ remove_column :dial_results, :cid
+ end
+end
diff --git a/web/db/migrate/20090303225838_add_enabled_to_providers.rb b/web/db…
@@ -0,0 +1,9 @@
+class AddEnabledToProviders < ActiveRecord::Migration
+ def self.up
+ add_column :providers, :enabled, :boolean
+ end
+
+ def self.down
+ remove_column :providers, :enabled
+ end
+end
diff --git a/web/db/schema.rb b/web/db/schema.rb
@@ -9,7 +9,7 @@
#
# It's strongly recommended to check this file into your version control syste…
-ActiveRecord::Schema.define(:version => 20090301084459) do
+ActiveRecord::Schema.define(:version => 20090303225838) do
create_table "dial_jobs", :force => true do |t|
t.string "range"
@@ -22,6 +22,7 @@ ActiveRecord::Schema.define(:version => 20090301084459) do
t.boolean "processed"
t.datetime "created_at"
t.datetime "updated_at"
+ t.string "cid_mask"
end
create_table "dial_results", :force => true do |t|
@@ -37,6 +38,7 @@ ActiveRecord::Schema.define(:version => 20090301084459) do
t.datetime "created_at"
t.datetime "updated_at"
t.datetime "processed_at"
+ t.string "cid"
end
create_table "providers", :force => true do |t|
@@ -48,6 +50,7 @@ ActiveRecord::Schema.define(:version => 20090301084459) do
t.integer "lines"
t.datetime "created_at"
t.datetime "updated_at"
+ t.boolean "enabled"
end
end
diff --git a/web/public/images/close.gif b/web/public/images/close.gif
Binary files differ.
diff --git a/web/public/images/loading.gif b/web/public/images/loading.gif
Binary files differ.
diff --git a/web/public/images/overlay.png b/web/public/images/overlay.png
Binary files differ.
diff --git a/web/public/javascripts/lightbox.js b/web/public/javascripts/lightb…
@@ -0,0 +1,426 @@
+/*
+ Lightbox JS: Fullsize Image Overlays
+ by Lokesh Dhakar - http://www.huddletogether.com
+
+ For more information on this script, visit:
+ http://huddletogether.com/projects/lightbox/
+
+ Script featured on Dynamic Drive code library Jan 24th, 06':
+ http://www.dynamicdrive.com
+
+ Licensed under the Creative Commons Attribution 2.5 License - http://c…
+ (basically, do anything you want, just leave my name and link)
+
+ Table of Contents
+ -----------------
+ Configuration
+
+ Functions
+ - getPageScroll()
+ - getPageSize()
+ - pause()
+ - getKey()
+ - listenKey()
+ - showLightbox()
+ - hideLightbox()
+ - initLightbox()
+ - addLoadEvent()
+
+ Function Calls
+ - addLoadEvent(initLightbox)
+
+*/
+
+
+
+//
+// Configuration
+//
+
+// If you would like to use a custom loading image or close button reference t…
+var loadingImage = '/images/loading.gif';
+var closeButton = '/images/close.gif';
+
+
+
+
+
+//
+// getPageScroll()
+// Returns array with x,y page scroll values.
+// Core code from - quirksmode.org
+//
+function getPageScroll(){
+
+ var yScroll;
+
+ if (self.pageYOffset) {
+ yScroll = self.pageYOffset;
+ } else if (document.documentElement && document.documentElement.scroll…
+ yScroll = document.documentElement.scrollTop;
+ } else if (document.body) {// all other Explorers
+ yScroll = document.body.scrollTop;
+ }
+
+ arrayPageScroll = new Array('',yScroll)
+ return arrayPageScroll;
+}
+
+
+
+//
+// getPageSize()
+// Returns array with page width, height and window width, height
+// Core code from - quirksmode.org
+// Edit for Firefox by pHaez
+//
+function getPageSize(){
+
+ var xScroll, yScroll;
+
+ if (window.innerHeight && window.scrollMaxY) {
+ xScroll = document.body.scrollWidth;
+ yScroll = window.innerHeight + window.scrollMaxY;
+ } else if (document.body.scrollHeight > document.body.offsetHeight){ /…
+ xScroll = document.body.scrollWidth;
+ yScroll = document.body.scrollHeight;
+ } else { // Explorer Mac...would also work in Explorer 6 Strict, Mozil…
+ xScroll = document.body.offsetWidth;
+ yScroll = document.body.offsetHeight;
+ }
+
+ var windowWidth, windowHeight;
+ if (self.innerHeight) { // all except Explorer
+ windowWidth = self.innerWidth;
+ windowHeight = self.innerHeight;
+ } else if (document.documentElement && document.documentElement.client…
+ windowWidth = document.documentElement.clientWidth;
+ windowHeight = document.documentElement.clientHeight;
+ } else if (document.body) { // other Explorers
+ windowWidth = document.body.clientWidth;
+ windowHeight = document.body.clientHeight;
+ }
+
+ // for small pages with total height less then height of the viewport
+ if(yScroll < windowHeight){
+ pageHeight = windowHeight;
+ } else {
+ pageHeight = yScroll;
+ }
+
+ // for small pages with total width less then width of the viewport
+ if(xScroll < windowWidth){
+ pageWidth = windowWidth;
+ } else {
+ pageWidth = xScroll;
+ }
+
+
+ arrayPageSize = new Array(pageWidth,pageHeight,windowWidth,windowHeigh…
+ return arrayPageSize;
+}
+
+
+//
+// pause(numberMillis)
+// Pauses code execution for specified time. Uses busy code, not good.
+// Code from http://www.faqts.com/knowledge_base/view.phtml/aid/1602
+//
+function pause(numberMillis) {
+ var now = new Date();
+ var exitTime = now.getTime() + numberMillis;
+ while (true) {
+ now = new Date();
+ if (now.getTime() > exitTime)
+ return;
+ }
+}
+
+//
+// getKey(key)
+// Gets keycode. If 'x' is pressed then it hides the lightbox.
+//
+
+function getKey(e){
+ if (e == null) { // ie
+ keycode = event.keyCode;
+ } else { // mozilla
+ keycode = e.which;
+ }
+ key = String.fromCharCode(keycode).toLowerCase();
+
+ if(key == 'x'){ hideLightbox(); }
+}
+
+
+//
+// listenKey()
+//
+function listenKey () { document.onkeypress = getKey; }
+
+
+//
+// showLightbox()
+// Preloads images. Pleaces new image in lightbox then centers and displays.
+//
+function showLightbox(objLink)
+{
+ // prep objects
+ var objOverlay = document.getElementById('overlay');
+ var objLightbox = document.getElementById('lightbox');
+ var objCaption = document.getElementById('lightboxCaption');
+ var objImage = document.getElementById('lightboxImage');
+ var objLoadingImage = document.getElementById('loadingImage');
+ var objLightboxDetails = document.getElementById('lightboxDetails');
+
+
+ var arrayPageSize = getPageSize();
+ var arrayPageScroll = getPageScroll();
+
+ // center loadingImage if it exists
+ if (objLoadingImage) {
+ objLoadingImage.style.top = (arrayPageScroll[1] + ((arrayPageS…
+ objLoadingImage.style.left = (((arrayPageSize[0] - 20 - objLoa…
+ objLoadingImage.style.display = 'block';
+ }
+
+ // set height of Overlay to take up whole page and show
+ objOverlay.style.height = (arrayPageSize[1] + 'px');
+ objOverlay.style.display = 'block';
+
+ // preload image
+ imgPreload = new Image();
+
+ imgPreload.onload=function(){
+ objImage.src = objLink.href;
+
+ // center lightbox and make sure that the top and left values …
+ // and the image placed outside the viewport
+ var lightboxTop = arrayPageScroll[1] + ((arrayPageSize[3] - 35…
+ var lightboxLeft = ((arrayPageSize[0] - 20 - imgPreload.width)…
+
+ objLightbox.style.top = (lightboxTop < 0) ? "0px" : lightboxTo…
+ objLightbox.style.left = (lightboxLeft < 0) ? "0px" : lightbox…
+
+
+ objLightboxDetails.style.width = imgPreload.width + 'px';
+
+ if(objLink.getAttribute('title')){
+ objCaption.style.display = 'block';
+ //objCaption.style.width = imgPreload.width + 'px';
+ objCaption.innerHTML = objLink.getAttribute('title');
+ } else {
+ objCaption.style.display = 'none';
+ }
+
+ // A small pause between the image loading and displaying is r…
+ // this prevents the previous image displaying for a short bur…
+ if (navigator.appVersion.indexOf("MSIE")!=-1){
+ pause(250);
+ }
+
+ if (objLoadingImage) { objLoadingImage.style.display = …
+ objLightbox.style.display = 'block';
+
+ // After image is loaded, update the overlay height as the new…
+ // increased the overall page height.
+ arrayPageSize = getPageSize();
+ objOverlay.style.height = (arrayPageSize[1] + 'px');
+
+ // Check for 'x' keypress
+ listenKey();
+
+ return false;
+ }
+
+ imgPreload.src = objLink.href;
+
+}
+
+
+
+
+
+//
+// hideLightbox()
+//
+function hideLightbox()
+{
+ // get objects
+ objOverlay = document.getElementById('overlay');
+ objLightbox = document.getElementById('lightbox');
+
+ // hide lightbox and overlay
+ objOverlay.style.display = 'none';
+ objLightbox.style.display = 'none';
+
+ // disable keypress listener
+ document.onkeypress = '';
+}
+
+
+
+
+//
+// initLightbox()
+// Function runs on window load, going through link tags looking for rel="ligh…
+// These links receive onclick events that enable the lightbox display for the…
+// The function also inserts html markup at the top of the page which will be …
+// container for the overlay pattern and the inline image.
+//
+function initLightbox()
+{
+
+ if (!document.getElementsByTagName){ return; }
+ var anchors = document.getElementsByTagName("a");
+
+ // loop through all anchor tags
+ for (var i=0; i<anchors.length; i++){
+ var anchor = anchors[i];
+
+ if (anchor.getAttribute("href") && (anchor.getAttribute("rel")…
+ anchor.onclick = function () {showLightbox(this); retu…
+ }
+ }
+
+ // the rest of this code inserts html at the top of the page that look…
+ //
+ // <div id="overlay">
+ // <a href="#" onclick="hideLightbox(); return false;">…
+ // </div>
+ // <div id="lightbox">
+ // <a href="#" onclick="hideLightbox(); return false;" …
+ // <img id="closeButton" />
+ // <img id="lightboxImage" />
+ // </a>
+ // <div id="lightboxDetails">
+ // <div id="lightboxCaption"></div>
+ // <div id="keyboardMsg"></div>
+ // </div>
+ // </div>
+
+ var objBody = document.getElementsByTagName("body").item(0);
+
+ // create overlay div and hardcode some functional styles (aesthetic s…
+ var objOverlay = document.createElement("div");
+ objOverlay.setAttribute('id','overlay');
+ objOverlay.onclick = function () {hideLightbox(); return false;}
+ objOverlay.style.display = 'none';
+ objOverlay.style.position = 'absolute';
+ objOverlay.style.top = '0';
+ objOverlay.style.left = '0';
+ objOverlay.style.zIndex = '90';
+ objOverlay.style.width = '100%';
+ objBody.insertBefore(objOverlay, objBody.firstChild);
+
+ var arrayPageSize = getPageSize();
+ var arrayPageScroll = getPageScroll();
+
+ // preload and create loader image
+ var imgPreloader = new Image();
+
+ // if loader image found, create link to hide lightbox and create load…
+ imgPreloader.onload=function(){
+
+ var objLoadingImageLink = document.createElement("a");
+ objLoadingImageLink.setAttribute('href','#');
+ objLoadingImageLink.onclick = function () {hideLightbox(); ret…
+ objOverlay.appendChild(objLoadingImageLink);
+
+ var objLoadingImage = document.createElement("img");
+ objLoadingImage.src = loadingImage;
+ objLoadingImage.setAttribute('id','loadingImage');
+ objLoadingImage.style.position = 'absolute';
+ objLoadingImage.style.zIndex = '150';
+ objLoadingImageLink.appendChild(objLoadingImage);
+
+ imgPreloader.onload=function(){}; // clear onLoa…
+
+ return false;
+ }
+
+ imgPreloader.src = loadingImage;
+
+ // create lightbox div, same note about styles as above
+ var objLightbox = document.createElement("div");
+ objLightbox.setAttribute('id','lightbox');
+ objLightbox.style.display = 'none';
+ objLightbox.style.position = 'absolute';
+ objLightbox.style.zIndex = '100';
+ objBody.insertBefore(objLightbox, objOverlay.nextSibling);
+
+ // create link
+ var objLink = document.createElement("a");
+ objLink.setAttribute('href','#');
+ objLink.setAttribute('title','Click to close');
+ objLink.onclick = function () {hideLightbox(); return false;}
+ objLightbox.appendChild(objLink);
+
+ // preload and create close button image
+ var imgPreloadCloseButton = new Image();
+
+ // if close button image found,
+ imgPreloadCloseButton.onload=function(){
+
+ var objCloseButton = document.createElement("img");
+ objCloseButton.src = closeButton;
+ objCloseButton.setAttribute('id','closeButton');
+ objCloseButton.style.position = 'absolute';
+ objCloseButton.style.zIndex = '200';
+ objLink.appendChild(objCloseButton);
+
+ return false;
+ }
+
+ imgPreloadCloseButton.src = closeButton;
+
+ // create image
+ var objImage = document.createElement("img");
+ objImage.setAttribute('id','lightboxImage');
+ objLink.appendChild(objImage);
+
+ // create details div, a container for the caption and keyboard message
+ var objLightboxDetails = document.createElement("div");
+ objLightboxDetails.setAttribute('id','lightboxDetails');
+ objLightbox.appendChild(objLightboxDetails);
+
+ // create caption
+ var objCaption = document.createElement("div");
+ objCaption.setAttribute('id','lightboxCaption');
+ objCaption.style.display = 'none';
+ objLightboxDetails.appendChild(objCaption);
+
+ // create keyboard message
+ var objKeyboardMsg = document.createElement("div");
+ objKeyboardMsg.setAttribute('id','keyboardMsg');
+ objKeyboardMsg.innerHTML = 'press <kbd>x</kbd> to close';
+ objLightboxDetails.appendChild(objKeyboardMsg);
+
+
+}
+
+
+
+
+//
+// addLoadEvent()
+// Adds event to window.onload without overwriting currently assigned onload f…
+// Function found at Simon Willison's weblog - http://simon.incutio.com/
+//
+function addLoadEvent(func)
+{
+ var oldonload = window.onload;
+ if (typeof window.onload != 'function'){
+ window.onload = func;
+ } else {
+ window.onload = function(){
+ oldonload();
+ func();
+ }
+ }
+
+}
+
+
+
+addLoadEvent(initLightbox); // run initLightbox onLoad
diff --git a/web/public/stylesheets/lightbox.css b/web/public/stylesheets/light…
@@ -0,0 +1,26 @@
+#lightbox{
+ background-color:#eee;
+ padding: 10px;
+ border-bottom: 2px solid #666;
+ border-right: 2px solid #666;
+ }
+#lightboxDetails{
+ font-size: 0.8em;
+ padding-top: 0.4em;
+ }
+#lightboxCaption{ float: left; }
+#keyboardMsg{ float: right; }
+#closeButton{ top: 5px; right: 5px; }
+
+#lightbox img{ border: none; clear: both;}
+#overlay img{ border: none; }
+
+#overlay{ background-image: url(overlay.png); }
+
+* html #overlay{
+ background-color: #333;
+ back\ground-color: transparent;
+ background-image: url(blank.gif);
+ filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="overla…
+ }
+
+\ No newline at end of file
You are viewing proxied material from jay.scot. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.