1- // try {
2- // console.log("game_frame and its contentDocument found.");
3- // var innerNode = document.getElementById('$styleIdI');
4- // if (innerNode) document.getElementsByTagName('head')[0].removeChild(innerNode);
5- // innerNode = document.createElement('div');
6-
7- // var shownode = document.getElementById('flashWrap');
8- // console.log("flashWrap: " + shownode);
9-
10- // innerNode.innerHTML = `
11- // <style id="$styleIdI">
12- // body { visibility: hidden; }
13- // #flashWrap { position: fixed; left: 0; top: 0; width: 100% !important; height: 100% !important; }
14- // #htmlWrap { visibility: visible; width: 100% !important; height: 100% !important; }
15- // // body > *:not(#flashWrap):not(#htmlWrap) { display: none; }
16- // </style>`;
17-
18- // document.getElementsByTagName('head')[0].appendChild(innerNode.lastChild);
19- // console.log("Inner CSS injected successfully.");
20- // } catch(e) {
21- // alert("ページCSS適用に失敗しました: " + e);
22- // }
23-
24- ( ( $ , _ ) => {
25- const html = $ . documentElement ,
26- gf = $ . getElementById ( 'game_frame' ) ;
1+ /**
2+ * Auto scale script for KanColle DMM page (iOS/Flutter webview friendly).
3+ *
4+ * Goals:
5+ * - Hide surrounding DMM chrome/ads and only show the game surface.
6+ * - Keep the 1200x720 game area centered and scaled to fit the viewport.
7+ * - Be idempotent: re‑running only re-applies sizing, does not stack listeners.
8+ */
9+ ( function autoScaleKancolle ( ) {
10+ const STYLE_ID = '__kc_auto_scale_css__' ;
11+ const FLAG_KEY = '__kc_auto_scale_bound__' ;
12+ const GAME_W = 1200 ;
13+ const GAME_H = 720 ;
14+ let lastScale = null ;
15+ let lastVW = 0 ;
16+ let lastVH = 0 ;
17+
18+ // If already bound, just trigger a forced refresh and exit.
19+ if ( window [ FLAG_KEY ] ) {
20+ if ( typeof window . __kcAutoScaleRefresh === 'function' ) {
21+ window . __kcAutoScaleRefresh ( { reset : true } ) ;
22+ }
23+ return ;
24+ }
25+ window [ FLAG_KEY ] = true ;
26+
27+ const hideSelectors = [
28+ '#header' ,
29+ '#ntg-recommend' ,
30+ '#dmm-ntgnavi' ,
31+ '.dmm-ntgnavi' ,
32+ '.area-naviapp' ,
33+ '.area-common' ,
34+ 'header' ,
35+ 'footer' ,
36+ '#foot' ,
37+ '#footer' ,
38+ '.area-footer' ,
39+ '.d-footer' ,
40+ '.social-wrap' ,
41+ '.gameStart' ,
42+ 'iframe[title*="ad"]' ,
43+ 'iframe[src*="ads"]' ,
44+ ] ;
45+
46+ function injectStyle ( doc , cssText ) {
47+ if ( ! doc || doc . getElementById ( STYLE_ID ) ) return ;
48+ const style = doc . createElement ( 'style' ) ;
49+ style . id = STYLE_ID ;
50+ style . textContent = cssText ;
51+ ( doc . head || doc . documentElement ) . appendChild ( style ) ;
52+ }
53+
54+ function buildBaseCSS ( ) {
55+ return `
56+ html, body {
57+ margin: 0 !important;
58+ padding: 0 !important;
59+ overflow: hidden !important;
60+ background: #000 !important;
61+ width: 100% !important;
62+ height: 100% !important;
63+ }
64+ ${ hideSelectors . join ( ',' ) } { display: none !important; }
65+ #main-ntg, #w, #contents, #container, #root {
66+ margin: 0 !important;
67+ padding: 0 !important;
68+ width: 100% !important;
69+ height: 100% !important;
70+ }
71+ ` ;
72+ }
73+
74+ function findGameFrame ( ) {
75+ return (
76+ document . getElementById ( 'game_frame' ) ||
77+ document . querySelector ( 'iframe[src*="kancolle"]' ) ||
78+ document . querySelector ( 'iframe[src*="kcs"]' ) ||
79+ document . querySelector ( 'iframe[src*="gadgets"]' )
80+ ) ;
81+ }
82+
83+ function resetFrameStyle ( gf ) {
84+ const style = gf . style ;
85+ style . position = 'fixed' ;
86+ style . width = `${ GAME_W } px` ;
87+ style . height = `${ GAME_H } px` ;
88+ style . transform = 'none' ;
89+ style . transformOrigin = 'top left' ;
90+ style . border = '0' ;
91+ style . margin = '0' ;
92+ style . padding = '0' ;
93+ style . left = '0' ;
94+ style . top = '0' ;
95+ style . zIndex = '9999' ;
96+ }
97+
98+ function applyScale ( { reset = false } = { } ) {
99+ const gf = findGameFrame ( ) ;
100+ injectStyle ( document , buildBaseCSS ( ) ) ;
27101 if ( ! gf ) return ;
28- const gs = gf . style ,
29- gw = gf . offsetWidth ,
30- gh = gw * .6 ;
31- let vp = $ . querySelector ( 'meta[name=viewport]' ) ,
32- t = 0 ;
33- vp || ( vp = $ . createElement ( 'meta' ) , vp . name = 'viewport' , $ . querySelector ( 'head' ) . appendChild ( vp ) ) ;
34- vp . content = 'width=' + gw ;
35- // 'orientation' in _ && html.webkitRequestFullscreen && html.webkitRequestFullscreen();
36- html . style . overflow = 'hidden' ; $ . body . style . cssText = 'min-width:0;padding:0;margin:0;overflow:hidden;margin:0' ; $ . querySelector ( '.dmm-ntgnavi' ) . style . display = 'none' ; $ . querySelector ( '.area-naviapp' ) . style . display = 'none' ;
37- gs . position = 'fixed' ;
38- gs . marginRight = 'auto' ;
39- gs . marginLeft = 'auto' ;
40- gs . top = '-16px' ;
41- gs . right = '0' ;
42- gs . zIndex = '100' ;
43- gs . transformOrigin = 'center top' ;
44- if ( ! _ . kancolleFit ) {
45- const k = ( ) => {
46- const w = html . clientWidth ,
47- h = _ . innerHeight ;
48- w / h < 1 / .6 ? gs . transform = 'scale(' + w / gw + ')' : gs . transform = 'scale(' + h / gh + ')' ;
49- w < gw ? gs . left = '-' + ( gw - w ) / 2 + 'px' : gs . left = '0'
50- } ;
51- _ . addEventListener ( 'resize' , ( ) => {
52- clearTimeout ( t ) ;
53- t = setTimeout ( k , 10 )
54- } ) ;
55- _ . kancolleFit = k
102+
103+ if ( reset ) {
104+ resetFrameStyle ( gf ) ;
105+ lastScale = null ;
106+ lastVW = 0 ;
107+ lastVH = 0 ;
108+ }
109+
110+ // Compute scale to fit viewport.
111+ const vw = window . innerWidth ;
112+ const vh = window . innerHeight ;
113+
114+ // Only accept real size changes (user resize / orientation), ignore hover-induced jitters.
115+ if (
116+ ! reset &&
117+ Math . abs ( vw - lastVW ) < 8 && // <8px delta: ignore
118+ Math . abs ( vh - lastVH ) < 8
119+ ) {
120+ return ;
121+ }
122+ lastVW = vw ;
123+ lastVH = vh ;
124+
125+ let scale = Math . min ( vw / GAME_W , vh / GAME_H ) ;
126+ lastScale = scale ;
127+
128+ const offsetX = ( vw - GAME_W * scale ) / 2 ;
129+ const offsetY = ( vh - GAME_H * scale ) / 2 ;
130+
131+ const style = gf . style ;
132+ style . transformOrigin = 'top left' ;
133+ style . transform = `translate(${ offsetX } px, ${ offsetY } px) scale(${ scale } )` ;
134+ gf . dataset . kcScale = String ( scale ) ;
135+
136+ // Keep parent from adding padding.
137+ const parent = gf . parentElement ;
138+ if ( parent ) {
139+ parent . style . margin = '0' ;
140+ parent . style . padding = '0' ;
141+ parent . style . width = '100%' ;
142+ parent . style . height = '100%' ;
143+ parent . style . background = '#000' ;
56144 }
57- kancolleFit ( )
58- } ) ( document , window )
145+
146+ // Best-effort: if same-origin, hide inner clutter too.
147+ try {
148+ const inner = gf . contentDocument ;
149+ if ( inner ) {
150+ injectStyle (
151+ inner ,
152+ `
153+ html, body { margin: 0 !important; padding: 0 !important; overflow: hidden !important; background: #000 !important; }
154+ #htmlWrap, #flashWrap { width: ${ GAME_W } px !important; height: ${ GAME_H } px !important; }
155+ body > *:not(#htmlWrap):not(#flashWrap) { display: none !important; }
156+ ` ,
157+ ) ;
158+ }
159+ } catch ( e ) {
160+ // Cross-origin; ignore.
161+ }
162+ }
163+
164+ // Public refresh hook for subsequent triggers.
165+ window . __kcAutoScaleRefresh = ( opts = { } ) =>
166+ requestAnimationFrame ( ( ) => applyScale ( opts ) ) ;
167+
168+ // Run once DOM is ready.
169+ if ( document . readyState === 'loading' ) {
170+ document . addEventListener (
171+ 'DOMContentLoaded' ,
172+ ( ) => window . __kcAutoScaleRefresh ( { reset : true } ) ,
173+ { once : true } ,
174+ ) ;
175+ } else {
176+ window . __kcAutoScaleRefresh ( { reset : true } ) ;
177+ }
178+
179+ // Avoid auto-resize on minor UI chrome changes; users can tap the scale button to refresh.
180+ window . addEventListener (
181+ 'orientationchange' ,
182+ ( ) => window . __kcAutoScaleRefresh ( { reset : true } ) ,
183+ { passive : true } ,
184+ ) ;
185+ } ) ( ) ;
0 commit comments