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
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. 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> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>CouchDB Data Explorer</title> <title>CouchDB Data Explorer</title>
<link rel="stylesheet" href="style/reset.css" media="screen"> <link rel="stylesheet" href="style/reset.css" media="screen">
<link rel="stylesheet" href="style/data-table.css" media="screen"> <link rel="stylesheet" href="style/data-table.css" media="screen">
<link rel="stylesheet" href="style/style.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>
<script type="text/javascript" src="../src/deps-min.js"></script> <script type="text/javascript" src="../src/backbone-webstore.js"></script>
<!-- TODO: move Backbone in deps-min --> <script type="text/javascript" src="../src/util.js"></script>
<script src="../vendor/backbone/0.5.1/backbone.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="../src/util.js"></script> <script type="text/javascript" src="js/app.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>
</head> </head>
<body class="bod"> <body class="bod">
<div class="container"> <div class="container">
<div class="menu-overlay" style="display: none; z-index: 101; ">&nbsp;</div> <!-- <div class="menu-overlay" style="display: none; z-index: 101; ">&nbsp;</div>
<ul class="menu"> <ul class="menu">
</ul> </ul>
<div id="header"> <div id="header">
@@ -31,20 +27,21 @@
<div class="main_content"> <div class="main_content">
<div class="left-panel"></div> <div class="left-panel"></div>
<div class="right-panel"></div> <div class="right-panel"></div>
</div> </div> -->
</div> <div class="data-table-container"></div>
</div>
<div id="notification-container"> <div id="notification-container">
<div id="notification"> <div id="notification">
<img src="images/small-spinner.gif" class="notification-loader"><span id="notification-message">Loading...</span> <img src="images/small-spinner.gif" class="notification-loader"><span id="notification-message">Loading...</span>
</div> </div>
</div> </div>
<div class="dialog-overlay" style="display: none; z-index: 101; ">&nbsp;</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 ui-draggable" style="display: none; z-index: 102; top: 101px; ">
<div class="dialog-frame" style="width: 700px; visibility: visible; "> <div class="dialog-frame" style="width: 700px; visibility: visible; ">
<div class="dialog-content dialog-border"></div> <div class="dialog-content dialog-border"></div>
</div> </div>
</div> </div>
<script type='text/mustache' class="busyTemplate"> <script type='text/mustache' class="busyTemplate">
@@ -165,10 +162,10 @@
<script type='text/mustache' class="signInTemplate"> <script type='text/mustache' class="signInTemplate">
<div class="dialog-header"> <div class="dialog-header">
Sign in Sign in
</div> </div>
<div class="dialog-body"> <div class="dialog-body">
<div class="grid-layout layout-tight layout-full"> <div class="grid-layout layout-tight layout-full">
<form name="sign-in-form" id="sign-in-form"> <form name="sign-in-form" id="sign-in-form">
<table class="form-table"> <table class="form-table">
<tbody> <tbody>
@@ -192,81 +189,81 @@
</tbody> </tbody>
</table> </table>
</form> </form>
</div> </div>
</div> </div>
<div class="dialog-footer"> <div class="dialog-footer">
<button class="okButton button">&nbsp;&nbsp;Sign in&nbsp;&nbsp;</button> <button class="okButton button">&nbsp;&nbsp;Sign in&nbsp;&nbsp;</button>
<button class="cancelButton button">Cancel</button> <button class="cancelButton button">Cancel</button>
</div> </div>
</script> </script>
<script type='text/mustache' class="transformTemplate"> <script type='text/mustache' class="transformTemplate">
<div class="dialog-header"> <div class="dialog-header">
Recursive transform on all rows Recursive transform on all rows
</div> </div>
<div class="dialog-body"> <div class="dialog-body">
<div class="grid-layout layout-full"> <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> <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> <table>
<tbody> <tbody>
<tr> <tr>
<td colspan="4"> <td colspan="4">
<div class="grid-layout layout-tight layout-full"> <div class="grid-layout layout-tight layout-full">
<table rows="4" cols="4"> <table rows="4" cols="4">
<tbody> <tbody>
<tr style="vertical-align: bottom;"> <tr style="vertical-align: bottom;">
<td colspan="4"> <td colspan="4">
Expression Expression
</td> </td>
</tr> </tr>
<tr> <tr>
<td colspan="3"> <td colspan="3">
<div class="input-container"> <div class="input-container">
<textarea class="expression-preview-code"></textarea> <textarea class="expression-preview-code"></textarea>
</div> </div>
</td> </td>
<td class="expression-preview-parsing-status" width="150" style="vertical-align: top;"> <td class="expression-preview-parsing-status" width="150" style="vertical-align: top;">
No syntax error. No syntax error.
</td> </td>
</tr> </tr>
<tr> <tr>
<td colspan="4"> <td colspan="4">
<div id="expression-preview-tabs" class="refine-tabs ui-tabs ui-widget ui-widget-content ui-corner-all"> <div id="expression-preview-tabs" class="refine-tabs ui-tabs ui-widget ui-widget-content ui-corner-all">
<span>Preview</span> <span>Preview</span>
<div id="expression-preview-tabs-preview" class="ui-tabs-panel ui-widget-content ui-corner-bottom"> <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 class="expression-preview-container" style="width: 652px; ">
</div> </div>
</div> </div>
</div> </div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
<div class="dialog-footer"> <div class="dialog-footer">
<button class="okButton button">&nbsp;&nbsp;Update All&nbsp;&nbsp;</button> <button class="okButton button">&nbsp;&nbsp;Update All&nbsp;&nbsp;</button>
<button class="cancelButton button">Cancel</button> <button class="cancelButton button">Cancel</button>
</div> </div>
</script> </script>
<script type='text/mustache' class="urlImportTemplate"> <script type='text/mustache' class="urlImportTemplate">
<div class="dialog-header"> <div class="dialog-header">
Download and import from a URL or API Download and import from a URL or API
</div> </div>
<div class="dialog-body"> <div class="dialog-body">
<div class="grid-layout layout-full"> <div class="grid-layout layout-full">
<p class="info"> <p class="info">
Currently only <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a>-enabled APIs are supported, for example: Currently only <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a>-enabled APIs are supported, for example:
</p> </p>
<p class="info"> <p class="info">
<code>https://api.github.com/repos/maxogden/recline/commits</code> <code>https://api.github.com/repos/maxogden/recline/commits</code>
</p> </p>
<form name="api-import-form" id="sign-in-form"> <form name="api-import-form" id="sign-in-form">
<table class="form-table"> <table class="form-table">
<tbody> <tbody>
@@ -282,105 +279,105 @@
</tbody> </tbody>
</table> </table>
</form> </form>
</div> </div>
</div> </div>
<div class="dialog-footer"> <div class="dialog-footer">
<button class="okButton button">&nbsp;&nbsp;Fetch&nbsp;&nbsp;</button> <button class="okButton button">&nbsp;&nbsp;Fetch&nbsp;&nbsp;</button>
<button class="cancelButton button">Cancel</button> <button class="cancelButton button">Cancel</button>
</div> </div>
</script> </script>
<script type='text/mustache' class="pasteImportTemplate"> <script type='text/mustache' class="pasteImportTemplate">
<div class="dialog-header"> <div class="dialog-header">
Import raw copy & pasted JSON Import raw copy & pasted JSON
</div> </div>
<div class="dialog-body"> <div class="dialog-body">
<div class="grid-layout layout-tight layout-full"> <div class="grid-layout layout-tight layout-full">
<p class="info"> <p class="info">
Paste in an array of JSON objects representing the documents that you would like to insert into the database. Paste in an array of JSON objects representing the documents that you would like to insert into the database.
</p> </p>
<p class="info"> <p class="info">
<code>[{"woo": "pizza"}, {"tasty": "muffins"}]</code> <code>[{"woo": "pizza"}, {"tasty": "muffins"}]</code>
</p> </p>
<div class="menu-container data-table-cell-editor"> <div class="menu-container data-table-cell-editor">
<textarea class="data-table-cell-copypaste-editor" bind="textarea">{{value}}</textarea> <textarea class="data-table-cell-copypaste-editor" bind="textarea">{{value}}</textarea>
</div> </div>
</div> </div>
</div> </div>
<div class="dialog-footer"> <div class="dialog-footer">
<button class="okButton button">&nbsp;&nbsp;Import&nbsp;&nbsp;</button> <button class="okButton button">&nbsp;&nbsp;Import&nbsp;&nbsp;</button>
<button class="cancelButton button">Cancel</button> <button class="cancelButton button">Cancel</button>
</div> </div>
</script> </script>
<script type='text/mustache' class="uploadImportTemplate"> <script type='text/mustache' class="uploadImportTemplate">
<div class="dialog-header"> <div class="dialog-header">
Upload and import a CSV Upload and import a CSV
</div> </div>
<div class="dialog-body"> <div class="dialog-body">
<div class="grid-layout layout-tight layout-full"> <div class="grid-layout layout-tight layout-full">
<strong>Please choose a CSV file to upload:</strong><br /> <strong>Please choose a CSV file to upload:</strong><br />
<input type="file" id="file" /> <input type="file" id="file" />
</div> </div>
</div> </div>
<div class="dialog-footer"> <div class="dialog-footer">
<button class="okButton button">&nbsp;&nbsp;Import&nbsp;&nbsp;</button> <button class="okButton button">&nbsp;&nbsp;Import&nbsp;&nbsp;</button>
<button class="cancelButton button">Cancel</button> <button class="cancelButton button">Cancel</button>
</div> </div>
</script> </script>
<script type='text/mustache' class="bulkEditTemplate"> <script type='text/mustache' class="bulkEditTemplate">
<div class="dialog-header"> <div class="dialog-header">
Functional transform on column {{name}} Functional transform on column {{name}}
</div> </div>
<div class="dialog-body"> <div class="dialog-body">
<div class="grid-layout layout-tight layout-full"> <div class="grid-layout layout-tight layout-full">
<table> <table>
<tbody> <tbody>
<tr> <tr>
<td colspan="4"> <td colspan="4">
<div class="grid-layout layout-tight layout-full"> <div class="grid-layout layout-tight layout-full">
<table rows="4" cols="4"> <table rows="4" cols="4">
<tbody> <tbody>
<tr style="vertical-align: bottom;"> <tr style="vertical-align: bottom;">
<td colspan="4"> <td colspan="4">
Expression Expression
</td> </td>
</tr> </tr>
<tr> <tr>
<td colspan="3"> <td colspan="3">
<div class="input-container"> <div class="input-container">
<textarea class="expression-preview-code"></textarea> <textarea class="expression-preview-code"></textarea>
</div> </div>
</td> </td>
<td class="expression-preview-parsing-status" width="150" style="vertical-align: top;"> <td class="expression-preview-parsing-status" width="150" style="vertical-align: top;">
No syntax error. No syntax error.
</td> </td>
</tr> </tr>
<tr> <tr>
<td colspan="4"> <td colspan="4">
<div id="expression-preview-tabs" class="refine-tabs ui-tabs ui-widget ui-widget-content ui-corner-all"> <div id="expression-preview-tabs" class="refine-tabs ui-tabs ui-widget ui-widget-content ui-corner-all">
<span>Preview</span> <span>Preview</span>
<div id="expression-preview-tabs-preview" class="ui-tabs-panel ui-widget-content ui-corner-bottom"> <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 class="expression-preview-container" style="width: 652px; ">
</div> </div>
</div> </div>
</div> </div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
<div class="dialog-footer"> <div class="dialog-footer">
<button class="okButton button">&nbsp;&nbsp;Update All&nbsp;&nbsp;</button> <button class="okButton button">&nbsp;&nbsp;Update All&nbsp;&nbsp;</button>
<button class="cancelButton button">Cancel</button> <button class="cancelButton button">Cancel</button>
</div> </div>
</script> </script>
<script type='text/mustache' class="cellEditorTemplate"> <script type='text/mustache' class="cellEditorTemplate">
@@ -400,43 +397,43 @@
<script type='text/mustache' class="jsonTreeTemplate"> <script type='text/mustache' class="jsonTreeTemplate">
<div class="dialog-header"> <div class="dialog-header">
Please highlight the array of JSON objects to convert to documents. Please highlight the array of JSON objects to convert to documents.
</div> </div>
<div class="dialog-body"> <div class="dialog-body">
<div id="document-container"> <div id="document-container">
<div id="document-editor"></div> <div id="document-editor"></div>
</div> </div>
</div> </div>
<div class="dialog-footer"> <div class="dialog-footer">
<button class="okButton button">&nbsp;&nbsp;Import&nbsp;&nbsp;</button> <button class="okButton button">&nbsp;&nbsp;Import&nbsp;&nbsp;</button>
<button class="cancelButton button">Cancel</button> <button class="cancelButton button">Cancel</button>
</div> </div>
</script> </script>
<script type='text/mustache' class="editPreviewTemplate"> <script type='text/mustache' class="editPreviewTemplate">
<div class="expression-preview-table-wrapper"> <div class="expression-preview-table-wrapper">
<table> <table>
<tbody> <tbody>
<tr> <tr>
<td class="expression-preview-heading"> <td class="expression-preview-heading">
before before
</td> </td>
<td class="expression-preview-heading"> <td class="expression-preview-heading">
after after
</td> </td>
</tr> </tr>
{{#rows}} {{#rows}}
<tr> <tr>
<td class="expression-preview-value"> <td class="expression-preview-value">
{{before}} {{before}}
</td> </td>
<td class="expression-preview-value"> <td class="expression-preview-value">
{{after}} {{after}}
</td> </td>
</tr> </tr>
{{/rows}} {{/rows}}
</tbody> </tbody>
</table> </table>
</div> </div>
</script> </script>
</body> </body>
</html> </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() { $(function() {
app.emitter.on('error', function(error) { window.app = new recline.DataTable({url: "awesome.com/webstore.json"});
util.notify("Server error: " + error);
})
util.traverse = require('traverse');
app.sammy.run();
}) })
// 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() { window.recline = {};
function showDialog(template, data) { recline.Document = Backbone.Model.extend({});
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() { recline.DocumentList = Backbone.Collection.extend({
$( '.menu li' ).click(function(e) { webStore: new WebStore(this.url),
var actions = { model: recline.Document
bulkEdit: function() { showDialog('bulkEdit', {name: app.currentColumn}) }, });
transform: function() { showDialog('transform') },
csv: function() { window.location.href = app.csvUrl }, recline.DataTable = Backbone.View.extend({
json: function() { window.location.href = "_rewrite/api/json" },
urlImport: function() { showDialog('urlImport') }, el: ".data-table-container",
pasteImport: function() { showDialog('pasteImport') },
uploadImport: function() { showDialog('uploadImport') }, documents: new recline.DocumentList(this.url),
deleteColumn: function() {
var msg = "Are you sure? This will delete '" + app.currentColumn + "' from all documents."; // template: TODO ???
if (confirm(msg)) costco.deleteColumn(app.currentColumn);
}, events: {
deleteRow: function() {
var doc = _.find(app.cache, function(doc) { return doc._id === app.currentRow }); },
doc._deleted = true;
costco.uploadDocs([doc]).then( initialize: function() {
function(updatedDocs) { var that = this;
util.notify("Row deleted successfully"); this.documents.fetch({
recline.initializeTable(app.offset); success: function(collection, resp) {
}, that.render()
function(err) { util.notify("Errorz! " + err) }
)
}
} }
util.hide('menu');
actions[$(e.target).attr('data-action')]();
e.preventDefault();
}) })
},
render: function() {
var template = $( ".dataTableTemplate:first" ).html()
, htmls = $.mustache(template, {rows: this.documents.toJSON()} )
;
$(this.el).html(htmls);
return this;
} }
});
function renderRows(rows) { // var recline = function() {
var rows = rows; //
// function showDialog(template, data) {
if (rows.length < 1) { // if (!data) data = {};
util.render('dataTable', 'data-table-container'); // util.show('dialog');
return; // util.render(template, 'dialog-content', data);
}; // util.observeExit($('.dialog-content'), function() {
// util.hide('dialog');
var tableRows = []; // })
// $('.dialog').draggable({ handle: '.dialog-header', cursor: 'move' });
rows.map(function(row) { // }
var cells = []; //
app.headers.map(function(header) { // function handleMenuClick() {
var value = ""; // $( '.menu li' ).click(function(e) {
if (row[header]) { // var actions = {
value = row[header]; // bulkEdit: function() { showDialog('bulkEdit', {name: app.currentColumn}) },
if (typeof(value) == "object") value = JSON.stringify(value); // transform: function() { showDialog('transform') },
} // csv: function() { window.location.href = app.csvUrl },
cells.push({header: header, value: value}); // json: function() { window.location.href = "_rewrite/api/json" },
}) // urlImport: function() { showDialog('urlImport') },
tableRows.push({id: row.id, cells: cells}); // pasteImport: function() { showDialog('pasteImport') },
}) // uploadImport: function() { showDialog('uploadImport') },
// deleteColumn: function() {
util.render('dataTable', 'data-table-container', { // var msg = "Are you sure? This will delete '" + app.currentColumn + "' from all documents.";
rows: tableRows, // if (confirm(msg)) costco.deleteColumn(app.currentColumn);
headers: app.headers, // },
notEmpty: function() { return app.headers.length > 0 } // deleteRow: function() {
}) // var doc = _.find(app.cache, function(doc) { return doc._id === app.currentRow });
// doc._deleted = true;
// TODO: sort out how we carry around offset info // costco.uploadDocs([doc]).then(
// app.offset = response.offset; // function(updatedDocs) {
// util.notify("Row deleted successfully");
function activate(e) { // recline.initializeTable(app.offset);
e.removeClass('inaction').addClass('action'); // },
} // function(err) { util.notify("Errorz! " + err) }
// )
function deactivate(e) { // }
e.removeClass('action').addClass('inaction'); // }
} //
// util.hide('menu');
if (app.offset + getPageSize() >= app.rowCount) { // actions[$(e.target).attr('data-action')]();
deactivate($( '.viewpanel-paging .last')); //
deactivate($( '.viewpanel-paging .next')); // e.preventDefault();
} else { // })
activate($( '.viewpanel-paging .last')); // }
activate($( '.viewpanel-paging .next')); //
} // function renderRows(rows) {
// var rows = rows;
if (app.offset === 0) { //
deactivate($( '.viewpanel-paging .previous')); // if (rows.length < 1) {
deactivate($( '.viewpanel-paging .first')); // util.render('dataTable', 'data-table-container');
} else { // return;
activate($( '.viewpanel-paging .previous')); // };
activate($( '.viewpanel-paging .first')); //
} // var tableRows = [];
} //
// rows.map(function(row) {
function activateControls() { // var cells = [];
$( '.viewPanel-pagingControls-page' ).click(function( e ) { // app.headers.map(function(header) {
$(".viewpanel-pagesize .selected").removeClass('selected'); // var value = "";
$(e.target).addClass('selected'); // if (row[header]) {
fetchRows(app.offset); // value = row[header];
}); // if (typeof(value) == "object") value = JSON.stringify(value);
$( '.viewpanel-paging a' ).click(function( e ) { // }
var action = $(e.target); // cells.push({header: header, value: value});
if (action.hasClass("last")) fetchRows(app.rowCount - getPageSize()); // })
if (action.hasClass("next")) fetchRows(app.offset + getPageSize()); // tableRows.push({id: row.id, cells: cells});
if (action.hasClass("previous")) fetchRows(app.offset - getPageSize()); // })
if (action.hasClass("first")) fetchRows(0); //
}); // util.render('dataTable', 'data-table-container', {
} // rows: tableRows,
// headers: app.headers,
function getPageSize() { // notEmpty: function() { return app.headers.length > 0 }
var pagination = $(".viewpanel-pagesize .selected"); // })
if (pagination.length > 0) { //
return parseInt(pagination.text()) // // TODO: sort out how we carry around offset info
} else { // // app.offset = response.offset;
return 10; //
} // function activate(e) {
} // e.removeClass('inaction').addClass('action');
// }
function fetchRows(offset) { //
if (offset != undefined) { // function deactivate(e) {
app.offset = offset; // e.removeClass('action').addClass('inaction');
} // }
var numRows = getPageSize(); //
app.tabularData.getRows(numRows, offset).then(function(rows) { // if (app.offset + getPageSize() >= app.rowCount) {
$('.viewpanel-pagingcount').text(offset + " - " + ((offset - 1) + getPageSize())); // deactivate($( '.viewpanel-paging .last'));
app.cache = rows; // deactivate($( '.viewpanel-paging .next'));
renderRows(rows); // } else {
}); // activate($( '.viewpanel-paging .last'));
} // activate($( '.viewpanel-paging .next'));
// }
function bootstrap(dataset) { //
util.listenFor(['esc', 'return']); // if (app.offset === 0) {
initializeTable(dataset); // deactivate($( '.viewpanel-paging .previous'));
} // deactivate($( '.viewpanel-paging .first'));
// } else {
function initializeTable(dataset) { // activate($( '.viewpanel-paging .previous'));
util.render( 'tableContainer', 'right-panel' ); // activate($( '.viewpanel-paging .first'));
showDialog('busy'); // }
dataset.getTabularData().then(function ( tabularData ) { // }
util.hide('dialog'); //
app.headers = tabularData.get('headers'); // function activateControls() {
// TODO: should this be callback like // $( '.viewPanel-pagingControls-page' ).click(function( e ) {
app.rowCount = tabularData.getLength(); // $(".viewpanel-pagesize .selected").removeClass('selected');
// TODO: delete? // $(e.target).addClass('selected');
util.render( 'actions', 'project-actions', $.extend({}, app.dbInfo, {url: app.csvUrl}) ); // fetchRows(app.offset);
var offset = 0; // });
app.tabularData = tabularData; // $( '.viewpanel-paging a' ).click(function( e ) {
fetchRows(offset); // 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());
return { // if (action.hasClass("first")) fetchRows(0);
handleMenuClick: handleMenuClick, // });
showDialog: showDialog, // }
bootstrap: bootstrap, //
fetchRows: fetchRows, // function getPageSize() {
activateControls: activateControls, // var pagination = $(".viewpanel-pagesize .selected");
getPageSize: getPageSize, // if (pagination.length > 0) {
renderRows: renderRows, // return parseInt(pagination.text())
initializeTable: initializeTable // } 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" /> <link rel="stylesheet" href="qunit/qunit.css" type="text/css" media="screen" />
<script src="../src/deps-min.js"></script> <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="qunit/qunit.js"></script>
<script type="text/javascript" src="../src/dataset.js"></script> <script type="text/javascript" src="../src/dataset.js"></script>