diff options
-rw-r--r-- | Gemfile | 2 | ||||
-rw-r--r-- | Gemfile.lock | 5 | ||||
-rw-r--r-- | app/assets/javascripts/application.js | 3 | ||||
-rw-r--r-- | app/assets/stylesheets/matches.css.scss | 28 | ||||
-rw-r--r-- | app/controllers/matches_controller.rb | 32 | ||||
-rw-r--r-- | app/views/layouts/application.html.erb | 5 | ||||
-rw-r--r-- | app/views/matches/show.html.erb | 38 | ||||
-rwxr-xr-x | bin/delayed_job | 5 | ||||
-rw-r--r-- | db/migrate/20140406195921_create_simple_captcha_data.rb (renamed from db/migrate/20140406131442_create_simple_captcha_data.rb) | 0 | ||||
-rw-r--r-- | db/migrate/20140406235927_create_delayed_jobs.rb | 22 | ||||
-rw-r--r-- | db/migrate/20140406235933_create_servers.rb (renamed from db/migrate/20140406171445_create_servers.rb) | 0 | ||||
-rw-r--r-- | db/migrate/20140406235940_create_matches.rb (renamed from db/migrate/20140406171448_create_matches.rb) | 0 | ||||
-rw-r--r-- | db/migrate/20140406235946_create_teams.rb (renamed from db/migrate/20140406171451_create_teams.rb) | 0 | ||||
-rw-r--r-- | db/migrate/20140406235952_create_alerts.rb (renamed from db/migrate/20140406171453_create_alerts.rb) | 0 | ||||
-rw-r--r-- | db/migrate/20140406235958_create_pms.rb (renamed from db/migrate/20140406171456_create_pms.rb) | 0 | ||||
-rw-r--r-- | db/migrate/20140407000005_create_tournaments.rb (renamed from db/migrate/20140406171459_create_tournaments.rb) | 0 | ||||
-rw-r--r-- | db/migrate/20140407000011_create_games.rb (renamed from db/migrate/20140406171502_create_games.rb) | 0 | ||||
-rw-r--r-- | db/migrate/20140407000017_create_users.rb (renamed from db/migrate/20140406171505_create_users.rb) | 0 | ||||
-rw-r--r-- | db/migrate/20140407000024_create_sessions.rb (renamed from db/migrate/20140406171508_create_sessions.rb) | 0 | ||||
-rw-r--r-- | db/migrate/20140407000030_create_server_settings.rb (renamed from db/migrate/20140406171510_create_server_settings.rb) | 0 | ||||
-rw-r--r-- | db/migrate/20140407000036_create_game_settings.rb (renamed from db/migrate/20140406171513_create_game_settings.rb) | 0 | ||||
-rw-r--r-- | db/migrate/20140407000042_create_tournament_preferences.rb (renamed from db/migrate/20140406171516_create_tournament_preferences.rb) | 0 | ||||
-rw-r--r-- | db/migrate/20140407000048_create_scores.rb (renamed from db/migrate/20140406171519_create_scores.rb) | 0 | ||||
-rw-r--r-- | db/migrate/20140407000054_create_remote_usernames.rb (renamed from db/migrate/20140406171522_create_remote_usernames.rb) | 0 | ||||
-rw-r--r-- | db/migrate/20140407000100_create_tournament_players_join_table.rb (renamed from db/migrate/20140406171525_create_tournament_players_join_table.rb) | 0 | ||||
-rw-r--r-- | db/migrate/20140407000106_create_tournament_hosts_join_table.rb (renamed from db/migrate/20140406171527_create_tournament_hosts_join_table.rb) | 0 | ||||
-rw-r--r-- | db/migrate/20140407000112_create_team_user_join_table.rb (renamed from db/migrate/20140406171530_create_team_user_join_table.rb) | 0 | ||||
-rw-r--r-- | db/migrate/20140407000118_create_match_team_join_table.rb (renamed from db/migrate/20140406171532_create_match_team_join_table.rb) | 0 | ||||
-rw-r--r-- | db/migrate/20140407000143_add_hidden_attrs_to_user.rb (renamed from db/migrate/20140406171543_add_hidden_attrs_to_user.rb) | 0 | ||||
-rw-r--r-- | db/schema.rb | 18 | ||||
-rwxr-xr-x | generate.sh | 1 | ||||
-rw-r--r-- | vendor/assets/javascripts/coordinates.js | 94 | ||||
-rw-r--r-- | vendor/assets/javascripts/drag.js | 229 | ||||
-rw-r--r-- | vendor/assets/javascripts/dragsort.js | 234 |
34 files changed, 681 insertions, 35 deletions
@@ -48,7 +48,7 @@ gem 'turbolinks' gem 'jbuilder', '~> 1.2' # Asynchronously handle longer or delayed tasks -gem 'delayed_job' +gem 'delayed_job_active_record' group :doc do # bundle exec rake doc:rails generates the API under doc/api. diff --git a/Gemfile.lock b/Gemfile.lock index 51e29e7..fd03576 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,6 +40,9 @@ GEM coffee-script-source (1.7.0) delayed_job (4.0.0) activesupport (>= 3.0, < 4.1) + delayed_job_active_record (4.0.0) + activerecord (>= 3.0, < 4.1) + delayed_job (>= 3.0, < 4.1) erubis (2.7.0) execjs (2.0.2) hike (1.2.3) @@ -128,7 +131,7 @@ DEPENDENCIES bcrypt-ruby (= 3.1.2) bootstrap-sass coffee-rails (~> 4.0.0) - delayed_job + delayed_job_active_record httparty jbuilder (~> 1.2) jquery-rails diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index d6925fa..cb6edeb 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -14,3 +14,6 @@ //= require jquery_ujs //= require turbolinks //= require_tree . +//= require 'drag.js' +//= require 'dragsort.js' +//= require 'coordinates.js' diff --git a/app/assets/stylesheets/matches.css.scss b/app/assets/stylesheets/matches.css.scss index e0bed85..3ef9170 100644 --- a/app/assets/stylesheets/matches.css.scss +++ b/app/assets/stylesheets/matches.css.scss @@ -1,11 +1,27 @@ // Place all the styles related to the matches controller here. // They will automatically be included in application.css. // You can use Sass (SCSS) here: http://sass-lang.com/ - -#match-tree{ - float: right; +#boxes li { + cursor: move; + position: relative; + float: left; + margin: 5px; + width: 180px; + height: 240px; + border: 1px solid rgb(0, 0, 0); + text-align: center; + padding-top: 10px; + background-color: rgb(238, 238, 255); } - -#hovered-info-text { - +#numeric li { + cursor: move; + position: relative; + float: left; + margin: 5px; + width: 180px; + height: 240px; + border: 1px solid rgb(0, 0, 0); + text-align: center; + padding-top: 10px; + background-color: rgb(238, 238, 255); }
\ No newline at end of file diff --git a/app/controllers/matches_controller.rb b/app/controllers/matches_controller.rb index 28bc512..e9fe727 100644 --- a/app/controllers/matches_controller.rb +++ b/app/controllers/matches_controller.rb @@ -5,6 +5,7 @@ class MatchesController < ApplicationController # GET /matches.json require 'httparty' require 'json' + require 'delayed_job' def index @matches = @tournament.matches @@ -14,6 +15,8 @@ class MatchesController < ApplicationController @height = 200 * 2**Math.log2(@matches.count).floor + 100; end + + def get_riot_info if signed_in? @@ -118,14 +121,41 @@ class MatchesController < ApplicationController end #end def # GET /matches/1 # GET /matches/1.json + + def is_match_over + response = HTTParty.get("https://prod.api.pvp.net/api/lol/na/v1.3/summoner/by-name/#{@first}?api_key=ad539f86-22fd-474d-9279-79a7a296ac38") + riot_id = response["#{@first}"]['id'] + #recent game information + game_info = HTTParty.get("https://prod.api.pvp.net/api/lol/na/v1.3/game/by-summoner/#{riot_id}/recent?api_key=ad539f86-22fd-474d-9279-79a7a296ac38") + first_id = game_info["games"][0]["gameId"] + + while true do + sleep(240) #wait four minutes + + recent = HTTParty.get("https://prod.api.pvp.net/api/lol/na/v1.3/game/by-summoner/#{riot_id}/recent?api_key=ad539f86-22fd-474d-9279-79a7a296ac38") + current_id = recent["games"][0]["gameId"] + + if current_id != first_id + @match.status = 2 + end + end #while + end + handle_asynchronously :is_match_over + def show + if @match.id == 1 + is_match_over + end + + end private # Use callbacks to share common setup or constraints between actions. def set_match set_tournament - @match = @tournament.matches.find(params[:id]); + @match = @tournament.matches.find(params[:id]) + @first = @match.teams.first.users.first.user_name.downcase end def set_tournament @tournament = Tournament.find(params[:tournament_id]) diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 6d1d7f4..de9f3b8 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -8,6 +8,11 @@ <%= yield :head %> </head> <body> +<script> + window.onload = function() { + BetterDragSort.makeListSortable(document.getElementById("boxes")); + }; +</script> <header><nav> <div class="navbar-brand"><%= link_to('Leaguer', root_path) %></div> <div> diff --git a/app/views/matches/show.html.erb b/app/views/matches/show.html.erb index 20860e2..ffa7682 100644 --- a/app/views/matches/show.html.erb +++ b/app/views/matches/show.html.erb @@ -2,7 +2,6 @@ <strong>Status:</strong> <%= @match.status %> </p> - <p> <strong>Tournament:</strong> <%= @match.tournament.id %> @@ -27,49 +26,38 @@ Note:- The change of status from 1 to 2 is coming from League Data Pull (RIOT API) --> -<!-- - This is what the HOST will see when the Match Status is NOT 3 ---> -<% if (@tournament.hosts.include?(current_user) and @match.winner.nil?) %> - <%= form_for([@tournament, @match], method: "put") do |f| %> - <ul> - <% @match.teams.each do |team| %> - <li><label><%= f.radio_button(:winner, team.id) %> - <%= team.users.collect{|u| u.user_name}.join(", ") %></label></li> - <% end %> - </ul> - <%= f.submit("Select Winner") %> - <% end %> -<% end %> <!-- This is what the Players and the Hosts of the tournament will view when the Match Status is 0 --> -<% if (@match.status==0) %> +<% if (@match.status==1) %> <% if (@tournament.players.include?(current_user) || @tournament.hosts.include?(current_user)) %> <% @match.teams.each do |team| %> - <ul> + <ol> <% team.users.collect{|u| u.user_name}.each do |k| %> - <li><label><%= k %></label></li> + <li><%= k %></li> <% end %> - </ul> + </ol> <% end %> <% end %> <% end %> <!-- + When Match Status is 2 Players see the Peer Review Page Host see the Game Status --> -<% if @match.status == 0 %> - <% if @tournament.players.include?(current_user) %> +<% if (@match.status==0) %> + <% if (@tournament.players.include?(current_user)) %> <% @match.teams.each do |team| %> - <ul> + <ol id="boxes" class="sortable"> <% team.users.collect{|u| u.user_name}.each do |k| %> - <li><label><%= k %></label></li> + <li><%= k %></li> <% end %> - </ul> - <% end %> + </ol> + <% end %> + <% elsif (@tournament.hosts.include?(current_user)) %> + <label>Game Status Page Goes here! Because you are a Host that is not a player!</label> <% end %> <% end %> diff --git a/bin/delayed_job b/bin/delayed_job new file mode 100755 index 0000000..edf1959 --- /dev/null +++ b/bin/delayed_job @@ -0,0 +1,5 @@ +#!/usr/bin/env ruby + +require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment')) +require 'delayed/command' +Delayed::Command.new(ARGV).daemonize diff --git a/db/migrate/20140406131442_create_simple_captcha_data.rb b/db/migrate/20140406195921_create_simple_captcha_data.rb index 4573b20..4573b20 100644 --- a/db/migrate/20140406131442_create_simple_captcha_data.rb +++ b/db/migrate/20140406195921_create_simple_captcha_data.rb diff --git a/db/migrate/20140406235927_create_delayed_jobs.rb b/db/migrate/20140406235927_create_delayed_jobs.rb new file mode 100644 index 0000000..ec0dd93 --- /dev/null +++ b/db/migrate/20140406235927_create_delayed_jobs.rb @@ -0,0 +1,22 @@ +class CreateDelayedJobs < ActiveRecord::Migration + def self.up + create_table :delayed_jobs, :force => true do |table| + table.integer :priority, :default => 0, :null => false # Allows some jobs to jump to the front of the queue + table.integer :attempts, :default => 0, :null => false # Provides for retries, but still fail eventually. + table.text :handler, :null => false # YAML-encoded string of the object that will do work + table.text :last_error # reason for last failure (See Note below) + table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future. + table.datetime :locked_at # Set when a client is working on this object + table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead) + table.string :locked_by # Who is working on this object (if locked) + table.string :queue # The name of the queue this job is in + table.timestamps + end + + add_index :delayed_jobs, [:priority, :run_at], :name => 'delayed_jobs_priority' + end + + def self.down + drop_table :delayed_jobs + end +end diff --git a/db/migrate/20140406171445_create_servers.rb b/db/migrate/20140406235933_create_servers.rb index fbe1b02..fbe1b02 100644 --- a/db/migrate/20140406171445_create_servers.rb +++ b/db/migrate/20140406235933_create_servers.rb diff --git a/db/migrate/20140406171448_create_matches.rb b/db/migrate/20140406235940_create_matches.rb index 31eea12..31eea12 100644 --- a/db/migrate/20140406171448_create_matches.rb +++ b/db/migrate/20140406235940_create_matches.rb diff --git a/db/migrate/20140406171451_create_teams.rb b/db/migrate/20140406235946_create_teams.rb index fdf9a68..fdf9a68 100644 --- a/db/migrate/20140406171451_create_teams.rb +++ b/db/migrate/20140406235946_create_teams.rb diff --git a/db/migrate/20140406171453_create_alerts.rb b/db/migrate/20140406235952_create_alerts.rb index 68a8e10..68a8e10 100644 --- a/db/migrate/20140406171453_create_alerts.rb +++ b/db/migrate/20140406235952_create_alerts.rb diff --git a/db/migrate/20140406171456_create_pms.rb b/db/migrate/20140406235958_create_pms.rb index 93bb5c6..93bb5c6 100644 --- a/db/migrate/20140406171456_create_pms.rb +++ b/db/migrate/20140406235958_create_pms.rb diff --git a/db/migrate/20140406171459_create_tournaments.rb b/db/migrate/20140407000005_create_tournaments.rb index c0d8929..c0d8929 100644 --- a/db/migrate/20140406171459_create_tournaments.rb +++ b/db/migrate/20140407000005_create_tournaments.rb diff --git a/db/migrate/20140406171502_create_games.rb b/db/migrate/20140407000011_create_games.rb index 5e4f56f..5e4f56f 100644 --- a/db/migrate/20140406171502_create_games.rb +++ b/db/migrate/20140407000011_create_games.rb diff --git a/db/migrate/20140406171505_create_users.rb b/db/migrate/20140407000017_create_users.rb index 8032870..8032870 100644 --- a/db/migrate/20140406171505_create_users.rb +++ b/db/migrate/20140407000017_create_users.rb diff --git a/db/migrate/20140406171508_create_sessions.rb b/db/migrate/20140407000024_create_sessions.rb index f667f1e..f667f1e 100644 --- a/db/migrate/20140406171508_create_sessions.rb +++ b/db/migrate/20140407000024_create_sessions.rb diff --git a/db/migrate/20140406171510_create_server_settings.rb b/db/migrate/20140407000030_create_server_settings.rb index dfdd91b..dfdd91b 100644 --- a/db/migrate/20140406171510_create_server_settings.rb +++ b/db/migrate/20140407000030_create_server_settings.rb diff --git a/db/migrate/20140406171513_create_game_settings.rb b/db/migrate/20140407000036_create_game_settings.rb index b1caf5d..b1caf5d 100644 --- a/db/migrate/20140406171513_create_game_settings.rb +++ b/db/migrate/20140407000036_create_game_settings.rb diff --git a/db/migrate/20140406171516_create_tournament_preferences.rb b/db/migrate/20140407000042_create_tournament_preferences.rb index 991d659..991d659 100644 --- a/db/migrate/20140406171516_create_tournament_preferences.rb +++ b/db/migrate/20140407000042_create_tournament_preferences.rb diff --git a/db/migrate/20140406171519_create_scores.rb b/db/migrate/20140407000048_create_scores.rb index 4ca0b0b..4ca0b0b 100644 --- a/db/migrate/20140406171519_create_scores.rb +++ b/db/migrate/20140407000048_create_scores.rb diff --git a/db/migrate/20140406171522_create_remote_usernames.rb b/db/migrate/20140407000054_create_remote_usernames.rb index e265985..e265985 100644 --- a/db/migrate/20140406171522_create_remote_usernames.rb +++ b/db/migrate/20140407000054_create_remote_usernames.rb diff --git a/db/migrate/20140406171525_create_tournament_players_join_table.rb b/db/migrate/20140407000100_create_tournament_players_join_table.rb index be240e8..be240e8 100644 --- a/db/migrate/20140406171525_create_tournament_players_join_table.rb +++ b/db/migrate/20140407000100_create_tournament_players_join_table.rb diff --git a/db/migrate/20140406171527_create_tournament_hosts_join_table.rb b/db/migrate/20140407000106_create_tournament_hosts_join_table.rb index 7521d89..7521d89 100644 --- a/db/migrate/20140406171527_create_tournament_hosts_join_table.rb +++ b/db/migrate/20140407000106_create_tournament_hosts_join_table.rb diff --git a/db/migrate/20140406171530_create_team_user_join_table.rb b/db/migrate/20140407000112_create_team_user_join_table.rb index f3b57fc..f3b57fc 100644 --- a/db/migrate/20140406171530_create_team_user_join_table.rb +++ b/db/migrate/20140407000112_create_team_user_join_table.rb diff --git a/db/migrate/20140406171532_create_match_team_join_table.rb b/db/migrate/20140407000118_create_match_team_join_table.rb index c2ed1b7..c2ed1b7 100644 --- a/db/migrate/20140406171532_create_match_team_join_table.rb +++ b/db/migrate/20140407000118_create_match_team_join_table.rb diff --git a/db/migrate/20140406171543_add_hidden_attrs_to_user.rb b/db/migrate/20140407000143_add_hidden_attrs_to_user.rb index 9b5c505..9b5c505 100644 --- a/db/migrate/20140406171543_add_hidden_attrs_to_user.rb +++ b/db/migrate/20140407000143_add_hidden_attrs_to_user.rb diff --git a/db/schema.rb b/db/schema.rb index 53bc413..f4612b8 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20140406171543) do +ActiveRecord::Schema.define(version: 20140407000143) do create_table "alerts", force: true do |t| t.integer "author_id" @@ -22,6 +22,22 @@ ActiveRecord::Schema.define(version: 20140406171543) do add_index "alerts", ["author_id"], name: "index_alerts_on_author_id" + create_table "delayed_jobs", force: true do |t| + t.integer "priority", default: 0, null: false + t.integer "attempts", default: 0, null: false + t.text "handler", null: false + t.text "last_error" + t.datetime "run_at" + t.datetime "locked_at" + t.datetime "failed_at" + t.string "locked_by" + t.string "queue" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "delayed_jobs", ["priority", "run_at"], name: "delayed_jobs_priority" + create_table "game_settings", force: true do |t| t.integer "game_id" t.integer "stype" diff --git a/generate.sh b/generate.sh index 18d0f63..e3e07f9 100755 --- a/generate.sh +++ b/generate.sh @@ -13,6 +13,7 @@ git rm -rf -- app test config/routes.rb db/migrate || true git checkout clean-start -- app test config/routes.rb bundle exec rails generate simple_captcha +bundle exec rails generate delayed_job:active_record # The whole shebang, models, views, and controllers bundle exec rails generate scaffold server default_user_permissions:integer diff --git a/vendor/assets/javascripts/coordinates.js b/vendor/assets/javascripts/coordinates.js new file mode 100644 index 0000000..e2f5bf2 --- /dev/null +++ b/vendor/assets/javascripts/coordinates.js @@ -0,0 +1,94 @@ +var Coordinates = { + ORIGIN : new Coordinate(0, 0), + + northwestPosition : function(element) { + var x = parseInt(element.style.left); + var y = parseInt(element.style.top); + + return new Coordinate(isNaN(x) ? 0 : x, isNaN(y) ? 0 : y); + }, + + southeastPosition : function(element) { + return Coordinates.northwestPosition(element).plus( + new Coordinate(element.offsetWidth, element.offsetHeight)); + }, + + northwestOffset : function(element, isRecursive) { + var offset = new Coordinate(element.offsetLeft, element.offsetTop); + + if (!isRecursive) return offset; + + var parent = element.offsetParent; + while (parent) { + offset = offset.plus( + new Coordinate(parent.offsetLeft, parent.offsetTop)); + parent = parent.offsetParent; + } + return offset; + }, + + southeastOffset : function(element, isRecursive) { + return Coordinates.northwestOffset(element, isRecursive).plus( + new Coordinate(element.offsetWidth, element.offsetHeight)); + }, + + fixEvent : function(event) { + event.windowCoordinate = new Coordinate(event.clientX, event.clientY); + } +}; + +function Coordinate(x, y) { + this.x = x; + this.y = y; +} + +Coordinate.prototype.toString = function() { + return "(" + this.x + "," + this.y + ")"; +} + +Coordinate.prototype.plus = function(that) { + return new Coordinate(this.x + that.x, this.y + that.y); +} + +Coordinate.prototype.minus = function(that) { + return new Coordinate(this.x - that.x, this.y - that.y); +} + +Coordinate.prototype.distance = function(that) { + var deltaX = this.x - that.x; + var deltaY = this.y - that.y; + + return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); +} + +Coordinate.prototype.max = function(that) { + var x = Math.max(this.x, that.x); + var y = Math.max(this.y, that.y); + return new Coordinate(x, y); +} + +Coordinate.prototype.constrain = function(min, max) { + if (min.x > max.x || min.y > max.y) return this; + + var x = this.x; + var y = this.y; + + if (min.x != null) x = Math.max(x, min.x); + if (max.x != null) x = Math.min(x, max.x); + if (min.y != null) y = Math.max(y, min.y); + if (max.y != null) y = Math.min(y, max.y); + + return new Coordinate(x, y); +} + +Coordinate.prototype.reposition = function(element) { + element.style["top"] = this.y + "px"; + element.style["left"] = this.x + "px"; +} + +Coordinate.prototype.equals = function(that) { + if (this == that) return true; + if (!that || that == null) return false; + + return this.x == that.x && this.y == that.y; +} diff --git a/vendor/assets/javascripts/drag.js b/vendor/assets/javascripts/drag.js new file mode 100644 index 0000000..9ba6746 --- /dev/null +++ b/vendor/assets/javascripts/drag.js @@ -0,0 +1,229 @@ +/* + * drag.js - click & drag DOM elements + * + * originally based on Youngpup's dom-drag.js, www.youngpup.net + */ + +var Drag = { + BIG_Z_INDEX : 10000, + group : null, + isDragging : false, + + makeDraggable : function(group) { + group.handle = group; + group.handle.group = group; + + group.minX = null; + group.minY = null; + group.maxX = null; + group.maxY = null; + group.threshold = 0; + group.thresholdY = 0; + group.thresholdX = 0; + + group.onDragStart = new Function(); + group.onDragEnd = new Function(); + group.onDrag = new Function(); + + // TODO: use element.prototype.myFunc + group.setDragHandle = Drag.setDragHandle; + group.setDragThreshold = Drag.setDragThreshold; + group.setDragThresholdX = Drag.setDragThresholdX; + group.setDragThresholdY = Drag.setDragThresholdY; + group.constrain = Drag.constrain; + group.constrainVertical = Drag.constrainVertical; + group.constrainHorizontal = Drag.constrainHorizontal; + + group.onmousedown = Drag.onMouseDown; + }, + + constrainVertical : function() { + var nwOffset = Coordinates.northwestOffset(this, true); + this.minX = nwOffset.x; + this.maxX = nwOffset.x; + }, + + constrainHorizontal : function() { + var nwOffset = Coordinates.northwestOffset(this, true); + this.minY = nwOffset.y; + this.maxY = nwOffset.y; + }, + + constrain : function(nwPosition, sePosition) { + this.minX = nwPosition.x; + this.minY = nwPosition.y; + this.maxX = sePosition.x; + this.maxY = sePosition.y; + }, + + setDragHandle : function(handle) { + if (handle && handle != null) + this.handle = handle; + else + this.handle = this; + + this.handle.group = this; + this.onmousedown = null; + this.handle.onmousedown = Drag.onMouseDown; + }, + + setDragThreshold : function(threshold) { + if (isNaN(parseInt(threshold))) return; + + this.threshold = threshold; + }, + + setDragThresholdX : function(threshold) { + if (isNaN(parseInt(threshold))) return; + + this.thresholdX = threshold; + }, + + setDragThresholdY : function(threshold) { + if (isNaN(parseInt(threshold))) return; + + this.thresholdY = threshold; + }, + + onMouseDown : function(event) { + event = Drag.fixEvent(event); + Drag.group = this.group; + + var group = this.group; + var mouse = event.windowCoordinate; + var nwOffset = Coordinates.northwestOffset(group, true); + var nwPosition = Coordinates.northwestPosition(group); + var sePosition = Coordinates.southeastPosition(group); + var seOffset = Coordinates.southeastOffset(group, true); + + group.originalOpacity = group.style.opacity; + group.originalZIndex = group.style.zIndex; + group.initialWindowCoordinate = mouse; + // TODO: need a better name, but don't yet understand how it + // participates in the magic while dragging + group.dragCoordinate = mouse; + + Drag.showStatus(mouse, nwPosition, sePosition, nwOffset, seOffset); + + group.onDragStart(nwPosition, sePosition, nwOffset, seOffset); + + // TODO: need better constraint API + if (group.minX != null) + group.minMouseX = mouse.x - nwPosition.x + + group.minX - nwOffset.x; + if (group.maxX != null) + group.maxMouseX = group.minMouseX + group.maxX - group.minX; + + if (group.minY != null) + group.minMouseY = mouse.y - nwPosition.y + + group.minY - nwOffset.y; + if (group.maxY != null) + group.maxMouseY = group.minMouseY + group.maxY - group.minY; + + group.mouseMin = new Coordinate(group.minMouseX, group.minMouseY); + group.mouseMax = new Coordinate(group.maxMouseX, group.maxMouseY); + + document.onmousemove = Drag.onMouseMove; + document.onmouseup = Drag.onMouseUp; + + return false; + }, + + showStatus : function(mouse, nwPosition, sePosition, nwOffset, seOffset) { + window.status = + "mouse: " + mouse.toString() + " " + + "NW pos: " + nwPosition.toString() + " " + + "SE pos: " + sePosition.toString() + " " + + "NW offset: " + nwOffset.toString() + " " + + "SE offset: " + seOffset.toString(); + }, + + onMouseMove : function(event) { + event = Drag.fixEvent(event); + var group = Drag.group; + var mouse = event.windowCoordinate; + var nwOffset = Coordinates.northwestOffset(group, true); + var nwPosition = Coordinates.northwestPosition(group); + var sePosition = Coordinates.southeastPosition(group); + var seOffset = Coordinates.southeastOffset(group, true); + + Drag.showStatus(mouse, nwPosition, sePosition, nwOffset, seOffset); + + if (!Drag.isDragging) { + if (group.threshold > 0) { + var distance = group.initialWindowCoordinate.distance( + mouse); + if (distance < group.threshold) return true; + } else if (group.thresholdY > 0) { + var deltaY = Math.abs(group.initialWindowCoordinate.y - mouse.y); + if (deltaY < group.thresholdY) return true; + } else if (group.thresholdX > 0) { + var deltaX = Math.abs(group.initialWindowCoordinate.x - mouse.x); + if (deltaX < group.thresholdX) return true; + } + + Drag.isDragging = true; + group.style["zIndex"] = Drag.BIG_Z_INDEX; + group.style["opacity"] = 0.75; + } + + // TODO: need better constraint API + var adjusted = mouse.constrain(group.mouseMin, group.mouseMax); + nwPosition = nwPosition.plus(adjusted.minus(group.dragCoordinate)); + nwPosition.reposition(group); + group.dragCoordinate = adjusted; + + // once dragging has started, the position of the group + // relative to the mouse should stay fixed. They can get out + // of sync if the DOM is manipulated while dragging, so we + // correct the error here + // + // TODO: what we really want to do is find the offset from + // our corner to the mouse coordinate and adjust to keep it + // the same + var offsetBefore = Coordinates.northwestOffset(group); + group.onDrag(nwPosition, sePosition, nwOffset, seOffset); + var offsetAfter = Coordinates.northwestOffset(group); + + if (!offsetBefore.equals(offsetAfter)) { + var errorDelta = offsetBefore.minus(offsetAfter); + nwPosition = Coordinates.northwestPosition(group).plus(errorDelta); + nwPosition.reposition(group); + } + + return false; + }, + + onMouseUp : function(event) { + event = Drag.fixEvent(event); + var group = Drag.group; + + var mouse = event.windowCoordinate; + var nwOffset = Coordinates.northwestOffset(group, true); + var nwPosition = Coordinates.northwestPosition(group); + var sePosition = Coordinates.southeastPosition(group); + var seOffset = Coordinates.southeastOffset(group, true); + + document.onmousemove = null; + document.onmouseup = null; + group.onDragEnd(nwPosition, sePosition, nwOffset, seOffset); + + if (Drag.isDragging) { + // restoring zIndex before opacity avoids visual flicker in Firefox + group.style["zIndex"] = group.originalZIndex; + group.style["opacity"] = group.originalOpacity; + } + + Drag.group = null; + Drag.isDragging = false; + + return false; + }, + + fixEvent : function(event) { + if (typeof event == 'undefined') event = window.event; + Coordinates.fixEvent(event); + + return event; + } +}; diff --git a/vendor/assets/javascripts/dragsort.js b/vendor/assets/javascripts/dragsort.js new file mode 100644 index 0000000..6356663 --- /dev/null +++ b/vendor/assets/javascripts/dragsort.js @@ -0,0 +1,234 @@ +// TODO: refactor away duplicationg in DragSort and DragSortX + +var BetterDragSort = { + + makeListSortable : function(list) { + var items = list.getElementsByTagName("li"); + + for (var i = 0; i < items.length; i++) { + BetterDragSort.makeItemSortable(items[i]); + } + }, + + makeItemSortable : function(item) { + Drag.makeDraggable(item); + item.setDragThresholdY(5); + + item.onDragStart = BetterDragSort.onDragStart; + item.onDrag = BetterDragSort.onDrag; + item.onDragEnd = BetterDragSort.onDragEnd; + }, + + onDragStart : function(nwPosition, sePosition, nwOffset, seOffset) { + var items = this.parentNode.getElementsByTagName("li"); + var minOffset = Coordinates.northwestOffset(items[0], true); + var maxOffset = minOffset; + for (var i = 0; i < items.length; i++) { + maxOffset = maxOffset.max(Coordinates.northwestOffset(items[i], true)); + } + this.constrain(minOffset, maxOffset); + }, + + onDrag : function(nwPosition, sePosition, nwOffset, seOffset) { + var swapper = null; + + var next = DragUtils.nextItem(this); + while (next != null) { + var nextNWOffset = Coordinates.northwestOffset(next, true); + var nextSEOffset = Coordinates.southeastOffset(next, true); + + if (nwOffset.y >= (nextNWOffset.y - 2) && + nwOffset.y <= (nextSEOffset.y + 2) && + nwOffset.x >= (nextNWOffset.x - 2) && + nwOffset.x <= (nextSEOffset.x + 2)) { + var swapper = next; + break; + } + var next = DragUtils.nextItem(next); + } + if (swapper != null) { + BetterDragSort.moveAfter(this, swapper); + return; + } + + var previous = DragUtils.previousItem(this); + while (previous != null) { + var previousNWOffset = Coordinates.northwestOffset(previous, true); + var previousSEOffset = Coordinates.southeastOffset(previous, true); + + var fudgeFactor = 2; + if (nwOffset.y >= (previousNWOffset.y - fudgeFactor) && + nwOffset.y <= (previousSEOffset.y + fudgeFactor) && + nwOffset.x >= (previousNWOffset.x - fudgeFactor) && + nwOffset.x <= (previousSEOffset.x + fudgeFactor)) { + var swapper = previous; + break; + } + var previous = DragUtils.previousItem(previous); + } + if (swapper != null) { + BetterDragSort.moveBefore(this, swapper); + return; + } + }, + + moveAfter : function(item1, item2) { + var parent = item1.parentNode; + parent.removeChild(item1); + parent.insertBefore(item1, item2.nextSibling); + + item1.style["top"] = "0px"; + item1.style["left"] = "0px"; + }, + + moveBefore : function(item1, item2) { + var parent = item1.parentNode; + parent.removeChild(item1); + parent.insertBefore(item1, item2); + + item1.style["top"] = "0px"; + item1.style["left"] = "0px"; + }, + + onDragEnd : function(nwPosition, sePosition, nwOffset, seOffset) { + this.style["top"] = "0px"; + this.style["left"] = "0px"; + } +}; + + +var DragSort = { + + makeListSortable : function(list) { + var items = list.getElementsByTagName("li"); + + for (var i = 0; i < items.length; i++) { + DragSort.makeItemSortable(items[i]); + } + }, + + makeItemSortable : function(item) { + Drag.makeDraggable(item); + item.setDragThresholdY(5); + + item.onDragStart = DragSort.onDragStart; + item.onDrag = DragSort.onDrag; + item.onDragEnd = DragSort.onDragEnd; + }, + + onDragStart : function(nwPosition, sePosition, nwOffset, seOffset) { + var items = this.parentNode.getElementsByTagName("li"); + var minOffset = Coordinates.northwestOffset(items[0], true); + var maxOffset = Coordinates.northwestOffset(items[items.length - 1], true); + this.constrain(minOffset, maxOffset); + }, + + onDrag : function(nwPosition, sePosition, nwOffset, seOffset) { + var parent = this.parentNode; + + var item = this; + var next = DragUtils.nextItem(item); + while (next != null && this.offsetTop >= next.offsetTop - 2) { + var item = next; + var next = DragUtils.nextItem(item); + } + if (this != item) { + DragUtils.swap(this, next); + return; + } + + var item = this; + var previous = DragUtils.previousItem(item); + while (previous != null && this.offsetTop <= previous.offsetTop + 2) { + var item = previous; + var previous = DragUtils.previousItem(item); + } + if (this != item) { + DragUtils.swap(this, item); + return; + } + }, + + onDragEnd : function(nwPosition, sePosition, nwOffset, seOffset) { + this.style["top"] = "0px"; + this.style["left"] = "0px"; + } +}; + +var DragSortX = { + + makeListSortable : function(list) { + var items = list.getElementsByTagName("li"); + + var minOffset = Coordinates.northwestOffset(items[0], true); + var maxOffset = Coordinates.northwestOffset(items[items.length - 1], true); + + for (var i = 0; i < items.length; i++) { + Drag.makeDraggable(items[i]); + items[i].constrain(minOffset, maxOffset); + items[i].setDragThresholdX(5); + + items[i].onDrag = DragSortX.onDrag; + + items[i].onDragEnd = function(nwPosition, sePosition, nwOffset, seOffset) { + this.style["top"] = "0px"; + this.style["left"] = "0px"; + }; + } + }, + + onDrag : function(nwPosition, sePosition, nwOffset, seOffset) { + var parent = this.parentNode; + + var item = this; + var next = DragUtils.nextItem(item); + while (next != null && this.offsetLeft >= next.offsetLeft - 2) { + var item = next; + var next = DragUtils.nextItem(item); + } + if (this != item) { + DragUtils.swap(this, next); + return; + } + + var item = this; + var previous = DragUtils.previousItem(item); + while (previous != null && this.offsetLeft <= previous.offsetLeft + 2) { + var item = previous; + var previous = DragUtils.previousItem(item); + } + if (this != item) { + DragUtils.swap(this, item); + return; + } + } +}; + +var DragUtils = { + swap : function(item1, item2) { + var parent = item1.parentNode; + parent.removeChild(item1); + parent.insertBefore(item1, item2); + + item1.style["top"] = "0px"; + item1.style["left"] = "0px"; + }, + + nextItem : function(item) { + var sibling = item.nextSibling; + while (sibling != null) { + if (sibling.nodeName == item.nodeName) return sibling; + sibling = sibling.nextSibling; + } + return null; + }, + + previousItem : function(item) { + var sibling = item.previousSibling; + while (sibling != null) { + if (sibling.nodeName == item.nodeName) return sibling; + sibling = sibling.previousSibling; + } + return null; + } +}; |