하이브리드앱

 

 

 

 전체 - 추가할 코드

 

    <!-- iOS, 안드로이드 크롬 브라우저 용 웹앱 전체 화면 설정 -->
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="mobile-web-app-capable" content="yes"/>  

       
	<!-- iOS 와 안드로이드 크롬 브라우저 용 웹클립 아이콘 설정 -->
    <link rel="apple-touch-icon" sizes="76x76" href="my_ios_icon_76.png"/>
    <link rel="apple-touch-icon" sizes="120x120" href="my_iphone_120.png"/>
    <link rel="apple-touch-icon" sizes="152x152" href="my_ipad_152.png"/>
    <link rel="shortcut icon" sizes="196x196" href="my_chrome_icon_196.png"/>    
   

   <!--  iOS 전용 시작화면 설정 -->
    <link rel="apple-touch-startup-image" href="startup.png"/>      
    <!-- iOS 전용 상태바 모습 설정 -->
    <meta name="apple-mobile-web-app-status-bar-style" content="black"/>




	<meta name="viewport" content="width=device-width, initial-scale=1.0" />
	<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.2/jquery.mobile-1.4.2.min.css" />
    <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
    <script src="http://code.jquery.com/mobile/1.4.2/jquery.mobile-1.4.2.min.js"></script>
    
    <!-- fotorama CDN 설정 추가 -->
    <link href="http://cdnjs.cloudflare.com/ajax/libs/fotorama/4.5.1/fotorama.css" rel="stylesheet">
    <script src="http://cdnjs.cloudflare.com/ajax/libs/fotorama/4.5.1/fotorama.js"></script>
    <!-- 링크 클릭 시 Full Screen을 유지하게 함 -->
    <script type="text/javascript" src="keep_full_screen.js"></script>

 

 

 

< 아이폰과 안드로이드 둘다 적용 >

사이즈 상관없이 아이콘 크기를 공통으로 적용할 경우 

<link rel="apple-touch-icon" href="my_custom_icon.png" />

 

만약 아이콘 크기도 지정하고 싶다면 다음처럼 'size'를 추가 지정할 수 있습니다.

	<!-- iOS, 안드로이드 크롬 브라우저 용 웹클립 아이콘 설정 -->
    <link rel="apple-touch-icon" sizes="76x76" href="my_ios_icon_76.png"/>
    <link rel="apple-touch-icon" sizes="120x120" href="my_iphone_120.png"/>
    <link rel="apple-touch-icon" sizes="152x152" href="my_ipad_152.png"/>

 

애플의 ios 와 비슷하게 안드로이드에서도 홈 화면용 아이콘을 사용할 수 있습니다. 안드로이드에서는 표시할 아이콘의 크기로

196*196 크기를 추천하므로 다음처럼 지정합니다.

    <link rel="shortcut icon" sizes="196x196" href="my_chrome_icon_196.png"/>   

 

==>요약 , IOS 와 안드로이드 아이콘 설정

	<!-- iOS, 안드로이드 크롬 브라우저 용 웹클립 아이콘 설정 -->
    <link rel="apple-touch-icon" sizes="76x76" href="my_ios_icon_76.png"/>
    <link rel="apple-touch-icon" sizes="120x120" href="my_iphone_120.png"/>
    <link rel="apple-touch-icon" sizes="152x152" href="my_ipad_152.png"/>
    <link rel="shortcut icon" sizes="196x196" href="my_chrome_icon_196.png"/>    

 

 

 

 

 

 

시작 이미지는 인터넷 접속하는 잠깐의 시간 동안 로딩 중이라고 표시하여 사용자가 현재 앱 로딩 상황을 인식하는 목적으로 사용되기도

하지만, '에어플레인 모드'처럼 완전히 통신이 멈췄을 때도 나타나기 때문에 매우 유용합니다. 사용하는 방법은 다음과 같은 태그를 

넣어주면 됩니다.

    <!--  iOS 전용 시작화면 설정 -->

    <!--  iOS 전용 시작화면 설정 -->
    <link rel="apple-touch-startup-image" href="startup.png"/>      

 

 

 

 

 

모바일웹은 태생적으로 웹으로 만들어졌으므로 반드시 브라우저를 통해서 접속해야 합니다.

그런데 브라우저는 상단의 주소줄이 항상 표시됩니다. 이러한 브라우저 UI 컴포넌트를 숨겨서 화면을 더욱 넓힐 수 있습니다.

그리고 화면 상단에는 상태바만 작게 나타나서 진짜 네이티브앱처럼 나타나게 해줍니다.

 

 iOS  와 안드로이드 크롬 브라우저 용 웹앱 전체 화면 설정 

    <!-- iOS, 안드로이드 크롬 브라우저 용 웹앱 전체 화면 설정 -->
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="mobile-web-app-capable" content="yes"/>     

 

 

이제 사파리 브라우저의 UI 컴포넌트도 사라지게 했고 상태바만 나타나게 했습니다. 마지막으로 남은 것은 상태바의 스타일을

바꾸는 것입니다. 상태바의 스타일은 크게 세 가지 모드를 지원합니다. 다음 그림처럼 일반(default), 검정색(black),

투명색 (black-translucent) 등으로 나눌 수 있습니다.

 

 

디바이스 상태바의 스타일 종류별 모습입니다. default 와 black 모두 상태바 밑에 웹 페이지가 표시되지만 balck-translucent를

입력하면 전체 화면이 되면서 상태바 밑에 페이지가 투명하게 비쳐 보입니다.

사용하는 방법은 다음과 같은 태그를 추가하면 됩니다.

 

    <!-- iOS 전용 상태바 모습 설정 -->
    <meta name="apple-mobile-web-app-status-bar-style" content="black"/>

 

name에는 "apple-mobile-web-app-staus-bar-style" 을 입력해서 웹앱의 상태바 스타일을 변경하겠다고 알려줍니다.

여기서는 "black"으로 지정하였습니다. 이 기능은 애플의 ios에서만 지원합니다.

 

 

IOS 에서 링크 클릭 때 새로운 브라우저를 안 뜨게 하기

 

JQuery Mobile에서 rel="external" 을 준 경우 안드로이드폰은 괜챃으나 ios에서 링트를 클릭하면 새 브라우저가 뜨면서 전체 화면이

사라지게 됩니다. 이런 경우 일관된 사용자 경험을 깨뜨립니다. 이것을 해결하는 방법은 아래 자바스크립트 코드를 모든 html 문서의 

처음 부분에 추가해 주면 됩니다.

 

 keep_full_screen.js 

// 링크 클릭시 새로운 브라우저가 뜨지 않게 함 
// 유의: 장면전환효과는 기본값만 실행됩니다. 
$(document).ready(function(){
      $('a').on('click', function(e){
        e.preventDefault();
        var new_location = $(this).attr('href');
		window.location = new_location;
      });
});	  

 

위의 소스코드는 링크가 클릭되면 rel="external" 에 의해 새로운 창이 뜨지 않고 자바스크립트의 window.location 객체를 이용해서

현재 창으로만 <a> 태그의 "href" 특성으로 전달받은 링크 주소로 이동합니다. 여기서 e.preventDefault() 는  <a> 태그가 원래 수행해야

했던 기능을 강제로 멈추게 합니다.

 

 

 

 

 

 

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>버추얼대학교</title>
    <!-- iOS, 안드로이드 크롬 브라우저 용 웹앱 전체 화면 설정 -->
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="mobile-web-app-capable" content="yes"/>         
	<!-- iOS, 안드로이드 크롬 브라우저 용 웹클립 아이콘 설정 -->
    <link rel="apple-touch-icon" sizes="76x76" href="my_ios_icon_76.png"/>
    <link rel="apple-touch-icon" sizes="120x120" href="my_iphone_120.png"/>
    <link rel="apple-touch-icon" sizes="152x152" href="my_ipad_152.png"/>
    <link rel="shortcut icon" sizes="196x196" href="my_chrome_icon_196.png"/>    
    <!--  iOS 전용 시작화면 설정 -->
    <link rel="apple-touch-startup-image" href="startup.png"/>      
    <!-- iOS 전용 상태바 모습 설정 -->
    <meta name="apple-mobile-web-app-status-bar-style" content="black"/>

	<meta name="viewport" content="width=device-width, initial-scale=1.0" />
	<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.2/jquery.mobile-1.4.2.min.css" />
    <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
    <script src="http://code.jquery.com/mobile/1.4.2/jquery.mobile-1.4.2.min.js"></script>
    
    <!-- fotorama CDN 설정 추가 -->
    <link href="http://cdnjs.cloudflare.com/ajax/libs/fotorama/4.5.1/fotorama.css" rel="stylesheet">
    <script src="http://cdnjs.cloudflare.com/ajax/libs/fotorama/4.5.1/fotorama.js"></script>
    <!-- 링크 클릭 시 Full Screen을 유지하게 함 -->
    <script type="text/javascript" src="keep_full_screen.js"></script>

   <!-- 대학교 소개 자바스크립트 설정 추가-->
    <script type="text/javascript" >
		$(document).ready(function() {
			// 처음 flag값을 false로 초기화 시킴
			$("#pic_univ").data("flag","false");
			
			// flag 값에 따라 스타일을 small과 large를 교환하며 적용
			$("#pic_univ").click(  function() {				
				var bflag = $("#pic_univ").data("flag");	
				if (bflag == "false" ) {
					bflag = "true";
					$("#pic_univ").removeClass("small");
					$("#pic_univ").addClass("large");
				} else {
					bflag = "false";
					$("#pic_univ").removeClass("large");
					$("#pic_univ").addClass("small");
				};
				$("#pic_univ").data("flag",bflag);
			} );				
		});
	</script>    
    
	<style type="text/css">
		/* 메인 페이지 용 */
		#logo {
			width:176px;
			height:132px;
			text-indent:-9999px;
			background-image: url(images/CI_TITLE.png);
			background-repeat: no-repeat;
			margin-top: 0px;
			margin-right: auto;
			margin-bottom: 10px;
			margin-left: auto;
		}   
		.title_style {
			color: #3F3C30;
			padding-top: 10px;
		}
		.fotorama__caption {
		text-align: center;
		}	   
		
		/* 대학교 소개용*/
		#pic_univ {
			float: left;
			margin-right: 10px;
			margin-bottom: 10px;
			-webkit-transition-property: width;
			-webkit-transition-duration: .3s;
		}
		.small {
			width: 30%;
		}
		.large {
			width: 100%;
		}
		
		.clear_both {
			clear: both;
		}		
		.bold_style {
			font-weight: bold;
			color: #950000;
		}
	</style>


</head>

<body>	
	<!-- 메인 페이지-->
    <div data-role="page" id="main" >
    	<div data-role="header" data-theme="b">     
			<h2 id="logo">가상 대학교</h2>
        </div>         
        <div data-role="content">
            <ul data-role="listview" data-inset="true"> 
                <li><a href="#campus_images" data-transition="slide">
                            <img src="images/pic_i.png" alt="" class="ui-li-thumb" />
                            <h3 class="title_style">캠퍼스 이미지</h3>
                      </a>
                </li> 
                <li ><a href="#intro" data-transition="slide">
                            <img src="images/intro_i.png" alt="" class="ui-li-thumb"/>
                            <h3 class="title_style">대학교소개</h3>
                        </a>
                </li> 
                <li><a href="video.html"  rel="external">
                            <img src="images/movie_i.png" alt="" class="ui-li-thumb"/>
                            <h3 class="title_style">홍보동영상</h3>
                      </a>
                </li> 
                <li><a href="map.html"  rel="external">
                            <img src="images/map_i.png" alt="" class="ui-li-thumb" />
                            <h3 class="title_style">캠퍼스맵</h3>
                      </a>
                </li> 
            </ul>         
        </div> <!-- content 끝-->		
    </div><!-- page 끝-->

    <div data-role="page"  id="campus_images">
          <div data-role="header" data-theme="b">
            <a href="#main" data-icon="home" data-rel="back" class="ui-btn-left">Home</a>     
            <h1>캠퍼스 이미지</h1>
          </div><!-- header 끝 -->
          
          <div data-role="content" >
                <div class="fotorama" data-arrows="true" data-width="100%" data-ratio="965/643" data-allowfullscreen="true" data-nav="thumbs" >
                        <a href="pic/full/cb01.jpg"><img src="pic/thumb/ca01.jpg" data-caption="호수공원" /></a>
                        <a href="pic/full/cb02.jpg"><img src="pic/thumb/ca02.jpg" data-caption="자유관" /></a>
                        <a href="pic/full/cb03.jpg"><img src="pic/thumb/ca03.jpg" data-caption="성실관" /></a>
                        <a href="pic/full/cb04.jpg"><img src="pic/thumb/ca04.jpg" data-caption="학생회관" /></a>
                        <a href="pic/full/cb05.jpg"><img src="pic/thumb/ca05.jpg" data-caption="자유관 앞 조형물" /></a>
                        <a href="pic/full/cb06.jpg"><img src="pic/thumb/ca06.jpg" data-caption="본관" /></a>
                        <a href="pic/full/cb07.jpg"><img src="pic/thumb/ca07.jpg" data-caption="채플관" /></a>                    
                </div>    
        </div>  
        <div data-role="footer" data-theme="b">
            <h2>버추얼 대학교</h2>
        </div>
    </div><!-- page 끝-->
    
  	<!-- 대학교 소개 페이지-->
 	<div data-role="page" id="intro" data-add-back-btn="true" > 
		<div data-role="header" data-theme="b" >
        	<a href="#main" data-icon="home" data-rel="back" class="ui-btn-left">Home</a>
        	<h1>대학교 소개</h1>
        </div> 
        
        <div data-role="content" >
           	<img class="small"  id="pic_univ" src="images/univ.jpg" alt="">
            <h2>&quot;<span class="bold_style">시대정신</span>을 선도하는 열린 <span class="bold_style">학문 공동체</span> 입니다. &quot;</h2>
            <div class="clear_both"></div>            
           	<p>안녕하십니까? 버추얼 대학교에 관심을 가져주셔서 감사합니다. 버추얼 대학교는 1세기의 역사를 지니며 그 동안 많은 발전을 거듭하였습니다. </p>
			<p>버추어 대학교는 세계에서 가장 선도적인 대학교 중의 하나로 인정받고 있습니다. 설립목적은 사회와 개인에 직접적인 기여와 성공을 준비할 수 있도록 하는 것입니다. 궁극적으로는 인류문명에 유용한 인재를 양성하는 것입니다. 오늘날 버추얼 대학교는 현재 인류가 당면하고 있는 여러 문제를 해결하고 다음 세대를 선도할 수 있는 리더가 될 수 있도록 학생들을 준비시키는 것입니다. </p>
			<p>여러분의 미래를 버추얼 대학교에서 함께하시길 희망합니다. </p>
			<p>총창 Luke Andrew</p>
        </div>
        <div data-role="footer" data-theme="b">
            <h2>버추얼 대학교</h2>
        </div>
     </div>
   
</body>
</html>

 

 

 

 

도전 Mission

 

 

 

mobile-bookmark-bubble 은 모바일웹앰의 하단에 홈 화면에 추가 기능을 풍선 도움말 형식으로 홍보해 주는 자바스크립트

라이브러리입니다. 설정된 시간 동안 자동으로 나타났다가 사라지므로 사용자는 손쉽게 웹앱의 웹클립 아이콘을 등록할 

수 있습니다.

https://github.com/okamototk/jqm-mobile-bookmark-bubble

 

bookmark.html

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>홈화면 추가</title>
    <meta name="viewport"
        content="width=device-width,minimum-scale=1.0,maximum-scale=1.0" />
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <link rel="apple-touch-icon-precomposed" href="icon114.png" />
    <script type="text/javascript" src="bookmark_bubble.js"></script>
    <script type="text/javascript" src="example.js"></script>
</head>

<body>
	<h1>홈화면 추가 메시지</h1>
    <p>자동으로 홈화면 추가 안내 메시지 입니다. </p>
</body>
</html>

 

bookmark_bubble.js

// JavaScript Document

/*
  Copyright 2010 Google Inc.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

/**
 * @fileoverview Bookmark bubble library. This is meant to be included in the
 * main JavaScript binary of a mobile web application.
 *
 * Supported browsers: iPhone / iPod / iPad Safari 3.0+
 */

var google = google || {};
google.bookmarkbubble = google.bookmarkbubble || {};


/**
 * Binds a context object to the function.
 * @param {Function} fn The function to bind to.
 * @param {Object} context The "this" object to use when the function is run.
 * @return {Function} A partially-applied form of fn.
 */
google.bind = function(fn, context) {
  return function() {
    return fn.apply(context, arguments);
  };
};


/**
 * Function used to define an abstract method in a base class. If a subclass
 * fails to override the abstract method, then an error will be thrown whenever
 * that method is invoked.
 */
google.abstractMethod = function() {
  throw Error('Unimplemented abstract method.');
};



/**
 * The bubble constructor. Instantiating an object does not cause anything to
 * be rendered yet, so if necessary you can set instance properties before
 * showing the bubble.
 * @constructor
 */
google.bookmarkbubble.Bubble = function() {
  /**
   * Handler for the scroll event. Keep a reference to it here, so it can be
   * unregistered when the bubble is destroyed.
   * @type {function()}
   * @private
   */
  this.boundScrollHandler_ = google.bind(this.setPosition, this);

  /**
   * The bubble element.
   * @type {Element}
   * @private
   */
  this.element_ = null;

  /**
   * Whether the bubble has been destroyed.
   * @type {boolean}
   * @private
   */
  this.hasBeenDestroyed_ = false;
};


/**
 * Shows the bubble if allowed. It is not allowed if:
 * - The browser is not Mobile Safari, or
 * - The user has dismissed it too often already, or
 * - The hash parameter is present in the location hash, or
 * - The application is in fullscreen mode, which means it was already loaded
 *   from a homescreen bookmark.
 * @return {boolean} True if the bubble is being shown, false if it is not
 *     allowed to show for one of the aforementioned reasons.
 */
google.bookmarkbubble.Bubble.prototype.showIfAllowed = function() {
  if (!this.isAllowedToShow_()) {
    return false;
  }

  this.show_();
  return true;
};


/**
 * Shows the bubble if allowed after loading the icon image. This method creates
 * an image element to load the image into the browser's cache before showing
 * the bubble to ensure that the image isn't blank. Use this instead of
 * showIfAllowed if the image url is http and cacheable.
 * This hack is necessary because Mobile Safari does not properly render
 * image elements with border-radius CSS.
 * @param {function()} opt_callback Closure to be called if and when the bubble
 *        actually shows.
 * @return {boolean} True if the bubble is allowed to show.
 */
google.bookmarkbubble.Bubble.prototype.showIfAllowedWhenLoaded =
    function(opt_callback) {
  if (!this.isAllowedToShow_()) {
    return false;
  }

  var self = this;
  // Attach to self to avoid garbage collection.
  var img = self.loadImg_ = document.createElement('img');
  img.src = self.getIconUrl_();
  img.onload = function() {
    if (img.complete) {
      delete self.loadImg_;
      img.onload = null;  // Break the circular reference.

      self.show_();
      opt_callback && opt_callback();
    }
  };
  img.onload();

  return true;
};


/**
 * Sets the parameter in the location hash. As it is
 * unpredictable what hash scheme is to be used, this method must be
 * implemented by the host application.
 *
 * This gets called automatically when the bubble is shown. The idea is that if
 * the user then creates a bookmark, we can later recognize on application
 * startup whether it was from a bookmark suggested with this bubble.
 *
 * NOTE: Using a hash parameter to track whether the bubble has been shown
 * conflicts with the navigation system in jQuery Mobile. If you are using that
 * library, you should implement this function to track the bubble's status in
 * a different way, e.g. using window.localStorage in HTML5.
 */
google.bookmarkbubble.Bubble.prototype.setHashParameter = google.abstractMethod;


/**
 * Whether the parameter is present in the location hash. As it is
 * unpredictable what hash scheme is to be used, this method must be
 * implemented by the host application.
 *
 * Call this method during application startup if you want to log whether the
 * application was loaded from a bookmark with the bookmark bubble promotion
 * parameter in it.
 *
 * @return {boolean} Whether the bookmark bubble parameter is present in the
 *     location hash.
 */
google.bookmarkbubble.Bubble.prototype.hasHashParameter = google.abstractMethod;


/**
 * The number of times the user must dismiss the bubble before we stop showing
 * it. This is a public property and can be changed by the host application if
 * necessary.
 * @type {number}
 */
google.bookmarkbubble.Bubble.prototype.NUMBER_OF_TIMES_TO_DISMISS = 2;


/**
 * Time in milliseconds. If the user does not dismiss the bubble, it will auto
 * destruct after this amount of time.
 * @type {number}
 */
google.bookmarkbubble.Bubble.prototype.TIME_UNTIL_AUTO_DESTRUCT = 15000;


/**
 * The prefix for keys in local storage. This is a public property and can be
 * changed by the host application if necessary.
 * @type {string}
 */
google.bookmarkbubble.Bubble.prototype.LOCAL_STORAGE_PREFIX = 'BOOKMARK_';


/**
 * The key name for the dismissed state.
 * @type {string}
 * @private
 */
google.bookmarkbubble.Bubble.prototype.DISMISSED_ = 'DISMISSED_COUNT';


/**
 * The arrow image in base64 data url format.
 * @type {string}
 * @private
 */
google.bookmarkbubble.Bubble.prototype.IMAGE_ARROW_DATA_URL_ = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAATCAMAAABSrFY3AAABKVBMVEUAAAD///8AAAAAAAAAAAAAAAAAAADf398AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD09PQAAAAAAAAAAAC9vb0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD19fUAAAAAAAAAAAAAAADq6uoAAAAAAAAAAAC8vLzU1NTT09MAAADg4OAAAADs7OwAAAAAAAAAAAD///+cueenwerA0vC1y+3a5fb5+/3t8vr4+v3w9PuwyOy3zO3h6vfh6vjq8Pqkv+mat+fE1fHB0/Cduuifu+iuxuuivemrxOvC1PDz9vzJ2fKpwuqmwOrb5vapw+q/0vDf6ffK2vLN3PPprJISAAAAQHRSTlMAAAEGExES7FM+JhUoQSxIRwMbNfkJUgXXBE4kDQIMHSA0Tw4xIToeTSc4Chz4OyIjPfI3QD/X5OZR6zzwLSUPrm1y3gAAAQZJREFUeF5lzsVyw0AURNE3IMsgmZmZgszQZoeZOf//EYlG5Yrhbs+im4Dj7slM5wBJ4OJ+undAUr68gK/Hyb6Bcp5yBR/w8jreNeAr5Eg2XE7g6e2/0z6cGw1JQhpmHP3u5aiPPnTTkIK48Hj9Op7bD3btAXTfgUdwYjwSDCVXMbizO0O4uDY/x4kYC5SWFnfC6N1a9RCO7i2XEmQJj2mHK1Hgp9Vq3QBRl9shuBLGhcNtHexcdQCnDUoUGetxDD+H2DQNG2xh6uAWgG2/17o1EmLqYH0Xej0UjHAaFxZIV6rJ/WK1kg7QZH8HU02zmdJinKZJaDV3TVMjM5Q9yiqYpUwiMwa/1apDXTNESjsAAAAASUVORK5CYII=';


/**
 * The close image in base64 data url format.
 * @type {string}
 * @private
 */
google.bookmarkbubble.Bubble.prototype.IMAGE_CLOSE_DATA_URL_ = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAALVBMVEXM3fm+1Pfb5/rF2fjw9f23z/aavPOhwfTp8PyTt/L3+v7T4vqMs/K7zP////+qRWzhAAAAXElEQVQIW2O4CwUM996BwVskxtOqd++2rwMyPI+ve31GD8h4Madqz2mwms5jZ/aBGS/mHIDoen3m+DowY8/hOVUgxusz+zqPg7SvPA1UxQfSvu/du0YUK2AMmDMA5H1qhVX33T8AAAAASUVORK5CYII=';


/**
 * The link used to locate the application's home screen icon to display inside
 * the bubble. The default link used here is for an iPhone home screen icon
 * without gloss. If your application uses a glossy icon, change this to
 * 'apple-touch-icon'.
 * @type {string}
 * @private
 */
google.bookmarkbubble.Bubble.prototype.REL_ICON_ =
    'apple-touch-icon-precomposed';


/**
 * Regular expression for detecting an iPhone or iPod or iPad.
 * @type {!RegExp}
 * @private
 */
google.bookmarkbubble.Bubble.prototype.MOBILE_SAFARI_USERAGENT_REGEX_ =
    /iPhone|iPod|iPad|Android/;


/**
 * Regular expression for detecting an iPad.
 * @type {!RegExp}
 * @private
 */
google.bookmarkbubble.Bubble.prototype.IPAD_USERAGENT_REGEX_ = /iPad/;

google.bookmarkbubble.Bubble.prototype.ANDROID_USERAGENT_REGEX_ = /Android/;


/**
 * Regular expression for extracting the iOS version. Only matches 2.0 and up.
 * @type {!RegExp}
 * @private
 */
google.bookmarkbubble.Bubble.prototype.IOS_VERSION_USERAGENT_REGEX_ =
    /OS (\d)_(\d)(?:_(\d))?/;


/**
 * Determines whether the bubble should be shown or not.
 * @return {boolean} Whether the bubble should be shown or not.
 * @private
 */
google.bookmarkbubble.Bubble.prototype.isAllowedToShow_ = function() {
  return this.isMobileSafari_() &&
      !this.hasBeenDismissedTooManyTimes_() &&
      !this.isFullscreen_() &&
      !this.hasHashParameter();
};


/**
 * Builds and shows the bubble.
 * @private
 */
google.bookmarkbubble.Bubble.prototype.show_ = function() {
  this.element_ = this.build_();

  document.body.appendChild(this.element_);
  this.element_.style.WebkitTransform =
      'translate3d(0,' + this.getHiddenYPosition_() + 'px,0)';

  this.setHashParameter();

  window.setTimeout(this.boundScrollHandler_, 1);
  window.addEventListener('scroll', this.boundScrollHandler_, false);

  // If the user does not dismiss the bubble, slide out and destroy it after
  // some time.
  window.setTimeout(google.bind(this.autoDestruct_, this),
      this.TIME_UNTIL_AUTO_DESTRUCT);
};


/**
 * Destroys the bubble by removing its DOM nodes from the document.
 */
google.bookmarkbubble.Bubble.prototype.destroy = function() {
  if (this.hasBeenDestroyed_) {
    return;
  }
  window.removeEventListener('scroll', this.boundScrollHandler_, false);
  if (this.element_ && this.element_.parentNode == document.body) {
    document.body.removeChild(this.element_);
    this.element_ = null;
  }
  this.hasBeenDestroyed_ = true;
};


/**
 * Remember that the user has dismissed the bubble once more.
 * @private
 */
google.bookmarkbubble.Bubble.prototype.rememberDismissal_ = function() {
  if (window.localStorage) {
    try {
      var key = this.LOCAL_STORAGE_PREFIX + this.DISMISSED_;
      var value = Number(window.localStorage[key]) || 0;
      window.localStorage[key] = String(value + 1);
    } catch (ex) {
      // Looks like we've hit the storage size limit. Currently we have no
      // fallback for this scenario, but we could use cookie storage instead.
      // This would increase the code bloat though.
    }
  }
};


/**
 * Whether the user has dismissed the bubble often enough that we will not
 * show it again.
 * @return {boolean} Whether the user has dismissed the bubble often enough
 *     that we will not show it again.
 * @private
 */
google.bookmarkbubble.Bubble.prototype.hasBeenDismissedTooManyTimes_ =
    function() {
  if (!window.localStorage) {
    // If we can not use localStorage to remember how many times the user has
    // dismissed the bubble, assume he has dismissed it. Otherwise we might end
    // up showing it every time the host application loads, into eternity.
    return true;
  }
  try {
    var key = this.LOCAL_STORAGE_PREFIX + this.DISMISSED_;

    // If the key has never been set, localStorage yields undefined, which
    // Number() turns into NaN. In that case we'll fall back to zero for
    // clarity's sake.
    var value = Number(window.localStorage[key]) || 0;

    return value >= this.NUMBER_OF_TIMES_TO_DISMISS;
  } catch (ex) {
    // If we got here, something is wrong with the localStorage. Make the same
    // assumption as when it does not exist at all. Exceptions should only
    // occur when setting a value (due to storage limitations) but let's be
    // extra careful.
    return true;
  }
};


/**
 * Whether the application is running in fullscreen mode.
 * @return {boolean} Whether the application is running in fullscreen mode.
 * @private
 */
google.bookmarkbubble.Bubble.prototype.isFullscreen_ = function() {
  return !!window.navigator.standalone;
};


/**
 * Whether the application is running inside Mobile Safari.
 * @return {boolean} True if the current user agent looks like Mobile Safari.
 * @private
 */
google.bookmarkbubble.Bubble.prototype.isMobileSafari_ = function() {
  return this.MOBILE_SAFARI_USERAGENT_REGEX_.test(window.navigator.userAgent);
};


/**
 * Whether the application is running on an iPad.
 * @return {boolean} True if the current user agent looks like an iPad.
 * @private
 */
google.bookmarkbubble.Bubble.prototype.isIpad_ = function() {
  return this.IPAD_USERAGENT_REGEX_.test(window.navigator.userAgent);
};

google.bookmarkbubble.Bubble.prototype.isAndroid_ = function() {
  return this.ANDROID_USERAGENT_REGEX_.test(window.navigator.userAgent);
};


/**
 * Creates a version number from 4 integer pieces between 0 and 127 (inclusive).
 * @param {*=} opt_a The major version.
 * @param {*=} opt_b The minor version.
 * @param {*=} opt_c The revision number.
 * @param {*=} opt_d The build number.
 * @return {number} A representation of the version.
 * @private
 */
google.bookmarkbubble.Bubble.prototype.getVersion_ = function(opt_a, opt_b,
    opt_c, opt_d) {
  // We want to allow implicit conversion of any type to number while avoiding
  // compiler warnings about the type.
  return /** @type {number} */ (opt_a) << 21 |
      /** @type {number} */ (opt_b) << 14 |
      /** @type {number} */ (opt_c) << 7 |
      /** @type {number} */ (opt_d);
};


/**
 * Gets the iOS version of the device. Only works for 2.0+.
 * @return {number} The iOS version.
 * @private
 */
google.bookmarkbubble.Bubble.prototype.getIosVersion_ = function() {
  var groups = this.IOS_VERSION_USERAGENT_REGEX_.exec(
      window.navigator.userAgent) || [];
  groups.shift();
  return this.getVersion_.apply(this, groups);
};


/**
 * Positions the bubble at the bottom of the viewport using an animated
 * transition.
 */
google.bookmarkbubble.Bubble.prototype.setPosition = function() {
  this.element_.style.WebkitTransition = '-webkit-transform 0.7s ease-out';
  this.element_.style.WebkitTransform =
      'translate3d(0,' + this.getVisibleYPosition_() + 'px,0)';
};


/**
 * Destroys the bubble by removing its DOM nodes from the document, and
 * remembers that it was dismissed.
 * @private
 */
google.bookmarkbubble.Bubble.prototype.closeClickHandler_ = function() {
  this.destroy();
  this.rememberDismissal_();
};


/**
 * Gets called after a while if the user ignores the bubble.
 * @private
 */
google.bookmarkbubble.Bubble.prototype.autoDestruct_ = function() {
  if (this.hasBeenDestroyed_) {
    return;
  }
  this.element_.style.WebkitTransition = '-webkit-transform 0.7s ease-in';
  this.element_.style.WebkitTransform =
      'translate3d(0,' + this.getHiddenYPosition_() + 'px,0)';
  window.setTimeout(google.bind(this.destroy, this), 700);
};


/**
 * Gets the y offset used to show the bubble (i.e., position it on-screen).
 * @return {number} The y offset.
 * @private
 */
google.bookmarkbubble.Bubble.prototype.getVisibleYPosition_ = function() {
  return this.isIpad_() ? window.pageYOffset + 17 :
      window.pageYOffset - this.element_.offsetHeight + window.innerHeight - 17;
};


/**
 * Gets the y offset used to hide the bubble (i.e., position it off-screen).
 * @return {number} The y offset.
 * @private
 */
google.bookmarkbubble.Bubble.prototype.getHiddenYPosition_ = function() {
  return this.isIpad_() ? window.pageYOffset - this.element_.offsetHeight :
      window.pageYOffset + window.innerHeight;
};


/**
 * The url of the app's bookmark icon.
 * @type {string|undefined}
 * @private
 */
google.bookmarkbubble.Bubble.prototype.iconUrl_;


/**
 * Scrapes the document for a link element that specifies an Apple favicon and
 * returns the icon url. Returns an empty data url if nothing can be found.
 * @return {string} A url string.
 * @private
 */
google.bookmarkbubble.Bubble.prototype.getIconUrl_ = function() {
  if (!this.iconUrl_) {
    var link = this.getLink(this.REL_ICON_);
    if (!link || !(this.iconUrl_ = link.href)) {
      this.iconUrl_ = 'data:image/png;base64,';
    }
  }
  return this.iconUrl_;
};


/**
 * Gets the requested link tag if it exists.
 * @param {string} rel The rel attribute of the link tag to get.
 * @return {Element} The requested link tag or null.
 */
google.bookmarkbubble.Bubble.prototype.getLink = function(rel) {
  rel = rel.toLowerCase();
  var links = document.getElementsByTagName('link');
  for (var i = 0; i < links.length; ++i) {
    var currLink = /** @type {Element} */ (links[i]);
    if (currLink.getAttribute('rel').toLowerCase() == rel) {
      return currLink;
    }
  }
  return null;
};


/**
 * Creates the bubble and appends it to the document.
 * @return {Element} The bubble element.
 * @private
 */
google.bookmarkbubble.Bubble.prototype.build_ = function() {
  var bubble = document.createElement('div');
  var isIpad = this.isIpad_();
  var isAndroid = this.isAndroid_();

  bubble.style.position = 'absolute';
  bubble.style.zIndex = 1000;
  bubble.style.width = '100%';
  bubble.style.left = '0';
  bubble.style.top = '0';

  var bubbleInner = document.createElement('div');
  bubbleInner.style.position = 'relative';
  bubbleInner.style.width = '214px';
  bubbleInner.style.margin = isIpad ? '0 0 0 82px' : '0 auto';
  bubbleInner.style.border = '2px solid #fff';
  bubbleInner.style.padding = '20px 20px 20px 10px';
  bubbleInner.style.WebkitBorderRadius = '8px';
  bubbleInner.style.WebkitBoxShadow = '0 0 8px rgba(0, 0, 0, 0.7)';
  bubbleInner.style.WebkitBackgroundSize = '100% 8px';
  bubbleInner.style.backgroundColor = '#b0c8ec';
  bubbleInner.style.background = '#cddcf3 -webkit-gradient(linear, ' +
      'left bottom, left top, ' + isIpad ?
          'from(#cddcf3), to(#b3caed)) no-repeat top' :
          'from(#b3caed), to(#cddcf3)) no-repeat bottom';
  bubbleInner.style.font = '13px/17px sans-serif';
  bubble.appendChild(bubbleInner);

  // The "Add to Home Screen" text is intended to be the exact same text
  // that is displayed in the menu of Android / Mobile Safari.
  if (isAndroid) { bubbleInner.innerHTML = '<b>스마트폰에 웹앱 설치하기:</b> ' +
        '먼저 책갈피에 추가한 후에 등록된 책갈피를 길게 누른 후 [대기화면에 단축메뉴 추가]를 선택하세요.'; }
  else {
  if (this.getIosVersion_() >= this.getVersion_(4, 2)) {
    bubbleInner.innerHTML = '스마트폰에 웹앱 설치하기: ' +
        '화살표를 터치한 후에 <b>\'홈 화면에 추가\'</b>';
  } else {
    bubbleInner.innerHTML = '스마트폰에 웹앱 설치하기: ' +
        '터치 <b style="font-size:15px">+</b> 한 후에 ' +
        '<b>\'홈 화면에 추가\'</b>';
  }
  }

  var icon = document.createElement('div');
  icon.style['float'] = 'left';
  icon.style.width = '55px';
  icon.style.height = '55px';
  icon.style.margin = '-2px 7px 3px 5px';
  icon.style.background =
      '#fff url(' + this.getIconUrl_() + ') no-repeat -1px -1px';
  icon.style.WebkitBackgroundSize = '57px';
  icon.style.WebkitBorderRadius = '10px';
  icon.style.WebkitBoxShadow = '0 2px 5px rgba(0, 0, 0, 0.4)';
  bubbleInner.insertBefore(icon, bubbleInner.firstChild);

  var arrow = document.createElement('div');
  arrow.style.backgroundImage = 'url(' + this.IMAGE_ARROW_DATA_URL_ + ')';
  arrow.style.width = '25px';
  arrow.style.height = '19px';
  arrow.style.position = 'absolute';
  arrow.style.left = '111px';
  if (isIpad) {
    arrow.style.WebkitTransform = 'rotate(180deg)';
    arrow.style.top = '-19px';
  } else {
    arrow.style.bottom = '-19px';
  }
  bubbleInner.appendChild(arrow);

  var close = document.createElement('a');
  close.onclick = google.bind(this.closeClickHandler_, this);
  close.style.position = 'absolute';
  close.style.display = 'block';
  close.style.top = '-3px';
  close.style.right = '-3px';
  close.style.width = '16px';
  close.style.height = '16px';
  close.style.border = '10px solid transparent';
  close.style.background =
      'url(' + this.IMAGE_CLOSE_DATA_URL_ + ') no-repeat';
  bubbleInner.appendChild(close);

  return bubble;
};

 

example.js

/*
  Copyright 2010 Google Inc.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

/** @fileoverview Example of how to use the bookmark bubble. */

window.addEventListener('load', function() {
  window.setTimeout(function() {
    var bubble = new google.bookmarkbubble.Bubble();

    var parameter = 'bmb=1';

    bubble.hasHashParameter = function() {
      return window.location.hash.indexOf(parameter) != -1;
    };

    bubble.setHashParameter = function() {
      if (!this.hasHashParameter()) {
        window.location.hash += parameter;
      }
    };

    bubble.getViewportHeight = function() {
      window.console.log('Example of how to override getViewportHeight.');
      return window.innerHeight;
    };

    bubble.getViewportScrollY = function() {
      window.console.log('Example of how to override getViewportScrollY.');
      return window.pageYOffset;
    };

    bubble.registerScrollHandler = function(handler) {
      window.console.log('Example of how to override registerScrollHandler.');
      window.addEventListener('scroll', handler, false);
    };

    bubble.deregisterScrollHandler = function(handler) {
      window.console.log('Example of how to override deregisterScrollHandler.');
      window.removeEventListener('scroll', handler, false);
    };

    bubble.showIfAllowed();
  }, 1000);
}, false);

 

 

 

 

about author

PHRASE

Level 60  머나먼나라

We die but once. (한 번밖에 안 죽는다

댓글 ( 0)

댓글 남기기

작성