Skip to content

Instantly share code, notes, and snippets.

@samwan
Created April 23, 2009 06:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save samwan/100358 to your computer and use it in GitHub Desktop.
Save samwan/100358 to your computer and use it in GitHub Desktop.
<!--
By Samuel Wan (samuelwan.com), April 2009.
This code comes with no warrantees or guarantees. Use at your own risk.
No rights reserved.
This experiment is interesting to me as an attempt to bootstrap an HTML / Javascript file list, editor, AND renderer into a single CouchDB design file.
Instructions:
1) Put this in your _utils folder and access it over couchdb. (Sub-directory reverse proxying not supported)
E.g. http://localhost/_utils/editor2.html
2) Enter the name of an existing or non-existing database.
3) Press install to install the design document called "_design/ide". Your browser will visit the installed app automatically.
4) Create, load, edit, view, or delete web documents with either text/html or text/javascript content type.
5) View the page by loading it in the editor and clicking the "View" button.
6) If you change the editor2.html code, you can click on the "Reinstall" button to reload the editor2.html code into your couchdb design document.
-->
<html>
<head>
<style type='text/css'>
label {display:block; float:left; width:110px; text-align:right; margin-right:8px;}
#theform textarea, #theform input {display:block; width:450px;}
#theform textarea {height:60%;}
</style>
<script type='text/javascript' src='/_utils/script/json2.js'></script>
<script type='text/javascript' src='/_utils/script/jquery.js'></script>
<script type='text/javascript' src='/_utils/script/jquery.couch.js'></script>
<script type='text/javascript'>
function stringFun(fun) { // copied from couch_tests.js
var string = fun.toSource ? fun.toSource() : "(" + fun.toString() + ")";
return string;
}
function parseQuerystring() { // Convert querystring into associative array
var pairs = window.location.search.substring(1).split("&");
var kv = {};
for(var i = 0; i < pairs.length; i++) {
var kvarray = pairs[i].split("=");
kv[kvarray[0]] = kvarray[1];
}
return kv;
}
function form2json() { // Convert form into json-friendly object
var formArray = $('#theform').serializeArray();
var formObject = {};
for(var i = 0; i < formArray.length; i++) {
formObject[formArray[i].name] = formArray[i].value;
}
return formObject;
}
function trim(str) { // http://blog.stevenlevithan.com/archives/faster-trim-javascript
return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
}
$(document).ready(function(){
var app_id = '_design/ide';
function initEditor() {
// The editor lets you create, load, update, delete, and view html or javascript documents with the property Content-Type HTTP header.
var dbname = $('#dbname').val();
var db = $.couch.db(dbname);
function refreshList() {
$('#webdocslist').children().remove(); // Clear select tag
// Load the list of html or javascript documents using the view defined in the installIDE function.
db.view(app_id.split('/')[1] + '/webdocs', {success:function(data, textStatus) {
$('#webdocslist').prepend($(document.createElement('option')).attr({selected:true, text:'-- Load Document --', value:''}));
for(var i = 0; i < data.rows.length; i++) {
var row = data.rows[i];
// Populate the select tag with options whose values are document id's.
$('#webdocslist').append($(document.createElement('option')).attr({
text:row.value.name + ' (' + row.value.contentType + ')',
value:row.id,
}));
}
}});
}
function load(id) {
id = trim(id);
if(id.length == 0) return; // Don't load empty id
$('#submitbtn').val('Update'); // Change button label from "Save" to "Update"
$('#deletebtn, #viewbtn').attr('disabled', false);
db.openDoc(id, {success:function(data, status){
// After opening the document, send matching properties to form element values.
for(var n in data) {
$('#theform input[name=' + n + '], #theform textarea[name=' + n + ']').val(data[n]);
}
}});
}
function savePage() {
var data = form2json();
if(trim(data['_id']).length == 0) { // don't try to update existing doc when _id not specified (new vs update)
delete data['_id'];
delete data['_rev'];
}
db.saveDoc(data, {success:function(data, status){
load(data.id);
refreshList(); // refresh document list after saving new or updating document.
}});
}
function deleteDoc() {
var data = form2json();
$('#theform')[0].reset();
if(data._id.length > 0) {
db.removeDoc(data, {success:function(data, status){
refreshList();
}});
}
}
$('#newbtn').click(function(){
$('#theform')[0].reset();
$('#submitbtn').val('Save');
$('#deletebtn, #viewbtn').attr('disabled', true);
var options = $('#webdocslist').children()[0].selected = true;
});
$('#submitbtn').click(savePage);
$('#reinstallbtn').click(function() {document.location.href = 'http://' + $('#reinstallpath').val()});
$('#deletebtn').click(deleteDoc);
$('#webdocslist').change(function() { load($(this).val()) });
$('#viewbtn').click(function(){ // If a document was loaded, view it in browser by using the design doc's _show/render function.
var id = $('#webdocslist').val();
if(id.length > 0) {
window.open('http://' + document.location.host + '/' + dbname + '/' + app_id + '/_show/render/' + id);
}
})
refreshList();
$('#newbtn').click();
} // end initEditor
function initInstaller() {
var dbname, db;
// Create a new document (a JSON object) containing this page's html as well as _show and _view members.
function installIDE(_rev) {
$('#installer').remove(); // Remove the installer UI before serializing the webpage's html into the json document object.
$('#editor, #preview').show(); // Show the editor before serializing
// Store the dbname and auto-reinstall URL as invisible INPUT elements
$('#editor').append($("<input id='dbname' type='hidden' value='" + dbname + "'/>", document));
$('#editor').append($("<input id='reinstallpath' type='hidden' value='" + document.location.host + document.location.pathname + "?autoinstall=" + dbname + "'/>", document));
// Serialize the page to prepare for the cool part.
var html = '<html>' + $('head').html() + $('body').html() + '</html>';
// Construct document JSON object. This is the cool part.
var doc = {
_id: app_id,
contentType: 'text/html',
createdAt: (new Date()).toString(),
shows: {
render:stringFun(function(doc, req) {
return {
body:doc.body,
headers: {
'Content-Type':doc.contentType
}
}
}),
},
views: {
webdocs: {
map:stringFun(function(doc) {if(doc.contentType == 'text/html' || doc.contentType == 'text/javascript') emit(null, {name:doc.name, contentType:doc.contentType})})
}
},
body: html,
}
if(_rev) doc._rev = _rev; // If you're reinstalling the design document instead of creating a new one in the database
db.saveDoc(doc, {success: function(data, status) {
// After installing, navigate browser to the URI for using the design doc to render itself
document.location.href = (db.uri + encodeURI(app_id) + '/_show/render/' + encodeURIComponent(data.id));
}});
}
// Install button will install the design document into a new or existing database
$('#installbtn').click(function(){
dbname = trim($('#installer_dbname').val());
if(dbname.length == 0) return; // Don't install if db not specified
db = $.couch.db(dbname);
db.info({ // Check database
error: function(data, status) { db.create({success:function(data, status) {installIDE()}}) }, // Database not found, install db & ide
success: function(data, status) { // Database found, check IDE
db.openDoc(app_id, {
error: function(data, status) {installIDE();}, // IDE not found, install new IDE
success: function(data, status) {installIDE(data._rev)}, // IDE found, update IDE
});
},
})
})
// Check for autoinstall in querystring, if user just pressed the "Reinstall" button to reload a modified editor2.html
var autoinstall = parseQuerystring()['autoinstall'];
if(autoinstall) { $('#installer_dbname').val(autoinstall); $('#installbtn').click(); }
} // end initInstaller
// Either initialize the installation or editing mode.
if($('#installer').length > 0) {
initInstaller();
} else {
initEditor();
}
});
</script>
</head>
<body>
<!-- The installer div gets removed before serializing into the _design/ide document -->
<div id='installer'>
<label>Database: </label><input type='text' id='installer_dbname'/><input type='button' id='installbtn' value='Install'/>
</div><!-- end installer -->
<div id='editor' style='display:none;'>
<form id='theform'>
<label>Name</label><input type='text' name='name' value='No name'/>
<label>Body</label><textarea id='body' name='body'>
<html>
<head>
<title>helloworld</title>
</head>
<body>
<b>Hello</b><i>World</i>
</body>
</html>
</textarea>
<label>Content Type</label><input type='text' name='contentType' value='text/html'/>
<label>_id</label><input type='text' name='_id' />
<label>_rev</label><input type='text' name='_rev' />
</form>
<label>Menu</label>
<input type='button' id='newbtn' value='New'/>
<select id='webdocslist' style='margin-right:4px'/>&nbsp;&nbsp;
<input type='button' id='submitbtn' value='Create'/>
<input type='button' id='deletebtn' value='Delete'/>
<input type='button' id='viewbtn' value='View'/>
<input type='button' id='reinstallbtn' value='Reinstall'/>
</div><!-- end editor -->
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment