1+ // (C) Copyright 2015 Moodle Pty Ltd.
2+ //
3+ // Licensed under the Apache License, Version 2.0 (the "License");
4+ // you may not use this file except in compliance with the License.
5+ // You may obtain a copy of the License at
6+ //
7+ // http://www.apache.org/licenses/LICENSE-2.0
8+ //
9+ // Unless required by applicable law or agreed to in writing, software
10+ // distributed under the License is distributed on an "AS IS" BASIS,
11+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ // See the License for the specific language governing permissions and
13+ // limitations under the License.
14+
15+ ( function ( ) {
16+ var url = location . href ;
17+
18+ if ( url . match ( / ^ m o o d l e a p p f s : \/ \/ l o c a l h o s t / i) || ! url . match ( / ^ [ a - z 0 - 9 ] + : \/ \/ / i) ) {
19+ // Same domain as the app, stop.
20+ return ;
21+ }
22+
23+ // Redefine window.open.
24+ window . open = function ( url , name , specs ) {
25+ if ( name == '_self' ) {
26+ // Link should be loaded in the same frame.
27+ location . href = toAbsolute ( url ) ;
28+
29+ return ;
30+ }
31+
32+ getRootWindow ( window ) . postMessage ( {
33+ environment : 'moodleapp' ,
34+ context : 'iframe' ,
35+ action : 'window_open' ,
36+ frameUrl : location . href ,
37+ url : url ,
38+ name : name ,
39+ specs : specs ,
40+ } , '*' ) ;
41+ } ;
42+
43+ // Handle link clicks.
44+ document . addEventListener ( 'click' , ( event ) => {
45+ if ( event . defaultPrevented ) {
46+ // Event already prevented by some other code.
47+ return ;
48+ }
49+
50+ // Find the link being clicked.
51+ var el = event . target ;
52+ while ( el && el . tagName !== 'A' ) {
53+ el = el . parentElement ;
54+ }
55+
56+ if ( ! el || el . treated ) {
57+ return ;
58+ }
59+
60+ // Add click listener to the link, this way if the iframe has added a listener to the link it will be executed first.
61+ el . treated = true ;
62+ el . addEventListener ( 'click' , function ( event ) {
63+ linkClicked ( el , event ) ;
64+ } ) ;
65+ } , {
66+ capture : true // Use capture to fix this listener not called if the element clicked is too deep in the DOM.
67+ } ) ;
68+
69+
70+
71+ /**
72+ * Concatenate two paths, adding a slash between them if needed.
73+ *
74+ * @param leftPath Left path.
75+ * @param rightPath Right path.
76+ * @return Concatenated path.
77+ */
78+ function concatenatePaths ( leftPath , rightPath ) {
79+ if ( ! leftPath ) {
80+ return rightPath ;
81+ } else if ( ! rightPath ) {
82+ return leftPath ;
83+ }
84+
85+ var lastCharLeft = leftPath . slice ( - 1 ) ;
86+ var firstCharRight = rightPath . charAt ( 0 ) ;
87+
88+ if ( lastCharLeft === '/' && firstCharRight === '/' ) {
89+ return leftPath + rightPath . substr ( 1 ) ;
90+ } else if ( lastCharLeft !== '/' && firstCharRight !== '/' ) {
91+ return leftPath + '/' + rightPath ;
92+ } else {
93+ return leftPath + rightPath ;
94+ }
95+ }
96+
97+ /**
98+ * Get the root window.
99+ *
100+ * @param win Current window to check.
101+ * @return Root window.
102+ */
103+ function getRootWindow ( win ) {
104+ if ( win . parent === win ) {
105+ return win ;
106+ }
107+
108+ return getRootWindow ( win . parent ) ;
109+ }
110+
111+ /**
112+ * Get the scheme from a URL.
113+ *
114+ * @param url URL to treat.
115+ * @return Scheme, undefined if no scheme found.
116+ */
117+ function getUrlScheme ( url ) {
118+ if ( ! url ) {
119+ return ;
120+ }
121+
122+ var matches = url . match ( / ^ ( [ a - z ] [ a - z 0 - 9 + \- . ] * ) : / ) ;
123+ if ( matches && matches [ 1 ] ) {
124+ return matches [ 1 ] ;
125+ }
126+ }
127+
128+ /**
129+ * Check if a URL is absolute.
130+ *
131+ * @param url URL to treat.
132+ * @return Whether it's absolute.
133+ */
134+ function isAbsoluteUrl ( url ) {
135+ return / ^ [ ^ : ] { 2 , } : \/ \/ / i. test ( url ) ;
136+ }
137+
138+ /**
139+ * Check whether a URL scheme belongs to a local file.
140+ *
141+ * @param scheme Scheme to check.
142+ * @return Whether the scheme belongs to a local file.
143+ */
144+ function isLocalFileUrlScheme ( scheme ) {
145+ if ( scheme ) {
146+ scheme = scheme . toLowerCase ( ) ;
147+ }
148+
149+ return scheme == 'cdvfile' ||
150+ scheme == 'file' ||
151+ scheme == 'filesystem' ||
152+ scheme == 'moodleappfs' ;
153+ }
154+
155+ /**
156+ * Handle a click on an anchor element.
157+ *
158+ * @param link Anchor element clicked.
159+ * @param event Click event.
160+ */
161+ function linkClicked ( link , event ) {
162+ if ( event . defaultPrevented ) {
163+ // Event already prevented by some other code.
164+ return ;
165+ }
166+
167+ var linkScheme = getUrlScheme ( link . href ) ;
168+ var pageScheme = getUrlScheme ( location . href ) ;
169+ var isTargetSelf = ! link . target || link . target == '_self' ;
170+
171+ if ( ! link . href || linkScheme == 'javascript' ) {
172+ // Links with no URL and Javascript links are ignored.
173+ return ;
174+ }
175+
176+ event . preventDefault ( ) ;
177+
178+ if ( isTargetSelf && ( isLocalFileUrlScheme ( linkScheme ) || ! isLocalFileUrlScheme ( pageScheme ) ) ) {
179+ // Link should be loaded in the same frame. Don't do it if link is online and frame is local.
180+ location . href = toAbsolute ( link . href ) ;
181+
182+ return ;
183+ }
184+
185+ getRootWindow ( window ) . postMessage ( {
186+ environment : 'moodleapp' ,
187+ context : 'iframe' ,
188+ action : 'link_clicked' ,
189+ frameUrl : location . href ,
190+ link : { href : link . href , target : link . target } ,
191+ } , '*' ) ;
192+ }
193+
194+ /**
195+ * Convert a URL to an absolute URL if needed using the frame src.
196+ *
197+ * @param url URL to convert.
198+ * @return Absolute URL.
199+ */
200+ function toAbsolute ( url ) {
201+ if ( isAbsoluteUrl ( url ) ) {
202+ return url ;
203+ }
204+
205+ // It's a relative URL, use the frame src to create the full URL.
206+ var pathToDir = location . href . substring ( 0 , location . href . lastIndexOf ( '/' ) ) ;
207+
208+ return concatenatePaths ( pathToDir , url ) ;
209+ }
210+ } ) ( ) ;
0 commit comments