827282a6b7ec461b8aa085988649308bebc15e93
[platform/framework/web/web-ui-fw.git] / src / loader / loader.js
1 /**
2  * loader.js
3  *
4  * Youmin Ha <youmin.ha@samsung.com>
5  */
6
7 ( function ($, Globalize, window, undefined) {
8
9          var tizen = {
10                 libFileName : "tizen-web-ui-fw(.min)?.js",
11
12                 frameworkData : {
13                         rootDir: '/usr/lib/tizen-web-ui-fw',
14                         version: '0.1',
15                         theme: "tizen-white",
16                         viewportScale: false,
17                         defaultFontSize: 16,
18                         minified: false
19                 },
20
21                 util : {
22                         loadScriptSync : function ( scriptPath, successCB, errorCB ) {
23                                 $.ajax( {
24                                         url: scriptPath,
25                                         dataType: 'script',
26                                         async: false,
27                                         crossDomain: false,
28                                         success: successCB,
29                                         error: function ( jqXHR, textStatus, errorThrown ) {
30                                                 if ( errorCB ) {
31                                                         errorCB( jqXHR, textStatus, errorThrown );
32                                                 } else {
33                                                         var ignoreStatusList = [ 404 ];  // 404: not found
34                                                         if ( -1 == $.inArray( jqXHR.status, ignoreStatusList ) ) {
35                                                                 window.alert( 'Error while loading ' + scriptPath + '\n' + jqXHR.status + ':' + jqXHR.statusText );
36                                                         } else {
37                                                                 console.log( 'Error while loading ' + scriptPath + '\n' + jqXHR.status + ':' + jqXHR.statusText );
38                                                         }
39                                                 }
40                                         }
41                                 } );
42                         },
43                         getScaleFactor: function ( ) {
44                                 var factor = navigator.scale,
45                                         width = 0,
46                                         defaultWidth = 720;
47
48                                 if ( !factor ) {
49                                         width = screen.width < screen.height ? screen.width : screen.height;
50                                         factor = width / defaultWidth;
51                                         if ( factor > 1 ) {
52                                                 // NOTE: some targets(e.g iPad) need to set scale equal or less than 1.0
53                                                 factor = 1;
54                                         }
55                                 }
56                                 console.log( "ScaleFactor: " + factor );
57                                 return factor;
58                         },
59                         isMobileBrowser: function ( ) {
60                                 var mobileIdx = window.navigator.appVersion.indexOf("Mobile"),
61                                         isMobile = -1 < mobileIdx;
62                                 return isMobile;
63                         }
64                 },
65
66                 css : {
67                         cacheBust: ( document.location.href.match( /debug=true/ ) ) ?
68                                         '?cacheBust=' + ( new Date( ) ).getTime( ) :
69                                         '',
70                         addElementToHead : function ( elem ) {
71                                 var head = document.getElementsByTagName( 'head' )[0];
72                                 if( head ) {
73                                         $( head ).prepend( elem );
74                                 }
75                         },
76                         makeLink : function ( href ) {
77                                 var cssLink = document.createElement( 'link' );
78                                 cssLink.setAttribute( 'rel', 'stylesheet' );
79                                 cssLink.setAttribute( 'href', href );
80                                 cssLink.setAttribute( 'name', 'tizen-theme' );
81                                 return cssLink;
82                         },
83                         load: function ( path ) {
84                                 var head = document.getElementsByTagName( 'head' )[0],
85                                         cssLinks = head.getElementsByTagName( 'link' ),
86                                         idx,
87                                         l = null;
88                                 // Find css link element
89                                 for ( idx = 0; idx < cssLinks.length; idx++ ) {
90                                         if( cssLinks[idx].getAttribute( 'name' ) == "tizen-theme" ) {
91                                                 l = cssLinks[idx];
92                                                 break;
93                                         }
94                                 }
95                                 if ( l ) {      // Found the link element!
96                                         l.setAttribute( 'href', path );
97                                 } else {
98                                         this.addElementToHead( this.makeLink( path ) );
99                                 }
100                         }
101                 },
102
103                 getParams: function ( ) {
104                         /* Get data-* params from <script> tag, and set tizen.frameworkData.* values
105                          * Returns true if proper <script> tag is found, or false if not.
106                          */
107                         // Find current <script> tag element
108                         var scriptElems = document.getElementsByTagName( 'script' ),
109                                 val = null,
110                                 foundScriptTag = false,
111                                 idx,
112                                 elem,
113                                 src,
114                                 tokens,
115                                 version_idx;
116
117                         function getTizenTheme( ) {
118                                 var t = navigator.theme ? navigator.theme.split( ':' )[0] : null;
119                                 if ( t ) {
120                                         t = t.replace('-hd', '');
121                                         if( ! t.match( /^tizen-/ ) ) {
122                                                 t = 'tizen-' + t;
123                                         }
124                                 }
125                                 return t;
126                         }
127
128                         for ( idx in scriptElems ) {
129                                 elem = scriptElems[idx];
130                                 src = elem.src ? elem.getAttribute( 'src' ) : undefined;
131                                 if (src && src.match( this.libFileName )) {
132                                         // Set framework data, only when they are given.
133                                         tokens = src.split(/[\/\\]/);
134                                         version_idx = -3;
135                                         this.frameworkData.rootDir = elem.getAttribute( 'data-framework-root' )
136                                                 || tokens.slice( 0, tokens.length + version_idx ).join( '/' )
137                                                 || this.frameworkData.rootDir;
138                                         this.frameworkData.version = elem.getAttribute( 'data-framework-version' )
139                                                 || tokens[ tokens.length + version_idx ]
140                                                 || this.frameworkData.version;
141                                         this.frameworkData.theme = elem.getAttribute( 'data-framework-theme' )
142                                                 || getTizenTheme( )
143                                                 || this.frameworkData.theme;
144                                         this.frameworkData.viewportScale = "true" === elem.getAttribute( 'data-framework-viewport-scale' ) ? true : this.frameworkData.viewportScale;
145                                         this.frameworkData.minified = src.search(/\.min\.js$/) > -1 ? true : false;
146                                         foundScriptTag = true;
147                                         break;
148                                 }
149                         }
150                         return foundScriptTag;
151                 },
152
153                 loadTheme: function ( theme ) {
154                         var themePath, cssPath, jsPath;
155
156                         if ( ! theme ) {
157                                 theme = tizen.frameworkData.theme;
158                         }
159                         themePath = [
160                                         tizen.frameworkData.rootDir,
161                                         tizen.frameworkData.version,
162                                         'themes',
163                                         theme
164                                 ].join( '/' ),
165
166                         jsPath = [themePath, 'theme.js'].join( '/' );
167
168                         if( tizen.frameworkData.minified ) {
169                                 cssPath = [themePath, 'tizen-web-ui-fw-theme.min.css'].join( '/' );
170                         } else {
171                                 cssPath = [themePath, 'tizen-web-ui-fw-theme.css'].join( '/' );
172                         }
173                         tizen.css.load( cssPath );
174                         tizen.util.loadScriptSync( jsPath );
175                 },
176
177                 /** Load Globalize culture file, and set default culture.
178                  *  @param[in]  language  (optional) Language code. ex) en-US, en, ko-KR, ko
179                  *                        If language is not given, read language from html 'lang' attribute, 
180                  *                        or from system setting.
181                  *  @param[in]  cultureDic (optional) Dictionary having language code->
182                  */
183                 loadGlobalizeCulture: function ( language, cultureDic ) {
184                         var self = this,
185                                 cFPath,
186                                 lang,
187                                 mockJSXHR;
188
189                         function getLang ( language ) {
190                                 var lang = language
191                                                 || $( 'html' ).attr( 'lang' )
192                                                 || window.navigator.language.split( '.' )[0]    // Webkit, Safari + workaround for Tizen
193                                                 || window.navigator.userLanguage        // IE
194                                                 || 'en',
195                                         countryCode = null,
196                                         countryCodeIdx = lang.lastIndexOf('-'),
197                                         ignoreCodes = ['Cyrl', 'Latn', 'Mong']; // Not country code!
198                                 if ( countryCodeIdx != -1 ) {   // Found country code!
199                                         countryCode = lang.substr( countryCodeIdx + 1 );
200                                         if ( ignoreCodes.join( '-' ).indexOf( countryCode ) < 0 ) {
201                                                 // countryCode is not found from ignoreCodes.
202                                                 // Make countryCode to uppercase.
203                                                 lang = [ lang.substr( 0, countryCodeIdx ), countryCode.toUpperCase( ) ].join( '-' );
204                                         }
205                                 }
206                                 // NOTE: 'en' to 'en-US', because globalize has no 'en' culture file.
207                                 lang = lang == 'en' ? 'en-US' : lang;
208                                 return lang;
209                         }
210
211                         function getNeutralLang ( lang ) {
212                                 var neutralLangIdx = lang.lastIndexOf( '-' ),
213                                         neutralLang;
214                                 if ( neutralLangIdx != -1 ) {
215                                         neutralLang = lang.substr( 0, neutralLangIdx );
216                                 }
217                                 return neutralLang;
218                         }
219
220                         function getCultureFilePath ( lang, cFDic ) {
221                                 var cFPath = null;      // error value
222
223                                 if ( "string" != typeof lang ) {
224                                         return null;
225                                 }
226                                 if ( cFDic ) {
227                                         if ( cFDic[lang] ) cFPath = cFDic[lang];
228                                 } else {
229                                         // Default Globalize culture file path
230                                         cFPath = [
231                                                 self.frameworkData.rootDir,
232                                                 self.frameworkData.version,
233                                                 'js',
234                                                 'cultures',
235                                                 ['globalize.culture.', lang, '.js'].join( '' ),
236                                         ].join( '/' );
237                                 }
238                                 return cFPath;
239                         }
240
241                         function printLoadError( cFPath, jqXHR ) {
242                                 console.log( "Error " + jqXHR.status + ": " + jqXHR.statusText );
243                                 console.log( "::Culture file (" + cFPath + ") is failed to load.");
244                         }
245
246                         function loadCultureFile ( cFPath, errCB ) {
247                                 function _successCB ( ) {
248                                         console.log( "Culture file (" + cFPath + ") is loaded successfully.");
249                                 }
250                                 function _errCB ( jqXHR, textStatus, err ) {
251                                         if( errCB ) {
252                                                 errCB( jqXHR, textStatus, err );
253                                         }
254                                         else {
255                                                 printLoadError( cFPath, jqXHR );
256                                         }
257                                 }
258
259                                 if( ! cFPath ) {        // Invalid cFPath -> Regard it as '404 Not Found' error.
260                                         mockJSXHR = {
261                                                 status: 404,
262                                                 statusText: "Not Found"
263                                         };
264                                         _errCB( mockJSXHR, null, null );
265                                 } else {
266                                         $.ajax( {
267                                                 url: cFPath,
268                                                 dataType: 'script',
269                                                 cache: true,
270                                                 async: false,
271                                                 success: _successCB,
272                                                 error: _errCB
273                                         } );
274                                 }
275                         }
276
277                         lang = getLang( language );
278                         cFPath = getCultureFilePath( lang, cultureDic );
279                         loadCultureFile( cFPath,
280                                 function ( jqXHR, textStatus, err ) {
281                                         if( jqXHR.status == 404 ) {
282                                                 // If culture file is not found, try once more with neutral lang.
283                                                 var nLang = getNeutralLang( lang ),
284                                                         cFPath = getCultureFilePath( nLang, cultureDic );
285                                                 loadCultureFile( cFPath, null );
286                                         } else {
287                                                 printLoadError( cFPath, jqXHR );
288                                         }
289                                 } );
290
291                         return lang;
292                 },
293                 setGlobalize: function ( ) {
294                         var lang = this.loadGlobalizeCulture( );
295
296                         // Set culture
297                         // NOTE: It is not needed to set with neutral lang.
298                         //       Globalize automatically deals with it.
299                         Globalize.culture( lang );
300                 },
301                 /**
302                  * Load custom globalize culture file
303                  * Find current system language, and load appropriate culture file from given colture file list.
304                  *
305                  * @param[in]   cultureDic      collection of 'language':'culture file path' key-val pair.
306                  * @example
307                  * var myCultures = {
308                  *              "en"    : "culture/en.js",
309                  *              "fr"    : "culture/fr.js",
310                  *              "ko-KR" : "culture/ko-KR.js"
311                  * };
312                  * loadCultomGlobalizeCulture( myCultures );
313                  *
314                  * ex) culture/fr.js
315                  * -------------------------------
316                  * Globalize.addCultureInfo( "fr", {
317                  *   messages: {
318                  *     "hello" : "bonjour",
319                  *     "translate" : "traduire"
320                  *   }
321                  * } );
322                  * -------------------------------
323                  */
324                 loadCustomGlobalizeCulture: function ( cultureDic ) {
325                         tizen.loadGlobalizeCulture( null, cultureDic );
326                 },
327
328                 /** Set viewport meta tag for mobile devices.
329                  *
330                  * @param[in]   viewportWidth   Viewport width. 'device-dpi' is also allowed.
331                  * @param[in]   useAutoScale    If true, cculate & use scale factor. otherwise, scale factor is 1.
332                  * @param[in]   useDeviceDpi    If true, add 'target-densityDpi=device-dpi' to viewport meta content.
333                  */
334                 setViewport: function ( viewportWidth, useAutoScale, useDeviceDpi ) {
335                         var meta,
336                                 scale = 1,
337                                 head,
338                                 content,
339                                 ratio,
340                                 threshold = 15,
341                                 standardWidth = 360,
342                                 screenWidth = screen.width;
343
344                         // Do nothing if viewport setting code is already in the code.
345                         $( "meta[name=viewport]" ).each( function ( ) {
346                                 console.log( "User set viewport... framework viewport will not be applied." );
347                                 meta = this;
348                                 return;
349                         });
350                         if( meta ) {
351                                 content = $( meta ).prop( "content" );
352                                 if ( content.indexOf( "device-width" ) > 0
353                                                 && content.indexOf( "device-dpi" ) > 0 ) {
354                                         ratio = screenWidth > standardWidth ? ( screenWidth/standardWidth) : 1;
355                                         $.vmouse.moveDistanceThreshold = threshold * ratio;
356                                         $.vmouse.clickDistanceThreshold = threshold * ratio;
357                                 }
358                                 return; // Ignore viewport setting, when viewport is already set.
359                         }
360
361                         // Set meta tag
362                         meta = document.createElement( "meta" );
363                         if ( meta ) {
364                                 scale = useAutoScale ? this.util.getScaleFactor( ) : scale;
365                                 meta.name = "viewport";
366                                 meta.content = "width=" + viewportWidth + ", initial-scale=" + scale + ", maximum-scale=" + scale + ", user-scalable=0";
367                                 if ( useDeviceDpi ) {
368                                         meta.content += ", target-densityDpi=device-dpi";
369                                 }
370                                 console.log( meta.content );
371                                 head = document.getElementsByTagName( 'head' ).item( 0 );
372                                 head.insertBefore( meta, head.firstChild );
373
374                                 // TODO : change threshold when scaleFactor is changed. Reference line 354-356
375                         }
376                 },
377
378                 /**     Read body's font-size, scale it, and reset it.
379                  *  param[in]   desired font-size / base font-size.
380                  */
381                 scaleBaseFontSize: function ( themeDefaultFontSize, ratio ) {
382                         var scaledFontSize = Math.round( themeDefaultFontSize * ratio );
383
384                         $( 'html.ui-mobile' ).css( { 'font-size': scaledFontSize + "px" } );
385                         console.log('html:font size is set to ' + scaledFontSize );
386                         $( document ).ready( function ( ) {
387                                 $( '.ui-mobile').children( 'body' ).css( { 'font-size': scaledFontSize + "px" } );
388                         } );
389                 },
390
391                 setScaling: function ( ) {
392                         var baseWidth = 720,            // Winset GUI Guide is 720 HD.
393                                 standardWidth = 360,
394                                 themeDefaultFontSize;
395
396                         themeDefaultFontSize = this.frameworkData.defaultFontSize;
397
398                         $( 'body' ).attr( 'data-tizen-theme-default-font-size', themeDefaultFontSize );
399
400                         if ( this.frameworkData.viewportScale ) {
401                                 // Use viewport scaling with base font-size
402                                 // NOTE: No font-size setting is needed.
403                                 this.setViewport( baseWidth, true, true );
404                         } else {
405                                 // Fixed viewport scale(=1.0) with scaled font size
406                                 this.setViewport( "device-width", false, undefined );
407                                 this.scaleBaseFontSize( themeDefaultFontSize, parseFloat( standardWidth / baseWidth ) );
408                         }
409                 }
410         };
411
412         function export2TizenNS ( $, tizen ) {
413                 if ( undefined == typeof $.tizen ) {
414                         $.tizen = { };
415                 }
416
417                 $.tizen.frameworkData = tizen.frameworkData;
418                 $.tizen.loadCustomGlobalizeCulture = tizen.loadCustomGlobalizeCulture;
419                 $.tizen.loadTheme = tizen.loadTheme;
420
421                 $.tizen.__tizen__ = tizen;      // for unit-test
422         }
423
424         export2TizenNS( $, tizen );
425
426         tizen.getParams( );
427         tizen.loadTheme( );
428         tizen.setScaling( );    // Run after loadTheme(), for the default font size.
429         tizen.setGlobalize( );
430
431         // Turn off JQM's auto initialization option.
432         // NOTE: This job must be done before domready.
433         $.mobile.autoInitializePage = false;
434
435         $(document).ready( function ( ) {
436                 $.mobile.initializePage( );
437         });
438
439 } ( jQuery, window.Globalize, window ) );