PageSourceSearch

https://gamedrive.jp/bookmark_bubble/bookmark_bubble.js?1790276385

js gamedrive.jp collected 2026-09-24 18:59:52 UTC 20,466 bytes, 608 lines download raw bytes

1/*
2  Copyright 2010 Google Inc.
3
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7
8       http://www.apache.org/licenses/LICENSE-2.0
9
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15*/
16
17/**
18 * @fileoverview Bookmark bubble library. This is meant to be included in the
19 * main JavaScript binary of a mobile web application.
20 *
21 * Supported browsers: iPhone / iPod / iPad Safari 3.0+
22 */
23
24var google = google || {};
25google.bookmarkbubble = google.bookmarkbubble || {};
26
27
28/**
29 * Binds a context object to the function.
30 * @param {Function} fn The function to bind to.
31 * @param {Object} context The "this" object to use when the function is run.
32 * @return {Function} A partially-applied form of fn.
33 */
34google.bind = function(fn, context) {
35  return function() {
36    return fn.apply(context, arguments);
37  };
38};
39
40
41/**
42 * Function used to define an abstract method in a base class. If a subclass
43 * fails to override the abstract method, then an error will be thrown whenever
44 * that method is invoked.
45 */
46google.abstractMethod = function() {
47  throw Error('Unimplemented abstract method.');
48};
49
50
51
52/**
53 * The bubble constructor. Instantiating an object does not cause anything to
54 * be rendered yet, so if necessary you can set instance properties before
55 * showing the bubble.
56 * @constructor
57 */
58google.bookmarkbubble.Bubble = function() {
59  /**
60   * Handler for the scroll event. Keep a reference to it here, so it can be
61   * unregistered when the bubble is destroyed.
62   * @type {function()}
63   * @private
64   */
65  this.boundScrollHandler_ = google.bind(this.setPosition, this);
66
67  /**
68   * The bubble element.
69   * @type {Element}
70   * @private
71   */
72  this.element_ = null;
73
74  /**
75   * Whether the bubble has been destroyed.
76   * @type {boolean}
77   * @private
78   */
79  this.hasBeenDestroyed_ = false;
80};
81
82
83/**
84 * Shows the bubble if allowed. It is not allowed if:
85 * - The browser is not Mobile Safari, or
86 * - The user has dismissed it too often already, or
87 * - The hash parameter is present in the location hash, or
88 * - The application is in fullscreen mode, which means it was already loaded
89 *   from a homescreen bookmark.
90 * @return {boolean} True if the bubble is being shown, false if it is not
91 *     allowed to show for one of the aforementioned reasons.
92 */
93google.bookmarkbubble.Bubble.prototype.showIfAllowed = function() {
94  if (!this.isAllowedToShow_()) {
95    return false;
96  }
97
98  this.show_();
99  return true;
100};
101
102
103/**
104 * Shows the bubble if allowed after loading the icon image. This method creates
105 * an image element to load the image into the browser's cache before showing
106 * the bubble to ensure that the image isn't blank. Use this instead of
107 * showIfAllowed if the image url is http and cacheable.
108 * This hack is necessary because Mobile Safari does not properly render
109 * image elements with border-radius CSS.
110 * @param {function()} opt_callback Closure to be called if and when the bubble
111 *        actually shows.
112 * @return {boolean} True if the bubble is allowed to show.
113 */
114google.bookmarkbubble.Bubble.prototype.showIfAllowedWhenLoaded =
115    function(opt_callback) {
116  if (!this.isAllowedToShow_()) {
117    return false;
118  }
119
120  var self = this;
121  // Attach to self to avoid garbage collection.
122  var img = self.loadImg_ = document.createElement('img');
123  img.src = self.getIconUrl_();
124  img.onload = function() {
125    if (img.complete) {
126      delete self.loadImg_;
127      img.onload = null;  // Break the circular reference.
128
129      self.show_();
130      opt_callback && opt_callback();
131    }
132  };
133  img.onload();
134
135  return true;
136};
137
138
139/**
140 * Sets the parameter in the location hash. As it is
141 * unpredictable what hash scheme is to be used, this method must be
142 * implemented by the host application.
143 *
144 * This gets called automatically when the bubble is shown. The idea is that if
145 * the user then creates a bookmark, we can later recognize on application
146 * startup whether it was from a bookmark suggested with this bubble.
147 *
148 * NOTE: Using a hash parameter to track whether the bubble has been shown
149 * conflicts with the navigation system in jQuery Mobile. If you are using that
150 * library, you should implement this function to track the bubble's status in
151 * a different way, e.g. using window.localStorage in HTML5.
152 */
153google.bookmarkbubble.Bubble.prototype.setHashParameter = google.abstractMethod;
154
155
156/**
157 * Whether the parameter is present in the location hash. As it is
158 * unpredictable what hash scheme is to be used, this method must be
159 * implemented by the host application.
160 *
161 * Call this method during application startup if you want to log whether the
162 * application was loaded from a bookmark with the bookmark bubble promotion
163 * parameter in it.
164 *
165 * @return {boolean} Whether the bookmark bubble parameter is present in the
166 *     location hash.
167 */
168google.bookmarkbubble.Bubble.prototype.hasHashParameter = google.abstractMethod;
169
170
171/**
172 * The number of times the user must dismiss the bubble before we stop showing
173 * it. This is a public property and can be changed by the host application if
174 * necessary.
175 * @type {number}
176 */
177google.bookmarkbubble.Bubble.prototype.NUMBER_OF_TIMES_TO_DISMISS = 2;
178
179
180/**
181 * Time in milliseconds. If the user does not dismiss the bubble, it will auto
182 * destruct after this amount of time.
183 * @type {number}
184 */
185google.bookmarkbubble.Bubble.prototype.TIME_UNTIL_AUTO_DESTRUCT = 10000;
186
187
188/**
189 * The prefix for keys in local storage. This is a public property and can be
190 * changed by the host application if necessary.
191 * @type {string}
192 */
193google.bookmarkbubble.Bubble.prototype.LOCAL_STORAGE_PREFIX = 'BOOKMARK_';
194
195
196/**
197 * The key name for the dismissed state.
198 * @type {string}
199 * @private
200 */
201google.bookmarkbubble.Bubble.prototype.DISMISSED_ = 'DISMISSED_COUNT';
202
203
204/**
205 * The arrow image in base64 data url format.
206 * @type {string}
207 * @private
208 */
209google.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=';
210
211
212/**
213 * The close image in base64 data url format.
214 * @type {string}
215 * @private
216 */
217google.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=';
218
219
220/**
221 * The link used to locate the application's home screen icon to display inside
222 * the bubble. The default link used here is for an iPhone home screen icon
223 * without gloss. If your application uses a glossy icon, change this to
224 * 'apple-touch-icon'.
225 * @type {string}
226 * @private
227 */
228google.bookmarkbubble.Bubble.prototype.REL_ICON_ =
229    'apple-touch-icon-precomposed';
230
231
232/**
233 * Regular expression for detecting an iPhone or iPod or iPad.
234 * @type {!RegExp}
235 * @private
236 */
237google.bookmarkbubble.Bubble.prototype.MOBILE_SAFARI_USERAGENT_REGEX_ =
238    /iPhone|iPod|iPad/;
239
240
241/**
242 * Regular expression for detecting an iPad.
243 * @type {!RegExp}
244 * @private
245 */
246google.bookmarkbubble.Bubble.prototype.IPAD_USERAGENT_REGEX_ = /iPad/;
247
248
249/**
250 * Regular expression for extracting the iOS version. Only matches 2.0 and up.
251 * @type {!RegExp}
252 * @private
253 */
254google.bookmarkbubble.Bubble.prototype.IOS_VERSION_USERAGENT_REGEX_ =
255    /OS (\d)_(\d)(?:_(\d))?/;
256
257
258/**
259 * Determines whether the bubble should be shown or not.
260 * @return {boolean} Whether the bubble should be shown or not.
261 * @private
262 */
263google.bookmarkbubble.Bubble.prototype.isAllowedToShow_ = function() {
264  return this.isMobileSafari_() &&
265      !this.hasBeenDismissedTooManyTimes_() &&
266      !this.isFullscreen_() &&
267      !this.hasHashParameter();
268};
269
270
271/**
272 * Builds and shows the bubble.
273 * @private
274 */
275google.bookmarkbubble.Bubble.prototype.show_ = function() {
276  this.element_ = this.build_();
277
278  document.body.appendChild(this.element_);
279  this.element_.style.WebkitTransform =
280      'translate3d(0,' + this.getHiddenYPosition_() + 'px,0)';
281
282  this.setHashParameter();
283
284  window.setTimeout(this.boundScrollHandler_, 1);
285  window.addEventListener('scroll', this.boundScrollHandler_, false);
286
287  // If the user does not dismiss the bubble, slide out and destroy it after
288  // some time.
289  window.setTimeout(google.bind(this.autoDestruct_, this),
290      this.TIME_UNTIL_AUTO_DESTRUCT);
291};
292
293
294/**
295 * Destroys the bubble by removing its DOM nodes from the document.
296 */
297google.bookmarkbubble.Bubble.prototype.destroy = function() {
298  if (this.hasBeenDestroyed_) {
299    return;
300  }
301  window.removeEventListener('scroll', this.boundScrollHandler_, false);
302  if (this.element_ && this.element_.parentNode == document.body) {
303    document.body.removeChild(this.element_);
304    this.element_ = null;
305  }
306  this.hasBeenDestroyed_ = true;
307};
308
309
310/**
311 * Remember that the user has dismissed the bubble once more.
312 * @private
313 */
314google.bookmarkbubble.Bubble.prototype.rememberDismissal_ = function() {
315  if (window.localStorage) {
316    try {
317      var key = this.LOCAL_STORAGE_PREFIX + this.DISMISSED_;
318      var value = Number(window.localStorage[key]) || 0;
319      window.localStorage[key] = String(value + 1);
320    } catch (ex) {
321      // Looks like we've hit the storage size limit. Currently we have no
322      // fallback for this scenario, but we could use cookie storage instead.
323      // This would increase the code bloat though.
324    }
325  }
326};
327
328
329/**
330 * Whether the user has dismissed the bubble often enough that we will not
331 * show it again.
332 * @return {boolean} Whether the user has dismissed the bubble often enough
333 *     that we will not show it again.
334 * @private
335 */
336google.bookmarkbubble.Bubble.prototype.hasBeenDismissedTooManyTimes_ =
337    function() {
338  if (!window.localStorage) {
339    // If we can not use localStorage to remember how many times the user has
340    // dismissed the bubble, assume he has dismissed it. Otherwise we might end
341    // up showing it every time the host application loads, into eternity.
342    return true;
343  }
344  try {
345    var key = this.LOCAL_STORAGE_PREFIX + this.DISMISSED_;
346
347    // If the key has never been set, localStorage yields undefined, which
348    // Number() turns into NaN. In that case we'll fall back to zero for
349    // clarity's sake.
350    var value = Number(window.localStorage[key]) || 0;
351
352    return value >= this.NUMBER_OF_TIMES_TO_DISMISS;
353  } catch (ex) {
354    // If we got here, something is wrong with the localStorage. Make the same
355    // assumption as when it does not exist at all. Exceptions should only
356    // occur when setting a value (due to storage limitations) but let's be
357    // extra careful.
358    return true;
359  }
360};
361
362
363/**
364 * Whether the application is running in fullscreen mode.
365 * @return {boolean} Whether the application is running in fullscreen mode.
366 * @private
367 */
368google.bookmarkbubble.Bubble.prototype.isFullscreen_ = function() {
369  return !!window.navigator.standalone;
370};
371
372
373/**
374 * Whether the application is running inside Mobile Safari.
375 * @return {boolean} True if the current user agent looks like Mobile Safari.
376 * @private
377 */
378google.bookmarkbubble.Bubble.prototype.isMobileSafari_ = function() {
379  return this.MOBILE_SAFARI_USERAGENT_REGEX_.test(window.navigator.userAgent);
380};
381
382
383/**
384 * Whether the application is running on an iPad.
385 * @return {boolean} True if the current user agent looks like an iPad.
386 * @private
387 */
388google.bookmarkbubble.Bubble.prototype.isIpad_ = function() {
389  return this.IPAD_USERAGENT_REGEX_.test(window.navigator.userAgent);
390};
391
392
393/**
394 * Creates a version number from 4 integer pieces between 0 and 127 (inclusive).
395 * @param {*=} opt_a The major version.
396 * @param {*=} opt_b The minor version.
397 * @param {*=} opt_c The revision number.
398 * @param {*=} opt_d The build number.
399 * @return {number} A representation of the version.
400 * @private
401 */
402google.bookmarkbubble.Bubble.prototype.getVersion_ = function(opt_a, opt_b,
403    opt_c, opt_d) {
404  // We want to allow implicit conversion of any type to number while avoiding
405  // compiler warnings about the type.
406  return /** @type {number} */ (opt_a) << 21 |
407      /** @type {number} */ (opt_b) << 14 |
408      /** @type {number} */ (opt_c) << 7 |
409      /** @type {number} */ (opt_d);
410};
411
412
413/**
414 * Gets the iOS version of the device. Only works for 2.0+.
415 * @return {number} The iOS version.
416 * @private
417 */
418google.bookmarkbubble.Bubble.prototype.getIosVersion_ = function() {
419  var groups = this.IOS_VERSION_USERAGENT_REGEX_.exec(
420      window.navigator.userAgent) || [];
421  groups.shift();
422  return this.getVersion_.apply(this, groups);
423};
424
425
426/**
427 * Positions the bubble at the bottom of the viewport using an animated
428 * transition.
429 */
430google.bookmarkbubble.Bubble.prototype.setPosition = function() {
431  this.element_.style.WebkitTransition = '-webkit-transform 0.7s ease-out';
432  this.element_.style.WebkitTransform =
433      'translate3d(0,' + this.getVisibleYPosition_() + 'px,0)';
434};
435
436
437/**
438 * Destroys the bubble by removing its DOM nodes from the document, and
439 * remembers that it was dismissed.
440 * @private
441 */
442google.bookmarkbubble.Bubble.prototype.closeClickHandler_ = function() {
443  this.destroy();
444  this.rememberDismissal_();
445};
446
447
448/**
449 * Gets called after a while if the user ignores the bubble.
450 * @private
451 */
452google.bookmarkbubble.Bubble.prototype.autoDestruct_ = function() {
453  if (this.hasBeenDestroyed_) {
454    return;
455  }
456  this.element_.style.WebkitTransition = '-webkit-transform 0.7s ease-in';
457  this.element_.style.WebkitTransform =
458      'translate3d(0,' + this.getHiddenYPosition_() + 'px,0)';
459  window.setTimeout(google.bind(this.destroy, this), 700);
460};
461
462
463/**
464 * Gets the y offset used to show the bubble (i.e., position it on-screen).
465 * @return {number} The y offset.
466 * @private
467 */
468google.bookmarkbubble.Bubble.prototype.getVisibleYPosition_ = function() {
469  return this.isIpad_() ? window.pageYOffset + 17 :
470      window.pageYOffset - this.element_.offsetHeight + window.innerHeight - 17;
471};
472
473
474/**
475 * Gets the y offset used to hide the bubble (i.e., position it off-screen).
476 * @return {number} The y offset.
477 * @private
478 */
479google.bookmarkbubble.Bubble.prototype.getHiddenYPosition_ = function() {
480  return this.isIpad_() ? window.pageYOffset - this.element_.offsetHeight :
481      window.pageYOffset + window.innerHeight;
482};
483
484
485/**
486 * The url of the app's bookmark icon.
487 * @type {string|undefined}
488 * @private
489 */
490google.bookmarkbubble.Bubble.prototype.iconUrl_;
491
492
493/**
494 * Scrapes the document for a link element that specifies an Apple favicon and
495 * returns the icon url. Returns an empty data url if nothing can be found.
496 * @return {string} A url string.
497 * @private
498 */
499google.bookmarkbubble.Bubble.prototype.getIconUrl_ = function() {
500  if (!this.iconUrl_) {
501    var link = this.getLink(this.REL_ICON_);
502    if (!link || !(this.iconUrl_ = link.href)) {
503      this.iconUrl_ = 'data:image/png;base64,';
504    }
505  }
506  return this.iconUrl_;
507};
508
509
510/**
511 * Gets the requested link tag if it exists.
512 * @param {string} rel The rel attribute of the link tag to get.
513 * @return {Element} The requested link tag or null.
514 */
515google.bookmarkbubble.Bubble.prototype.getLink = function(rel) {
516  rel = rel.toLowerCase();
517  var links = document.getElementsByTagName('link');
518  for (var i = 0; i < links.length; ++i) {
519    var currLink = /** @type {Element} */ (links[i]);
520    if (currLink.getAttribute('rel').toLowerCase() == rel) {
521      return currLink;
522    }
523  }
524  return null;
525};
526
527
528/**
529 * Creates the bubble and appends it to the document.
530 * @return {Element} The bubble element.
531 * @private
532 */
533google.bookmarkbubble.Bubble.prototype.build_ = function() {
534  var bubble = document.createElement('div');
535  var isIpad = this.isIpad_();
536
537  bubble.style.position = 'absolute';
538  bubble.style.zIndex = 1000;
539  bubble.style.width = '100%';
540  bubble.style.left = '0';
541  bubble.style.top = '0';
542
543  var bubbleInner = document.createElement('div');
544  bubbleInner.style.position = 'relative';
545  bubbleInner.style.width = '250px';
546  bubbleInner.style.margin = isIpad ? '0 0 0 82px' : '0 auto';
547  bubbleInner.style.border = '2px solid #fff';
548  bubbleInner.style.padding = '20px 20px 20px 10px';
549  bubbleInner.style.WebkitBorderRadius = '8px';
550  bubbleInner.style.WebkitBoxShadow = '0 0 8px rgba(0, 0, 0, 0.7)';
551  bubbleInner.style.WebkitBackgroundSize = '100% 8px';
552  bubbleInner.style.backgroundColor = '#b0c8ec';
553  bubbleInner.style.background = '#cddcf3 -webkit-gradient(linear, ' +
554      'left bottom, left top, ' + isIpad ?
555          'from(#cddcf3), to(#b3caed)) no-repeat top' :
556          'from(#b3caed), to(#cddcf3)) no-repeat bottom';
557  bubbleInner.style.font = '13px/17px sans-serif';
558  bubble.appendChild(bubbleInner);
559
560  // The "Add to Home Screen" text is intended to be the exact same text
561  // that is displayed in the menu of Mobile Safari.
562  if (this.getIosVersion_() >= this.getVersion_(4, 2)) {
563    bubbleInner.innerHTML = 'ゲームドライブを登録しよう!<br>矢印をタップして「ホーム画面に追加」で完了!';
564  } else {
565    bubbleInner.innerHTML = 'ゲームドライブを登録しよう!<br>矢印をタップして「ホーム画面に追加」で完了!';
566  }
567
568  var icon = document.createElement('div');
569  icon.style['float'] = 'left';
570  icon.style.width = '55px';
571  icon.style.height = '55px';
572  icon.style.margin = '-2px 7px 3px 5px';
573  icon.style.background =
574      '#fff url(' + this.getIconUrl_() + ') no-repeat -1px -1px';
575  icon.style.WebkitBackgroundSize = '57px';
576  icon.style.WebkitBorderRadius = '10px';
577  icon.style.WebkitBoxShadow = '0 2px 5px rgba(0, 0, 0, 0.4)';
578  bubbleInner.insertBefore(icon, bubbleInner.firstChild);
579
580  var arrow = document.createElement('div');
581  arrow.style.backgroundImage = 'url(' + this.IMAGE_ARROW_DATA_URL_ + ')';
582  arrow.style.width = '25px';
583  arrow.style.height = '19px';
584  arrow.style.position = 'absolute';
585  arrow.style.left = '111px';
586  if (isIpad) {
587    arrow.style.WebkitTransform = 'rotate(180deg)';
588    arrow.style.top = '-19px';
589  } else {
590    arrow.style.bottom = '-19px';
591  }
592  bubbleInner.appendChild(arrow);
593
594  var close = document.createElement('a');
595  close.onclick = google.bind(this.closeClickHandler_, this);
596  close.style.position = 'absolute';
597  close.style.display = 'block';
598  close.style.top = '-5px';
599  close.style.right = '-2px';
600  close.style.width = '32px';
601  close.style.height = '32px';
602  close.style.border = '10px solid transparent';
603  close.style.background =
604      'url(' + this.IMAGE_CLOSE_DATA_URL_ + ') no-repeat';
605  bubbleInner.appendChild(close);
606
607  return bubble;
608};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.