1 /** 2 * @fileoverview Pure Javascript mobilization solution 3 * 4 * @module mobilize.core 5 * 6 */ 7 8 /** 9 * @namespace Mobilization core 10 * <p> 11 * <i>mobilize</i> is a core class of mobilize.js. 12 * <p> 13 * You only want to use this class directly if you are developing mobilization 14 * for your own framework. Otherwise use one of the stock CMS classes supplied with 15 * mobilize.js. 16 * <p> 17 * mobilize does not need to be instiated and it's a static singleton. Instead, 18 * the functionality is extended by an <i>extender</i>. 19 * Extender is a Javascript which directly overrides mobilize member functions 20 * in mobilize namespace. 21 * <p> 22 * @author Mikko Ohtamaa, Jussi Toivola 23 * 24 */ 25 var mobilize = { 26 27 /** 28 * @class 29 * 30 * <p> 31 * Options and their default values. 32 * <p> 33 * These default values can be overriden by extender getExtendedOptions() 34 * or user supplied parameters to init(). 35 * <p> 36 * 37 * 38 * @see mobilize.init 39 * 40 * @see mobilize.getExtendedOptions 41 */ 42 options : { 43 44 /** 45 * You need to set this value in order to 46 * cache mobile page template in localStorage. 47 * <p> 48 * Every time template is upgraded this values must 49 * be changed within your construction script tag. 50 * <p> 51 * If value is null caching is not used. 52 * <p> 53 * @default null 54 */ 55 templateCacheVersion : null, 56 57 /** 58 * 59 * <script src=""> whitelist for filtering web specific 60 * elements from <head> 61 * <p> 62 * If src attribute has substring match of any list element, 63 * the tag is left to mobile version also. 64 * <p> 65 * @default allow scripts which have mobilize in their name. 66 */ 67 whitelistScriptSrc : ["mobilize"], 68 69 /** 70 * <style type="text/css> @import whitelist for filtering web specific 71 * elements from <head> 72 * <p> 73 * If CSS @import content has substring match of any list element, 74 * the tag is left to mobile version also. 75 * <p> 76 * 77 * @see mobilize.options.inlineStyleMaxCheckLength 78 * 79 * @default empty list 80 */ 81 whitelistStyleImport : [], 82 83 // <link rel="stylesheet"> href whitelist 84 whitelistCSSLinks : [], 85 86 // Which template file to use - relative file or URL 87 template : "template.html", 88 89 /** How many characters <style> inner text may contain it to be run through inline CSS importer check */ 90 inlineStyleMaxCheckLength : 256, 91 92 // Go always with mobile rendering path (useful for testing) 93 forceMobilize : false, 94 95 // Force user agent 96 forceUserAgent : null, 97 98 // Which URL load jQuery from. 99 // Default to Google CDN version. 100 //jQueryURL : "http://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js", 101 //jQueryURL : "http://code.jquery.com/jquery-1.5.1.min.js", 102 103 // TODO: Add cdn.mobilizejs.com URL 104 jQueryURL : null, 105 106 // Which HTTP GET parameter we can use to forc mobilization 107 mobilizeQueryParameter : "mobilize" 108 }, 109 110 /** Async flag indicating that jQuery Mobile has been loaded */ 111 jQueryMobileLoaded : false, 112 113 /** Async flag indicating that mobile page transform is complete */ 114 transformComplete : false, 115 116 117 /** 118 * Initialize mobilize class. 119 * 120 * <h2>Options<h2> 121 * 122 * <table><tbody> 123 * 124 * <tr><th>resourceWhitelist</th> 125 * <td>String tags which mark head tag JS and CSS resources not to be purged</td></tr> 126 * 127 * 128 * @static 129 * 130 * @param options 131 */ 132 init : function(options) { 133 134 // Override default parameters with user supplied versions 135 136 if(!options) { 137 options = {}; 138 } 139 140 141 // Extend global options with subclass supplied ones 142 var extendedOptions = mobilize.getExtendedOptions(); 143 mobilize.extend(mobilize.options, extendedOptions); 144 145 // Extend global options with user supplied ones 146 mobilize.extend(mobilize.options, options); 147 148 if(!mobilize.options.jQueryURL) { 149 throw "options.jQueryURL must be given to init()"; 150 } 151 152 }, 153 154 /** 155 * Simple shallow copy from an object to another. 156 * <p> 157 * 158 * @tag utility 159 * 160 * @param {Object} target Javascript object to receive new members 161 * @param {Object} source Javascript object to source new members 162 */ 163 extend : function(target, source) { 164 for(name in source) { 165 var val = source[name]; 166 target[name] = val; 167 } 168 }, 169 170 /** 171 * Return plug-in specific options overrides 172 * 173 * @static 174 * 175 * @returns Object containing mobilize options to override 176 */ 177 getExtendedOptions : function() { 178 return {} 179 }, 180 181 /** 182 * Entry point to mobilize.js machinery. 183 * <p> 184 * Stop loading current HTML resources, start async processes 185 * to get the page mobilized. 186 * 187 * @static 188 */ 189 bootstrap : function() { 190 191 mobilize.log("Bootstrap"); 192 if(mobilize.checkMobileBrowser(mobilize.options)) { 193 mobilize.enableMobileRendering(); 194 } else { 195 mobilize.log("Web mode wanted"); 196 } 197 }, 198 199 200 /** 201 * Utility for internal debug logging 202 * 203 * @param msg: message to log 204 * */ 205 log : function(msg) { 206 if(window.console) { 207 if(console.log) { 208 console.log(msg); 209 } 210 } 211 }, 212 213 /** 214 * <p>Get baseurl from url by ignoring file and url parameters</p> 215 * 216 * <b>Example</b> 217 * <pre> 218 * mobilize.baseurl(window.location.href) 219 * </pre> 220 * @param url : Url to parse 221 * 222 * */ 223 baseurl : function (aUrl) { 224 225 var end; 226 var url; 227 228 end = aUrl.indexOf('?'); 229 230 if(end <= 0) { 231 end = aUrl.length-1; 232 } 233 234 url = aUrl.slice(0, end); 235 // Ignore slash at the end of url 236 if(url[url.length-1] == "/" ) { 237 url = url.slice(0,url.length-2); 238 } 239 240 // But add the slash to result for convenient concat 241 end = url.lastIndexOf("/") + 1; 242 url = url.slice(0,end); 243 244 return url; 245 }, 246 247 /** Make a function call and report possible exceptions back to a centralized server. 248 * 249 * We use this to track problems with possible not-so-well-implemented mobile browsers. 250 */ 251 callWithErrorReporting : function(func) { 252 try { 253 func(); 254 } catch(e) { 255 // 256 } 257 }, 258 259 /** 260 * Read URL parameters to dict. 261 * 262 * See: http://jquery-howto.blogspot.com/2009/09/get-url-parameters-values-with-jquery.html 263 */ 264 getUrlVars : function () 265 { 266 // Cache this call results 267 if(this._urlvars) { 268 return this._urlvars; 269 } 270 var vars = [], hash; 271 var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); 272 273 for(var i = 0; i < hashes.length; i++) 274 { 275 hash = hashes[i].split('='); 276 vars.push(hash[0]); 277 vars[hash[0]] = hash[1]; 278 } 279 280 this._urlvars = vars; 281 return vars; 282 }, 283 284 /** 285 * See: http://www.quirksmode.org/js/cookies.html 286 */ 287 createCookie : function(name,value,days) { 288 if (days) { 289 var date = new Date(); 290 date.setTime(date.getTime()+(days*24*60*60*1000)); 291 var expires = "; expires="+date.toGMTString(); 292 } 293 else var expires = ""; 294 document.cookie = name+"="+value+expires+"; path=/"; 295 }, 296 297 /** 298 * See: http://www.quirksmode.org/js/cookies.html 299 */ 300 readCookie : function(name) { 301 var nameEQ = name + "="; 302 var ca = document.cookie.split(';'); 303 for(var i=0;i < ca.length;i++) { 304 var c = ca[i]; 305 while (c.charAt(0)==' ') c = c.substring(1,c.length); 306 if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); 307 } 308 return null; 309 }, 310 /** 311 * See: http://www.quirksmode.org/js/cookies.html 312 */ 313 eraseCookie : function(name) { 314 createCookie(name,"",-1); 315 }, 316 317 /** Check if browser is running on mobile platform 318 * 319 * @param userAgent = userAgent name. Uses browser's userAgent by default 320 * 321 * @param forceMobilize = Force detection to mobile to true or false regardless of real type 322 * 323 * URL parameter mobilize=<true,1> can also be used to force mobile. 324 * 325 * The state is also stored to 'mobilize-mobile' cookie this 326 * information is passed to server for the following requests. 327 * URL and options.force paremeters 328 * override cookie and detection. 329 * 330 * @return: true if browser is considered as mobile browser. 331 * 332 * @see: http://detectmobilebrowser.com/ for the detection code. 333 */ 334 checkMobileBrowser : function (opts) 335 { 336 var forced; 337 var name; 338 if(!opts) { 339 opts = {}; 340 } 341 342 // Using cookie by default 343 // forced = mobilize.readCookie("mobilize-mobile"); 344 345 // For user agent testing 346 name = opts.forceUserAgent; 347 348 // Note: URL parameter and option overrides cookie 349 // Get URL var to mobilize page 350 forced = mobilize.getUrlVars()[mobilize.options.mobilizeQueryParameter]; 351 352 // Javascript option to always render in mobile mode 353 if(opts.forceMobilize) { 354 forced = true; 355 } 356 357 if(forced !== undefined && forced !== null ) { 358 result = (forced == "true" || forced == "1" || forced === true || forced === 1); 359 } 360 else { 361 if(!name) { 362 name = (navigator.userAgent || navigator.vendor || window.opera); 363 } 364 365 result = (function(a) { 366 if (/android|avantgo|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i 367 .test(a) 368 || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|e\-|e\/|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(\-|2|g)|yas\-|your|zeto|zte\-/i 369 .test(a.substr(0, 4))) 370 return true; 371 else 372 return false; 373 })(name); 374 } 375 376 // Update cookie 377 var cookie = result ? "1" : "0"; 378 mobilize.createCookie("mobilize-mobile", cookie); 379 380 return result; 381 }, 382 383 /** 384 * 385 * Stop loading all web page resources until mobile template is properly placed 386 * and template transformation has taken place. 387 * 388 */ 389 enableMobileRendering : function() { 390 391 mobilize.log("Enabling mobile rendering"); 392 393 this.suspendRendering(); 394 395 this.cleanHead(); 396 397 mobilize.log("Syncronous boostrap done"); 398 399 // We cannot directly load template, since <body> has not been constructed 400 var self = this; 401 402 function onJQueryLoad() { 403 mobilize.log("jQuery load success"); 404 405 if(!jQuery) { 406 throw "jQuery object construction failed"; 407 } 408 409 jQuery(document).ready(function() { self.loadMobileTemplate(); } ); 410 411 // TODO: Handle case when document is already ready in this point 412 } 413 414 // Clear conflicting jQuery objects 415 // - if two jQuery instances are loaded then event handlers do not function 416 // properly 417 418 if(window.jQuery !== undefined) { 419 delete window.jQuery; 420 } 421 422 if(window.$ !== undefined) { 423 delete window.$; 424 } 425 426 mobilize.loadScript(mobilize.options.jQueryURL, onJQueryLoad); 427 // 428 }, 429 430 /** 431 * Helper function to do AJAXy requests before jQuery has been loaded. 432 * 433 * @param {String} url 434 * 435 * @param callback(payload) 436 */ 437 getAJAX : function(url, callback) { 438 var req = new XMLHttpRequest(); 439 req.open('GET', url, true); 440 req.onreadystatechange = function (aEvt) { 441 if(req.readyState == 4) { 442 if (req.status == 200) { 443 callback(req.responseText); 444 } else { 445 mobilize.log("Could not AJAX url:" + url + " got status:" + req.status); 446 } 447 } 448 }; 449 req.send(null); 450 }, 451 452 /** 453 * Magical script loader. 454 * 455 * Use AJAX to load Javascript code, then eval() it. 456 * This ensures that code is executed (not just loaded) 457 * when triggering the callback. 458 * 459 * http://blog.client9.com/2008/11/javascript-eval-in-global-scope.html 460 * 461 * @param {String} url 462 * 463 * @param {Object} callbacl 464 */ 465 loadScript : function(url, callback) { 466 467 function loaded(javascript) { 468 mobilize.log("Loaded payload for " + url + ", now evaling() it "); 469 eval.call(null, javascript); 470 callback(); 471 } 472 473 mobilize.getAJAX(url, loaded); 474 }, 475 476 /** 477 * Check if a given link is on resource whitelist. 478 * 479 * @param src URL 480 * 481 * @param list List of substring matches. If matches do not remove the element. 482 * 483 * @returns true if the src string has substring match of any list element 484 */ 485 checkResourceWhitelist : function(src, list) { 486 for(var i=0; i<list.length; i++) { 487 var matcher = list[i]; 488 if(src.indexOf(matcher) >= 0) { 489 return true; 490 } 491 } 492 return false; 493 }, 494 495 /** 496 * Remove unnecessary script tags if not needed for mobile. 497 * 498 * Use options.resourceWhitelist matching. 499 */ 500 cleanJavascript : function() { 501 502 mobilize.log("Cleaning <script>s"); 503 504 var tags = document.getElementsByTagName("script"); 505 506 for(var i=0; i<tags.length; i++) { 507 var script = tags[i]; 508 var src = script.getAttribute("src"); 509 510 if(!src) { 511 // TODO: Inline script 512 continue; 513 } 514 515 if(!mobilize.checkResourceWhitelist(src, mobilize.options.whitelistScriptSrc)) { 516 mobilize.log("Cleaning script tag"); 517 mobilize.log(script); 518 var parent = script.parentNode; 519 parent.removeChild(script); 520 } 521 } 522 }, 523 524 /** 525 * Remove web only <link rel="stylesheet"> tags 526 * 527 */ 528 cleanCSSLink : function() { 529 530 mobilize.log("Cleaning <link rel='stylesheet'>s"); 531 532 var tags = document.getElementsByTagName("link"); 533 534 for(var i=0; i<tags.length; i++) { 535 script = tags[i]; 536 537 var rel = script.getAttribute("rel"); 538 if(rel != "stylesheet") { 539 continue; 540 } 541 542 var src = script.getAttribute("href"); 543 if(!mobilize.checkResourceWhitelist(src, mobilize.options.whitelistCSSLinks)) { 544 var parent = script.parentNode; 545 parent.removeChild(script); 546 } 547 } 548 }, 549 550 /** 551 * Remove unnecessary CSS links from <head> if not needed for mobile. 552 * 553 * Supports syntaxes 554 * 555 * <style type="text/css"> 556 * @import url(http://plone.org/portal_css/Sunburst%20Theme/newplone-cachekey7531.css); 557 * </style> 558 * 559 * Use options.resourceWhitelist matching. 560 */ 561 cleanCSSStyle : function() { 562 563 mobilize.log("Cleaning <style>s"); 564 565 var tags = document.getElementsByTagName("style"); 566 567 for(var i=0; i<tags.length; i++) { 568 var style = tags[i]; 569 570 // http://bytes.com/topic/javascript/answers/600139-get-file-name-style-tag 571 572 function remove() { 573 var parent = style.parentNode; 574 parent.removeChild(style); 575 } 576 577 var text = style.textContent; 578 579 if(!text) { 580 mobilize.log("Interesting style node:"); 581 mobilize.log(style); 582 continue; 583 } 584 585 // Make sure we don't start searching through very long 586 // inline CSS 587 if(text.length < mobilize.options.inlineStyleMaxCheckLength) { 588 589 // This is inline CSS import 590 var matches = text.match(/@import url\(.*\);?/mg); 591 592 if(matches != null) { 593 for(var i=0; i<matches.length; i++) { 594 if(!mobilize.checkResourceWhitelist(matches[i], mobilize.options.whitelistStyleImport)) { 595 remove(); 596 break; 597 } 598 } 599 } 600 } else { 601 // too long CSS snippet, drop unconditionally 602 remove(); 603 } 604 } 605 }, 606 607 /** 608 * Stop loading Javascripts and CSS we do not need in mobile mode. 609 */ 610 cleanHead : function() { 611 mobilize.cleanJavascript(); 612 mobilize.cleanCSSLink(); 613 mobilize.cleanCSSStyle(); 614 615 }, 616 617 618 /** 619 * Make sure the browser does not load anything extra before mobile transform has taken place 620 */ 621 suspendRendering : function() { 622 623 mobilize.log("Suspending page rendering"); 624 625 if(!document.body) { 626 // DOM tree loading, couldn't get hang off it 627 throw "Could not find body while loading?"; 628 } 629 630 document.body.style.display = "none"; 631 }, 632 633 /** 634 * Must be called before template loading, 635 * as immediately when jQuery Mobile script tag is inserted to DOM, 636 * some of its event handlers are run. 637 */ 638 bindTemplateEventHandlers : function() { 639 // Assign jQuery Mobile event handlers 640 $(window.document).bind("mobileinit", mobilize.onMobileInit); 641 }, 642 643 /** 644 * Start loading mobile template to DOM tree. 645 * 646 * Check possible mobile template cache places. 647 */ 648 loadMobileTemplate : function() { 649 650 var self = this; 651 652 // Create the element which will hold the mobile template 653 // + transformation result 654 $("body").append("<div id='mobile-template-holder'>"); 655 656 mobilize.bindTemplateEventHandlers(); 657 658 $("#mobile-template-holder").load(mobilize.options.template, function() { 659 self.transform(); 660 }); 661 }, 662 663 /** 664 * Put mobile template to DOM tree 665 */ 666 prepareMobileTemplate : function() { 667 668 }, 669 670 /** 671 * Get rid of mobile template 672 */ 673 closeMobileTemplate : function() { 674 }, 675 676 677 prepareTransform : function() { 678 679 if(!jQuery) { 680 throw "jQuery needed in order to run content transform"; 681 } 682 683 }, 684 685 /** 686 * Transform the web page content to mobile frame. 687 * 688 * Subclasses must override this. 689 * 690 * After the function has been finished mobilize.completeTransform() must 691 * be called to allow async handlers to proceed. 692 */ 693 transform : function() { 694 mobilize.constructHead(); 695 mobilize.constructBody(); 696 mobilize.completeTransform(); 697 }, 698 699 /** 700 * We can proceed with the page visual enhancements 701 */ 702 completeTransform : function() { 703 mobilize.transformComplete = true; 704 mobilize.prepareFinish(); 705 }, 706 707 /** 708 * @param href Link as a string 709 * 710 * @returns New link target as string or null if the link should be removed 711 */ 712 rewriteLinkTarget : function(href) { 713 return href; 714 }, 715 716 /** 717 * Based on mobilize options, rewrite link targets with mobile ones 718 * or hide links. 719 * 720 * @param callback to be called if the link is to be removed 721 */ 722 remapLinks : function(selection, removeCallback) { 723 selection.each(function() { 724 var input = $(this); 725 output = mobilize.rewriteLink(input); 726 if(!output) { 727 if(removeCallback) { 728 removeCallback(input); 729 } 730 } 731 }); 732 }, 733 734 /** 735 * 736 * @param {Object} a DOM node or jQuery object of <a> 737 * 738 * @returns null if the link is to be discarded 739 */ 740 rewriteLink : function(a) { 741 var a = $(a); 742 743 var href = a.attr("href"); 744 745 if(!href) { 746 // Not a link 747 return null; 748 } 749 750 href = mobilize.rewriteLinkTarget(href); 751 a.attr("href", href); 752 753 return a; 754 }, 755 756 /** 757 * Create <head> section of a mobile rendered version. 758 * 759 * The default transform is just to copy everything 760 * in #mobile-head from template to <head> of the page. 761 */ 762 constructHead : function() { 763 mobilize.log("constructHead"); 764 $("head").append($("#mobile-head").children()); 765 // Make events to be fired when each CSS/Javascript has been loadeds 766 }, 767 768 769 /** 770 * Create jQuery Mobile navigation links out of arbitary link list. 771 * 772 * Creates navigation or news box from existing jQuery content selection. 773 * The selection can be list or arbitary elements or list of a a hrefs. 774 * 775 * @param {Object} selection jQuery selection which to transform 776 * 777 * @param title If present add a link box with a title using ui-list 778 * 779 * @returns Constructed jQuery tree, ready to place to the document 780 */ 781 createNavigationBox : function(selection, title, outputter) { 782 783 var list; 784 785 list = $("<ul data-inset='true' data-role='listview'>"); 786 selection.each(function() { 787 788 var input = $(this); 789 var a; 790 var contentish; 791 792 mobilize.log("Creating navigation box link " + this); 793 794 // We can be iterating through <a> or <li> element 795 if(this.tagName.toLowerCase() == 'a') { 796 a = input; 797 contentish = false; 798 } else { 799 var content = input; 800 801 // Assume we have 0 or 1 links in the content HTML 802 a = content.find("a"); 803 if(a.size() == 0) { 804 a = null; 805 } 806 contentish = true; 807 } 808 809 if (a) { 810 var a = mobilize.rewriteLink(a); 811 } 812 813 if (outputter) { 814 outputter(list, input, a); 815 } else { 816 817 // Create normal bulleted lists 818 var output = $("<li role='option'>"); 819 820 if (a) { 821 output.append(a).appendTo(list); 822 } 823 824 if (contentish) { 825 // Format link content 826 output.appendTo(content.children()); 827 } 828 } 829 }); 830 831 if(title) { 832 list.prepend("<li data-role='list-divider'>" + title + "</li>"); 833 } 834 835 return list; 836 }, 837 838 /** 839 * Create <body> section of a mobile rendered version. 840 * 841 * This transformation is always CMS specific 842 * and your subclass must override this function. 843 */ 844 constructBody : function() { 845 }, 846 847 /** 848 * Make the transformed mobile template body visible and remove the other body data. 849 */ 850 swapBody : function() { 851 var mobileBody = $("#mobile-body").detach(); 852 $("body").empty(); 853 $("body").append(mobileBody.children()); 854 }, 855 856 /** 857 * Check that all async conditions have been completed allowing us to finish the page. 858 */ 859 prepareFinish : function() { 860 861 mobilize.log("prepareFinish()"); 862 if(!mobilize.jQueryMobileLoaded) { 863 mobilize.log("Waiting for jQuery Mobile to load"); 864 } 865 866 if(!mobilize.transformComplete) { 867 mobilize.log("Waiting transform() to complete"); 868 } 869 870 if(mobilize.jQueryMobileLoaded && mobilize.transformComplete) { 871 mobilize.finish(); 872 } 873 }, 874 875 /** 876 * Mobile transformation is done. Show mobile site to the user. 877 */ 878 finish : function() { 879 880 this.swapBody(); 881 882 // Draw jQuery Mobile chrome 883 try{ 884 $.mobile.initializePage(); 885 }catch(e){ 886 mobilize.log("mobilize::finish initializePage failed>" + e); 887 } 888 889 // Show constructed page to the user 890 $("body").show(); 891 892 // Execute handlers which can be run 893 // after jQuery Mobile has completed its internal transforms 894 mobilize.log("Triggering mobilizefinish"); 895 //$(document).trigger("mobilizefinish"); 896 897 mobilize.bindEventHandlers(); 898 }, 899 900 /** 901 * jQuery mobile initializer handler 902 * 903 * @param {Object} e 904 */ 905 onMobileInit : function(e) { 906 907 // We'll manage our own workflow and don't want to jQuery Mobile 908 // start doing things instantly when the script is loaded 909 mobilize.log("Disabling autoInitialize, was:" + $.mobile.autoInitialize); 910 $.mobile.autoInitialize = false; 911 $.mobile.ajaxEnabled = false; 912 913 mobilize.jQueryMobileLoaded = true; 914 mobilize.prepareFinish(); 915 916 }, 917 918 /** 919 * Subclass may override. 920 * 921 * This is called after jQuery Mobile has been set up. 922 * You can now attach event handlers to jQuery UI elements. 923 * 924 */ 925 bindEventHandlers : function() { 926 927 } 928 }; 929 930 if(typeof(exports) !== "undefined") 931 exports.mobilize = mobilize; 932