1/** 2 * @license 3 * jQuery Tools @VERSION Scrollable - New wave UI design 4 * 5 * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE. 6 * 7 * http://flowplayer.org/tools/scrollable.html 8 * 9 * Since: March 2008 10 * Date: @DATE 11 */ 12(function($) { 13 14 // static constructs 15 $.tools = $.tools || {version: '@VERSION'}; 16 17 $.tools.scrollable = { 18 19 conf: { 20 activeClass: 'active', 21 circular: false, 22 clonedClass: 'cloned', 23 disabledClass: 'disabled', 24 easing: 'swing', 25 initialIndex: 0, 26 item: '> *', 27 items: '.items', 28 keyboard: true, 29 mousewheel: false, 30 next: '.next', 31 prev: '.prev', 32 size: 1, 33 speed: 400, 34 vertical: false, 35 touch: true, 36 wheelSpeed: 0 37 } 38 }; 39 40 // get hidden element's width or height even though it's hidden 41 function dim(el, key) { 42 var v = parseInt(el.css(key), 10); 43 if (v) { return v; } 44 var s = el[0].currentStyle; 45 return s && s.width && parseInt(s.width, 10); 46 } 47 48 function find(root, query) { 49 var el = $(query); 50 return el.length < 2 ? el : root.parent().find(query); 51 } 52 53 var current; 54 55 // constructor 56 function Scrollable(root, conf) { 57 58 // current instance 59 var self = this, 60 fire = root.add(self), 61 itemWrap = root.children(), 62 index = 0, 63 vertical = conf.vertical; 64 65 if (!current) { current = self; } 66 if (itemWrap.length > 1) { itemWrap = $(conf.items, root); } 67 68 69 // in this version circular not supported when size > 1 70 if (conf.size > 1) { conf.circular = false; } 71 72 // methods 73 $.extend(self, { 74 75 getConf: function() { 76 return conf; 77 }, 78 79 getIndex: function() { 80 return index; 81 }, 82 83 getSize: function() { 84 return self.getItems().size(); 85 }, 86 87 getNaviButtons: function() { 88 return prev.add(next); 89 }, 90 91 getRoot: function() { 92 return root; 93 }, 94 95 getItemWrap: function() { 96 return itemWrap; 97 }, 98 99 getItems: function() { 100 return itemWrap.find(conf.item).not("." + conf.clonedClass); 101 }, 102 103 move: function(offset, time) { 104 return self.seekTo(index + offset, time); 105 }, 106 107 next: function(time) { 108 return self.move(conf.size, time); 109 }, 110 111 prev: function(time) { 112 return self.move(-conf.size, time); 113 }, 114 115 begin: function(time) { 116 return self.seekTo(0, time); 117 }, 118 119 end: function(time) { 120 return self.seekTo(self.getSize() -1, time); 121 }, 122 123 focus: function() { 124 current = self; 125 return self; 126 }, 127 128 addItem: function(item) { 129 item = $(item); 130 131 if (!conf.circular) { 132 itemWrap.append(item); 133 next.removeClass("disabled"); 134 135 } else { 136 itemWrap.children().last().before(item); 137 itemWrap.children().first().replaceWith(item.clone().addClass(conf.clonedClass)); 138 } 139 140 fire.trigger("onAddItem", [item]); 141 return self; 142 }, 143 144 145 /* all seeking functions depend on this */ 146 seekTo: function(i, time, fn) { 147 148 // ensure numeric index 149 if (!i.jquery) { i *= 1; } 150 151 // avoid seeking from end clone to the beginning 152 if (conf.circular && i === 0 && index == -1 && time !== 0) { return self; } 153 154 // check that index is sane 155 if (!conf.circular && i < 0 || i > self.getSize() || i < -1) { return self; } 156 157 var item = i; 158 159 if (i.jquery) { 160 i = self.getItems().index(i); 161 162 } else { 163 item = self.getItems().eq(i); 164 } 165 166 // onBeforeSeek 167 var e = $.Event("onBeforeSeek"); 168 if (!fn) { 169 fire.trigger(e, [i, time]); 170 if (e.isDefaultPrevented() || !item.length) { return self; } 171 } 172 173 var props = vertical ? {top: -item.position().top} : {left: -item.position().left}; 174 175 index = i; 176 current = self; 177 if (time === undefined) { time = conf.speed; } 178 179 itemWrap.animate(props, time, conf.easing, fn || function() { 180 fire.trigger("onSeek", [i]); 181 }); 182 183 return self; 184 } 185 186 }); 187 188 // callbacks 189 $.each(['onBeforeSeek', 'onSeek', 'onAddItem'], function(i, name) { 190 191 // configuration 192 if ($.isFunction(conf[name])) { 193 $(self).on(name, conf[name]); 194 } 195
196 self[name] = function(fn) { 197 if (fn) { $(self).on(name, fn); } 198 return self; 199 }; 200 }); 201 202 // circular loop 203 if (conf.circular) { 204 205 var cloned1 = self.getItems().slice(-1).clone().prependTo(itemWrap), 206 cloned2 = self.getItems().eq(1).clone().appendTo(itemWrap); 207 208 cloned1.add(cloned2).addClass(conf.clonedClass); 209 210 self.onBeforeSeek(function(e, i, time) { 211 212 if (e.isDefaultPrevented()) { return; } 213 214 /* 215 1. animate to the clone without event triggering 216 2. seek to correct position with 0 speed 217 */ 218 if (i == -1) { 219 self.seekTo(cloned1, time, function() { 220 self.end(0); 221 }); 222 return e.preventDefault(); 223 224 } else if (i == self.getSize()) { 225 self.seekTo(cloned2, time, function() { 226 self.begin(0); 227 }); 228 } 229 230 }); 231 232 // seek over the cloned item 233 234 // if the scrollable is hidden the calculations for seekTo position 235 // will be incorrect (eg, if the scrollable is inside an overlay). 236 // ensure the elements are shown, calculate the correct position, 237 // then re-hide the elements. This must be done synchronously to 238 // prevent the hidden elements being shown to the user. 239 240 // See: https://github.com/jquerytools/jquerytools/issues#issue/87 241 242 var hidden_parents = root.parents().add(root).filter(function () { 243 if ($(this).css('display') === 'none') { 244 return true; 245 } 246 }); 247 if (hidden_parents.length) { 248 hidden_parents.show(); 249 self.seekTo(0, 0, function() {}); 250 hidden_parents.hide(); 251 } 252 else { 253 self.seekTo(0, 0, function() {}); 254 } 255 256 } 257 258 // next/prev buttons 259 var prev = find(root, conf.prev).click(function(e) { e.stopPropagation(); self.prev(); }), 260 next = find(root, conf.next).click(function(e) { e.stopPropagation(); self.next(); }); 261 262 if (!conf.circular) { 263 self.onBeforeSeek(function(e, i) { 264 setTimeout(function() { 265 if (!e.isDefaultPrevented()) { 266 prev.toggleClass(conf.disabledClass, i <= 0); 267 next.toggleClass(conf.disabledClass, i >= self.getSize() -1); 268 } 269 }, 1); 270 }); 271 272 if (!conf.initialIndex) { 273 prev.addClass(conf.disabledClass); 274 } 275 } 276 277 if (self.getSize() < 2) { 278 prev.add(next).addClass(conf.disabledClass); 279 } 280 281 // mousewheel support 282 if (conf.mousewheel && $.fn.mousewheel) { 283 root.mousewheel(function(e, delta) { 284 if (conf.mousewheel) { 285 self.move(delta < 0 ? 1 : -1, conf.wheelSpeed || 50); 286 return false; 287 } 288 }); 289 } 290 291 // touch event 292 if (conf.touch) { 293 var touch = {}; 294 295 itemWrap[0].ontouchstart = function(e) { 296 var t = e.touches[0]; 297 touch.x = t.clientX; 298 touch.y = t.clientY; 299 }; 300 301 itemWrap[0].ontouchmove = function(e) { 302 303 // only deal with one finger 304 if (e.touches.length == 1 && !itemWrap.is(":animated")) { 305 var t = e.touches[0], 306 deltaX = touch.x - t.clientX, 307 deltaY = touch.y - t.clientY; 308 309 self[vertical && deltaY > 0 || !vertical && deltaX > 0 ? 'next' : 'prev'](); 310 e.preventDefault(); 311 } 312 }; 313 } 314 315 if (conf.keyboard) { 316 317 $(document).on("keydown.scrollable", function(evt) { 318 319 // skip certain conditions 320 if (!conf.keyboard || evt.altKey || evt.ctrlKey || evt.metaKey || $(evt.target).is(":input")) { 321 return; 322 } 323 324 // does this instance have focus? 325 if (conf.keyboard != 'static' && current != self) { return; } 326 327 var key = evt.keyCode; 328 329 if (vertical && (key == 38 || key == 40)) { 330 self.move(key == 38 ? -1 : 1); 331 return evt.preventDefault(); 332 } 333 334 if (!vertical && (key == 37 || key == 39)) { 335 self.move(key == 37 ? -1 : 1); 336 return evt.preventDefault(); 337 } 338 339 }); 340 } 341 342 // initial index 343 if (conf.initialIndex) { 344 self.seekTo(conf.initialIndex, 0, function() {}); 345 } 346 } 347 348 349 // jQuery plugin implementation 350 $.fn.scrollable = function(conf) { 351 352 // already constructed --> return API 353 var el = this.data("scrollable"); 354 if (el) { return el; } 355 356 conf = $.extend({}, $.tools.scrollable.conf, conf); 357 358 this.each(function() { 359 el = new Scrollable($(this), conf); 360 $(this).data("scrollable", el); 361 }); 362 363 return conf.api ? el: this; 364 365 }; 366 367 368})(jQuery);
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.