image_picker 0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ 10/23/08 - Initial version [Matthew Bass]
@@ -0,0 +1,16 @@
1
+ Copyright (c) 2007-2008 Matthew Bass (http://matthewbass.com)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software
4
+ and associated documentation files (the "Software"), to deal in the Software without
5
+ restriction, including without limitation the rights to use, copy, modify, merge, publish,
6
+ distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
7
+ Software is furnished to do so, subject to the following conditions:
8
+
9
+ The above copyright notice and this permission notice shall be included in all copies or
10
+ substantial portions of the Software.
11
+
12
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
13
+ BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
14
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
15
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,73 @@
1
+ = image_picker
2
+
3
+ A simple, customizable image browser.
4
+
5
+ == Installation
6
+
7
+ Install the gem directly:
8
+
9
+ sudo gem install pelargir-image_picker --source=http://gems.github.com
10
+
11
+ Or install the gem in your Rails project as a plugin:
12
+
13
+ script/plugin install git://github.com/pelargir/image_picker.git
14
+
15
+ Or clone the project:
16
+
17
+ git clone git://github.com/pelargir/image_picker.git
18
+
19
+ Then install the required image, JS, and CSS files into your Rails project:
20
+
21
+ rake image_picker:install
22
+
23
+ == Usage
24
+
25
+ First, add the picker to your view and pass it the name of the image
26
+ being picked. This name is used to create a hidden field that stores
27
+ the database ID of the image that gets picked:
28
+
29
+ <%= image_picker :avatar %>
30
+
31
+ Now tell your controller that you'll be picking images, and which
32
+ ActiveRecord model you'll be using:
33
+
34
+ class SomeController < ActionController::Base
35
+ image_picker :model => Image
36
+ end
37
+
38
+ The ActiveRecord class must respond to three method calls:
39
+
40
+ thumbnail: the path to the image's thumbnail (e.g. /images/foo_small.gif)
41
+ title: a friendly title for the image
42
+ description: a description of the image (optional)
43
+
44
+ That's it! The picker will show up in your view and, once you've
45
+ picked an image, its database ID will be set as the value of the
46
+ hidden field "<name>_id" where <name> is the name you passed
47
+ to the image_picker helper in your view. The hidden field can
48
+ then be submitted via a form or referenced in JavaScript.
49
+
50
+ == Other Options
51
+
52
+ The controller helper accepts any arguments you can pass to an
53
+ ActiveRecord finder. For example:
54
+
55
+ class SomeController < ActionController::Base
56
+ image_picker :model => Image, :order => "created_at DESC"
57
+ end
58
+
59
+ If the will_paginate plugin is installed, the image picker will
60
+ use it automatically. You can pass pagination options to the
61
+ controller helper as well. For example:
62
+
63
+ class SomeController < ActionController::Base
64
+ image_picker :model => Image, :per_page => 6
65
+ end
66
+
67
+ == Resources
68
+
69
+ Repository: http://github.com/pelargir/image_picker/
70
+ Blog: http://matthewbass.com
71
+ Author: Matthew Bass
72
+
73
+ Extraction work sponsored by Terralien
@@ -0,0 +1,22 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the image_picker plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.pattern = 'test/**/*_test.rb'
12
+ t.verbose = true
13
+ end
14
+
15
+ desc 'Generate documentation for the image_picker plugin.'
16
+ Rake::RDocTask.new(:rdoc) do |rdoc|
17
+ rdoc.rdoc_dir = 'rdoc'
18
+ rdoc.title = 'image_picker'
19
+ rdoc.options << '--line-numbers' << '--inline-source'
20
+ rdoc.rdoc_files.include('README')
21
+ rdoc.rdoc_files.include('lib/**/*.rb')
22
+ end
@@ -0,0 +1,14 @@
1
+ # Generated by the asset_copier plugin
2
+ # http://github.com/pelargir/asset_copier
3
+ #
4
+ # Files that have been removed from the plugin and should also be removed from
5
+ # any Rails projects that use the plugin should be listed here, one entry
6
+ # per line. For example:
7
+ #
8
+ # public/javascripts/foo.js
9
+ # app/views/foo/bar.erb.html
10
+ #
11
+ # Adding the paths above to this file would ensure that foo.js and bar.erb.html
12
+ # both get removed from the target Rails project the next time the plugin's
13
+ # files are copied over.
14
+
@@ -0,0 +1,21 @@
1
+ var ImagePicker = {
2
+ pick:function(field, image_id, image_title, image_url) {
3
+ $(field + '_id').value = image_id;
4
+ $(field).src = image_url;
5
+ $(field).alt = image_title;
6
+ Modalbox.hide();
7
+ },
8
+
9
+ clear:function(field, image_url) {
10
+ $(field + '_id').value = '';
11
+ $(field).src = image_url;
12
+ $(field).alt = 'No_image';
13
+ Modalbox.hide();
14
+ },
15
+
16
+ open_picker:function(params) {
17
+ url = $H(params).unset('url');
18
+ Modalbox.show(url + '?' + $H(params).toQueryString(),
19
+ {title: 'Select an image...', width: 520, afterLoad: ImagePicker.setup});
20
+ }
21
+ };
@@ -0,0 +1,573 @@
1
+ /*
2
+ ModalBox - The pop-up window thingie with AJAX, based on prototype and script.aculo.us.
3
+
4
+ Copyright Andrey Okonetchnikov (andrej.okonetschnikow@gmail.com), 2006-2007
5
+ All rights reserved.
6
+
7
+ VERSION 1.6.0
8
+ Last Modified: 12/13/2007
9
+ */
10
+
11
+ if (!window.Modalbox)
12
+ var Modalbox = new Object();
13
+
14
+ Modalbox.Methods = {
15
+ overrideAlert: false, // Override standard browser alert message with ModalBox
16
+ focusableElements: new Array,
17
+ currFocused: 0,
18
+ initialized: false,
19
+ active: true,
20
+ options: {
21
+ title: "ModalBox Window", // Title of the ModalBox window
22
+ overlayClose: true, // Close modal box by clicking on overlay
23
+ width: 500, // Default width in px
24
+ height: 90, // Default height in px
25
+ overlayOpacity: .65, // Default overlay opacity
26
+ overlayDuration: .25, // Default overlay fade in/out duration in seconds
27
+ slideDownDuration: .5, // Default Modalbox appear slide down effect in seconds
28
+ slideUpDuration: .5, // Default Modalbox hiding slide up effect in seconds
29
+ resizeDuration: .25, // Default resize duration seconds
30
+ inactiveFade: true, // Fades MB window on inactive state
31
+ transitions: true, // Toggles transition effects. Transitions are enabled by default
32
+ loadingString: "Please wait. Loading...", // Default loading string message
33
+ closeString: "Close window", // Default title attribute for close window link
34
+ closeValue: "&times;", // Default string for close link in the header
35
+ params: {},
36
+ method: 'get', // Default Ajax request method
37
+ autoFocusing: true, // Toggles auto-focusing for form elements. Disable for long text pages.
38
+ aspnet: false // Should be use then using with ASP.NET costrols. Then true Modalbox window will be injected into the first form element.
39
+ },
40
+ _options: new Object,
41
+
42
+ setOptions: function(options) {
43
+ Object.extend(this.options, options || {});
44
+ },
45
+
46
+ _init: function(options) {
47
+ // Setting up original options with default options
48
+ Object.extend(this._options, this.options);
49
+ this.setOptions(options);
50
+
51
+ //Create the overlay
52
+ this.MBoverlay = new Element("div", { id: "MB_overlay", opacity: "0" });
53
+
54
+ //Create DOm for the window
55
+ this.MBwindow = new Element("div", {id: "MB_window", style: "display: none"}).update(
56
+ this.MBframe = new Element("div", {id: "MB_frame"}).update(
57
+ this.MBheader = new Element("div", {id: "MB_header"}).update(
58
+ this.MBcaption = new Element("div", {id: "MB_caption"})
59
+ )
60
+ )
61
+ );
62
+ this.MBclose = new Element("a", {id: "MB_close", title: this.options.closeString, href: "#"}).update("<span>" + this.options.closeValue + "</span>");
63
+ this.MBheader.insert({'bottom':this.MBclose});
64
+
65
+ this.MBcontent = new Element("div", {id: "MB_content"}).update(
66
+ this.MBloading = new Element("div", {id: "MB_loading"}).update(this.options.loadingString)
67
+ );
68
+ this.MBframe.insert({'bottom':this.MBcontent});
69
+
70
+ // Inserting into DOM. If parameter set and form element have been found will inject into it. Otherwise will inject into body as topmost element.
71
+ // Be sure to set padding and marging to null via CSS for both body and (in case of asp.net) form elements.
72
+ var injectToEl = this.options.aspnet ? $(document.body).down('form') : $(document.body);
73
+ injectToEl.insert({'top':this.MBwindow});
74
+ injectToEl.insert({'top':this.MBoverlay});
75
+
76
+ // Initial scrolling position of the window. To be used for remove scrolling effect during ModalBox appearing
77
+ this.initScrollX = window.pageXOffset || document.body.scrollLeft || document.documentElement.scrollLeft;
78
+ this.initScrollY = window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop;
79
+
80
+ //Adding event observers
81
+ this.hideObserver = this._hide.bindAsEventListener(this);
82
+ this.kbdObserver = this._kbdHandler.bindAsEventListener(this);
83
+ this._initObservers();
84
+
85
+ this.initialized = true; // Mark as initialized
86
+ },
87
+
88
+ show: function(content, options) {
89
+ if(!this.initialized) this._init(options); // Check for is already initialized
90
+
91
+ this.content = content;
92
+ this.setOptions(options);
93
+
94
+ if(this.options.title) // Updating title of the MB
95
+ $(this.MBcaption).update(this.options.title);
96
+ else { // If title isn't given, the header will not displayed
97
+ $(this.MBheader).hide();
98
+ $(this.MBcaption).hide();
99
+ }
100
+
101
+ if(this.MBwindow.style.display == "none") { // First modal box appearing
102
+ this._appear();
103
+ this.event("onShow"); // Passing onShow callback
104
+ }
105
+ else { // If MB already on the screen, update it
106
+ this._update();
107
+ this.event("onUpdate"); // Passing onUpdate callback
108
+ }
109
+ },
110
+
111
+ hide: function(options) { // External hide method to use from external HTML and JS
112
+ if(this.initialized) {
113
+ // Reading for options/callbacks except if event given as a pararmeter
114
+ if(options && typeof options.element != 'function') Object.extend(this.options, options);
115
+ // Passing beforeHide callback
116
+ this.event("beforeHide");
117
+ if(this.options.transitions)
118
+ Effect.SlideUp(this.MBwindow, { duration: this.options.slideUpDuration, transition: Effect.Transitions.sinoidal, afterFinish: this._deinit.bind(this) } );
119
+ else {
120
+ $(this.MBwindow).hide();
121
+ this._deinit();
122
+ }
123
+ } else throw("Modalbox is not initialized.");
124
+ },
125
+
126
+ _hide: function(event) { // Internal hide method to use with overlay and close link
127
+ event.stop(); // Stop event propaganation for link elements
128
+ /* Then clicked on overlay we'll check the option and in case of overlayClose == false we'll break hiding execution [Fix for #139] */
129
+ if(event.element().id == 'MB_overlay' && !this.options.overlayClose) return false;
130
+ this.hide();
131
+ },
132
+
133
+ alert: function(message){
134
+ var html = '<div class="MB_alert"><p>' + message + '</p><input type="button" onclick="Modalbox.hide()" value="OK" /></div>';
135
+ Modalbox.show(html, {title: 'Alert: ' + document.title, width: 300});
136
+ },
137
+
138
+ _appear: function() { // First appearing of MB
139
+ if(Prototype.Browser.IE && !navigator.appVersion.match(/\b7.0\b/)) { // Preparing IE 6 for showing modalbox
140
+ window.scrollTo(0,0);
141
+ this._prepareIE("100%", "hidden");
142
+ }
143
+ this._setWidth();
144
+ this._setPosition();
145
+ if(this.options.transitions) {
146
+ $(this.MBoverlay).setStyle({opacity: 0});
147
+ new Effect.Fade(this.MBoverlay, {
148
+ from: 0,
149
+ to: this.options.overlayOpacity,
150
+ duration: this.options.overlayDuration,
151
+ afterFinish: function() {
152
+ new Effect.SlideDown(this.MBwindow, {
153
+ duration: this.options.slideDownDuration,
154
+ transition: Effect.Transitions.sinoidal,
155
+ afterFinish: function(){
156
+ this._setPosition();
157
+ this.loadContent();
158
+ }.bind(this)
159
+ });
160
+ }.bind(this)
161
+ });
162
+ } else {
163
+ $(this.MBoverlay).setStyle({opacity: this.options.overlayOpacity});
164
+ $(this.MBwindow).show();
165
+ this._setPosition();
166
+ this.loadContent();
167
+ }
168
+ this._setWidthAndPosition = this._setWidthAndPosition.bindAsEventListener(this);
169
+ Event.observe(window, "resize", this._setWidthAndPosition);
170
+ },
171
+
172
+ resize: function(byWidth, byHeight, options) { // Change size of MB without loading content
173
+ var wHeight = $(this.MBwindow).getHeight();
174
+ var wWidth = $(this.MBwindow).getWidth();
175
+ var hHeight = $(this.MBheader).getHeight();
176
+ var cHeight = $(this.MBcontent).getHeight();
177
+ var newHeight = ((wHeight - hHeight + byHeight) < cHeight) ? (cHeight + hHeight - wHeight) : byHeight;
178
+ if(options) this.setOptions(options); // Passing callbacks
179
+ if(this.options.transitions) {
180
+ new Effect.ScaleBy(this.MBwindow, byWidth, newHeight, {
181
+ duration: this.options.resizeDuration,
182
+ afterFinish: function() {
183
+ this.event("_afterResize"); // Passing internal callback
184
+ this.event("afterResize"); // Passing callback
185
+ }.bind(this)
186
+ });
187
+ } else {
188
+ this.MBwindow.setStyle({width: wWidth + byWidth + "px", height: wHeight + newHeight + "px"});
189
+ setTimeout(function() {
190
+ this.event("_afterResize"); // Passing internal callback
191
+ this.event("afterResize"); // Passing callback
192
+ }.bind(this), 1);
193
+
194
+ }
195
+
196
+ },
197
+
198
+ resizeToContent: function(options){
199
+
200
+ // Resizes the modalbox window to the actual content height.
201
+ // This might be useful to resize modalbox after some content modifications which were changed ccontent height.
202
+
203
+ var byHeight = this.options.height - this.MBwindow.offsetHeight;
204
+ if(byHeight != 0) {
205
+ if(options) this.setOptions(options); // Passing callbacks
206
+ Modalbox.resize(0, byHeight);
207
+ }
208
+ },
209
+
210
+ resizeToInclude: function(element, options){
211
+
212
+ // Resizes the modalbox window to the camulative height of element. Calculations are using CSS properties for margins and border.
213
+ // This method might be useful to resize modalbox before including or updating content.
214
+
215
+ var el = $(element);
216
+ var elHeight = el.getHeight() + parseInt(el.getStyle('margin-top')) + parseInt(el.getStyle('margin-bottom')) + parseInt(el.getStyle('border-top-width')) + parseInt(el.getStyle('border-bottom-width'));
217
+ if(elHeight > 0) {
218
+ if(options) this.setOptions(options); // Passing callbacks
219
+ Modalbox.resize(0, elHeight);
220
+ }
221
+ },
222
+
223
+ _update: function() { // Updating MB in case of wizards
224
+ $(this.MBcontent).update("");
225
+ this.MBcontent.appendChild(this.MBloading);
226
+ $(this.MBloading).update(this.options.loadingString);
227
+ this.currentDims = [this.MBwindow.offsetWidth, this.MBwindow.offsetHeight];
228
+ Modalbox.resize((this.options.width - this.currentDims[0]), (this.options.height - this.currentDims[1]), {_afterResize: this._loadAfterResize.bind(this) });
229
+ },
230
+
231
+ loadContent: function () {
232
+ if(this.event("beforeLoad") != false) { // If callback passed false, skip loading of the content
233
+ if(typeof this.content == 'string') {
234
+ var htmlRegExp = new RegExp(/<\/?[^>]+>/gi);
235
+ if(htmlRegExp.test(this.content)) { // Plain HTML given as a parameter
236
+ this._insertContent(this.content.stripScripts());
237
+ this._putContent(function(){
238
+ this.content.extractScripts().map(function(script) {
239
+ return eval(script.replace("<!--", "").replace("// -->", ""));
240
+ }.bind(window));
241
+ }.bind(this));
242
+ } else // URL given as a parameter. We'll request it via Ajax
243
+ new Ajax.Request( this.content, { method: this.options.method.toLowerCase(), parameters: this.options.params,
244
+ onSuccess: function(transport) {
245
+ var response = new String(transport.responseText);
246
+ this._insertContent(transport.responseText.stripScripts());
247
+ this._putContent(function(){
248
+ response.extractScripts().map(function(script) {
249
+ return eval(script.replace("<!--", "").replace("// -->", ""));
250
+ }.bind(window));
251
+ });
252
+ }.bind(this),
253
+ onException: function(instance, exception){
254
+ Modalbox.hide();
255
+ throw('Modalbox Loading Error: ' + exception);
256
+ }
257
+ });
258
+
259
+ } else if (typeof this.content == 'object') {// HTML Object is given
260
+ this._insertContent(this.content);
261
+ this._putContent();
262
+ } else {
263
+ Modalbox.hide();
264
+ throw('Modalbox Parameters Error: Please specify correct URL or HTML element (plain HTML or object)');
265
+ }
266
+ }
267
+ },
268
+
269
+ _insertContent: function(content){
270
+ $(this.MBcontent).hide().update("");
271
+ if(typeof content == 'string') {
272
+ setTimeout(function() { // Hack to disable content flickering in Firefox
273
+ this.MBcontent.update(content);
274
+ }.bind(this), 1);
275
+ } else if (typeof content == 'object') { // HTML Object is given
276
+ var _htmlObj = content.cloneNode(true); // If node already a part of DOM we'll clone it
277
+ // If clonable element has ID attribute defined, modifying it to prevent duplicates
278
+ if(content.id) content.id = "MB_" + content.id;
279
+ /* Add prefix for IDs on all elements inside the DOM node */
280
+ $(content).select('*[id]').each(function(el){ el.id = "MB_" + el.id; });
281
+ this.MBcontent.appendChild(_htmlObj);
282
+ this.MBcontent.down().show(); // Toggle visibility for hidden nodes
283
+ if(Prototype.Browser.IE) // Toggling back visibility for hidden selects in IE
284
+ $$("#MB_content select").invoke('setStyle', {'visibility': ''});
285
+ }
286
+ },
287
+
288
+ _putContent: function(callback){
289
+ // Prepare and resize modal box for content
290
+ if(this.options.height == this._options.height) {
291
+ setTimeout(function() { // MSIE sometimes doesn't display content correctly
292
+ Modalbox.resize(0, $(this.MBcontent).getHeight() - $(this.MBwindow).getHeight() + $(this.MBheader).getHeight(), {
293
+ afterResize: function(){
294
+ this.MBcontent.show().makePositioned();
295
+ this.focusableElements = this._findFocusableElements();
296
+ this._setFocus(); // Setting focus on first 'focusable' element in content (input, select, textarea, link or button)
297
+ setTimeout(function(){ // MSIE fix
298
+ if(callback != undefined)
299
+ callback(); // Executing internal JS from loaded content
300
+ this.event("afterLoad"); // Passing callback
301
+ }.bind(this),1);
302
+ }.bind(this)
303
+ });
304
+ }.bind(this), 1);
305
+ } else { // Height is defined. Creating a scrollable window
306
+ this._setWidth();
307
+ this.MBcontent.setStyle({overflow: 'auto', height: $(this.MBwindow).getHeight() - $(this.MBheader).getHeight() - 13 + 'px'});
308
+ this.MBcontent.show();
309
+ this.focusableElements = this._findFocusableElements();
310
+ this._setFocus(); // Setting focus on first 'focusable' element in content (input, select, textarea, link or button)
311
+ setTimeout(function(){ // MSIE fix
312
+ if(callback != undefined)
313
+ callback(); // Executing internal JS from loaded content
314
+ this.event("afterLoad"); // Passing callback
315
+ }.bind(this),1);
316
+ }
317
+ },
318
+
319
+ activate: function(options){
320
+ this.setOptions(options);
321
+ this.active = true;
322
+ $(this.MBclose).observe("click", this.hideObserver);
323
+ if(this.options.overlayClose)
324
+ $(this.MBoverlay).observe("click", this.hideObserver);
325
+ $(this.MBclose).show();
326
+ if(this.options.transitions && this.options.inactiveFade)
327
+ new Effect.Appear(this.MBwindow, {duration: this.options.slideUpDuration});
328
+ },
329
+
330
+ deactivate: function(options) {
331
+ this.setOptions(options);
332
+ this.active = false;
333
+ $(this.MBclose).stopObserving("click", this.hideObserver);
334
+ if(this.options.overlayClose)
335
+ $(this.MBoverlay).stopObserving("click", this.hideObserver);
336
+ $(this.MBclose).hide();
337
+ if(this.options.transitions && this.options.inactiveFade)
338
+ new Effect.Fade(this.MBwindow, {duration: this.options.slideUpDuration, to: .75});
339
+ },
340
+
341
+ _initObservers: function(){
342
+ $(this.MBclose).observe("click", this.hideObserver);
343
+ if(this.options.overlayClose)
344
+ $(this.MBoverlay).observe("click", this.hideObserver);
345
+ if(Prototype.Browser.IE)
346
+ Event.observe(document, "keydown", this.kbdObserver);
347
+ else
348
+ Event.observe(document, "keypress", this.kbdObserver);
349
+ },
350
+
351
+ _removeObservers: function(){
352
+ $(this.MBclose).stopObserving("click", this.hideObserver);
353
+ if(this.options.overlayClose)
354
+ $(this.MBoverlay).stopObserving("click", this.hideObserver);
355
+ if(Prototype.Browser.IE)
356
+ Event.stopObserving(document, "keydown", this.kbdObserver);
357
+ else
358
+ Event.stopObserving(document, "keypress", this.kbdObserver);
359
+ },
360
+
361
+ _loadAfterResize: function() {
362
+ this._setWidth();
363
+ this._setPosition();
364
+ this.loadContent();
365
+ },
366
+
367
+ _setFocus: function() {
368
+ /* Setting focus to the first 'focusable' element which is one with tabindex = 1 or the first in the form loaded. */
369
+ if(this.focusableElements.length > 0 && this.options.autoFocusing == true) {
370
+ var firstEl = this.focusableElements.find(function (el){
371
+ return el.tabIndex == 1;
372
+ }) || this.focusableElements.first();
373
+ this.currFocused = this.focusableElements.toArray().indexOf(firstEl);
374
+ firstEl.focus(); // Focus on first focusable element except close button
375
+ } else if($(this.MBclose).visible())
376
+ $(this.MBclose).focus(); // If no focusable elements exist focus on close button
377
+ },
378
+
379
+ _findFocusableElements: function(){ // Collect form elements or links from MB content
380
+ this.MBcontent.select('input:not([type~=hidden]), select, textarea, button, a[href]').invoke('addClassName', 'MB_focusable');
381
+ return this.MBcontent.select('.MB_focusable');
382
+ },
383
+
384
+ _kbdHandler: function(event) {
385
+ var node = event.element();
386
+ switch(event.keyCode) {
387
+ case Event.KEY_TAB:
388
+ event.stop();
389
+
390
+ /* Switching currFocused to the element which was focused by mouse instead of TAB-key. Fix for #134 */
391
+ if(node != this.focusableElements[this.currFocused])
392
+ this.currFocused = this.focusableElements.toArray().indexOf(node);
393
+
394
+ if(!event.shiftKey) { //Focusing in direct order
395
+ if(this.currFocused == this.focusableElements.length - 1) {
396
+ this.focusableElements.first().focus();
397
+ this.currFocused = 0;
398
+ } else {
399
+ this.currFocused++;
400
+ this.focusableElements[this.currFocused].focus();
401
+ }
402
+ } else { // Shift key is pressed. Focusing in reverse order
403
+ if(this.currFocused == 0) {
404
+ this.focusableElements.last().focus();
405
+ this.currFocused = this.focusableElements.length - 1;
406
+ } else {
407
+ this.currFocused--;
408
+ this.focusableElements[this.currFocused].focus();
409
+ }
410
+ }
411
+ break;
412
+ case Event.KEY_ESC:
413
+ if(this.active) this._hide(event);
414
+ break;
415
+ case 32:
416
+ this._preventScroll(event);
417
+ break;
418
+ case 0: // For Gecko browsers compatibility
419
+ if(event.which == 32) this._preventScroll(event);
420
+ break;
421
+ case Event.KEY_UP:
422
+ case Event.KEY_DOWN:
423
+ case Event.KEY_PAGEDOWN:
424
+ case Event.KEY_PAGEUP:
425
+ case Event.KEY_HOME:
426
+ case Event.KEY_END:
427
+ // Safari operates in slightly different way. This realization is still buggy in Safari.
428
+ if(Prototype.Browser.WebKit && !["textarea", "select"].include(node.tagName.toLowerCase()))
429
+ event.stop();
430
+ else if( (node.tagName.toLowerCase() == "input" && ["submit", "button"].include(node.type)) || (node.tagName.toLowerCase() == "a") )
431
+ event.stop();
432
+ break;
433
+ }
434
+ },
435
+
436
+ _preventScroll: function(event) { // Disabling scrolling by "space" key
437
+ if(!["input", "textarea", "select", "button"].include(event.element().tagName.toLowerCase()))
438
+ event.stop();
439
+ },
440
+
441
+ _deinit: function()
442
+ {
443
+ this._removeObservers();
444
+ Event.stopObserving(window, "resize", this._setWidthAndPosition );
445
+ if(this.options.transitions) {
446
+ Effect.toggle(this.MBoverlay, 'appear', {duration: this.options.overlayDuration, afterFinish: this._removeElements.bind(this) });
447
+ } else {
448
+ this.MBoverlay.hide();
449
+ this._removeElements();
450
+ }
451
+ $(this.MBcontent).setStyle({overflow: '', height: ''});
452
+ },
453
+
454
+ _removeElements: function () {
455
+ $(this.MBoverlay).remove();
456
+ $(this.MBwindow).remove();
457
+ if(Prototype.Browser.IE && !navigator.appVersion.match(/\b7.0\b/)) {
458
+ this._prepareIE("", ""); // If set to auto MSIE will show horizontal scrolling
459
+ window.scrollTo(this.initScrollX, this.initScrollY);
460
+ }
461
+
462
+ /* Replacing prefixes 'MB_' in IDs for the original content */
463
+ if(typeof this.content == 'object') {
464
+ if(this.content.id && this.content.id.match(/MB_/)) {
465
+ this.content.id = this.content.id.replace(/MB_/, "");
466
+ }
467
+ this.content.select('*[id]').each(function(el){ el.id = el.id.replace(/MB_/, ""); });
468
+ }
469
+ /* Initialized will be set to false */
470
+ this.initialized = false;
471
+ this.event("afterHide"); // Passing afterHide callback
472
+ this.setOptions(this._options); //Settings options object into intial state
473
+ },
474
+
475
+ _setWidth: function () { //Set size
476
+ $(this.MBwindow).setStyle({width: this.options.width + "px", height: this.options.height + "px"});
477
+ },
478
+
479
+ _setPosition: function () {
480
+ $(this.MBwindow).setStyle({left: Math.round((Element.getWidth(document.body) - Element.getWidth(this.MBwindow)) / 2 ) + "px"});
481
+ },
482
+
483
+ _setWidthAndPosition: function () {
484
+ $(this.MBwindow).setStyle({width: this.options.width + "px"});
485
+ this._setPosition();
486
+ },
487
+
488
+ _getScrollTop: function () { //From: http://www.quirksmode.org/js/doctypes.html
489
+ var theTop;
490
+ if (document.documentElement && document.documentElement.scrollTop)
491
+ theTop = document.documentElement.scrollTop;
492
+ else if (document.body)
493
+ theTop = document.body.scrollTop;
494
+ return theTop;
495
+ },
496
+ _prepareIE: function(height, overflow){
497
+ $$('html, body').invoke('setStyle', {width: height, height: height, overflow: overflow}); // IE requires width and height set to 100% and overflow hidden
498
+ $$("select").invoke('setStyle', {'visibility': overflow}); // Toggle visibility for all selects in the common document
499
+ },
500
+ event: function(eventName) {
501
+ if(this.options[eventName]) {
502
+ var returnValue = this.options[eventName](); // Executing callback
503
+ this.options[eventName] = null; // Removing callback after execution
504
+ if(returnValue != undefined)
505
+ return returnValue;
506
+ else
507
+ return true;
508
+ }
509
+ return true;
510
+ }
511
+ };
512
+
513
+ Object.extend(Modalbox, Modalbox.Methods);
514
+
515
+ if(Modalbox.overrideAlert) window.alert = Modalbox.alert;
516
+
517
+ Effect.ScaleBy = Class.create();
518
+ Object.extend(Object.extend(Effect.ScaleBy.prototype, Effect.Base.prototype), {
519
+ initialize: function(element, byWidth, byHeight, options) {
520
+ this.element = $(element)
521
+ var options = Object.extend({
522
+ scaleFromTop: true,
523
+ scaleMode: 'box', // 'box' or 'contents' or {} with provided values
524
+ scaleByWidth: byWidth,
525
+ scaleByHeight: byHeight
526
+ }, arguments[3] || {});
527
+ this.start(options);
528
+ },
529
+ setup: function() {
530
+ this.elementPositioning = this.element.getStyle('position');
531
+
532
+ this.originalTop = this.element.offsetTop;
533
+ this.originalLeft = this.element.offsetLeft;
534
+
535
+ this.dims = null;
536
+ if(this.options.scaleMode=='box')
537
+ this.dims = [this.element.offsetHeight, this.element.offsetWidth];
538
+ if(/^content/.test(this.options.scaleMode))
539
+ this.dims = [this.element.scrollHeight, this.element.scrollWidth];
540
+ if(!this.dims)
541
+ this.dims = [this.options.scaleMode.originalHeight,
542
+ this.options.scaleMode.originalWidth];
543
+
544
+ this.deltaY = this.options.scaleByHeight;
545
+ this.deltaX = this.options.scaleByWidth;
546
+ },
547
+ update: function(position) {
548
+ var currentHeight = this.dims[0] + (this.deltaY * position);
549
+ var currentWidth = this.dims[1] + (this.deltaX * position);
550
+
551
+ currentHeight = (currentHeight > 0) ? currentHeight : 0;
552
+ currentWidth = (currentWidth > 0) ? currentWidth : 0;
553
+
554
+ this.setDimensions(currentHeight, currentWidth);
555
+ },
556
+
557
+ setDimensions: function(height, width) {
558
+ var d = {};
559
+ d.width = width + 'px';
560
+ d.height = height + 'px';
561
+
562
+ var topd = Math.round((height - this.dims[0])/2);
563
+ var leftd = Math.round((width - this.dims[1])/2);
564
+ if(this.elementPositioning == 'absolute' || this.elementPositioning == 'fixed') {
565
+ if(!this.options.scaleFromTop) d.top = this.originalTop-topd + 'px';
566
+ d.left = this.originalLeft-leftd + 'px';
567
+ } else {
568
+ if(!this.options.scaleFromTop) d.top = -topd + 'px';
569
+ d.left = -leftd + 'px';
570
+ }
571
+ this.element.setStyle(d);
572
+ }
573
+ });
@@ -0,0 +1,95 @@
1
+ #MB_overlay {
2
+ position: absolute;
3
+ margin: auto;
4
+ top: 0; left: 0;
5
+ width: 100%; height: 100%;
6
+ z-index: 9999;
7
+ background-color: #000!important;
8
+ }
9
+ #MB_overlay[id] { position: fixed; }
10
+
11
+ #MB_window {
12
+ position: absolute;
13
+ top: 0;
14
+ border: 0 solid;
15
+ text-align: left;
16
+ z-index: 10000;
17
+ }
18
+ #MB_window[id] { position: fixed!important; }
19
+
20
+ #MB_frame {
21
+ position: relative;
22
+ background-color: #EFEFEF;
23
+ height: 100%;
24
+ }
25
+
26
+ #MB_header {
27
+ margin: 0;
28
+ padding: 0;
29
+ }
30
+
31
+ #MB_content {
32
+ padding: 6px .75em;
33
+ overflow: auto;
34
+ }
35
+
36
+ #MB_caption {
37
+ font: bold 100% "Lucida Grande", Arial, sans-serif;
38
+ text-shadow: #FFF 0 1px 0;
39
+ padding: .5em 2em .5em .75em;
40
+ margin: 0;
41
+ text-align: left;
42
+ }
43
+
44
+ #MB_close {
45
+ display: block;
46
+ position: absolute;
47
+ right: 5px; top: 4px;
48
+ padding: 2px 3px;
49
+ font-weight: bold;
50
+ text-decoration: none;
51
+ font-size: 13px;
52
+ }
53
+ #MB_close:hover {
54
+ background: transparent;
55
+ }
56
+
57
+ #MB_loading {
58
+ padding: 1.5em;
59
+ text-indent: -10000px;
60
+ background: transparent url(spinner.gif) 50% 0 no-repeat;
61
+ }
62
+
63
+ /* Color scheme */
64
+ #MB_frame {
65
+ padding-bottom: 7px;
66
+ -webkit-border-radius: 7px;
67
+ -moz-border-radius: 7px;
68
+ border-radius: 7px;
69
+ }
70
+ #MB_window {
71
+ background-color: #EFEFEF;
72
+ color: #000;
73
+ -webkit-box-shadow: 0 8px 64px #000;
74
+ -moz-box-shadow: 0 0 64px #000;
75
+ box-shadow: 0 0 64px #000;
76
+
77
+ -webkit-border-radius: 7px;
78
+ -moz-border-radius: 7px;
79
+ border-radius: 7px;
80
+ }
81
+ #MB_content { border-top: 1px solid #F9F9F9; }
82
+ #MB_header {
83
+ background-color: #DDD;
84
+ border-bottom: 1px solid #CCC;
85
+ }
86
+ #MB_caption { color: #000 }
87
+ #MB_close { color: #777 }
88
+ #MB_close:hover { color: #000 }
89
+
90
+
91
+ /* Alert message */
92
+ .MB_alert {
93
+ margin: 10px 0;
94
+ text-align: center;
95
+ }
@@ -0,0 +1,34 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "image_picker"
3
+ s.version = "0.5"
4
+ s.date = "2008-10-23"
5
+ s.summary = "A simple, customizable image browser."
6
+ s.email = "pelargir@gmail.com"
7
+ s.homepage = "http://github.com/pelargir/image_picker"
8
+ s.description = "A simple, customizable image browser."
9
+ s.has_rdoc = true
10
+ s.authors = ["Matthew Bass"]
11
+ s.files = [
12
+ "CHANGELOG",
13
+ "deleted_files",
14
+ "files/public/images/no_image.jpg",
15
+ "files/public/images/spinner.gif",
16
+ "files/public/javascripts/image_picker.js",
17
+ "files/public/javascripts/modalbox.js",
18
+ "files/public/stylesheets/modalbox.css",
19
+ "image_picker.gemspec",
20
+ "init.rb",
21
+ "install.rb",
22
+ "lib/image_picker.rb",
23
+ "lib/image_picker/asset_copier.rb",
24
+ "lib/image_picker/controller.rb",
25
+ "lib/image_picker/link_renderer.rb",
26
+ "MIT-LICENSE",
27
+ "Rakefile",
28
+ "README",
29
+ "tasks/asset_copier.rake",
30
+ "templates/open_picker.html.erb"
31
+ ]
32
+ s.rdoc_options = ["--main", "README"]
33
+ s.extra_rdoc_files = ["README"]
34
+ end
data/init.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'image_picker'
2
+ require 'image_picker/controller'
3
+ require 'image_picker/link_renderer'
4
+ require 'image_picker/asset_copier'
5
+
6
+ if RAILS_ENV == "development"
7
+ ImagePicker::AssetCopier.copy "image_picker"
8
+ elsif RAILS_ENV == "production"
9
+ ImagePicker::AssetCopier.warn "image_picker"
10
+ end
@@ -0,0 +1,20 @@
1
+ # Generated by the asset_copier plugin
2
+ # http://github.com/pelargir/asset_copier
3
+
4
+ require 'rake'
5
+
6
+ begin
7
+ puts "============================================================="
8
+ puts "Attempting to install required files into your application..."
9
+ puts "============================================================="
10
+ RAKE_FILE = File.expand_path(File.join(File.dirname(__FILE__), 'tasks', 'asset_copier.rake'))
11
+ load RAKE_FILE
12
+
13
+ Rake::Task['image_picker:install'].invoke
14
+ puts "=========================================================="
15
+ puts "Success!"
16
+ puts "=========================================================="
17
+ rescue Exception => ex
18
+ puts "FAILED TO INSTALL REQUIRED FILES. PLEASE RUN rake image_picker:install."
19
+ puts "EXCEPTION: #{ex}"
20
+ end
@@ -0,0 +1,11 @@
1
+ module ImagePicker
2
+ def image_picker(field)
3
+ url = url_for :action => "open_picker"
4
+ html = stylesheet_link_tag "modalbox"
5
+ html += javascript_include_tag :defaults, "image_picker", "modalbox"
6
+ html += link_to_function image_tag("no_image.jpg", :id => field), "ImagePicker.open_picker({field:'#{field}', url:'#{url}'})"
7
+ html += hidden_field_tag "#{field}_id"
8
+ end
9
+ end
10
+
11
+ ActionView::Base.send :include, ImagePicker
@@ -0,0 +1,90 @@
1
+ # Generated by the asset_copier plugin
2
+ # http://github.com/pelargir/asset_copier
3
+
4
+ require 'find'
5
+ require 'digest/md5'
6
+
7
+ module ImagePicker
8
+ class AssetCopier
9
+ @source = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'files'))
10
+ @destination = RAILS_ROOT
11
+ @deleted_files = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'deleted_files'))
12
+ class << self
13
+ attr_accessor :source, :destination, :deleted_files
14
+ end
15
+
16
+ def self.copy(plugin_name)
17
+ begin
18
+ each_path do |path, dest_path, short_path|
19
+ if File.directory?(path)
20
+ unless File.exists?(dest_path)
21
+ FileUtils.mkdir_p(dest_path)
22
+ log "Creating directory #{short_path} for #{plugin_name}"
23
+ end
24
+ elsif !compare(path, dest_path)
25
+ FileUtils.cp(path, dest_path)
26
+ log "Copying #{short_path} from #{plugin_name}"
27
+ end
28
+ end
29
+ rescue Exception => e
30
+ log "Error trying to copy files: #{e.inspect}"
31
+ raise e
32
+ end
33
+ print_deletion_warnings(plugin_name)
34
+ end
35
+
36
+ def self.warn(plugin_name)
37
+ each_path do |path, dest_path, short_path|
38
+ next if File.directory?(path)
39
+ reinstall = false
40
+ if File.exists?(dest_path)
41
+ unless compare(path, dest_path)
42
+ log "WARNING: #{short_path} is out of date and needs to be reinstalled"
43
+ reinstall = true
44
+ end
45
+ else
46
+ reinstall = true
47
+ log "WARNING: #{short_path} is missing and needs to be installed"
48
+ end
49
+ log "WARNING: Please run rake #{plugin_name}:install" if reinstall
50
+ end
51
+ print_deletion_warnings(plugin_name)
52
+ end
53
+
54
+ def self.compare(file1, file2)
55
+ File.exists?(file1) && File.exists?(file2) &&
56
+ Digest::MD5.hexdigest(File.read(file1)) == Digest::MD5.hexdigest(File.read(file2))
57
+ end
58
+
59
+ def self.print_deletion_warnings(plugin_name)
60
+ File.open(deleted_files, "r") do |f|
61
+ f.readlines.reject { |l| l =~ /^#/ || l.strip.blank? }.each do |l|
62
+ log "WARNING: #{l} is no longer required by the #{plugin_name} plugin " <<
63
+ "and can can be safely removed" if File.exists?(l)
64
+ end
65
+ end
66
+ end
67
+
68
+ def self.paths
69
+ returning [] do |paths|
70
+ Find.find(source) do |path|
71
+ Find.prune if path =~ /\/\..+/
72
+ Find.prune if path =~ /(CVS|.svn|.git)/
73
+ paths << path
74
+ end
75
+ end
76
+ end
77
+
78
+ def self.each_path
79
+ paths.each do |path|
80
+ dest_path = path.gsub(source, destination)
81
+ short_path = dest_path.gsub("#{destination}/", "")
82
+ yield path, dest_path, short_path
83
+ end
84
+ end
85
+
86
+ def self.log(msg)
87
+ puts msg
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,28 @@
1
+ module ImagePicker
2
+ module Controller
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+ def image_picker(options={})
9
+ define_method "open_picker" do
10
+ model = options.delete(:model)
11
+ @field = params[:field]
12
+ @images = defined?(WillPaginate) ? model.paginate(options.merge(:page => params[:page])) : model.all(options)
13
+ render :file => "#{RAILS_ROOT}/vendor/plugins/image_picker/templates/open_picker.html.erb"
14
+ end
15
+
16
+ define_method "pick" do
17
+ image = Image.find(params[:id])
18
+ field = params[:field]
19
+ render :update do |page|
20
+ page.call "parent.ImagePicker.pick", field, image.id, image.title, image.thumbnail
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ ActionController::Base.send :include, ImagePicker::Controller
@@ -0,0 +1,9 @@
1
+ if defined?(WillPaginate)
2
+ module ImagePicker
3
+ class LinkRenderer < WillPaginate::LinkRenderer
4
+ def page_link(page, text, attributes = {})
5
+ @template.link_to_function text, "ImagePicker.open_picker({field:$('target_field').value, page:'#{page}', terms:$('terms').value});"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ # Generated by the asset_copier plugin
2
+ # http://github.com/pelargir/asset_copier
3
+
4
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/image_picker/asset_copier')
5
+
6
+ namespace :image_picker do
7
+ desc "Install files required by image_picker"
8
+ task :install do
9
+ ImagePicker::AssetCopier.copy "image_picker"
10
+ end
11
+ end
@@ -0,0 +1,38 @@
1
+ <div style="width: 500px">
2
+ <%= hidden_field_tag :target_field, @field %>
3
+ <%= hidden_field_tag :default_image, "/images/no_image.jpg" %>
4
+
5
+ <div style="float:right">
6
+ <%= link_to_function "clear image", "ImagePicker.clear($('target_field').value, $('default_image').value);", :id => "clear" %>
7
+ </div>
8
+
9
+ <% if defined?(WillPaginate) -%>
10
+ <div style="text-align:center; clear:both;">
11
+ <%= will_paginate @images, :renderer => ImagePicker::LinkRenderer %>
12
+ </div>
13
+ <% end -%>
14
+
15
+ <% if @images.any? -%>
16
+ <table border="0", cellpadding="10" cellspacing="5">
17
+ <% @images.in_groups_of(3) do |e| -%>
18
+ <tr>
19
+ <% e.compact.each do |i| -%>
20
+ <td style="text-align:center" valign="top">
21
+ <%= link_to_remote image_tag(i.thumbnail, :alt => i.title), :url => {:action => "pick", :field => @field, :id => i}, :method => :post %>
22
+ <br/>
23
+ <strong><%= i.title %></strong>
24
+ <% if i.respond_to?(:description) -%>
25
+ <br/>
26
+ <%= i.description %>
27
+ <% end -%>
28
+ </td>
29
+ <% end -%>
30
+ </tr>
31
+ <% end -%>
32
+ </table>
33
+ <% else -%>
34
+ <div style="margin:10px; text-align:center; font-style:italic;">
35
+ Nothing found.
36
+ </div>
37
+ <% end -%>
38
+ </div>
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: image_picker
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.5"
5
+ platform: ruby
6
+ authors:
7
+ - Matthew Bass
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-10-23 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: A simple, customizable image browser.
17
+ email: pelargir@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ files:
25
+ - CHANGELOG
26
+ - deleted_files
27
+ - files/public/images/no_image.jpg
28
+ - files/public/images/spinner.gif
29
+ - files/public/javascripts/image_picker.js
30
+ - files/public/javascripts/modalbox.js
31
+ - files/public/stylesheets/modalbox.css
32
+ - image_picker.gemspec
33
+ - init.rb
34
+ - install.rb
35
+ - lib/image_picker.rb
36
+ - lib/image_picker/asset_copier.rb
37
+ - lib/image_picker/controller.rb
38
+ - lib/image_picker/link_renderer.rb
39
+ - MIT-LICENSE
40
+ - Rakefile
41
+ - README
42
+ - tasks/asset_copier.rake
43
+ - templates/open_picker.html.erb
44
+ has_rdoc: true
45
+ homepage: http://github.com/pelargir/image_picker
46
+ licenses: []
47
+
48
+ post_install_message:
49
+ rdoc_options:
50
+ - --main
51
+ - README
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ version:
66
+ requirements: []
67
+
68
+ rubyforge_project:
69
+ rubygems_version: 1.3.4
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: A simple, customizable image browser.
73
+ test_files: []
74
+