Merge branch 'master' of github.com:okfn/recline

Conflicts:
	demo/index.html
	demo/js/app.js
	src/recline.js
This commit is contained in:
rgrp 2011-11-03 18:31:00 +00:00
commit f7e5a03174
10 changed files with 4008 additions and 651 deletions

View File

@ -17,3 +17,10 @@ Designed for standalone use or as a library to integrate into your own app.
Open demo/index.html in your favourite browser.
## Minifying dependencies
npm install -g uglify
cd vendor
cat *.js | uglifyjs -o ../src/deps-min.js
note: make sure underscore.js goes in at the top of the file as a few deps currently depend on it

View File

@ -1,25 +1,21 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>CouchDB Data Explorer</title>
<meta charset="utf-8">
<title>CouchDB Data Explorer</title>
<link rel="stylesheet" href="style/reset.css" media="screen">
<link rel="stylesheet" href="style/data-table.css" media="screen">
<link rel="stylesheet" href="style/style.css" media="screen">
<!-- only using jqueryui for draggable -- a lighter solution would be nice -->
<script type="text/javascript" src="../src/deps-min.js"></script>
<!-- TODO: move Backbone in deps-min -->
<script src="../vendor/backbone/0.5.1/backbone.js"></script>
<script type="text/javascript" src="../src/util.js"></script>
<script type="text/javascript" src="../src/costco.js"></script>
<script type="text/javascript" src="../src/dataset.js"></script>
<script type="text/javascript" src="../src/recline.js"></script>
<script type="text/javascript" src="js/app.js"></script>
<link rel="stylesheet" href="style/data-table.css" media="screen">
<link rel="stylesheet" href="style/style.css" media="screen">
<script type="text/javascript" src="../src/deps-min.js"></script>
<script type="text/javascript" src="../src/backbone-webstore.js"></script>
<script type="text/javascript" src="../src/util.js"></script>
<script type="text/javascript" src="../src/costco.js"></script>
<script type="text/javascript" src="../src/recline.js"></script>
<script type="text/javascript" src="js/app.js"></script>
</head>
<body class="bod">
<div class="container">
<div class="menu-overlay" style="display: none; z-index: 101; ">&nbsp;</div>
<div class="container">
<!-- <div class="menu-overlay" style="display: none; z-index: 101; ">&nbsp;</div>
<ul class="menu">
</ul>
<div id="header">
@ -31,20 +27,21 @@
<div class="main_content">
<div class="left-panel"></div>
<div class="right-panel"></div>
</div> -->
<div class="data-table-container"></div>
</div>
<div id="notification-container">
<div id="notification">
<img src="images/small-spinner.gif" class="notification-loader"><span id="notification-message">Loading...</span>
</div>
</div>
<div id="notification-container">
<div id="notification">
<img src="images/small-spinner.gif" class="notification-loader"><span id="notification-message">Loading...</span>
</div>
</div>
<div class="dialog-overlay" style="display: none; z-index: 101; ">&nbsp;</div>
<div class="dialog ui-draggable" style="display: none; z-index: 102; top: 101px; ">
<div class="dialog-frame" style="width: 700px; visibility: visible; ">
<div class="dialog-content dialog-border"></div>
</div>
<div class="dialog-frame" style="width: 700px; visibility: visible; ">
<div class="dialog-content dialog-border"></div>
</div>
</div>
<script type='text/mustache' class="busyTemplate">
@ -165,10 +162,10 @@
<script type='text/mustache' class="signInTemplate">
<div class="dialog-header">
Sign in
</div>
<div class="dialog-body">
<div class="grid-layout layout-tight layout-full">
Sign in
</div>
<div class="dialog-body">
<div class="grid-layout layout-tight layout-full">
<form name="sign-in-form" id="sign-in-form">
<table class="form-table">
<tbody>
@ -192,81 +189,81 @@
</tbody>
</table>
</form>
</div>
</div>
<div class="dialog-footer">
<button class="okButton button">&nbsp;&nbsp;Sign in&nbsp;&nbsp;</button>
<button class="cancelButton button">Cancel</button>
</div>
</div>
</div>
<div class="dialog-footer">
<button class="okButton button">&nbsp;&nbsp;Sign in&nbsp;&nbsp;</button>
<button class="cancelButton button">Cancel</button>
</div>
</script>
<script type='text/mustache' class="transformTemplate">
<div class="dialog-header">
Recursive transform on all rows
</div>
<div class="dialog-body">
<div class="grid-layout layout-full">
<p class="info">Traverse and transform objects by visiting every node on a recursive walk using <a href="https://github.com/substack/js-traverse">js-traverse</a>.</p>
<table>
<tbody>
<tr>
<td colspan="4">
<div class="grid-layout layout-tight layout-full">
<table rows="4" cols="4">
<tbody>
<tr style="vertical-align: bottom;">
<td colspan="4">
Expression
</td>
</tr>
<tr>
<td colspan="3">
<div class="input-container">
<textarea class="expression-preview-code"></textarea>
</div>
</td>
<td class="expression-preview-parsing-status" width="150" style="vertical-align: top;">
No syntax error.
</td>
</tr>
<tr>
<td colspan="4">
<div id="expression-preview-tabs" class="refine-tabs ui-tabs ui-widget ui-widget-content ui-corner-all">
<span>Preview</span>
<div id="expression-preview-tabs-preview" class="ui-tabs-panel ui-widget-content ui-corner-bottom">
<div class="expression-preview-container" style="width: 652px; ">
</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="dialog-footer">
<button class="okButton button">&nbsp;&nbsp;Update All&nbsp;&nbsp;</button>
<button class="cancelButton button">Cancel</button>
</div>
Recursive transform on all rows
</div>
<div class="dialog-body">
<div class="grid-layout layout-full">
<p class="info">Traverse and transform objects by visiting every node on a recursive walk using <a href="https://github.com/substack/js-traverse">js-traverse</a>.</p>
<table>
<tbody>
<tr>
<td colspan="4">
<div class="grid-layout layout-tight layout-full">
<table rows="4" cols="4">
<tbody>
<tr style="vertical-align: bottom;">
<td colspan="4">
Expression
</td>
</tr>
<tr>
<td colspan="3">
<div class="input-container">
<textarea class="expression-preview-code"></textarea>
</div>
</td>
<td class="expression-preview-parsing-status" width="150" style="vertical-align: top;">
No syntax error.
</td>
</tr>
<tr>
<td colspan="4">
<div id="expression-preview-tabs" class="refine-tabs ui-tabs ui-widget ui-widget-content ui-corner-all">
<span>Preview</span>
<div id="expression-preview-tabs-preview" class="ui-tabs-panel ui-widget-content ui-corner-bottom">
<div class="expression-preview-container" style="width: 652px; ">
</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="dialog-footer">
<button class="okButton button">&nbsp;&nbsp;Update All&nbsp;&nbsp;</button>
<button class="cancelButton button">Cancel</button>
</div>
</script>
<script type='text/mustache' class="urlImportTemplate">
<div class="dialog-header">
Download and import from a URL or API
</div>
<div class="dialog-body">
<div class="grid-layout layout-full">
<p class="info">
Currently only <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a>-enabled APIs are supported, for example:
</p>
<p class="info">
<code>https://api.github.com/repos/maxogden/recline/commits</code>
</p>
Download and import from a URL or API
</div>
<div class="dialog-body">
<div class="grid-layout layout-full">
<p class="info">
Currently only <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a>-enabled APIs are supported, for example:
</p>
<p class="info">
<code>https://api.github.com/repos/maxogden/recline/commits</code>
</p>
<form name="api-import-form" id="sign-in-form">
<table class="form-table">
<tbody>
@ -282,105 +279,105 @@
</tbody>
</table>
</form>
</div>
</div>
<div class="dialog-footer">
<button class="okButton button">&nbsp;&nbsp;Fetch&nbsp;&nbsp;</button>
<button class="cancelButton button">Cancel</button>
</div>
</div>
</div>
<div class="dialog-footer">
<button class="okButton button">&nbsp;&nbsp;Fetch&nbsp;&nbsp;</button>
<button class="cancelButton button">Cancel</button>
</div>
</script>
<script type='text/mustache' class="pasteImportTemplate">
<div class="dialog-header">
Import raw copy & pasted JSON
</div>
<div class="dialog-body">
<div class="grid-layout layout-tight layout-full">
<p class="info">
Paste in an array of JSON objects representing the documents that you would like to insert into the database.
</p>
<p class="info">
<code>[{"woo": "pizza"}, {"tasty": "muffins"}]</code>
</p>
Import raw copy & pasted JSON
</div>
<div class="dialog-body">
<div class="grid-layout layout-tight layout-full">
<p class="info">
Paste in an array of JSON objects representing the documents that you would like to insert into the database.
</p>
<p class="info">
<code>[{"woo": "pizza"}, {"tasty": "muffins"}]</code>
</p>
<div class="menu-container data-table-cell-editor">
<textarea class="data-table-cell-copypaste-editor" bind="textarea">{{value}}</textarea>
</div>
</div>
</div>
<div class="dialog-footer">
<button class="okButton button">&nbsp;&nbsp;Import&nbsp;&nbsp;</button>
<button class="cancelButton button">Cancel</button>
</div>
</div>
</div>
<div class="dialog-footer">
<button class="okButton button">&nbsp;&nbsp;Import&nbsp;&nbsp;</button>
<button class="cancelButton button">Cancel</button>
</div>
</script>
<script type='text/mustache' class="uploadImportTemplate">
<div class="dialog-header">
Upload and import a CSV
</div>
<div class="dialog-body">
<div class="grid-layout layout-tight layout-full">
Upload and import a CSV
</div>
<div class="dialog-body">
<div class="grid-layout layout-tight layout-full">
<strong>Please choose a CSV file to upload:</strong><br />
<input type="file" id="file" />
</div>
</div>
<div class="dialog-footer">
<button class="okButton button">&nbsp;&nbsp;Import&nbsp;&nbsp;</button>
<button class="cancelButton button">Cancel</button>
</div>
</div>
</div>
<div class="dialog-footer">
<button class="okButton button">&nbsp;&nbsp;Import&nbsp;&nbsp;</button>
<button class="cancelButton button">Cancel</button>
</div>
</script>
<script type='text/mustache' class="bulkEditTemplate">
<div class="dialog-header">
Functional transform on column {{name}}
</div>
<div class="dialog-body">
<div class="grid-layout layout-tight layout-full">
<table>
<tbody>
<tr>
<td colspan="4">
<div class="grid-layout layout-tight layout-full">
<table rows="4" cols="4">
<tbody>
<tr style="vertical-align: bottom;">
<td colspan="4">
Expression
</td>
</tr>
<tr>
<td colspan="3">
<div class="input-container">
<textarea class="expression-preview-code"></textarea>
</div>
</td>
<td class="expression-preview-parsing-status" width="150" style="vertical-align: top;">
No syntax error.
</td>
</tr>
<tr>
<td colspan="4">
<div id="expression-preview-tabs" class="refine-tabs ui-tabs ui-widget ui-widget-content ui-corner-all">
<span>Preview</span>
<div id="expression-preview-tabs-preview" class="ui-tabs-panel ui-widget-content ui-corner-bottom">
<div class="expression-preview-container" style="width: 652px; ">
</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="dialog-footer">
<button class="okButton button">&nbsp;&nbsp;Update All&nbsp;&nbsp;</button>
<button class="cancelButton button">Cancel</button>
</div>
Functional transform on column {{name}}
</div>
<div class="dialog-body">
<div class="grid-layout layout-tight layout-full">
<table>
<tbody>
<tr>
<td colspan="4">
<div class="grid-layout layout-tight layout-full">
<table rows="4" cols="4">
<tbody>
<tr style="vertical-align: bottom;">
<td colspan="4">
Expression
</td>
</tr>
<tr>
<td colspan="3">
<div class="input-container">
<textarea class="expression-preview-code"></textarea>
</div>
</td>
<td class="expression-preview-parsing-status" width="150" style="vertical-align: top;">
No syntax error.
</td>
</tr>
<tr>
<td colspan="4">
<div id="expression-preview-tabs" class="refine-tabs ui-tabs ui-widget ui-widget-content ui-corner-all">
<span>Preview</span>
<div id="expression-preview-tabs-preview" class="ui-tabs-panel ui-widget-content ui-corner-bottom">
<div class="expression-preview-container" style="width: 652px; ">
</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="dialog-footer">
<button class="okButton button">&nbsp;&nbsp;Update All&nbsp;&nbsp;</button>
<button class="cancelButton button">Cancel</button>
</div>
</script>
<script type='text/mustache' class="cellEditorTemplate">
@ -400,43 +397,43 @@
<script type='text/mustache' class="jsonTreeTemplate">
<div class="dialog-header">
Please highlight the array of JSON objects to convert to documents.
</div>
<div class="dialog-body">
</div>
<div class="dialog-body">
<div id="document-container">
<div id="document-editor"></div>
</div>
</div>
<div class="dialog-footer">
<button class="okButton button">&nbsp;&nbsp;Import&nbsp;&nbsp;</button>
<button class="cancelButton button">Cancel</button>
</div>
</div>
<div class="dialog-footer">
<button class="okButton button">&nbsp;&nbsp;Import&nbsp;&nbsp;</button>
<button class="cancelButton button">Cancel</button>
</div>
</script>
<script type='text/mustache' class="editPreviewTemplate">
<div class="expression-preview-table-wrapper">
<table>
<tbody>
<tr>
<td class="expression-preview-heading">
before
</td>
<td class="expression-preview-heading">
after
</td>
</tr>
{{#rows}}
<tr>
<td class="expression-preview-value">
{{before}}
</td>
<td class="expression-preview-value">
{{after}}
</td>
</tr>
{{/rows}}
</tbody>
</table>
</div>
</script>
<div class="expression-preview-table-wrapper">
<table>
<tbody>
<tr>
<td class="expression-preview-heading">
before
</td>
<td class="expression-preview-heading">
after
</td>
</tr>
{{#rows}}
<tr>
<td class="expression-preview-value">
{{before}}
</td>
<td class="expression-preview-value">
{{after}}
</td>
</tr>
{{/rows}}
</tbody>
</table>
</div>
</script>
</body>
</html>

View File

@ -1,271 +1,229 @@
var app = {
baseURL: util.getBaseURL(document.location.pathname),
container: 'main_content',
emitter: util.registerEmitter()
};
app.handler = function(route) {
if (route.params && route.params.route) {
var path = route.params.route;
app.routes[path](route.params.id);
} else {
app.routes['home']();
}
};
app.routes = {
home: function() {
// HACK: this should be in the html file (and really we need a much simpler example and keep this as recline-full example)
var demoData = {
headers: ['x', 'y', 'z']
, rows: [
{x: 1, y: 2, z: 3}
, {x: 2, y: 4, z: 6}
, {x: 3, y: 6, z: 9}
, {x: 4, y: 8, z: 12}
, {x: 5, y: 10, z: 15}
, {x: 6, y: 12, z: 18}
]
};
var dataset = new RECLINE.Model.Dataset({
title: 'My Demo Dataset'
},
demoData);
recline.bootstrap(dataset);
}
}
app.after = {
tableContainer: function() {
recline.activateControls();
},
dataTable: function() {
$('.column-header-menu').click(function(e) {
app.currentColumn = $(e.target).siblings().text();
util.position('menu', e);
util.render('columnActions', 'menu');
});
$('.row-header-menu').click(function(e) {
app.currentRow = $(e.target).parents('tr:first').attr('data-id');
util.position('menu', e);
util.render('rowActions', 'menu');
});
$('.data-table-cell-edit').click(function(e) {
var editing = $('.data-table-cell-editor-editor');
if (editing.length > 0) {
editing.parents('.data-table-cell-value').html(editing.text()).siblings('.data-table-cell-edit').removeClass("hidden");
}
$(e.target).addClass("hidden");
var cell = $(e.target).siblings('.data-table-cell-value');
cell.data("previousContents", cell.text());
util.render('cellEditor', cell, {value: cell.text()});
})
},
columnActions: function() { recline.handleMenuClick() },
rowActions: function() { recline.handleMenuClick() },
cellEditor: function() {
$('.data-table-cell-editor .okButton').click(function(e) {
var cell = $(e.target);
var rowId = cell.parents('tr').attr('data-id');
var header = cell.parents('td').attr('data-header');
var doc = _.find(app.cache, function(cacheDoc) {
return cacheDoc._id === rowId;
});
doc[header] = cell.parents('.data-table-cell-editor').find('.data-table-cell-editor-editor').val();
util.notify("Updating row...", {persist: true, loader: true});
costco.updateDoc(doc).then(function(response) {
util.notify("Row updated successfully");
recline.initializeTable();
})
})
$('.data-table-cell-editor .cancelButton').click(function(e) {
var cell = $(e.target).parents('.data-table-cell-value');
cell.html(cell.data('previousContents')).siblings('.data-table-cell-edit').removeClass("hidden");
})
},
actions: function() {
$('.button').click(function(e) {
var action = $(e.target).attr('data-action');
util.position('menu', e, {left: -60, top: 5});
util.render(action + 'Actions', 'menu');
recline.handleMenuClick();
});
},
controls: function() {
$('#logged-in-status').click(function(e) {
if ($(e.target).text() === "Sign in") {
recline.showDialog("signIn");
} else if ($(e.target).text() === "Sign out") {
util.notify("Signing you out...", {persist: true, loader: true});
couch.logout().then(function(response) {
util.notify("Signed out");
util.render('controls', 'project-controls', {text: "Sign in"});
})
}
});
},
signIn: function() {
$('.dialog-content #username-input').focus();
$('.dialog-content').find('#sign-in-form').submit(function(e) {
$('.dialog-content .okButton').click();
return false;
})
$('.dialog-content .okButton').click(function(e) {
util.hide('dialog');
util.notify("Signing you in...", {persist: true, loader: true});
var form = $(e.target).parents('.dialog-content').find('#sign-in-form');
var credentials = {
username: form.find('#username-input').val(),
password: form.find('#password-input').val()
}
couch.login(credentials).then(function(response) {
util.notify("Signed in");
util.render('controls', 'project-controls', {text: "Sign out"});
}, function(error) {
if (error.statusText === "error") util.notify(JSON.parse(error.responseText).reason);
})
})
},
bulkEdit: function() {
$('.dialog-content .okButton').click(function(e) {
var funcText = $('.expression-preview-code').val();
var editFunc = costco.evalFunction(funcText);
;
if (editFunc.errorMessage) {
util.notify("Error with function! " + editFunc.errorMessage);
return;
}
util.hide('dialog');
costco.updateDocs(editFunc);
})
var editor = $('.expression-preview-code');
editor.val("function(doc) {\n doc['"+ app.currentColumn+"'] = doc['"+ app.currentColumn+"'];\n return doc;\n}");
editor.focus().get(0).setSelectionRange(18, 18);
editor.keydown(function(e) {
// if you don't setTimeout it won't grab the latest character if you call e.target.value
window.setTimeout( function() {
var errors = $('.expression-preview-parsing-status');
var editFunc = costco.evalFunction(e.target.value);
if (!editFunc.errorMessage) {
errors.text('No syntax error.');
costco.previewTransform(app.cache, editFunc, app.currentColumn);
} else {
errors.text(editFunc.errorMessage);
}
}, 1, true);
});
editor.keydown();
},
transform: function() {
$('.dialog-content .okButton').click(function(e) {
util.notify("Not implemented yet, sorry! :D");
util.hide('dialog');
})
var editor = $('.expression-preview-code');
editor.val("function(val) {\n if(_.isString(val)) this.update(\"pizza\")\n}");
editor.focus().get(0).setSelectionRange(62,62);
editor.keydown(function(e) {
// if you don't setTimeout it won't grab the latest character if you call e.target.value
window.setTimeout( function() {
var errors = $('.expression-preview-parsing-status');
var editFunc = costco.evalFunction(e.target.value);
if (!editFunc.errorMessage) {
errors.text('No syntax error.');
var traverseFunc = function(doc) {
util.traverse(doc).forEach(editFunc);
return doc;
}
costco.previewTransform(app.cache, traverseFunc);
} else {
errors.text(editFunc.errorMessage);
}
}, 1, true);
});
editor.keydown();
},
urlImport: function() {
$('.dialog-content .okButton').click(function(e) {
app.apiURL = $('#url-input').val().trim();
util.notify("Fetching data...", {persist: true, loader: true});
$.getJSON(app.apiURL + "?callback=?").then(
function(docs) {
app.apiDocs = docs;
util.notify("Data fetched successfully!");
recline.showDialog('jsonTree');
},
function (err) {
util.hide('dialog');
util.notify("Data fetch error: " + err.responseText);
}
);
})
},
uploadImport: function() {
$('.dialog-content .okButton').click(function(e) {
util.hide('dialog');
util.notify("Saving documents...", {persist: true, loader: true});
costco.uploadCSV();
})
},
jsonTree: function() {
util.renderTree(app.apiDocs);
$('.dialog-content .okButton').click(function(e) {
util.hide('dialog');
util.notify("Saving documents...", {persist: true, loader: true});
costco.uploadDocs(util.lookupPath(util.selectedTreePath())).then(function(msg) {
util.notify("Docs saved successfully!");
recline.initializeTable(app.offset);
});
})
},
pasteImport: function() {
$('.dialog-content .okButton').click(function(e) {
util.notify("Uploading documents...", {persist: true, loader: true});
try {
var docs = JSON.parse($('.data-table-cell-copypaste-editor').val());
} catch(e) {
util.notify("JSON parse error: " + e);
}
if (docs) {
if(_.isArray(docs)) {
costco.uploadDocs(docs).then(
function(docs) {
util.notify("Data uploaded successfully!");
recline.initializeTable(app.offset);
util.hide('dialog');
},
function (err) {
util.hide('dialog');
}
);
} else {
util.notify("Error: JSON must be an array of objects");
}
}
})
}
}
app.sammy = $.sammy(function () {
this.get('', app.handler);
this.get("#/", app.handler);
this.get("#:route", app.handler);
this.get("#:route/:id", app.handler);
});
$(function() {
app.emitter.on('error', function(error) {
util.notify("Server error: " + error);
})
util.traverse = require('traverse');
app.sammy.run();
window.app = new recline.DataTable({url: "awesome.com/webstore.json"});
})
// app.after = {
// tableContainer: function() {
// recline.activateControls();
// },
// dataTable: function() {
// $('.column-header-menu').click(function(e) {
// app.currentColumn = $(e.target).siblings().text();
// util.position('menu', e);
// util.render('columnActions', 'menu');
// });
//
// $('.row-header-menu').click(function(e) {
// app.currentRow = $(e.target).parents('tr:first').attr('data-id');
// util.position('menu', e);
// util.render('rowActions', 'menu');
// });
//
// $('.data-table-cell-edit').click(function(e) {
// var editing = $('.data-table-cell-editor-editor');
// if (editing.length > 0) {
// editing.parents('.data-table-cell-value').html(editing.text()).siblings('.data-table-cell-edit').removeClass("hidden");
// }
// $(e.target).addClass("hidden");
// var cell = $(e.target).siblings('.data-table-cell-value');
// cell.data("previousContents", cell.text());
// util.render('cellEditor', cell, {value: cell.text()});
// })
// },
// columnActions: function() { recline.handleMenuClick() },
// rowActions: function() { recline.handleMenuClick() },
// cellEditor: function() {
// $('.data-table-cell-editor .okButton').click(function(e) {
// var cell = $(e.target);
// var rowId = cell.parents('tr').attr('data-id');
// var header = cell.parents('td').attr('data-header');
// var doc = _.find(app.cache, function(cacheDoc) {
// return cacheDoc._id === rowId;
// });
// doc[header] = cell.parents('.data-table-cell-editor').find('.data-table-cell-editor-editor').val();
// util.notify("Updating row...", {persist: true, loader: true});
// costco.updateDoc(doc).then(function(response) {
// util.notify("Row updated successfully");
// recline.initializeTable();
// })
// })
// $('.data-table-cell-editor .cancelButton').click(function(e) {
// var cell = $(e.target).parents('.data-table-cell-value');
// cell.html(cell.data('previousContents')).siblings('.data-table-cell-edit').removeClass("hidden");
// })
// },
// actions: function() {
// $('.button').click(function(e) {
// var action = $(e.target).attr('data-action');
// util.position('menu', e, {left: -60, top: 5});
// util.render(action + 'Actions', 'menu');
// recline.handleMenuClick();
// });
// },
// controls: function() {
// $('#logged-in-status').click(function(e) {
// if ($(e.target).text() === "Sign in") {
// recline.showDialog("signIn");
// } else if ($(e.target).text() === "Sign out") {
// util.notify("Signing you out...", {persist: true, loader: true});
// couch.logout().then(function(response) {
// util.notify("Signed out");
// util.render('controls', 'project-controls', {text: "Sign in"});
// })
// }
// });
// },
// signIn: function() {
//
// $('.dialog-content #username-input').focus();
//
// $('.dialog-content').find('#sign-in-form').submit(function(e) {
// $('.dialog-content .okButton').click();
// return false;
// })
//
// $('.dialog-content .okButton').click(function(e) {
// util.hide('dialog');
// util.notify("Signing you in...", {persist: true, loader: true});
// var form = $(e.target).parents('.dialog-content').find('#sign-in-form');
// var credentials = {
// username: form.find('#username-input').val(),
// password: form.find('#password-input').val()
// }
// couch.login(credentials).then(function(response) {
// util.notify("Signed in");
// util.render('controls', 'project-controls', {text: "Sign out"});
// }, function(error) {
// if (error.statusText === "error") util.notify(JSON.parse(error.responseText).reason);
// })
// })
//
// },
// bulkEdit: function() {
// $('.dialog-content .okButton').click(function(e) {
// var funcText = $('.expression-preview-code').val();
// var editFunc = costco.evalFunction(funcText);
// ;
// if (editFunc.errorMessage) {
// util.notify("Error with function! " + editFunc.errorMessage);
// return;
// }
// util.hide('dialog');
// costco.updateDocs(editFunc);
// })
//
// var editor = $('.expression-preview-code');
// editor.val("function(doc) {\n doc['"+ app.currentColumn+"'] = doc['"+ app.currentColumn+"'];\n return doc;\n}");
// editor.focus().get(0).setSelectionRange(18, 18);
// editor.keydown(function(e) {
// // if you don't setTimeout it won't grab the latest character if you call e.target.value
// window.setTimeout( function() {
// var errors = $('.expression-preview-parsing-status');
// var editFunc = costco.evalFunction(e.target.value);
// if (!editFunc.errorMessage) {
// errors.text('No syntax error.');
// costco.previewTransform(app.cache, editFunc, app.currentColumn);
// } else {
// errors.text(editFunc.errorMessage);
// }
// }, 1, true);
// });
// editor.keydown();
// },
// transform: function() {
// $('.dialog-content .okButton').click(function(e) {
// util.notify("Not implemented yet, sorry! :D");
// util.hide('dialog');
// })
//
// var editor = $('.expression-preview-code');
// editor.val("function(val) {\n if(_.isString(val)) this.update(\"pizza\")\n}");
// editor.focus().get(0).setSelectionRange(62,62);
// editor.keydown(function(e) {
// // if you don't setTimeout it won't grab the latest character if you call e.target.value
// window.setTimeout( function() {
// var errors = $('.expression-preview-parsing-status');
// var editFunc = costco.evalFunction(e.target.value);
// if (!editFunc.errorMessage) {
// errors.text('No syntax error.');
// var traverseFunc = function(doc) {
// util.traverse(doc).forEach(editFunc);
// return doc;
// }
// costco.previewTransform(app.cache, traverseFunc);
// } else {
// errors.text(editFunc.errorMessage);
// }
// }, 1, true);
// });
// editor.keydown();
// },
// urlImport: function() {
// $('.dialog-content .okButton').click(function(e) {
// app.apiURL = $('#url-input').val().trim();
// util.notify("Fetching data...", {persist: true, loader: true});
// $.getJSON(app.apiURL + "?callback=?").then(
// function(docs) {
// app.apiDocs = docs;
// util.notify("Data fetched successfully!");
// recline.showDialog('jsonTree');
// },
// function (err) {
// util.hide('dialog');
// util.notify("Data fetch error: " + err.responseText);
// }
// );
// })
// },
// uploadImport: function() {
// $('.dialog-content .okButton').click(function(e) {
// util.hide('dialog');
// util.notify("Saving documents...", {persist: true, loader: true});
// costco.uploadCSV();
// })
// },
// jsonTree: function() {
// util.renderTree(app.apiDocs);
// $('.dialog-content .okButton').click(function(e) {
// util.hide('dialog');
// util.notify("Saving documents...", {persist: true, loader: true});
// costco.uploadDocs(util.lookupPath(util.selectedTreePath())).then(function(msg) {
// util.notify("Docs saved successfully!");
// recline.initializeTable(app.offset);
// });
// })
// },
// pasteImport: function() {
// $('.dialog-content .okButton').click(function(e) {
// util.notify("Uploading documents...", {persist: true, loader: true});
// try {
// var docs = JSON.parse($('.data-table-cell-copypaste-editor').val());
// } catch(e) {
// util.notify("JSON parse error: " + e);
// }
// if (docs) {
// if(_.isArray(docs)) {
// costco.uploadDocs(docs).then(
// function(docs) {
// util.notify("Data uploaded successfully!");
// recline.initializeTable(app.offset);
// util.hide('dialog');
// },
// function (err) {
// util.hide('dialog');
// }
// );
// } else {
// util.notify("Error: JSON must be an array of objects");
// }
// }
// })
// }
// }
//
// app.sammy = $.sammy(function () {
// this.get('', app.handler);
// this.get("#/", app.handler);
// this.get("#:route", app.handler);
// this.get("#:route/:id", app.handler);
// });

48
src/backbone-webstore.js Normal file
View File

@ -0,0 +1,48 @@
// replaces `Backbone.sync` with a OKFN webstore based tabular data source
var WebStore = function(url) {
this.url = url;
this.headers = [];
this.totalRows = 0;
this.getTabularData = function() {
var dfd = $.Deferred();
var tabularData = {
headers: ['x', 'y', 'z']
, rows: [
{x: 1, y: 2, z: 3}
, {x: 2, y: 4, z: 6}
, {x: 3, y: 6, z: 9}
, {x: 4, y: 8, z: 12}
, {x: 5, y: 10, z: 15}
, {x: 6, y: 12, z: 18}
]
, getLength: function() { return this.rows.length; }
, getRows: function(numRows, start) {
if (start === undefined) {
start = 0;
}
var dfd = $.Deferred();
var results = this.rows.slice(start, start + numRows);
dfd.resolve(results);
return dfd.promise();
}
}
dfd.resolve(tabularData);
return dfd.promise();
}
};
// Override `Backbone.sync` to delegate to the model or collection's
// webStore property, which should be an instance of `WebStore`.
Backbone.sync = function(method, model, options) {
var resp;
var store = model.webStore || model.collection.webStore;
if (method === "read") {
store.getTabularData().then(function(tabularData) {
tabularData.getRows(10).then(options.success, options.error)
})
}
};

3320
src/deps-min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,171 +1,209 @@
var recline = function() {
function showDialog(template, data) {
if (!data) data = {};
util.show('dialog');
util.render(template, 'dialog-content', data);
util.observeExit($('.dialog-content'), function() {
util.hide('dialog');
})
$('.dialog').draggable({ handle: '.dialog-header', cursor: 'move' });
}
function handleMenuClick() {
$( '.menu li' ).click(function(e) {
var actions = {
bulkEdit: function() { showDialog('bulkEdit', {name: app.currentColumn}) },
transform: function() { showDialog('transform') },
csv: function() { window.location.href = app.csvUrl },
json: function() { window.location.href = "_rewrite/api/json" },
urlImport: function() { showDialog('urlImport') },
pasteImport: function() { showDialog('pasteImport') },
uploadImport: function() { showDialog('uploadImport') },
deleteColumn: function() {
var msg = "Are you sure? This will delete '" + app.currentColumn + "' from all documents.";
if (confirm(msg)) costco.deleteColumn(app.currentColumn);
},
deleteRow: function() {
var doc = _.find(app.cache, function(doc) { return doc._id === app.currentRow });
doc._deleted = true;
costco.uploadDocs([doc]).then(
function(updatedDocs) {
util.notify("Row deleted successfully");
recline.initializeTable(app.offset);
},
function(err) { util.notify("Errorz! " + err) }
)
}
}
util.hide('menu');
actions[$(e.target).attr('data-action')]();
e.preventDefault();
})
}
function renderRows(rows) {
var rows = rows;
if (rows.length < 1) {
util.render('dataTable', 'data-table-container');
return;
};
var tableRows = [];
rows.map(function(row) {
var cells = [];
app.headers.map(function(header) {
var value = "";
if (row[header]) {
value = row[header];
if (typeof(value) == "object") value = JSON.stringify(value);
}
cells.push({header: header, value: value});
})
tableRows.push({id: row.id, cells: cells});
})
util.render('dataTable', 'data-table-container', {
rows: tableRows,
headers: app.headers,
notEmpty: function() { return app.headers.length > 0 }
})
// TODO: sort out how we carry around offset info
// app.offset = response.offset;
window.recline = {};
function activate(e) {
e.removeClass('inaction').addClass('action');
}
function deactivate(e) {
e.removeClass('action').addClass('inaction');
}
if (app.offset + getPageSize() >= app.rowCount) {
deactivate($( '.viewpanel-paging .last'));
deactivate($( '.viewpanel-paging .next'));
} else {
activate($( '.viewpanel-paging .last'));
activate($( '.viewpanel-paging .next'));
}
if (app.offset === 0) {
deactivate($( '.viewpanel-paging .previous'));
deactivate($( '.viewpanel-paging .first'));
} else {
activate($( '.viewpanel-paging .previous'));
activate($( '.viewpanel-paging .first'));
}
}
recline.Document = Backbone.Model.extend({});
recline.DocumentList = Backbone.Collection.extend({
webStore: new WebStore(this.url),
model: recline.Document
});
recline.DataTable = Backbone.View.extend({
el: ".data-table-container",
function activateControls() {
$( '.viewPanel-pagingControls-page' ).click(function( e ) {
$(".viewpanel-pagesize .selected").removeClass('selected');
$(e.target).addClass('selected');
fetchRows(app.offset);
});
$( '.viewpanel-paging a' ).click(function( e ) {
var action = $(e.target);
if (action.hasClass("last")) fetchRows(app.rowCount - getPageSize());
if (action.hasClass("next")) fetchRows(app.offset + getPageSize());
if (action.hasClass("previous")) fetchRows(app.offset - getPageSize());
if (action.hasClass("first")) fetchRows(0);
});
}
documents: new recline.DocumentList(this.url),
function getPageSize() {
var pagination = $(".viewpanel-pagesize .selected");
if (pagination.length > 0) {
return parseInt(pagination.text())
} else {
return 10;
}
}
function fetchRows(offset) {
if (offset != undefined) {
app.offset = offset;
}
var numRows = getPageSize();
app.tabularData.getRows(numRows, offset).then(function(rows) {
$('.viewpanel-pagingcount').text(offset + " - " + ((offset - 1) + getPageSize()));
app.cache = rows;
renderRows(rows);
});
}
function bootstrap(dataset) {
util.listenFor(['esc', 'return']);
initializeTable(dataset);
}
function initializeTable(dataset) {
util.render( 'tableContainer', 'right-panel' );
showDialog('busy');
dataset.getTabularData().then(function ( tabularData ) {
util.hide('dialog');
app.headers = tabularData.get('headers');
// TODO: should this be callback like
app.rowCount = tabularData.getLength();
// TODO: delete?
util.render( 'actions', 'project-actions', $.extend({}, app.dbInfo, {url: app.csvUrl}) );
var offset = 0;
app.tabularData = tabularData;
fetchRows(offset);
// template: TODO ???
events: {
},
initialize: function() {
var that = this;
this.documents.fetch({
success: function(collection, resp) {
that.render()
}
})
},
render: function() {
var template = $( ".dataTableTemplate:first" ).html()
, htmls = $.mustache(template, {rows: this.documents.toJSON()} )
;
$(this.el).html(htmls);
return this;
}
return {
handleMenuClick: handleMenuClick,
showDialog: showDialog,
bootstrap: bootstrap,
fetchRows: fetchRows,
activateControls: activateControls,
getPageSize: getPageSize,
renderRows: renderRows,
initializeTable: initializeTable
};
}();
});
// var recline = function() {
//
// function showDialog(template, data) {
// if (!data) data = {};
// util.show('dialog');
// util.render(template, 'dialog-content', data);
// util.observeExit($('.dialog-content'), function() {
// util.hide('dialog');
// })
// $('.dialog').draggable({ handle: '.dialog-header', cursor: 'move' });
// }
//
// function handleMenuClick() {
// $( '.menu li' ).click(function(e) {
// var actions = {
// bulkEdit: function() { showDialog('bulkEdit', {name: app.currentColumn}) },
// transform: function() { showDialog('transform') },
// csv: function() { window.location.href = app.csvUrl },
// json: function() { window.location.href = "_rewrite/api/json" },
// urlImport: function() { showDialog('urlImport') },
// pasteImport: function() { showDialog('pasteImport') },
// uploadImport: function() { showDialog('uploadImport') },
// deleteColumn: function() {
// var msg = "Are you sure? This will delete '" + app.currentColumn + "' from all documents.";
// if (confirm(msg)) costco.deleteColumn(app.currentColumn);
// },
// deleteRow: function() {
// var doc = _.find(app.cache, function(doc) { return doc._id === app.currentRow });
// doc._deleted = true;
// costco.uploadDocs([doc]).then(
// function(updatedDocs) {
// util.notify("Row deleted successfully");
// recline.initializeTable(app.offset);
// },
// function(err) { util.notify("Errorz! " + err) }
// )
// }
// }
//
// util.hide('menu');
// actions[$(e.target).attr('data-action')]();
//
// e.preventDefault();
// })
// }
//
// function renderRows(rows) {
// var rows = rows;
//
// if (rows.length < 1) {
// util.render('dataTable', 'data-table-container');
// return;
// };
//
// var tableRows = [];
//
// rows.map(function(row) {
// var cells = [];
// app.headers.map(function(header) {
// var value = "";
// if (row[header]) {
// value = row[header];
// if (typeof(value) == "object") value = JSON.stringify(value);
// }
// cells.push({header: header, value: value});
// })
// tableRows.push({id: row.id, cells: cells});
// })
//
// util.render('dataTable', 'data-table-container', {
// rows: tableRows,
// headers: app.headers,
// notEmpty: function() { return app.headers.length > 0 }
// })
//
// // TODO: sort out how we carry around offset info
// // app.offset = response.offset;
//
// function activate(e) {
// e.removeClass('inaction').addClass('action');
// }
//
// function deactivate(e) {
// e.removeClass('action').addClass('inaction');
// }
//
// if (app.offset + getPageSize() >= app.rowCount) {
// deactivate($( '.viewpanel-paging .last'));
// deactivate($( '.viewpanel-paging .next'));
// } else {
// activate($( '.viewpanel-paging .last'));
// activate($( '.viewpanel-paging .next'));
// }
//
// if (app.offset === 0) {
// deactivate($( '.viewpanel-paging .previous'));
// deactivate($( '.viewpanel-paging .first'));
// } else {
// activate($( '.viewpanel-paging .previous'));
// activate($( '.viewpanel-paging .first'));
// }
// }
//
// function activateControls() {
// $( '.viewPanel-pagingControls-page' ).click(function( e ) {
// $(".viewpanel-pagesize .selected").removeClass('selected');
// $(e.target).addClass('selected');
// fetchRows(app.offset);
// });
// $( '.viewpanel-paging a' ).click(function( e ) {
// var action = $(e.target);
// if (action.hasClass("last")) fetchRows(app.rowCount - getPageSize());
// if (action.hasClass("next")) fetchRows(app.offset + getPageSize());
// if (action.hasClass("previous")) fetchRows(app.offset - getPageSize());
// if (action.hasClass("first")) fetchRows(0);
// });
// }
//
// function getPageSize() {
// var pagination = $(".viewpanel-pagesize .selected");
// if (pagination.length > 0) {
// return parseInt(pagination.text())
// } else {
// return 10;
// }
// }
//
// function fetchRows(offset) {
// if (offset != undefined) {
// app.offset = offset;
// }
// var numRows = getPageSize();
// app.tabularData.getRows(numRows, offset).then(function(rows) {
// $('.viewpanel-pagingcount').text(offset + " - " + ((offset - 1) + getPageSize()));
// app.cache = rows;
// renderRows(rows);
// });
// }
//
// function bootstrap(dataset) {
// util.listenFor(['esc', 'return']);
// initializeTable(dataset);
// }
//
// function initializeTable(dataset) {
// util.render( 'tableContainer', 'right-panel' );
// showDialog('busy');
// dataset.getTabularData().then(function ( tabularData ) {
// util.hide('dialog');
// app.headers = tabularData.headers;
// // TODO: should this be callback like
// app.rowCount = tabularData.getLength();
// util.render( 'actions', 'project-actions', $.extend({}, app.dbInfo, {url: app.csvUrl}) );
// var offset = 0;
// app.tabularData = tabularData;
// fetchRows(offset);
// })
// }
//
// return {
// handleMenuClick: handleMenuClick,
// showDialog: showDialog,
// bootstrap: bootstrap,
// fetchRows: fetchRows,
// activateControls: activateControls,
// getPageSize: getPageSize,
// renderRows: renderRows,
// initializeTable: initializeTable
// };
// }();

View File

@ -6,7 +6,6 @@
<link rel="stylesheet" href="qunit/qunit.css" type="text/css" media="screen" />
<script src="../src/deps-min.js"></script>
<script src="../vendor/backbone/0.5.1/backbone.js"></script>
<script type="text/javascript" src="qunit/qunit.js"></script>
<script type="text/javascript" src="../src/dataset.js"></script>