1 //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
2 //>>description: Loader doing theme loading, viewport setting, globalize loading, etc.
6 define( [ 'jquery.mobile.tizen.core' ], function ( ) {
7 //>>excludeEnd("jqmBuildExclude");
13 * Youmin Ha <youmin.ha@samsung.com>
18 Web UI scaling concept in Tizen Web UI
20 Generally, web applications must be designed to be showed acceptable on various size and resolution of screens, and web winsets have to be scaled well. Tizen Web UI Framework supports various viewport settings, and Tizen Web UI widgets are designed to be scalable on various screen sizes. In order to make web applications scalable on many devices which have different screen size, it is necessary to understand how mobile web browsers deal with screen resolution, and how Tizen Web UI Framework supports scaling for web applications.
23 * Viewport on mobile web browser
25 Viewport is an area showing web content on the browser. Unlike desktop browsers, mobile browsers support logical viewport seting, which means that application can set viewport width/height and zoom level by itself.
26 The very important thing that to be remembered is that the viewport resolution in pixel is 'Logical', not physical. For example, if the viewport width is set to 480 on a mobile device having 720px screen width, the viewport width is considered to 480px logically. All elements put on right side from 480px horizontal position will not be shown on the viewport.
27 Most mobile browsers set viewport with given content attribute with <meta name="viewport" content="..."> tag in <head> section in the application source html, whereas desktop browsers ignore the tag.
28 Detailed usage of viewport meta tag is found in here: http://www.w3.org/TR/mwabp/#bp-viewport
31 * Viewport setting by application developers
33 When developers write <meta name="viewport" content="..."> in the <head> section of the web application HTML file, Tizen Web UI Framework does not add another viewport meta tag, nor modify developer-defined viewport.
36 * Automatic viewport setting by Tizen Web UI Framework
38 If developers do not give a viewport meta tag, Tizen Web UI Framework automatically add a viewport meta tag with default viewport setting.
41 * Portrait/landscape mode
44 * Tizen Web UI widgets scaling
48 ( function ($, Globalize, window, undefined) {
51 libFileName : "tizen-web-ui-fw(.min)?.js",
54 rootDir: '/usr/lib/tizen-web-ui-fw',
57 viewportWidth: "device-width",
67 debug : function ( msg ) {
68 if ( tizen.frameworkData.debug ) {
72 warn : function ( msg ) {
75 error : function ( msg ) {
78 alert : function ( msg ) {
85 loadScriptSync : function ( scriptPath, successCB, errorCB ) {
92 error: function ( jqXHR, textStatus, errorThrown ) {
94 errorCB( jqXHR, textStatus, errorThrown );
96 var ignoreStatusList = [ 404 ], // 404: not found
97 errmsg = ( 'Error while loading ' + scriptPath + '\n' + jqXHR.status + ':' + jqXHR.statusText );
98 if ( -1 == $.inArray( jqXHR.status, ignoreStatusList ) ) {
99 tizen.log.alert( errmsg );
101 tizen.log.warn( errmsg );
107 isMobileBrowser: function ( ) {
108 var mobileIdx = window.navigator.appVersion.indexOf("Mobile"),
109 isMobile = -1 < mobileIdx;
115 cacheBust: ( document.location.href.match( /debug=true/ ) ) ?
116 '?cacheBust=' + ( new Date( ) ).getTime( ) :
118 addElementToHead : function ( elem ) {
119 var head = document.getElementsByTagName( 'head' )[0];
121 $( head ).prepend( elem );
124 makeLink : function ( href ) {
125 var cssLink = document.createElement( 'link' );
126 cssLink.setAttribute( 'rel', 'stylesheet' );
127 cssLink.setAttribute( 'href', href );
128 cssLink.setAttribute( 'name', 'tizen-theme' );
131 load: function ( path ) {
132 var head = document.getElementsByTagName( 'head' )[0],
133 cssLinks = head.getElementsByTagName( 'link' ),
136 // Find css link element
137 for ( idx = 0; idx < cssLinks.length; idx++ ) {
138 if ( cssLinks[idx].getAttribute( 'rel' ) != "stylesheet" ) {
141 if ( cssLinks[idx].getAttribute( 'name' ) == "tizen-theme"
142 || cssLinks[idx].getAttribute( 'href' ) == path ) {
147 if ( l ) { // Found the link element!
148 if ( l.getAttribute( 'href' ) == path ) {
149 tizen.log.debug( "Theme is already loaded. Skip theme loading in the framework." );
151 l.setAttribute( 'href', path );
154 this.addElementToHead( this.makeLink( path ) );
159 getParams: function ( ) {
160 /* Get data-* params from <script> tag, and set tizen.frameworkData.* values
161 * Returns true if proper <script> tag is found, or false if not.
163 // Find current <script> tag element
164 var scriptElems = document.getElementsByTagName( 'script' ),
166 foundScriptTag = false,
173 function getTizenTheme( ) {
174 var t = navigator.theme ? navigator.theme.split( ':' )[0] : null;
176 t = t.replace('-hd', '');
177 if ( ! t.match( /^tizen-/ ) ) {
184 for ( idx in scriptElems ) {
185 elem = scriptElems[idx];
186 src = elem.src ? elem.getAttribute( 'src' ) : undefined;
187 if (src && src.match( this.libFileName )) {
188 // Set framework data, only when they are given.
189 tokens = src.split(/[\/\\]/);
191 this.frameworkData.rootDir = ( elem.getAttribute( 'data-framework-root' )
192 || tokens.slice( 0, tokens.length + version_idx ).join( '/' )
193 || this.frameworkData.rootDir ).replace( /^file:(\/\/)?/, '' );
194 this.frameworkData.version = elem.getAttribute( 'data-framework-version' )
195 || tokens[ tokens.length + version_idx ]
196 || this.frameworkData.version;
197 this.frameworkData.theme = elem.getAttribute( 'data-framework-theme' )
199 || this.frameworkData.theme;
200 this.frameworkData.viewportWidth = elem.getAttribute( 'data-framework-viewport-width' )
201 || this.frameworkData.viewportWidth;
202 this.frameworkData.viewportScale =
203 "true" === elem.getAttribute( 'data-framework-viewport-scale' ) ? true
204 : this.frameworkData.viewportScale;
205 this.frameworkData.minified = src.search(/\.min\.js$/) > -1 ? true : false;
206 this.frameworkData.debug = "true" === elem.getAttribute( 'data-framework-debug' ) ? true
207 : this.frameworkData.debug;
208 foundScriptTag = true;
212 return foundScriptTag;
215 loadTheme: function ( theme ) {
221 theme = tizen.frameworkData.theme;
224 themePath = tizen.frameworkData.rootDir + '/' + tizen.frameworkData.version + '/themes/' + theme;
226 jsPath = themePath + '/theme.js';
228 if ( tizen.frameworkData.minified ) {
229 cssPath = themePath + '/tizen-web-ui-fw-theme.min.css';
231 cssPath = themePath + '/tizen-web-ui-fw-theme.css';
233 tizen.css.load( cssPath );
234 tizen.util.loadScriptSync( jsPath );
237 /** Load Globalize culture file, and set default culture.
238 * @param[in] language (optional) Language code. ex) en-US, en, ko-KR, ko
239 * If language is not given, read language from html 'lang' attribute,
240 * or from system setting.
241 * @param[in] cultureDic (optional) Dictionary having language code->
243 loadGlobalizeCulture: function ( language, cultureDic ) {
249 function getLang ( language ) {
251 || $( 'html' ).attr( 'lang' )
252 || window.navigator.language.split( '.' )[0] // Webkit, Safari + workaround for Tizen
253 || window.navigator.userLanguage // IE
256 countryCodeIdx = lang.lastIndexOf('-'),
257 ignoreCodes = ['Cyrl', 'Latn', 'Mong']; // Not country code!
258 if ( countryCodeIdx != -1 ) { // Found country code!
259 countryCode = lang.substr( countryCodeIdx + 1 );
260 if ( ignoreCodes.join( '-' ).indexOf( countryCode ) < 0 ) {
261 // countryCode is not found from ignoreCodes.
262 // Make countryCode to uppercase.
263 lang = [ lang.substr( 0, countryCodeIdx ), countryCode.toUpperCase( ) ].join( '-' );
266 // NOTE: 'en' to 'en-US', because globalize has no 'en' culture file.
267 lang = lang == 'en' ? 'en-US' : lang;
271 function getNeutralLang ( lang ) {
272 var neutralLangIdx = lang.lastIndexOf( '-' ),
274 if ( neutralLangIdx != -1 ) {
275 neutralLang = lang.substr( 0, neutralLangIdx );
280 function getCultureFilePath ( lang, cFDic ) {
281 var cFPath = null; // error value
283 if ( "string" != typeof lang ) {
286 if ( cFDic && cFDic[lang] ) {
287 cFPath = cFDic[lang];
289 // Default Globalize culture file path
291 self.frameworkData.rootDir,
292 self.frameworkData.version,
295 ['globalize.culture.', lang, '.js'].join( '' )
301 function printLoadError( cFPath, jqXHR ) {
302 tizen.log.error( "Error " + jqXHR.status + ": " + jqXHR.statusText
303 + "::Culture file (" + cFPath + ") is failed to load.");
306 function loadCultureFile ( cFPath, errCB ) {
307 function _successCB ( ) {
308 tizen.log.debug( "Culture file (" + cFPath + ") is loaded successfully." );
310 function _errCB ( jqXHR, textStatus, err ) {
312 errCB( jqXHR, textStatus, err );
314 printLoadError( cFPath, jqXHR );
318 if ( ! cFPath ) { // Invalid cFPath -> Regard it as '404 Not Found' error.
321 statusText: "Not Found"
323 _errCB( mockJSXHR, null, null );
336 lang = getLang( language );
337 cFPath = getCultureFilePath( lang, cultureDic );
338 loadCultureFile( cFPath,
339 function ( jqXHR, textStatus, err ) {
340 if ( jqXHR.status == 404 ) {
341 // If culture file is not found, try once more with neutral lang.
342 var nLang = getNeutralLang( lang ),
343 ncFPath = getCultureFilePath( nLang, cultureDic );
344 loadCultureFile( ncFPath, null );
346 printLoadError( cFPath, jqXHR );
352 setGlobalize: function ( ) {
353 var lang = this.loadGlobalizeCulture( );
356 // NOTE: It is not needed to set with neutral lang.
357 // Globalize automatically deals with it.
358 Globalize.culture( lang );
361 * Load custom globalize culture file
362 * Find current system language, and load appropriate culture file from given colture file list.
364 * @param[in] cultureDic collection of 'language':'culture file path' key-val pair.
367 * "en" : "culture/en.js",
368 * "fr" : "culture/fr.js",
369 * "ko-KR" : "culture/ko-KR.js"
371 * loadCultomGlobalizeCulture( myCultures );
374 * -------------------------------
375 * Globalize.addCultureInfo( "fr", {
377 * "hello" : "bonjour",
378 * "translate" : "traduire"
381 * -------------------------------
383 loadCustomGlobalizeCulture: function ( cultureDic ) {
384 tizen.loadGlobalizeCulture( null, cultureDic );
387 /** Set viewport meta tag for mobile devices.
389 * @param[in] viewportWidth viewport width. "device-width" is OK.
391 setViewport: function ( viewportWidth ) {
396 // Do nothing if viewport setting code is already in the code.
397 $( "meta[name=viewport]" ).each( function ( ) {
401 if ( meta ) { // Found custom viewport!
402 content = $( meta ).prop( "content" );
403 viewportWidth = content.replace( /.*width=(device-width|\d+)\s*,?.*$/gi, "$1" );
404 tizen.log.debug( "Viewport is set to '" + viewportWidth + "' in a meta tag. Framework skips viewport setting." );
407 meta = document.createElement( "meta" );
409 meta.name = "viewport";
410 content = "width=" + viewportWidth + ", user-scalable=no";
411 if ( ! isNaN( viewportWidth ) ) {
412 // Fix scale to 1.0, if viewport width is set to fixed value.
413 // NOTE: Works wrong in Tizen browser!
414 //content = [ content, ", initial-scale=1.0, maximum-scale=1.0" ].join( "" );
416 meta.content = content;
417 tizen.log.debug( content );
418 head = document.getElementsByTagName( 'head' ).item( 0 );
419 head.insertBefore( meta, head.firstChild );
422 return viewportWidth;
425 /** Read body's font-size, scale it, and reset it.
426 * param[in] desired font-size / base font-size.
428 scaleBaseFontSize: function ( themeDefaultFontSize, ratio ) {
429 tizen.log.debug( "themedefaultfont size: " + themeDefaultFontSize + ", ratio: " + ratio );
430 var scaledFontSize = Math.max( Math.floor( themeDefaultFontSize * ratio ), 4 );
432 $( 'html' ).css( { 'font-size': scaledFontSize + "px" } );
433 tizen.log.debug( 'html:font size is set to ' + scaledFontSize );
434 $( document ).ready( function ( ) {
435 $( '.ui-mobile' ).children( 'body' ).css( { 'font-size': scaledFontSize + "px" } );
439 setScaling: function ( ) {
440 var viewportWidth = this.frameworkData.viewportWidth,
441 themeDefaultFontSize = this.frameworkData.defaultFontSize, // comes from theme.js
444 // Keep original font size
445 $( 'body' ).attr( 'data-tizen-theme-default-font-size', themeDefaultFontSize );
447 if ( !tizen.util.isMobileBrowser() ) {
451 // Legacy support: tizen.frameworkData.viewportScale
452 if ( this.frameworkData.viewportScale == true ) {
453 viewportWidth = "screen-width";
456 // screen-width support
457 if ( "screen-width" == viewportWidth ) {
458 if ( window.self == window.top ) {
459 // Top frame: for target. Use window.outerWidth.
460 viewportWidth = window.outerWidth;
462 // iframe: for web simulator. Use clientWidth.
463 viewportWidth = document.documentElement.clientWidth;
467 // set viewport meta tag
468 viewportWidth = this.setViewport( viewportWidth ); // If custom viewport setting exists, get viewport width
470 if ( viewportWidth == "device-width" ) {
472 } else { // fixed width!
473 ratio = parseFloat( viewportWidth / this.frameworkData.defaultViewportWidth );
474 this.scaleBaseFontSize( themeDefaultFontSize, ratio );
479 function export2TizenNS ( $, tizen ) {
484 $.tizen.frameworkData = tizen.frameworkData;
485 $.tizen.loadCustomGlobalizeCulture = tizen.loadCustomGlobalizeCulture;
486 $.tizen.loadTheme = tizen.loadTheme;
488 $.tizen.__tizen__ = tizen; // for unit-test
491 export2TizenNS( $, tizen );
495 tizen.setScaling( ); // Run after loadTheme(), for the default font size.
496 tizen.setGlobalize( );
497 // Turn off JQM's auto initialization option.
498 // NOTE: This job must be done before domready.
499 $.mobile.autoInitializePage = false;
501 $(document).ready( function ( ) {
502 $.mobile.initializePage( );
505 } ( jQuery, window.Globalize, window ) );
507 //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
509 //>>excludeEnd("jqmBuildExclude");