Skip to content

Commit

Permalink
Implementing Web Console
Browse files Browse the repository at this point in the history
  • Loading branch information
richaseh committed Jun 27, 2015
1 parent 84b3a37 commit 7cc10bc
Show file tree
Hide file tree
Showing 6 changed files with 380 additions and 0 deletions.
37 changes: 37 additions & 0 deletions data/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
textarea {
width: 100%;
margin: 10px auto 10px;
height: 100px;
border-color: black;
}
.btn.grid {
width: 20%;
margin: 50px;
background-color: lightgrey;
border: 2px solid;
border-radius: 2px;
border-color: black;
cursor: pointer;
font-size: 25px;
}
.container {
background: lightgrey;
}
.grid {
float: left;
padding: 10px 50px;
width: 50%;
overflow: hidden;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.output {
border-left: 1px solid darkgrey;
overflow: hidden;
}
.output iframe {
border-color: black;
width: 100%;
height: 380px;
}
145 changes: 145 additions & 0 deletions data/web-console.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
<!doctype html>
<html>
<head>
<title></title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container grid">
<form>
<h3>HTML</h3>
<textarea id="html"></textarea>
<h3>CSS</h3>
<textarea id="css"></textarea>
<h3>JavaScript</h3>
<textarea id="js"></textarea>
</form>
</div>
<div class="output grid">
<h2>Output:</h2>
<iframe id="iframe"></iframe>
</div>
<div class="btn grid" id="button"><b>Run</b></div>
<div id="internal-use-trigger-save-text" style="visibility:hidden;"></div>
<textarea id="internal-use-saved-text" style="visibility:hidden;"></textarea>
<script>
(function() {
// Base template of the iframe.
var baseTpl =
"<!doctype html>\n" +
"<html>\n\t" +
"<head>\n\t\t" +
"</head>\n\t" +
"<body>\n\t\n\t" +
"<h3>Console:</h3>\n" +
"<div style=\"padding: 0 5x; margin-bottom: 10px; border: 1px solid black; width: 100%; height: 100px\" id=\"console\"></div>\n" +
"</body>\n" +
"</html>";

var baseTplNoConsole =
"<!doctype html>\n" +
"<html>\n\t" +
"<head>\n\t\t" +
"</head>\n\t" +
"<body>" +
"</body>\n" +
"</html>";

var consoleTpl =
"var console = {};\n" +
"console.log = function(s) {\n" +
"var para = document.createElement(\"p\");\n" +
"para.style.marginTop = \"5px\";\n" +
"para.style.marginBottom = \"5px\";\n" +
"var node = document.createTextNode(s);\n" +
"para.appendChild(node);\n" +
"var consoleDiv = document.getElementById('console');\n" +
"consoleDiv.appendChild(para);\n" +
"};\n" +
"console.error = function(s) {\n" +
"var para = document.createElement(\"p\");\n" +
"para.style.color = 'red';\n" +
"para.style.marginTop = \"5px\";\n" +
"para.style.marginBottom = \"5px\";\n" +
"var node = document.createTextNode(s);\n" +
"para.appendChild(node);\n" +
"var consoleDiv = document.getElementById('console');\n" +
"consoleDiv.appendChild(para);\n" +
"};";

// This function fills the base content of the iframe with that
// input by the user.
var fillTemplateContent = function(addConsole) {
// Get the Input elements.
var htmlInput = document.querySelector('#html'),
cssInput = document.querySelector('#css'),
jsInput = document.querySelector('#js');

// Get the input values.
var html = htmlInput.value,
css = cssInput.value,
js = jsInput.value,
content = '';

// HTML
if (addConsole) {
content = baseTpl.replace('</body>', html + '</body>');
} else {
content = baseTplNoConsole.replace('</body>', html + '</body>');
}

// CSS
css = '<style>' + css + '</style>';
content = content.replace('<head>', '<head>' + css);

// Javascript
// We escape the closing script tag so that the browser parsing is
// not messed up.
js = '<script>' + js + '<\/script>';
content = content.replace('<head>', '<head>' + js);

// Console
if (addConsole) {
var consoleDiv = '<script>' + consoleTpl + '<\/script>';
content = content.replace('<head>', '<head>' + consoleDiv);
}

return content;
};

// This renders the content in the iframe.
var render = function() {
var iframeContent = fillTemplateContent(true);

var iframe = document.querySelector('#iframe'),
iframeDocument = iframe.contentDocument;

// Note: write() in general is not recommended to use.
// This is OK for quick prototype, but should not be used in
// production code.
iframeDocument.open();
iframeDocument.write(iframeContent);
iframeDocument.close();
};

// Add a click event listener to the saveTextBtn button.
var saveTextBtn = document.querySelector('#internal-use-trigger-save-text');
saveTextBtn.addEventListener('click', function() {
var savedText = fillTemplateContent(false);
var savedTextDiv = document.querySelector('#internal-use-saved-text');
savedTextDiv.value = savedText;
console.log(savedText);
});

// Add a click event listener to the Run button.
var button = document.querySelector('#button');
button.addEventListener('click', function() {
render() ;
});

// We initially call render so that Console comes.
render();
}());
</script>
</body>
</html>
10 changes: 10 additions & 0 deletions icons/go_webconsole.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions webactivity.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ def _seed_xs_cookie(cookie_jar):
from webtoolbar import PrimaryToolbar
from edittoolbar import EditToolbar
from viewtoolbar import ViewToolbar
from webconsole import WebConsole
import downloadmanager

# TODO: make the registration clearer SL #3087
Expand Down Expand Up @@ -184,6 +185,12 @@ def __init__(self, handle):

self._primary_toolbar.connect('reset-home', self._reset_home_button_cb)

self._primary_toolbar.connect('go-webconsole', self._go_webconsole_button_cb)

self._primary_toolbar.connect('save-file-webconsole', self._save_file_webconsole_button_cb)

self._primary_toolbar.connect('open-file-webconsole', self._open_file_webconsole_button_cb)

self._edit_toolbar_button = ToolbarButton(
page=self._edit_toolbar, icon_name='toolbar-edit')

Expand Down Expand Up @@ -238,6 +245,8 @@ def __init__(self, handle):
else:
_logger.debug('Created activity')

self._web_console = WebConsole(self)

# README: this is a workaround to remove old temp file
# http://bugs.sugarlabs.org/ticket/3973
self._cleanup_temp_files()
Expand Down Expand Up @@ -470,6 +479,15 @@ def _link_add_button_cb(self, button):
def _go_home_button_cb(self, button):
self._tabbed_view.load_homepage()

def _go_webconsole_button_cb(self, button):
self._web_console.open_new_tab()

def _save_file_webconsole_button_cb(self, button):
self._web_console.save_file()

def _open_file_webconsole_button_cb(self, button):
self._web_console.open_file()

def _go_library_button_cb(self, button):
self._tabbed_view.load_homepage(ignore_gconf=True)

Expand Down
137 changes: 137 additions & 0 deletions webconsole.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Copyright (C) 2015, Richa Sehgal
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

from filepicker import FilePicker
import os
import tempfile

from sugar3.activity import activity
from sugar3.datastore import datastore

class WebConsole():
def __init__(self, act):
self._activity = act

src_path = os.path.join(activity.get_bundle_path(),
"data/web-console.html")
self._src_uri = "file://" + src_path

self._storage_dir = os.path.join(act.get_activity_root(),
"Web_Console_Files")

def _get_file_text(self):
browser = self._activity._tabbed_view.props.current_browser
frame = browser.get_main_frame()

original_title = frame.get_title()
if original_title is None:
original_title = ""
text_script = \
"var saveTextBtn = document.getElementById('internal-use-trigger-save-text');" \
"saveTextBtn.click();" \
"var savedTextDiv = document.querySelector('#internal-use-saved-text');" \
"var text = savedTextDiv.value; " \
"document.title = text;"
browser.execute_script(text_script)
file_text = frame.get_title()

reset_title_script = "document.title = '" + original_title + "';"
browser.execute_script(reset_title_script)

return file_text

def _add_to_journal(self, title, file_path):
jobject = datastore.create()

jobject.metadata['title'] = title
jobject.metadata['description'] = "Saved from web console"

jobject.metadata['mime_type'] = "text/html"
jobject.file_path = file_path
datastore.write(jobject)


def open_new_tab(self):
browser = self._activity._tabbed_view.props.current_browser
browser.open_new_tab(self._src_uri)

def save_file(self):
browser = self._activity._tabbed_view.props.current_browser
if browser.get_uri() != self._src_uri:
self._activity._alert("It looks like the Web Console is not open." +
"You can only Save a file from Web Console")
return
file_text = self._get_file_text()
if not os.path.exists(self._storage_dir):
os.makedirs(self._storage_dir)
fd, dest_path = tempfile.mkstemp(dir=self._storage_dir)
# Write to file
os.write(fd, file_text)
os.close(fd)

self._add_to_journal(dest_path, dest_path)

def _get_javascript_input(self, data):
start = data.find("<script>")
end = data.find("</script>")
if start > -1 and end > -1 and start < end:
return data[start + 8 : end]
return ""

def _get_css_input(self, data):
start = data.find("<style>")
end = data.find("</style>")
if start > -1 and end > -1 and start < end:
return data[start + 7 : end]
return ""

def _get_html_input(self, data):
start = data.find("<body>")
end = data.find("</body>")
if start > -1 and end > -1 and start < end:
return data[start + 6 : end]
return ""

def open_file(self):
browser = self._activity._tabbed_view.props.current_browser
if browser.get_uri() != self._src_uri:
self._activity._alert("It looks like the Web Console is not open." +
"You can only Open a file from Web Console")
return
picker = FilePicker(self._activity)
chosen = picker.run()
picker.destroy()
f = open(chosen, 'r')
data = f.read()

js = self._get_javascript_input(data).replace("'", "\\\'")
css = self._get_css_input(data).replace("'", "\\\'")
html = self._get_html_input(data).replace("'", "\\\'")

fill_js_script = \
"var div = document.getElementById('js');" \
"div.value = '" + js + "';"
browser.execute_script(fill_js_script)

fill_css_script = \
"var div = document.getElementById('css');" \
"div.value = '" + css + "';"
browser.execute_script(fill_css_script)

fill_html_script = \
"var div = document.getElementById('html');" \
"div.value = '" + html + "';"
browser.execute_script(fill_html_script)
Loading

0 comments on commit 7cc10bc

Please sign in to comment.