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 * 0.3 2008-10-22 Added option to repeat the animation a number of times, then stop 20 * 0.3.1 2008-11-11 Better error messages 21 * 0.3.2 2008-11-11 Fixed a couple of CSS bugs, contributed by Erwin Bot <[email protected]> 22 */ 23 24jQuery.fn.crossSlide = function(opts, plan) { 25 var self = this, 26 self_width = this.width(), 27 self_height = this.height(); 28 29 // generic utilities 30 function format(str) { 31 for (var i = 1; i < arguments.length; i++) 32 str = str.replace(new RegExp('\\{' + (i-1) + '}', 'g'), arguments[i]); 33 return str; 34 } 35 36 function abort() { 37 arguments[0] = 'crossSlide: ' + arguments[0]; 38 throw format.apply(null, arguments); 39 } 40 41 // first preload all the images, while getting their actual width and height 42 (function(proceed) { 43 44 var n_loaded = 0; 45 function loop(i, img) { 46 // for (i = 0; i < plan.length; i++) but with independent var i, img (for the closures) 47 img.onload = function(e) { 48 n_loaded++; 49 plan[i].width = img.width; 50 plan[i].height = img.height; 51 if (n_loaded == plan.length) 52 proceed(); 53 } 54 img.src = plan[i].src; 55 if (i + 1 < plan.length) 56 loop(i + 1, new Image()); 57 } 58 loop(0, new Image()); 59 60 })(function() { // then proceed 61 62 // utility to parse "from" and "to" parameters 63 function parse_position_param(param) { 64 var zoom = 1; 65 var tokens = param.replace(/^\s*|\s*$/g, '').split(/\s+/); 66 if (tokens.length > 3) throw new Error(); 67 if (tokens[0] == 'center') 68 if (tokens.length == 1) 69 tokens = ['center', 'center']; 70 else if (tokens.length == 2 && tokens[1].match(/^[\d.]+x$/i)) 71 tokens = ['center', 'center', tokens[1]]; 72 if (tokens.length == 3) 73 zoom = parseFloat(tokens[2].match(/^([\d.]+)x$/i)[1]); 74 var pos = tokens[0] + ' ' + tokens[1]; 75 if (pos == 'left top' || pos == 'top left') return { xrel: 0, yrel: 0, zoom: zoom }; 76 if (pos == 'left center' || pos == 'center left') return { xrel: 0, yrel: .5, zoom: zoom }; 77 if (pos == 'left bottom' || pos == 'bottom left') return { xrel: 0, yrel: 1, zoom: zoom }; 78 if (pos == 'center top' || pos == 'top center') return { xrel: .5, yrel: 0, zoom: zoom }; 79 if (pos == 'center center') return { xrel: .5, yrel: .5, zoom: zoom }; 80 if (pos == 'center bottom' || pos == 'bottom center') return { xrel: .5, yrel: 1, zoom: zoom }; 81 if (pos == 'right top' || pos == 'top right') return { xrel: 1, yrel: 0, zoom: zoom }; 82 if (pos == 'right center' || pos == 'center right') return { xrel: 1, yrel: .5, zoom: zoom }; 83 if (pos == 'right bottom' || pos == 'bottom right') return { xrel: 1, yrel: 1, zoom: zoom }; 84 return { 85 xrel: parseInt(tokens[0].match(/^(\d+)%$/)[1]) / 100, 86 yrel: parseInt(tokens[1].match(/^(\d+)%$/)[1]) / 100, 87 zoom: zoom 88 }; 89 } 90 91 // utility to compute the css for a given phase between p.from and p.to 92 // phase = 1: begin fade-in, 2: end fade-in, 3: begin fade-out, 4: end fade-out 93 function position_to_css(p, phase) { 94 switch (phase) { 95 case 1: 96 var pos = 0; 97 break; 98 case 2: 99 var pos = fade_ms / (p.time_ms + 2 * fade_ms); 100 break; 101 case 3: 102 var pos = 1 - fade_ms / (p.time_ms + 2 * fade_ms); 103 break; 104 case 4: 105 var pos = 1; 106 break; 107 } 108 return { 109 left: Math.round(p.from.left + pos * (p.to.left - p.from.left )), 110 top: Math.round(p.from.top + pos * (p.to.top - p.from.top )), 111 width: Math.round(p.from.width + pos * (p.to.width - p.from.width )), 112 height: Math.round(p.from.height + pos * (p.to.height - p.from.height)) 113 }; 114 } 115 116 // check global params 117 if (! opts.fade) 118 abort('missing fade parameter.'); 119 if (opts.speed && opts.sleep)
120 abort('you cannot set both speed and sleep at the same time.'); 121 // conversion from sec to ms; from px/sec to px/ms 122 var fade_ms = Math.round(opts.fade * 1000); 123 if (opts.sleep) 124 var sleep = Math.round(opts.sleep * 1000); 125 if (opts.speed) 126 var speed = opts.speed / 1000, 127 fade_px = Math.round(fade_ms * speed); 128 129 // set container css 130 self.empty().css({ 131 overflow: 'hidden', 132 padding: 0 133 }); 134 if (! self.css('position').match(/absolute|relative|fixed/)) 135 self.css({ position: 'relative' }); 136 if (! self.width() || ! self.height()) 137 abort('container element does not have its own width and height'); 138 139 // prepare each image 140 for (var i = 0; i < plan.length; ++i) { 141 142 var p = plan[i]; 143 if (! p.src) 144 abort('missing src parameter in picture {0}.', i + 1); 145 146 if (speed) { // speed/dir mode 147 148 // check parameters and translate speed/dir mode into full mode (from/to/time) 149 switch (p.dir) { 150 case 'up': 151 p.from = { xrel: .5, yrel: 0, zoom: 1 }; 152 p.to = { xrel: .5, yrel: 1, zoom: 1 }; 153 var slide_px = p.height - self_height - 2 * fade_px; 154 break; 155 case 'down': 156 p.from = { xrel: .5, yrel: 1, zoom: 1 }; 157 p.to = { xrel: .5, yrel: 0, zoom: 1 }; 158 var slide_px = p.height - self_height - 2 * fade_px; 159 break; 160 case 'left': 161 p.from = { xrel: 0, yrel: .5, zoom: 1 }; 162 p.to = { xrel: 1, yrel: .5, zoom: 1 }; 163 var slide_px = p.width - self_width - 2 * fade_px; 164 break; 165 case 'right': 166 p.from = { xrel: 1, yrel: .5, zoom: 1 }; 167 p.to = { xrel: 0, yrel: .5, zoom: 1 }; 168 var slide_px = p.width - self_width - 2 * fade_px; 169 break; 170 default: 171 abort('missing or malformed "dir" parameter in picture {0}.', i + 1); 172 } 173 if (slide_px <= 0) 174 abort('picture number {0} is too short for the desired fade duration.', i + 1); 175 p.time_ms = Math.round(slide_px / speed); 176 177 } else if (! sleep) { // full mode 178 179 // check and parse parameters 180 if (! p.from || ! p.to || ! p.time) 181 abort('missing either speed/sleep option, or from/to/time params in picture {0}.', i + 1); 182 try { 183 p.from = parse_position_param(p.from) 184 } catch (e) { 185 abort('malformed "from" parameter in picture {0}.', i + 1); 186 } 187 try { 188 p.to = parse_position_param(p.to) 189 } catch (e) { 190 abort('malformed "to" parameter in picture {0}.', i + 1); 191 } 192 if (! p.time) 193 abort('missing "time" parameter in picture {0}.', i + 1); 194 p.time_ms = Math.round(p.time * 1000) 195 } 196 197 // precalculate left/top/width/height bounding values 198 if (p.from) 199 jQuery.each([ p.from, p.to ], function(i, from_to) { 200 from_to.width = Math.round(p.width * from_to.zoom); 201 from_to.height = Math.round(p.height * from_to.zoom); 202 from_to.left = Math.round((self_width - from_to.width) * from_to.xrel); 203 from_to.top = Math.round((self_height - from_to.height) * from_to.yrel); 204 }); 205 206 // append the image element to the container 207 var html = p.href 208 ? format('<a href="{0}"><img src="{1}"/></a>', p.href, p.src) 209 : format('<img src="{0}"/>', p.src); 210 jQuery(html).appendTo(self).css({ 211 position: 'absolute', 212 visibility: 'hidden', 213 top: 0, 214 left: 0 215 }).find('img').css({ 216 border: 0 217 }) 218 } 219 speed = undefined; // speed mode has now been translated to full mode 220 221 var imgs = self.children(); 222 223 // show first image 224 imgs.eq(0).css({ visibility: 'visible' }); 225 if (! sleep) 226 imgs.eq(0).css(position_to_css(plan[0], 2)); 227 228 // create animation chain 229 var countdown = opts.loop; 230 function create_chain(i, chainf) { 231 // building the chain backwards, or inside out 232 233 if (i % 2 == 0) { 234 if (sleep) { 235 236 // still image sleep 237 238 var i_sleep = i / 2, 239 i_hide = (i_sleep - 1 + plan.length) % plan.length, 240 img_sleep = imgs.eq(i_sleep), 241 img_hide = imgs.eq(i_hide); 242 243 var newf = function() { 244 img_hide.css('visibility', 'hidden'); 245 setTimeout(chainf, sleep); 246 }; 247 248 } else { 249 250 // single image slide 251 252 var i_slide = i / 2, 253 i_hide = (i_slide - 1 + plan.length) % plan.length, 254 img_slide = imgs.eq(i_slide), 255 img_hide = imgs.eq(i_hide), 256 time = plan[i_slide].time_ms, 257 slide_anim = position_to_css(plan[i_slide], 3); 258 259 var newf = function() { 260 img_hide.css('visibility', 'hidden'); 261 img_slide.animate(slide_anim, time, 'linear', chainf); 262 }; 263 264 } 265 } else { 266 if (sleep) { 267 268 // still image cross-fade 269 270 var i_from = Math.floor(i / 2), 271 i_to = Math.ceil(i / 2) % plan.length, 272 img_from = imgs.eq(i_from), 273 img_to = imgs.eq(i_to), 274 from_anim = {}, 275 to_init = { visibility: 'visible' }, 276 to_anim = {}; 277 278 if (i_to > i_from) { 279 to_init.opacity = 0; 280 to_anim.opacity = 1; 281 } else { 282 from_anim.opacity = 0; 283 } 284 285 var newf = function() { 286 img_to.css(to_init); 287 if (from_anim.opacity != undefined) 288 img_from.animate(from_anim, fade_ms, 'linear', chainf); 289 else 290 img_to.animate(to_anim, fade_ms, 'linear', chainf); 291 }; 292 293 } else { 294 295 // cross-slide + cross-fade 296 297 var i_from = Math.floor(i / 2), 298 i_to = Math.ceil(i / 2) % plan.length, 299 img_from = imgs.eq(i_from), 300 img_to = imgs.eq(i_to), 301 from_anim = position_to_css(plan[i_from], 4), 302 to_init = position_to_css(plan[i_to], 1), 303 to_anim = position_to_css(plan[i_to], 2); 304 305 if (i_to > i_from) { 306 to_init.opacity = 0; 307 to_anim.opacity = 1; 308 } else { 309 from_anim.opacity = 0; 310 } 311 to_init.visibility = 'visible'; 312 313 var newf = function() { 314 img_from.animate(from_anim, fade_ms, 'linear'); 315 img_to.css(to_init); 316 img_to.animate(to_anim, fade_ms, 'linear', chainf); 317 }; 318 319 } 320 } 321 322 // if the loop option was requested, push a countdown check 323 if (opts.loop && i == plan.length * 2 - 2) { 324 var newf_orig = newf; 325 newf = function() { 326 if (--countdown) newf_orig(); 327 } 328 } 329 330 if (i > 0) 331 return create_chain(i - 1, newf); 332 else 333 return newf; 334 } 335 var animation = create_chain(plan.length * 2 - 1, function() { return animation(); }); 336 337 // start animation 338 animation(); 339 340 }); 341 342 return self; 343};
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.