datahub/src/util.js

446 lines
14 KiB
JavaScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

var util = function() {
var templates = {
transformActions: '<li><a data-action="transform" class="menuAction" href="JavaScript:void(0);">Global transform...</a></li>'
, columnActions: ' \
<li><a data-action="bulkEdit" class="menuAction" href="JavaScript:void(0);">Transform...</a></li> \
<li><a data-action="deleteColumn" class="menuAction" href="JavaScript:void(0);">Delete this column</a></li> \
'
, rowActions: '<li><a data-action="deleteRow" class="menuAction" href="JavaScript:void(0);">Delete this row</a></li>'
, cellEditor: ' \
<div class="menu-container data-table-cell-editor"> \
<textarea class="data-table-cell-editor-editor" bind="textarea">{{value}}</textarea> \
<div id="data-table-cell-editor-actions"> \
<div class="data-table-cell-editor-action"> \
<button class="okButton btn primary">Update</button> \
<button class="cancelButton btn danger">Cancel</button> \
</div> \
</div> \
</div> \
'
, editPreview: ' \
<div class="expression-preview-table-wrapper"> \
<table> \
<thead> \
<tr> \
<th class="expression-preview-heading"> \
before \
</th> \
<th class="expression-preview-heading"> \
after \
</th> \
</tr> \
</thead> \
<tbody> \
{{#rows}} \
<tr> \
<td class="expression-preview-value"> \
{{before}} \
</td> \
<td class="expression-preview-value"> \
{{after}} \
</td> \
</tr> \
{{/rows}} \
</tbody> \
</table> \
</div> \
'
};
$.fn.serializeObject = function() {
var o = {};
var a = this.serializeArray();
$.each(a, function() {
if (o[this.name]) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
return o;
};
function inURL(url, str) {
var exists = false;
if ( url.indexOf( str ) > -1 ) {
exists = true;
}
return exists;
}
function registerEmitter() {
var Emitter = function(obj) {
this.emit = function(obj, channel) {
if (!channel) var channel = 'data';
this.trigger(channel, obj);
};
};
MicroEvent.mixin(Emitter);
return new Emitter();
}
function listenFor(keys) {
var shortcuts = { // from jquery.hotkeys.js
8: "backspace", 9: "tab", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause",
20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home",
37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del",
96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7",
104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/",
112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8",
120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 191: "/", 224: "meta"
}
window.addEventListener("keyup", function(e) {
var pressed = shortcuts[e.keyCode];
if(_.include(keys, pressed)) app.emitter.emit("keyup", pressed);
}, false);
}
function observeExit(elem, callback) {
var cancelButton = elem.find('.cancelButton');
// TODO: remove (commented out as part of Backbon-i-fication
// app.emitter.on('esc', function() {
// cancelButton.click();
// app.emitter.clear('esc');
// });
cancelButton.click(callback);
}
function show( thing ) {
$('.' + thing ).show();
$('.' + thing + '-overlay').show();
}
function hide( thing ) {
$('.' + thing ).hide();
$('.' + thing + '-overlay').hide();
// TODO: remove or replace (commented out as part of Backbon-i-fication
// if (thing === "dialog") app.emitter.clear('esc'); // todo more elegant solution
}
function position( thing, elem, offset ) {
var position = $(elem.target).position();
if (offset) {
if (offset.top) position.top += offset.top;
if (offset.left) position.left += offset.left;
}
$('.' + thing + '-overlay').show().click(function(e) {
$(e.target).hide();
$('.' + thing).hide();
});
$('.' + thing).show().css({top: position.top + $(elem.target).height(), left: position.left});
}
function render( template, target, options ) {
if ( !options ) options = {data: {}};
if ( !options.data ) options = {data: options};
var html = $.mustache( templates[template], options.data );
if (target instanceof jQuery) {
var targetDom = target;
} else {
var targetDom = $( "." + target + ":first" );
}
if( options.append ) {
targetDom.append( html );
} else {
targetDom.html( html );
}
// TODO: remove (commented out as part of Backbon-i-fication
// if (template in app.after) app.after[template]();
}
function notify(message, options) {
if (!options) var options = {};
var tmplData = _.extend({
msg: message,
category: 'warning'
},
options);
var _template = ' \
<div class="alert-message {{category}} fade in" data-alert="alert"><a class="close" href="#">×</a> \
<p>{{msg}} \
{{#loader}} \
<img src="images/small-spinner.gif" class="notification-loader"> \
{{/loader}} \
</p> \
</div>';
var _templated = $.mustache(_template, tmplData);
_templated = $(_templated).appendTo($('.data-explorer .alert-messages'));
if (!options.persist) {
setTimeout(function() {
$(_templated).remove();
}, 3000);
}
}
function formatMetadata(data) {
out = '<dl>';
$.each(data, function(key, val) {
if (typeof(val) == 'string' && key[0] != '_') {
out = out + '<dt>' + key + '<dd>' + val;
} else if (typeof(val) == 'object' && key != "geometry" && val != null) {
if (key == 'properties') {
$.each(val, function(attr, value){
out = out + '<dt>' + attr + '<dd>' + value;
})
} else {
out = out + '<dt>' + key + '<dd>' + val.join(', ');
}
}
});
out = out + '</dl>';
return out;
}
function getBaseURL(url) {
var baseURL = "";
if ( inURL(url, '_design') ) {
if (inURL(url, '_rewrite')) {
var path = url.split("#")[0];
if (path[path.length - 1] === "/") {
baseURL = "";
} else {
baseURL = '_rewrite/';
}
} else {
baseURL = '_rewrite/';
}
}
return baseURL;
}
var persist = {
restore: function() {
$('.persist').each(function(i, el) {
var inputId = $(el).attr('id');
if(localStorage.getItem(inputId)) $('#' + inputId).val(localStorage.getItem(inputId));
})
},
save: function(id) {
localStorage.setItem(id, $('#' + id).val());
},
clear: function() {
$('.persist').each(function(i, el) {
localStorage.removeItem($(el).attr('id'));
})
}
}
// simple debounce adapted from underscore.js
function delay(func, wait) {
return function() {
var context = this, args = arguments;
var throttler = function() {
delete app.timeout;
func.apply(context, args);
};
if (!app.timeout) app.timeout = setTimeout(throttler, wait);
};
};
function resetForm(form) {
$(':input', form)
.not(':button, :submit, :reset, :hidden')
.val('')
.removeAttr('checked')
.removeAttr('selected');
}
function largestWidth(selector, min) {
var min_width = min || 0;
$(selector).each(function(i, n){
var this_width = $(n).width();
if (this_width > min_width) {
min_width = this_width;
}
});
return min_width;
}
function getType(obj) {
if (obj === null) {
return 'null';
}
if (typeof obj === 'object') {
if (obj.constructor.toString().indexOf("Array") !== -1) {
return 'array';
} else {
return 'object';
}
} else {
return typeof obj;
}
}
function lookupPath(path) {
var docs = app.apiDocs;
try {
_.each(path, function(node) {
docs = docs[node];
})
} catch(e) {
util.notify("Error selecting documents" + e);
docs = [];
}
return docs;
}
function nodePath(docField) {
if (docField.children('.object-key').length > 0) return docField.children('.object-key').text();
if (docField.children('.array-key').length > 0) return docField.children('.array-key').text();
if (docField.children('.doc-key').length > 0) return docField.children('.doc-key').text();
return "";
}
function selectedTreePath() {
var nodes = []
, parent = $('.chosen');
while (parent.length > 0) {
nodes.push(nodePath(parent));
parent = parent.parents('.doc-field:first');
}
return _.compact(nodes).reverse();
}
// TODO refactor handlers so that they dont stack up as the tree gets bigger
function handleTreeClick(e) {
var clicked = $(e.target);
if(clicked.hasClass('expand')) return;
if (clicked.children('.array').length > 0) {
var field = clicked;
} else if (clicked.siblings('.array').length > 0) {
var field = clicked.parents('.doc-field:first');
} else {
var field = clicked.parents('.array').parents('.doc-field:first');
}
$('.chosen').removeClass('chosen');
field.addClass('chosen');
return false;
}
var createTreeNode = {
"string": function (obj, key) {
var val = $('<div class="doc-value string-type"></div>');
if (obj[key].length > 45) {
val.append($('<span class="string-type"></span>')
.text(obj[key].slice(0, 45)))
.append(
$('<span class="expand">...</span>')
.click(function () {
val.html('')
.append($('<span class="string-type"></span>')
.text(obj[key].length ? obj[key] : " ")
)
})
)
}
else {
var val = $('<div class="doc-value string-type"></div>');
val.append(
$('<span class="string-type"></span>')
.text(obj[key].length ? obj[key] : " ")
)
}
return val;
}
, "number": function (obj, key) {
var val = $('<div class="doc-value number"></div>')
val.append($('<span class="number-type">' + obj[key] + '</span>'))
return val;
}
, "null": function (obj, key) {
var val = $('<div class="doc-value null"></div>')
val.append($('<span class="null-type">' + obj[key] + '</span>'))
return val;
}
, "boolean": function (obj, key) {
var val = $('<div class="fue null"></div>')
val.append($('<span class="null-type">' + obj[key] + '</span>'))
return val;
}
, "array": function (obj, key, indent) {
if (!indent) indent = 1;
var val = $('<div class="doc-value array"></div>')
$('<span class="array-type">[</span><span class="expand" style="float:left">...</span><span class="array-type">]</span>')
.click(function (e) {
var n = $(this).parent();
var cls = 'sub-'+key+'-'+indent
n.html('')
n.append('<span style="padding-left:'+((indent - 1) * 10)+'px" class="array-type">[</span>')
for (i in obj[key]) {
var field = $('<div class="doc-field"></div>').click(handleTreeClick);
n.append(
field
.append('<div class="array-key '+cls+'" >'+i+'</div>')
.append(createTreeNode[getType(obj[key][i])](obj[key], i, indent + 1))
)
}
n.append('<span style="padding-left:'+((indent - 1) * 10)+'px" class="array-type">]</span>')
$('div.'+cls).width(largestWidth('div.'+cls))
})
.appendTo($('<div class="array-type"></div>').appendTo(val))
return val;
}
, "object": function (obj, key, indent) {
if (!indent) indent = 1;
var val = $('<div class="doc-value object"></div>')
$('<span class="object-type">{</span><span class="expand" style="float:left">...</span><span class="object-type">}</span>')
.click(function (e) {
var n = $(this).parent();
n.html('')
n.append('<span style="padding-left:'+((indent - 1) * 10)+'px" class="object-type">{</span>')
for (i in obj[key]) {
var field = $('<div class="doc-field"></div>').click(handleTreeClick);
var p = $('<div class="id-space" style="margin-left:'+(indent * 10)+'px"/>');
var di = $('<div class="object-key">'+i+'</div>')
field.append(p)
.append(di)
.append(createTreeNode[getType(obj[key][i])](obj[key], i, indent + 1))
n.append(field)
}
n.append('<span style="padding-left:'+((indent - 1) * 10)+'px" class="object-type">}</span>')
di.width(largestWidth('div.object-key'))
})
.appendTo($('<div class="object-type"></div>').appendTo(val))
return val;
}
}
function renderTree(doc) {
var d = $('div#document-editor');
for (i in doc) {
var field = $('<div class="doc-field"></div>').click(handleTreeClick);
$('<div class="id-space" />').appendTo(field);
field.append('<div class="doc-key doc-key-base">'+i+'</div>')
field.append(createTreeNode[getType(doc[i])](doc, i));
d.append(field);
}
$('div.doc-key-base').width(largestWidth('div.doc-key-base'))
}
return {
inURL: inURL,
registerEmitter: registerEmitter,
listenFor: listenFor,
show: show,
hide: hide,
position: position,
render: render,
notify: notify,
observeExit: observeExit,
formatMetadata:formatMetadata,
getBaseURL:getBaseURL,
resetForm: resetForm,
delay: delay,
persist: persist,
lookupPath: lookupPath,
selectedTreePath: selectedTreePath,
renderTree: renderTree
};
}();