/* * Copyright 2009, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @fileoverview This file is the master javascript file for the interactive * editor/runner for all the simple samples. * */ // For browserFun // TODO: // Make it so that you can make the code editing window wider... // Make it so that you can click links to the documentation of each object // Add Ping Pong // Fix the spacing in the editor (when u hit tab) // Try to see if for the javascript part you can eval line by line so that you // can output the line number var fileTypes = { 'js' : 'javascript', 'html' : 'html', 'php' : 'php' }; function InteractiveSample() { this.categories = []; this.codeTitles = []; this.adjustCodeBoxAmount = 50; this.selectCode; this.codeDiv; this.codeLIs = []; this.currentCode = new Object(); this.curI = ''; this.cleanWindowObj; // Assume we're offline until we know that file reading doesn't work this.online = false; } function sortByCategory(a, b) { return a.category > b.category; } function sortByName(a, b) { return a.sampleName > b.sampleName; } function nameToHashName(name) { var hashName = name.toLowerCase(); hashName = hashName.replace(/ /g, '_'); return hashName; } InteractiveSample.prototype.init = function(codeDiv) { this.codeDiv = codeDiv; this.createCategories(); this.addShowHideClicks(); // TODO: Sort samples // codeArray.sort(sortByCategory); // // for (var i=0; i < codeArray.length; i++) { // codeArray[i].samples.sort(sortByName); // } // Gotta set this for the iFrame to know it and change it // This is specific to O3D because a transparent overlay that is // under the tag will actually hide the tag // unless we muck with the zIndex after the loads window.iframeZindex = '4999'; // This is so that we can restore the first level of the window object // after we run a sample... so samples can get run a lot and not clutter // the window object. this.cleanWindowObj = singleLevelKeyCopy(window); }; InteractiveSample.prototype.createCategories = function() { // codeArray is from interactive_samples.js this.selectCode = _gel('selectCode'); for (var i = 0; i < codeArray.length; i++) { var container = _cel('span'); container.className = 'category'; var catName = _cel('span'); var img = _cel('img'); img.className = 'collapse'; img.src = 'interactive_sampler_assets/images/cleardot.gif'; // addEvent(img, 'click', this.toggleExpand(img), false); catName.appendChild(img); catName.innerHTML += codeArray[i].category; container.appendChild(catName); var ul = _cel('ul'); ul.className = 'categoryItems'; container.appendChild(ul); for (var j = 0; j < codeArray[i].samples.length; j++) { var item = codeArray[i].samples[j]; var li = _cel('li'); li.innerHTML = item.sampleName; this.codeTitles.push(li); var files = [] for (var index = 0; index < codeArray[i].samples[j].files.length; ++index) { var dirName = ''; var baseName = codeArray[i].samples[j].files[index]; var slashIndex = baseName.lastIndexOf('/'); if (slashIndex >= 0) { dirName = baseName.substring(0, slashIndex + 1); baseName = baseName.substring(slashIndex + 1); } files[index] = dirName + 'sampler_' + baseName; } //var files = codeArray[i].samples[j].files; addEvent(li, 'click', this.showSample(this, item, files, li)); if (i == 0 && j == 0) { this.showSample(this, item, files, li, true)(); } if (window.location.hash.length > 0) { var hashName = nameToHashName(item.sampleName); if (window.location.hash.substring(1) == hashName) { this.showSample(this, item, files, li)(); } } this.codeLIs.push(li); ul.appendChild(li); } this.selectCode.appendChild(container); this.categories.push(container); } }; InteractiveSample.prototype.toggleShowHide = function(category, interactiveSample) { return function() { var li = category.nextSibling; var el = category.childNodes[0]; if (el.className == 'expand') el.className = 'collapse'; else el.className = 'expand'; if (li.style.display == 'none') { li.style.display = 'block'; } else { li.style.display = 'none'; } }; }; InteractiveSample.prototype.addShowHideClicks = function() { for (var i = 0; i < this.categories.length; i++) { categoryName = this.categories[i].childNodes[0]; addEvent(categoryName, 'click', this.toggleShowHide(categoryName, this)); } }; InteractiveSample.prototype.loadLocally = function(relativeUrl, filename, fileType, opt_changeCodeMirror) { // readFile is in utils.js var data = readFile(relativeUrl); if (data == null) { this.online = true; return false; } if (fileType == 'javascript') { data = this.replaceV8Call(data); } if (opt_changeCodeMirror == true) { this.changeCodeMirror(data, fileType); } is_instance.currentCode[filename] = { code : data }; console.log(relativeUrl + ': loaded locally.'); return true; }; InteractiveSample.prototype.loadRemotely = function(relativeUrl, filename, fileType, opt_changeCodeMirror) { is_instance = this; downloadUrl(relativeUrl, function(data, status) { if (fileType == 'javascript') { data = this.replaceV8Call(data); } if (opt_changeCodeMirror == true) { is_instance.changeCodeMirror(data, fileType); } is_instance.currentCode[filename] = { code : data }; }); }; InteractiveSample.prototype.loadCode = function(filename, opt_changeCodeMirror) { // If the code is in the currentCode buffer, then grab it there // otherwise, load it via XHR // If opt_changeCodeMirror is specified, load it into the window // Get filetype var filenameSplit = filename.split('.'); var extension = filenameSplit[filenameSplit.length - 1]; var fileType = fileTypes[extension.toLowerCase()]; var inBuffer = (this.currentCode[filename] && this.currentCode[filename].code) ? true : false; if (inBuffer && opt_changeCodeMirror == true) { this.changeCodeMirror(this.currentCode[filename].code, fileType); } else { var relativeUrl = filename; is_instance = this; if (!this.online) { this.loadLocally(relativeUrl, filename, fileType, opt_changeCodeMirror); } if (this.online) { this.loadRemotely(relativeUrl, filename, fileType, opt_changeCodeMirror); } } }; // TODO: can is_instance just be set as is_instance = this above return // function() InteractiveSample.prototype.showSample = function(is_instance, codeItem, files, thisLI, def) { return function() { is_instance.refreshInlinePlugin(); var codeDiv = is_instance.codeDiv; var codeLIs = is_instance.codeLIs; for (var i = 0; i < codeLIs.length; i++) { codeLIs[i].className = ''; } // turn off the "Run Here" button if this sample should be run in its own // window. var runHere = document.getElementById('runHere'); runHere.style.visibility = codeItem.ownWindow ? 'hidden' : 'visible'; // For linking purposes if (!def) { window.location.hash = nameToHashName(thisLI.innerHTML); } // Make code selected designate this as selected thisLI.className = 'selected'; is_instance.currentCode = new Object(); // add file names at top var tab_bar = _gel('tab_bar'); tab_bar.innerHTML = ''; // prototype syntax files.each(function(file, index) { var tabClass = 'lb'; if (index == 0) { tabClass = 'db'; is_instance.loadCode(file, true); } else { is_instance.loadCode(file, false); } var containerDiv = _cel('div'); containerDiv.className = 'roundedcornr_box'; addEvent(containerDiv, 'click', is_instance.changeTab(file, is_instance)); var html = '
'; html += '
'; html += file; html += '
'; containerDiv.innerHTML = html; tab_bar.appendChild(containerDiv); }); // is_instance.loadCode(files[0], textArea); is_instance.curI = files[0]; }; }; InteractiveSample.prototype.changeTab = function(i, is_instance) { return function() { var siblings = this.parentNode.childNodes; is_instance.currentCode[is_instance.curI].code = is_instance.getCode(); // Swap the colors of the tabs for (var z = 0; z < siblings.length; z++) { if (siblings[z].childNodes[1].innerHTML == i) { siblings[z].childNodes[0].className = 'db_top'; siblings[z].childNodes[1].className = 'db_roundedcornr_content'; } else { siblings[z].childNodes[0].className = 'lb_top'; siblings[z].childNodes[1].className = 'lb_roundedcornr_content'; } } is_instance.loadCode(i, true); is_instance.curI = i; }; }; InteractiveSample.prototype.increaseCodeBoxHeight = function() { var curHeight = this.textArea.style.height; curHeight = curHeight.substr(0, curHeight.indexOf('px')); var newHeight = parseInt(curHeight) + this.adjustCodeBoxAmount; newHeight += 'px'; this.textArea.style.height = newHeight; }; InteractiveSample.prototype.decreaseCodeBoxHeight = function() { var curHeight = this.textArea.style.height; curHeight = curHeight.substr(0, curHeight.indexOf('px')); var newHeight = parseInt(curHeight) - this.adjustCodeBoxAmount; newHeight += 'px'; this.textArea.style.height = newHeight; }; InteractiveSample.prototype.replaceV8Call = function(code) { return code.replace( /(o3djs.util.setMainEngine[^;\n]+)[;\n]/, "\n // Can't use V8 in the interactive sampler yet.\n //$1"); } InteractiveSample.prototype.prepareAllCodeRun = function() { // TODO: Change this so it doesn't rely on the first file being HTML // TODO: Change this to use REGEX to replace this.deleteOldWindowStuff(); this.refreshInlinePlugin(); this.currentCode[this.curI].code = this.getCode(); var html = ''; for (var i in this.currentCode) { if (i.indexOf('.html') != -1) html = this.currentCode[i].code; } var pattern = /', scriptSrcEnd) + 9; var found = false; for (var z in this.currentCode) { if (z == script) { var data = this.replaceV8Call(this.currentCode[z].code); script = ''; found = true; break; } } if (found) { html = html.substring(0, result.index) + script + html.substring(endScriptLoc); } } // console.log(html); window.codeToRun = html; // console.log(html.slice(scriptLoc, endScriptLoc)); // console.log(html); }; InteractiveSample.prototype.refreshInlinePlugin = function() { var o3dobject = _gel('o3d'); o3dparent = o3dobject.parentNode; o3dparent.removeChild(o3dobject); var nO3D = _cel('div'); nO3D.id = 'o3d'; nO3D.style.width = '300px'; nO3D.style.height = '300px'; nO3D.style.backgroundColor = 'gray'; o3dparent.appendChild(nO3D); }; InteractiveSample.prototype.runJS = function() { // TODO don't assume that we run javascript in any order. Make it so that // it checks the HTML for which order JS goes in this.refreshInlinePlugin(); // console.log(this.currentCode[0].code.split('\n')); window.is.startJS(); }; InteractiveSample.prototype.deleteOldWindowStuff = function() { for (var i in window) { if (typeof this.cleanWindowObj[i] == 'boolean' && this.cleanWindowObj[i] == true) { } else { window[i] = null; // Delete doesn't remove properties created with var. delete window[i]; // But just in case we can, we do. } } }; InteractiveSample.prototype.startJS = function() { this.deleteOldWindowStuff(); var topHolder = _gel('HTMLforInlineJavascript'); topHolder.innerHTML = ""; var scriptHolder = document.createElement('div'); topHolder.appendChild(scriptHolder); var htmlHolder = document.createElement('div'); topHolder.appendChild(htmlHolder); // NOTE: These are ordered such that no file depends on anything after it. var forced_includes = [ "o3djs/base.js", "o3djs/error.js", "o3djs/dump.js", "o3djs/math.js", "o3djs/serialization.js", "o3djs/element.js", "o3djs/shape.js", "o3djs/rendergraph.js", "o3djs/test.js", "o3djs/particles.js", "o3djs/primitives.js", "o3djs/quaternions.js", "o3djs/arcball.js", "o3djs/event.js", "o3djs/debug.js", "o3djs/picking.js", "o3djs/io.js", "o3djs/util.js", "o3djs/effect.js", "o3djs/material.js", "o3djs/camera.js", "o3djs/canvas.js", "o3djs/pack.js", "o3djs/scene.js", "o3djs/loader.js", "o3djs/simple.js", "o3djs/fps.js" // If fps isn't last on this list, update the check below. ]; for (var i = 0; i < forced_includes.length; ++i) { var scr = document.createElement('script'); scr.type = "text/javascript"; scr.src = forced_includes[i]; scriptHolder.appendChild(scr); } this.currentCode[this.curI].code = this.getCode(); var sampleJS; var sampleHTML; for (var i in this.currentCode) { if (i.indexOf('.html') != -1) { // Change the name and id of the object tag before storing the HTML, // otherwise it will override the inline object we actually want the demo // to be run in. sampleHTML = this.currentCode[i].code; sampleHTML = sampleHTML.replace('id="o3d"', 'id="notouchy"'); sampleHTML = sampleHTML.replace('id=\'o3d\'', 'id="notouchy"'); sampleHTML = sampleHTML.replace('name="o3d"', 'name="notouchy"'); sampleHTML = sampleHTML.replace('name=\'o3d\'', 'name="notouchy"'); } if (i.indexOf('.js') != -1) { sampleJS = this.currentCode[i].code; } } var intervalId; var callback = function() { // fps is the last on the forced_includes list. if (o3djs != undefined && o3djs.fps) { clearInterval(intervalId); intervalId = null; // TODO Is there any chance that the onload could get called before // we're done loading the script tag? If so, we'll need to add another // stage to the callback. htmlHolder.innerHTML = sampleHTML; var scr = document.createElement('script'); scr.type = "text/javascript"; scr.text = sampleJS; scriptHolder.appendChild(scr); try { if (typeof window.onload == 'function') { window.onload() } else if (typeof window.init == 'function') { window.init(); } else if (typeof window.initClient == 'function') { window.initClient(); } else if (typeof window.createClients == 'function') { window.createClients(); } } catch(e) { alert(e.message); } _gel('o3d').focus(); } } intervalId = setInterval(callback, 10); }; InteractiveSample.prototype.changeCodeMirror = function(content, lang) { if (lang == 'javascript') { window.jsEditor.setCode(content); window.jsEditor.frame.style.display = 'inline'; window.htmlEditor.frame.style.display = 'none'; } else if (lang == 'html') { window.htmlEditor.setCode(content); window.htmlEditor.frame.style.display = 'inline'; window.jsEditor.frame.style.display = 'none'; } }; InteractiveSample.prototype.getCode = function() { if (window.htmlEditor.frame.style.display != 'none') { return window.htmlEditor.getCode(); } else if (window.jsEditor.frame.style.display != 'none') { return window.jsEditor.getCode(); } }; // Todo have the window automatically size to the size of the window InteractiveSample.prototype.increaseWidth = function() { var container = document.getElementById('container'); var curWidth = container.style.maxWidth = '1800px'; };