1/* 2 * Copyright 2007-2008 by Tobia Conforto <[email protected]> 3 * 4 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General 5 * Public License as published by the Free Software Foundation; either version 2 of the License, or (at your 6 * option) any later version. 7 * 8 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 9 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 10 * for more details. 11 * 12 * You should have received a copy of the GNU General Public License along with this program; if not, write to 13 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 14 * 15 * Versions: 0.1 2007-08-19 Initial release 16 * 2008-08-21 Re-released under GPL v2 17 * 0.1.1 2008-09-18 Compatibility with prototype.js 18 * 0.2 2008-10-15 Linkable images, contributed by Tim Rainey <[email protected]> 19 */ 20 21jQuery.fn.crossSlide = function(opts, plan) { 22 var self = this, 23 self_width = this.width(), 24 self_height = this.height(); 25 26 // generic utilities 27 function format(str) { 28 for (var i = 1; i < arguments.length; i++) 29 str = str.replace(new RegExp('\\{' + (i-1) + '}', 'g'), arguments[i]); 30 return str; 31 } 32 33 function dump_obj(o) { 34 var s = '{ '; 35 for (var n in o) 36 s += n + ': ' + o[n] + ', '; 37 return (s == '{ ' ? '{}' : s.slice(0, -2) + ' }'); 38 } 39 40 // first preload all the images, while getting their actual width and height 41 (function(proceed) { 42 43 var n_loaded = 0; 44 function loop(i, img) { 45 // for (i = 0; i < plan.length; i++) but with independent var i, img (for the closures) 46 img.onload = function(e) { 47 n_loaded++; 48 plan[i].width = img.width; 49 plan[i].height = img.height; 50 if (n_loaded == plan.length) 51 proceed(); 52 } 53 img.src = plan[i].src; 54 if (i + 1 < plan.length) 55 loop(i + 1, new Image()); 56 } 57 loop(0, new Image()); 58 59 })(function() { // then proceed 60 61 // utility to parse "from" and "to" parameters 62 function parse_position_param(param) { 63 var zoom = 1; 64 var tokens = param.replace(/^\s*|\s*$/g, '').split(/\s+/); 65 if (tokens.length > 3) throw new Error(); 66 if (tokens[0] == 'center') 67 if (tokens.length == 1) 68 tokens = ['center', 'center']; 69 else if (tokens.length == 2 && tokens[1].match(/^[\d.]+x$/i)) 70 tokens = ['center', 'center', tokens[1]]; 71 if (tokens.length == 3) 72 zoom = parseFloat(tokens[2].match(/^([\d.]+)x$/i)[1]); 73 var pos = tokens[0] + ' ' + tokens[1]; 74 if (pos == 'left top' || pos == 'top left') return { xrel: 0, yrel: 0, zoom: zoom }; 75 if (pos == 'left center' || pos == 'center left') return { xrel: 0, yrel: .5, zoom: zoom }; 76 if (pos == 'left bottom' || pos == 'bottom left') return { xrel: 0, yrel: 1, zoom: zoom }; 77 if (pos == 'center top' || pos == 'top center') return { xrel: .5, yrel: 0, zoom: zoom }; 78 if (pos == 'center center') return { xrel: .5, yrel: .5, zoom: zoom }; 79 if (pos == 'center bottom' || pos == 'bottom center') return { xrel: .5, yrel: 1, zoom: zoom }; 80 if (pos == 'right top' || pos == 'top right') return { xrel: 1, yrel: 0, zoom: zoom }; 81 if (pos == 'right center' || pos == 'center right') return { xrel: 1, yrel: .5, zoom: zoom }; 82 if (pos == 'right bottom' || pos == 'bottom right') return { xrel: 1, yrel: 1, zoom: zoom }; 83 return { 84 xrel: parseInt(tokens[0].match(/^(\d+)%$/)[1]) / 100, 85 yrel: parseInt(tokens[1].match(/^(\d+)%$/)[1]) / 100, 86 zoom: zoom 87 }; 88 } 89 90 // utility to compute the css for a given phase between p.from and p.to 91 // phase = 1: begin fade-in, 2: end fade-in, 3: begin fade-out, 4: end fade-out 92 function position_to_css(p, phase) { 93 switch (phase) { 94 case 1: 95 var pos = 0; 96 break; 97 case 2: 98 var pos = fade_ms / (p.time_ms + 2 * fade_ms); 99 break; 100 case 3: 101 var pos = 1 - fade_ms / (p.time_ms + 2 * fade_ms); 102 break; 103 case 4: 104 var pos = 1; 105 break; 106 } 107 return { 108 left: Math.round(p.from.left + pos * (p.to.left - p.from.left )), 109 top: Math.round(p.from.top + pos * (p.to.top - p.from.top )), 110 width: Math.round(p.from.width + pos * (p.to.width - p.from.width )), 111 height: Math.round(p.from.height + pos * (p.to.height - p.from.height)) 112 }; 113 } 114 115 // check global params 116 if (! opts.fade) 117 throw 'Missing fade parameter.'; 118 if (opts.speed && opts.sleep)
119 throw 'You cannot set both speed and sleep at the same time.'; 120 // conversion from sec to ms; from px/sec to px/ms 121 var fade_ms = Math.round(opts.fade * 1000); 122 if (opts.sleep) 123 var sleep = Math.round(opts.sleep * 1000); 124 if (opts.speed) 125 var speed = opts.speed / 1000, 126 fade_px = Math.round(fade_ms * speed); 127 128 // a debug: true option may be added to get a dump of the generated JavaScript code 129 if (opts.debug) 130 var debug = jQuery('<pre><hr/></pre>'); 131 132 // set container css 133 self.empty().css({ 134 overflow: 'hidden', 135 padding: 0 136 }); 137 if (! self.css('position').match(/absolute|relative|fixed/)) 138 self.css({ position: 'relative' }) 139 140 // prepare each image 141 for (var i = 0; i < plan.length; ++i) { 142 143 var p = plan[i]; 144 if (! p.src) 145 throw format('Missing src parameter in picture {0}.', i + 1); 146 147 if (speed) { // speed/dir mode 148 149 // check parameters and translate speed/dir mode into full mode (from/to/time) 150 switch (p.dir) { 151 case 'up': 152 p.from = { xrel: .5, yrel: 0, zoom: 1 }; 153 p.to = { xrel: .5, yrel: 1, zoom: 1 }; 154 var slide_px = p.height - self_height - 2 * fade_px; 155 break; 156 case 'down': 157 p.from = { xrel: .5, yrel: 1, zoom: 1 }; 158 p.to = { xrel: .5, yrel: 0, zoom: 1 }; 159 var slide_px = p.height - self_height - 2 * fade_px; 160 break; 161 case 'left': 162 p.from = { xrel: 0, yrel: .5, zoom: 1 }; 163 p.to = { xrel: 1, yrel: .5, zoom: 1 }; 164 var slide_px = p.width - self_width - 2 * fade_px; 165 break; 166 case 'right': 167 p.from = { xrel: 1, yrel: .5, zoom: 1 }; 168 p.to = { xrel: 0, yrel: .5, zoom: 1 }; 169 var slide_px = p.width - self_width - 2 * fade_px; 170 break; 171 default: 172 throw format('Missing or malformed "dir" parameter in picture {0}.', i + 1); 173 } 174 if (slide_px <= 0) 175 throw format('Picture number {0} is too short for the desired fade duration.', i + 1); 176 p.time_ms = Math.round(slide_px / speed); 177 178 } else if (! sleep) { // full mode 179 180 // check and parse parameters 181 if (! p.from || ! p.to || ! p.time) 182 throw format('Missing either speed/sleep option, or from/to/time params in picture {0}.', i + 1); 183 try { 184 p.from = parse_position_param(p.from) 185 } catch (e) { 186 throw format('Malformed "from" parameter in picture {0}.', i + 1); 187 } 188 try { 189 p.to = parse_position_param(p.to) 190 } catch (e) { 191 throw format('Malformed "to" parameter in picture {0}.', i + 1); 192 } 193 if (! p.time) 194 throw format('Missing "time" parameter in picture {0}.', i + 1); 195 p.time_ms = Math.round(p.time * 1000) 196 } 197 198 // precalculate left/top/width/height bounding values 199 if (p.from) 200 jQuery.each([ p.from, p.to ], function(i, from_to) { 201 from_to.width = Math.round(p.width * from_to.zoom); 202 from_to.height = Math.round(p.height * from_to.zoom); 203 from_to.left = Math.round((self_width - from_to.width) * from_to.xrel); 204 from_to.top = Math.round((self_height - from_to.height) * from_to.yrel); 205 }); 206 207 // append the image element to the container 208 var html = p.href 209 ? format('<a href="{0}"><img src="{1}"/></a>', p.href, p.src) 210 : format('<img src="{0}"/>', p.src); 211 jQuery(html).appendTo(self).css({ 212 position: 'absolute', 213 visibility: 'hidden' 214 }); 215 } 216 speed = undefined; // speed mode has now been translated to full mode 217 218 var imgs = self.children(); 219 220 // show first image 221 imgs.eq(0).css({ visibility: 'visible' }); 222 if (! sleep) 223 imgs.eq(0).css(position_to_css(plan[0], 2)); 224 225 // create animation chain 226 function create_chain(i, chainf) { 227 // building the chain backwards, or inside out 228 229 if (i % 2 == 0) { 230 if (sleep) { 231 232 // still image sleep 233 234 var i_sleep = i / 2, 235 i_hide = (i_sleep - 1 + plan.length) % plan.length, 236 img_sleep = imgs.eq(i_sleep), 237 img_hide = imgs.eq(i_hide); 238 239 var newf = function() { 240 img_hide.css('visibility', 'hidden'); 241 setTimeout(chainf, sleep); 242 }; 243 244 if (debug) 245 debug.prepend('<hr/>' 246 + format("img[{0}].css(visibility, hidden)\n", i_hide) 247 + format("setTimeout(▼, {0})", sleep)); 248 249 } else { 250 251 // single image slide 252 253 var i_slide = i / 2, 254 i_hide = (i_slide - 1 + plan.length) % plan.length, 255 img_slide = imgs.eq(i_slide), 256 img_hide = imgs.eq(i_hide), 257 time = plan[i_slide].time_ms, 258 slide_anim = position_to_css(plan[i_slide], 3); 259 260 var newf = function() { 261 img_hide.css('visibility', 'hidden'); 262 img_slide.animate(slide_anim, time, 'linear', chainf); 263 }; 264 265 if (debug) 266 debug.prepend('<hr/>' 267 + format("img[{0}].css(visibility, hidden)\n", i_hide) 268 + format("img[{0}].animate({1}, {2}, linear, ▼)", i_slide, dump_obj(slide_anim), time)); 269 270 } 271 } else { 272 if (sleep) { 273 274 // still image cross-fade 275 276 var i_from = Math.floor(i / 2), 277 i_to = Math.ceil(i / 2) % plan.length, 278 img_from = imgs.eq(i_from), 279 img_to = imgs.eq(i_to), 280 from_anim = {}, 281 to_init = { visibility: 'visible' }, 282 to_anim = {}; 283 284 if (i_to > i_from) { 285 to_init.opacity = 0; 286 to_anim.opacity = 1; 287 } else { 288 from_anim.opacity = 0; 289 } 290 291 var newf = function() { 292 img_to.css(to_init); 293 if (from_anim.opacity != undefined) 294 img_from.animate(from_anim, fade_ms, 'linear', chainf); 295 else 296 img_to.animate(to_anim, fade_ms, 'linear', chainf); 297 }; 298 299 if (debug) 300 debug.prepend('<hr/>' 301 + format("img[{0}].css({1}
301)\n", i_to, dump_obj(to_init)) 302 + (from_anim.opacity != undefined 303 ? format("img[{0}].animate({1}, {2}, linear, ▼)", i_from, dump_obj(from_anim), fade_ms) 304 : format("img[{0}].animate({1}, {2}, linear, ▼)", i_to, dump_obj(to_anim), fade_ms))); 305 306 } else { 307 308 // cross-slide + cross-fade 309 310 var i_from = Math.floor(i / 2), 311 i_to = Math.ceil(i / 2) % plan.length, 312 img_from = imgs.eq(i_from), 313 img_to = imgs.eq(i_to), 314 from_anim = position_to_css(plan[i_from], 4), 315 to_init = position_to_css(plan[i_to], 1), 316 to_anim = position_to_css(plan[i_to], 2); 317 318 if (i_to > i_from) { 319 to_init.opacity = 0; 320 to_anim.opacity = 1; 321 } else { 322 from_anim.opacity = 0; 323 } 324 to_init.visibility = 'visible'; 325 326 var newf = function() { 327 img_from.animate(from_anim, fade_ms, 'linear'); 328 img_to.css(to_init); 329 img_to.animate(to_anim, fade_ms, 'linear', chainf); 330 }; 331 332 if (debug) 333 debug.prepend('<hr/>' 334 + format("img[{0}].animate({1}, {2}, linear)\n", i_from, dump_obj(from_anim), fade_ms) 335 + format("img[{0}].css({1})\n", i_to, dump_obj(to_init)) 336 + format("img[{0}].animate({1}, {2}, linear, ▼)", i_to, dump_obj(to_anim), fade_ms)); 337 338 } 339 } 340 if (i > 0) 341 return create_chain(i - 1, newf); 342 else 343 return newf; 344 } 345 var animation = create_chain(plan.length * 2 - 1, function() { return animation(); }); 346 347 // show debug window, if enabled 348 if (debug) 349 jQuery(window.open('', 'debug', 'width=600,height=500,menubar=no,toolbar=no,directories=no,' 350 + 'location=no,status=no,scrollbars=yes,resizable=yes').document.body) 351 .empty() 352 .append(debug); 353 354 // start animation 355 animation(); 356 357 }); 358 359 return self; 360};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.