tagging implemented. js stuff

rails2
Espen Antonsen 2009-05-25 21:39:43 +02:00
parent c6882bae3c
commit 510b351979
28 changed files with 1520 additions and 46 deletions

View File

@ -16,9 +16,10 @@ class AlbumsController < ApplicationController
format.html
format.json { render :json => @album }
format.xml { render :xml => @album }
format.pdf { render :pdf => @album.title }
end
end
def new
@album = Album.new
end

View File

@ -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 }

View 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

View File

@ -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 )

View File

@ -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

View File

@ -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/>

View File

@ -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) %>
</ul>
<%= 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>

View File

@ -0,0 +1,3 @@
<h1><%= @album.title %></h1>
<%= render :partial => @album.photos.find(:all, :limit => 10) %>

View File

@ -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>

View 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>

View File

@ -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>

View File

@ -0,0 +1 @@
<%= image_tag APP_CONFIG[:photos_path_public] + photo.path %><br/>

View 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 %>

View 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>

View 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 %>

View File

@ -0,0 +1,4 @@
<h1>Tags</h1>
<% for tag in @tags %>
<%= tag.title %>
<% end %>

View File

@ -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'

View File

@ -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') ) )
});

View 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

View 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)

View 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);

View 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);

View 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>

View 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);

File diff suppressed because one or more lines are too long

View 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;
}
}

View 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);

View 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;
}