1// Copyright: Copyright 2015 Codepops Inc. 2// License: All Rights Reserved. 3 4/** 5 * @fileoverview Provide the Sandbox class. 6 * @author: Scott Lininger ([email protected]) 7 */ 8 9 /** 10 * Namespace where we'll hang everything off of. 11 * @namespace 12 * @expose 13 * @export 14 */ 15var codepops = codepops || {}; 16 17/** 18 * Provides an iframe-hosted Bitsbox library sandbox. We run code inside the 19 * the iframe so it is cross-domain isolated and so its variable space can 20 * be destroyed between runs. 21 * @param {EditorPage} page The page we're running within. 22 * @param {Element} renderDiv The div to render the iframe within. 23 * @param {Object=} opt_settings Optional hash of settings. 24 * @constructor 25 * @export 26 * @expose 27 */ 28codepops.Sandbox = function(page, renderDiv, opt_settings) { 29 var settings = opt_settings || {}; 30 31 /** 32 * Contains a reference to the window. Useful for dependency injection. 33 * @type {window} 34 * @private 35 */ 36 this.window_ = settings['window'] || window; 37 38 /** 39 * Contains a reference to the document. Useful for dependency injection. 40 * @type {document} 41 * @private 42 */ 43 this.document_ = settings['document'] || window.document; 44 45 /** 46 * Contains a reference to the parent Page. 47 * @type {codepops.EditorPage} 48 * @private 49 */ 50 this.page_ = page; 51 52 /** 53 * The div we'll render the control into. 54 * @type {Element} 55 * @private 56 */ 57 this.renderDiv_ = renderDiv; 58 59 /** 60 * A datastore path to be used for send/get exchanges. 61 * @type {string} 62 * @private 63 */ 64 this.collabPath_ = null; 65 66 /** 67 * Functions that will be called on various iframe mouse events. 68 * @type {function} 69 * @private 70 */ 71 this.onMouseMove_ = null; 72 this.onMouseOver_ = null; 73 this.onMouseOut_ = null; 74 75 /** 76 * Functions that will be called on various iframe touch events. 77 * @type {function} 78 * @private 79 */ 80 this.onTouchMove_ = null; 81 this.onTouchStart_ = null; 82 83 /** 84 * Preload an iframe that is ready to run code within. 85 * @private 86 */ 87 this.iframe_ = this.document_.createElement('iframe'); 88 this.iframe_.style.visibility = 'hidden'; 89 this.iframe_.title = 'Sandbox iframe'; 90 this.iframe_.src = '/sandbox.html'; 91 this.renderDiv_.appendChild(this.iframe_); 92 93 /** 94 * Figure out if we're running inside Interet Explorer. 95 * @type {boolean} 96 * @private 97 */ 98 this.isIE_ = c123.isIE(); 99 100 /** 101 * Figure out if we're running inside Firefox. 102 * @type {boolean} 103 * @private 104 */ 105 this.isFirefox_ = c123.isFirefox(); 106}; 107 108 109/** 110 * Runs the passed code inside the next available iframe. 111 * @param {Object} code The code that we want to run. 112 * @param {?Object} opt_customAssetHash A hash of custom assets, reflecting 113 * the customAssets collection in the user preferences. 114 */ 115codepops.Sandbox.prototype.run = function(code, opt_customAssetHash) { 116 // If there is no iframe to run in, let's wait just a bit. 117 if (!this.iframe_ || !this.iframe_.contentWindow || 118 !this.iframe_.contentWindow.run) { 119 setTimeout(codepops.bind(function() { 120 this.run(code, opt_customAssetHash); 121 }, this), 200); 122 return; 123 } 124 125 // IE is *really slow* to load iframes, so once we have a running iframe, just 126 // use that one and return. 127 if (this.isIE_ && this.currentlyRunningIFrame_) { 128 this.currentlyRunningIFrame_.contentWindow.setCollabPath(this.collabPath_); 129 this.currentlyRunningIFrame_.contentWindow.run(code || '', 130 opt_customAssetHash); 131 return; 132 } 133 134 // If we have a currently running app, destroy that iframe. (Unless the 135 // user has IE.) 136 if (!this.isIE_ && this.currentlyRunningIFrame_) { 137 this.renderDiv_.removeChild(this.currentlyRunningIFrame_); 138 } 139 140 // Attach the various error and event handlers to the iframe. 141 this.iframe_.contentWindow.setCollabPath(this.collabPath_); 142 this.iframe_.contentWindow.setOnErrorHandler(this.onError_); 143 this.iframe_.contentWindow.setOnSuccessHandler(this.onSuccess_); 144 this.iframe_.contentWindow.setOnAssetLoadErrorHandler(this.onAssetLoadError_); 145 146 var globalErrorHandler = function() {}; 147 if (this.onError_) { 148 globalErrorHandler = this.onError_; 149 } 150 151 this.iframe_.contentWindow.onerror = function(message, url, lineNumber) { 152 globalErrorHandler(message); 153 }; 154 155 var canvas = this.iframe_.contentWindow.getCanvas(); 156 canvas.addEventListener('mousemove', this.onMouseMove_); 157 canvas.addEventListener('mouseover', this.onMouseOver_); 158 canvas.addEventListener('mouseout', this.onMouseOut_); 159 canvas.addEventListener('touchstart', this.onTouchStart_); 160 canvas.addEventListener('touchmove', this.onTouchMove_); 161 162 // Now run the code and record this iframe as the currently running one. 163 this.iframe_.contentWindow.run(code || '', opt_customAssetHash); 164 this.iframe_.style.visibility = 'visible'; 165 this.currentlyRunningIFrame_ = this.iframe_; 166 167 // Create a new iframe so we're ready to run within it next time around. 168 // But only do this if we're in a real browser. If it's IE, we never 169 // destroy and create new iframes. 170 if (!this.isIE_) { 171 this.iframe_ = this.document_.createElement('iframe'); 172 this.iframe_.style.visibility = 'hidden'; 173 this.iframe_.title = 'Sandbox iframe'; 174 this.iframe_.src = '/sandbox.html'; 175 this.renderDiv_.appendChild(this.iframe_); 176 } 177}; 178 179 180/** 181 * Attaches an event handler for mouse move. 182 * @param {Function} handler Function to be called in order to handle 183 * mouse move events. 184 */ 185codepops.Sandbox.prototype.setOnMouseMoveHandler = function(handler) { 186 this.onMouseMove_ = handler; 187}; 188 189 190/** 191 * Attaches an event handler for mouse out. 192 * @param {Function} handler Function to be called in order to handle 193 * mouse out events. 194 */ 195codepops.Sandbox.prototype.setOnMouseOutHandler = function(handler) { 196 this.onMouseOut_ = handler; 197}; 198 199 200/** 201 * Attaches an event handler for mouse over. 202 * @param {Function} handler Function to be called in order to handle 203 * mouse over events. 204 */ 205codepops.Sandbox.prototype.setOnMouseOverHandler = function(handler) { 206 this.onMouseOver_ = handler; 207}; 208 209 210/** 211 * Attaches an event handler for touch move. 212 * @param {Function} handler Function to be called in order to handle 213 * touch move events. 214 */ 215codepops.Sandbox.prototype.setOnTouchMoveHandler = function(handler) { 216 this.onTouchMove_ = handler; 217}; 218 219 220/** 221 * Attaches an event handler for touch start. 222 * @param {Function} handler Function to be called in order to handle 223 * touch start events. 224 */ 225codepops.Sandbox.prototype.setOnTouchStartHandler = function(handler) { 226 this.onTouchStart_ = handler; 227}; 228 229 230/** 231 * Attaches a javascript error handler for the sandbox. 232 * @param {Object} onError Function to be called if there is a javascript 233 * error of any kind. 234 */ 235codepops.Sandbox.prototype.setOnErrorHandler = function(onError) { 236 this.onError_ = onError; 237}; 238 239 240/** 241 * Attaches a javascript "run success" handler for the sandbox. 242 * @param {Object} onSuccess Function to be called if there is a javascript 243 * run without errors. 244 */ 245codepops.Sandbox.prototype.setOnSuccessHandler = function(onSuccess) { 246 this.onSuccess_ = onSuccess; 247}; 248 249 250/** 251 * Attaches an asset load failure handler for the sandbox. 252 * @param {Object} onAssetLoadError Function to be called if there is failure 253 * when the system loads an asset. 254 */ 255codepops.Sandbox.prototype.setOnAssetLoadErrorHandler = 256 function(onAssetLoadError) { 257 258 this.onAssetLoadError_ = onAssetLoadError; 259}; 260 261 262/** 263 * Make all sounds stop. 264 */ 265codepops.Sandbox.prototype.silence = function() { 266 if (this.iframe_ && this.iframe_.contentWindow.silence) { 267 this.iframe_.contentWindow.silence(); 268 } 269 if (this.currentlyRunningIFrame_ && 270 this.currentlyRunningIFrame_.contentWindow.silence) { 271 this.currentlyRunningIFrame_.contentWindow.silence(); 272 } 273}; 274 275 276/** 277 * Reset the library and clear all error handlers and intervals. 278 * This stops any animations. 279 */ 280codepops.Sandbox.prototype.resetLibrary = function() { 281 if (this.isIE_) { 282 if (this.currentlyRunningIFrame_ && 283 this.currentlyRunningIFrame_.contentWindow.resetLibrary) { 284 this.currentlyRunningIFrame_.contentWindow.resetLibrary(); 285 } 286 } else { 287 // By running a neutral bit of javascript, the sandbox will 288 // wipe out the currently running iframe, which clears everything. 289 this.run('reset()'); 290 } 291}; 292 293 294/** 295 * Clear out any loop functions that may be running. 296 * This stops any loops. 297 */ 298codepops.Sandbox.prototype.resetLoop = function() { 299 if (this.currentlyRunningIFrame_ && 300 this.currentlyRunningIFrame_.contentWindow && 301 this.currentlyRunningIFrame_.contentWindow.loop) { 302 this.currentlyRunningIFrame_.contentWindow.loop = function() {}; 303 } 304}; 305 306 307 308/** 309 * Get a reference to the currently running library object, or null 310 * is nothing is running. 311 * @return {?Library} Either the library, or null. 312 */ 313codepops.Sandbox.prototype.getRunningLibrary = function() { 314 if (this.currentlyRunningIFrame_ && 315 this.currentlyRunningIFrame_.contentWindow && 316 this.currentlyRunningIFrame_.contentWindow.library) { 317 return this.currentlyRunningIFrame_.contentWindow.library; 318 } 319 return null; 320}; 321 322 323/** 324 * Returns a string data URL representing the canvas at its native resolution. 325 * @param {string} type Optional DOMString indicating the image format. The 326 * default type is 'image/png'. 327 * @param {number} encoderOptions A Number between 0 and 1 indicating image 328 * quality if the requested type is image/jpeg or image/webp. 329 * @return {string} The URL. 330 */ 331codepops.Sandbox.prototype.toDataURL = function(type, encoderOptions) { 332 if (this.currentlyRunningIFrame_ && 333 this.currentlyRunningIFrame_.contentWindow && 334 this.currentlyRunningIFrame_.contentWindow.library) { 335 return this.currentlyRunningIFrame_.contentWindow.library.toDataURL( 336 type, encoderOptions); 337 } 338 return null; 339}; 340 341 342/** 343 * Tells the sandbox what datastore path should be used for exch
343anging set/get 344 * calls between multiple users of the same app. 345 * @param {string} path The datastore path. 346 */ 347codepops.Sandbox.prototype.setCollabPath = function(path) { 348 this.collabPath_ = path; 349};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.