1/*! 2* Metro JS for jQuery 3* http://drewgreenwell.com/ 4* For details and usage info see: http://drewgreenwell.com/projects/metrojs 5 6Copyright (C) 2013, Drew Greenwell 7 8Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 9to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 10and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 12The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 13 14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 16WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17*/ 18;(function ($) {
19 // the metrojs object contains helper methods and theme settings 20 $.fn.metrojs = { 21 capabilities: null, 22 checkCapabilities: function(stgs, recheck){ 23 if($.fn.metrojs.capabilities == null || (typeof(recheck) != "undefined" && recheck == true)) 24 $.fn.metrojs.capabilities = new $.fn.metrojs.MetroModernizr(stgs); 25 return $.fn.metrojs.capabilities; 26 } 27 }; 28 var metrojs = $.fn.metrojs, 29 console = window.console; 30 if (typeof console !== "object") { 31 console = {}; 32 console.log = function() {}; 33 console.error=function() {}; 34 } 35 var throwError = typeof ($.error) === "function" ? $.error : console.error; 36var MAX_LOOP_COUNT = 99000; 37// .liveTile 38$.fn.liveTile = function (method) { 39 if (pubMethods[method]) { 40 var args = []; 41 for (var i = 1; i <= arguments.length; i++) { 42 args[i - 1] = arguments[i]; 43 } 44 return pubMethods[method].apply(this, args); 45 } else if (typeof method === 'object' || !method) { 46 return pubMethods.init.apply(this, arguments); 47 } else { 48 $.error('Method ' + method + ' does not exist on jQuery.liveTile'); 49 return null; 50 } 51}; 52 53 54$.fn.liveTile.contentModules = { 55 modules: [], 56 /* the default module layout 57 [ 58 defaultModules.imageSwap, 59 defaultModules.htmlSwap 60 ],*/ 61 addContentModule: function (moduleName, module) { 62 if (!(this.modules instanceof Array)) 63 this.modules = []; 64 this.modules.push(module); 65 }, 66 hasContentModule: function (moduleName) { 67 if (typeof (moduleName) === "undefined" || !(this.modules instanceof Array)) 68 return -1; 69 for (var i = 0; i < this.modules.length; i++) { 70 if (typeof (this.modules[i].moduleName) != "undefined" && this.modules[i].moduleName == moduleName) 71 return i; 72 } 73 return -1; 74 } 75}; 76 77// default option values for .liveTile 78$.fn.liveTile.defaults = { 79 mode: 'slide', // 'fade', 'slide', 'flip', 'flip-list', carousel 80 speed: 500, // how fast should animations be performed, in milliseconds 81 initDelay: -1, // how long to wait before the initial animation 82 delay: 5000, // how long to wait between animations 83 stops: "100%", // how much of the back tile should 'slide' reveal before starting a delay 84 stack: false, // should tiles in slide mode appear stacked (e.g Me tile) 85 direction: 'vertical', // which direction should animations be performed(horizontal | vertical) 86 animationDirection: 'forward', // the direction that carousel mode uses to determine which way to slide in tiles 87 tileSelector: '>div,>li,>p,>img,>a', // the selector used by carousel mode and flip-list to choose tile containers 88 tileFaceSelector: '>div,>li,>p,>img,>a',// the selector used to choose the front and back containers 89 ignoreDataAttributes: false, // should data attributes be ignored 90 click: null, // function ($tile, tdata) { return true; } 91 link: '', // a url to go to when clicked 92 newWindow: false, // should the link be opened in a new window 93 bounce: false, // should the tile shrink when tapped 94 bounceDirections: 'all', // which direction the tile will tile 'all', 'edges, 'corners' 95 bounceFollowsMove: true, // should a tile in bounce state tilt in the direction of the mouse as it moves 96 pauseOnHover: false, // should tile animations be paused on hover in and restarted on hover out 97 pauseOnHoverEvent: 'both', // pause is called on mouseover, mouseout, or both 98 playOnHover: false, // should "play" be called on hover 99 playOnHoverEvent: 'both', // play is called on mouseover, mouseout, or both 100 onHoverDelay: 0, // the amount of time to wait before the onHover event is fired 101 onHoverOutDelay: 200, // the amount of time in addition to the speed to wait before the onHoverOut event is fired 102 repeatCount: -1, // number of times to repeat the animation 103 appendBack: true, // appends the .last tile if one doesnt exist (slide and flip only) 104 alwaysTrigger: false, // should every item in a flip list trigger every time a delay passes 105 flipListOnHover: false, // should items in flip-list flip and stop when hovered 106 flipListOnHoverEvent: 'mouseout', // which event should be used to trigger the flip-list faces
107 noHAflipOpacity: '1', // the opacity level set for the backside of the flip animation on unaccelerated browsers 108 haTransFunc: 'ease', // the tranisiton-timing function to use in hardware accelerated mode 109 noHaTransFunc: 'linear', // the tranisiton-timing function to use in non hardware accelerated mode 110 currentIndex: 0, // what is the current stop index for slide mode or slide index for carousel mode 111 startNow: true, // should the tile immediately start or wait util play or restart has been called 112 useModernizr: (typeof (window.Modernizr) !== "undefined"), // checks to see if modernizer is already in use 113 useHardwareAccel: true, // should css animations, transitions and transforms be used when available 114 useTranslate: true, 115 faces: { 116 $front: null, // the jQuery element to use as the front face of the tile; this will bypass tileCssSelector 117 $back: null // the jQuery element to use as the back face of the tile; this will bypass tileCssSelector 118 }, 119 animationStarting: function (tileData, $front, $back) { 120 // returning false will cancel the animation 121 }, 122 animationComplete: function (tileData, $front, $back) { 123 }, 124 triggerDelay: function (idx) { // used by flip-list to decide how random the tile flipping should be 125 return Math.random() * 3000; 126 }, 127 swap: '', // which swap modules are active for this tile (image, html) 128 swapFront: '-', // override the available swap modules for the front face 129 swapBack: '-', // override the available swap modules for the back face 130 contentModules: [], 131 rebindMessage: "tile data is missing. Are you missing a call to rebind or destroy? You may also be able to avoid this error by calling stop or pause" 132}; 133// public methods that can be called via .liveTile(method name) 134var pubMethods = { 135 init: function (options) { 136 // Setup the public options for the livetile 137 var settings = $.extend({}, $.fn.liveTile.defaults, options); 138 // checks for browser feature support to enable hardware acceleration 139 metrojs.checkCapabilities(settings); 140 helperMethods.getBrowserPrefix(); 141 // setup the default content modules 142 if ($.fn.liveTile.contentModules.hasContentModule("image") == -1) 143 $.fn.liveTile.contentModules.addContentModule("image", defaultModules.imageSwap); 144 if ($.fn.liveTile.contentModules.hasContentModule("html") == -1) 145 $.fn.liveTile.contentModules.addContentModule("html", defaultModules.htmlSwap); 146 // this is where the magic happens 147 return $(this).each(function (tileIndex, ele) { 148 var $this = $(ele), 149 data = privMethods.initTileData($this, settings); 150 // append back tiles and add appropriate classes to prepare tiles 151 data.faces = privMethods.prepTile($this, data); 152 // action methods 153 data.fade = function (count) { privMethods.fade($this, count); }; 154 data.slide = function (count) { privMethods.slide($this, count); }; 155 data.carousel = function (count) { privMethods.carousel($this, count); }; 156 data.flip = function (count) { privMethods.flip($this, count); }; 157 data.flipList = function (count) { privMethods.flipList($this, count); }; 158 var actions = { 159 fade: data.fade, 160 slide: data.slide, 161 carousel: data.carousel, 162 flip: data.flip, 163 'flip-list': data.flipList 164 }; 165 data.doAction = function (count) { 166 // get the action for the current mode 167 var action = actions[data.mode]; 168 if (typeof (action) === "function") { 169 action(count); 170 data.hasRun = true; 171 } 172 // prevent pauseOnHover from resuming a tile that has finished 173 if (count == data.timer.repeatCount) 174 data.runEvents = false; 175 }; 176 177 // create a new tile timer 178 data.timer = new $.fn.metrojs.TileTimer(data.delay, data.doAction, data.repeatCount); 179 // apply the data 180 $this.data("LiveTile", data); 181 // handle events 182 // only bind pause / play on hover if we are not using a fliplist or flipListOnHover isn't set set 183 if (data.mode !== "flip-list" || data.flipListOnHover == false) { 184 if (data.pauseOnHover) { 185 privMethods.bindPauseOnHover($this); 186 } else if (data.playOnHover) { 187 privMethods.bindPlayOnHover($this, data); 188 } 189 } 190 // add a click / link to the tile 191 if (data.link.length > 0 || typeof (data.click) === "function") { 192 $this.css({ cursor: 'pointer' }).bind("click.liveTile", function (e) { 193 var proceed = true; 194 if (typeof (data.click) === "function") { 195 proceed = data.click($this, data) || false; 196 } 197 if (proceed && data.link.length > 0) { 198 e.preventDefault(); 199 if (data.newWindow) 200 window.open(data.link); 201 else 202 window.location = data.link; 203 } 204 }); 205 } 206 // add bounce if set 207 privMethods.bindBounce($this, data); 208 // start timer 209 if (data.startNow && data.mode != "none") { 210 data.runEvents = true; 211 data.timer.start(data.initDelay); 212 } 213 }); 214 }, 215 // goto is a future reserved word 216 'goto': function (options) { 217 var opts, t = typeof (options); 218 if (t === "undefined") { 219 opts = { 220 index: -99, // same as next 221 delay: 0, 222 autoAniDirection: false 223 }; 224 } 225 if (t === "number" || !isNaN(options)) { 226 opts = { 227 index: parseInt(options, 10), 228 delay: 0 229 }; 230 } else if (t === "string") { 231 if (options == "next") { 232 opts = { 233 index: -99, 234 delay: 0 235 }; 236 } else if (options.indexOf("prev") === 0) { 237 opts = { 238 index: -100, 239 delay: 0 240 }; 241 } else { 242 $.error(options + " is not a recognized action for .liveTile(\"goto\")"); 243 return $(this); 244 } 245 } else if (t === "object") { 246 if (typeof (options.delay) === "undefined") { 247 options.delay = 0; 248 } 249 var ti = typeof (options.index); 250 if (ti === "undefined") { 251 options.index = 0; 252 } else if (ti === "string") { 253 if (options.index === "next") 254 options.index = -99; 255 else if (options.index.indexOf("prev") === 0) 256 options.index = -100; 257 } 258 opts = options; 259 } 260 return $(this).each(function (tileIndex, ele) { 261 var $tile = $(ele), 262 data = $tile.data("LiveTile"), 263 aniData = $tile.data("metrojs.tile"), 264 goTo = opts.index; 265 if (aniData.animating === true) 266 return $(this); 267 if (data.mode === "carousel") { 268 // get the index based off of the active carousel slide 269 var $cur = data.faces.$listTiles.filter(".active"); 270 var curIdx = data.faces.$listTiles.index($cur); 271 // carousel will look for these values as triggers 272 if (goTo === -100) { // prev 273 // autoAniDirection determines if a forward or backward animation should be used based on the goTo index 274 if (typeof (opts.autoAniDirection) === "undefined" || opts.autoAniDirection == true) 275 data.tempValues.animationDirection = typeof (opts.animationDirection) === "undefined" ? "backward" : opts.animationDirection; 276 goTo = curIdx === 0 ? data.faces.$listTiles.length - 1 : curIdx - 1; 277 } else if (goTo === -99) { // next 278 if (typeof (opts.autoAniDirection) === "undefined" || opts.autoAniDirection == true) 279 data.tempValues.animationDirection = typeof (opts.animationDirection) === "undefined" ? "forward" : opts.animationDirection; 280 goTo = curIdx + 1; 281 } 282 if (curIdx == goTo) { 283 return; 284 } 285 if (typeof (opts.direction) !== "undefined") { 286 data.tempValues.direction = opts.direction; 287 } 288 if (typeof (opts.animationDirection) !== "undefined") { 289 data.tempValues.animationDirection = opts.animationDirection; 290 } 291 // the index is offset by 1 and incremented on animate 292 if (goTo == 0) 293 data.currentIndex = data.faces.$listTiles.length; 294 else 295 data.currentIndex = goTo - 1; 296 } else // slide mode will use the index directly 297 data.currentIndex = goTo; 298 // start the timer 299 data.runEvents = true; 300 data.timer.start(opts.delay >= 0 ? opts.delay : data.delay); 301 }); 302 }, 303 play: function (options) { 304 var opts, t = typeof (options); 305 if (t === "undefined") { 306 opts = { 307 delay: -1 308 }; 309 } else if (t === "number") { 310 opts = { 311 delay: options 312 }; 313 } else if (t === "object") { 314 if (typeof (options.delay) === "undefined") { 315 options.delay = -1; 316 } 317 opts = options; 318 } 319 return $(this).each(function (tileIndex, ele) { 320 var $tile = $(ele), 321 data = $tile.data("LiveTile"); 322 data.runEvents = true;
323 if (opts.delay < 0 && !data.hasRun) 324 opts.delay = data.initDelay; 325 data.timer.start(opts.delay >= 0 ? opts.delay : data.delay); 326 }); 327 }, 328 animate: function () { // this is really only useful for certain edge cases in slide mode, use 'play' to toggle animations 329 return $(this).each(function (tileIndex, ele) { 330 var $tile = $(ele), 331 data = $tile.data("LiveTile"); 332 data.doAction(); 333 }); 334 }, 335 stop: function () { 336 return $(this).each(function (tileIndex, ele) { 337 var $tile = $(ele), 338 data = $tile.data("LiveTile"); 339 data.hasRun = false; 340 data.runEvents = false; 341 data.timer.stop(); 342 window.clearTimeout(data.eventTimeout); 343 window.clearTimeout(data.flCompleteTimeout); 344 window.clearTimeout(data.completeTimeout); 345 if (data.mode === "flip-list") { 346 data.faces.$listTiles.each(function (idx, li) { 347 var ldata = $(li).data("metrojs.tile"); 348 window.clearTimeout(ldata.eventTimeout); 349 window.clearTimeout(ldata.flCompleteTimeout); 350 window.clearTimeout(ldata.completeTimeout); 351 }); 352 } 353 }); 354 }, 355 pause: function () { 356 return $(this).each(function (tileIndex, ele) { 357 var $tile = $(ele), 358 data = $tile.data("LiveTile"); 359 data.timer.pause(); 360 data.runEvents = false; 361 window.clearTimeout(data.eventTimeout); 362 window.clearTimeout(data.flCompleteTimeout); 363 window.clearTimeout(data.completeTimeout); 364 if (data.mode === "flip-list") { 365 data.faces.$listTiles.each(function (idx, li) { 366 var ldata = $(li).data("metrojs.tile"); 367 window.clearTimeout(ldata.eventTimeout); 368 window.clearTimeout(ldata.flCompleteTimeout); 369 window.clearTimeout(ldata.completeTimeout); 370 }); 371 } 372 }); 373 }, 374 restart: function (options) { 375 var opts, t = typeof (options); 376 if (t === "undefined") { 377 opts = { 378 delay: -1 379 }; 380 } else if (t === "number") { 381 opts = { 382 delay: options 383 }; 384 } else if (t === "object") { 385 if (typeof (options.delay) === "undefined") { 386 options.delay = -1; 387 } 388 opts = options; 389 } 390 return $(this).each(function (tileIndex, ele) { 391 var $tile = $(ele), 392 data = $tile.data("LiveTile");
393 if (opts.delay < 0 && !data.hasRun) 394 opts.delay = data.initDelay; 395 data.hasRun = false; 396 data.runEvents = true; 397 data.timer.restart(opts.delay >= 0 ? opts.delay : data.delay); 398 }); 399 }, 400 rebind: function (options) { 401 return $(this).each(function (tileIndex, ele) { 402 if (typeof (options) !== "undefined") { 403 if (typeof (options.timer) !== "undefined" && options.timer != null) { 404 options.timer.stop(); 405 } 406 options.hasRun = false; 407 pubMethods["init"].apply(ele, [options]); 408 } else { 409 pubMethods["init"].apply(ele, [{}]); 410 } 411 }); 412 }, 413 destroy: function (options) { 414 var t = typeof (options), opts; 415 if (t === "undefined") { 416 opts = { 417 removeCss: false 418 }; 419 } else if (t === "boolean") { 420 opts = { 421 removeCss: options 422 }; 423 } else if (t === "object") { 424 if (typeof (options.removeCss) === "undefined") { 425 options.removeCss = false; 426 } 427 opts = options; 428 } 429 return $(this).each(function (tileIndex, ele) { 430 var $tile = $(ele); 431 var data = $tile.data("LiveTile"); 432 if (typeof (data) === "undefined") 433 return; 434 $tile.unbind(".liveTile"); 435 var resetCss = helperMethods.appendStyleProperties({ margin: '', cursor: '' }, ['transform', 'transition'], ['', '']); 436 data.timer.stop(); 437 window.clearTimeout(data.eventTimeout); 438 window.clearTimeout(data.flCompleteTimeout); 439 window.clearTimeout(data.completeTimeout); 440 if (data.faces.$listTiles != null) { 441 data.faces.$listTiles.each(function (idx, li) { 442 var $li = $(li); 443 if (data.mode === "flip-list") { 444 var ldata = $li.data("metrojs.tile"); 445 window.clearTimeout(ldata.eventTimeout); 446 window.clearTimeout(ldata.flCompleteTimeout); 447 window.clearTimeout(ldata.completeTimeout); 448 } else if (data.mode === "carousel") { 449 var sdata = data.listData[idx]; 450 if (sdata.bounce) { 451 privMethods.unbindMsBounce($li, sdata); 452 } 453 } 454 if (opts.removeCss) { 455 $li.removeClass("ha"); 456 $li.find(data.tileFaceSelector) 457 .unbind(".liveTile") 458 .removeClass("bounce flip-front flip-back ha slide slide-front slide-back") 459 .css(resetCss); 460 } else { 461 $li.find(data.tileFaceSelector).unbind(".liveTile"); 462 } 463 $li.removeData("metrojs.tile"); 464 }).unbind(".liveTile"); 465 } 466 if (data.faces.$front != null && opts.removeCss) { 467 data.faces.$front.removeClass("flip-front flip-back ha slide slide-front slide-back") 468 .css(resetCss); 469 } 470 if (data.faces.$back != null && opts.removeCss) { 471 data.faces.$back.removeClass("flip-front flip-back ha slide slide-front slide-back") 472 .css(resetCss); 473 } 474 // remove the bounce and hover methods 475 // todo: combine all mouse/touch events (down, move, up) 476 if (data.bounce) { 477 privMethods.unbindMsBounce($tile, data); 478 } 479 if (data.playOnHover) { 480 privMethods.unbindMsPlayOnHover($tile, data); 481 } 482 if (data.pauseOnhover) { 483 privMethods.unbindMsPauseOnHover($tile, data); 484 } 485 $tile.removeClass("ha"); 486 $tile.removeData("LiveTile"); 487 $tile.removeData("metrojs.tile"); 488 data = null; 489 }); 490 } 491}; 492 493// private methods that are called by .liveTile 494var privMethods = { 495 //getDataOrDefault for older versions of jQuery that dont look for 'data-' properties 496 dataAtr: function ($ele, name, def) { 497 return typeof ($ele.attr('data-' + name)) !== "undefined" ? $ele.attr('data-' + name) : def; 498 }, 499 dataMethod: function ($ele, name, def) { 500 return typeof ($ele.data(name)) !== "undefined" ? $ele.data(name) : def; 501 }, 502 getDataOrDefault: null, 503 initTileData: function ($tile, stgs) { 504 var useData = stgs.ignoreDataAttributes == false, 505 tdata = null; 506 if (this.getDataOrDefault == null) 507 this.getDataOrDefault = metrojs.capabilities.isOldJQuery ? this.dataAtr : this.dataMethod; 508 if (useData) { 509 tdata = { //an object to store settings for later access 510 speed: this.getDataOrDefault($tile, "speed", stgs.speed), 511 delay: this.getDataOrDefault($tile, "delay", stgs.delay), 512 stops: this.getDataOrDefault($tile, "stops", stgs.stops), 513 stack: this.getDataOrDefault($tile, "stack", stgs.stack), 514 mode: this.getDataOrDefault($tile, "mode", stgs.mode), 515 direction: this.getDataOrDefault($tile, "direction", stgs.direction), 516 useHardwareAccel: this.getDataOrDefault($tile, "ha", stgs.useHardwareAccel), 517 repeatCount: this.getDataOrDefault($tile, "repeat", stgs.repeatCount), 518 swap: this.getDataOrDefault($tile, "swap", stgs.swap), 519 appendBack: this.getDataOrDefault($tile, "appendback", stgs.appendBack), 520 currentIndex: this.getDataOrDefault($tile, "start-index", stgs.currentIndex), 521 animationDirection: this.getDataOrDefault($tile, "ani-direction", stgs.animationDirection), 522 startNow: this.getDataOrDefault($tile, "start-now", stgs.startNow), 523 tileSelector: this.getDataOrDefault($tile, "tile-selector", stgs.tileSelector), 524 tileFaceSelector: this.getDataOrDefault($tile, "face-selector", stgs.tileFaceSelector), 525 bounce: this.getDataOrDefault($tile, "bounce", stgs.bounce), 526 bounceDirections: this.getDataOrDefault($tile, "bounce-dir", stgs.bounceDirections), 527 bounceFollowsMove: this.getDataOrDefault($tile, "bounce-follows", stgs.bounceFollowsMove), 528 click: this.getDataOrDefault($tile, "click", stgs.click), 529 link: this.getDataOrDefault($tile, "link", stgs.link), 530 newWindow: this.getDataOrDefault($tile, "new-window", stgs.newWindow), 531 alwaysTrigger: this.getDataOrDefault($tile, "always-trigger", stgs.alwaysTrigger), 532 flipListOnHover: this.getDataOrDefault($tile, "flip-onhover", stgs.flipListOnHover), 533 pauseOnHover: this.getDataOrDefault($tile, "pause-onhover", stgs.pauseOnHover), 534 playOnHover: this.getDataOrDefault($tile, "play-onhover", stgs.playOnHover), 535 onHoverDelay: this.getDataOrDefault($tile, "hover-delay", stgs.onHoverDelay), 536 onHoverOutDelay: this.getDataOrDefault($tile, "hoverout-delay", stgs.onHoverOutDelay), 537 noHAflipOpacity: this.getDataOrDefault($tile, "flip-opacity", stgs.noHAflipOpacity), 538 useTranslate: this.getDataOrDefault($tile, "use-translate", stgs.useTranslate), 539 runEvents: false, 540 isReversed: false, 541 loopCount: 0, 542 contentModules: [], 543 listData: [], 544 height: $tile.height(), 545 width: $tile.width(), 546 tempValues: {} 547 }; 548 } else { 549 tdata = $.extend(true, { 550 runEvents: false, 551 isReversed: false, 552 loopCount: 0, 553 contentModules: [], 554 listData: [], 555 height: $tile.height(), 556 width: $tile.width(), 557 tempValues: {} 558 }, stgs); 559 } 560 tdata.useTranslate = tdata.useTranslate && tdata.useHardwareAccel && metrojs.capabilities.canTransform && metrojs.capabilities.canTransition; 561 // set the margin to half of the height or width based on the direction 562 tdata.margin = (tdata.direction === "vertical") ? tdata.height / 2 : tdata.width / 2; 563 // convert stops if needed 564 tdata.stops = (typeof (stgs.stops) === "object" && (stgs.stops instanceof Array)) ? stgs.stops : ("" + tdata.stops).split(","); 565 // add a return stop 566 if (tdata.stops.length === 1) 567 tdata.stops.push("0px"); 568 // add content modules, start with global swaps 569 var swaps = tdata.swap instanceof Array ? tdata.swap : tdata.swap.replace(' ', '').split(","); 570 // get the front and back swap data 571 var sf = useData ? this.getDataOrDefault($tile, "swap-front", stgs.swapFront) : stgs.swapFront; 572 var sb = useData ? this.getDataOrDefault($tile, "swap-back", stgs.swapBack) : stgs.swapBack; 573 // set the data to the global value if its still the default 574 if (sf instanceof Array) { 575 tdata.swapFront = sf; 576 } else { 577 tdata.swapFront = sf === '-' ? swaps : sf.replace(' ', '').split(","); 578 } 579 580 if (sb instanceof Array) { 581 tdata.swapBack = sb; 582 } else { 583 tdata.swapBack = sb === '-' ? swaps : sb.replace(' ', '').split(","); 584 } 585 // make sure the swaps includes all front and back swaps 586 var i; 587 for (i = 0; i < tdata.swapFront.length; i++) { 588 if (tdata.swapFront[i].length > 0 && $.inArray(tdata.swapFront[i], swaps) === -1) 589 swaps.push(tdata.swapFront[i]); 590 } 591 for (i = 0; i < tdata.swapBack.length; i++) { 592 if (tdata.swapBack[i].length > 0 && $.inArray(tdata.swapBack[i], swaps) === -1) 593 swaps.push(tdata.swapBack[i]); 594 } 595 tdata.swap = swaps; 596 // add all required content modules for the swaps 597 for (i = 0; i < swaps.length; i++) { 598 if (swaps[i].length > 0) { 599 var moduleIdx = $.fn.liveTile.contentModules.hasContentModule(swaps[i]); 600 if (moduleIdx > -1) { 601 tdata.contentModules.push($.fn.liveTile.contentModules.modules[moduleIdx]); 602 } 603 } 604 } 605 // set the initDelay value to the delay if it's not set 606 tdata.initDelay = useData ? this.getDataOrDefault($tile, "initdelay", stgs.initDelay) : stgs.initDelay; 607 // if the delay is -1 call the triggerDelay function to get a value 608 if (tdata.delay < -1) 609 tdata.delay = stgs.triggerDelay(1); 610 else if (tdata.delay < 0) 611 tdata.delay = 3500 + (Math.random() * 4501); 612 // match the delay value if less than 0 613 if (tdata.initDelay < 0) 614 tdata.initDelay = tdata.delay; 615 // merge the objects 616 var mergedData = {}; 617 for (i = 0; i < tdata.contentModules.length; i++) 618 $.extend(mergedData, tdata.contentModules[i].data); 619 $.extend(mergedData, stgs, tdata); 620 // add flip-list / carousel data 621 var $tiles; 622 if (mergedData.mode === "flip-list") { 623 $tiles = $tile.find(mergedData.tileSelector).not(".tile-title"); 624 $tiles.each(function (idx, ele) { 625 var $li = $(ele); 626 var ldata = { 627 direction: useData ? privMethods.getDataOrDefault($li, "direction", mergedData.direction) : mergedData.direction, 628 newWindow: useData ? privMethods.getDataOrDefault($li, "new-window", false) : false, 629 link: useData ? privMethods.getDataOrDefault($li, "link", "") : "", 630 faces: { $front: null, $back: null }, 631 height: $li.height(), 632 width: $li.width(), 633 isReversed: false 634 }; 635 ldata.margin = ldata.direction === "vertical" ? ldata.height / 2 : ldata.width / 2; 636 mergedData.listData.push(ldata); 637 }); 638 } else if (mergedData.mode === "carousel") { 639 mergedData.stack = true; 640 $tiles = $tile.find(mergedData.tileSelector).not(".tile-title"); 641 $tiles.each(function (idx, ele) { 642 var $slide = $(ele); 643 var sdata = { 644 bounce: useData ? privMethods.getDataOrDefault($slide, "bounce", false) : false, 645 bounceDirections: useData ? privMethods.getDataOrDefault($slide, "bounce-dir", "all") : "all", 646 link: useData ? privMethods.getDataOrDefault($slide, "link", "") : "", 647 newWindow: useData ? privMethods.getDataOrDefault($slide, "new-window", false) : false, 648 animationDirection: useData ? privMethods.getDataOrDefault($slide, "ani-direction", "") : "", 649 direction: useData ? privMethods.getDataOrDefault($slide, "direction", "") : "" 650 }; 651 mergedData.listData.push(sdata); 652 }); 653 } 654 // get any additional options from the modules 655 for (i = 0; i < tdata.contentModules.length; i++) { 656 if (typeof (mergedData.contentModules[i].initData) === "function") 657 mergedData.contentModules[i].initData(mergedData, $tile); 658 } 659 tdata = null; 660 return mergedData; 661 }, 662 prepTile: function ($tile, tdata) { 663 //add the mode to the tile if it's not already there. 664 $tile.addClass(tdata.mode); 665 var ret = { 666 $tileFaces: null, // all possible tile faces in a liveTile in a non list mode 667 $listTiles: null, // all possible tiles in a liveTile in a list mode 668 $front: null, // the front face of a tile in a non list mode 669 $back: null // the back face of a tile in a non list mode 670 }; 671 var rotateDir, frontCss, backCss, tileCss; 672 // prepare the tile based on the current mode 673 switch (tdata.mode) { 674 case "fade": 675 // front and back tile faces 676 ret.$tileFaces = $tile.find(tdata.tileFaceSelector).not(".tile-title"); 677 ret.$front = (tdata.faces.$front != null && tdata.faces.$front.length > 0) ? 678 tdata.faces.$front.addClass('fade-front') : 679 ret.$tileFaces.filter(":first").addClass('fade-front'); 680 // get back face from settings, via selector, or append it if necessary 681 if (tdata.faces.$back != null && tdata.faces.$back.length > 0) // use $back option 682 ret.$back = tdata.faces.$back.addClass('fade-back'); 683 else if (ret.$tileFaces.length > 1) // get the last tile face 684 ret.$back = ret.$tileFaces.filter(":last").addClass('fade-back'); 685 else if (tdata.appendBack) // append the back t
685ile 686 ret.$back = $('<div class="fade-back"></div>').appendTo($tile); 687 else // just keep an empty placeholder 688 ret.$back = $('<div></div>'); 689 break; 690 case "slide": 691 // front and back tile faces 692 ret.$tileFaces = $tile.find(tdata.tileFaceSelector).not(".tile-title"); 693 // get front face from settings or via selector 694 ret.$front = (tdata.faces.$front != null && tdata.faces.$front.length > 0) ? 695 tdata.faces.$front.addClass('slide-front') : 696 ret.$tileFaces.filter(":first").addClass('slide-front'); // using :first for pre jQuery 1.4 697 // get back face from settings, via selector, or append it if necessary 698 if (tdata.faces.$back != null && tdata.faces.$back.length > 0) // use $back option 699 ret.$back = tdata.faces.$back.addClass('slide-back'); 700 else if (ret.$tileFaces.length > 1) // get the last tile face 701 ret.$back = ret.$tileFaces.filter(":last").addClass('slide-back'); 702 else if (tdata.appendBack) // append the back tile 703 ret.$back = $('<div class="slide-back"></div>').appendTo($tile); 704 else // just keep an empty placeholder 705 ret.$back = $('<div></div>'); 706 // stack mode 707 if (tdata.stack == true) { 708 var prop, 709 translate; 710 if (tdata.direction === "vertical") { 711 prop = "top", 712 translate = 'translate(0%, -100%) translateZ(0)'; 713 } else { 714 prop = "left", 715 translate = 'translate(-100%, 0%) translateZ(0)'; 716 } 717 backCss = {}; 718 if (tdata.useTranslate) 719 helperMethods.appendStyleProperties(backCss, ['transform'], [translate]); 720 else 721 backCss[prop] = "-100%"; 722 ret.$back.css(backCss); 723 } 724 $tile.data("metrojs.tile", { animating: false }); 725 if (metrojs.capabilities.canTransition && tdata.useHardwareAccel) { // hardware accelerated :) 726 $tile.addClass("ha"); 727 ret.$front.addClass("ha"); 728 ret.$back.addClass("ha"); 729 } 730 break; 731 case "carousel": 732 ret.$listTiles = $tile.find(tdata.tileSelector).not(".tile-title"); 733 var numberOfSlides = ret.$listTiles.length; 734 $tile.data("metrojs.tile", { animating: false }); 735 tdata.currentIndex = Math.min(tdata.currentIndex, numberOfSlides - 1); 736 ret.$listTiles.each(function (idx, ele) { 737 var $slide = $(ele).addClass("slide"); 738 var sdata = tdata.listData[idx], 739 aniDir = typeof (sdata.animationDirection) === "string" && sdata.animationDirection.length > 0 ? sdata.animationDirection : tdata.animationDirection, 740 dir = typeof (sdata.direction) === "string" && sdata.direction.length > 0 ? sdata.direction : tdata.direction; 741 if (idx == tdata.currentIndex) { 742 $slide.addClass("active"); 743 } else if (aniDir === "forward") { 744 if (dir === "vertical") { 745 tileCss = tdata.useTranslate ? helperMethods.appendStyleProperties({}, ['transform'], ['translate(0%, 100%) translateZ(0)']) : 746 { left: '0%', top: '100%' }; 747 $slide.css(tileCss); 748 } else { 749 tileCss = tdata.useTranslate ? helperMethods.appendStyleProperties({}, ['transform'], ['translate(100%, 0%) translateZ(0)']) : 750 { left: '100%', top: '0%' }; 751 $slide.css(tileCss); 752 } 753 } else if (aniDir === "backward") { 754 if (dir === "vertical") { 755 tileCss = tdata.useTranslate ? helperMethods.appendStyleProperties({}, ['transform'], ['translate(0%, -100%) translateZ(0)']) : 756 { left: '0%', top: '-100%' }; 757 $slide.css(tileCss); 758 } else { 759 tileCss = tdata.useTranslate ? helperMethods.appendStyleProperties({}, ['transform'], ['translate(-100%, 0%) translateZ(0)']) :
760 { left: '-100%', top: '0%' }; 761 $slide.css(tileCss); 762 } 763 } 764 // link and bounce can be bound per slide 765 // add the click handler and link property 766 privMethods.bindLink($slide, sdata); 767 // add the bounce effect 768 if (tdata.useHardwareAccel && metrojs.capabilities.canTransition) 769 privMethods.bindBounce($slide, sdata); 770 $slide = null; 771 sdata = null; 772 }); 773 // hardware accelerated :) 774 if (metrojs.capabilities.canFlip3d && tdata.useHardwareAccel) { 775 $tile.addClass("ha"); 776 ret.$listTiles.addClass("ha"); 777 } 778 break; 779 case "flip-list": 780 // the tile containers inside the list 781 ret.$listTiles = $tile.find(tdata.tileSelector).not(".tile-title"); 782 ret.$listTiles.each(function (idx, ele) { 783 var $li = $(ele).addClass("tile-" + (idx + 1)); 784 // add the flip class to the front face 785 var $lFront = $li.find(tdata.tileFaceSelector).filter(":first").addClass("flip-front").css({ margin: "0px" }); 786 // append a back tile face if one isnt present 787 if ($li.find(tdata.tileFaceSelector).length === 1 && tdata.appendBack == true) 788 $li.append("<div></div>"); 789 // add the flip class to the back face 790 var $lBack = $li.find(tdata.tileFaceSelector).filter(":last").addClass("flip-back").css({ margin: "0px" }); 791 // update the tdata object with the faces 792 tdata.listData[idx].faces.$front = $lFront; 793 tdata.listData[idx].faces.$back = $lBack; 794 // set data for overrides and easy access 795 $li.data("metrojs.tile", { 796 animating: false, 797 count: 1, 798 completeTimeout: null, 799 flCompleteTimeout: null, 800 index: idx 801 }); 802 var ldata = $li.data("metrojs.tile"); 803 // add the hardware accelerated classes 804 if (metrojs.capabilities.canFlip3d && tdata.useHardwareAccel) { // hardware accelerated :) 805 $li.addClass("ha"); 806 $lFront.addClass("ha"); 807 $lBack.addClass("ha"); 808 rotateDir = tdata.listData[idx].direction === "vertical" ? "rotateX(180deg)" : "rotateY(180deg)"; 809 backCss = helperMethods.appendStyleProperties({}, ["transform"], [rotateDir]); 810 $lBack.css(backCss); 811 } else { // not hardware accelerated :( 812 // the front tile face will take up the entire tile 813 frontCss = (tdata.listData[idx].direction === "vertical") ? 814 { height: '100%', width: '100%', marginTop: '0px', opacity: '1' } : 815 { height: '100%', width: '100%', marginLeft: '0px', opacity: '1' }; 816 // the back tile face is hidden by default and expanded halfway through a flip 817 backCss = (tdata.listData[idx].direction === "vertical") ? 818 { height: '0px', width: '100%', marginTop: tdata.listData[idx].margin + 'px', opacity: tdata.noHAflipOpacity } : 819 { height: '100%', width: '0px', marginLeft: tdata.listData[idx].margin + 'px', opacity: tdata.noHAflipOpacity }; 820 $lFront.css(frontCss); 821 $lBack.css(backCss); 822 } 823 var flipEnded = function () { 824 ldata.count++; 825 if (ldata.count >= MAX_LOOP_COUNT) 826 ldata.count = 1; 827 }; 828 if (tdata.flipListOnHover) { 829 var event = tdata.flipListOnHoverEvent + ".liveTile"; 830 $lFront.bind(event, function () { 831 privMethods.flip($li, ldata.count, tdata, flipEnded); 832 }); 833 $lBack.bind(event, function () { 834 privMethods.flip($li, ldata.count, tdata, flipEnded); 835 }); 836 } 837 if (tdata.listData[idx].link.length > 0) { 838 $li.css({ cursor: 'pointer' }).bind("click.liveTile", function () { 839 if (tdata.listData[idx].newWindow) 840 window.open(tdata.listData[idx].link); 841 else 842 window.location = tdata.listData[idx].link; 843 }); 844 } 845 }); 846 break; 847 case "flip": 848 // front and back tile faces 849 ret.$tileFaces = $tile.find(tdata.tileFaceSelector).not(".tile-title"); 850 // get front face from settings or via selector 851 ret.$front = (tdata.faces.$front != null && tdata.faces.$front.length > 0) ? 852 tdata.faces.$front.addClass('flip-front') : 853 ret.$tileFaces.filter(":first").addClass('flip-front'); 854 // get back face from settings, via selector, or append it if necessary 855 if (tdata.faces.$back != null && tdata.faces.$back.length > 0) { 856 // use $back option 857 ret.$back = tdata.faces.$back.addClass('flip-back'); 858 } else if (ret.$tileFaces.length > 1) { 859 // get the last tile face 860 ret.$back = ret.$tileFaces.filter(":last").addClass('flip-back'); 861 } else if (tdata.appendBack) {
862 // append the back tile 863 ret.$back = $('<div class="flip-back"></div>').appendTo($tile); 864 } else { 865 // just keep an empty placeholder 866 ret.$back = $('<div></div>'); 867 } 868 $tile.data("metrojs.tile", { animating: false }); 869 if (metrojs.capabilities.canFlip3d && tdata.useHardwareAccel) { 870 // hardware accelerated :) 871 $tile.addClass("ha"); 872 ret.$front.addClass("ha"); 873 ret.$back.addClass("ha"); 874 rotateDir = tdata.direction === "vertical" ? "rotateX(180deg)" : "rotateY(180deg)"; 875 backCss = helperMethods.appendStyleProperties({}, ["transform"], [rotateDir]); 876 ret.$back.css(backCss); 877 878 } else { 879 // not hardware accelerated :( 880 // the front tile face will take up the entire tile 881 frontCss = (tdata.direction === "vertical") ? 882 { height: '100%', width: '100%', marginTop: '0px', opacity: '1' } : 883 { height: '100%', width: '100%', marginLeft: '0px', opacity: '1' }; 884 // the back tile face is hidden by default and expanded halfway through a flip 885 backCss = (tdata.direction === "vertical") ? 886 { height: '0%', width: '100%', marginTop: tdata.margin + 'px', opacity: '0' } : 887 { height: '100%', width: '0%', marginLeft: tdata.margin + 'px', opacity: '0' }; 888 ret.$front.css(frontCss); 889 ret.$back.css(backCss); 890 } 891 break; 892 } 893 return ret; 894 }, 895 bindPauseOnHover: function ($tile) { 896 // stop the tile when hovered and resume after a delay 897 (function () { 898 var data = $tile.data("LiveTile"), 899 isOver = false, 900 isPending = false, 901 pauseIn = (data.pauseOnHoverEvent == "both" || data.pauseOnHoverEvent == "mouseover" || data.pauseOnHoverEvent == "mouseenter"), 902 pauseOut = (data.pauseOnHoverEvent == "both" || data.pauseOnHoverEvent == "mouseout" || data.pauseOnHoverEvent == "mouseleave"); 903 data.pOnHoverMethods = { 904 pause: function () { 905 data.timer.pause(); 906 if (data.mode === "flip-list") { 907 data.faces.$listTiles.each(function (idx, li) { 908 window.clearTimeout($(li).data("metrojs.tile").completeTimeout); 909 }); 910 } 911 }, 912 over: function (e) { 913 if (isOver || isPending) 914 return; 915 if (data.runEvents) { 916 isPending = true; 917 data.eventTimeout = window.setTimeout(function () { 918 isPending = false; 919 if (pauseOut) 920 isOver = true; 921 data.pOnHoverMethods.pause(); 922 }, data.onHoverDelay); 923 } 924 }, 925 out: function (e) { 926 if (isPending) { 927 window.clearTimeout(data.eventTimeout); 928 isPending = false; 929 return; 930 } 931 if (pauseIn) { 932 if (!isOver && !isPending) 933 return; 934 if (data.runEvents) { 935 // todo: use a custom value if provided 936 data.timer.start(data.hasRun ? data.delay : data.initDelay); 937 } 938 } else { 939 data.pOnHoverMethods.pause(); 940 } 941 isOver = false; 942 } 943 }; 944 if (!metrojs.capabilities.canTouch) { 945 if (pauseIn) 946 $tile.bind("mouseover.liveTile", data.pOnHoverMethods.over); 947 if (pauseOut) 948 $tile.bind("mouseout.liveTile", data.pOnHoverMethods.out); 949 } else { 950 if (window.navigator.msPointerEnabled) { // pointer 951 if (pauseIn) 952 $tile[0].addEventListener('MSPointerOver', data.pOnHoverMethods.over, false); 953 if (pauseOut) 954 $tile[0].addEventListener('MSPointerOut', data.pOnHoverMethods.out, false); 955 } else { // touch events 956 if (pauseIn) 957 $tile.bind("touchstart.liveTile", data.pOnHoverMethods.over); 958 if (pauseOut) 959 $tile.bind("touchend.liveTile", data.pOnHoverMethods.out); 960 } 961 } 962 })(); 963 }, 964 unbindMsPauseOnHover: function ($tile, data) { 965 if (typeof (data.pOnHoverMethods) !== "undefined" && window.navigator.msPointerEnabled) { 966 $tile[0].removeEventListener('MSPointerOver', data.pOnHoverMethods.over, false); 967 $tile[0].removeEventListener('MSPointerOut', data.pOnHoverMethods.out, false); 968 } 969 }, 970 bindPlayOnHover: function ($tile, data) { 971 // play the tile immediately when hovered 972 (function () { 973 var isOver = false, 974 isPending = false, 975 playIn = (data.playOnHoverEvent == "both" || data.playOnHoverEvent == "mouseover" || data.playOnHoverEvent == "mouseenter"), 976 playOut = (data.playOnHoverEvent == "both" || data.playOnHoverEvent == "mouseout" || data.playOnHoverEvent == "mouseleave"); 977 data.onHoverMethods = { 978 over: function (event) { 979 if (isOver || isPending || (data.bounce && data.bounceMethods.down != "no")) 980 return; 981 // if startNow is set use the opposite of isReversed so we're in sync 982 var rev = (data.mode == "flip") || (data.startNow ? !data.isReversed : data.isReversed); 983 window.clearTimeout(data.eventTimeout);
984 if ((data.runEvents && rev) || !data.hasRun) { 985 isPending = true; 986 data.eventTimeout = window.setTimeout(function () { 987 isPending = false; 988 if (playOut) 989 isOver = true; 990 pubMethods["play"].apply($tile[0], [0]); 991 }, data.onHoverDelay); 992 } 993 }, 994 out: function (event) { 995 if (isPending) { 996 window.clearTimeout(data.eventTimeout); 997 isPending = false; 998 return; 999 } 1000 if (playIn) { 1001 if (!isOver && !isPending) { 1002 return; 1003 } 1004 } 1005 window.clearTimeout(data.eventTimeout); 1006 data.eventTimeout = window.setTimeout(function () { 1007 var rev = (data.mode == "flip") || (data.startNow ? data.isReversed : !data.isReversed); 1008 if (data.runEvents && rev) { 1009 pubMethods["play"].apply($tile[0], [0]); 1010 } 1011 isOver = false; 1012 }, data.speed + data.onHoverOutDelay); 1013 } 1014 }; 1015 if (!metrojs.capabilities.canTouch) { 1016 if (playIn) 1017 $tile.bind('mouseenter.liveTile', data.onHoverMethods.over); 1018 if (playOut) 1019 $tile.bind('mouseleave.liveTile', data.onHoverMethods.out); 1020 } else { 1021 if (window.navigator.msPointerEnabled) { // pointer 1022 if (playIn) 1023 $tile[0].addEventListener('MSPointerDown', data.onHoverMethods.over, false); 1024 // mouseleave gives a more consistent effect than out when the children are transformed 1025 if (playOut) 1026 $tile.bind("mouseleave.liveTile", data.onHoverMethods.out); 1027 } else { // touch events 1028 if (playIn) 1029 $tile.bind("touchstart.liveTile", data.onHoverMethods.over); 1030 if (playOut) 1031 $tile.bind("touchend.liveTile", data.onHoverMethods.out); 1032 } 1033 1034 } 1035 })(); 1036 }, 1037 unbindMsPlayOnHover: function ($tile, data) { 1038 if (typeof (data.onHoverMethods) !== "undefined" && window.navigator.msPointerEnabled) { 1039 $tile[0].removeEventListener('MSPointerDown', data.onHoverMethods.over, false); 1040 } 1041 }, 1042 bindBounce: function ($tile, data) { 1043 // add bounce 1044 if (data.bounce) { 1045 $tile.addClass("bounce"); 1046 $tile.addClass("noselect"); 1047 (function () { 1048 data.bounceMethods = { 1049 down: "no", 1050 threshold: 30, 1051 zeroPos: { x: 0, y: 0 }, 1052 eventPos: { x: 0, y: 0 }, 1053 inTilePos: { x: 0, y: 0 }, 1054 pointPos: { x: 0, y: 0 }, 1055 regions: { 1056 c: [0, 0], // center 1057 tl: [-1, -1], // top left 1058 tr: [1, -1], // top right 1059 bl: [-1, 1], // bottom left 1060 br: [1, 1], // bottom right 1061 t: [null, -1], // top 1062 r: [1, null], // right
1063 b: [null, 1], // bottom 1064 l: [-1, null] // left 1065 }, 1066 targets: { 1067 all: ['c', 't', 'r', 'b', 'l', 'tl', 'tr', 'bl', 'br'], 1068 edges: ['c', 't', 'r', 'b', 'l'], 1069 corners: ['c', 'tl', 'tr', 'bl', 'br'] 1070 }, 1071 hitTest: function ($el, pos, targetRegions, omegaC) { 1072 var regions = data.bounceMethods.regions, 1073 checkFor = data.bounceMethods.targets[targetRegions], 1074 i = 0, 1075 strictMatch = null, 1076 looseMatch = null, 1077 defResult = { hit: [0, 0], name: 'c' }; 1078 // scale only for android 2.x and old ie 1079 if (metrojs.capabilities.isOldAndroid || !metrojs.capabilities.canTransition) 1080 return defResult; 1081 if (typeof (checkFor) == "undefined") { 1082 if (typeof (targetRegions) === "string") 1083 checkFor = targetRegions.split(','); 1084 // only default to center if explicitly requested 1085 if ($.isArray(checkFor) && $.inArray('c') == -1) { 1086 omegaC = 0; 1087 defResult = null; 1088 } 1089 } 1090 // check for a matching region 1091 var w = $el.width(), 1092 h = $el.height(), 1093 // center threshold - maximum amount from center 1094 ct = [w * omegaC, h * omegaC], 1095 // how far from the center is the point 1096 diffX = pos.x - (w * 0.5), 1097 diffY = pos.y - (h * 0.5), 1098 // if we're beyond the center threshold, set -1 or 1 else 0 1099 hit = [ 1100 diffX > 0 ? (Math.abs(diffX) <= ct[0] ? 0 : 1) : (Math.abs(diffX) <= ct[0] ? 0 : -1), 1101 diffY > 0 ? (Math.abs(diffY) <= ct[1] ? 0 : 1) : (Math.abs(diffY) <= ct[1] ? 0 : -1) 1102 ]; 1103 for (; i < checkFor.length; i++) { 1104 if (strictMatch != null) 1105 return strictMatch; 1106 var r = checkFor[i], 1107 region = regions[r]; 1108 if (r == "*") { 1109 r = checkFor[i + 1]; 1110 return { region: regions[r], name: r }; 1111 } 1112 if (hit[0] == region[0] && hit[1] == region[1]) { 1113 // found the region with a strict lookup 1114 strictMatch = { hit: region, name: r }; 1115 } else if ((hit[0] == region[0] || region[0] == null) && (hit[1] == region[1] || region[1] == null)) { 1116 // found the region with a loose lookup 1117 looseMatch = { hit: region, name: r }; 1118 } 1119 } 1120 // prefer a strict match 1121 if (strictMatch != null) 1122 return strictMatch; 1123 else if (looseMatch != null) 1124 return looseMatch; 1125 else // no matches were found, return center 1126 return defResult; 1127 }, 1128 bounceDown: function (e) { 1129 if (e.target.tagName == "A" && !$(e).is(".bounce")) 1130 return; 1131 var point = e.originalEvent && e.originalEvent.touches ? e.originalEvent.touches[0] : e, 1132 offsetOfTile = $tile.offset(), 1133 scrollX = window.pageXOffset, 1134 scrollY = window.pageYOffset; 1135 data.bounceMethods.pointPos = { 1136 x: point.pageX, 1137 y: point.pageY 1138 }; 1139 data.bounceMethods.inTilePos = { 1140 x: point.pageX - offsetOfTile.left, 1141 y: point.pageY - offsetOfTile.top 1142 }; 1143 1144 if (!data.$tileParent) { 1145 data.$tileParent = $tile.parent(); 1146 } 1147 var offsetOfParent = data.$tileParent.offset(); 1148 data.bounceMethods.eventPos = { 1149 x: (offsetOfTile.left - offsetOfParent.left) + ($tile.width() / 2), 1150 y: (offsetOfTile.top - offsetOfParent.top) + ($tile.height() / 2) 1151 }; 1152 var hit = data.bounceMethods.hitTest($tile, data.bounceMethods.inTilePos, data.bounceDirections, 0.25); 1153 if (hit == null) 1154 data.bounceMethods.down = "no"; 1155 else { 1156 if (window.navigator.msPointerEnabled) { 1157 document.addEventListener('MSPointerUp', data.bounceMethods.bounceUp, false); 1158 $tile[0].addEventListener('MSPointerUp', data.bounceMethods.bounceUp, false); 1159 document.addEventListener('MSPointerCancel', data.bounceMethods.bounceUp, false); 1160 if (data.bounceFollowsMove) 1161 $tile[0].addEventListener('MSPointerMove', data.bounceMethods.bounceMove, false); 1162 } else { 1163 $(document).bind("mouseup.liveTile, touchend.liveTile, touchcancel.liveTile, dragstart.liveTile", data.bounceMethods.bounceUp); 1164 if (data.bounceFollowsMove) { 1165 $tile.bind("touchmove.liveTile", data.bounceMethods.bounceMove); 1166 $tile.bind("mousemove.liveTile", data.bounceMethods.bounceMove); 1167 } 1168 } 1169 var bClass = "bounce-" + hit.name; 1170 $tile.addClass(bClass); 1171 data.bounceMethods.down = bClass; 1172 data.bounceMethods.downPcss = helperMethods.appendStyleProperties({}, ['perspective-origin'], [data.bounceMethods.eventPos.x + "px " + data.bounceMethods.eventPos.y + "px"]); 1173 data.$tileParent.css(data.bounceMethods.downPcss); 1174 } 1175 }, 1176 bounceUp: function () { 1177 if (data.bounceMethods.down != "no") { 1178 data.bounceMethods.unBounce(); 1179 if (window.navigator.msPointerEnabled) { 1180 document.removeEventListener('MSPointerUp', data.bounceMethods.bounceUp, false); 1181 $tile[0].removeEventListener('MSPointerUp', data.bounceMethods.bounceUp, false); 1182 document.removeEventListener('MSPointerCancel', data.bounceMethods.bounceUp, false); 1183 if (data.bounceFollowsMove) 1184 $tile[0].removeEventListener('MSPointerMove', data.bounceMethods.bounceMove, false); 1185 1186 } else 1187 $(document).unbind("mouseup.liveTile, touchend.liveTile, touchcancel.liveTile, dragstart.liveTile", data.bounceMethods.bounceUp); 1188 if (data.bounceFollowsMove) { 1189 $tile.unbind("touchmove.liveTile", data.bounceMethods.bounceMove); 1190 $tile.unbind("mousemove.liveTile", data.bounceMethods.bounceMove); 1191 } 1192 } 1193 }, 1194 bounceMove: function (e) { 1195 if (data.bounceMethods.down != "no") { 1196 var point = e.originalEvent && e.originalEvent.touches ? e.originalEvent.touches[0] : e, 1197 x = Math.abs(point.pageX - data.bounceMethods.pointPos.x), 1198 y = Math.abs(point.pageY - data.bounceMethods.pointPos.y); 1199 if (x > data.bounceMethods.threshold || y > data.bounceMethods.threshold) { 1200 var bounceClass = data.bounceMethods.down; 1201 data.bounceMethods.bounceDown(e); 1202 if (bounceClass != data.bounceMethods.down) 1203 $tile.removeClass(bounceClass); 1204 } 1205 } 1206 }, 1207 unBounce: function () { 1208 $tile.removeClass(data.bounceMethods.down); 1209 if (typeof (data.bounceMethods.downPcss) == "object") { 1210 var names = ['perspective-origin', 'perspective-origin-x', 'perspective-origin-y'], 1211 vals = ['', '', '']; 1212 data.bounceMethods.downPcss = helperMethods.appendStyleProperties({}, names, vals); 1213 // let the bounce finish and then strip out the perspective 1214 window.setTimeout(function () { 1215 data.$tileParent.css(data.bounceMethods.downPcss); 1216 }, 200); 1217 } 1218 data.bounceMethods.down = "no"; 1219 data.bounceMethods.inTilePos = data.bounceMethods.zeroPos; 1220 data.bounceMethods.eventPos = data.bounceMethods.zeroPos; 1221 } 1222 }; 1223 // IE 10+ 1224 if (window.navigator.msPointerEnabled) {// touch only -> // && window.navigator.msMaxTouchPoints) { 1225 $tile[0].addEventListener('MSPointerDown', data.bounceMethods.bounceDown, false); 1226 } else if (metrojs.capabilities.canTouch) { 1227 // everybody else 1228 $tile.bind("touchstart.liveTile", data.bounceMethods.bounceDown);
1229 1230 } else { 1231 $tile.bind("mousedown.liveTile", data.bounceMethods.bounceDown); 1232 } 1233 })(); 1234 } 1235 }, 1236 unbindMsBounce: function ($tile, data) { 1237 if (data.bounce && window.navigator.msPointerEnabled) {// touch only -> // && window.navigator.msMaxTouchPoints) { 1238 $tile[0].removeEventListener('MSPointerDown', data.bounceMethods.bounceDown, false); 1239 $tile[0].removeEventListener('MSPointerCancel', data.bounceMethods.bounceUp, false); 1240 $tile[0].removeEventListener('MSPointerOut', data.bounceMethods.bounceUp, false); 1241 //$tile[0].removeEventListener('MSPointerMove', data.bounceMethods.bounceMove, false); 1242 } 1243 }, 1244 bindLink: function ($tile, data) { 1245 if (data.link.length > 0) { 1246 $tile.css({ cursor: 'pointer' }).bind("click.liveTile", function (e) { 1247 if (e.target.tagName == "A" && !$(e).is(".live-tile,.slide,.flip")) 1248 return; 1249 if (data.newWindow) 1250 window.open(data.link); 1251 else 1252 window.location = data.link; 1253 }); 1254 } 1255 }, 1256 runContenModules: function (data, $front, $back, index) { 1257 for (var i = 0; i < data.contentModules.length; i++) { 1258 var currentModule = data.contentModules[i]; 1259 if (typeof (currentModule.action) == "function") 1260 currentModule.action(data, $front, $back, index); 1261 } 1262 }, 1263 fade: function ($tile, count, data) { 1264 var tdata = typeof (data) === "object" ? data : $tile.data("LiveTile"), 1265 resumeTimer = function () { 1266 // if the tile should run again start the timer back with the current delay 1267 if (tdata.timer.repeatCount > 0 || tdata.timer.repeatCount == -1) { 1268 if (tdata.timer.count != tdata.timer.repeatCount) { 1269 tdata.timer.start(tdata.delay); 1270 } 1271 } 1272 }; 1273 if (typeof (tdata) === "undefined") { 1274 throwError($.fn.liveTile.defaults.rebindMessage); 1275 return; 1276 } 1277 if (tdata.faces.$front.is(":animated")) 1278 return; 1279 tdata.timer.pause(); 1280 var loopCount = tdata.loopCount + 1; 1281 tdata.isReversed = loopCount % 2 === 0; // the count starts at 1 1282 var start = tdata.animationStarting.call($tile[0], tdata, tdata.faces.$front, tdata.faces.$back); 1283 if (typeof (start) != "undefined" && start == false) { 1284 resumeTimer(); 1285 return; 1286 } 1287 tdata.loopCount = loopCount; 1288 var faded = function () { 1289 resumeTimer(); 1290 // run content modules and animationComplete callback 1291 privMethods.runContenModules(tdata, tdata.faces.$front, tdata.faces.$back); 1292 tdata.animationComplete.call($tile[0], tdata, tdata.faces.$front, tdata.faces.$back); 1293 }; 1294 if (tdata.isReversed) 1295 tdata.faces.$front.fadeIn(tdata.speed, tdata.noHaTransFunc, faded); 1296 else 1297 tdata.faces.$front.fadeOut(tdata.speed, tdata.noHaTransFunc, faded); 1298 }, 1299 slide: function ($tile, count, data, stopIndex, callback) { 1300 var tdata = typeof (data) === "object" ? data : $tile.data("LiveTile"), 1301 aniData = $tile.data("metrojs.tile"); 1302 if (typeof (tdata) === "undefined") { 1303 throwError($.fn.liveTile.defaults.rebindMessage); 1304 return; 1305 } 1306 if (aniData.animating == true || $tile.is(":animated")) { 1307 tdata = null; 1308 aniData = null; 1309 return; 1310 } 1311 var resumeTimer = function () { 1312 // if the tile should run again start the timer back with the current delay 1313 if (tdata.timer.repeatCount > 0 || tdata.timer.repeatCount == -1) { 1314 if (tdata.timer.count != tdata.timer.repeatCount) { 1315 tdata.timer.start(tdata.delay); 1316 } 1317 } 1318 }; 1319 if (tdata.mode !== "carousel") { 1320 tdata.isReversed = tdata.currentIndex % 2 !== 0; // the count starts at 1 1321 // carousel mode maintains its own timer 1322 tdata.timer.pause(); 1323 var start = tdata.animationStarting.call($tile[0], tdata, tdata.faces.$front, tdata.faces.$back); 1324 if (typeof (start) != "undefined" && start == false) { 1325 resumeTimer(); 1326 return; 1327 } 1328 tdata.loopCount = tdata.loopCount + 1; 1329 } else { 1330 // in carousel mode the face that just left the stage is always the $back 1331 tdata.isReversed = true; 1332 } 1333 // get temp values passed in from data methods 1334 var direction; 1335 if (typeof (tdata.tempValues.direction) === "string" && tdata.tempValues.direction.length > 0) 1336 direction = tdata.tempValues.direction; 1337 else 1338 direction = tdata.direction; 1339 tdata.tempValues.direction = null; 1340 var css = {}, 1341 cssback = {}, 1342 // the stop index is overridden in carousel mode 1343 stopIdx = typeof (stopIndex) === "undefined" ? tdata.currentIndex : stopIndex, 1344 stop = $.trim(tdata.stops[Math.min(stopIdx, tdata.stops.length - 1)]), 1345 pxIdx = stop.indexOf('px'), 1346 offset = 0, 1347 amount = 0, 1348 metric = (direction === "vertical") ? tdata.height : tdata.width, 1349 tProp = (direction === "vertical") ? "top" : "left", 1350 stack = tdata.stack == true; 1351 // when the slide is complete increment the index or call the callback 1352 var slideFinished = function () { 1353 if (typeof (callback) === "undefined") { 1354 tdata.currentIndex = tdata.currentIndex + 1; 1355 if (tdata.currentIndex > tdata.stops.length - 1) { 1356 tdata.currentIndex = 0; 1357 } 1358 } else { 1359 callback(); 1360 } 1361 if (tdata.mode != "carousel") { 1362 resumeTimer(); 1363 } 1364 // run content modules and animationComplete callback 1365 privMethods.runContenModules(tdata, tdata.faces.$front, tdata.faces.$back, tdata.currentIndex); 1366 tdata.animationComplete.call($tile[0], tdata, tdata.faces.$front, tdata.faces.$back); 1367 tdata = null; 1368 aniData = null; 1369 }; 1370 if (pxIdx > 0) { 1371 amount = parseInt(stop.substring(0, pxIdx), 10); 1372 offset = (amount - metric) + 'px'; 1373 } else { 1374 //is a percentage 1375 amount = parseInt(stop.replace('%', ''), 10); 1376 offset = (amount - 100) + '%'; 1377 } 1378 // hardware accelerated :) 1379 if (metrojs.capabilities.canTransition && tdata.useHardwareAccel) { 1380 if (typeof (aniData.animating) !== "undefined" && aniData.animating == true) 1381 return; 1382 aniData.animating = true; 1383 var props = ['transition-property', 'transition-duration', 'transition-timing-function'], 1384 vals = [tdata.useTranslate ? "transform" : tProp, tdata.speed + 'ms', tdata.haTransFunc]; 1385 vals[helperMethods.browserPrefix + 'transition-property'] = helperMethods.browserPrefix + "transform"; 1386 css = helperMethods.appendStyleProperties(css, props, vals); 1387 cssback = helperMethods.appendStyleProperties(cssback, props, vals); 1388 var vertical = direction === "vertical", 1389 prop = vertical ? "top" : "left", 1390 translateTo; 1391 if (!tdata.useTranslate) { 1392 css[prop] = stop; 1393 if (stack) 1394 cssback[prop] = offset; 1395 } else { 1396 translateTo = vertical ? "translate(0%, " + stop + ")" : "translate(" + stop + ", 0%)"; 1397 css = helperMethods.appendStyleProperties(css, ['transform'], [translateTo + "translateZ(0)"]); 1398 if (stack) { 1399 translateTo = vertical ? "translate(0%, " + offset + ")" : "translate(" + offset + ", 0%)"; 1400 cssback = helperMethods.appendStyleProperties(cssback, ['transform'], [translateTo + "translateZ(0)"]); 1401 } 1402 } 1403 tdata.faces.$front.css(css); 1404 if (stack) 1405 tdata.faces.$back.css(cssback); 1406 window.clearTimeout(tdata.completeTimeout); 1407 tdata.completeTimeout = window.setTimeout(function () { 1408 aniData.animating = false; 1409 slideFinished(); 1410 }, tdata.speed); 1411 } else { 1412 // not hardware accelerated :( 1413 css[tProp] = stop; 1414 cssback[tProp] = offset; 1415 aniData.animating = true; 1416 var $front = tdata.faces.$front.stop(), 1417 $back = tdata.faces.$back.stop(); 1418 $front.animate(css, tdata.speed, tdata.noHaTransFunc, function () { 1419 aniData.animating = false; 1420 slideFinished(); 1421 }); 1422 // change the css value to the offset 1423 if (stack) 1424 $back.animate(cssback, tdata.speed, tdata.noHaTransFunc, function () { });
1425 } 1426 }, 1427 carousel: function ($tile, count) { 1428 var tdata = $tile.data("LiveTile"); 1429 if (typeof (tdata) === "undefined") { 1430 throwError($.fn.liveTile.defaults.rebindMessage); 1431 return; 1432 } 1433 // dont update css or call slide if animated or if there's only one face 1434 var aniData = $tile.data("metrojs.tile"); 1435 if (aniData.animating == true || tdata.faces.$listTiles.length <= 1) { 1436 aniData = null; 1437 return; 1438 } 1439 var resumeTimer = function () { 1440 if (tdata.timer.repeatCount > 0 || tdata.timer.repeatCount == -1) { 1441 if (tdata.timer.count != tdata.timer.repeatCount) { 1442 tdata.timer.start(tdata.delay); 1443 } 1444 } 1445 }; 1446 // pause the timer and use a per slide delay 1447 tdata.timer.pause(); 1448 var $cur = tdata.faces.$listTiles.filter(".active"), 1449 idx = tdata.faces.$listTiles.index($cur), 1450 goTo = tdata.currentIndex, 1451 eq = goTo != idx ? goTo : idx, 1452 nxtIdx = eq + 1 >= tdata.faces.$listTiles.length ? 0 : eq + 1, 1453 sdata = tdata.listData[nxtIdx]; 1454 if (idx == nxtIdx) { 1455 aniData = null; 1456 $cur = null; 1457 return; 1458 } 1459 // get temp values passed in from data methods 1460 var animationDirection; 1461 if (typeof (tdata.tempValues.animationDirection) === "string" && tdata.tempValues.animationDirection.length > 0) 1462 animationDirection = tdata.tempValues.animationDirection; 1463 else if (typeof (sdata.animationDirection) === "string" && sdata.animationDirection.length > 0) { 1464 animationDirection = sdata.animationDirection; 1465 } else 1466 animationDirection = tdata.animationDirection; 1467 // the temp value for animation direction is not used in slide so i'm setting it to null 1468 tdata.tempValues.animationDirection = null; 1469 var direction; 1470 if (typeof (tdata.tempValues.direction) === "string" && tdata.tempValues.direction.length > 0) { 1471 direction = tdata.tempValues.direction; 1472 } else if (typeof (sdata.direction) === "string" && sdata.direction.length > 0) { 1473 direction = sdata.direction; 1474 tdata.tempValues.direction = direction; 1475 } else { 1476 direction = tdata.direction; 1477 } 1478 var $nxt = tdata.faces.$listTiles.eq(nxtIdx), 1479 start = tdata.animationStarting.call($tile[0], tdata, $cur, $nxt); 1480 if (typeof (start) != "undefined" && start == false) { 1481 resumeTimer(); 1482 return; 1483 } 1484 tdata.loopCount = tdata.loopCount + 1; 1485 var nxtCss = helperMethods.appendStyleProperties({}, ['transition-duration'], ['0s']), 1486 vertical = direction === "vertical", 1487 translateTo; 1488 if (animationDirection === "backward") { 1489 if (!tdata.useTranslate || !metrojs.capabilities.canTransition) { 1490 if (vertical) { 1491 nxtCss.top = "-100%"; 1492 nxtCss.left = "0%"; 1493 } else { 1494 nxtCss.top = "0%"; 1495 nxtCss.left = "-100%"; 1496 } 1497 tdata.stops = ['100%']; 1498 } else { 1499 translateTo = vertical ? "translate(0%, -100%)" : "translate(-100%, 0%)"; 1500 nxtCss = helperMethods.appendStyleProperties(nxtCss, ["transform"], [translateTo + " translateZ(0)"]); 1501 tdata.stops = ['100%']; 1502 } 1503 tdata.faces.$front = $cur; 1504 tdata.faces.$back = $nxt; 1505 1506 } else { 1507 if (!tdata.useTranslate || !metrojs.capabilities.canTransition) { 1508 if (vertical) { 1509 nxtCss.top = "100%"; 1510 nxtCss.left = "0%"; 1511 } else { 1512 nxtCss.top = "0%"; 1513 nxtCss.left = "100%"; 1514 } 1515 } else { 1516 translateTo = vertical ? "translate(0%, 100%)" : "translate(100%, 0%)"; 1517 nxtCss = helperMethods.appendStyleProperties(nxtCss, ["transform"], [translateTo + " translateZ(0)"]); 1518 } 1519 tdata.faces.$front = $nxt; 1520 tdata.faces.$back = $cur; 1521 tdata.stops = ['0%']; 1522 } 1523 $nxt.css(nxtCss); 1524 // the timeout wrapper gives the css call above enough time to finish in case we dynamically set the direction 1525 window.setTimeout(function () { 1526 $cur.removeClass("active"); 1527 $nxt.addClass("active"); 1528 privMethods.slide($tile, count, tdata, 0, function () { 1529 tdata.currentIndex = nxtIdx; 1530 aniData = null; 1531 $cur = null; 1532 $nxt = null; 1533 resumeTimer(); 1534 }); 1535 }, 150); 1536 1537 }, 1538 flip: function ($tile, count, data, callback) { 1539 var aniData = $tile.data("metrojs.tile"); 1540 if (typeof (aniData) !== "undefined" && aniData.animating == true) { 1541 aniData = null; 1542 return; 1543 } 1544 var tdata = typeof (data) === "object" ? data : $tile.data("LiveTile"); 1545 if (typeof (tdata) === "undefined") { 1546 throwError($.fn.liveTile.defaults.rebindMessage); 1547 return; 1548 } 1549 var $front, $back, direction, deg, rotateDir, css, 1550 raiseEvt = typeof (callback) === "undefined", 1551 index = 0, 1552 isReversed, // the count starts at 1 1553 resumeTimer = function () { 1554 // if the tile should run again start the timer back with the current delay 1555 if (tdata.timer.repeatCount > 0 || tdata.timer.repeatCount == -1) { 1556 if (tdata.timer.count != tdata.timer.repeatCount) { 1557 tdata.timer.start(tdata.delay); 1558 } 1559 } 1560 }; 1561 // the timer is only paused if animationComplete is fired 1562 if (raiseEvt) { 1563 tdata.timer.pause(); 1564 var loopCount = tdata.loopCount + 1; 1565 isReversed = loopCount % 2 === 0; 1566 tdata.isReversed = isReversed; 1567 $front = tdata.faces.$front; 1568 $back = tdata.faces.$back; 1569 var args = isReversed ? [tdata, $back, $front] : [tdata, $front, $back]; 1570 var start = tdata.animationStarting.apply($tile[0], args); 1571 if (typeof (start) != "undefined" && start == false) { 1572 resumeTimer(); 1573 return; 1574 } 1575 direction = tdata.direction; 1576 height = tdata.height; 1577 width = tdata.width; 1578 margin = tdata.margin; 1579 tdata.loopCount = loopCount; 1580 } else { 1581 isReversed = count % 2 === 0; 1582 index = aniData.index; 1583 $front = tdata.listData[index].faces.$front; 1584 $back = tdata.listData[index].faces.$back; 1585 tdata.listData[index].isReversed = isReversed; 1586 direction = tdata.listData[index].direction; 1587 height = tdata.listData[index].height; 1588 width = tdata.listData[index].width; 1589 margin = tdata.listData[index].margin; 1590 } 1591 1592 if (metrojs.capabilities.canFlip3d && tdata.useHardwareAccel) { // Hardware accelerated :) 1593 deg = !isReversed ? "180deg" : "360deg"; 1594 rotateDir = direction === "vertical" ? "rotateX(" + deg + ")" : "rotateY(" + deg + ")"; 1595 css = helperMethods.appendStyleProperties({}, ["transform", "transition"], [rotateDir, "all " + tdata.speed + "ms " + tdata.haTransFunc + " 0s"]); 1596 var bDeg = !isReversed ? "360deg" : "540deg"; 1597 var bRotateDir = direction === "vertical" ? "rotateX(" + bDeg + ")" : "rotateY(" + bDeg + ")"; 1598 var bCss = helperMethods.appendStyleProperties({}, ["transform", "transition"], [bRotateDir, "all " + tdata.speed + "ms " + tdata.haTransFunc + " 0s"]); 1599 $front.css(css); 1600 $back.css(bCss); 1601 1602 var action = function () { 1603 aniData.animating = false; 1604 var resetDir, newCss; 1605 if (!isReversed) { 1606 privMethods.runContenModules(tdata, $back, $front, index); 1607 if (raiseEvt) { 1608 resumeTimer(); 1609 tdata.animationComplete.call($tile[0], tdata, $back, $front); 1610 } else 1611 callback(tdata, $back, $front); 1612 } else { 1613 resetDir = direction === "vertical" ? "rotateX(0deg)" : "rotateY(0deg)"; 1614 newCss = helperMethods.appendStyleProperties({}, ["transform", "transition"], [resetDir, "all 0s " + tdata.haTransFunc + " 0s"]); 1615 $front.css(newCss); 1616 //call content modules 1617 privMethods.runContenModules(tdata, $front, $back, index); 1618 if (raiseEvt) { 1619 resumeTimer(); 1620 tdata.animationComplete.call($tile[0], tdata, $front, $back); 1621 } else 1622 callback(tdata, $front, $back); 1623 $front = null; 1624 $back = null; 1625 tdata = null; 1626 aniData = null; 1627 } 1628 }; 1629 if (tdata.mode === "flip-list") { 1630 window.clearTimeout(tdata.listData[index].completeTimeout); 1631 tdata.listData[index].completeTimeout = window.setTimeout(action, tdata.speed); 1632 } else { 1633 window.clearTimeout(tdata.completeTimeout); 1634 tdata.completeTimeout = window.setTimeout(action, tdata.speed); 1635 } 1636 } else { // not Hardware accelerated :( 1637 var speed = tdata.speed / 2; 1638 var hideCss = (direction === "vertical") ? 1639 { height: '0px', width: '100%', marginTop: margin + 'px', opacity: tdata.noHAflipOpacity } :
1640 { height: '100%', width: '0px', marginLeft: margin + 'px', opacity: tdata.noHAflipOpacity }; 1641 var showCss = (direction === "vertical") ? 1642 { height: '100%', width: '100%', marginTop: '0px', opacity: '1' } : 1643 { height: '100%', width: '100%', marginLeft: '0px', opacity: '1' }; 1644 var noHaAction; 1645 if (!isReversed) { 1646 aniData.animating = true; 1647 $front.stop().animate(hideCss, { duration: speed }); 1648 noHaAction = function () { 1649 aniData.animating = false; 1650 $back.stop().animate(showCss, { 1651 duration: speed, 1652 complete: function () { 1653 privMethods.runContenModules(tdata, $back, $front, index); 1654 if (raiseEvt) { 1655 resumeTimer(); 1656 tdata.animationComplete.call($tile[0], tdata, $back, $front); 1657 } else 1658 callback(tdata, $back, $front); 1659 $front = null; 1660 $back = null; 1661 tdata = null; 1662 aniData = null; 1663 } 1664 }); 1665 }; 1666 if (tdata.mode === "flip-list") { 1667 window.clearTimeout(tdata.listData[aniData.index].completeTimeout); 1668 tdata.listData[aniData.index].completeTimeout = window.setTimeout(noHaAction, speed); 1669 } else { 1670 window.clearTimeout(tdata.completeTimeout); 1671 tdata.completeTimeout = window.setTimeout(noHaAction, speed); 1672 } 1673 } else { 1674 aniData.animating = true; 1675 $back.stop().animate(hideCss, { duration: speed }); 1676 noHaAction = function () { 1677 aniData.animating = false; 1678 $front.stop().animate(showCss, { 1679 duration: speed, 1680 complete: function () { 1681 privMethods.runContenModules(tdata, $front, $back, index); 1682 if (raiseEvt) { 1683 resumeTimer(); 1684 tdata.animationComplete.call($tile[0], tdata, $front, $back); 1685 } else 1686 callback(tdata, $front, $back); 1687 aniData = null; 1688 $front = null; 1689 $back = null; 1690 } 1691 }); 1692 }; 1693 if (tdata.mode === "flip-list") { 1694 window.clearTimeout(tdata.listData[aniData.index].completeTimeout); 1695 tdata.listData[aniData.index].completeTimeout = window.setTimeout(noHaAction, speed); 1696 } else { 1697 window.clearTimeout(tdata.completeTimeout); 1698 tdata.completeTimeout = window.setTimeout(noHaAction, speed); 1699 } 1700 } 1701 1702 } 1703 }, 1704 flipList: function ($tile, count) { 1705 var tdata = $tile.data("LiveTile"), 1706 maxDelay = tdata.speed, 1707 triggered = false, 1708 resumeTimer = function () { 1709 if (tdata.timer.repeatCount > 0 || tdata.timer.repeatCount == -1) { 1710 if (tdata.timer.count != tdata.timer.repeatCount) { 1711 tdata.timer.start(tdata.delay); 1712 } 1713 } 1714 }; 1715 if (typeof (tdata) === "undefined") { 1716 throwError($.fn.liveTile.defaults.rebindMessage); 1717 return; 1718 } 1719 tdata.timer.pause(); 1720 var start = tdata.animationStarting.call($tile[0], tdata, null, null); 1721 if (typeof (start) != "undefined" && start == false) { 1722 resumeTimer(); 1723 return; 1724 } 1725 tdata.loopCount = tdata.loopCount + 1; 1726 tdata.faces.$listTiles.each(function (idx, ele) { 1727 var $li = $(ele), 1728 ldata = $li.data("metrojs.tile"), 1729 tDelay = tdata.triggerDelay(idx), 1730 triggerDelay = tdata.speed + Math.max(tDelay, 0), 1731 trigger = tdata.alwaysTrigger; 1732 if (!trigger) 1733 trigger = (Math.random() * 351) > 150 ? true : false; 1734 if (trigger) { 1735 triggered = true; 1736 maxDelay = Math.max(triggerDelay + tdata.speed, maxDelay); 1737 window.clearTimeout(ldata.flCompleteTimeout); 1738 ldata.flCompleteTimeout = window.setTimeout(function () { 1739 // call the flip method with the merged data, but dont fire animationComplete 1740 privMethods.flip($li, ldata.count, tdata, function (data) { 1741 ldata.count++; 1742 if (ldata.count >= MAX_LOOP_COUNT) 1743 ldata.count = 1; 1744 $li = null; 1745 ldata = null; 1746 }); 1747 }, triggerDelay); 1748 } 1749 }); 1750 if (triggered) { 1751 window.clearTimeout(tdata.flCompleteTimeout); 1752 tdata.flCompleteTimeout = window.setTimeout(function () { 1753 privMethods.runContenModules(tdata, null, null, -1); 1754 tdata.animationComplete.call($tile[0], tdata, null, null); 1755 resumeTimer(); 1756 }, maxDelay + tdata.speed); // add some padding to make sure the final callback finished 1757 1758 } 1759 } 1760}; 1761 1762 1763// methods that can be called more universally 1764var helperMethods = { 1765 stylePrefixes: 'Webkit Moz O ms Khtml '.split(' '), 1766 domPrefixes: '-webkit- -moz- -o- -ms- -khtml- '.split(' '), 1767 browserPrefix: null, 1768 // a method to append css3 properties for each browser 1769 // note: values are identical for each property 1770 appendStyleProperties: function (obj, names, values) { 1771 for (var i = 0; i <= names.length - 1; i++) { 1772 obj[$.trim(this.browserPrefix + names[i])] = values[i]; 1773 obj[$.trim(names[i])] = values[i]; 1774 } 1775 return obj;
1776 }, 1777 applyStyleValue: function (obj, name, value) { 1778 obj[$.trim(this.browserPrefix + name)] = value; 1779 obj[name] = value; 1780 return obj; 1781 }, 1782 getBrowserPrefix: function () { 1783 if (this.browserPrefix == null) { 1784 var prefix = ""; 1785 for (var i = 0; i <= this.domPrefixes.length - 1; i++) { 1786 if (typeof (document.body.style[this.domPrefixes[i] + "transform"]) != "undefined") 1787 prefix = this.domPrefixes[i]; 1788 } 1789 return this.browserPrefix = prefix; 1790 } 1791 return this.browserPrefix; 1792 }, 1793 //a shuffle method to provide more randomness than sort 1794 //credit: http://javascript.about.com/library/blshuffle.htm 1795 //note: avoiding prototype for sharepoint compatability 1796 shuffleArray: function (array) { 1797 var s = []; 1798 while (array.length) s.push(array.splice(Math.random() * array.length, 1)); 1799 while (s.length) array.push(s.pop()); 1800 return array; 1801 } 1802}; 1803 1804var defaultModules = { 1805 moduleName: 'custom', 1806 customSwap: { 1807 data: { 1808 customDoSwapFront: function () { return false; }, 1809 customDoSwapBack: function () { return false; }, 1810 customGetContent: function (tdata, $front, $back, index) { return null; } 1811 }, 1812 initData: function (tdata, $ele) { 1813 var swapData = {}; 1814 swapData.doSwapFront = $.inArray('custom', tdata.swapFront) > -1 && tdata.customDoSwapFront(); 1815 swapData.doSwapBack = $.inArray('custom', tdata.swapBack) > -1 && tdata.customDoSwapBack(); 1816 if (typeof (tdata.customSwap) !== "undefined") 1817 tdata.customSwap = $.extend(swapData, tdata.customSwap); 1818 else 1819 tdata.customSwap = swapData; 1820 }, 1821 action: function (tdata, $front, $back, index) { 1822 1823 } 1824 }, 1825 htmlSwap: { 1826 moduleName: 'html', 1827 data: { // public data for the swap module 1828 frontContent: [], // a list of html to use for the front 1829 frontIsRandom: true, // should html be chosen at random or in order 1830 frontIsInGrid: false, // only chooses one item for each iteration in flip-list 1831 backContent: [], // a list of html to use for the back 1832 backIsRandom: true, // should html be chosen at random or in order 1833 backIsInGrid: false // only chooses one item for each iteration in flip-list 1834 }, 1835 initData: function (tdata, $ele) { 1836 var swapData = { // private data for the swap module 1837 backBag: [], 1838 backIndex: 0, 1839 backStaticIndex: 0, 1840 backStaticRndm: -1, 1841 prevBackIndex: -1, 1842 frontBag: [], 1843 frontIndex: 0, 1844 frontStaticIndex: 0, 1845 frontStaticRndm: -1, 1846 prevFrontIndex: -1 1847 }; 1848 if (!tdata.ignoreDataAttributes) { 1849 swapData.frontIsRandom = privMethods.getDataOrDefault($ele, "front-israndom", tdata.frontIsRandom); 1850 swapData.frontIsInGrid = privMethods.getDataOrDefault($ele, "front-isingrid", tdata.frontIsInGrid); 1851 swapData.backIsRandom = privMethods.getDataOrDefault($ele, "back-israndom", tdata.backIsRandom); 1852 swapData.backIsInGrid = privMethods.getDataOrDefault($ele, "back-isingrid", tdata.backIsInGrid); 1853 } else { 1854 swapData.frontIsRandom = tdata.frontIsRandom; 1855 swapData.frontIsInGrid = tdata.frontIsInGrid; 1856 swapData.backIsRandom = tdata.backIsRandom; 1857 swapData.backIsInGrid = tdata.backIsInGrid; 1858 } 1859 swapData.doSwapFront = $.inArray('html', tdata.swapFront) > -1 && (tdata.frontContent instanceof Array) && tdata.frontContent.length > 0; 1860 swapData.doSwapBack = $.inArray('html', tdata.swapBack) > -1 && (tdata.backContent instanceof Array) && tdata.backContent.length > 0; 1861 if (typeof (tdata.htmlSwap) !== "undefined") 1862 tdata.htmlSwap = $.extend(swapData, tdata.htmlSwap); 1863 else 1864 tdata.htmlSwap = swapData; 1865 if (tdata.htmlSwap.doSwapFront) { 1866 tdata.htmlSwap.frontBag = this.prepBag(tdata.htmlSwap.frontBag, tdata.frontContent, tdata.htmlSwap.prevFrontIndex); 1867 tdata.htmlSwap.frontStaticRndm = tdata.htmlSwap.frontBag.pop(); 1868 } 1869 if (tdata.htmlSwap.doSwapBack) { 1870 tdata.htmlSwap.backBag = this.prepBag(tdata.htmlSwap.backBag, tdata.backContent, tdata.htmlSwap.prevBackIndex); 1871 tdata.htmlSwap.backStaticRndm = tdata.htmlSwap.backBag.pop();
1872 } 1873 }, 1874 prepBag: function (bag, content, prevIdx) { 1875 bag = bag || []; 1876 var bagCount = 0; 1877 for (var i = 0; i < content.length; i++) { 1878 //make sure there's not an immediate repeat 1879 if (i != prevIdx || bag.length === 1) { 1880 bag[bagCount] = i; 1881 bagCount++; 1882 } 1883 } 1884 return helperMethods.shuffleArray(bag); 1885 }, 1886 getFrontSwapIndex: function (tdata) { 1887 var idx = 0; 1888 if (!tdata.htmlSwap.frontIsRandom) { 1889 idx = tdata.htmlSwap.frontIsInGrid ? tdata.htmlSwap.frontStaticIndex : tdata.htmlSwap.frontIndex; 1890 } else { 1891 if (tdata.htmlSwap.frontBag.length === 0) { 1892 tdata.htmlSwap.frontBag = this.prepBag(tdata.htmlSwap.frontBag, tdata.frontContent, tdata.htmlSwap.prevFrontIndex); 1893 } 1894 if (tdata.htmlSwap.frontIsInGrid) { 1895 idx = tdata.htmlSwap.frontStaticRndm; 1896 } else { 1897 idx = tdata.htmlSwap.frontBag.pop(); 1898 } 1899 } 1900 return idx; 1901 }, 1902 getBackSwapIndex: function (tdata) { 1903 var idx = 0; 1904 if (!tdata.htmlSwap.backIsRandom) { 1905 idx = tdata.htmlSwap.backIsInGrid ? tdata.htmlSwap.backStaticIndex : tdata.htmlSwap.backIndex; 1906 } else { 1907 if (tdata.htmlSwap.backBag.length === 0) { 1908 tdata.htmlSwap.backBag = this.prepBag(tdata.htmlSwap.backBag, tdata.backContent, tdata.htmlSwap.prevBackIndex); 1909 } 1910 if (tdata.htmlSwap.backIsInGrid) { 1911 idx = tdata.htmlSwap.backStaticRndm; 1912 } else { 1913 idx = tdata.htmlSwap.backBag.pop(); 1914 } 1915 } 1916 return idx; 1917 }, 1918 action: function (tdata, $front, $back, index) { 1919 if (!tdata.htmlSwap.doSwapFront && !tdata.htmlSwap.doSwapBack) 1920 return; 1921 var isList = tdata.mode === "flip-list"; 1922 var swapIndex = 0; 1923 var isReversed = isList ? tdata.listData[Math.max(index, 0)].isReversed : tdata.isReversed; 1924 if (isList && index == -1) { 1925 // flip list completed 1926 if (!isReversed) { 1927 if (tdata.htmlSwap.doSwapFront) { 1928 // update the random value for grid mode 1929 if (tdata.htmlSwap.frontBag.length === 0) 1930 tdata.htmlSwap.frontBag = this.prepBag(tdata.htmlSwap.frontBag, tdata.frontContent, tdata.htmlSwap.frontStaticRndm); 1931 tdata.htmlSwap.frontStaticRndm = tdata.htmlSwap.frontBag.pop(); 1932 // update the static index 1933 tdata.htmlSwap.frontStaticIndex++; 1934 if (tdata.htmlSwap.frontStaticIndex >= tdata.frontContent.length) 1935 tdata.htmlSwap.frontStaticIndex = 0; 1936 } 1937 } else { 1938 if (tdata.htmlSwap.doSwapBack) { 1939 // update the random value for grid mode 1940 if (tdata.htmlSwap.backBag.length === 0) 1941 tdata.htmlSwap.backBag = this.prepBag(tdata.htmlSwap.backBag, tdata.backContent, tdata.htmlSwap.backStaticRndm); 1942 tdata.htmlSwap.backStaticRndm = tdata.htmlSwap.backBag.pop(); 1943 // update the static index 1944 tdata.htmlSwap.backStaticIndex++; 1945 if (tdata.htmlSwap.backStaticIndex >= tdata.backContent.length) 1946 tdata.htmlSwap.backStaticIndex = 0; 1947 } 1948 } 1949 return; 1950 } 1951 if (!isReversed) { 1952 if (!tdata.htmlSwap.doSwapFront) 1953 return; 1954 swapIndex = this.getFrontSwapIndex(tdata); 1955 tdata.htmlSwap.prevFrontIndex = swapIndex; 1956 if (tdata.mode === "slide") { 1957 if (!tdata.startNow) 1958 $front.html(tdata.frontContent[swapIndex]); 1959 else 1960 $back.html(tdata.frontContent[swapIndex]); 1961 } else 1962 $back.html(tdata.frontContent[swapIndex]); 1963 // increment the front index to get the next item from the list 1964 tdata.htmlSwap.frontIndex++; 1965 if (tdata.htmlSwap.frontIndex >= tdata.frontContent.length) 1966 tdata.htmlSwap.frontIndex = 0; 1967 if (!isList) { 1968 // increment the static index if we're not in list mode 1969 tdata.htmlSwap.frontStaticIndex++; 1970 if (tdata.htmlSwap.frontStaticIndex >= tdata.frontContent.length) 1971 tdata.htmlSwap.frontStaticIndex = 0; 1972 } else { 1973 // flip list 1974 } 1975 } else { 1976 if (!tdata.htmlSwap.doSwapBack) 1977 return; 1978 swapIndex = this.getBackSwapIndex(tdata); 1979 tdata.htmlSwap.prevBackIndex = swapIndex; 1980 $back.html(tdata.backContent[tdata.htmlSwap.backIndex]); 1981 tdata.htmlSwap.backIndex++; 1982 if (tdata.htmlSwap.backIndex >= tdata.backContent.length) 1983 tdata.htmlSwap.backIndex = 0; 1984 if (!isList) { 1985 tdata.htmlSwap.backStaticIndex++; 1986 if (tdata.htmlSwap.backStaticIndex >= tdata.backContent.length) 1987 tdata.htmlSwap.backStaticIndex = 0; 1988 } else { 1989 // flip list 1990 } 1991 } 1992 } 1993 }, 1994 imageSwap: { 1995 moduleName: 'image', 1996 data: { 1997 preloadImages: false, 1998 imageCssSelector: '>img,>a>img', // the selector used to choose a an image to apply a src or background to
1999 fadeSwap: false, // fade the image before swapping 2000 frontImages: [], // a list of images to use for the front 2001 frontIsRandom: true, // should images be chosen at random or in order 2002 frontIsBackgroundImage: false, // set the src attribute or css background-image property 2003 frontIsInGrid: false, // only chooses one item for each iteration in flip-list 2004 backImages: null, // a list of images to use for the back 2005 backIsRandom: true, // should images be chosen at random or in order 2006 backIsBackgroundImage: false, // set the src attribute or css background-image property 2007 backIsInGrid: false // only chooses one item for each iteration in flip-list 2008 }, 2009 initData: function (tdata, $ele) { 2010 var swapData = { 2011 backBag: [], 2012 backIndex: 0, 2013 backStaticIndex: 0, 2014 backStaticRndm: -1, 2015 frontBag: [], 2016 frontIndex: 0, 2017 frontStaticIndex: 0, 2018 frontStaticRndm: -1, 2019 prevBackIndex: -1, 2020 prevFrontIndex: -1 2021 }, useData = tdata.ignoreDataAttributes; 2022 if (useData) { 2023 swapData.imageCssSelector = privMethods.getDataOrDefault($ele, "image-css", tdata.imageCssSelector); 2024 swapData.fadeSwap = privMethods.getDataOrDefault($ele, "fadeswap", tdata.fadeSwap); 2025 swapData.frontIsRandom = privMethods.getDataOrDefault($ele, "front-israndom", tdata.frontIsRandom); 2026 swapData.frontIsInGrid = privMethods.getDataOrDefault($ele, "front-isingrid", tdata.frontIsInGrid); 2027 swapData.frontIsBackgroundImage = privMethods.getDataOrDefault($ele, "front-isbg", tdata.frontIsBackgroundImage); 2028 swapData.backIsRandom = privMethods.getDataOrDefault($ele, "back-israndom", tdata.backIsRandom); 2029 swapData.backIsInGrid = privMethods.getDataOrDefault($ele, "back-isingrid", tdata.backIsInGrid); 2030 swapData.backIsBackgroundImage = privMethods.getDataOrDefault($ele, "back-isbg", tdata.backIsBackgroundImage); 2031 swapData.doSwapFront = $.inArray('image', tdata.swapFront) > -1 && (tdata.frontImages instanceof Array) && tdata.frontImages.length > 0; 2032 swapData.doSwapBack = $.inArray('image', tdata.swapBack) > -1 && (tdata.backImages instanceof Array) && tdata.backImages.length > 0; 2033 swapData.alwaysSwapFront = privMethods.getDataOrDefault($ele, "front-alwaysswap", tdata.alwaysSwapFront); 2034 swapData.alwaysSwapBack = privMethods.getDataOrDefault($ele, "back-alwaysswap", tdata.alwaysSwapBack); 2035 } else { 2036 swapData.imageCssSelector = tdata.imageCssSelector; 2037 swapData.fadeSwap = tdata.fadeSwap; 2038 swapData.frontIsRandom = tdata.frontIsRandom; 2039 swapData.frontIsInGrid = tdata.frontIsInGrid; 2040 swapData.frontIsBackgroundImage = tdata.frontIsBackgroundImage; 2041 swapData.backIsRandom = tdata.backIsRandom; 2042 swapData.backIsInGrid = tdata.backIsInGrid; 2043 swapData.backIsBackgroundImage = tdata.backIsBackgroundImage; 2044 swapData.doSwapFront = $.inArray('image', tdata.swapFront) > -1 && (tdata.frontImages instanceof Array) && tdata.frontImages.length > 0; 2045 swapData.doSwapBack = $.inArray('image', tdata.swapBack) > -1 && (tdata.backImages instanceof Array) && tdata.backImages.length > 0; 2046 swapData.alwaysSwapFront = tdata.alwaysSwapFront; 2047 swapData.alwaysSwapBack = tdata.alwaysSwapBack; 2048 } 2049 if (typeof (tdata.imgSwap) !== "undefined") 2050 tdata.imgSwap = $.extend(swapData, tdata.imgSwap); 2051 else 2052 tdata.imgSwap = swapData; 2053 if (tdata.imgSwap.doSwapFront) { 2054 tdata.imgSwap.frontBag = this.prepBag(tdata.imgSwap.frontBag, tdata.frontImages, tdata.imgSwap.prevFrontIndex); 2055 tdata.imgSwap.frontStaticRndm = tdata.imgSwap.frontBag.pop(); 2056 if (tdata.preloadImages) 2057 $(tdata.frontImages).metrojs.preloadImages(function () { });
2058 } 2059 if (tdata.imgSwap.doSwapBack) { 2060 tdata.imgSwap.backBag = this.prepBag(tdata.imgSwap.backBag, tdata.backImages, tdata.imgSwap.prevBackIndex); 2061 tdata.imgSwap.backStaticRndm = tdata.imgSwap.backBag.pop(); 2062 if (tdata.preloadImages) 2063 $(tdata.backImages).metrojs.preloadImages(function () { }); 2064 } 2065 }, 2066 prepBag: function (bag, content, prevIdx) { 2067 bag = bag || []; 2068 var bagCount = 0; 2069 for (var i = 0; i < content.length; i++) { 2070 //make sure there's not an immediate repeat 2071 if (i != prevIdx || content.length === 1) { 2072 bag[bagCount] = i; 2073 bagCount++; 2074 } 2075 } 2076 return helperMethods.shuffleArray(bag); 2077 }, 2078 getFrontSwapIndex: function (tdata) { 2079 var idx = 0; 2080 if (!tdata.imgSwap.frontIsRandom) { 2081 idx = tdata.imgSwap.frontIsInGrid ? tdata.imgSwap.frontStaticIndex : tdata.imgSwap.frontIndex; 2082 } else { 2083 if (tdata.imgSwap.frontBag.length === 0) { 2084 tdata.imgSwap.frontBag = this.prepBag(tdata.imgSwap.frontBag, tdata.frontImages, tdata.imgSwap.prevFrontIndex); 2085 } 2086 if (tdata.imgSwap.frontIsInGrid) { 2087 idx = tdata.imgSwap.frontStaticRndm; 2088 } else { 2089 idx = tdata.imgSwap.frontBag.pop(); 2090 } 2091 } 2092 return idx; 2093 }, 2094 getBackSwapIndex: function (tdata) { 2095 var idx = 0; 2096 if (!tdata.imgSwap.backIsRandom) { 2097 idx = tdata.imgSwap.backIsInGrid ? tdata.imgSwap.backStaticIndex : tdata.imgSwap.backIndex; 2098 } else { 2099 if (tdata.imgSwap.backBag.length === 0) { 2100 tdata.imgSwap.backBag = this.prepBag(tdata.imgSwap.backBag, tdata.backImages, tdata.imgSwap.prevBackIndex); 2101 } 2102 if (tdata.imgSwap.backIsInGrid) { 2103 idx = tdata.imgSwap.backStaticRndm; 2104 } else { 2105 idx = tdata.imgSwap.backBag.pop(); 2106 } 2107 } 2108 return idx; 2109 }, 2110 setImageProperties: function ($img, image, isBackground) { 2111 var css = {}, // css object to apply 2112 attr = {}; // attribute values to apply 2113 // get image source 2114 if (typeof (image.src) !== 'undefined') { 2115 if (!isBackground) 2116 attr.src = image.src; 2117 else 2118 css.backgroundImage = "url('" + image.src + "')"; 2119 } 2120 // get alt text 2121 if (typeof (image.alt) !== 'undefined') 2122 attr.alt = image.alt; 2123 // set css 2124 if (typeof (image.css) === 'object') 2125 $img.css($.extend(css, image.css)); 2126 else 2127 $img.css(css); 2128 // set attributes 2129 if (typeof (image.attr) === 'object') 2130 $img.attr($.extend(attr, image.attr)); 2131 else 2132 $img.attr(attr); 2133 }, 2134 action: function (tdata, $front, $back, index) { 2135 if (!tdata.imgSwap.doSwapFront && !tdata.imgSwap.doSwapBack) 2136 return; 2137 var isList = tdata.mode === "flip-list", 2138 isSlide = tdata.mode == "slide", 2139 swapIndex = 0, 2140 isReversed = isList ? tdata.listData[Math.max(index, 0)].isReversed : tdata.isReversed; 2141 if (isList && index == -1) { 2142 // flip list completed 2143 if (tdata.alwaysSwapFront || !isReversed) { 2144 if (tdata.imgSwap.doSwapFront) { 2145 // update the random value for grid mode 2146 if (tdata.imgSwap.frontBag.length === 0) 2147 tdata.imgSwap.frontBag = this.prepBag(tdata.imgSwap.frontBag, tdata.frontImages, tdata.imgSwap.frontStaticRndm); 2148 tdata.imgSwap.frontStaticRndm = tdata.imgSwap.frontBag.pop(); 2149 // update the static index 2150 tdata.imgSwap.frontStaticIndex++; 2151 if (tdata.imgSwap.frontStaticIndex >= tdata.frontImages.length) 2152 tdata.imgSwap.frontStaticIndex = 0; 2153 } 2154 } 2155 if (tdata.alwaysSwapBack || isReversed) { 2156 if (tdata.imgSwap.doSwapBack) { 2157 // update the random value for grid mode 2158 if (tdata.imgSwap.backBag.length === 0) 2159 tdata.imgSwap.backBag = this.prepBag(tdata.imgSwap.backBag, tdata.backImages, tdata.imgSwap.backStaticRndm); 2160 tdata.imgSwap.backStaticRndm = tdata.imgSwap.backBag.pop(); 2161 // update the static index 2162 tdata.imgSwap.backStaticIndex++; 2163 if (tdata.imgSwap.backStaticIndex >= tdata.backImages.length) 2164 tdata.imgSwap.backStaticIndex = 0; 2165 } 2166 } 2167 return; 2168 } 2169 var $face, // face being swapped 2170 $img, // image to apply values 2171 image,// image object to hold properties 2172 swap; // wrapper for setimageProperties for fade
2173 if (tdata.alwaysSwapFront || !isReversed) { 2174 if (!tdata.imgSwap.doSwapFront) 2175 return; 2176 swapIndex = this.getFrontSwapIndex(tdata); 2177 tdata.imgSwap.prevFrontIndex = swapIndex; 2178 // slide mode has a static front and back face 2179 $face = (tdata.mode === "slide") ? $front : $back; 2180 $img = $face.find(tdata.imgSwap.imageCssSelector); 2181 image = typeof (tdata.frontImages[swapIndex]) === "object" ? tdata.frontImages[swapIndex] : { src: tdata.frontImages[swapIndex] }; 2182 swap = function (callback) { 2183 // set src, alt, css and attribute values 2184 var isBg = tdata.imgSwap.frontIsBackgroundImage; 2185 if (typeof (callback) == "function") { 2186 if (isBg) 2187 window.setTimeout(callback, 100); 2188 else 2189 $img[0].onload = callback; 2190 } 2191 defaultModules.imageSwap.setImageProperties($img, image, isBg); 2192 2193 }; 2194 if (tdata.fadeSwap) { 2195 $img.fadeOut(function () { 2196 swap(function () { 2197 $img.fadeIn(); 2198 }); 2199 }); 2200 } else 2201 swap(); 2202 // increment indexes 2203 tdata.imgSwap.frontIndex++; 2204 if (tdata.imgSwap.frontIndex >= tdata.frontImages.length) 2205 tdata.imgSwap.frontIndex = 0; 2206 if (!isList) { 2207 tdata.imgSwap.frontStaticIndex++; 2208 if (tdata.imgSwap.frontStaticIndex >= tdata.frontImages.length) 2209 tdata.imgSwap.frontStaticIndex = 0; 2210 } else { 2211 2212 } 2213 } 2214 if (tdata.alwaysSwapBack || isReversed) { 2215 if (!tdata.imgSwap.doSwapBack) 2216 return; 2217 // get the new index 2218 swapIndex = this.getBackSwapIndex(tdata); 2219 tdata.imgSwap.prevBackIndex = swapIndex; 2220 // use the $face var for consistency 2221 $face = $back; 2222 $img = $face.find(tdata.imgSwap.imageCssSelector); 2223 image = typeof (tdata.backImages[swapIndex]) === "object" ? tdata.backImages[swapIndex] : { src: tdata.backImages[swapIndex] }; 2224 swap = function () { 2225 // set src, alt, css and attribute values 2226 defaultModules.imageSwap.setImageProperties($img, image, tdata.imgSwap.backIsBackgroundImage); 2227 }; 2228 if (tdata.fadeSwap) { 2229 $img.fadeOut(function () { 2230 swap(function () { 2231 $img.fadeIn(); 2232 }); 2233 }); 2234 } else 2235 swap(); 2236 // increment indexes 2237 tdata.imgSwap.backIndex++; 2238 if (tdata.imgSwap.backIndex >= tdata.backImages.length) 2239 tdata.imgSwap.backIndex = 0; 2240 if (!isList) { 2241 tdata.imgSwap.backStaticIndex++; 2242 if (tdata.imgSwap.backStaticIndex >= tdata.backImages.length) 2243 tdata.imgSwap.backStaticIndex = 0; 2244 } else { 2245 2246 } 2247 } 2248 } 2249 } 2250}; 2251 2252// object to maintain timer state 2253$.fn.metrojs.TileTimer = function (interval, callback, repeatCount) { 2254 this.timerId = null; // the id of the current timeout 2255 this.interval = interval; // the amount of time to wait between each action call 2256 this.action = callback; // the method that is fired on each tick 2257 this.count = 0; // the number of times the action has been fired 2258 this.repeatCount = typeof (repeatCount) === "undefined" ? 0 : repeatCount; // the number of times the action will be fired 2259 // call the action method after a delay and call start | stop based on repeat count 2260 this.start = function (delay) { 2261 window.clearTimeout(this.timerId); 2262 var t = this; 2263 this.timerId = window.setTimeout(function () { 2264 t.tick.call(t, interval); 2265 }, delay); 2266 }; 2267 2268 this.tick = function (when) { 2269 this.action(this.count + 1); 2270 this.count++; 2271 // reset the loop count 2272 if (this.count >= MAX_LOOP_COUNT) 2273 this.count = 0; 2274 if (this.repeatCount > 0 || this.repeatCount == -1) { 2275 if (this.count != this.repeatCount) { 2276 this.start(when); 2277 } else 2278 this.stop(); 2279 } 2280 } 2281 // clear the timer and reset the count 2282 this.stop = function () { 2283 this.timerId = window.clearTimeout(this.timerId); 2284 this.reset(); 2285 }; 2286 2287 this.resume = function () { 2288 if (this.repeatCount > 0 || this.repeatCount == -1) { 2289 if (this.count != this.repeatCount) { 2290 this.start(interval); 2291 } 2292 } 2293 }; 2294 2295 // clear the timer but leave the count intact 2296 this.pause = function () { 2297 this.timerId = window.clearTimeout(this.timerId); 2298 }; 2299 2300 // reset count 2301 this.reset = function () { 2302 this.count = 0; 2303 }; 2304 2305 // reset count and timer 2306 this.restart = function (delay) { 2307 this.stop(); 2308 this.start(delay); 2309 }; 2310}; 2311jQuery.fn.metrojs.theme = { 2312 loadDefaultTheme: function (stgs) { 2313 if (typeof (stgs) === "undefined" || stgs == null) { 2314 stgs = jQuery.fn.metrojs.theme.defaults; 2315 } else { 2316 var stg = jQuery.fn.metrojs.theme.defaults; 2317 jQuery.extend(stg, stgs); 2318 stgs = stg; 2319 } 2320 //get theme from local storage or set base theme 2321 var hasLocalStorage = typeof (window.localStorage) !== "undefined"; 2322 var hasKeyAndValue = function (key) { 2323 return (typeof (window.localStorage[key]) !== "undefined" && window.localStorage[key] != null); 2324 }; 2325 if (hasLocalStorage && (!hasKeyAndValue("Metro.JS.AccentColor") || !hasKeyAndValue("Metro.JS.BaseAcce
2325ntColor"))) { 2326 //base theme 2327 window.localStorage["Metro.JS.AccentColor"] = stgs.accentColor; 2328 window.localStorage["Metro.JS.BaseAccentColor"] = stgs.baseTheme; 2329 jQuery(stgs.accentCssSelector).addClass(stgs.accentColor).data("accent", stgs.accentColor); 2330 jQuery(stgs.baseThemeCssSelector).addClass(stgs.baseTheme); 2331 if (typeof (stgs.loaded) === "function") 2332 stgs.loaded(stgs.baseTheme, stgs.accentColor); 2333 //preload light theme image 2334 if (typeof (stgs.preloadAltBaseTheme) !== "undefined" && stgs.preloadAltBaseTheme) 2335 jQuery([(stgs.baseTheme == 'dark') ? stgs.metroLightUrl : stgs.metroDarkUrl]).metrojs.preloadImages(function () { }); 2336 } else { 2337 if (hasLocalStorage) { 2338 stgs.accentColor = window.localStorage["Metro.JS.AccentColor"]; 2339 stgs.baseTheme = window.localStorage["Metro.JS.BaseAccentColor"]; 2340 jQuery(stgs.accentCssSelector).addClass(stgs.accentColor).data("accent", stgs.accentColor); 2341 jQuery(stgs.baseThemeCssSelector).addClass(stgs.baseTheme); 2342 if (typeof (stgs.loaded) === "function") 2343 stgs.loaded(stgs.baseTheme, stgs.accentColor); 2344 } else { 2345 jQuery(stgs.accentCssSelector).addClass(stgs.accentColor).data("accent", stgs.accentColor); 2346 jQuery(stgs.baseThemeCssSelector).addClass(stgs.baseTheme); 2347 if (typeof (stgs.loaded) === "function") 2348 stgs.loaded(stgs.baseTheme, stgs.accentColor); 2349 //preload light theme image 2350 if (typeof (stgs.preloadAltBaseTheme) !== "undefined" && stgs.preloadAltBaseTheme) 2351 jQuery([(stgs.baseTheme == 'dark') ? stgs.metroLightUrl : stgs.metroDarkUrl]).metrojs.preloadImages(function () { });
2352 } 2353 } 2354 }, 2355 applyTheme: function (tColor, aColor, stgs) { 2356 if (typeof (stgs) === "undefined" || stgs == null) { 2357 stgs = jQuery.fn.metrojs.theme.defaults; 2358 } else { 2359 var stg = jQuery.fn.metrojs.theme.defaults; 2360 stgs = jQuery.extend({}, stg, stgs); 2361 } 2362 2363 if (typeof (tColor) !== "undefined" && tColor != null) { 2364 if (typeof (window.localStorage) !== "undefined") { 2365 window.localStorage["Metro.JS.BaseAccentColor"] = tColor; 2366 } 2367 var $theme = jQuery(stgs.baseThemeCssSelector); 2368 if ($theme.length > 0) { 2369 if (tColor == "dark") 2370 $theme.addClass("dark").removeClass("light"); 2371 else if (tColor == "light") 2372 $theme.addClass("light").removeClass("dark"); 2373 } 2374 } 2375 if (typeof (aColor) !== "undefined" && aColor != null) { 2376 if (typeof (window.localStorage) !== "undefined") { 2377 window.localStorage["Metro.JS.AccentColor"] = aColor; 2378 } 2379 var $accent = jQuery(stgs.accentCssSelector); 2380 if ($accent.length > 0) { 2381 var themeset = false; 2382 $accent.each(function () { 2383 jQuery(this).addClass(aColor); 2384 var dAccent = jQuery(this).data("accent"); 2385 if (dAccent != aColor) { 2386 var cleanClass = jQuery(this).attr("class").replace(dAccent, ""); 2387 cleanClass = cleanClass.replace(/(\s)+/, ' '); 2388 jQuery(this).attr("class", cleanClass); 2389 jQuery(this).data("accent", aColor); 2390 themeset = true; 2391 } 2392 }); 2393 if (themeset && typeof (stgs.accentPicked) === "function") 2394 stgs.accentPicked(aColor); 2395 } 2396 } 2397 }, 2398 appendAccentColors: function (stgs) { 2399 if (typeof (stgs) === "undefined" || stgs == null) { 2400 stgs = jQuery.fn.metrojs.theme.defaults; 2401 } else { 2402 var stg = jQuery.fn.metrojs.theme.defaults; 2403 stgs = jQuery.extend({}, stg, stgs); 2404 } 2405 var themeList = ""; 2406 var themes = stgs.accentColors; 2407 var template = stgs.accentListTemplate; 2408 for (var i = 0; i < themes.length; i++) { 2409 themeList += template.replace(/\{0\}/g, themes[i]); 2410 } 2411 $(themeList).appendTo(stgs.accentListContainer); 2412 }, 2413 appendBaseThemes: function (stgs) { 2414 if (typeof (stgs) === "undefined" || stgs == null) { 2415 stgs = jQuery.fn.metrojs.theme.defaults; 2416 } else { 2417 var stg = jQuery.fn.metrojs.theme.defaults; 2418 stgs = jQuery.extend({}, stg, stgs); 2419 } 2420 var themeList = "", 2421 themes = stgs.baseThemes, 2422 template = stgs.baseThemeListTemplate; 2423 for (var i = 0; i < themes.length; i++) { 2424 themeList += template.replace(/\{0\}/g, themes[i]); 2425 } 2426 $(themeList).appendTo(stgs.baseThemeListContainer); 2427 }, 2428 // default options for theme 2429 defaults: { 2430 baseThemeCssSelector: 'body', // selector to place dark or light class after load or selection 2431 accentCssSelector: '.tiles', // selector to place accent color class after load or selection 2432 accentColor: 'blue', // the default accent color. options are blue, brown, green, lime, magenta, mango, pink, purple, red, teal 2433 baseTheme: 'dark', // the default theme color. options are dark, light 2434 accentColors: [ 2435 'amber', 'blue', 'brown', 'cobalt', 'crimson', 'cyan', 2436 'magenta', 'lime', 'indigo', 'green', 'emerald', 2437 'mango', 'mauve', 'olive', 'orange', 'pink', 'red', 2438 'sienna', 'steel', 'teal', 'violet', 'yellow' 2439 ], 2440 baseThemes: [ 2441 'light', 2442 'dark' 2443 ], 2444 accentListTemplate: "<li><a href='javascript:;' title='{0}' class='accent {0}'></a></li>", // template to generate accent options 2445 accentListContainer: "ul.theme-options,.theme-options>ul", // selector of container to append accents 2446 baseThemeListTemplate: "<li><a href='javascript:;' title='{0}' class='accent {0}'></a></li>", // template to generate accent options 2447 baseThemeListContainer: "ul.base-theme-options,.base-theme-options>ul" // selector of container to append accents 2448 } 2449}; 2450 2451jQuery.fn.applicationBar = function (options) { 2452 /* Setup the public options for the applicationBar */ 2453 var stgs = typeof (jQuery.fn.metrojs.theme) !== "undefined" ? jQuery.fn.metrojs.theme.defaults : {}; 2454 jQuery.extend(stgs, jQuery.fn.applicationBar.defaults, options); 2455 if (typeof (jQuery.fn.metrojs.theme) != "undefined") { 2456 var theme = jQuery.fn.metrojs.theme; 2457 if (stgs.shouldApplyTheme) { 2458 theme.loadDefaultTheme(stgs); 2459 } 2460 var themeContainer = stgs.accentListContainer.replace(",", " a,") + " a"; 2461 var themeContainerClick = function () { 2462 var accent = jQuery(this).attr("class").replace("accent", "").replace(" ", ""); 2463 theme.applyTheme(null, accent, stgs); 2464 if (typeof (stgs.accentPicked) == "function") 2465 stgs.accentPicked(accent); 2466 }; 2467 var baseContainer = stgs.baseThemeListContainer.replace(","," a,") + " a"; 2468 var baseContainerClick = function () { 2469 var accent = jQuery(this).attr("class").replace("accent", '').replace(' ', ''); 2470 theme.applyTheme(accent, null, stgs); 2471 if (typeof (stgs.themePicked) == "function") 2472 stgs.themePicked(accent); 2473 }; 2474 if (typeof ($.fn.on) === "function") { 2475 $(this).on("click.appBar", themeContainer, themeContainerClick); 2476 $(this).on("click.appBar", baseContainer, baseContainerClick); 2477 } else { 2478 $(themeContainer).live("click.appBar", themeContainerClick); 2479 $(baseContainer).live("click.appBar", baseContainerClick); 2480 } 2481 } 2482 //this should really only run once but we can support multiple application bars 2483 return $(this).each(function (idx, ele) { 2484 var $this = $(ele), 2485 data = $.extend({}, stgs); 2486 if(data.collapseHeight == "auto") 2487 data.collapseHeight = $(this).outerHeight(); 2488 2489 //unfortunately we have to sniff out mobile browsers because of the inconsistent implementation of position:fixed 2490 //most desktop methods return false positives on a mobile 2491 //todo: find/come up with a better fixed position test 2492 if (navigator.userAgent.match(/(Android|webOS|iPhone|iPod|BlackBerry|PIE|IEMobile)/i)) { 2493 // IEMobile10 supports position:fixed. This should cover up to IE20 or at least until fixed positioning gets sorted 2494 // let iOS 5+ pass as well, hopefully by iOS 9 fixed pos will be standard :/ 2495 if (!navigator.userAgent.match(/(IEMobile\/1)/i) && !navigator.userAgent.match(/(iPhone OS [56789])/i)) { 2496 $this.css({ position: 'absolute', bottom: '0px' }); 2497 } 2498 } 2499 data.slideOpen = function () { 2500 if (!$this.hasClass("expanded")) 2501 data.animateAppBar(false); 2502 }; 2503 data.slideClosed = function () { 2504 if ($this.hasClass("expanded")) 2505 data.animateAppBar(true); 2506 }; 2507 data.animateAppBar = function (isExpanded) { 2508 var hgt = isExpanded ? data.collapseHeight : data.expandHeight; 2509 if (isExpanded) 2510 $this.removeClass("expanded"); 2511 else 2512 if (!$this.hasClass("expanded")) 2513 $this.addClass("expanded"); 2514 $this.stop().animate({ height: hgt }, { duration: data.duration }); 2515 }; 2516 $this.data("ApplicationBar", data) 2517 2518 $this.find(stgs.handleSelector).click(function () { 2519 data.animateAppBar($this.hasClass("expanded")); 2520 }); 2521 2522 if (data.bindKeyboard == true) { 2523 jQuery(document.documentElement).keyup(function (event) { 2524 // handle cursor keys 2525 if (event.keyCode == 38) { 2526 // expand 2527 if (event.target && event.target.tagName.match(/INPUT|TEXTAREA|SELECT/i) == null) { 2528 if (!$this.hasClass("expanded")) { 2529 data.animateAppBar(false); 2530 } 2531 } 2532 2533 } else if (event.keyCode == 40) { 2534 // collapse 2535 if (event.target && event.target.tagName.match(/INPUT|TEXTAREA|SELECT/i) == null) { 2536 if ($this.hasClass("expanded")) { 2537 data.animateAppBar(true); 2538 } 2539 } 2540 } 2541 }); 2542 } 2543 });
2544}; 2545 2546// default options for applicationBar, the theme defaults are merged with this object when the applicationBar function is called 2547jQuery.fn.applicationBar.defaults = { 2548 applyTheme: true, // should the theme be loaded from local storage and applied to the page 2549 themePicked: function (tColor) { }, // called when a new theme is chosen. the chosen theme (dark | light) 2550 accentPicked: function (aColor) { }, // called when a new accent is chosen. the chosen theme (blue, mango, purple, etc.) 2551 loaded: function (tColor, aColor) { }, // called if applyTheme is true onload when a theme has been loaded from local storage or overridden by options 2552 duration: 300, // how fast should animation be performed, in milliseconds 2553 expandHeight: "320px", // height the application bar to expand to when opened 2554 collapseHeight: "auto", // height the application bar will collapse back to when closed 2555 bindKeyboard: true, // should up and down keys on keyborad be bound to the application bar 2556 handleSelector: "a.etc", 2557 metroLightUrl: 'images/metroIcons_light.jpg', // the url for the metro light icons (only needed if preload 'preloadAltBaseTheme' is true) 2558 metroDarkUrl: 'images/metroIcons.jpg', // the url for the metro dark icons (only needed if preload 'preloadAltBaseTheme' is true) 2559 preloadAltBaseTheme: false // should the applicationBar icons be pre loaded for the alternate theme to enable fast theme switching 2560}; 2561/* Preload Images */ 2562// Usage: jQuery(['img1.jpg', { src: 'img2.jpg' }]).metrojs.preloadImages(function(){ ... }); 2563// Callback function gets called after all images are preloaded 2564$.fn.metrojs.preloadImages = function (callback) { 2565 var checklist = $(this).toArray(); 2566 var $img = $("<img style='display:none;' />").appendTo("body"); 2567 $(this).each(function () { 2568 var src = this; 2569 if (typeof(this) == "object") 2570 src = this.src; 2571 $img.attr({ src: src }).load(function() { 2572 for (var i = 0; i < checklist.length; i++) { 2573 if (checklist[i] == element) { 2574 checklist.splice(i, 1); 2575 } 2576 } 2577 if (checklist.length == 0) { 2578 callback(); 2579 } 2580 }); 2581 2582 }); 2583 $img.remove(); 2584}; 2585// object used for compatibility checks 2586$.fn.metrojs.MetroModernizr = function (stgs) { 2587 if(typeof(stgs) === "undefined") { 2588 stgs = { useHardwareAccel: true, useModernizr: typeof(window.Modernizr) !== "undefined" }; 2589 } 2590 this.isOldJQuery = /^1\.[0123]/.test($.fn.jquery), 2591 this.isOldAndroid = (function(){ 2592 try{ 2593 var ua = navigator.userAgent; 2594 if( ua.indexOf("Android") >= 0 ) 2595 { 2596 var androidversion = parseFloat(ua.slice(ua.indexOf("Android")+8)); 2597 if (androidversion < 2.3) 2598 return true; 2599 } 2600 }catch(err){ $.error(err); } 2601 return false; 2602 })(); 2603 this.canTransform = false; 2604 this.canTransition = false; 2605 this.canTransform3d = false; 2606 this.canAnimate = false; 2607 this.canTouch = false; 2608 this.canFlip3d = stgs.useHardwareAccel; 2609 if (stgs.useHardwareAccel == true) { 2610 if (stgs.useModernizr == false) { 2611 //determine if the browser supports the neccessary accelerated features 2612 if (typeof (window.MetroModernizr) !== "undefined") { 2613 this.canTransform = window.MetroModernizr.canTransform; 2614 this.canTransition = window.MetroModernizr.canTransition; 2615 this.canTransform3d = window.MetroModernizr.canTransform3d; 2616 this.canAnimate = window.MetroModernizr.canAnimate; 2617 this.canTouch = window.MetroModernizr.canTouch; 2618 } else { 2619 window.MetroModernizr = {}; 2620 /***** check for browser capabilities credit: modernizr-1.7 http://modernizr.com/ *****/ 2621 var mod = 'metromodernizr'; 2622 var docElement = document.documentElement; 2623 var docHead = document.head || document.getElementsByTagName('head')[0]; 2624 var modElem = document.createElement(mod); 2625 var m_style = modElem.style; 2626 var prefixes = ' -webkit- -moz- -o- -ms- '.split(' '); 2627 var domPrefixes = 'Webkit Moz O ms Khtml'.split(' '); 2628 var test_props = function (props, callback) { 2629 for (var i in props) { 2630 if (m_style[props[i]] !== undefined && (!callback || callback(props[i], modElem))) { 2631 return true; 2632 } 2633 } 2634 }; 2635 var test_props_all = function (prop, callback) { 2636 var uc_prop = prop.charAt(0).toUpperCase() + prop.substr(1), 2637 props = (prop + ' ' + domPrefixes.join(uc_prop + ' ') + uc_prop).split(' '); 2638 return !!test_props(props, callback); 2639 }; 2640 var test_3d = function () { 2641 var ret = !!test_props(['perspectiveProperty', 'WebkitPerspective', 'MozPerspective', 'OPerspective', 'msPerspective']); 2642 if (ret && 'webkitPerspective' in docElement.style) { 2643 // Webkit allows this media query to succeed only if the feature is enabled. 2644 // '@media (transform-3d),(-o-transform-3d),(-moz-transform-3d),(-ms-transform-3d),(-webkit-transform-3d),(modernizr){ ... }' 2645 ret = testMediaQuery(['@media (',prefixes.join('transform-3d),('),mod,')','{#metromodernizr{left:9px;position:absolute;height:3px;}}'].join(''), function(div){ 2646 return div.offsetHeight === 3 && div.offsetLeft === 9; 2647 }); 2648 } 2649 return ret; 2650 }; 2651 var testMediaQuery = function (mq, predicate) { 2652 var st = document.createElement('style'), 2653 div = document.createElement('div'), 2654 ret; 2655 st.textContent = mq; 2656 docHead.appendChild(st); 2657 div.id = mod; 2658 docElement.appendChild(div); 2659 ret = predicate(div); 2660 st.parentNode.removeChild(st); 2661 div.parentNode.removeChild(div); 2662 return !!ret; 2663 }; 2664 var test_touch = function() { 2665 return canTouch = ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch || 2666 (typeof(window.navigator.msMaxTouchPoints) !== "undefined" && window.navigator.msMaxTouchPoints > 0) || 2667 testMediaQuery(['@media (',prefixes.join('touch-enabled),('),mod,')','{#metromodernizr{top:9px;position:absolute}}'].join(''), function(div){ 2668 return div.offsetTop === 9; 2669 }); 2670 }; 2671 this.canTransform = !!test_props(['transformProperty', 'WebkitTransform', 'MozTransform', 'OTransform', 'msTransform']); 2672 this.canTransition = test_props_all('transitionProperty'); 2673 this.canTransform3d = test_3d(); 2674 this.canAnimate = test_props_all('animationName'); 2675 this.canTouch = test_touch(); 2676 window.MetroModernizr.canTransform = this.canTransform; 2677 window.MetroModernizr.canTransition = this.canTransition; 2678 window.MetroModernizr.canTransform3d = this.canTransform3d; 2679 window.MetroModernizr.canAnimate = this.canAnimate; 2680 window.MetroModernizr.canTouch = this.canTouch; 2681 docElement = null; 2682 docHead = null; 2683 modElem = null; 2684 m_style = null; 2685 } 2686 } else { 2687 this.canTransform = $("html").hasClass("csstransforms"); 2688 this.canTransition = $("html").hasClass("csstransitions"); 2689 this.canTransform3d = $("html").hasClass("csstransforms3d"); 2690 this.canAnimate = $("html").hasClass("cssanimations"); 2691 this.canTouch = $("html").hasClass("touch") || (typeof(window.navigator.msMaxTouchPoints) !== "undefined" && window.navigator.msMaxTouchPoints > 0); 2692 } 2693 } 2694 this.canFlip3d = this.canFlip3d && this.canAnimate && this.canTransform && this.canTransform3d; 2695}; 2696 2697})(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.