Revision 24d0c6a9

b/ui/static/jQueryRotate.js
1
// VERSION: 1.7 LAST UPDATE: 16.12.2010
2
/*
3
 * THIS IS FREE SCRIPT BUT LEAVE THIS COMMENT IF
4
 * YOU WANT USE THIS CODE ON YOUR SITE
5
 * 
6
 * Made by Wilq32, wilq32@gmail.com, Wroclaw, Poland, 01.2009
7
 * http://wilq32.blogspot.com
8
 * 
9
 */
10
/*
11
Description:
12

  
13
This is an final product of a Wilq32.PhotoEffect Snippet. Actually you can
14
 use this simple and tiny script to get effect of rotated images directly 
15
 from client side (for ex. user generated content), and animate them using
16
 own functions. 
17

  
18

  
19
Notices:
20

  
21
Include script after including main jQuery. Whole plugin uses jQuery
22
namespace and should be compatible with older version (unchecked). 
23

  
24
Usage:
25

  
26
jQuery(imgElement).rotate(angleValue)
27
jQuery(imgElement).rotate(parameters)
28
jQuery(imgElement).rotateAnimation(parameters)
29
jQuery(imgElement).rotateAnimation(parameters)
30

  
31

  
32

  
33
Returns:
34

  
35
jQueryRotateElement - !!! NOTICE !!! function return rotateElement
36
instance to help connect events with actually created 'rotation' element.
37

  
38
Parameters:
39

  
40
    ({angle:angleValue,
41
     [animateAngle:animateAngleValue],
42
	 [easing:easingFunction],
43
	 [duration:durationValue],
44
     !!! DEPRECATED !!!    [maxAngle:maxAngleValue],
45
     !!! DEPRECATED !!!    [minAngle:minAngleValue],
46
     [callback:callbackFunction],
47
	 [animatedGif:animatedGifBoolean],
48
     [bind:[{event: function},{event:function} ] })
49
jQuery(imgElement).rotateAnimation
50

  
51
Where:
52

  
53
- angleValue - clockwise rotation given in degrees,
54
- [animateAngleValue] - optional parameter, animate rotating into this value,
55
- [easing] - optional parameter, function to control animation speed - supports native easing
56
             function and a jquery easing plugin, default: easeOutQuart
57
- [duration] - optional parameter, duration of a animation - default 1000ms 
58
- [maxAngleValue] - !!! DEPRECATED !!!    optional parameter, maximum angle possible for animation,
59
- [minAngleValue] - !!! DEPRECATED !!!    optional parameter, minimum angle possible for animation,
60
- [callbackFunction] - optional function to run after animation is done
61
- [animatedGifBoolean](boolean)  - optional set to display animated gif in firefox/chrome/safari
62
            !!! this might slow down browser because it need to render image again and
63
			again to display animation,
64
- [bind: [ {event: function}...] -optional parameter, list of events binded
65
  to newly created rotateable object
66

  
67
Examples:
68

  
69
		$(document).ready(function()
70
		{
71
			$('#image').rotate(-25);			
72
		});
73

  
74
		$(document).ready(function()
75
		{
76
			$('#image2').rotate({angle:5});	
77
		});
78

  
79
		$(document).ready(function()
80
		{
81
			var rot=$('#image3').rotate({maxAngle:25,minAngle:-55, duration:570,
82
			easing:$.easing.easeInOutExpo,
83
			bind:
84
				[
85
					{"mouseover":function(){rot[0].rotateAnimation(85);}},
86
					{"mouseout":function(){rot[0].rotateAnimation(-35);}}
87
				]
88
			});
89
		});
90
*/
91

  
92
(function($) {
93
var supportedCSS,styles=document.getElementsByTagName("head")[0].style,toCheck="transformProperty WebkitTransform OTransform msTransform".split(" "); //MozTransform <- firefox works slower with css!!!
94
for (var a=0;a<toCheck.length;a++) if (styles[toCheck[a]] !== undefined) supportedCSS = toCheck[a];
95
// Bad eval to preven google closure to remove it from code o_O
96
// After compresion replace it back to var IE = 'v' == '\v'
97
var IE = eval('"v"=="\v"');
98

  
99
jQuery.fn.extend({
100
ImageRotate:function(parameters)
101
{
102
	// If this element is already a Wilq32.PhotoEffect object, skip creation
103
	if (this.Wilq32&&this.Wilq32.PhotoEffect) return;
104
	// parameters might be applied to many objects - so because we use them later - a fresh instance is needed 
105
	var paramClone = $.extend(true, {}, parameters); 
106
	return (new Wilq32.PhotoEffect(this.get(0),paramClone))._temp;
107
},
108
rotate:function(parameters)
109
{
110
	if (this.length===0||typeof parameters=="undefined") return;
111
	if (typeof parameters=="number") parameters={angle:parameters};
112
	var returned=[];
113
	for (var i=0,i0=this.length;i<i0;i++)
114
	{
115
	    var element=this.get(i);	
116
		if (typeof element.Wilq32 == "undefined") 
117
			returned.push($($(element).ImageRotate(parameters)));
118
		else 
119
		{
120
			element.Wilq32.PhotoEffect._rotate(parameters.angle);
121
		}
122
	}
123
	return returned;
124
},
125

  
126
rotateAnimation:function(parameters)
127
{
128
	if (this.length===0||typeof parameters=="undefined") return;
129
	if (typeof parameters=="number") parameters={animateAngle:parameters};
130
	var returned=[];
131
	for (var i=0,i0=this.length;i<i0;i++)
132
	{	
133
	    var element=this.get(i);
134
		if (typeof element.Wilq32 == "undefined") 
135
			returned.push($($(element).ImageRotate(parameters)));
136
		else 
137
		{
138
			element.Wilq32.PhotoEffect.rotateAnimation(parameters);
139
		}
140
	}
141
	return returned;
142
}
143

  
144
});
145

  
146
// Library agnostic interface
147

  
148
Wilq32=window.Wilq32||{};
149
Wilq32.PhotoEffect=(function(){
150
	function setupParameters(img,parameters){
151
		this._img = img;
152
		this._parameters = parameters || {};
153
		this._parameters.angle = this._angle = parameters.angle || 0;
154
		this._parameters.animateAngle = typeof parameters.animateAngle=="number" ? parameters.animateAngle : this._angle;
155
		this._parameters.easing = parameters.easing || function (x, t, b, c, d) { return -c * ((t=t/d-1)*t*t*t - 1) + b; }
156
		this._parameters.duration = parameters.duration || 1000;
157
	}
158
	if (supportedCSS) {
159
		return function(img,parameters){
160
			setupParameters.call(this,img,parameters);
161
			img.Wilq32 = {
162
				PhotoEffect: this
163
			};
164
			// TODO: needed to have a _temp variable accessible outside - used for object retrieval, 
165
			//        needs refactor + change name (temp is not self descriptive)
166
			// also need better passing values between functions - to FIX (remove _temp and _img at all)
167
			this._temp = this._img;
168
			this._BindEvents(img,this._parameters.bind);
169
			this._rotate(this._parameters.angle);
170
			if (this._parameters.angle!=this._parameters.animateAngle) this.rotateAnimation(this._parameters);
171
		}
172
	} else {
173
		return function(img,parameters) {
174
			setupParameters.call(this,img,parameters);			
175
			// Make sure that class and id are also copied - just in case you would like to refeer to an newly created object
176
			this._parameters.className=img.className;
177
			this._parameters.id=img.getAttribute('id');
178

  
179
			this._temp=document.createElement('span');
180
			this._temp.style.display="inline-block";
181
			this._temp.Wilq32 = 
182
				{
183
					PhotoEffect: this
184
				};
185
			img.parentNode.insertBefore(this._temp,img);
186
			
187
			if (img.complete) {
188
				this._Loader();
189
			} else {
190
				var self=this;
191
				// TODO: Remove jQuery dependency
192
				jQuery(this._img).bind("load", function()
193
				{
194
					self._Loader();
195
				});
196
			}
197
		}
198
	}
199
})();
200

  
201
Wilq32.PhotoEffect.prototype={
202

  
203
	rotateAnimation : function(parameters){
204
		this._parameters.animateAngle = parameters.animateAngle;
205
		this._parameters.callback = parameters.callback || this._parameters.callback || function(){};	
206
		this._animateStart();
207
	},
208

  
209
	_BindEvents:function(element,events){
210
		if (events) 
211
		{
212
			for (var a in events) if (events.hasOwnProperty(a)) 
213
				for (var b in events[a]) if (events[a].hasOwnProperty(b)) 
214
				// TODO: Remove jQuery dependency
215
					jQuery(element).bind(b,events[a][b]);
216
		}
217
	},
218

  
219
	_Loader:(function()
220
	{
221
		if (IE)
222
		return function()
223
		{
224
			var width=this._img.width;
225
			var height=this._img.height;
226
			this._img.parentNode.removeChild(this._img);
227
							
228
			this._vimage = this.createVMLNode('image');
229
			this._vimage.src=this._img.src;
230
			this._vimage.style.height=height+"px";
231
			this._vimage.style.width=width+"px";
232
			this._vimage.style.position="absolute"; // FIXES IE PROBLEM - its only rendered if its on absolute position!
233
			this._vimage.style.top = "0px";
234
			this._vimage.style.left = "0px";
235

  
236
			/* Group minifying a small 1px precision problem when rotating object */
237
			this._container =  this.createVMLNode('group');
238
			this._container.style.width=width;
239
			this._container.style.height=height;
240
			this._container.style.position="absolute";
241
			this._container.setAttribute('coordsize',width-1+','+(height-1)); // This -1, -1 trying to fix that ugly problem
242
			this._container.appendChild(this._vimage);
243
			
244
			this._temp.appendChild(this._container);
245
			this._temp.style.position="relative"; // FIXES IE PROBLEM
246
			this._temp.style.width=width+"px";
247
			this._temp.style.height=height+"px";
248
			this._temp.setAttribute('id',this._parameters.id);
249
			this._temp.className=this._parameters.className;			
250
			
251
			this._BindEvents(this._temp,this._parameters.bind);
252
			_finally.call(this);
253
			
254
		}
255
		else
256
		return function ()
257
		{
258
			this._temp.setAttribute('id',this._parameters.id);
259
			this._temp.className=this._parameters.className;
260
			
261
			this._width=this._img.width;
262
			this._height=this._img.height;
263
			this._widthHalf=this._width/2; // used for optimisation
264
			this._heightHalf=this._height/2;// used for optimisation
265
			
266
			var _widthMax=Math.sqrt((this._height)*(this._height) + (this._width) * (this._width));
267

  
268
			this._widthAdd = _widthMax - this._width;
269
			this._heightAdd = _widthMax - this._height;	// widthMax because maxWidth=maxHeight
270
			this._widthAddHalf=this._widthAdd/2; // used for optimisation
271
			this._heightAddHalf=this._heightAdd/2;// used for optimisation
272
			
273
			this._img.parentNode.removeChild(this._img);	
274
			
275
			this._aspectW = ((parseInt(this._img.style.width,10)) || this._width)/this._img.width;
276
			this._aspectH = ((parseInt(this._img.style.height,10)) || this._height)/this._img.height;
277
			
278
			this._canvas=document.createElement('canvas');
279
			this._canvas.setAttribute('width',this._width);
280
			this._canvas.style.position="relative";
281
			this._canvas.style.left = -this._widthAddHalf + "px";
282
			this._canvas.style.top = -this._heightAddHalf + "px";
283
			this._canvas.Wilq32 = this._temp.Wilq32;
284
			
285
			this._temp.appendChild(this._canvas);
286
			this._temp.style.width=this._width+"px";
287
			this._temp.style.height=this._height+"px";
288
			
289
			this._BindEvents(this._canvas,this._parameters.bind);
290
			this._cnv=this._canvas.getContext('2d');
291
			_finally.call(this);
292
		}
293
		function _finally(){
294
			this._rotate(this._parameters.angle);
295
			if (this._parameters.angle!=this._parameters.animateAngle) this.rotateAnimation(this._parameters);		
296
		}
297

  
298
	})(),
299

  
300
	_animateStart:function()
301
	{	
302
		if (this._timer) {
303
			clearTimeout(this._timer);
304
		}
305
		this._animateStartTime = +new Date;
306
		this._animateStartAngle = this._angle;
307
		this._animate();
308
	},
309
	_animate:function()
310
	{
311
		var actualTime = +new Date;
312
		//var checkEnd = !!(Math.round(this._angle * 100 - this._parameters.animateAngle * 100)) == 0 && !!this._timer;
313
		var checkEnd = actualTime - this._animateStartTime > this._parameters.duration;
314
		if (this._parameters.callback && checkEnd){
315
			this._parameters.callback();
316
		}
317

  
318
		// TODO: Bug for animatedGif for static rotation ? (to test)
319
		if (checkEnd && !this._parameters.animatedGif) 
320
			{
321
				clearTimeout(this._timer);
322
			}
323
			else 
324
			{
325
				if (this._canvas||this._vimage||this._img) {
326
					// TODO: implement easing and speed of animation
327
					this._angle = this._parameters.easing(0, actualTime - this._animateStartTime, this._animateStartAngle, this._parameters.animateAngle - this._animateStartAngle, this._parameters.duration);
328
					//this._angle-=(this._angle-this._parameters.animateAngle)*0.1;
329
					//if (typeof this._parameters.minAngle!="undefined") this._angle=Math.max(this._angle,this._parameters.minAngle);
330
					//if (typeof this._parameters.maxAngle!="undefined") this._angle=Math.min(this._angle,this._parameters.maxAngle);
331
					this._rotate((~~(this._angle*10))/10);
332
				}
333
				var self = this;
334
				this._timer = setTimeout(function()
335
				{
336
					self._animate.call(self);
337
				}, 10);
338
			}
339
	},
340

  
341
	_rotate : (function()
342
	{
343
		var rad = Math.PI/180;
344
		if (IE)
345
		return function(angle)
346
		{
347
			this._container.style.rotation=angle+"deg";
348
		}
349
		else if (supportedCSS)
350
		return function(angle){
351
			this._img.style[supportedCSS]="rotate("+angle+"deg)";
352
		}
353
		else 
354
		return function(angle)
355
		{
356

  
357
			if (!this._img.width||typeof angle!="number") return;
358
			angle=(angle%360)* rad;
359
			// clear canvas	
360
			this._canvas.width = this._width+this._widthAdd;
361
			this._canvas.height = this._height+this._heightAdd;
362
						
363
			// REMEMBER: all drawings are read from backwards.. so first function is translate, then rotate, then translate, translate..
364
			this._cnv.translate(this._widthAddHalf,this._heightAddHalf);	// at least center image on screen
365
			this._cnv.translate(this._widthHalf,this._heightHalf);			// we move image back to its orginal 
366
			this._cnv.rotate(angle);										// rotate image
367
			this._cnv.translate(-this._widthHalf,-this._heightHalf);		// move image to its center, so we can rotate around its center
368
			this._cnv.scale(this._aspectW,this._aspectH); // SCALE - if needed ;)
369
			this._cnv.drawImage(this._img, 0, 0);							// First - we draw image
370
		}
371

  
372
	})()
373
}
374

  
375
if (IE)
376
{
377
Wilq32.PhotoEffect.prototype.createVMLNode=(function(){
378
document.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)");
379
		try {
380
			!document.namespaces.rvml && document.namespaces.add("rvml", "urn:schemas-microsoft-com:vml");
381
			return function (tagName) {
382
				return document.createElement('<rvml:' + tagName + ' class="rvml">');
383
			};
384
		} catch (e) {
385
			return function (tagName) {
386
				return document.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');
387
			};
388
		}		
389
})();
390
}
391

  
392
})(jQuery);
b/ui/static/main.css
935 935
    background-color: #87AADE;
936 936
}
937 937

  
938
.more-tabs {
939
    float: right;
940
    margin: -15px 50px 0px 0px;
941
}
942

  
943
.more-tabs:hover {
944
    cursor: pointer;
945
}
946

  
b/ui/templates/home.html
4 4
	<title>{{ project }}</title>
5 5
	<!-- include the Tools -->
6 6
	<script src="static/jquery.tools.min.js"></script>
7
    <script src="static/jQueryRotate.js"></script>
7 8
	<link rel="stylesheet" type="text/css" href="static/main.css"/>	
8 9
    <link href='http://fonts.googleapis.com/css?family=PT+Sans' rel='stylesheet' type='text/css' />
9 10

  
......
26 27
        </div>
27 28
        <!-- tabs -->
28 29
        <ul class="css-tabs">
29
	        <li><a href="machines" title="{% trans "manage your virtual machines" %}" id="machines">{% trans "machines" %}</a></li>
30
	        <li><a href="machines" title="{% trans "manage your virtual machines" %}" id="machines">{% trans "machines" %}
31
                </a></li>
30 32
	        <li><a href="disks" title="{% trans "manage your storage volumes" %}" id="disks">{% trans "disks" %}</a></li>
31 33
	        <li><a href="images" title="{% trans "manage boot images" %}" id="images">{% trans "images" %}</a></li>
32 34
	        <li><a href="networks" title="{% trans "configure networking" %}" id="networks">{% trans "networks" %}</a></li>
......
34 36
	        <li><a href="desktops" title="{% trans "your desktops" %}" id="desktops">{% trans "desktops" %}</a></li>
35 37
	        <li><a href="apps" title="{% trans "your apps" %}" id="apps">{% trans "apps" %}</a></li>
36 38
        </ul>
39
        <div class="more-tabs"><img src="static/arrow.png" id="arrow"></img></div>
37 40
        <div class="css-panes">
38 41
	        <div id="machines-pane" class="pane" style="display:block">{% include "machines.html" %}</div>
39 42
	        <div id="disks-pane" class="pane"></div>
......
66 69
		        }
67 70
	        });
68 71
        });
72
        // what should work
73
        /*
74
        $("#arrow").click(function(event){
75
            $(this).addClass("clicked");
76
            $(this).rotateAnimation(90);
77
        });
78

  
79
        $(".clicked").click(function(event){
80
            $(this).removeClass("clicked");
81
            $(this).rotateAnimation(0);
82
        });
83
        */
84
        
85
        // what is working
86
        var counter = 1;
87
        var angle;
88

  
89
        $("#arrow").click(function(event){
90
            $(this).rotate({animateAngle: (90), bind:[{
91
                "click":function(){
92
                    if (counter % 2 == 0)
93
                        $(this).rotateAnimation(90);
94
                    else
95
                       $(this).rotateAnimation(0); 
96
                    counter += 1;
97
                }}]
98
            });
99
        });
69 100
    </script>
70 101
</body>
71 102
</html>

Also available in: Unified diff