1 /*
  2  * URLContent.js
  3  *
  4  * Sweet Home 3D, Copyright (c) 2015 Emmanuel PUYBARET / eTeks <info@eteks.com>
  5  * 
  6  * This program is free software; you can redistribute it and/or modify
  7  * it under the terms of the GNU General Public License as published by
  8  * the Free Software Foundation; either version 2 of the License, or
  9  * (at your option) any later version.
 10  *
 11  * This program is distributed in the hope that it will be useful,
 12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
 14  * GNU General Public License for more details.
 15  *
 16  * You should have received a copy of the GNU General Public License
 17  * along with this program; if not, write to the Free Software
 18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
 19  */
 20 
 21 /**
 22  * Content wrapper for strings used as URLs.
 23  * @param {string} url  the URL from which this content will be read
 24  * @constructor
 25  * @author Emmanuel Puybaret
 26  */
 27 function URLContent(url) {
 28   this.url = url;
 29 }
 30 
 31 URLContent["__interfaces"] = ["com.eteks.sweethome3d.model.Content"];
 32 
 33 /**
 34  * Returns the URL of this content.
 35  * @return {string}
 36  */
 37 URLContent.prototype.getURL = function() {
 38   return this.url;
 39 }
 40 
 41 /**
 42  * Returns <code>true</code> if the URL stored by this content 
 43  * references an entry in a JAR.
 44  * @return {boolean}
 45  */
 46 URLContent.prototype.isJAREntry = function() {
 47   return this.url.indexOf("jar:") === 0 && this.url.indexOf('!') !== -1; 
 48 }
 49 
 50 /**
 51  * Returns the URL base of a JAR entry.
 52  * @return {string}
 53  */
 54 URLContent.prototype.getJAREntryURL = function() {
 55   if (!this.isJAREntry()) {
 56     throw new IllegalStateException("Content isn't a JAR entry");
 57   }
 58   return this.url.substring("jar:".length, this.url.indexOf('!'));
 59 }
 60 
 61 /**
 62  * Returns the name of a JAR entry. 
 63  * If the JAR entry in the URL given at creation time was encoded in application/x-www-form-urlencoded format,
 64  * this method will return it unchanged and not decoded.
 65  * @return {string}
 66  * @throws IllegalStateException if the URL of this content 
 67  *                    doesn't reference an entry in a JAR URL.
 68  */
 69 URLContent.prototype.getJAREntryName = function() {
 70   if (!this.isJAREntry()) {
 71     throw new IllegalStateException("Content isn't a JAR entry");
 72   }
 73   return this.url.substring(this.url.indexOf('!') + 2);
 74 }
 75 
 76 /**
 77  * Returns <code>true</code> if the object in parameter is an URL content
 78  * that references the same URL as this object.
 79  * @return {boolean}
 80  */
 81 URLContent.prototype.equals = function(obj) {
 82   if (obj === this) {
 83     return true;
 84   } else if (obj instanceof URLContent) {
 85     var urlContent = obj;
 86     return urlContent.url == this.url;
 87   } else {
 88     return false;
 89   }
 90 }
 91 
 92 /**
 93  * An URL content read from a home stream.
 94  * @param {string} url  the URL from which this content will be read
 95  * @constructor
 96  * @ignore
 97  * @author Emmanuel Puybaret
 98  */
 99 function HomeURLContent(url) {
100   URLContent.call(this, url);
101 }
102 HomeURLContent.prototype = Object.create(URLContent.prototype);
103 HomeURLContent.prototype.constructor = HomeURLContent;
104 
105 HomeURLContent["__interfaces"] = ["com.eteks.sweethome3d.model.Content"];
106 
107 /**
108  * ZIP reading utilities.
109  * @class
110  * @author Emmanuel Puybaret
111  */
112 var ZIPTools = {};
113 
114 ZIPTools.READING = "Reading";
115 
116 ZIPTools.openedZips = {};
117 ZIPTools.runningRequests = [];
118 
119 /**
120  * Reads the ZIP data in the given URL.
121  * @param url the URL of a zip file containing an OBJ entry that will be loaded
122  *            or an URL noted as jar:url!/objEntry where objEntry will be loaded.
123  * @param synchronous optional parameter equal to false by default
124  * @param {{zipReady, zipError, progression}} zipObserver An observer containing zipReady(zip), 
125  *            zipError(error), progression(part, info, percentage) methods that
126  *            will called at various phases.
127  */
128 ZIPTools.getZIP = function(url, synchronous, zipObserver) {
129   if (zipObserver === undefined) {
130     zipObserver = synchronous;
131     synchronous = false;
132   }
133   if (url in ZIPTools.openedZips) {
134     zipObserver.zipReady(ZIPTools.openedZips [url]); 
135   } else {
136     try {
137       var request = new XMLHttpRequest();
138       request.open('GET', url, !synchronous);
139       request.responseType = "arraybuffer";
140       request.overrideMimeType("application/octet-stream");
141       request.addEventListener("readystatechange", 
142           function(ev) {
143             if (request.readyState === XMLHttpRequest.DONE) {
144               if ((request.status === 200 || request.status === 0)
145                   && request.response != null) {
146                 try {
147                   ZIPTools.runningRequests.splice(ZIPTools.runningRequests.indexOf(request), 1);
148                   var zip = new JSZip(request.response);
149                   ZIPTools.openedZips [url] = zip;
150                   zipObserver.zipReady(ZIPTools.openedZips [url]); 
151                 } catch (ex) {
152                   zipObserver.zipError(ex);
153                 }
154               } else {
155                 // Report error for requests that weren't aborted
156                 var index = ZIPTools.runningRequests.indexOf(request);              
157                 if (index >= 0) {
158                   ZIPTools.runningRequests.splice(index, 1);                
159                   zipObserver.zipError(new Error(request.status + " while requesting " + url)); 
160                 }
161               }
162             }
163           });
164       request.addEventListener("progress", 
165           function(ev) {
166             if (ev.lengthComputable
167                 && zipObserver.progression !== undefined) {
168               zipObserver.progression(ZIPTools.READING, url, ev.loaded / ev.total);
169             }
170           });
171       request.send();
172       ZIPTools.runningRequests.push(request);
173     } catch (ex) {
174       zipObserver.zipError(ex);
175     }
176   }
177 }
178 
179 
180 /**
181  * Clears cache and aborts running requests.
182  */
183 ZIPTools.clear = function() {
184   ZIPTools.openedZips = {};
185   // Abort running requests
186   while (ZIPTools.runningRequests.length > 0) {
187     var request = ZIPTools.runningRequests [ZIPTools.runningRequests.length - 1];
188     ZIPTools.runningRequests.splice(ZIPTools.runningRequests.length - 1, 1);
189     request.abort();
190   }
191 }
192 
193 /**
194  * Removes from cache the content matching the given <code>url</code>. 
195  */
196 ZIPTools.disposeZIP = function(url) {
197   delete ZIPTools.openedZips [url];
198 }
199 
200 /**
201  * Returns true if the given image data describes a transparent PNG file.
202  * @package
203  * @ignore
204  */
205 ZIPTools.isTranparentImage = function(imageData) {
206   return imageData.charAt(0).charCodeAt(0) === 0x89 
207   && imageData.charAt(1).charCodeAt(0) === 0x50 
208   && imageData.charAt(2).charCodeAt(0) === 0x4e 
209   && imageData.charAt(3).charCodeAt(0) === 0x47 
210   && imageData.charAt(4).charCodeAt(0) === 0x0d 
211   && imageData.charAt(5).charCodeAt(0) === 0x0a 
212   && imageData.charAt(6).charCodeAt(0) === 0x1a 
213   && imageData.charAt(7).charCodeAt(0) === 0x0a
214   && (imageData.charAt(25).charCodeAt(0) === 4
215       || imageData.charAt(25).charCodeAt(0) === 6
216       || (imageData.indexOf("PLTE") !== -1 && imageData.indexOf("tRNS") !== -1));
217 }
218 
219 /**
220  * Returns the folder where a given Javascript .js file was read from.  
221  * @package
222  * @ignore
223  */
224 ZIPTools.getScriptFolder = function(script) {
225   var baseUrl = "http://www.sweethome3d.com/libjs/"; 
226   // Search the base URL of this script
227   var scripts = document.getElementsByTagName("script");      
228   for (var i = 0; i < scripts.length; i++) {
229     if (scripts[i].src.indexOf(script) !== -1) {
230       baseUrl = scripts[i].src.substring(0, scripts[i].src.lastIndexOf("/") + 1);
231       break;
232     }
233   }
234   return baseUrl;
235 }
236