filemanager 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. data/CHANGELOG +3 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README +32 -0
  4. data/Rakefile +30 -0
  5. data/lib/filemanager.rb +59 -0
  6. data/lib/filemanager/controller.rb +197 -0
  7. data/lib/locale.rb +183 -0
  8. data/lib/locale/base.rb +60 -0
  9. data/lib/locale/cgi.rb +103 -0
  10. data/lib/locale/jruby.rb +36 -0
  11. data/lib/locale/object.rb +233 -0
  12. data/lib/locale/posix.rb +22 -0
  13. data/lib/locale/win32.rb +49 -0
  14. data/lib/locale/win32_table.rb +235 -0
  15. data/rails/app/controllers/fm/filemanager_controller.rb +23 -0
  16. data/rails/app/helpers/fm/filemanager_helper.rb +61 -0
  17. data/rails/app/views/fm/filemanager/index.html.erb +509 -0
  18. data/rails/app/views/fm/filemanager/view.html.erb +67 -0
  19. data/rails/config/filemanager.yml +16 -0
  20. data/rails/fm_temp/temp +0 -0
  21. data/rails/public/filemanager/images/avi.gif +0 -0
  22. data/rails/public/filemanager/images/box.gif +0 -0
  23. data/rails/public/filemanager/images/bullet.gif +0 -0
  24. data/rails/public/filemanager/images/close.gif +0 -0
  25. data/rails/public/filemanager/images/closelabel.gif +0 -0
  26. data/rails/public/filemanager/images/doc.gif +0 -0
  27. data/rails/public/filemanager/images/donate-button.gif +0 -0
  28. data/rails/public/filemanager/images/download-icon.gif +0 -0
  29. data/rails/public/filemanager/images/folder.gif +0 -0
  30. data/rails/public/filemanager/images/gif.gif +0 -0
  31. data/rails/public/filemanager/images/htm.gif +0 -0
  32. data/rails/public/filemanager/images/html.gif +0 -0
  33. data/rails/public/filemanager/images/image-1.jpg +0 -0
  34. data/rails/public/filemanager/images/image.gif +0 -0
  35. data/rails/public/filemanager/images/jpg.gif +0 -0
  36. data/rails/public/filemanager/images/loading.gif +0 -0
  37. data/rails/public/filemanager/images/mov.gif +0 -0
  38. data/rails/public/filemanager/images/nextlabel.gif +0 -0
  39. data/rails/public/filemanager/images/pdf.gif +0 -0
  40. data/rails/public/filemanager/images/png.gif +0 -0
  41. data/rails/public/filemanager/images/ppt.gif +0 -0
  42. data/rails/public/filemanager/images/prevlabel.gif +0 -0
  43. data/rails/public/filemanager/images/rar.gif +0 -0
  44. data/rails/public/filemanager/images/rm.gif +0 -0
  45. data/rails/public/filemanager/images/rmvb.gif +0 -0
  46. data/rails/public/filemanager/images/separator.gif +0 -0
  47. data/rails/public/filemanager/images/spacer.gif +0 -0
  48. data/rails/public/filemanager/images/swf.gif +0 -0
  49. data/rails/public/filemanager/images/thumb-1.jpg +0 -0
  50. data/rails/public/filemanager/images/tool_copy.gif +0 -0
  51. data/rails/public/filemanager/images/tool_cut.gif +0 -0
  52. data/rails/public/filemanager/images/tool_del.gif +0 -0
  53. data/rails/public/filemanager/images/tool_folder.gif +0 -0
  54. data/rails/public/filemanager/images/tool_imagemanager.gif +0 -0
  55. data/rails/public/filemanager/images/tool_new.gif +0 -0
  56. data/rails/public/filemanager/images/tool_paste.gif +0 -0
  57. data/rails/public/filemanager/images/tool_props.gif +0 -0
  58. data/rails/public/filemanager/images/tool_refresh.gif +0 -0
  59. data/rails/public/filemanager/images/tool_unzip.gif +0 -0
  60. data/rails/public/filemanager/images/tool_upload.gif +0 -0
  61. data/rails/public/filemanager/images/tool_zip.gif +0 -0
  62. data/rails/public/filemanager/images/txt.gif +0 -0
  63. data/rails/public/filemanager/images/unknown.gif +0 -0
  64. data/rails/public/filemanager/images/wmv.gif +0 -0
  65. data/rails/public/filemanager/images/xls.gif +0 -0
  66. data/rails/public/filemanager/images/zip.gif +0 -0
  67. data/rails/public/filemanager/javascripts/builder.js +136 -0
  68. data/rails/public/filemanager/javascripts/effects.js +1122 -0
  69. data/rails/public/filemanager/javascripts/filemanager.js +6 -0
  70. data/rails/public/filemanager/javascripts/lang/en.js +31 -0
  71. data/rails/public/filemanager/javascripts/lang/zh_CN.js +31 -0
  72. data/rails/public/filemanager/javascripts/lightbox.js +497 -0
  73. data/rails/public/filemanager/javascripts/prototype.js +4221 -0
  74. data/rails/public/filemanager/javascripts/prototype_ext.js +117 -0
  75. data/rails/public/filemanager/javascripts/scriptaculous.js +58 -0
  76. data/rails/public/filemanager/javascripts/translate.js +42 -0
  77. data/rails/public/filemanager/stylesheets/filelist.css +147 -0
  78. data/rails/public/filemanager/stylesheets/general.css +135 -0
  79. data/rails/public/filemanager/stylesheets/lightbox.css +27 -0
  80. data/rails/public/fm_resources/resources.txt +1 -0
  81. metadata +147 -0
@@ -0,0 +1,67 @@
1
+ <%
2
+ randome_key = Time.new.to_i
3
+ path = @lock_path + encode(@path) + '?' + randome_key.to_s
4
+ if @controller.is_image?
5
+ %>
6
+
7
+ <a href="<%= path %>" rel="lightbox"><img onload="if (this.width > 290) {this.width = 290}" src="<%= path %>" border="0" /></a>
8
+ <%
9
+ elsif @controller.is_flash?
10
+ %>
11
+ <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" height="295" width="295">
12
+ <param name="movie" value="<%= path %>" />
13
+ <param name="quality" value="high" />
14
+ <embed src="<%= path %>" quality="high" name="home" align="middle" type="application/x-shockwave-flash" height="295" width="295" pluginspage="http://www.macromedia.com/go/getflashplayer" />
15
+ </object>
16
+ <%
17
+ elsif @controller.is_rm?
18
+ %>
19
+ <embed type="audio/x-pn-realaudio-plugin" CONSOLE="Clip1" CONTROLS="ImageWindow" HEIGHT="240" WIDTH="295" AUTOSTART="true" MAINTAINSPECT="TRUE"/>
20
+ <embed SRC="<%= path %>" type="audio/x-pn-realaudio-plugin" CONSOLE="Clip1" CONTROLS="default" HEIGHT="50" WIDTH="295" AUTOSTART="true" />
21
+ <!--
22
+ <object width="300" height="240" classid="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA">
23
+ <param name="CONTROLS" value="ImageWindow">
24
+ <param name="CONSOLE" value="Video">
25
+ <param name="CENTER" value="TRUE">
26
+ <param name="MAINTAINSPECT" value="TRUE">
27
+ </object>
28
+ <object width="300" height="30" classid="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA">
29
+ <param name="CONTROLS" value="StatusBar">
30
+ <param name="CONSOLE" value="Video">
31
+ </object>
32
+ <object width="300" height="30" classid="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA">
33
+ <param name="CONTROLS" value="ControlPanel">
34
+ <param name="CONSOLE" value="Video">
35
+ <param name="SRC" value="<%= path %>">
36
+ <param name="AUTOSTART" value="TRUE">
37
+ <param name="PREFETCH" value="0">
38
+ <param name="LOOP" value="0">
39
+ <param name="NUMLOOP" value="0">
40
+ </object>
41
+ -->
42
+ <%
43
+ elsif @controller.is_movie?
44
+ %>
45
+ <embed SRC="<%= path %>" CONSOLE="Clip1" CONTROLS="All" HEIGHT="295" WIDTH="295" AUTOSTART="true" />
46
+ <%
47
+ elsif @controller.is_plain?
48
+ %>
49
+ <iframe width=300 height=300 frameborder=0 scrolling=auto src="<%= path %>"></iframe>
50
+ <%
51
+ elsif @controller.is_office?
52
+ %>
53
+ <iframe width=300 height=300 frameborder=0 scrolling=auto src="<%= path %>"></iframe>
54
+ <%
55
+ elsif @controller.is_none?
56
+ %>
57
+ <%
58
+ else
59
+ %>
60
+ <div style="font-weight:normal;">
61
+ <pre>
62
+ <%= @controller.file_content %>
63
+ </pre>
64
+ </div>
65
+ <%
66
+ end
67
+ %>
@@ -0,0 +1,16 @@
1
+ image.type: jpg gif png
2
+ flash.type: swf
3
+ movie.type: mov avi wmv
4
+ rm.type: rm rmvb
5
+ plain.type: txt ini inf html htm rb jsp php asp
6
+ office.type: doc dot xls xla ppt pps ppz pot hlp chm ods
7
+ word.type: doc dot
8
+ excel.type: xls xla ods
9
+ ppt.type: ppt pps ppz pot
10
+ help.type: hlp chm
11
+ none.type: rar zip
12
+ resources.url: /fm_resources
13
+ resources.path: <%= RAILS_ROOT %>/public/fm_resources
14
+ encoding.to: UTF-8
15
+ encoding.from:
16
+ temp.dir:
File without changes
@@ -0,0 +1,136 @@
1
+ // script.aculo.us builder.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
2
+
3
+ // Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
4
+ //
5
+ // script.aculo.us is freely distributable under the terms of an MIT-style license.
6
+ // For details, see the script.aculo.us web site: http://script.aculo.us/
7
+
8
+ var Builder = {
9
+ NODEMAP: {
10
+ AREA: 'map',
11
+ CAPTION: 'table',
12
+ COL: 'table',
13
+ COLGROUP: 'table',
14
+ LEGEND: 'fieldset',
15
+ OPTGROUP: 'select',
16
+ OPTION: 'select',
17
+ PARAM: 'object',
18
+ TBODY: 'table',
19
+ TD: 'table',
20
+ TFOOT: 'table',
21
+ TH: 'table',
22
+ THEAD: 'table',
23
+ TR: 'table'
24
+ },
25
+ // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
26
+ // due to a Firefox bug
27
+ node: function(elementName) {
28
+ elementName = elementName.toUpperCase();
29
+
30
+ // try innerHTML approach
31
+ var parentTag = this.NODEMAP[elementName] || 'div';
32
+ var parentElement = document.createElement(parentTag);
33
+ try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
34
+ parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
35
+ } catch(e) {}
36
+ var element = parentElement.firstChild || null;
37
+
38
+ // see if browser added wrapping tags
39
+ if(element && (element.tagName.toUpperCase() != elementName))
40
+ element = element.getElementsByTagName(elementName)[0];
41
+
42
+ // fallback to createElement approach
43
+ if(!element) element = document.createElement(elementName);
44
+
45
+ // abort if nothing could be created
46
+ if(!element) return;
47
+
48
+ // attributes (or text)
49
+ if(arguments[1])
50
+ if(this._isStringOrNumber(arguments[1]) ||
51
+ (arguments[1] instanceof Array) ||
52
+ arguments[1].tagName) {
53
+ this._children(element, arguments[1]);
54
+ } else {
55
+ var attrs = this._attributes(arguments[1]);
56
+ if(attrs.length) {
57
+ try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
58
+ parentElement.innerHTML = "<" +elementName + " " +
59
+ attrs + "></" + elementName + ">";
60
+ } catch(e) {}
61
+ element = parentElement.firstChild || null;
62
+ // workaround firefox 1.0.X bug
63
+ if(!element) {
64
+ element = document.createElement(elementName);
65
+ for(attr in arguments[1])
66
+ element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
67
+ }
68
+ if(element.tagName.toUpperCase() != elementName)
69
+ element = parentElement.getElementsByTagName(elementName)[0];
70
+ }
71
+ }
72
+
73
+ // text, or array of children
74
+ if(arguments[2])
75
+ this._children(element, arguments[2]);
76
+
77
+ return element;
78
+ },
79
+ _text: function(text) {
80
+ return document.createTextNode(text);
81
+ },
82
+
83
+ ATTR_MAP: {
84
+ 'className': 'class',
85
+ 'htmlFor': 'for'
86
+ },
87
+
88
+ _attributes: function(attributes) {
89
+ var attrs = [];
90
+ for(attribute in attributes)
91
+ attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
92
+ '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'&quot;') + '"');
93
+ return attrs.join(" ");
94
+ },
95
+ _children: function(element, children) {
96
+ if(children.tagName) {
97
+ element.appendChild(children);
98
+ return;
99
+ }
100
+ if(typeof children=='object') { // array can hold nodes and text
101
+ children.flatten().each( function(e) {
102
+ if(typeof e=='object')
103
+ element.appendChild(e)
104
+ else
105
+ if(Builder._isStringOrNumber(e))
106
+ element.appendChild(Builder._text(e));
107
+ });
108
+ } else
109
+ if(Builder._isStringOrNumber(children))
110
+ element.appendChild(Builder._text(children));
111
+ },
112
+ _isStringOrNumber: function(param) {
113
+ return(typeof param=='string' || typeof param=='number');
114
+ },
115
+ build: function(html) {
116
+ var element = this.node('div');
117
+ $(element).update(html.strip());
118
+ return element.down();
119
+ },
120
+ dump: function(scope) {
121
+ if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope
122
+
123
+ var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
124
+ "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
125
+ "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
126
+ "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
127
+ "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
128
+ "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);
129
+
130
+ tags.each( function(tag){
131
+ scope[tag] = function() {
132
+ return Builder.node.apply(Builder, [tag].concat($A(arguments)));
133
+ }
134
+ });
135
+ }
136
+ }
@@ -0,0 +1,1122 @@
1
+ // script.aculo.us effects.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
2
+
3
+ // Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
4
+ // Contributors:
5
+ // Justin Palmer (http://encytemedia.com/)
6
+ // Mark Pilgrim (http://diveintomark.org/)
7
+ // Martin Bialasinki
8
+ //
9
+ // script.aculo.us is freely distributable under the terms of an MIT-style license.
10
+ // For details, see the script.aculo.us web site: http://script.aculo.us/
11
+
12
+ // converts rgb() and #xxx to #xxxxxx format,
13
+ // returns self (or first argument) if not convertable
14
+ String.prototype.parseColor = function() {
15
+ var color = '#';
16
+ if (this.slice(0,4) == 'rgb(') {
17
+ var cols = this.slice(4,this.length-1).split(',');
18
+ var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
19
+ } else {
20
+ if (this.slice(0,1) == '#') {
21
+ if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
22
+ if (this.length==7) color = this.toLowerCase();
23
+ }
24
+ }
25
+ return (color.length==7 ? color : (arguments[0] || this));
26
+ };
27
+
28
+ /*--------------------------------------------------------------------------*/
29
+
30
+ Element.collectTextNodes = function(element) {
31
+ return $A($(element).childNodes).collect( function(node) {
32
+ return (node.nodeType==3 ? node.nodeValue :
33
+ (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
34
+ }).flatten().join('');
35
+ };
36
+
37
+ Element.collectTextNodesIgnoreClass = function(element, className) {
38
+ return $A($(element).childNodes).collect( function(node) {
39
+ return (node.nodeType==3 ? node.nodeValue :
40
+ ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
41
+ Element.collectTextNodesIgnoreClass(node, className) : ''));
42
+ }).flatten().join('');
43
+ };
44
+
45
+ Element.setContentZoom = function(element, percent) {
46
+ element = $(element);
47
+ element.setStyle({fontSize: (percent/100) + 'em'});
48
+ if (Prototype.Browser.WebKit) window.scrollBy(0,0);
49
+ return element;
50
+ };
51
+
52
+ Element.getInlineOpacity = function(element){
53
+ return $(element).style.opacity || '';
54
+ };
55
+
56
+ Element.forceRerendering = function(element) {
57
+ try {
58
+ element = $(element);
59
+ var n = document.createTextNode(' ');
60
+ element.appendChild(n);
61
+ element.removeChild(n);
62
+ } catch(e) { }
63
+ };
64
+
65
+ /*--------------------------------------------------------------------------*/
66
+
67
+ var Effect = {
68
+ _elementDoesNotExistError: {
69
+ name: 'ElementDoesNotExistError',
70
+ message: 'The specified DOM element does not exist, but is required for this effect to operate'
71
+ },
72
+ Transitions: {
73
+ linear: Prototype.K,
74
+ sinoidal: function(pos) {
75
+ return (-Math.cos(pos*Math.PI)/2) + 0.5;
76
+ },
77
+ reverse: function(pos) {
78
+ return 1-pos;
79
+ },
80
+ flicker: function(pos) {
81
+ var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
82
+ return pos > 1 ? 1 : pos;
83
+ },
84
+ wobble: function(pos) {
85
+ return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
86
+ },
87
+ pulse: function(pos, pulses) {
88
+ pulses = pulses || 5;
89
+ return (
90
+ ((pos % (1/pulses)) * pulses).round() == 0 ?
91
+ ((pos * pulses * 2) - (pos * pulses * 2).floor()) :
92
+ 1 - ((pos * pulses * 2) - (pos * pulses * 2).floor())
93
+ );
94
+ },
95
+ spring: function(pos) {
96
+ return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
97
+ },
98
+ none: function(pos) {
99
+ return 0;
100
+ },
101
+ full: function(pos) {
102
+ return 1;
103
+ }
104
+ },
105
+ DefaultOptions: {
106
+ duration: 1.0, // seconds
107
+ fps: 100, // 100= assume 66fps max.
108
+ sync: false, // true for combining
109
+ from: 0.0,
110
+ to: 1.0,
111
+ delay: 0.0,
112
+ queue: 'parallel'
113
+ },
114
+ tagifyText: function(element) {
115
+ var tagifyStyle = 'position:relative';
116
+ if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
117
+
118
+ element = $(element);
119
+ $A(element.childNodes).each( function(child) {
120
+ if (child.nodeType==3) {
121
+ child.nodeValue.toArray().each( function(character) {
122
+ element.insertBefore(
123
+ new Element('span', {style: tagifyStyle}).update(
124
+ character == ' ' ? String.fromCharCode(160) : character),
125
+ child);
126
+ });
127
+ Element.remove(child);
128
+ }
129
+ });
130
+ },
131
+ multiple: function(element, effect) {
132
+ var elements;
133
+ if (((typeof element == 'object') ||
134
+ Object.isFunction(element)) &&
135
+ (element.length))
136
+ elements = element;
137
+ else
138
+ elements = $(element).childNodes;
139
+
140
+ var options = Object.extend({
141
+ speed: 0.1,
142
+ delay: 0.0
143
+ }, arguments[2] || { });
144
+ var masterDelay = options.delay;
145
+
146
+ $A(elements).each( function(element, index) {
147
+ new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
148
+ });
149
+ },
150
+ PAIRS: {
151
+ 'slide': ['SlideDown','SlideUp'],
152
+ 'blind': ['BlindDown','BlindUp'],
153
+ 'appear': ['Appear','Fade']
154
+ },
155
+ toggle: function(element, effect) {
156
+ element = $(element);
157
+ effect = (effect || 'appear').toLowerCase();
158
+ var options = Object.extend({
159
+ queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
160
+ }, arguments[2] || { });
161
+ Effect[element.visible() ?
162
+ Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
163
+ }
164
+ };
165
+
166
+ Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;
167
+
168
+ /* ------------- core effects ------------- */
169
+
170
+ Effect.ScopedQueue = Class.create(Enumerable, {
171
+ initialize: function() {
172
+ this.effects = [];
173
+ this.interval = null;
174
+ },
175
+ _each: function(iterator) {
176
+ this.effects._each(iterator);
177
+ },
178
+ add: function(effect) {
179
+ var timestamp = new Date().getTime();
180
+
181
+ var position = Object.isString(effect.options.queue) ?
182
+ effect.options.queue : effect.options.queue.position;
183
+
184
+ switch(position) {
185
+ case 'front':
186
+ // move unstarted effects after this effect
187
+ this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
188
+ e.startOn += effect.finishOn;
189
+ e.finishOn += effect.finishOn;
190
+ });
191
+ break;
192
+ case 'with-last':
193
+ timestamp = this.effects.pluck('startOn').max() || timestamp;
194
+ break;
195
+ case 'end':
196
+ // start effect after last queued effect has finished
197
+ timestamp = this.effects.pluck('finishOn').max() || timestamp;
198
+ break;
199
+ }
200
+
201
+ effect.startOn += timestamp;
202
+ effect.finishOn += timestamp;
203
+
204
+ if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
205
+ this.effects.push(effect);
206
+
207
+ if (!this.interval)
208
+ this.interval = setInterval(this.loop.bind(this), 15);
209
+ },
210
+ remove: function(effect) {
211
+ this.effects = this.effects.reject(function(e) { return e==effect });
212
+ if (this.effects.length == 0) {
213
+ clearInterval(this.interval);
214
+ this.interval = null;
215
+ }
216
+ },
217
+ loop: function() {
218
+ var timePos = new Date().getTime();
219
+ for(var i=0, len=this.effects.length;i<len;i++)
220
+ this.effects[i] && this.effects[i].loop(timePos);
221
+ }
222
+ });
223
+
224
+ Effect.Queues = {
225
+ instances: $H(),
226
+ get: function(queueName) {
227
+ if (!Object.isString(queueName)) return queueName;
228
+
229
+ return this.instances.get(queueName) ||
230
+ this.instances.set(queueName, new Effect.ScopedQueue());
231
+ }
232
+ };
233
+ Effect.Queue = Effect.Queues.get('global');
234
+
235
+ Effect.Base = Class.create({
236
+ position: null,
237
+ start: function(options) {
238
+ function codeForEvent(options,eventName){
239
+ return (
240
+ (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +
241
+ (options[eventName] ? 'this.options.'+eventName+'(this);' : '')
242
+ );
243
+ }
244
+ if (options && options.transition === false) options.transition = Effect.Transitions.linear;
245
+ this.options = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
246
+ this.currentFrame = 0;
247
+ this.state = 'idle';
248
+ this.startOn = this.options.delay*1000;
249
+ this.finishOn = this.startOn+(this.options.duration*1000);
250
+ this.fromToDelta = this.options.to-this.options.from;
251
+ this.totalTime = this.finishOn-this.startOn;
252
+ this.totalFrames = this.options.fps*this.options.duration;
253
+
254
+ eval('this.render = function(pos){ '+
255
+ 'if (this.state=="idle"){this.state="running";'+
256
+ codeForEvent(this.options,'beforeSetup')+
257
+ (this.setup ? 'this.setup();':'')+
258
+ codeForEvent(this.options,'afterSetup')+
259
+ '};if (this.state=="running"){'+
260
+ 'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+
261
+ 'this.position=pos;'+
262
+ codeForEvent(this.options,'beforeUpdate')+
263
+ (this.update ? 'this.update(pos);':'')+
264
+ codeForEvent(this.options,'afterUpdate')+
265
+ '}}');
266
+
267
+ this.event('beforeStart');
268
+ if (!this.options.sync)
269
+ Effect.Queues.get(Object.isString(this.options.queue) ?
270
+ 'global' : this.options.queue.scope).add(this);
271
+ },
272
+ loop: function(timePos) {
273
+ if (timePos >= this.startOn) {
274
+ if (timePos >= this.finishOn) {
275
+ this.render(1.0);
276
+ this.cancel();
277
+ this.event('beforeFinish');
278
+ if (this.finish) this.finish();
279
+ this.event('afterFinish');
280
+ return;
281
+ }
282
+ var pos = (timePos - this.startOn) / this.totalTime,
283
+ frame = (pos * this.totalFrames).round();
284
+ if (frame > this.currentFrame) {
285
+ this.render(pos);
286
+ this.currentFrame = frame;
287
+ }
288
+ }
289
+ },
290
+ cancel: function() {
291
+ if (!this.options.sync)
292
+ Effect.Queues.get(Object.isString(this.options.queue) ?
293
+ 'global' : this.options.queue.scope).remove(this);
294
+ this.state = 'finished';
295
+ },
296
+ event: function(eventName) {
297
+ if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
298
+ if (this.options[eventName]) this.options[eventName](this);
299
+ },
300
+ inspect: function() {
301
+ var data = $H();
302
+ for(property in this)
303
+ if (!Object.isFunction(this[property])) data.set(property, this[property]);
304
+ return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
305
+ }
306
+ });
307
+
308
+ Effect.Parallel = Class.create(Effect.Base, {
309
+ initialize: function(effects) {
310
+ this.effects = effects || [];
311
+ this.start(arguments[1]);
312
+ },
313
+ update: function(position) {
314
+ this.effects.invoke('render', position);
315
+ },
316
+ finish: function(position) {
317
+ this.effects.each( function(effect) {
318
+ effect.render(1.0);
319
+ effect.cancel();
320
+ effect.event('beforeFinish');
321
+ if (effect.finish) effect.finish(position);
322
+ effect.event('afterFinish');
323
+ });
324
+ }
325
+ });
326
+
327
+ Effect.Tween = Class.create(Effect.Base, {
328
+ initialize: function(object, from, to) {
329
+ object = Object.isString(object) ? $(object) : object;
330
+ var args = $A(arguments), method = args.last(),
331
+ options = args.length == 5 ? args[3] : null;
332
+ this.method = Object.isFunction(method) ? method.bind(object) :
333
+ Object.isFunction(object[method]) ? object[method].bind(object) :
334
+ function(value) { object[method] = value };
335
+ this.start(Object.extend({ from: from, to: to }, options || { }));
336
+ },
337
+ update: function(position) {
338
+ this.method(position);
339
+ }
340
+ });
341
+
342
+ Effect.Event = Class.create(Effect.Base, {
343
+ initialize: function() {
344
+ this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
345
+ },
346
+ update: Prototype.emptyFunction
347
+ });
348
+
349
+ Effect.Opacity = Class.create(Effect.Base, {
350
+ initialize: function(element) {
351
+ this.element = $(element);
352
+ if (!this.element) throw(Effect._elementDoesNotExistError);
353
+ // make this work on IE on elements without 'layout'
354
+ if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
355
+ this.element.setStyle({zoom: 1});
356
+ var options = Object.extend({
357
+ from: this.element.getOpacity() || 0.0,
358
+ to: 1.0
359
+ }, arguments[1] || { });
360
+ this.start(options);
361
+ },
362
+ update: function(position) {
363
+ this.element.setOpacity(position);
364
+ }
365
+ });
366
+
367
+ Effect.Move = Class.create(Effect.Base, {
368
+ initialize: function(element) {
369
+ this.element = $(element);
370
+ if (!this.element) throw(Effect._elementDoesNotExistError);
371
+ var options = Object.extend({
372
+ x: 0,
373
+ y: 0,
374
+ mode: 'relative'
375
+ }, arguments[1] || { });
376
+ this.start(options);
377
+ },
378
+ setup: function() {
379
+ this.element.makePositioned();
380
+ this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
381
+ this.originalTop = parseFloat(this.element.getStyle('top') || '0');
382
+ if (this.options.mode == 'absolute') {
383
+ this.options.x = this.options.x - this.originalLeft;
384
+ this.options.y = this.options.y - this.originalTop;
385
+ }
386
+ },
387
+ update: function(position) {
388
+ this.element.setStyle({
389
+ left: (this.options.x * position + this.originalLeft).round() + 'px',
390
+ top: (this.options.y * position + this.originalTop).round() + 'px'
391
+ });
392
+ }
393
+ });
394
+
395
+ // for backwards compatibility
396
+ Effect.MoveBy = function(element, toTop, toLeft) {
397
+ return new Effect.Move(element,
398
+ Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
399
+ };
400
+
401
+ Effect.Scale = Class.create(Effect.Base, {
402
+ initialize: function(element, percent) {
403
+ this.element = $(element);
404
+ if (!this.element) throw(Effect._elementDoesNotExistError);
405
+ var options = Object.extend({
406
+ scaleX: true,
407
+ scaleY: true,
408
+ scaleContent: true,
409
+ scaleFromCenter: false,
410
+ scaleMode: 'box', // 'box' or 'contents' or { } with provided values
411
+ scaleFrom: 100.0,
412
+ scaleTo: percent
413
+ }, arguments[2] || { });
414
+ this.start(options);
415
+ },
416
+ setup: function() {
417
+ this.restoreAfterFinish = this.options.restoreAfterFinish || false;
418
+ this.elementPositioning = this.element.getStyle('position');
419
+
420
+ this.originalStyle = { };
421
+ ['top','left','width','height','fontSize'].each( function(k) {
422
+ this.originalStyle[k] = this.element.style[k];
423
+ }.bind(this));
424
+
425
+ this.originalTop = this.element.offsetTop;
426
+ this.originalLeft = this.element.offsetLeft;
427
+
428
+ var fontSize = this.element.getStyle('font-size') || '100%';
429
+ ['em','px','%','pt'].each( function(fontSizeType) {
430
+ if (fontSize.indexOf(fontSizeType)>0) {
431
+ this.fontSize = parseFloat(fontSize);
432
+ this.fontSizeType = fontSizeType;
433
+ }
434
+ }.bind(this));
435
+
436
+ this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
437
+
438
+ this.dims = null;
439
+ if (this.options.scaleMode=='box')
440
+ this.dims = [this.element.offsetHeight, this.element.offsetWidth];
441
+ if (/^content/.test(this.options.scaleMode))
442
+ this.dims = [this.element.scrollHeight, this.element.scrollWidth];
443
+ if (!this.dims)
444
+ this.dims = [this.options.scaleMode.originalHeight,
445
+ this.options.scaleMode.originalWidth];
446
+ },
447
+ update: function(position) {
448
+ var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
449
+ if (this.options.scaleContent && this.fontSize)
450
+ this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
451
+ this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
452
+ },
453
+ finish: function(position) {
454
+ if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
455
+ },
456
+ setDimensions: function(height, width) {
457
+ var d = { };
458
+ if (this.options.scaleX) d.width = width.round() + 'px';
459
+ if (this.options.scaleY) d.height = height.round() + 'px';
460
+ if (this.options.scaleFromCenter) {
461
+ var topd = (height - this.dims[0])/2;
462
+ var leftd = (width - this.dims[1])/2;
463
+ if (this.elementPositioning == 'absolute') {
464
+ if (this.options.scaleY) d.top = this.originalTop-topd + 'px';
465
+ if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
466
+ } else {
467
+ if (this.options.scaleY) d.top = -topd + 'px';
468
+ if (this.options.scaleX) d.left = -leftd + 'px';
469
+ }
470
+ }
471
+ this.element.setStyle(d);
472
+ }
473
+ });
474
+
475
+ Effect.Highlight = Class.create(Effect.Base, {
476
+ initialize: function(element) {
477
+ this.element = $(element);
478
+ if (!this.element) throw(Effect._elementDoesNotExistError);
479
+ var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });
480
+ this.start(options);
481
+ },
482
+ setup: function() {
483
+ // Prevent executing on elements not in the layout flow
484
+ if (this.element.getStyle('display')=='none') { this.cancel(); return; }
485
+ // Disable background image during the effect
486
+ this.oldStyle = { };
487
+ if (!this.options.keepBackgroundImage) {
488
+ this.oldStyle.backgroundImage = this.element.getStyle('background-image');
489
+ this.element.setStyle({backgroundImage: 'none'});
490
+ }
491
+ if (!this.options.endcolor)
492
+ this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
493
+ if (!this.options.restorecolor)
494
+ this.options.restorecolor = this.element.getStyle('background-color');
495
+ // init color calculations
496
+ this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
497
+ this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
498
+ },
499
+ update: function(position) {
500
+ this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
501
+ return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
502
+ },
503
+ finish: function() {
504
+ this.element.setStyle(Object.extend(this.oldStyle, {
505
+ backgroundColor: this.options.restorecolor
506
+ }));
507
+ }
508
+ });
509
+
510
+ Effect.ScrollTo = function(element) {
511
+ var options = arguments[1] || { },
512
+ scrollOffsets = document.viewport.getScrollOffsets(),
513
+ elementOffsets = $(element).cumulativeOffset(),
514
+ max = (window.height || document.body.scrollHeight) - document.viewport.getHeight();
515
+
516
+ if (options.offset) elementOffsets[1] += options.offset;
517
+
518
+ return new Effect.Tween(null,
519
+ scrollOffsets.top,
520
+ elementOffsets[1] > max ? max : elementOffsets[1],
521
+ options,
522
+ function(p){ scrollTo(scrollOffsets.left, p.round()) }
523
+ );
524
+ };
525
+
526
+ /* ------------- combination effects ------------- */
527
+
528
+ Effect.Fade = function(element) {
529
+ element = $(element);
530
+ var oldOpacity = element.getInlineOpacity();
531
+ var options = Object.extend({
532
+ from: element.getOpacity() || 1.0,
533
+ to: 0.0,
534
+ afterFinishInternal: function(effect) {
535
+ if (effect.options.to!=0) return;
536
+ effect.element.hide().setStyle({opacity: oldOpacity});
537
+ }
538
+ }, arguments[1] || { });
539
+ return new Effect.Opacity(element,options);
540
+ };
541
+
542
+ Effect.Appear = function(element) {
543
+ element = $(element);
544
+ var options = Object.extend({
545
+ from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
546
+ to: 1.0,
547
+ // force Safari to render floated elements properly
548
+ afterFinishInternal: function(effect) {
549
+ effect.element.forceRerendering();
550
+ },
551
+ beforeSetup: function(effect) {
552
+ effect.element.setOpacity(effect.options.from).show();
553
+ }}, arguments[1] || { });
554
+ return new Effect.Opacity(element,options);
555
+ };
556
+
557
+ Effect.Puff = function(element) {
558
+ element = $(element);
559
+ var oldStyle = {
560
+ opacity: element.getInlineOpacity(),
561
+ position: element.getStyle('position'),
562
+ top: element.style.top,
563
+ left: element.style.left,
564
+ width: element.style.width,
565
+ height: element.style.height
566
+ };
567
+ return new Effect.Parallel(
568
+ [ new Effect.Scale(element, 200,
569
+ { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
570
+ new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
571
+ Object.extend({ duration: 1.0,
572
+ beforeSetupInternal: function(effect) {
573
+ Position.absolutize(effect.effects[0].element)
574
+ },
575
+ afterFinishInternal: function(effect) {
576
+ effect.effects[0].element.hide().setStyle(oldStyle); }
577
+ }, arguments[1] || { })
578
+ );
579
+ };
580
+
581
+ Effect.BlindUp = function(element) {
582
+ element = $(element);
583
+ element.makeClipping();
584
+ return new Effect.Scale(element, 0,
585
+ Object.extend({ scaleContent: false,
586
+ scaleX: false,
587
+ restoreAfterFinish: true,
588
+ afterFinishInternal: function(effect) {
589
+ effect.element.hide().undoClipping();
590
+ }
591
+ }, arguments[1] || { })
592
+ );
593
+ };
594
+
595
+ Effect.BlindDown = function(element) {
596
+ element = $(element);
597
+ var elementDimensions = element.getDimensions();
598
+ return new Effect.Scale(element, 100, Object.extend({
599
+ scaleContent: false,
600
+ scaleX: false,
601
+ scaleFrom: 0,
602
+ scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
603
+ restoreAfterFinish: true,
604
+ afterSetup: function(effect) {
605
+ effect.element.makeClipping().setStyle({height: '0px'}).show();
606
+ },
607
+ afterFinishInternal: function(effect) {
608
+ effect.element.undoClipping();
609
+ }
610
+ }, arguments[1] || { }));
611
+ };
612
+
613
+ Effect.SwitchOff = function(element) {
614
+ element = $(element);
615
+ var oldOpacity = element.getInlineOpacity();
616
+ return new Effect.Appear(element, Object.extend({
617
+ duration: 0.4,
618
+ from: 0,
619
+ transition: Effect.Transitions.flicker,
620
+ afterFinishInternal: function(effect) {
621
+ new Effect.Scale(effect.element, 1, {
622
+ duration: 0.3, scaleFromCenter: true,
623
+ scaleX: false, scaleContent: false, restoreAfterFinish: true,
624
+ beforeSetup: function(effect) {
625
+ effect.element.makePositioned().makeClipping();
626
+ },
627
+ afterFinishInternal: function(effect) {
628
+ effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
629
+ }
630
+ })
631
+ }
632
+ }, arguments[1] || { }));
633
+ };
634
+
635
+ Effect.DropOut = function(element) {
636
+ element = $(element);
637
+ var oldStyle = {
638
+ top: element.getStyle('top'),
639
+ left: element.getStyle('left'),
640
+ opacity: element.getInlineOpacity() };
641
+ return new Effect.Parallel(
642
+ [ new Effect.Move(element, {x: 0, y: 100, sync: true }),
643
+ new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
644
+ Object.extend(
645
+ { duration: 0.5,
646
+ beforeSetup: function(effect) {
647
+ effect.effects[0].element.makePositioned();
648
+ },
649
+ afterFinishInternal: function(effect) {
650
+ effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
651
+ }
652
+ }, arguments[1] || { }));
653
+ };
654
+
655
+ Effect.Shake = function(element) {
656
+ element = $(element);
657
+ var options = Object.extend({
658
+ distance: 20,
659
+ duration: 0.5
660
+ }, arguments[1] || {});
661
+ var distance = parseFloat(options.distance);
662
+ var split = parseFloat(options.duration) / 10.0;
663
+ var oldStyle = {
664
+ top: element.getStyle('top'),
665
+ left: element.getStyle('left') };
666
+ return new Effect.Move(element,
667
+ { x: distance, y: 0, duration: split, afterFinishInternal: function(effect) {
668
+ new Effect.Move(effect.element,
669
+ { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
670
+ new Effect.Move(effect.element,
671
+ { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
672
+ new Effect.Move(effect.element,
673
+ { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
674
+ new Effect.Move(effect.element,
675
+ { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
676
+ new Effect.Move(effect.element,
677
+ { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
678
+ effect.element.undoPositioned().setStyle(oldStyle);
679
+ }}) }}) }}) }}) }}) }});
680
+ };
681
+
682
+ Effect.SlideDown = function(element) {
683
+ element = $(element).cleanWhitespace();
684
+ // SlideDown need to have the content of the element wrapped in a container element with fixed height!
685
+ var oldInnerBottom = element.down().getStyle('bottom');
686
+ var elementDimensions = element.getDimensions();
687
+ return new Effect.Scale(element, 100, Object.extend({
688
+ scaleContent: false,
689
+ scaleX: false,
690
+ scaleFrom: window.opera ? 0 : 1,
691
+ scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
692
+ restoreAfterFinish: true,
693
+ afterSetup: function(effect) {
694
+ effect.element.makePositioned();
695
+ effect.element.down().makePositioned();
696
+ if (window.opera) effect.element.setStyle({top: ''});
697
+ effect.element.makeClipping().setStyle({height: '0px'}).show();
698
+ },
699
+ afterUpdateInternal: function(effect) {
700
+ effect.element.down().setStyle({bottom:
701
+ (effect.dims[0] - effect.element.clientHeight) + 'px' });
702
+ },
703
+ afterFinishInternal: function(effect) {
704
+ effect.element.undoClipping().undoPositioned();
705
+ effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
706
+ }, arguments[1] || { })
707
+ );
708
+ };
709
+
710
+ Effect.SlideUp = function(element) {
711
+ element = $(element).cleanWhitespace();
712
+ var oldInnerBottom = element.down().getStyle('bottom');
713
+ var elementDimensions = element.getDimensions();
714
+ return new Effect.Scale(element, window.opera ? 0 : 1,
715
+ Object.extend({ scaleContent: false,
716
+ scaleX: false,
717
+ scaleMode: 'box',
718
+ scaleFrom: 100,
719
+ scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
720
+ restoreAfterFinish: true,
721
+ afterSetup: function(effect) {
722
+ effect.element.makePositioned();
723
+ effect.element.down().makePositioned();
724
+ if (window.opera) effect.element.setStyle({top: ''});
725
+ effect.element.makeClipping().show();
726
+ },
727
+ afterUpdateInternal: function(effect) {
728
+ effect.element.down().setStyle({bottom:
729
+ (effect.dims[0] - effect.element.clientHeight) + 'px' });
730
+ },
731
+ afterFinishInternal: function(effect) {
732
+ effect.element.hide().undoClipping().undoPositioned();
733
+ effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
734
+ }
735
+ }, arguments[1] || { })
736
+ );
737
+ };
738
+
739
+ // Bug in opera makes the TD containing this element expand for a instance after finish
740
+ Effect.Squish = function(element) {
741
+ return new Effect.Scale(element, window.opera ? 1 : 0, {
742
+ restoreAfterFinish: true,
743
+ beforeSetup: function(effect) {
744
+ effect.element.makeClipping();
745
+ },
746
+ afterFinishInternal: function(effect) {
747
+ effect.element.hide().undoClipping();
748
+ }
749
+ });
750
+ };
751
+
752
+ Effect.Grow = function(element) {
753
+ element = $(element);
754
+ var options = Object.extend({
755
+ direction: 'center',
756
+ moveTransition: Effect.Transitions.sinoidal,
757
+ scaleTransition: Effect.Transitions.sinoidal,
758
+ opacityTransition: Effect.Transitions.full
759
+ }, arguments[1] || { });
760
+ var oldStyle = {
761
+ top: element.style.top,
762
+ left: element.style.left,
763
+ height: element.style.height,
764
+ width: element.style.width,
765
+ opacity: element.getInlineOpacity() };
766
+
767
+ var dims = element.getDimensions();
768
+ var initialMoveX, initialMoveY;
769
+ var moveX, moveY;
770
+
771
+ switch (options.direction) {
772
+ case 'top-left':
773
+ initialMoveX = initialMoveY = moveX = moveY = 0;
774
+ break;
775
+ case 'top-right':
776
+ initialMoveX = dims.width;
777
+ initialMoveY = moveY = 0;
778
+ moveX = -dims.width;
779
+ break;
780
+ case 'bottom-left':
781
+ initialMoveX = moveX = 0;
782
+ initialMoveY = dims.height;
783
+ moveY = -dims.height;
784
+ break;
785
+ case 'bottom-right':
786
+ initialMoveX = dims.width;
787
+ initialMoveY = dims.height;
788
+ moveX = -dims.width;
789
+ moveY = -dims.height;
790
+ break;
791
+ case 'center':
792
+ initialMoveX = dims.width / 2;
793
+ initialMoveY = dims.height / 2;
794
+ moveX = -dims.width / 2;
795
+ moveY = -dims.height / 2;
796
+ break;
797
+ }
798
+
799
+ return new Effect.Move(element, {
800
+ x: initialMoveX,
801
+ y: initialMoveY,
802
+ duration: 0.01,
803
+ beforeSetup: function(effect) {
804
+ effect.element.hide().makeClipping().makePositioned();
805
+ },
806
+ afterFinishInternal: function(effect) {
807
+ new Effect.Parallel(
808
+ [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
809
+ new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
810
+ new Effect.Scale(effect.element, 100, {
811
+ scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
812
+ sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
813
+ ], Object.extend({
814
+ beforeSetup: function(effect) {
815
+ effect.effects[0].element.setStyle({height: '0px'}).show();
816
+ },
817
+ afterFinishInternal: function(effect) {
818
+ effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
819
+ }
820
+ }, options)
821
+ )
822
+ }
823
+ });
824
+ };
825
+
826
+ Effect.Shrink = function(element) {
827
+ element = $(element);
828
+ var options = Object.extend({
829
+ direction: 'center',
830
+ moveTransition: Effect.Transitions.sinoidal,
831
+ scaleTransition: Effect.Transitions.sinoidal,
832
+ opacityTransition: Effect.Transitions.none
833
+ }, arguments[1] || { });
834
+ var oldStyle = {
835
+ top: element.style.top,
836
+ left: element.style.left,
837
+ height: element.style.height,
838
+ width: element.style.width,
839
+ opacity: element.getInlineOpacity() };
840
+
841
+ var dims = element.getDimensions();
842
+ var moveX, moveY;
843
+
844
+ switch (options.direction) {
845
+ case 'top-left':
846
+ moveX = moveY = 0;
847
+ break;
848
+ case 'top-right':
849
+ moveX = dims.width;
850
+ moveY = 0;
851
+ break;
852
+ case 'bottom-left':
853
+ moveX = 0;
854
+ moveY = dims.height;
855
+ break;
856
+ case 'bottom-right':
857
+ moveX = dims.width;
858
+ moveY = dims.height;
859
+ break;
860
+ case 'center':
861
+ moveX = dims.width / 2;
862
+ moveY = dims.height / 2;
863
+ break;
864
+ }
865
+
866
+ return new Effect.Parallel(
867
+ [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
868
+ new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
869
+ new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
870
+ ], Object.extend({
871
+ beforeStartInternal: function(effect) {
872
+ effect.effects[0].element.makePositioned().makeClipping();
873
+ },
874
+ afterFinishInternal: function(effect) {
875
+ effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
876
+ }, options)
877
+ );
878
+ };
879
+
880
+ Effect.Pulsate = function(element) {
881
+ element = $(element);
882
+ var options = arguments[1] || { };
883
+ var oldOpacity = element.getInlineOpacity();
884
+ var transition = options.transition || Effect.Transitions.sinoidal;
885
+ var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
886
+ reverser.bind(transition);
887
+ return new Effect.Opacity(element,
888
+ Object.extend(Object.extend({ duration: 2.0, from: 0,
889
+ afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
890
+ }, options), {transition: reverser}));
891
+ };
892
+
893
+ Effect.Fold = function(element) {
894
+ element = $(element);
895
+ var oldStyle = {
896
+ top: element.style.top,
897
+ left: element.style.left,
898
+ width: element.style.width,
899
+ height: element.style.height };
900
+ element.makeClipping();
901
+ return new Effect.Scale(element, 5, Object.extend({
902
+ scaleContent: false,
903
+ scaleX: false,
904
+ afterFinishInternal: function(effect) {
905
+ new Effect.Scale(element, 1, {
906
+ scaleContent: false,
907
+ scaleY: false,
908
+ afterFinishInternal: function(effect) {
909
+ effect.element.hide().undoClipping().setStyle(oldStyle);
910
+ } });
911
+ }}, arguments[1] || { }));
912
+ };
913
+
914
+ Effect.Morph = Class.create(Effect.Base, {
915
+ initialize: function(element) {
916
+ this.element = $(element);
917
+ if (!this.element) throw(Effect._elementDoesNotExistError);
918
+ var options = Object.extend({
919
+ style: { }
920
+ }, arguments[1] || { });
921
+
922
+ if (!Object.isString(options.style)) this.style = $H(options.style);
923
+ else {
924
+ if (options.style.include(':'))
925
+ this.style = options.style.parseStyle();
926
+ else {
927
+ this.element.addClassName(options.style);
928
+ this.style = $H(this.element.getStyles());
929
+ this.element.removeClassName(options.style);
930
+ var css = this.element.getStyles();
931
+ this.style = this.style.reject(function(style) {
932
+ return style.value == css[style.key];
933
+ });
934
+ options.afterFinishInternal = function(effect) {
935
+ effect.element.addClassName(effect.options.style);
936
+ effect.transforms.each(function(transform) {
937
+ effect.element.style[transform.style] = '';
938
+ });
939
+ }
940
+ }
941
+ }
942
+ this.start(options);
943
+ },
944
+
945
+ setup: function(){
946
+ function parseColor(color){
947
+ if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
948
+ color = color.parseColor();
949
+ return $R(0,2).map(function(i){
950
+ return parseInt( color.slice(i*2+1,i*2+3), 16 )
951
+ });
952
+ }
953
+ this.transforms = this.style.map(function(pair){
954
+ var property = pair[0], value = pair[1], unit = null;
955
+
956
+ if (value.parseColor('#zzzzzz') != '#zzzzzz') {
957
+ value = value.parseColor();
958
+ unit = 'color';
959
+ } else if (property == 'opacity') {
960
+ value = parseFloat(value);
961
+ if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
962
+ this.element.setStyle({zoom: 1});
963
+ } else if (Element.CSS_LENGTH.test(value)) {
964
+ var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
965
+ value = parseFloat(components[1]);
966
+ unit = (components.length == 3) ? components[2] : null;
967
+ }
968
+
969
+ var originalValue = this.element.getStyle(property);
970
+ return {
971
+ style: property.camelize(),
972
+ originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),
973
+ targetValue: unit=='color' ? parseColor(value) : value,
974
+ unit: unit
975
+ };
976
+ }.bind(this)).reject(function(transform){
977
+ return (
978
+ (transform.originalValue == transform.targetValue) ||
979
+ (
980
+ transform.unit != 'color' &&
981
+ (isNaN(transform.originalValue) || isNaN(transform.targetValue))
982
+ )
983
+ )
984
+ });
985
+ },
986
+ update: function(position) {
987
+ var style = { }, transform, i = this.transforms.length;
988
+ while(i--)
989
+ style[(transform = this.transforms[i]).style] =
990
+ transform.unit=='color' ? '#'+
991
+ (Math.round(transform.originalValue[0]+
992
+ (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
993
+ (Math.round(transform.originalValue[1]+
994
+ (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
995
+ (Math.round(transform.originalValue[2]+
996
+ (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
997
+ (transform.originalValue +
998
+ (transform.targetValue - transform.originalValue) * position).toFixed(3) +
999
+ (transform.unit === null ? '' : transform.unit);
1000
+ this.element.setStyle(style, true);
1001
+ }
1002
+ });
1003
+
1004
+ Effect.Transform = Class.create({
1005
+ initialize: function(tracks){
1006
+ this.tracks = [];
1007
+ this.options = arguments[1] || { };
1008
+ this.addTracks(tracks);
1009
+ },
1010
+ addTracks: function(tracks){
1011
+ tracks.each(function(track){
1012
+ track = $H(track);
1013
+ var data = track.values().first();
1014
+ this.tracks.push($H({
1015
+ ids: track.keys().first(),
1016
+ effect: Effect.Morph,
1017
+ options: { style: data }
1018
+ }));
1019
+ }.bind(this));
1020
+ return this;
1021
+ },
1022
+ play: function(){
1023
+ return new Effect.Parallel(
1024
+ this.tracks.map(function(track){
1025
+ var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
1026
+ var elements = [$(ids) || $$(ids)].flatten();
1027
+ return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });
1028
+ }).flatten(),
1029
+ this.options
1030
+ );
1031
+ }
1032
+ });
1033
+
1034
+ Element.CSS_PROPERTIES = $w(
1035
+ 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
1036
+ 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
1037
+ 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
1038
+ 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
1039
+ 'fontSize fontWeight height left letterSpacing lineHeight ' +
1040
+ 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
1041
+ 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
1042
+ 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
1043
+ 'right textIndent top width wordSpacing zIndex');
1044
+
1045
+ Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
1046
+
1047
+ String.__parseStyleElement = document.createElement('div');
1048
+ String.prototype.parseStyle = function(){
1049
+ var style, styleRules = $H();
1050
+ if (Prototype.Browser.WebKit)
1051
+ style = new Element('div',{style:this}).style;
1052
+ else {
1053
+ String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
1054
+ style = String.__parseStyleElement.childNodes[0].style;
1055
+ }
1056
+
1057
+ Element.CSS_PROPERTIES.each(function(property){
1058
+ if (style[property]) styleRules.set(property, style[property]);
1059
+ });
1060
+
1061
+ if (Prototype.Browser.IE && this.include('opacity'))
1062
+ styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);
1063
+
1064
+ return styleRules;
1065
+ };
1066
+
1067
+ if (document.defaultView && document.defaultView.getComputedStyle) {
1068
+ Element.getStyles = function(element) {
1069
+ var css = document.defaultView.getComputedStyle($(element), null);
1070
+ return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
1071
+ styles[property] = css[property];
1072
+ return styles;
1073
+ });
1074
+ };
1075
+ } else {
1076
+ Element.getStyles = function(element) {
1077
+ element = $(element);
1078
+ var css = element.currentStyle, styles;
1079
+ styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {
1080
+ results[property] = css[property];
1081
+ return results;
1082
+ });
1083
+ if (!styles.opacity) styles.opacity = element.getOpacity();
1084
+ return styles;
1085
+ };
1086
+ };
1087
+
1088
+ Effect.Methods = {
1089
+ morph: function(element, style) {
1090
+ element = $(element);
1091
+ new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
1092
+ return element;
1093
+ },
1094
+ visualEffect: function(element, effect, options) {
1095
+ element = $(element)
1096
+ var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
1097
+ new Effect[klass](element, options);
1098
+ return element;
1099
+ },
1100
+ highlight: function(element, options) {
1101
+ element = $(element);
1102
+ new Effect.Highlight(element, options);
1103
+ return element;
1104
+ }
1105
+ };
1106
+
1107
+ $w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
1108
+ 'pulsate shake puff squish switchOff dropOut').each(
1109
+ function(effect) {
1110
+ Effect.Methods[effect] = function(element, options){
1111
+ element = $(element);
1112
+ Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
1113
+ return element;
1114
+ }
1115
+ }
1116
+ );
1117
+
1118
+ $w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(
1119
+ function(f) { Effect.Methods[f] = Element[f]; }
1120
+ );
1121
+
1122
+ Element.addMethods(Effect.Methods);