UNPKG

15.7 kBJavaScriptView Raw
1/*
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20*/
21
22/* jslint sloppy:true */
23/* global Windows:true, setImmediate */
24
25var cordova = require('cordova'),
26 urlutil = require('cordova/urlutil');
27
28var browserWrap,
29 popup,
30 navigationButtonsDiv,
31 navigationButtonsDivInner,
32 backButton,
33 forwardButton,
34 closeButton,
35 bodyOverflowStyle,
36 navigationEventsCallback,
37 hardwareBackCallback;
38
39// x-ms-webview is available starting from Windows 8.1 (platformId is 'windows')
40// http://msdn.microsoft.com/en-us/library/windows/apps/dn301831.aspx
41var isWebViewAvailable = cordova.platformId === 'windows';
42
43function attachNavigationEvents(element, callback) {
44 if (isWebViewAvailable) {
45 element.addEventListener("MSWebViewNavigationStarting", function (e) {
46 callback({ type: "loadstart", url: e.uri}, {keepCallback: true} );
47 });
48
49 element.addEventListener("MSWebViewNavigationCompleted", function (e) {
50 if (e.isSuccess) {
51 callback({ type: "loadstop", url: e.uri }, { keepCallback: true });
52 } else {
53 callback({ type: "loaderror", url: e.uri, code: e.webErrorStatus, message: "Navigation failed with error code " + e.webErrorStatus}, { keepCallback: true });
54 }
55 });
56
57 element.addEventListener("MSWebViewUnviewableContentIdentified", function (e) {
58 // WebView found the content to be not HTML.
59 // http://msdn.microsoft.com/en-us/library/windows/apps/dn609716.aspx
60 callback({ type: "loaderror", url: e.uri, code: e.webErrorStatus, message: "Navigation failed with error code " + e.webErrorStatus}, { keepCallback: true });
61 });
62
63 element.addEventListener("MSWebViewContentLoading", function (e) {
64 if (navigationButtonsDiv && popup) {
65 if (popup.canGoBack) {
66 backButton.removeAttribute("disabled");
67 } else {
68 backButton.setAttribute("disabled", "true");
69 }
70
71 if (popup.canGoForward) {
72 forwardButton.removeAttribute("disabled");
73 } else {
74 forwardButton.setAttribute("disabled", "true");
75 }
76 }
77 });
78 } else {
79 var onError = function () {
80 callback({ type: "loaderror", url: this.contentWindow.location}, {keepCallback: true});
81 };
82
83 element.addEventListener("unload", function () {
84 callback({ type: "loadstart", url: this.contentWindow.location}, {keepCallback: true});
85 });
86
87 element.addEventListener("load", function () {
88 callback({ type: "loadstop", url: this.contentWindow.location}, {keepCallback: true});
89 });
90
91 element.addEventListener("error", onError);
92 element.addEventListener("abort", onError);
93 }
94}
95
96var IAB = {
97 close: function (win, lose) {
98 setImmediate(function () {
99 if (browserWrap) {
100 if (navigationEventsCallback) {
101 navigationEventsCallback({ type: "exit" });
102 }
103
104 browserWrap.parentNode.removeChild(browserWrap);
105 // Reset body overflow style to initial value
106 document.body.style.msOverflowStyle = bodyOverflowStyle;
107 browserWrap = null;
108 popup = null;
109
110 document.removeEventListener("backbutton", hardwareBackCallback, false);
111 }
112 });
113 },
114 show: function (win, lose) {
115 setImmediate(function () {
116 if (browserWrap) {
117 browserWrap.style.display = "block";
118 }
119 });
120 },
121 hide: function (win, lose) {
122 if (browserWrap) {
123 browserWrap.style.display = "none";
124 }
125 },
126 open: function (win, lose, args) {
127 // make function async so that we can add navigation events handlers before view is loaded and navigation occured
128 setImmediate(function () {
129 var strUrl = args[0],
130 target = args[1],
131 features = args[2],
132 url;
133
134 navigationEventsCallback = win;
135
136 if (target === "_system") {
137 url = new Windows.Foundation.Uri(strUrl);
138 Windows.System.Launcher.launchUriAsync(url);
139 } else if (target === "_self" || !target) {
140 window.location = strUrl;
141 } else {
142 // "_blank" or anything else
143 if (!browserWrap) {
144 var browserWrapStyle = document.createElement('link');
145 browserWrapStyle.rel = "stylesheet";
146 browserWrapStyle.type = "text/css";
147 browserWrapStyle.href = urlutil.makeAbsolute("/www/css/inappbrowser.css");
148
149 document.head.appendChild(browserWrapStyle);
150
151 browserWrap = document.createElement("div");
152 browserWrap.className = "inAppBrowserWrap";
153
154 if (features.indexOf("fullscreen=yes") > -1) {
155 browserWrap.classList.add("inAppBrowserWrapFullscreen");
156 }
157
158 // Save body overflow style to be able to reset it back later
159 bodyOverflowStyle = document.body.style.msOverflowStyle;
160
161 browserWrap.onclick = function () {
162 setTimeout(function () {
163 IAB.close(navigationEventsCallback);
164 }, 0);
165 };
166
167 document.body.appendChild(browserWrap);
168 // Hide scrollbars for the whole body while inappbrowser's window is open
169 document.body.style.msOverflowStyle = "none";
170 }
171
172 if (features.indexOf("hidden=yes") !== -1) {
173 browserWrap.style.display = "none";
174 }
175
176 popup = document.createElement(isWebViewAvailable ? "x-ms-webview" : "iframe");
177 if (popup instanceof HTMLIFrameElement) {
178 // For iframe we need to override bacground color of parent element here
179 // otherwise pages without background color set will have transparent background
180 popup.style.backgroundColor = "white";
181 }
182 popup.style.borderWidth = "0px";
183 popup.style.width = "100%";
184 popup.style.marginBottom = "-5px";
185
186 browserWrap.appendChild(popup);
187
188 var closeHandler = function (e) {
189 setTimeout(function () {
190 IAB.close(navigationEventsCallback);
191 }, 0);
192 };
193
194 if (features.indexOf("hardwareback=yes") > -1 || features.indexOf("hardwareback") === -1) {
195 hardwareBackCallback = function () {
196 if (browserWrap.style.display === 'none') {
197 // NOTE: backbutton handlers have to throw an exception in order to prevent
198 // returning 'true' inside cordova-js, which would mean that the event is handled by user.
199 // Throwing an exception means that the default/system navigation behavior will take place,
200 // which is to exit the app if the navigation stack is empty.
201 throw 'Exit the app';
202 }
203
204 if (popup.canGoBack) {
205 popup.goBack();
206 } else {
207 closeHandler();
208 }
209 };
210 } else if (features.indexOf("hardwareback=no") > -1) {
211 hardwareBackCallback = function () {
212 if (browserWrap.style.display === 'none') {
213 // See comment above
214 throw 'Exit the app';
215 }
216
217 closeHandler();
218 };
219 }
220
221 document.addEventListener("backbutton", hardwareBackCallback, false);
222
223 if (features.indexOf("location=yes") !== -1 || features.indexOf("location") === -1) {
224 popup.style.height = "calc(100% - 70px)";
225
226 navigationButtonsDiv = document.createElement("div");
227 navigationButtonsDiv.className = "inappbrowser-app-bar";
228 navigationButtonsDiv.onclick = function (e) {
229 e.cancelBubble = true;
230 };
231
232 navigationButtonsDivInner = document.createElement("div");
233 navigationButtonsDivInner.className = "inappbrowser-app-bar-inner";
234 navigationButtonsDivInner.onclick = function (e) {
235 e.cancelBubble = true;
236 };
237
238 backButton = document.createElement("div");
239 backButton.innerText = "back";
240 backButton.className = "app-bar-action action-back";
241 backButton.addEventListener("click", function (e) {
242 if (popup.canGoBack)
243 popup.goBack();
244 });
245
246 forwardButton = document.createElement("div");
247 forwardButton.innerText = "forward";
248 forwardButton.className = "app-bar-action action-forward";
249 forwardButton.addEventListener("click", function (e) {
250 if (popup.canGoForward)
251 popup.goForward();
252 });
253
254 closeButton = document.createElement("div");
255 closeButton.innerText = "close";
256 closeButton.className = "app-bar-action action-close";
257 closeButton.addEventListener("click", closeHandler);
258
259 if (!isWebViewAvailable) {
260 // iframe navigation is not yet supported
261 backButton.setAttribute("disabled", "true");
262 forwardButton.setAttribute("disabled", "true");
263 }
264
265 navigationButtonsDivInner.appendChild(backButton);
266 navigationButtonsDivInner.appendChild(forwardButton);
267 navigationButtonsDivInner.appendChild(closeButton);
268 navigationButtonsDiv.appendChild(navigationButtonsDivInner);
269
270 browserWrap.appendChild(navigationButtonsDiv);
271 } else {
272 popup.style.height = "100%";
273 }
274
275 // start listening for navigation events
276 attachNavigationEvents(popup, navigationEventsCallback);
277
278 if (isWebViewAvailable) {
279 strUrl = strUrl.replace("ms-appx://", "ms-appx-web://");
280 }
281 popup.src = strUrl;
282 }
283 });
284 },
285
286 injectScriptCode: function (win, fail, args) {
287 setImmediate(function () {
288 var code = args[0],
289 hasCallback = args[1];
290
291 if (isWebViewAvailable && browserWrap && popup) {
292 var op = popup.invokeScriptAsync("eval", code);
293 op.oncomplete = function (e) {
294 if (hasCallback) {
295 // return null if event target is unavailable by some reason
296 var result = (e && e.target) ? [e.target.result] : [null];
297 win(result);
298 }
299 };
300 op.onerror = function () { };
301 op.start();
302 }
303 });
304 },
305
306 injectScriptFile: function (win, fail, args) {
307 setImmediate(function () {
308 var filePath = args[0],
309 hasCallback = args[1];
310
311 if (!!filePath) {
312 filePath = urlutil.makeAbsolute(filePath);
313 }
314
315 if (isWebViewAvailable && browserWrap && popup) {
316 // CB-12364 getFileFromApplicationUriAsync does not support ms-appx-web
317 var uri = new Windows.Foundation.Uri(filePath.replace('ms-appx-web:', 'ms-appx:'));
318 Windows.Storage.StorageFile.getFileFromApplicationUriAsync(uri).done(function (file) {
319 Windows.Storage.FileIO.readTextAsync(file).done(function (code) {
320 var op = popup.invokeScriptAsync("eval", code);
321 op.oncomplete = function(e) {
322 if (hasCallback) {
323 var result = [e.target.result];
324 win(result);
325 }
326 };
327 op.onerror = function () { };
328 op.start();
329 });
330 });
331 }
332 });
333 },
334
335 injectStyleCode: function (win, fail, args) {
336 setImmediate(function () {
337 var code = args[0],
338 hasCallback = args[1];
339
340 if (isWebViewAvailable && browserWrap && popup) {
341 injectCSS(popup, code, hasCallback && win);
342 }
343 });
344 },
345
346 injectStyleFile: function (win, fail, args) {
347 setImmediate(function () {
348 var filePath = args[0],
349 hasCallback = args[1];
350
351 filePath = filePath && urlutil.makeAbsolute(filePath);
352
353 if (isWebViewAvailable && browserWrap && popup) {
354 // CB-12364 getFileFromApplicationUriAsync does not support ms-appx-web
355 var uri = new Windows.Foundation.Uri(filePath.replace('ms-appx-web:', 'ms-appx:'));
356 Windows.Storage.StorageFile.getFileFromApplicationUriAsync(uri).then(function (file) {
357 return Windows.Storage.FileIO.readTextAsync(file);
358 }).done(function (code) {
359 injectCSS(popup, code, hasCallback && win);
360 }, function () {
361 // no-op, just catch an error
362 });
363 }
364 });
365 }
366};
367
368function injectCSS (webView, cssCode, callback) {
369 // This will automatically escape all thing that we need (quotes, slashes, etc.)
370 var escapedCode = JSON.stringify(cssCode);
371 var evalWrapper = "(function(d){var c=d.createElement('style');c.innerHTML=%s;d.head.appendChild(c);})(document)"
372 .replace('%s', escapedCode);
373
374 var op = webView.invokeScriptAsync("eval", evalWrapper);
375 op.oncomplete = function() {
376 if (callback) {
377 callback([]);
378 }
379 };
380 op.onerror = function () { };
381 op.start();
382}
383
384module.exports = IAB;
385
386require("cordova/exec/proxy").add("InAppBrowser", module.exports);