tagging implemented. js stuff
This commit is contained in:
parent
c6882bae3c
commit
510b351979
|
@ -16,6 +16,7 @@ class AlbumsController < ApplicationController
|
|||
format.html
|
||||
format.json { render :json => @album }
|
||||
format.xml { render :xml => @album }
|
||||
format.pdf { render :pdf => @album.title }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ class PhotosController < ApplicationController
|
|||
before_filter :require_user, :only => [:new, :create, :edit, :update, :destroy]
|
||||
|
||||
def index
|
||||
@photos = Photo.find(:all)
|
||||
@photos = Tag.find_by_title( params[:tag_id] ).photos
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json { render :json => @photos }
|
||||
|
|
11
app/controllers/tags_controller.rb
Normal file
11
app/controllers/tags_controller.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
class TagsController < ApplicationController
|
||||
|
||||
def index
|
||||
@tags = Tag.find( :all)
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json { render :json => @tags }
|
||||
format.xml { render :xml => @tags }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,12 +3,30 @@ class Photo < ActiveRecord::Base
|
|||
has_many :photo_tags, :dependent => :destroy
|
||||
has_many :tags, :through => :photo_tags
|
||||
|
||||
#accepts_nested_attributes_for :photo_tags, :allow_destroy => true
|
||||
|
||||
validates_uniqueness_of :path, :message => "Photo already exsists on disc"
|
||||
validates_presence_of :title
|
||||
|
||||
before_destroy :destroy_file
|
||||
|
||||
attr_accessor :tag_list
|
||||
|
||||
def tag_list
|
||||
return self.tags.find(:all, :order => 'title').collect{ |t| t.title }.join(" ")
|
||||
end
|
||||
|
||||
def tag_list=(tags)
|
||||
ts = Array.new
|
||||
tags.split(" ").each do |tag|
|
||||
ts.push( Tag.find_or_create_by_title( :title => tag) )
|
||||
end
|
||||
self.tags = ts
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
|
||||
def destroy_file
|
||||
puts "DELETE FILE " + APP_CONFIG[:photos_path] + self.path
|
||||
File.delete( APP_CONFIG[:photos_path] + self.path ) if File.exists?( APP_CONFIG[:photos_path] + self.path )
|
||||
|
|
|
@ -1,4 +1,14 @@
|
|||
class Tag < ActiveRecord::Base
|
||||
has_many :photo_tags
|
||||
has_many :photos, :through => :photo_tags
|
||||
|
||||
validates_uniqueness_of :title
|
||||
|
||||
def to_param
|
||||
#{ }"#{id}-#{name.gsub(/[^a-z0-9]+/i, '-')}"
|
||||
#id.to_s+'-'+name.downcase.gsub(/[^a-z0-9]+/i, '-')
|
||||
#id.to_s+'-'+name.downcase.gsub(' ', '-')
|
||||
self.title
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
<%= form.label :title %><br />
|
||||
<%= form.text_field :title %><br />
|
||||
<%= form.label :description %><br />
|
||||
<%= form.text_area :description %><br />
|
||||
<br />
|
||||
<%= @album.path %>
|
||||
Location on disk: <i><%= APP_CONFIG[:photos_path] + @album.path %></i><br/>
|
||||
Contains: <%= @album.photos.count %> photos<br/>
|
|
@ -1,45 +1,30 @@
|
|||
<% content_for :head do %>
|
||||
<link rel="stylesheet" href="/javascripts/galleria/galleria.css" type="text/css" media="screen" charset="utf-8">
|
||||
<% end %>
|
||||
|
||||
<% content_for :javascript do %>
|
||||
<script type="text/javascript" src="/javascripts/galleria/jquery.galleria.js"></script>
|
||||
<script type="text/javascript" src="/javascripts/scrollable/jquery.scrollable-1.0.2.js"></script>
|
||||
<script type="text/javascript" src="/javascripts/jquery.mousewheel.3.0.2/jquery.mousewheel.js"></script>
|
||||
<% end %>
|
||||
|
||||
<h1><%= @album.title %></h1>
|
||||
<script src="/javascripts/jquery-1.3.2.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<!--
|
||||
<link rel="stylesheet" href="/javascripts/jquery-galleryview-2.0/galleryview.css" type="text/css" media="screen" title="no title" charset="utf-8">
|
||||
<link rel="stylesheet" href="/javascripts/jquery-galleryview-2.0/galleryview.css" type="text/css" media="screen" title="no title" charset="utf-8">
|
||||
<script src="/javascripts/jquery-galleryview-2.0/jquery.timers-1.1.2.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="/javascripts/jquery-galleryview-2.0/jquery.galleryview-2.0.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
|
||||
<div id="photos" class="gallery">
|
||||
<div class="panel">
|
||||
<div class="panel-overlay">
|
||||
This is the text in the background
|
||||
</div>
|
||||
<div class="overlay-background"></div>
|
||||
</div>
|
||||
<ul class="filmstrip">
|
||||
<%= render :partial => @album.photos.find(:all, :limit => 10) %>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
$('#photos').galleryView({
|
||||
panel_width: 800,
|
||||
panel_height: 400,
|
||||
frame_width: 100,
|
||||
frame_height: 100
|
||||
});
|
||||
</script>
|
||||
|
||||
-->
|
||||
|
||||
<% if current_user %>
|
||||
<%= link_to "Edit album", edit_album_path( @album )%>
|
||||
<% end %>
|
||||
<link href="/javascripts/galleria/galleria.css" rel="stylesheet" type="text/css" media="screen">
|
||||
<script type="text/javascript" src="/javascripts/galleria/jquery.galleria.js"></script>
|
||||
<script type="text/javascript">
|
||||
jQuery(function($) { $('ul.gallery').galleria(); });
|
||||
</script>
|
||||
<ul class="gallery">
|
||||
<%= render :partial => @album.photos.find(:all, :limit => 10) %>
|
||||
<%= link_to "Generate PDF", album_path( @album, :pdf) %>
|
||||
|
||||
<div id="photo">
|
||||
<div id="photo_metadata"></div>
|
||||
<div id="photo_large">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="thumbstrip">
|
||||
<ul id="thumbs" class="gallery">
|
||||
<%= render :partial => @album.photos.find(:all) %>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<p><%= @album.description %></p>
|
3
app/views/albums/show.pdf.erb
Normal file
3
app/views/albums/show.pdf.erb
Normal file
|
@ -0,0 +1,3 @@
|
|||
<h1><%= @album.title %></h1>
|
||||
|
||||
<%= render :partial => @album.photos.find(:all, :limit => 10) %>
|
|
@ -6,11 +6,15 @@
|
|||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||
|
||||
<title>Gallery</title>
|
||||
<%= yield :head %>
|
||||
<%= stylesheet_link_tag 'application' %>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<%= yield %>
|
||||
|
||||
<%= javascript_include_tag 'jquery-1.3.2.js', 'application' %>
|
||||
<%= yield :javascript %>
|
||||
</body>
|
||||
</html>
|
||||
|
|
11
app/views/photos/_form.html.erb
Normal file
11
app/views/photos/_form.html.erb
Normal file
|
@ -0,0 +1,11 @@
|
|||
<% content_for :javascript do %>
|
||||
<script src="/javascripts/tag/tag.js" type="text/javascript" charset="utf-8"></script>
|
||||
<% end %>
|
||||
<%= hidden_field_tag :all_tags, "'#{Tag.find(:all).map { |tag| tag.title }.join('\',\'')}'" %>
|
||||
<%= form.label :title %><br />
|
||||
<%= form.text_field :title %><br />
|
||||
<br />
|
||||
<%= form.label :tag_list %><br />
|
||||
<%= form.text_field :tag_list, { :autocomplete => "off"} %><br />
|
||||
<br/>
|
||||
<p>On disk: ~/<%= @photo.path %></p>
|
|
@ -1,4 +1,4 @@
|
|||
<li>
|
||||
<a href="<%= APP_CONFIG[:thumbs_path_public] + photo.album.path + "/" + photo.id.to_s + "_large.jpg" %>" title="<%= photo.title %>">
|
||||
<%= image_tag APP_CONFIG[:thumbs_path_public] + photo.album.path + "/" + photo.id.to_s + "_small.jpg" %></a>
|
||||
<%= image_tag APP_CONFIG[:thumbs_path_public] + photo.album.path + "/" + photo.id.to_s + "_small.jpg", { :id => 'thumb_' + photo.id.to_s } %></a>
|
||||
</li>
|
||||
|
|
1
app/views/photos/_photo.pdf.erb
Normal file
1
app/views/photos/_photo.pdf.erb
Normal file
|
@ -0,0 +1 @@
|
|||
<%= image_tag APP_CONFIG[:photos_path_public] + photo.path %><br/>
|
10
app/views/photos/edit.html.erb
Normal file
10
app/views/photos/edit.html.erb
Normal file
|
@ -0,0 +1,10 @@
|
|||
<h1>Edit Photo</h1>
|
||||
|
||||
<% form_for @photo do |f| %>
|
||||
<%= f.error_messages %>
|
||||
<%= render :partial => "form", :object => f %>
|
||||
<%= f.submit "Update" %>
|
||||
<% end %>
|
||||
|
||||
<%= image_tag APP_CONFIG[:thumbs_path_public] + @photo.album.path + "/" + @photo.id.to_s + "_large.jpg" %>
|
||||
<br /><%= link_to "All albums", albums_path %>
|
16
app/views/photos/index.html.erb
Normal file
16
app/views/photos/index.html.erb
Normal file
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||
|
||||
<title>index.html</title>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
7
app/views/photos/show.html.erb
Normal file
7
app/views/photos/show.html.erb
Normal file
|
@ -0,0 +1,7 @@
|
|||
<h1><%= @photo.title%></h1>
|
||||
<%= image_tag APP_CONFIG[:thumbs_path_public] + @photo.album.path + "/" + @photo.id.to_s + "_large.jpg" %>
|
||||
|
||||
<br/>
|
||||
Tagged with: <%= @photo.tag_list %>
|
||||
<p><%= @photo.description %></p>
|
||||
<br /><%= link_to "All albums", albums_path %>
|
4
app/views/tags/index.html.erb
Normal file
4
app/views/tags/index.html.erb
Normal file
|
@ -0,0 +1,4 @@
|
|||
<h1>Tags</h1>
|
||||
<% for tag in @tags %>
|
||||
<%= tag.title %>
|
||||
<% end %>
|
|
@ -7,6 +7,7 @@ ActionController::Routing::Routes.draw do |map|
|
|||
map.logout "logout", :controller => "user_sessions", :action => "destroy"
|
||||
map.resources :photos
|
||||
map.resources :albums
|
||||
map.resources :tags, :has_many => [ :photos ]
|
||||
#map.connect ':controller/:action/:id'
|
||||
#map.connect ':controller/:action/:id.:format'
|
||||
|
||||
|
|
|
@ -1,2 +1,41 @@
|
|||
// Place your application-specific JavaScript functions and classes here
|
||||
// This file is automatically included by javascript_include_tag :defaults
|
||||
jQuery(function($) {
|
||||
if ( $('ul.gallery').length ) {
|
||||
$('ul.gallery').galleria( {
|
||||
clickNext : true,
|
||||
insert: "#photo_large",
|
||||
onImage: function ( image, caption, thumb ) {
|
||||
image.css('display','none').fadeIn()
|
||||
|
||||
thumb.parents('li').siblings().children('img.selected').fadeTo(500,0.3)
|
||||
thumb.fadeTo('fast',1).addClass('selected')
|
||||
$( '#photo_metadata' ).html( '<a href=\'/photos/' + thumb.attr('id').replace('thumb_', '') + '/edit\'>Update photo details</a>' )
|
||||
|
||||
var scrollable = $("#thumbstrip").scrollable()
|
||||
scrollable.seekTo( thumb.parents('ul').children().index( thumb.parents('li') ) )
|
||||
},
|
||||
onThumb: function ( thumb) {
|
||||
thumb.css({display:'none',opacity: (thumb.parents('li').is('.active') ? '1' : '0.3') }).fadeIn(1500)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
if ( $('#thumbstrip').length ) {
|
||||
$('#thumbstrip').scrollable( {
|
||||
items : '#thumbs',
|
||||
clickable: true,
|
||||
keyboard : false
|
||||
})
|
||||
if ( $('#thumbs li.active').length == 0 ){
|
||||
//$('div.scrollable').scrollable().click(0)
|
||||
$('#thumbs li:first').addClass('active')
|
||||
}
|
||||
}
|
||||
if ( $('#photo_tag_list').length ) {
|
||||
$('#photo_tag_list').tagSuggest({
|
||||
tags: $('#all_tags').val().split('\'')
|
||||
})
|
||||
}
|
||||
|
||||
//$('div.scrollable').scrollable().click( $('#gallery ul').children().index( $('#gallery li.active') ) )
|
||||
});
|
49
public/javascripts/jquery.mousewheel.3.0.2/ChangeLog.markdown
Executable file
49
public/javascripts/jquery.mousewheel.3.0.2/ChangeLog.markdown
Executable file
|
@ -0,0 +1,49 @@
|
|||
# Mouse Wheel ChangeLog
|
||||
|
||||
|
||||
# 3.0.2
|
||||
|
||||
* Fixed delta being opposite value in latest Opera
|
||||
* No longer fix pageX, pageY for older mozilla browsers
|
||||
* Removed browser detection
|
||||
* Cleaned up the code
|
||||
|
||||
|
||||
# 3.0.1
|
||||
|
||||
* Bad release... creating a new release due to plugins.jquery.com issue :(
|
||||
|
||||
|
||||
# 3.0
|
||||
|
||||
* Uses new special events API in jQuery 1.2.2+
|
||||
* You can now treat "mousewheel" as a normal event and use .bind, .unbind and .trigger
|
||||
* Using jQuery.data API for expandos
|
||||
|
||||
|
||||
# 2.2
|
||||
|
||||
* Fixed pageX, pageY, clientX and clientY event properties for Mozilla based browsers
|
||||
|
||||
|
||||
# 2.1.1
|
||||
|
||||
* Updated to work with jQuery 1.1.3
|
||||
* Used one instead of bind to do unload event for clean up.
|
||||
|
||||
|
||||
# 2.1
|
||||
|
||||
* Fixed an issue with the unload handler
|
||||
|
||||
|
||||
# 2.0
|
||||
|
||||
* Major reduction in code size and complexity (internals have change a whole lot)
|
||||
|
||||
|
||||
# 1.0
|
||||
|
||||
* Fixed Opera issue
|
||||
* Fixed an issue with children elements that also have a mousewheel handler
|
||||
* Added ability to handle multiple handlers
|
11
public/javascripts/jquery.mousewheel.3.0.2/README.markdown
Executable file
11
public/javascripts/jquery.mousewheel.3.0.2/README.markdown
Executable file
|
@ -0,0 +1,11 @@
|
|||
# jQuery Mouse Wheel Plugin
|
||||
|
||||
A jQuery plugin that adds cross-browser mouse wheel support.
|
||||
|
||||
The latest stable release can be downloaded from the [project page](http://plugins.jquery.com/project/mousewheel)
|
||||
|
||||
## License
|
||||
|
||||
The mousewheel plugin is dual licensed *(just like jQuery)* under the [MIT](http://www.opensource.org/licenses/mit-license.php) and [GPL](http://www.opensource.org/licenses/gpl-license.php) licenses.
|
||||
|
||||
Copyright (c) 2009 [Brandon Aaron](http://brandonaaron.net)
|
60
public/javascripts/jquery.mousewheel.3.0.2/jquery.mousewheel.js
Executable file
60
public/javascripts/jquery.mousewheel.3.0.2/jquery.mousewheel.js
Executable file
|
@ -0,0 +1,60 @@
|
|||
/*! Copyright (c) 2009 Brandon Aaron (http://brandonaaron.net)
|
||||
* Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
|
||||
* and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
|
||||
* Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
|
||||
* Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
|
||||
*
|
||||
* Version: 3.0.2
|
||||
*
|
||||
* Requires: 1.2.2+
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
|
||||
var types = ['DOMMouseScroll', 'mousewheel'];
|
||||
|
||||
$.event.special.mousewheel = {
|
||||
setup: function() {
|
||||
if ( this.addEventListener )
|
||||
for ( var i=types.length; i; )
|
||||
this.addEventListener( types[--i], handler, false );
|
||||
else
|
||||
this.onmousewheel = handler;
|
||||
},
|
||||
|
||||
teardown: function() {
|
||||
if ( this.removeEventListener )
|
||||
for ( var i=types.length; i; )
|
||||
this.removeEventListener( types[--i], handler, false );
|
||||
else
|
||||
this.onmousewheel = null;
|
||||
}
|
||||
};
|
||||
|
||||
$.fn.extend({
|
||||
mousewheel: function(fn) {
|
||||
return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel");
|
||||
},
|
||||
|
||||
unmousewheel: function(fn) {
|
||||
return this.unbind("mousewheel", fn);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function handler(event) {
|
||||
var args = [].slice.call( arguments, 1 ), delta = 0, returnValue = true;
|
||||
|
||||
event = $.event.fix(event || window.event);
|
||||
event.type = "mousewheel";
|
||||
|
||||
if ( event.wheelDelta ) delta = event.wheelDelta/120;
|
||||
if ( event.detail ) delta = -event.detail/3;
|
||||
|
||||
// Add events and delta to the front of the arguments
|
||||
args.unshift(event, delta);
|
||||
|
||||
return $.event.handle.apply(this, args);
|
||||
}
|
||||
|
||||
})(jQuery);
|
11
public/javascripts/jquery.mousewheel.3.0.2/jquery.mousewheel.min.js
vendored
Normal file
11
public/javascripts/jquery.mousewheel.3.0.2/jquery.mousewheel.min.js
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
/* Copyright (c) 2009 Brandon Aaron (http://brandonaaron.net)
|
||||
* Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
|
||||
* and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
|
||||
* Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
|
||||
* Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
|
||||
*
|
||||
* Version: 3.0.2
|
||||
*
|
||||
* Requires: 1.2.2+
|
||||
*/
|
||||
(function(c){var a=["DOMMouseScroll","mousewheel"];c.event.special.mousewheel={setup:function(){if(this.addEventListener){for(var d=a.length;d;){this.addEventListener(a[--d],b,false)}}else{this.onmousewheel=b}},teardown:function(){if(this.removeEventListener){for(var d=a.length;d;){this.removeEventListener(a[--d],b,false)}}else{this.onmousewheel=null}}};c.fn.extend({mousewheel:function(d){return d?this.bind("mousewheel",d):this.trigger("mousewheel")},unmousewheel:function(d){return this.unbind("mousewheel",d)}});function b(f){var d=[].slice.call(arguments,1),g=0,e=true;f=c.event.fix(f||window.event);f.type="mousewheel";if(f.wheelDelta){g=f.wheelDelta/120}if(f.detail){g=-f.detail/3}d.unshift(f,g);return c.event.handle.apply(this,d)}})(jQuery);
|
204
public/javascripts/jquery.mousewheel.3.0.2/test/index.html
Executable file
204
public/javascripts/jquery.mousewheel.3.0.2/test/index.html
Executable file
|
@ -0,0 +1,204 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Testing mousewheel plugin</title>
|
||||
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js"></script>
|
||||
<script type="text/javascript" src="../jquery.mousewheel.js"></script>
|
||||
|
||||
<style>
|
||||
#test1 {
|
||||
background-color: #000;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
color: #fff;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#test2 {
|
||||
background-color: #333;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
color: #fff;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#test3 {
|
||||
background-color: #666;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
color: #fff;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#test4 {
|
||||
background-color: #000;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
color: #fff;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#test5 {
|
||||
background-color: #333;
|
||||
padding: 5px;
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
color: #fff;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#test6 {
|
||||
background-color: #666;
|
||||
padding: 5px;
|
||||
width: 250px;
|
||||
height: 250px;
|
||||
color: #fff;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#test7 {
|
||||
background-color: #000;
|
||||
padding: 5px;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
color: #fff;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#forceScroll {
|
||||
clear: both;
|
||||
height: 1000px;
|
||||
}
|
||||
|
||||
#logger {
|
||||
position: absolute;
|
||||
top: 395px;
|
||||
left: 12px;
|
||||
width: 380px;
|
||||
height: 290px;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
$('#userAgent').html(navigator.userAgent);
|
||||
|
||||
|
||||
$('#test1')
|
||||
.mousewheel(function(event, delta) {
|
||||
if (delta > 0)
|
||||
log('#test1: up ('+delta+')');
|
||||
else if (delta < 0)
|
||||
log('#test1: down ('+delta+')');
|
||||
|
||||
log('pageX: ' + event.pageX + ' pageY: ' + event.pageY );
|
||||
});
|
||||
|
||||
$('#test2')
|
||||
.mousewheel(function(event, delta) {
|
||||
if (delta > 0)
|
||||
log('#test2: up ('+delta+')');
|
||||
else if (delta < 0)
|
||||
log('#test2: down ('+delta+')');
|
||||
return false; // prevent default
|
||||
});
|
||||
|
||||
$('#test3')
|
||||
.hover(function() { log('#test3: mouseover'); }, function() { log('#test3: mouseout'); })
|
||||
.mousewheel(function(event, delta) {
|
||||
log('#test3: I should not have been logged');
|
||||
})
|
||||
.unmousewheel();
|
||||
|
||||
var testRemoval = function(event, delta) {
|
||||
log('#test4: I should not have been logged');
|
||||
};
|
||||
|
||||
$('#test4')
|
||||
.mousewheel(function(event, delta) {
|
||||
if (delta > 0)
|
||||
log('#test4: up ('+delta+')');
|
||||
else if (delta < 0)
|
||||
log('#test4: down ('+delta+')');
|
||||
return false;
|
||||
})
|
||||
.mousewheel(testRemoval)
|
||||
.mousewheel(function(event, delta) {
|
||||
if (delta > 0)
|
||||
log('#test4: up ('+delta+') from 2nd handler');
|
||||
else if (delta < 0)
|
||||
log('#test4: down ('+delta+') from 2nd handler');
|
||||
return false;
|
||||
})
|
||||
.unmousewheel(testRemoval);
|
||||
|
||||
$('#test5')
|
||||
.mousewheel(function(event, delta) {
|
||||
if (delta > 0)
|
||||
log('#test5: up ('+delta+')');
|
||||
else if (delta < 0)
|
||||
log('#test5: down ('+delta+')');
|
||||
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
$('#test6')
|
||||
.mousewheel(function(event, delta) {
|
||||
if (delta > 0)
|
||||
log('#test6: up ('+delta+')');
|
||||
else if (delta < 0)
|
||||
log('#test6: down ('+delta+')');
|
||||
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
$('#test7')
|
||||
.mousewheel(function(event, delta) {
|
||||
if (delta > 0)
|
||||
log('#test7: up ('+delta+')');
|
||||
else if (delta < 0)
|
||||
log('#test7 down ('+delta+')');
|
||||
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
function log(msg) {
|
||||
$('#logger').append(msg+'<br>').scrollTop(999999);
|
||||
};
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id="banner">jQuery mousewheel.js - Test</h1>
|
||||
<h2 id="userAgent"></h2>
|
||||
|
||||
<ul>
|
||||
<li><strong>Test1</strong> is just using the plain on mousewheel() with a function passed in and does not prevent default. (Also logs the value of pageX and pageY event properties.)</li>
|
||||
<li><strong>Test2</strong> should prevent the default action.</li>
|
||||
<li><strong>Test3</strong> should only log a mouseover and mouseout event. Testing unmousewheel().</li>
|
||||
<li><strong>Test4</strong> has two handlers.</li>
|
||||
<li><strong>Test5</strong> is like Test2 but has children. The children should not scroll until mousing over them.</li>
|
||||
<li><strong>Test6</strong> is like Test5 but should not scroll children or parents.</li>
|
||||
<li><strong>Test7</strong> is like Test6 but has no children. It will propagate the event and scroll test 6 as well.</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<div id="test1"><p>Test1</p></div>
|
||||
<div id="test2"><p>Test2</p></div>
|
||||
<div id="test3"><p>Test3</p></div>
|
||||
<div id="test4"><p>Test4</p></div>
|
||||
<div id="test5">
|
||||
<p>Test5</p>
|
||||
<div id="test6">
|
||||
<p>Test6</p>
|
||||
<div id="test7"><p>Test7</p></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="logger"></div>
|
||||
|
||||
<div id="forceScroll"></div>
|
||||
</body>
|
||||
</html>
|
457
public/javascripts/scrollable/jquery.scrollable-1.0.2.js
Normal file
457
public/javascripts/scrollable/jquery.scrollable-1.0.2.js
Normal file
|
@ -0,0 +1,457 @@
|
|||
/**
|
||||
* jquery.scrollable 1.0.2. Put your HTML scroll.
|
||||
*
|
||||
* Copyright (c) 2009 Tero Piirainen
|
||||
* http://flowplayer.org/tools/scrollable.html
|
||||
*
|
||||
* Dual licensed under MIT and GPL 2+ licenses
|
||||
* http://www.opensource.org/licenses
|
||||
*
|
||||
* Launch : March 2008
|
||||
* Version : 1.0.2 - Tue Feb 24 2009 10:52:08 GMT-0000 (GMT+00:00)
|
||||
*/
|
||||
(function($) {
|
||||
|
||||
function fireEvent(opts, name, self, arg) {
|
||||
var fn = opts[name];
|
||||
|
||||
if ($.isFunction(fn)) {
|
||||
try {
|
||||
return fn.call(self, arg);
|
||||
|
||||
} catch (error) {
|
||||
if (opts.alert) {
|
||||
alert("Error calling scrollable." + name + ": " + error);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
var current = null;
|
||||
|
||||
|
||||
// constructor
|
||||
function Scrollable(root, conf) {
|
||||
|
||||
// current instance
|
||||
var self = this;
|
||||
if (!current) { current = self; }
|
||||
|
||||
// horizontal flag
|
||||
var horizontal = !conf.vertical;
|
||||
|
||||
|
||||
// wrap (root elements for items)
|
||||
var wrap = $(conf.items, root);
|
||||
|
||||
// current index
|
||||
var index = 0;
|
||||
|
||||
|
||||
// get handle to navigational elements
|
||||
var navi = root.siblings(conf.navi).eq(0);
|
||||
var prev = root.siblings(conf.prev).eq(0);
|
||||
var next = root.siblings(conf.next).eq(0);
|
||||
var prevPage = root.siblings(conf.prevPage).eq(0);
|
||||
var nextPage = root.siblings(conf.nextPage).eq(0);
|
||||
|
||||
|
||||
// methods
|
||||
$.extend(self, {
|
||||
|
||||
getVersion: function() {
|
||||
return [1, 0, 1];
|
||||
},
|
||||
|
||||
getIndex: function() {
|
||||
return index;
|
||||
},
|
||||
|
||||
getConf: function() {
|
||||
return conf;
|
||||
},
|
||||
|
||||
getSize: function() {
|
||||
return self.getItems().size();
|
||||
},
|
||||
|
||||
getPageAmount: function() {
|
||||
return Math.ceil(this.getSize() / conf.size);
|
||||
},
|
||||
|
||||
getPageIndex: function() {
|
||||
return Math.ceil(index / conf.size);
|
||||
},
|
||||
|
||||
getRoot: function() {
|
||||
return root;
|
||||
},
|
||||
|
||||
getItemWrap: function() {
|
||||
return wrap;
|
||||
},
|
||||
|
||||
getItems: function() {
|
||||
return wrap.children();
|
||||
},
|
||||
|
||||
/* all seeking functions depend on this */
|
||||
seekTo: function(i, time, fn) {
|
||||
|
||||
// default speed
|
||||
time = time || conf.speed;
|
||||
|
||||
// function given as second argument
|
||||
if ($.isFunction(time)) {
|
||||
fn = time;
|
||||
time = conf.speed;
|
||||
}
|
||||
|
||||
if (i < 0) { i = 0; }
|
||||
if (i > self.getSize() - conf.size) { return self; }
|
||||
|
||||
var item = self.getItems().eq(i);
|
||||
if (!item.length) { return self; }
|
||||
|
||||
// onBeforeSeek
|
||||
if (fireEvent(conf, "onBeforeSeek", self, i) === false) {
|
||||
return self;
|
||||
}
|
||||
|
||||
if (horizontal) {
|
||||
var left = -(item.outerWidth(true) * i);
|
||||
wrap.animate({left: left}, time, conf.easing, fn ? function() { fn.call(self); } : null);
|
||||
|
||||
} else {
|
||||
var top = -(item.outerHeight(true) * i); // wrap.offset().top - item.offset().top;
|
||||
wrap.animate({top: top}, time, conf.easing, fn ? function() { fn.call(self); } : null);
|
||||
}
|
||||
|
||||
|
||||
// navi status update
|
||||
if (navi.length) {
|
||||
var klass = conf.activeClass;
|
||||
var page = Math.ceil(i / conf.size);
|
||||
page = Math.min(page, navi.children().length - 1);
|
||||
navi.children().removeClass(klass).eq(page).addClass(klass);
|
||||
}
|
||||
|
||||
// prev buttons disabled flag
|
||||
if (i === 0) {
|
||||
prev.add(prevPage).addClass(conf.disabledClass);
|
||||
} else {
|
||||
prev.add(prevPage).removeClass(conf.disabledClass);
|
||||
}
|
||||
|
||||
// next buttons disabled flag
|
||||
if (i >= self.getSize() - conf.size) {
|
||||
next.add(nextPage).addClass(conf.disabledClass);
|
||||
} else {
|
||||
next.add(nextPage).removeClass(conf.disabledClass);
|
||||
}
|
||||
|
||||
current = self;
|
||||
index = i;
|
||||
|
||||
// onSeek after index being updated
|
||||
fireEvent(conf, "onSeek", self, i);
|
||||
|
||||
return self;
|
||||
|
||||
},
|
||||
|
||||
move: function(offset, time, fn) {
|
||||
var to = index + offset;
|
||||
if (conf.loop && to > (self.getSize() - conf.size)) {
|
||||
to = 0;
|
||||
}
|
||||
return this.seekTo(to, time, fn);
|
||||
},
|
||||
|
||||
next: function(time, fn) {
|
||||
return this.move(1, time, fn);
|
||||
},
|
||||
|
||||
prev: function(time, fn) {
|
||||
return this.move(-1, time, fn);
|
||||
},
|
||||
|
||||
movePage: function(offset, time, fn) {
|
||||
return this.move(conf.size * offset, time, fn);
|
||||
},
|
||||
|
||||
setPage: function(page, time, fn) {
|
||||
var size = conf.size;
|
||||
var index = size * page;
|
||||
var lastPage = index + size >= this.getSize();
|
||||
if (lastPage) {
|
||||
index = this.getSize() - conf.size;
|
||||
}
|
||||
return this.seekTo(index, time, fn);
|
||||
},
|
||||
|
||||
prevPage: function(time, fn) {
|
||||
return this.setPage(this.getPageIndex() - 1, time, fn);
|
||||
},
|
||||
|
||||
nextPage: function(time, fn) {
|
||||
return this.setPage(this.getPageIndex() + 1, time, fn);
|
||||
},
|
||||
|
||||
begin: function(time, fn) {
|
||||
return this.seekTo(0, time, fn);
|
||||
},
|
||||
|
||||
end: function(time, fn) {
|
||||
return this.seekTo(this.getSize() - conf.size, time, fn);
|
||||
},
|
||||
|
||||
reload: function() {
|
||||
return load();
|
||||
},
|
||||
|
||||
click: function(index, time, fn) {
|
||||
|
||||
var item = self.getItems().eq(index);
|
||||
var klass = conf.activeClass;
|
||||
|
||||
if (!item.hasClass(klass) && (index >= 0 || index < this.getSize())) {
|
||||
self.getItems().removeClass(klass);
|
||||
item.addClass(klass);
|
||||
var delta = Math.floor(conf.size / 2);
|
||||
var to = index - delta;
|
||||
|
||||
// next to last item must work
|
||||
if (to > self.getSize() - conf.size) { to--; }
|
||||
|
||||
if (to !== index) {
|
||||
return this.seekTo(to, time, fn);
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
// mousewheel
|
||||
if ($.isFunction($.fn.mousewheel)) {
|
||||
root.bind("mousewheel.scrollable", function(e, delta) {
|
||||
// opera goes to opposite direction
|
||||
var step = $.browser.opera ? 1 : -1;
|
||||
|
||||
self.move(delta > 0 ? step : -step, 50);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// prev button
|
||||
prev.addClass(conf.disabledClass).click(function() {
|
||||
self.prev();
|
||||
});
|
||||
|
||||
|
||||
// next button
|
||||
next.click(function() {
|
||||
self.next();
|
||||
});
|
||||
|
||||
// prev page button
|
||||
nextPage.click(function() {
|
||||
self.nextPage();
|
||||
});
|
||||
|
||||
|
||||
// next page button
|
||||
prevPage.addClass(conf.disabledClass).click(function() {
|
||||
self.prevPage();
|
||||
});
|
||||
|
||||
|
||||
// keyboard
|
||||
if (conf.keyboard) {
|
||||
|
||||
// unfortunately window.keypress does not work on IE.
|
||||
$(window).unbind("keypress.scrollable").bind("keypress.scrollable", function(evt) {
|
||||
|
||||
var el = current;
|
||||
if (!el) { return; }
|
||||
|
||||
if (horizontal && (evt.keyCode == 37 || evt.keyCode == 39)) {
|
||||
el.move(evt.keyCode == 37 ? -1 : 1);
|
||||
return evt.preventDefault();
|
||||
}
|
||||
|
||||
if (!horizontal && (evt.keyCode == 38 || evt.keyCode == 40)) {
|
||||
el.move(evt.keyCode == 38 ? -1 : 1);
|
||||
return evt.preventDefault();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// navi
|
||||
function load() {
|
||||
|
||||
navi.each(function() {
|
||||
|
||||
var nav = $(this);
|
||||
|
||||
// generate new entries
|
||||
if (nav.is(":empty") || nav.data("me") == self) {
|
||||
|
||||
nav.empty();
|
||||
nav.data("me", self);
|
||||
|
||||
for (var i = 0; i < self.getPageAmount(); i++) {
|
||||
|
||||
var item = $("<" + conf.naviItem + "/>").attr("href", i).click(function(e) {
|
||||
var el = $(this);
|
||||
el.parent().children().removeClass(conf.activeClass);
|
||||
el.addClass(conf.activeClass);
|
||||
self.setPage(el.attr("href"));
|
||||
return e.preventDefault();
|
||||
});
|
||||
|
||||
if (i === 0) { item.addClass(conf.activeClass); }
|
||||
nav.append(item);
|
||||
}
|
||||
|
||||
// assign onClick events to existing entries
|
||||
} else {
|
||||
|
||||
// find a entries first -> syntaxically correct
|
||||
var els = nav.children();
|
||||
|
||||
els.each(function(i) {
|
||||
var item = $(this);
|
||||
item.attr("href", i);
|
||||
if (i === 0) { item.addClass(conf.activeClass); }
|
||||
|
||||
item.click(function() {
|
||||
nav.find("." + conf.activeClass).removeClass(conf.activeClass);
|
||||
item.addClass(conf.activeClass);
|
||||
self.setPage(item.attr("href"));
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
// item.click()
|
||||
if (conf.clickable) {
|
||||
self.getItems().each(function(index, arg) {
|
||||
var el = $(this);
|
||||
if (!el.data("set")) {
|
||||
el.bind("click.scrollable", function() {
|
||||
self.click(index);
|
||||
});
|
||||
el.data("set", true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// hover
|
||||
if (conf.hoverClass) {
|
||||
self.getItems().hover(function() {
|
||||
$(this).addClass(conf.hoverClass);
|
||||
}, function() {
|
||||
$(this).removeClass(conf.hoverClass);
|
||||
});
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
load();
|
||||
|
||||
|
||||
// interval stuff
|
||||
var timer = null;
|
||||
|
||||
function setTimer() {
|
||||
timer = setInterval(function() {
|
||||
self.next();
|
||||
|
||||
}, conf.interval);
|
||||
}
|
||||
|
||||
if (conf.interval > 0) {
|
||||
|
||||
root.hover(function() {
|
||||
clearInterval(timer);
|
||||
}, function() {
|
||||
setTimer();
|
||||
});
|
||||
|
||||
setTimer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// jQuery plugin implementation
|
||||
jQuery.prototype.scrollable = function(conf) {
|
||||
|
||||
// already constructed --> return API
|
||||
var api = this.eq(typeof conf == 'number' ? conf : 0).data("scrollable");
|
||||
if (api) { return api; }
|
||||
|
||||
|
||||
var opts = {
|
||||
|
||||
// basics
|
||||
size: 5,
|
||||
vertical:false,
|
||||
clickable: true,
|
||||
loop: false,
|
||||
interval: 0,
|
||||
speed: 400,
|
||||
keyboard: true,
|
||||
|
||||
// other
|
||||
activeClass:'active',
|
||||
disabledClass: 'disabled',
|
||||
hoverClass: null,
|
||||
easing: 'swing',
|
||||
|
||||
// navigational elements
|
||||
items: '.items',
|
||||
prev: '.prev',
|
||||
next: '.next',
|
||||
prevPage: '.prevPage',
|
||||
nextPage: '.nextPage',
|
||||
navi: '.navi',
|
||||
naviItem: 'a',
|
||||
|
||||
|
||||
// callbacks
|
||||
onBeforeSeek: null,
|
||||
onSeek: null,
|
||||
alert: true
|
||||
};
|
||||
|
||||
|
||||
$.extend(opts, conf);
|
||||
|
||||
this.each(function() {
|
||||
var el = new Scrollable($(this), opts);
|
||||
$(this).data("scrollable", el);
|
||||
});
|
||||
|
||||
return this;
|
||||
|
||||
};
|
||||
|
||||
|
||||
})(jQuery);
|
13
public/javascripts/scrollable/jquery.scrollable-1.0.2.min.js
vendored
Normal file
13
public/javascripts/scrollable/jquery.scrollable-1.0.2.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
223
public/javascripts/shortcut/shortcut.js
Normal file
223
public/javascripts/shortcut/shortcut.js
Normal file
|
@ -0,0 +1,223 @@
|
|||
/**
|
||||
* http://www.openjs.com/scripts/events/keyboard_shortcuts/
|
||||
* Version : 2.01.B
|
||||
* By Binny V A
|
||||
* License : BSD
|
||||
*/
|
||||
shortcut = {
|
||||
'all_shortcuts':{},//All the shortcuts are stored in this array
|
||||
'add': function(shortcut_combination,callback,opt) {
|
||||
//Provide a set of default options
|
||||
var default_options = {
|
||||
'type':'keydown',
|
||||
'propagate':false,
|
||||
'disable_in_input':false,
|
||||
'target':document,
|
||||
'keycode':false
|
||||
}
|
||||
if(!opt) opt = default_options;
|
||||
else {
|
||||
for(var dfo in default_options) {
|
||||
if(typeof opt[dfo] == 'undefined') opt[dfo] = default_options[dfo];
|
||||
}
|
||||
}
|
||||
|
||||
var ele = opt.target;
|
||||
if(typeof opt.target == 'string') ele = document.getElementById(opt.target);
|
||||
var ths = this;
|
||||
shortcut_combination = shortcut_combination.toLowerCase();
|
||||
|
||||
//The function to be called at keypress
|
||||
var func = function(e) {
|
||||
e = e || window.event;
|
||||
|
||||
if(opt['disable_in_input']) { //Don't enable shortcut keys in Input, Textarea fields
|
||||
var element;
|
||||
if(e.target) element=e.target;
|
||||
else if(e.srcElement) element=e.srcElement;
|
||||
if(element.nodeType==3) element=element.parentNode;
|
||||
|
||||
if(element.tagName == 'INPUT' || element.tagName == 'TEXTAREA') return;
|
||||
}
|
||||
|
||||
//Find Which key is pressed
|
||||
if (e.keyCode) code = e.keyCode;
|
||||
else if (e.which) code = e.which;
|
||||
var character = String.fromCharCode(code).toLowerCase();
|
||||
|
||||
if(code == 188) character=","; //If the user presses , when the type is onkeydown
|
||||
if(code == 190) character="."; //If the user presses , when the type is onkeydown
|
||||
|
||||
var keys = shortcut_combination.split("+");
|
||||
//Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked
|
||||
var kp = 0;
|
||||
|
||||
//Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken
|
||||
var shift_nums = {
|
||||
"`":"~",
|
||||
"1":"!",
|
||||
"2":"@",
|
||||
"3":"#",
|
||||
"4":"$",
|
||||
"5":"%",
|
||||
"6":"^",
|
||||
"7":"&",
|
||||
"8":"*",
|
||||
"9":"(",
|
||||
"0":")",
|
||||
"-":"_",
|
||||
"=":"+",
|
||||
";":":",
|
||||
"'":"\"",
|
||||
",":"<",
|
||||
".":">",
|
||||
"/":"?",
|
||||
"\\":"|"
|
||||
}
|
||||
//Special Keys - and their codes
|
||||
var special_keys = {
|
||||
'esc':27,
|
||||
'escape':27,
|
||||
'tab':9,
|
||||
'space':32,
|
||||
'return':13,
|
||||
'enter':13,
|
||||
'backspace':8,
|
||||
|
||||
'scrolllock':145,
|
||||
'scroll_lock':145,
|
||||
'scroll':145,
|
||||
'capslock':20,
|
||||
'caps_lock':20,
|
||||
'caps':20,
|
||||
'numlock':144,
|
||||
'num_lock':144,
|
||||
'num':144,
|
||||
|
||||
'pause':19,
|
||||
'break':19,
|
||||
|
||||
'insert':45,
|
||||
'home':36,
|
||||
'delete':46,
|
||||
'end':35,
|
||||
|
||||
'pageup':33,
|
||||
'page_up':33,
|
||||
'pu':33,
|
||||
|
||||
'pagedown':34,
|
||||
'page_down':34,
|
||||
'pd':34,
|
||||
|
||||
'left':37,
|
||||
'up':38,
|
||||
'right':39,
|
||||
'down':40,
|
||||
|
||||
'f1':112,
|
||||
'f2':113,
|
||||
'f3':114,
|
||||
'f4':115,
|
||||
'f5':116,
|
||||
'f6':117,
|
||||
'f7':118,
|
||||
'f8':119,
|
||||
'f9':120,
|
||||
'f10':121,
|
||||
'f11':122,
|
||||
'f12':123
|
||||
}
|
||||
|
||||
var modifiers = {
|
||||
shift: { wanted:false, pressed:false},
|
||||
ctrl : { wanted:false, pressed:false},
|
||||
alt : { wanted:false, pressed:false},
|
||||
meta : { wanted:false, pressed:false} //Meta is Mac specific
|
||||
};
|
||||
|
||||
if(e.ctrlKey) modifiers.ctrl.pressed = true;
|
||||
if(e.shiftKey) modifiers.shift.pressed = true;
|
||||
if(e.altKey) modifiers.alt.pressed = true;
|
||||
if(e.metaKey) modifiers.meta.pressed = true;
|
||||
|
||||
for(var i=0; k=keys[i],i<keys.length; i++) {
|
||||
//Modifiers
|
||||
if(k == 'ctrl' || k == 'control') {
|
||||
kp++;
|
||||
modifiers.ctrl.wanted = true;
|
||||
|
||||
} else if(k == 'shift') {
|
||||
kp++;
|
||||
modifiers.shift.wanted = true;
|
||||
|
||||
} else if(k == 'alt') {
|
||||
kp++;
|
||||
modifiers.alt.wanted = true;
|
||||
} else if(k == 'meta') {
|
||||
kp++;
|
||||
modifiers.meta.wanted = true;
|
||||
} else if(k.length > 1) { //If it is a special key
|
||||
if(special_keys[k] == code) kp++;
|
||||
|
||||
} else if(opt['keycode']) {
|
||||
if(opt['keycode'] == code) kp++;
|
||||
|
||||
} else { //The special keys did not match
|
||||
if(character == k) kp++;
|
||||
else {
|
||||
if(shift_nums[character] && e.shiftKey) { //Stupid Shift key bug created by using lowercase
|
||||
character = shift_nums[character];
|
||||
if(character == k) kp++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(kp == keys.length &&
|
||||
modifiers.ctrl.pressed == modifiers.ctrl.wanted &&
|
||||
modifiers.shift.pressed == modifiers.shift.wanted &&
|
||||
modifiers.alt.pressed == modifiers.alt.wanted &&
|
||||
modifiers.meta.pressed == modifiers.meta.wanted) {
|
||||
callback(e);
|
||||
|
||||
if(!opt['propagate']) { //Stop the event
|
||||
//e.cancelBubble is supported by IE - this will kill the bubbling process.
|
||||
e.cancelBubble = true;
|
||||
e.returnValue = false;
|
||||
|
||||
//e.stopPropagation works in Firefox.
|
||||
if (e.stopPropagation) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.all_shortcuts[shortcut_combination] = {
|
||||
'callback':func,
|
||||
'target':ele,
|
||||
'event': opt['type']
|
||||
};
|
||||
//Attach the function with the event
|
||||
if(ele.addEventListener) ele.addEventListener(opt['type'], func, false);
|
||||
else if(ele.attachEvent) ele.attachEvent('on'+opt['type'], func);
|
||||
else ele['on'+opt['type']] = func;
|
||||
},
|
||||
|
||||
//Remove the shortcut - just specify the shortcut and I will remove the binding
|
||||
'remove':function(shortcut_combination) {
|
||||
shortcut_combination = shortcut_combination.toLowerCase();
|
||||
var binding = this.all_shortcuts[shortcut_combination];
|
||||
delete(this.all_shortcuts[shortcut_combination])
|
||||
if(!binding) return;
|
||||
var type = binding['event'];
|
||||
var ele = binding['target'];
|
||||
var callback = binding['callback'];
|
||||
|
||||
if(ele.detachEvent) ele.detachEvent('on'+type, callback);
|
||||
else if(ele.removeEventListener) ele.removeEventListener(type, callback, false);
|
||||
else ele['on'+type] = false;
|
||||
}
|
||||
}
|
267
public/javascripts/tag/tag.js
Normal file
267
public/javascripts/tag/tag.js
Normal file
|
@ -0,0 +1,267 @@
|
|||
/*
|
||||
@author: remy sharp / http://remysharp.com
|
||||
@url: http://remysharp.com/2007/12/28/jquery-tag-suggestion/
|
||||
@usage: setGlobalTags(['javascript', 'jquery', 'java', 'json']); // applied tags to be used for all implementations
|
||||
$('input.tags').tagSuggest(options);
|
||||
|
||||
The selector is the element that the user enters their tag list
|
||||
@params:
|
||||
matchClass - class applied to the suggestions, defaults to 'tagMatches'
|
||||
tagContainer - the type of element uses to contain the suggestions, defaults to 'span'
|
||||
tagWrap - the type of element the suggestions a wrapped in, defaults to 'span'
|
||||
sort - boolean to force the sorted order of suggestions, defaults to false
|
||||
url - optional url to get suggestions if setGlobalTags isn't used. Must return array of suggested tags
|
||||
tags - optional array of tags specific to this instance of element matches
|
||||
delay - optional sets the delay between keyup and the request - can help throttle ajax requests, defaults to zero delay
|
||||
separator - optional separator string, defaults to ' ' (Brian J. Cardiff)
|
||||
@license: Creative Commons License - ShareAlike http://creativecommons.org/licenses/by-sa/3.0/
|
||||
@version: 1.4
|
||||
@changes: fixed filtering to ajax hits
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
var globalTags = [];
|
||||
|
||||
// creates a public function within our private code.
|
||||
// tags can either be an array of strings OR
|
||||
// array of objects containing a 'tag' attribute
|
||||
window.setGlobalTags = function(tags /* array */) {
|
||||
globalTags = getTags(tags);
|
||||
};
|
||||
|
||||
function getTags(tags) {
|
||||
var tag, i, goodTags = [];
|
||||
for (i = 0; i < tags.length; i++) {
|
||||
tag = tags[i];
|
||||
if (typeof tags[i] == 'object') {
|
||||
tag = tags[i].tag;
|
||||
}
|
||||
goodTags.push(tag.toLowerCase());
|
||||
}
|
||||
|
||||
return goodTags;
|
||||
}
|
||||
|
||||
$.fn.tagSuggest = function (options) {
|
||||
var defaults = {
|
||||
'matchClass' : 'tagMatches',
|
||||
'tagContainer' : 'span',
|
||||
'tagWrap' : 'span',
|
||||
'sort' : true,
|
||||
'tags' : null,
|
||||
'url' : null,
|
||||
'delay' : 0,
|
||||
'separator' : ' '
|
||||
};
|
||||
|
||||
var i, tag, userTags = [], settings = $.extend({}, defaults, options);
|
||||
|
||||
if (settings.tags) {
|
||||
userTags = getTags(settings.tags);
|
||||
} else {
|
||||
userTags = globalTags;
|
||||
}
|
||||
|
||||
return this.each(function () {
|
||||
var tagsElm = $(this);
|
||||
var elm = this;
|
||||
var matches, fromTab = false;
|
||||
var suggestionsShow = false;
|
||||
var workingTags = [];
|
||||
var currentTag = {"position": 0, tag: ""};
|
||||
var tagMatches = document.createElement(settings.tagContainer);
|
||||
|
||||
function showSuggestionsDelayed(el, key) {
|
||||
if (settings.delay) {
|
||||
if (elm.timer) clearTimeout(elm.timer);
|
||||
elm.timer = setTimeout(function () {
|
||||
showSuggestions(el, key);
|
||||
}, settings.delay);
|
||||
} else {
|
||||
showSuggestions(el, key);
|
||||
}
|
||||
}
|
||||
|
||||
function showSuggestions(el, key) {
|
||||
workingTags = el.value.split(settings.separator);
|
||||
matches = [];
|
||||
var i, html = '', chosenTags = {}, tagSelected = false;
|
||||
|
||||
// we're looking to complete the tag on currentTag.position (to start with)
|
||||
currentTag = { position: currentTags.length-1, tag: '' };
|
||||
|
||||
for (i = 0; i < currentTags.length && i < workingTags.length; i++) {
|
||||
if (!tagSelected &&
|
||||
currentTags[i].toLowerCase() != workingTags[i].toLowerCase()) {
|
||||
currentTag = { position: i, tag: workingTags[i].toLowerCase() };
|
||||
tagSelected = true;
|
||||
}
|
||||
// lookup for filtering out chosen tags
|
||||
chosenTags[currentTags[i].toLowerCase()] = true;
|
||||
}
|
||||
|
||||
if (currentTag.tag) {
|
||||
// collect potential tags
|
||||
if (settings.url) {
|
||||
$.ajax({
|
||||
'url' : settings.url,
|
||||
'dataType' : 'json',
|
||||
'data' : { 'tag' : currentTag.tag },
|
||||
'async' : false, // wait until this is ajax hit is complete before continue
|
||||
'success' : function (m) {
|
||||
matches = m;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
for (i = 0; i < userTags.length; i++) {
|
||||
if (userTags[i].indexOf(currentTag.tag) === 0) {
|
||||
matches.push(userTags[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
matches = $.grep(matches, function (v, i) {
|
||||
return !chosenTags[v.toLowerCase()];
|
||||
});
|
||||
|
||||
if (settings.sort) {
|
||||
matches = matches.sort();
|
||||
}
|
||||
|
||||
for (i = 0; i < matches.length; i++) {
|
||||
html += '<' + settings.tagWrap + ' class="_tag_suggestion">' + matches[i] + '</' + settings.tagWrap + '>';
|
||||
}
|
||||
|
||||
tagMatches.html(html);
|
||||
suggestionsShow = !!(matches.length);
|
||||
} else {
|
||||
hideSuggestions();
|
||||
}
|
||||
}
|
||||
|
||||
function hideSuggestions() {
|
||||
tagMatches.empty();
|
||||
matches = [];
|
||||
suggestionsShow = false;
|
||||
}
|
||||
|
||||
function setSelection() {
|
||||
var v = tagsElm.val();
|
||||
|
||||
// tweak for hintted elements
|
||||
// http://remysharp.com/2007/01/25/jquery-tutorial-text-box-hints/
|
||||
if (v == tagsElm.attr('title') && tagsElm.is('.hint')) v = '';
|
||||
|
||||
currentTags = v.split(settings.separator);
|
||||
hideSuggestions();
|
||||
}
|
||||
|
||||
function chooseTag(tag) {
|
||||
var i, index;
|
||||
for (i = 0; i < currentTags.length; i++) {
|
||||
if (currentTags[i].toLowerCase() != workingTags[i].toLowerCase()) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index == workingTags.length - 1) tag = tag + settings.separator;
|
||||
|
||||
workingTags[i] = tag;
|
||||
|
||||
tagsElm.val(workingTags.join(settings.separator));
|
||||
tagsElm.blur().focus();
|
||||
setSelection();
|
||||
}
|
||||
|
||||
function handleKeys(ev) {
|
||||
fromTab = false;
|
||||
var type = ev.type;
|
||||
var resetSelection = false;
|
||||
|
||||
switch (ev.keyCode) {
|
||||
case 37: // ignore cases (arrow keys)
|
||||
case 38:
|
||||
case 39:
|
||||
case 40: {
|
||||
hideSuggestions();
|
||||
return true;
|
||||
}
|
||||
case 224:
|
||||
case 17:
|
||||
case 16:
|
||||
case 18: {
|
||||
return true;
|
||||
}
|
||||
|
||||
case 8: {
|
||||
// delete - hide selections if we're empty
|
||||
if (this.value == '') {
|
||||
hideSuggestions();
|
||||
setSelection();
|
||||
return true;
|
||||
} else {
|
||||
type = 'keyup'; // allow drop through
|
||||
resetSelection = true;
|
||||
showSuggestionsDelayed(this);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 9: // return and tab
|
||||
case 13: {
|
||||
if (suggestionsShow) {
|
||||
// complete
|
||||
chooseTag(matches[0]);
|
||||
|
||||
fromTab = true;
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
case 27: {
|
||||
hideSuggestions();
|
||||
setSelection();
|
||||
return true;
|
||||
}
|
||||
case 32: {
|
||||
setSelection();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (type == 'keyup') {
|
||||
switch (ev.charCode) {
|
||||
case 9:
|
||||
case 13: {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (resetSelection) {
|
||||
setSelection();
|
||||
}
|
||||
showSuggestionsDelayed(this, ev.charCode);
|
||||
}
|
||||
}
|
||||
|
||||
tagsElm.after(tagMatches).keypress(handleKeys).keyup(handleKeys).blur(function () {
|
||||
if (fromTab == true || suggestionsShow) { // tweak to support tab selection for Opera & IE
|
||||
fromTab = false;
|
||||
tagsElm.focus();
|
||||
}
|
||||
});
|
||||
|
||||
// replace with jQuery version
|
||||
tagMatches = $(tagMatches).click(function (ev) {
|
||||
if (ev.target.nodeName == settings.tagWrap.toUpperCase() && $(ev.target).is('._tag_suggestion')) {
|
||||
chooseTag(ev.target.innerHTML);
|
||||
}
|
||||
}).addClass(settings.matchClass);
|
||||
|
||||
// initialise
|
||||
setSelection();
|
||||
});
|
||||
};
|
||||
})(jQuery);
|
55
public/stylesheets/application.css
Normal file
55
public/stylesheets/application.css
Normal file
|
@ -0,0 +1,55 @@
|
|||
.galleria_container {
|
||||
margin:0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#thumbstrip {
|
||||
|
||||
/* required settings */
|
||||
position:relative;
|
||||
overflow:hidden;
|
||||
width: 602px;
|
||||
height: 85px;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
background-color: #000000;
|
||||
}
|
||||
|
||||
#thumbstrip ul {
|
||||
/* this cannot be too large */
|
||||
width:20000em;
|
||||
position:absolute;
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
a single item. must be floated on horizontal scrolling
|
||||
typically this element is the one that *you* will style
|
||||
the most.
|
||||
*/
|
||||
#thumbstrip ul li {
|
||||
float:left;
|
||||
background: black;
|
||||
padding: 5px;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
#photo_large img {
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
SPAN.tagMatches {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
SPAN.tagMatches SPAN {
|
||||
padding: 2px;
|
||||
margin-right: 4px;
|
||||
background-color: #0000AB;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
Loading…
Reference in a new issue