From 2b9f8b41a83b1a18565fc97b3f29dcee5ac4726d Mon Sep 17 00:00:00 2001 From: Espen Antonsen Date: Thu, 6 Aug 2009 15:01:39 +0200 Subject: [PATCH] better way of handling albums in collection --- app/models/album.rb | 4 +- app/models/collection.rb | 8 +- app/views/collections/_form.html.erb | 27 +- app/views/layouts/application.html.erb | 2 +- config/environments/development.rb | 4 +- config/settings.example.yml | 12 +- public/images/delete-24x24.png | Bin 0 -> 1535 bytes .../javascripts/{application.js => balder.js} | 26 ++ public/javascripts/jquery.livequery.js | 250 ++++++++++++++++++ public/stylesheets/application.css | 26 +- 10 files changed, 341 insertions(+), 18 deletions(-) create mode 100644 public/images/delete-24x24.png rename public/javascripts/{application.js => balder.js} (64%) create mode 100644 public/javascripts/jquery.livequery.js diff --git a/app/models/album.rb b/app/models/album.rb index 7c97946..d411d94 100644 --- a/app/models/album.rb +++ b/app/models/album.rb @@ -13,7 +13,9 @@ class Album < ActiveRecord::Base attr_accessor :tags attr_protected :path - named_scope :untouched, :conditions => "albums.Id IN ( SELECT DISTINCT photos.album_id FROM photos WHERE photos.description IS NULL AND photos.id NOT IN ( SELECT photo_id FROM photo_tags) )", :order => 'title' + named_scope :untouched, :conditions => "albums.id IN ( SELECT DISTINCT photos.album_id FROM photos WHERE photos.description IS NULL AND photos.id NOT IN ( SELECT photo_id FROM photo_tags) )", :order => 'title' + named_scope :unused, :conditions => "albums.id NOT IN (SELECT album_id FROM collection_albums)" + named_scope :used, :conditions => "albums.id IN (SELECT album_id FROM collection_albums)" def to_param "#{id}-#{title.parameterize}" diff --git a/app/models/collection.rb b/app/models/collection.rb index 97967ac..2e81eb7 100644 --- a/app/models/collection.rb +++ b/app/models/collection.rb @@ -1,18 +1,14 @@ class Collection < ActiveRecord::Base has_many :collection_albums has_many :albums, :through => :collection_albums - #accepts_nested_attributes_for :albums, :allow_destroy => true attr_accessor :album_list def to_param - #title.gsub(/[^a-z0-9]+/i, '-') "#{id}-#{title.parameterize}" end - + def album_list=(albums) - self.albums = Album.find(albums) + self.albums = Album.find(albums.map{|album|album[0]}) end - - end \ No newline at end of file diff --git a/app/views/collections/_form.html.erb b/app/views/collections/_form.html.erb index 15743bc..9efc9c2 100644 --- a/app/views/collections/_form.html.erb +++ b/app/views/collections/_form.html.erb @@ -4,5 +4,28 @@ <%= form.label :description %>
<%= form.text_area :description %>
-<%= form.label :album %>
-<%= collection_select :collection, :album_list, Album.find(:all, :order => :title), :id, :title, {:selected => @collection.albums.map{|album|album.id} }, {:multiple => true, :rows => 10} %>
\ No newline at end of file +<%= form.label :albums %>
+ +
+<% for album in @collection.albums %> +<% form.fields_for :album_list do |album_fields| %> + + +<%= image_tag album.photos.first.path_modified_public('album'), :alt => album.title %> +<%= album_fields.hidden_field album.id %> + +<% end %> +<% end %> +
+ +

+<% +grouped_options = [ + ['Available albums',[['Choose album to add','']]], + ['Not used', Album.unused.map{|album|[album.title, album.id]} ], + ['In use', Album.used.map{|album|[album.title, album.id]} ] +] +grouped_options_for_select(grouped_options) +%> +<%= select_tag 'available_albums', grouped_options_for_select(grouped_options) %> +

\ No newline at end of file diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index cd3f53c..68db9da 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -41,7 +41,7 @@ -<%= javascript_include_tag 'jquery-1.3.2.js', 'application' %> +<%= javascript_include_tag 'jquery-1.3.2.js', 'jquery.livequery.js', 'balder' %> <%= yield :javascript %> diff --git a/config/environments/development.rb b/config/environments/development.rb index 85c9a60..429325c 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -14,4 +14,6 @@ config.action_view.debug_rjs = true config.action_controller.perform_caching = false # Don't care if the mailer can't send -config.action_mailer.raise_delivery_errors = false \ No newline at end of file +config.action_mailer.raise_delivery_errors = false + +ENV["RAILS_ASSET_ID"] = "" \ No newline at end of file diff --git a/config/settings.example.yml b/config/settings.example.yml index 3e5ca4c..c702afa 100644 --- a/config/settings.example.yml +++ b/config/settings.example.yml @@ -1,18 +1,18 @@ development: site_name: Photos - photos_path: 'photos' - thumbs_path: 'public/thumbs/' + photos_path: <%= RAILS_ROOT + '/photos/' %> + thumbs_path: <%= RAILS_ROOT + '/public/thumbs/' %> photos_path_public: '/files/' thumbs_path_public: '/thumbs/' production: site_name: Photos - photos_path: 'photos' - thumbs_path: 'public/thumbs/' + photos_path: <%= RAILS_ROOT + '/photos/' %> + thumbs_path: <%= RAILS_ROOT + '/public/thumbs/' %> photos_path_public: '/files/' thumbs_path_public: '/thumbs/' test: site_name: Photos - photos_path: 'photos' - thumbs_path: 'public/thumbs/' + photos_path: <%= RAILS_ROOT + '/photos/' %> + thumbs_path: <%= RAILS_ROOT + '/public/thumbs/' %> photos_path_public: '/files/' thumbs_path_public: '/thumbs/' \ No newline at end of file diff --git a/public/images/delete-24x24.png b/public/images/delete-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..aa1a746ce18c0478fdd113097fbd2f7663ee05bd GIT binary patch literal 1535 zcmVJsTo0u9E32L>ewxLaZVGy*` zSX(vLsHsi+qx~r%RBfd^7ArOuW$^)`JQmnx_wFwDPUqZx6rwerWOC1QzB4o5%(=k+ zr(M|aK4^5pype(aH9DQHL?;LtTD?J_B@BpOzu)J-6$k|CqT?r>m;L_6m9@w5X?oyk zlNUE`$ag;MIb@qNr#NeIF^h_d0u)7LV*x;9$h2ApdOh=aJkWl*0UbAPG+3u3m%n-R z>*i+yoLjdR{A1E~izT{Z;cLqUaJ!j22nvuOs#Hvy zFc~^K@8Hd*rlS_CwRFwtvp50ZCreLGRyIy>5lLrvH?zjZ@)#*&i9~HO zGkbfx54GB&x6hnwhHqCa2XWM0S5#2A1Y9mqQ5d;$uUCYT8M82TX$k8(QUz(&Sh4`P z=aB{dP9i{G38SdVj%W$vzEDftBvh7kz6bKpy*pT6T`Hf`M6 zJzBe2v4#lq6nFve2P=^3c|6%2dyp2 zcWQ24goO`Un(^irpRokJUJ1Aq4nFz1!{z=9s&i+Hv*N5e^*FYK`@NzMU-*14zb!=u z5vXA>1WM~}X@(oScVVJlP?Sm`MoA%dbqoypp*nL0rp3lYlu}Y5dc6!qF97-1X^3Lk zM~^{rP9BaU66?0NGW(_t5aabAs@0U)1^|NrdGdw9?mCAU)XYr9B%_h3WQmlDO4f#H z%Im^#l#ylE)UxE9+zBP1yOjV+H!_Q7l(HJad7)%CFC(%_&3Z>i8GJP>O|+n_quT~! zfEY=x5u*vS57n^b+&qPzEJV})Kw7|Fz6H&r3P^;eN?)x;H>mt@e12Y=sqfL;u@Hm8 zw|A3kaw-qADLIjVvWM60$~S3lPGqjf-oje8ZDl5}Ct?K$(eOl#PrMGbONy$J?mJ3{ z=w50<<`%e!`vo<}U`kF-__okvw`1FmG9?Dt2dW4-C&I2CipQT@J_HlhuCN7E0Z9=5eX+s<+{dqx$7E*_VCl`JVc7m$=T z)>N^fpMC&Mh((-j6mHpPo4E*16fXqPZ1gqL^*N#G{O-UYBc7*m zmrf^5ekwPelM;>?0ZQL%Y@PVvgh6DZX}jUBqaqU}p_ks@$29L(a&BU&;0JXyPM}*) zrorE!_KT@$do*tM!FAVKo_sRLw=)!z5vTJ|(qMma8d<{~HcE;(5DidAr;NeF;Gpd4 zwN7fV#Ke@pbE)CkkNyN}UR(eggJB;1psZ1gVu^a7KSLAn^WPRWrq%hRxVT%AV5l?d lwC4j}Z{zyQ*Pi;@@-JK-(aJ#j*pdJM002ovPDHLkV1n0m+1&sD literal 0 HcmV?d00001 diff --git a/public/javascripts/application.js b/public/javascripts/balder.js similarity index 64% rename from public/javascripts/application.js rename to public/javascripts/balder.js index fca4027..de1f85c 100644 --- a/public/javascripts/application.js +++ b/public/javascripts/balder.js @@ -17,6 +17,32 @@ jQuery(function($) { mapInitialize() $('#map_canvas').show() } + + $('#collection_albums .delete').livequery('click', function() { + $(this).parent('span').fadeOut('slow', function() { $(this).remove() }) + }) + + $("#available_albums").change( function() { + if ( this.value == '' ) { + return false + } + else if ( $('#collection_album_list_' + this.value).length ) { + $('#collection_album_list_' + this.value).parent('span').fadeTo('slow', 0.33, function () { + $(this).fadeTo('slow', 1) + }) + return false + } + $.getJSON("/albums/" + this.value + '/photos', + function(data){ + html = '' + html += '' + $(' + html += '' + html += '' + $('#collection_albums').append(html) + $('#collection_album_list_' + $('#available_albums :selected').val() ).parent('span').fadeIn('slow') + }) + } + ) $("#album_address").change( function() { if( !map ) { diff --git a/public/javascripts/jquery.livequery.js b/public/javascripts/jquery.livequery.js new file mode 100644 index 0000000..dde8ad8 --- /dev/null +++ b/public/javascripts/jquery.livequery.js @@ -0,0 +1,250 @@ +/*! Copyright (c) 2008 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. + * + * Version: 1.0.3 + * Requires jQuery 1.1.3+ + * Docs: http://docs.jquery.com/Plugins/livequery + */ + +(function($) { + +$.extend($.fn, { + livequery: function(type, fn, fn2) { + var self = this, q; + + // Handle different call patterns + if ($.isFunction(type)) + fn2 = fn, fn = type, type = undefined; + + // See if Live Query already exists + $.each( $.livequery.queries, function(i, query) { + if ( self.selector == query.selector && self.context == query.context && + type == query.type && (!fn || fn.$lqguid == query.fn.$lqguid) && (!fn2 || fn2.$lqguid == query.fn2.$lqguid) ) + // Found the query, exit the each loop + return (q = query) && false; + }); + + // Create new Live Query if it wasn't found + q = q || new $.livequery(this.selector, this.context, type, fn, fn2); + + // Make sure it is running + q.stopped = false; + + // Run it immediately for the first time + q.run(); + + // Contnue the chain + return this; + }, + + expire: function(type, fn, fn2) { + var self = this; + + // Handle different call patterns + if ($.isFunction(type)) + fn2 = fn, fn = type, type = undefined; + + // Find the Live Query based on arguments and stop it + $.each( $.livequery.queries, function(i, query) { + if ( self.selector == query.selector && self.context == query.context && + (!type || type == query.type) && (!fn || fn.$lqguid == query.fn.$lqguid) && (!fn2 || fn2.$lqguid == query.fn2.$lqguid) && !this.stopped ) + $.livequery.stop(query.id); + }); + + // Continue the chain + return this; + } +}); + +$.livequery = function(selector, context, type, fn, fn2) { + this.selector = selector; + this.context = context || document; + this.type = type; + this.fn = fn; + this.fn2 = fn2; + this.elements = []; + this.stopped = false; + + // The id is the index of the Live Query in $.livequery.queries + this.id = $.livequery.queries.push(this)-1; + + // Mark the functions for matching later on + fn.$lqguid = fn.$lqguid || $.livequery.guid++; + if (fn2) fn2.$lqguid = fn2.$lqguid || $.livequery.guid++; + + // Return the Live Query + return this; +}; + +$.livequery.prototype = { + stop: function() { + var query = this; + + if ( this.type ) + // Unbind all bound events + this.elements.unbind(this.type, this.fn); + else if (this.fn2) + // Call the second function for all matched elements + this.elements.each(function(i, el) { + query.fn2.apply(el); + }); + + // Clear out matched elements + this.elements = []; + + // Stop the Live Query from running until restarted + this.stopped = true; + }, + + run: function() { + // Short-circuit if stopped + if ( this.stopped ) return; + var query = this; + + var oEls = this.elements, + els = $(this.selector, this.context), + nEls = els.not(oEls); + + // Set elements to the latest set of matched elements + this.elements = els; + + if (this.type) { + // Bind events to newly matched elements + nEls.bind(this.type, this.fn); + + // Unbind events to elements no longer matched + if (oEls.length > 0) + $.each(oEls, function(i, el) { + if ( $.inArray(el, els) < 0 ) + $.event.remove(el, query.type, query.fn); + }); + } + else { + // Call the first function for newly matched elements + nEls.each(function() { + query.fn.apply(this); + }); + + // Call the second function for elements no longer matched + if ( this.fn2 && oEls.length > 0 ) + $.each(oEls, function(i, el) { + if ( $.inArray(el, els) < 0 ) + query.fn2.apply(el); + }); + } + } +}; + +$.extend($.livequery, { + guid: 0, + queries: [], + queue: [], + running: false, + timeout: null, + + checkQueue: function() { + if ( $.livequery.running && $.livequery.queue.length ) { + var length = $.livequery.queue.length; + // Run each Live Query currently in the queue + while ( length-- ) + $.livequery.queries[ $.livequery.queue.shift() ].run(); + } + }, + + pause: function() { + // Don't run anymore Live Queries until restarted + $.livequery.running = false; + }, + + play: function() { + // Restart Live Queries + $.livequery.running = true; + // Request a run of the Live Queries + $.livequery.run(); + }, + + registerPlugin: function() { + $.each( arguments, function(i,n) { + // Short-circuit if the method doesn't exist + if (!$.fn[n]) return; + + // Save a reference to the original method + var old = $.fn[n]; + + // Create a new method + $.fn[n] = function() { + // Call the original method + var r = old.apply(this, arguments); + + // Request a run of the Live Queries + $.livequery.run(); + + // Return the original methods result + return r; + } + }); + }, + + run: function(id) { + if (id != undefined) { + // Put the particular Live Query in the queue if it doesn't already exist + if ( $.inArray(id, $.livequery.queue) < 0 ) + $.livequery.queue.push( id ); + } + else + // Put each Live Query in the queue if it doesn't already exist + $.each( $.livequery.queries, function(id) { + if ( $.inArray(id, $.livequery.queue) < 0 ) + $.livequery.queue.push( id ); + }); + + // Clear timeout if it already exists + if ($.livequery.timeout) clearTimeout($.livequery.timeout); + // Create a timeout to check the queue and actually run the Live Queries + $.livequery.timeout = setTimeout($.livequery.checkQueue, 20); + }, + + stop: function(id) { + if (id != undefined) + // Stop are particular Live Query + $.livequery.queries[ id ].stop(); + else + // Stop all Live Queries + $.each( $.livequery.queries, function(id) { + $.livequery.queries[ id ].stop(); + }); + } +}); + +// Register core DOM manipulation methods +$.livequery.registerPlugin('append', 'prepend', 'after', 'before', 'wrap', 'attr', 'removeAttr', 'addClass', 'removeClass', 'toggleClass', 'empty', 'remove'); + +// Run Live Queries when the Document is ready +$(function() { $.livequery.play(); }); + + +// Save a reference to the original init method +var init = $.prototype.init; + +// Create a new init method that exposes two new properties: selector and context +$.prototype.init = function(a,c) { + // Call the original init and save the result + var r = init.apply(this, arguments); + + // Copy over properties if they exist already + if (a && a.selector) + r.context = a.context, r.selector = a.selector; + + // Set properties + if ( typeof a == 'string' ) + r.context = c || document, r.selector = a; + + // Return the result + return r; +}; + +// Give the init function the jQuery prototype for later instantiation (needed after Rev 4091) +$.prototype.init.prototype = $.prototype; + +})(jQuery); \ No newline at end of file diff --git a/public/stylesheets/application.css b/public/stylesheets/application.css index 206dc08..29f7564 100644 --- a/public/stylesheets/application.css +++ b/public/stylesheets/application.css @@ -1,3 +1,5 @@ +/* @override http://localhost:3000/stylesheets/application.css */ + /* @group Reset */ html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, code, del, dfn, em, img, q, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td {margin:0;padding:0;border:0;font-weight:inherit;font-style:inherit;font-size:100%;font-family:inherit;vertical-align:baseline;} @@ -65,6 +67,9 @@ caption {background:#eee;} .last {margin-right:0;padding-right:0;} .top {margin-top:0;padding-top:0;} .bottom {margin-bottom:0;padding-bottom:0;} +.clear { + clear: both; +} @@ -213,4 +218,23 @@ td { } p#notice { color: green; -} \ No newline at end of file +} + +DIV#collection_albums { + width: 710px; + clear: both; +} +DIV#collection_albums span { + position: relative; + display: block; + float: left; + padding: 5px; +} +DIV#collection_albums IMG.delete { + display: block; + vertical-align: top; + position: absolute; + cursor: pointer; + right: 8px; + top: 8px; +}