images_gallery 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/Rakefile +10 -1
- data/lib/images_gallery/generator.rb +16 -1
- data/lib/images_gallery/models/collection.rb +4 -0
- data/lib/images_gallery/models/image.rb +9 -5
- data/lib/images_gallery/models/source.rb +8 -0
- data/lib/images_gallery/templates/_thumbnails.html.erb +3 -2
- data/lib/images_gallery/templates/layout.html.erb +1 -1
- data/lib/images_gallery/version.rb +1 -1
- data/lib/images_gallery/views/base.rb +6 -4
- data/lib/images_gallery/views/iso.rb +30 -0
- data/lib/images_gallery/views/model.rb +1 -0
- data/spec/features/iso_pages/canon_eos_20d_iso_100_page_spec.rb +14 -0
- data/spec/features/iso_pages/lux_d_3_iso_100_page_spec.rb +14 -0
- data/spec/features/iso_pages/lux_d_3_iso_200_page_spec.rb +14 -0
- data/spec/features/models_pages/canon_eos_20d_page_spec.rb +7 -0
- data/spec/features/models_pages/lux_d_3_page_spec.rb +11 -0
- data/spec/lib/images_gallery/models/collection_spec.rb +1 -0
- data/spec/lib/images_gallery/models/source_spec.rb +7 -2
- data/spec/lib/images_gallery/views/iso_spec.rb +12 -0
- data/spec/support/spec_for_image_interface.rb +4 -0
- data/spec/support/spec_for_iso_page.rb +15 -0
- data/spec/support/spec_for_view_interface.rb +49 -0
- data/spec/tmp/canon.html +5 -3
- data/spec/tmp/canon/canon_eos_20d.html +9 -1
- data/spec/tmp/canon/canon_eos_20d/100.html +77 -0
- data/spec/tmp/canon/canon_eos_400d_digital.html +9 -1
- data/spec/tmp/canon/canon_eos_400d_digital/200.html +77 -0
- data/spec/tmp/fuji_photo_film_co_ltd.html +3 -2
- data/spec/tmp/fuji_photo_film_co_ltd/slp1000se.html +9 -1
- data/spec/tmp/fuji_photo_film_co_ltd/slp1000se/speed_ratings_unknown.html +77 -0
- data/spec/tmp/fujifilm.html +3 -2
- data/spec/tmp/fujifilm/finepix_s6500fd.html +9 -1
- data/spec/tmp/fujifilm/finepix_s6500fd/400.html +77 -0
- data/spec/tmp/index.html +21 -11
- data/spec/tmp/leica.html +11 -6
- data/spec/tmp/leica/d_lux_3.html +37 -1
- data/spec/tmp/leica/d_lux_3/100.html +87 -0
- data/spec/tmp/leica/d_lux_3/200.html +77 -0
- data/spec/tmp/leica/d_lux_3/800.html +77 -0
- data/spec/tmp/nikon_corporation.html +3 -2
- data/spec/tmp/nikon_corporation/nikon_d80.html +9 -1
- data/spec/tmp/nikon_corporation/nikon_d80/100.html +77 -0
- data/spec/tmp/panasonic.html +5 -3
- data/spec/tmp/panasonic/dmc_fz30.html +15 -1
- data/spec/tmp/panasonic/dmc_fz30/80.html +82 -0
- data/spec/tmp/unknown_make.html +5 -3
- data/spec/tmp/unknown_make/unknown_model.html +15 -1
- data/spec/tmp/unknown_make/unknown_model/speed_ratings_unknown.html +82 -0
- metadata +80 -35
- data/spec/support/spec_for_view_inerface.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9c7660b3a472fe00fb56538c5c8fc8c7f6773e02
|
4
|
+
data.tar.gz: 861279385d115536a7d833e6f539da39c14dbc07
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c57e8b3352d328f3226d77bc597aea7327c7fcf36a9bca5aed750710a422fd02c9f3280dc625f84059a9812e19e1f2395b6abd4f422418c7cb9a23ea1503608e
|
7
|
+
data.tar.gz: 2316bca9500ad182e6a03079c7bd2f46b4472cafd67142dac52b594b4b0429ca7c3df3825548f7dce1bcb24a903ae5d19b661f67ab71073d704514f9db8adccb
|
data/README.md
CHANGED
@@ -59,7 +59,7 @@ The `ImageGallery::Source` relies on the **LibXML** SAX parser to extract the im
|
|
59
59
|
About
|
60
60
|
-----
|
61
61
|
|
62
|
-
This kata aims at writing a command-line tool to process **large** XML files which contain images metadata. Part of that metadata is relevant, and the images gallery should allow to preview a collection of thumbnails classified by camera **make** and **
|
62
|
+
This kata aims at writing a command-line tool to process **large** XML files which contain images metadata. Part of that metadata is relevant, and the images gallery should allow to preview a collection of thumbnails classified by camera **make**, **model** and **ISO speed ratings**.
|
63
63
|
|
64
64
|
Because the images collections can be really large, [care has been taken][parser] to avoid loading the XML document in memory while parsing it.
|
65
65
|
|
@@ -67,7 +67,7 @@ Because the images collections can be really large, [care has been taken][parser
|
|
67
67
|
|
68
68
|
Yet huges collections to review do also mean you probably don't want to review them without involving your team. That's to say the images gallery deployment is a key aspect of the task at hand, and keeping the files tree as simple as possible is a way to make the deployments as straightforward as possible. No external CSS, nor font, nor javascipts then.
|
69
69
|
|
70
|
-
The views/templates pattern makes the design extensible, this galleries generator is no <abbr title="Content Management System">CMS</abbr> however! Priority has been given to simplify as much as possible the rendering engine task. Plain ERb should keep the HTML generation fast, while a basic partials system ensures that adding front-end features remains [a _pomodoro_-sized task][pomodoro].
|
70
|
+
The views/templates pattern makes the design extensible, this galleries generator is no <abbr title="Content Management System">CMS</abbr> however! Priority has been given to simplify as much as possible the rendering engine task. Plain ERb should keep the HTML generation fast, while a basic partials system ensures that adding a few front-end features remains [a _pomodoro_-sized task][pomodoro]. (If more features would to be added, however, the views mechanics and the templates management should be refined - see the naviagtion links generation for example.)
|
71
71
|
|
72
72
|
[pomodoro]: https://github.com/gonzalo-bulnes/kata-images_gallery_generator/commit/a90590e63f65d0b166c93e709a17a267c9ec119f
|
73
73
|
|
data/Rakefile
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'html/proofer'
|
1
2
|
require 'rainbow'
|
2
3
|
require 'rspec/core/rake_task'
|
3
4
|
|
@@ -21,4 +22,12 @@ rescue LoadError
|
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
24
|
-
|
25
|
+
namespace :validate do
|
26
|
+
desc 'Verify HTML files are valid.'
|
27
|
+
task :html do
|
28
|
+
system 'images_gallery generate spec/fixtures/works.xml spec/tmp > /dev/null'
|
29
|
+
HTML::Proofer.new('spec/tmp', disable_external: true, check_html: true).run
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
task default: [:spec, 'validate:html', :inch]
|
@@ -4,6 +4,7 @@ require 'images_gallery/models/source'
|
|
4
4
|
require 'images_gallery/views/index'
|
5
5
|
require 'images_gallery/views/make'
|
6
6
|
require 'images_gallery/views/model'
|
7
|
+
require 'images_gallery/views/iso'
|
7
8
|
|
8
9
|
module ImagesGallery
|
9
10
|
class Generator
|
@@ -44,6 +45,15 @@ module ImagesGallery
|
|
44
45
|
images_by_model << image
|
45
46
|
view = Views::Model.new(images_by_model)
|
46
47
|
files[view.file_identifier(image.make, image.model)] = view.render
|
48
|
+
|
49
|
+
images_by_model.iso_values.each do |iso_value|
|
50
|
+
images_by_iso_value = Models::Collection.new
|
51
|
+
images_by_model.select{ |image| image.iso == iso_value }.each do |image|
|
52
|
+
images_by_iso_value << image
|
53
|
+
view = Views::ISO.new(images_by_iso_value)
|
54
|
+
files[view.file_identifier(image.make, image.model, image.iso)] = view.render
|
55
|
+
end
|
56
|
+
end
|
47
57
|
end
|
48
58
|
end
|
49
59
|
end
|
@@ -56,12 +66,17 @@ module ImagesGallery
|
|
56
66
|
dir_path = "#{target}/#{name}".gsub('//', '/')
|
57
67
|
file_path = dir_path + '.html'
|
58
68
|
@index_path = file_path if name == 'index'
|
59
|
-
FileUtils.mkdir_p(dir_path) unless File.exists?(dir_path) || (name == 'index')
|
69
|
+
FileUtils.mkdir_p(parent(dir_path)) unless File.exists?(dir_path) || (name == 'index')
|
60
70
|
File.open(file_path, 'w') do |file|
|
61
71
|
file.write content
|
62
72
|
end
|
63
73
|
end
|
64
74
|
@index_path
|
65
75
|
end
|
76
|
+
|
77
|
+
def parent(dir_path)
|
78
|
+
return dir_path.match('(.*)\/.*$').captures.first unless dir_path.match('(.*)\/.*$').nil?
|
79
|
+
'.'
|
80
|
+
end
|
66
81
|
end
|
67
82
|
end
|
@@ -2,7 +2,15 @@ module ImagesGallery
|
|
2
2
|
module Models
|
3
3
|
class Image
|
4
4
|
|
5
|
-
attr_accessor :id, :
|
5
|
+
attr_accessor :id, :iso, :make, :model, :src
|
6
|
+
|
7
|
+
def description
|
8
|
+
"Image #{@id}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def iso
|
12
|
+
@iso || 'speed ratings unknown'
|
13
|
+
end
|
6
14
|
|
7
15
|
def make
|
8
16
|
@make || 'Unknown Make'
|
@@ -12,10 +20,6 @@ module ImagesGallery
|
|
12
20
|
@model || 'Unknown Model'
|
13
21
|
end
|
14
22
|
|
15
|
-
def description
|
16
|
-
"Image #{@id}"
|
17
|
-
end
|
18
|
-
|
19
23
|
end
|
20
24
|
end
|
21
25
|
end
|
@@ -66,6 +66,10 @@ module ImagesGallery
|
|
66
66
|
@current_image.id = @current_id
|
67
67
|
end
|
68
68
|
|
69
|
+
if element == 'iso_speed_ratings'
|
70
|
+
@current_image.iso = @current_iso
|
71
|
+
end
|
72
|
+
|
69
73
|
if element == 'make'
|
70
74
|
@current_image.make = @current_make
|
71
75
|
end
|
@@ -86,6 +90,10 @@ module ImagesGallery
|
|
86
90
|
@current_id = char
|
87
91
|
end
|
88
92
|
|
93
|
+
if @current_element == 'iso_speed_ratings'
|
94
|
+
@current_iso = char
|
95
|
+
end
|
96
|
+
|
89
97
|
if @current_element == 'make'
|
90
98
|
@current_make = char
|
91
99
|
end
|
@@ -2,10 +2,11 @@
|
|
2
2
|
<% images.each do |image| %>
|
3
3
|
<div class="grid-item">
|
4
4
|
<img alt="<%= image.description %>" src="<%= image.src %>"/>
|
5
|
-
<% unless depth
|
6
|
-
<a class="grid-item-details" href="<%= link_to(depth, image.make, image.model) %>" title="Browse all the <%= image.model %> images.">
|
5
|
+
<% unless depth > 1 %>
|
6
|
+
<a class="grid-item-details" href="<%= link_to(depth, image.make, image.model, image.iso) %>" title="Browse all the <%= image.model %> @ ISO <%= image.iso %> images.">
|
7
7
|
<div><%= image.make %></div>
|
8
8
|
<div><%= image.model %></div>
|
9
|
+
<div><small>ISO <%= image.iso %></small></div>
|
9
10
|
</a>
|
10
11
|
<% end %>
|
11
12
|
</div>
|
@@ -41,15 +41,17 @@ module ImagesGallery
|
|
41
41
|
ERB.new(File.new(template).read).result(binding)
|
42
42
|
end
|
43
43
|
|
44
|
-
def link_to(depth, make, model=nil)
|
45
|
-
('../' * depth) + file_identifier(make, model) + '.html'
|
44
|
+
def link_to(depth, make, model=nil, iso_value=nil)
|
45
|
+
('../' * depth) + file_identifier(make, model, iso_value) + '.html'
|
46
46
|
end
|
47
47
|
|
48
|
-
def file_identifier(make, model=nil)
|
48
|
+
def file_identifier(make, model=nil, iso_value=nil)
|
49
49
|
if model.nil?
|
50
50
|
"#{make.to_filename}"
|
51
|
-
|
51
|
+
elsif iso_value.nil?
|
52
52
|
"#{make.to_filename}/#{model.to_filename}"
|
53
|
+
else
|
54
|
+
"#{make.to_filename}/#{model.to_filename}/#{iso_value.to_filename}"
|
53
55
|
end
|
54
56
|
end
|
55
57
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
require 'images_gallery/views/base'
|
4
|
+
|
5
|
+
module ImagesGallery
|
6
|
+
module Views
|
7
|
+
class ISO < Base
|
8
|
+
|
9
|
+
attr_reader :depth, :links, :iso, :make, :model, :sample_images, :title
|
10
|
+
private :depth, :links, :iso, :make, :model, :sample_images, :title
|
11
|
+
|
12
|
+
def initialize(images)
|
13
|
+
super()
|
14
|
+
|
15
|
+
@depth = 2
|
16
|
+
@make = images.first.make
|
17
|
+
@model = images.first.model
|
18
|
+
@iso = images.first.iso
|
19
|
+
@links = [{ name: "Browse all the images", href: link_to(depth, 'index') }]
|
20
|
+
@links << { name: "Browse all the #{model} images", href: link_to(depth, make, model) }
|
21
|
+
@sample_images = images
|
22
|
+
@title = "Images by (#{make}) #{model} @ ISO #{iso}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def template
|
26
|
+
File.expand_path('../../templates/layout.html.erb', __FILE__)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -17,6 +17,7 @@ module ImagesGallery
|
|
17
17
|
@model = images.first.model
|
18
18
|
@links = [{ name: "Browse all the images", href: link_to(depth, 'index') }]
|
19
19
|
@links << { name: "Browse all the #{make} images", href: link_to(depth, make) }
|
20
|
+
@links += images.iso_values.map{ |iso_value| { name: "ISO #{iso_value}", href: link_to(depth, make, model, iso_value) } }
|
20
21
|
@sample_images = images
|
21
22
|
@title = "Images by (#{make}) #{model}"
|
22
23
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'The Canon EOS 20D @ ISO 100 page', type: :feature do
|
4
|
+
|
5
|
+
let!(:generator) { ImagesGallery::Generator.new }
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
generator.run('spec/fixtures/works.xml', 'spec/tmp/')
|
9
|
+
visit '/canon/canon_eos_20d/100.html'
|
10
|
+
end
|
11
|
+
|
12
|
+
it_behaves_like('an images gallery', 1, header_selector, navigation_selector, title_selector)
|
13
|
+
it_behaves_like('an ISO page', 'canon', 'canon_eos_20d', navigation_selector)
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'The LUX-D 3 @ ISO 100 page', type: :feature do
|
4
|
+
|
5
|
+
let!(:generator) { ImagesGallery::Generator.new }
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
generator.run('spec/fixtures/works.xml', 'spec/tmp/')
|
9
|
+
visit '/leica/d_lux_3/100.html'
|
10
|
+
end
|
11
|
+
|
12
|
+
it_behaves_like('an images gallery', 3, header_selector, navigation_selector, title_selector)
|
13
|
+
it_behaves_like('an ISO page', 'leica', 'd_lux_3', navigation_selector)
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'The LUX-D 3 @ ISO 200 page', type: :feature do
|
4
|
+
|
5
|
+
let!(:generator) { ImagesGallery::Generator.new }
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
generator.run('spec/fixtures/works.xml', 'spec/tmp/')
|
9
|
+
visit '/leica/d_lux_3/200.html'
|
10
|
+
end
|
11
|
+
|
12
|
+
it_behaves_like('an images gallery', 1, header_selector, navigation_selector, title_selector)
|
13
|
+
it_behaves_like('an ISO page', 'leica', 'd_lux_3', navigation_selector)
|
14
|
+
end
|
@@ -11,4 +11,11 @@ describe 'The Canon EOS 20D model page', type: :feature do
|
|
11
11
|
|
12
12
|
it_behaves_like('an images gallery', 1, header_selector, navigation_selector, title_selector)
|
13
13
|
it_behaves_like('a model page', 'canon', navigation_selector)
|
14
|
+
|
15
|
+
describe 'navigation' do
|
16
|
+
|
17
|
+
it 'contains a link to the ISO 100 page' do
|
18
|
+
expect(page).to have_selector "#{navigation_selector} a[href='../canon/canon_eos_20d/100.html']"
|
19
|
+
end
|
20
|
+
end
|
14
21
|
end
|
@@ -11,4 +11,15 @@ describe 'The LUX-D 3 model page', type: :feature do
|
|
11
11
|
|
12
12
|
it_behaves_like('an images gallery', 5, header_selector, navigation_selector, title_selector)
|
13
13
|
it_behaves_like('a model page', 'leica', navigation_selector)
|
14
|
+
|
15
|
+
describe 'navigation' do
|
16
|
+
|
17
|
+
it 'contains a link to the ISO 200 page' do
|
18
|
+
expect(page).to have_selector "#{navigation_selector} a[href='../leica/d_lux_3/200.html']"
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'contains a link to the ISO 100 page' do
|
22
|
+
expect(page).to have_selector "#{navigation_selector} a[href='../leica/d_lux_3/100.html']"
|
23
|
+
end
|
24
|
+
end
|
14
25
|
end
|
@@ -25,12 +25,17 @@ describe 'Models::Source' do
|
|
25
25
|
source.parse
|
26
26
|
source.images.each do |image|
|
27
27
|
|
28
|
-
# There is some duplication here that I couldn't remove
|
28
|
+
# There is some duplication here that I couldn't remove because
|
29
|
+
# the shared specs do not allow to inject the object under test.
|
30
|
+
#
|
31
|
+
# I did copy the examples from the image interface spec.
|
29
32
|
# See spec/support/spec_for_image_interface.
|
30
|
-
|
33
|
+
#
|
34
|
+
# The following examples mean in fact:
|
31
35
|
#it_behaves_like 'an image'
|
32
36
|
|
33
37
|
expect(image).to respond_to :description
|
38
|
+
expect(image).to respond_to :iso
|
34
39
|
expect(image).to respond_to :make
|
35
40
|
expect(image).to respond_to :model
|
36
41
|
expect(image).to respond_to :src
|
@@ -0,0 +1,15 @@
|
|
1
|
+
RSpec.shared_examples 'an ISO page' do |make, model, navigation_selector|
|
2
|
+
|
3
|
+
describe 'navigation' do
|
4
|
+
|
5
|
+
it 'contains a link to the index page' do
|
6
|
+
expect(page).to have_selector "#{navigation_selector} a[href='../../index.html']"
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'contains a link to the model page' do
|
10
|
+
expect(page).to have_selector "#{navigation_selector} a[href='../../#{make}/#{model}.html']"
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'contains a link to the make page'
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
RSpec.shared_examples 'a view' do
|
2
|
+
|
3
|
+
it 'responds to :link_to' do
|
4
|
+
expect(view).to respond_to :link_to
|
5
|
+
end
|
6
|
+
|
7
|
+
it 'responds to :render' do
|
8
|
+
expect(view).to respond_to :render
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'responds to :template' do
|
12
|
+
expect(view).to respond_to :template
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'defines :template' do
|
16
|
+
expect { view.template }.not_to raise_error
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'when built with no data' do
|
20
|
+
|
21
|
+
it 'exits gracefully (?)'
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#link_to' do
|
25
|
+
|
26
|
+
let(:depth) { 1 }
|
27
|
+
|
28
|
+
context 'when provided a depth and a make' do
|
29
|
+
|
30
|
+
it 'generates a make page URL' do
|
31
|
+
expect(view.link_to(depth, 'make')).to eq '../make.html'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'when provided a depth, a make and a model' do
|
36
|
+
|
37
|
+
it 'generates a model page URL' do
|
38
|
+
expect(view.link_to(depth, 'make', 'model')).to eq '../make/model.html'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when provided a depth, a make, a model, and an ISO value' do
|
43
|
+
|
44
|
+
it 'generates an ISO page' do
|
45
|
+
expect(view.link_to(depth, 'make', 'model', 'iso_value')).to eq '../make/model/iso_value.html'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/spec/tmp/canon.html
CHANGED
@@ -35,7 +35,7 @@
|
|
35
35
|
color: #333;
|
36
36
|
font-size: .8rem;
|
37
37
|
font-variant: small-caps;
|
38
|
-
height:
|
38
|
+
height: 4rem;
|
39
39
|
left: 0;
|
40
40
|
line-height: 1.2rem;
|
41
41
|
opacity: 0;
|
@@ -71,9 +71,10 @@
|
|
71
71
|
<div class="grid-item">
|
72
72
|
<img alt="Image 2041" src="http://ih1.redbubble.net/work.2041.1.flat,135x135,075,f.jpg"/>
|
73
73
|
|
74
|
-
<a class="grid-item-details" href="canon/canon_eos_20d.html" title="Browse all the Canon EOS 20D images.">
|
74
|
+
<a class="grid-item-details" href="canon/canon_eos_20d/100.html" title="Browse all the Canon EOS 20D @ ISO 100 images.">
|
75
75
|
<div>Canon</div>
|
76
76
|
<div>Canon EOS 20D</div>
|
77
|
+
<div><small>ISO 100</small></div>
|
77
78
|
</a>
|
78
79
|
|
79
80
|
</div>
|
@@ -81,9 +82,10 @@
|
|
81
82
|
<div class="grid-item">
|
82
83
|
<img alt="Image 777577" src="http://ih1.redbubble.net/work.777577.1.flat,135x135,075,f.jpg"/>
|
83
84
|
|
84
|
-
<a class="grid-item-details" href="canon/canon_eos_400d_digital.html" title="Browse all the Canon EOS 400D DIGITAL images.">
|
85
|
+
<a class="grid-item-details" href="canon/canon_eos_400d_digital/200.html" title="Browse all the Canon EOS 400D DIGITAL @ ISO 200 images.">
|
85
86
|
<div>Canon</div>
|
86
87
|
<div>Canon EOS 400D DIGITAL</div>
|
88
|
+
<div><small>ISO 200</small></div>
|
87
89
|
</a>
|
88
90
|
|
89
91
|
</div>
|