@@ -22,12 +22,36 @@ angular.module('mm.addons.remotestyles')
2222 * @name $mmaRemoteStyles
2323 */
2424. factory ( '$mmaRemoteStyles' , function ( $log , $q , $mmSite , $mmSitesManager , $mmFilepool , $http , $mmFS , mmaRemoteStylesComponent ,
25- mmCoreNotDownloaded ) {
25+ mmCoreNotDownloaded , $mmUtil , md5 ) {
2626
2727 $log = $log . getInstance ( '$mmaRemoteStyles' ) ;
2828
2929 var self = { } ,
30- remoteStylesEl = angular . element ( document . querySelector ( '#mobilecssurl' ) ) ;
30+ remoteStylesEls = { } ;
31+
32+ /**
33+ * Add a style element for a site and load the styles for that element. The style will be disabled.
34+ *
35+ * @module mm.addons.remotestyles
36+ * @ngdoc method
37+ * @name $mmaRemoteStyles#addSite
38+ * @param {String } siteId Site ID.
39+ * @return {Promise } Promise resolved when added and loaded.
40+ */
41+ self . addSite = function ( siteId ) {
42+ if ( ! siteId || remoteStylesEls [ siteId ] ) {
43+ return $q . when ( ) ;
44+ }
45+
46+ var el = angular . element ( '<style id="mobilecssurl-' + siteId + '" disabled="disabled"></style>' ) ;
47+ angular . element ( document . head ) . append ( el ) ;
48+ remoteStylesEls [ siteId ] = {
49+ element : el ,
50+ hash : ''
51+ } ;
52+
53+ return self . load ( siteId , true ) ;
54+ } ;
3155
3256 /**
3357 * Clear remote styles added to the DOM.
@@ -37,7 +61,47 @@ angular.module('mm.addons.remotestyles')
3761 * @name $mmaRemoteStyles#clear
3862 */
3963 self . clear = function ( ) {
40- remoteStylesEl . html ( '' ) ;
64+ // Disable all the styles.
65+ angular . element ( document . querySelectorAll ( 'style[id*=mobilecssurl]' ) ) . attr ( 'disabled' , true ) ;
66+ } ;
67+
68+ /**
69+ * Downloads a CSS file and remove old files if needed.
70+ *
71+ * @param {String } siteId Site ID.
72+ * @param {String } url File URL.
73+ * @return {Promise } Promise resolved when the file is downloaded.
74+ */
75+ function downloadFileAndRemoveOld ( siteId , url ) {
76+ return $mmFilepool . getFileStateByUrl ( siteId , url ) . then ( function ( state ) {
77+ return state !== mmCoreNotDownloaded ;
78+ } ) . catch ( function ( ) {
79+ return true ; // An error occurred while getting state (shouldn't happen). Don't delete downloaded file.
80+ } ) . then ( function ( isDownloaded ) {
81+ if ( ! isDownloaded ) {
82+ // File not downloaded, URL has changed or first time. Delete downloaded CSS files.
83+ return $mmFilepool . removeFilesByComponent ( siteId , mmaRemoteStylesComponent , 1 ) ;
84+ }
85+ } ) . then ( function ( ) {
86+ return $mmFilepool . downloadUrl ( siteId , url , false , mmaRemoteStylesComponent , 1 ) ;
87+ } ) ;
88+ }
89+
90+ /**
91+ * Enable the styles of a certain site.
92+ *
93+ * @module mm.addons.remotestyles
94+ * @ngdoc method
95+ * @name $mmaRemoteStyles#addSite
96+ * @param {String } [siteId] Site ID. If not defined, current site.
97+ * @return {Void }
98+ */
99+ self . enable = function ( siteId ) {
100+ siteId = siteId || $mmSite . getId ( ) ;
101+
102+ if ( remoteStylesEls [ siteId ] ) {
103+ remoteStylesEls [ siteId ] . element . attr ( 'disabled' , false ) ;
104+ }
41105 } ;
42106
43107 /**
@@ -46,47 +110,33 @@ angular.module('mm.addons.remotestyles')
46110 * @module mm.addons.remotestyles
47111 * @ngdoc method
48112 * @name $mmaRemoteStyles#get
49- * @param {String } siteid Site ID.
50- * @return {Promise } Promise resolved with the styles.
113+ * @param {String } [siteId] Site ID. If not defined, current site .
114+ * @return {Promise } Promise resolved with the styles and the URL of the loaded CSS file (local if downloaded) .
51115 */
52- self . get = function ( siteid ) {
53- var promise ;
116+ self . get = function ( siteId ) {
117+ var fileUrl ;
54118
55- siteid = siteid || $mmSite . getId ( ) ;
56- if ( ! siteid ) {
119+ siteId = siteId || $mmSite . getId ( ) ;
120+ if ( ! siteId ) {
57121 return $q . reject ( ) ;
58122 }
59123
60- // Downloads a CSS file and remove old files if needed.
61- function downloadFileAndRemoveOld ( url ) {
62- return $mmFilepool . getFileStateByUrl ( siteid , url ) . then ( function ( state ) {
63- return state !== mmCoreNotDownloaded ;
64- } ) . catch ( function ( ) {
65- return true ; // An error occurred while getting state (shouldn't happen). Don't delete downloaded file.
66- } ) . then ( function ( isDownloaded ) {
67- if ( ! isDownloaded ) {
68- // File not downloaded, URL has changed or first time. Delete downloaded CSS files.
69- return $mmFilepool . removeFilesByComponent ( siteid , mmaRemoteStylesComponent , 1 ) ;
70- }
71- } ) . then ( function ( ) {
72- return $mmFilepool . downloadUrl ( siteid , url , false , mmaRemoteStylesComponent , 1 ) ;
73- } ) ;
74- }
75-
76- return $mmSitesManager . getSite ( siteid ) . then ( function ( site ) {
124+ return $mmSitesManager . getSite ( siteId ) . then ( function ( site ) {
77125 var infos = site . getInfo ( ) ;
78126 if ( infos && infos . mobilecssurl ) {
127+ fileUrl = infos . mobilecssurl ;
128+
79129 if ( $mmFS . isAvailable ( ) ) {
80130 // The file system is available. Download the file and remove old CSS files if needed.
81- return downloadFileAndRemoveOld ( infos . mobilecssurl ) ;
131+ return downloadFileAndRemoveOld ( siteId , infos . mobilecssurl ) ;
82132 } else {
83133 // We return the online URL. We're probably on browser.
84134 return infos . mobilecssurl ;
85135 }
86136 } else {
87137 if ( infos . mobilecssurl === '' ) {
88138 // CSS URL is empty. Delete downloaded files (if any).
89- $mmFilepool . removeFilesByComponent ( siteid , mmaRemoteStylesComponent , 1 )
139+ $mmFilepool . removeFilesByComponent ( siteId , mmaRemoteStylesComponent , 1 ) ;
90140 }
91141 return $q . reject ( ) ;
92142 }
@@ -95,30 +145,152 @@ angular.module('mm.addons.remotestyles')
95145 return $http . get ( url ) ;
96146 } ) . then ( function ( response ) {
97147 if ( typeof response . data == 'string' ) {
98- return response . data ;
148+ return { file : fileUrl , styles : response . data } ;
99149 } else {
100150 return $q . reject ( ) ;
101151 }
102152 } ) ;
103153 } ;
104154
105155 /**
106- * Load styles for current site.
156+ * Load styles for a certain site.
107157 *
108158 * @module mm.addons.remotestyles
109159 * @ngdoc method
110160 * @name $mmaRemoteStyles#load
161+ * @param {String } [siteId] Site ID. If not defined, current site.
162+ * @param {Boolean } disabled True if loaded styles should be disabled, false if they should be enabled.
163+ * @return {Promise } Promise resolved when styles are loaded.
111164 */
112- self . load = function ( ) {
113- var siteid = $mmSite . getId ( ) ;
114- if ( siteid ) {
115- self . get ( siteid ) . then ( function ( styles ) {
116- if ( siteid === $mmSite . getId ( ) ) { // Make sure it hasn't logout while retrieving styles.
117- remoteStylesEl . html ( styles ) ;
165+ self . load = function ( siteId , disabled ) {
166+ siteId = siteId || $mmSite . getId ( ) ;
167+ disabled = ! ! disabled ;
168+
169+ $log . debug ( 'Load site: ' , siteId , disabled ) ;
170+ if ( siteId && remoteStylesEls [ siteId ] ) {
171+ // Enable or disable the styles.
172+ remoteStylesEls [ siteId ] . element . attr ( 'disabled' , disabled ) ;
173+
174+ return self . get ( siteId ) . then ( function ( data ) {
175+ var hash = md5 . createHash ( data . styles ) ;
176+
177+ // Update the styles only if they have changed.
178+ if ( remoteStylesEls [ siteId ] . hash !== hash ) {
179+ remoteStylesEls [ siteId ] . element . html ( data . styles ) ;
180+ remoteStylesEls [ siteId ] . hash = hash ;
181+
182+ // New styles will be applied even if the style is disabled. We'll disable it again if needed.
183+ if ( disabled && remoteStylesEls [ siteId ] . element . attr ( 'disabled' ) == 'disabled' ) {
184+ remoteStylesEls [ siteId ] . element . attr ( 'disabled' , true ) ;
185+ }
118186 }
187+
188+ // Styles have been loaded, now treat the CSS.
189+ treatCSSCode ( siteId , data . file , data . styles ) ;
119190 } ) ;
120191 }
192+
193+ return $q . reject ( ) ;
121194 } ;
122195
196+ /**
197+ * Preload the styles of the current site (stored in DB). Please do not use.
198+ *
199+ * @module mm.addons.remotestyles
200+ * @ngdoc method
201+ * @name $mmaRemoteStyles#_preloadCurrentSite
202+ * @return {Promise } Promise resolved when loaded.
203+ * @protected
204+ */
205+ self . _preloadCurrentSite = function ( ) {
206+ return $mmSitesManager . getStoredCurrentSiteId ( ) . then ( function ( siteId ) {
207+ return self . addSite ( siteId ) ;
208+ } ) ;
209+ } ;
210+
211+ /**
212+ * Preload the styles of all the stored sites. Please do not use.
213+ *
214+ * @module mm.addons.remotestyles
215+ * @ngdoc method
216+ * @name $mmaRemoteStyles#_preloadSites
217+ * @return {Promise } Promise resolved when loaded.
218+ * @protected
219+ */
220+ self . _preloadSites = function ( ) {
221+ return $mmSitesManager . getSitesIds ( ) . then ( function ( ids ) {
222+ var promises = [ ] ;
223+ angular . forEach ( ids , function ( siteId ) {
224+ promises . push ( self . addSite ( siteId ) ) ;
225+ } ) ;
226+ return $q . all ( promises ) ;
227+ } ) ;
228+ } ;
229+
230+ /**
231+ * Remove the styles of a certain site.
232+ *
233+ * @module mm.addons.remotestyles
234+ * @ngdoc method
235+ * @name $mmaRemoteStyles#removeSite
236+ * @param {String } siteId Site ID.
237+ * @return {Void }
238+ */
239+ self . removeSite = function ( siteId ) {
240+ if ( siteId && remoteStylesEls [ siteId ] ) {
241+ remoteStylesEls [ siteId ] . element . remove ( ) ;
242+ delete remoteStylesEls [ siteId ] ;
243+ }
244+ } ;
245+
246+ /**
247+ * Search for files in a CSS code and try to download them. Once downloaded, replace their URLs
248+ * and store the result in the CSS file.
249+ *
250+ * @param {String } siteId Site ID.
251+ * @param {String } fileUrl CSS file URL.
252+ * @param {String } cssCode CSS code.
253+ * @return {Promise } Promise resolved with the CSS code.
254+ */
255+ function treatCSSCode ( siteId , fileUrl , cssCode ) {
256+ if ( ! $mmFS . isAvailable ( ) ) {
257+ return $q . reject ( ) ;
258+ }
259+
260+ var urls = $mmUtil . extractUrlsFromCSS ( cssCode ) ,
261+ promises = [ ] ,
262+ filePath ,
263+ updated = false ;
264+
265+ // Get the path of the CSS file.
266+ promises . push ( $mmFilepool . getFilePathByUrl ( siteId , fileUrl ) . then ( function ( path ) {
267+ filePath = path ;
268+ } ) ) ;
269+
270+ angular . forEach ( urls , function ( url ) {
271+ // Download the file only if it's an online URL.
272+ if ( url . indexOf ( 'http' ) == 0 ) {
273+ promises . push ( $mmFilepool . downloadUrl ( siteId , url , false , mmaRemoteStylesComponent , 2 ) . then ( function ( fileUrl ) {
274+ if ( fileUrl != url ) {
275+ cssCode = cssCode . replace ( new RegExp ( url , 'g' ) , fileUrl ) ;
276+ updated = true ;
277+ }
278+ } ) . catch ( function ( error ) {
279+ // It shouldn't happen. Ignore errors.
280+ $log . warn ( 'MMRMSTYLES Error treating file ' , url , error ) ;
281+ } ) ) ;
282+ }
283+ } ) ;
284+
285+ return $q . all ( promises ) . then ( function ( ) {
286+ // All files downloaded. Store the result if it has changed.
287+ if ( updated ) {
288+ return $mmFS . writeFile ( filePath , cssCode ) ;
289+ }
290+ } ) . then ( function ( ) {
291+ return cssCode ;
292+ } ) ;
293+ }
294+
123295 return self ;
124296} ) ;
0 commit comments