// -*- js2-basic-offset: 2; indent-tabs-mode: nil -*-
"use strict";
function prim2html(prim, limit) {
switch(typeof prim) {
case 'undefined':
return 'undefined';
case 'boolean':
return prim ? 'true' : 'false';
case 'number':
return String(prim);
case 'string':
if (prim.length > limit) {
prim = prim.substring(0, limit) + '...';
}
return escapeHTML(prim);
case 'array':
case 'object':
if (prim == null) {
return 'null';
} else if ($.isArray(prim)) {
return 'array'; /* TODO: implement */
} else {
return 'object'; /* TODO: implement */
}
default:
return 'ERROR';
}
}
function escapeHTML(str) {
return str.replace(/&/g, "&")
.replace(/"/g, """)
.replace(//g, ">");
}
var Groonga = {
key_type_list: ['Int8', 'UInt8', 'Int16', 'UInt16', 'Int32', 'UInt32',
'Int64', 'UInt64', 'Float', 'Time', 'ShortText',
'TokyoGeoPoint', 'WGS84GeoPoint'],
value_type_list: ['Object', 'Bool',
'Int8', 'UInt8', 'Int16', 'UInt16', 'Int32', 'UInt32',
'Int64', 'UInt64', 'Float', 'Time'],
column_type_list: ['Object', 'Bool',
'Int8', 'UInt8', 'Int16', 'UInt16', 'Int32', 'UInt32',
'Int64', 'UInt64', 'Float', 'Time', 'ShortText',
'Text', 'LongText', 'TokyoGeoPoint', 'WGS84GeoPoint'],
tokenizer_list: ['TokenDelimit', 'TokenUnigram', 'TokenBigram', 'TokenTrigram', 'TokenMecab'],
GRN_OBJ_PERSISTENT: (0x01<<15),
GRN_OBJ_TABLE_TYPE_MASK: (0x07),
GRN_OBJ_TABLE_HASH_KEY: (0x00),
GRN_OBJ_TABLE_PAT_KEY: (0x01),
GRN_OBJ_TABLE_NO_KEY: (0x03),
GRN_OBJ_KEY_WITH_SIS: (0x01<<6),
GRN_OBJ_KEY_NORMALIZE: (0x01<<7),
GRN_OBJ_COLUMN_TYPE_MASK: (0x07),
GRN_OBJ_COLUMN_SCALAR: (0x00),
GRN_OBJ_COLUMN_VECTOR: (0x01),
GRN_OBJ_COLUMN_INDEX: (0x02),
GRN_OBJ_COMPRESS_MASK: (0x07<<4),
GRN_OBJ_COMPRESS_NONE: (0x00<<4),
GRN_OBJ_COMPRESS_ZLIB: (0x01<<4),
GRN_OBJ_COMPRESS_LZO: (0x02<<4),
GRN_OBJ_WITH_SECTION: (0x01<<7),
GRN_OBJ_WITH_WEIGHT: (0x01<<8),
GRN_OBJ_WITH_POSITION: (0x01<<9)
};
$.widget("ui.paginate", {
version: "1.0",
options: {
total: 0,
nItemsPerPage: 10,
currentPage: 0,
nShowLinks: 10,
callback: null
},
_create: function() {
var that = this;
var element = this.element;
element.addClass("pager");
var total = this.options.total;
var nItemsPerPage = this.options.nItemsPerPage;
var currentPage = this.options.currentPage;
var nShowLinks = this.options.nShowLinks;
var lastPage = Math.floor((total - 1) / nItemsPerPage) + 1;
var start = currentPage - Math.floor(nShowLinks / 2);
start = (start < 1) ? 1 : start;
var end = start + nShowLinks - 1;
end = (end > lastPage) ? lastPage : end;
var callback = this.options.callback;
if (start > 1) {
element.append($('')
.addClass('pager')
.append($('')
.attr('href', '#')
.text('1')
.click(function () {callback(0)})));
element.append($('').text('....'));
}
for (var i = start; i <= end; i++) {
var page = $('').append($('')
.attr('href', '#')
.text(String(i))
.click(function () {
callback(Number($(this).text()) - 1);
}));
if (i == currentPage) {
page.addClass('pager-current');
} else {
page.addClass('pager');
}
element.append(page);
}
if (end < lastPage) {
element.append($('')
.text('....'));
element.append($('')
.addClass('pager')
.append($('')
.attr('href', '#')
.text(String(lastPage))
.click(function () {callback(lastPage - 1);})));
}
}
});
function GroongaAdmin() {
this.current_table = null;
this.statusTimer = null;
this.semaphore = new Array();
this.current_status = 0;
this.reload_record_func = function(){};
var that = this;
this._initializeTabs();
$('#tab-tablelist-link').click(function() {
that.tablelist();
});
$('#tab-columnlist-link').click(function() {
that.columnlist(that.current_table);
});
$('#tab-createrecord-link').click(function() {
that.update_createrecord(that.current_table);
});
$('#tab-recordlist-link').click(function() {
that.reload_record_func();
});
$('#createtable-add-table').click(function() {
that.createtable();
});
$('#createrecord-add-record').click(function() {
that.createrecord();
});
$('#createcolumn-add-column').click(function() {
that.createcolumn();
});
$('#recordlist-remove-record').click(function() {
that.removerecord();
});
$('#columnlist-remove-column').click(function() {
that.removecolumn();
});
$('#tablelist-remove-table').click(function() {
that.removetable();
});
$('#tab-recordlist-form').submit(function() {
if ($('#table-tab-recordlist-full-checkbox').attr('checked')) {
// full
var d = {
'table': that.current_table
}
$.each(that.SELECT_PARAMS, function(i, val) {
var e = $('#tab-recordlist-' + val);
if (e.val()) {
d[val] = e.val();
}
});
that.recordlist(d, true);
} else {
// simple
that.recordlist_simple(
that.current_table,
$('#tab-recordlist-simplequery').val(),
$('#tab-recordlist-simplequerytype').val(),
1);
}
return false;
});
this._initializeSideMenu();
this.update_tablelist();
var e1 = $('#createtable-key-type-builtin');
$.each(Groonga.key_type_list, function(i, val) {
e1.append($('').val(val).text(val));
});
e1 = $('#createtable-value-type-builtin');
e1.append($('').val('').text('なし'));
$.each(Groonga.value_type_list, function(i, val) {
e1.append($('').val(val).text(val));
});
e1 = $('#createtable-default-tokenizer-builtin');
e1.append($('').val('').text('なし'));
$.each(Groonga.tokenizer_list, function(i, val) {
e1.append($('').val(val).text(val));
});
e1 = $('#createcolumn-type-builtin');
$.each(Groonga.column_type_list, function(i, val) {
e1.append($('').val(val).text(val));
});
$('#tab-recordlist-simplequerytype').change(function() {
if ($(this).val() == 'scorer') {
$('#tab-recordlist-incremental').hide();
$('#tab-recordlist-incremental-label').hide();
} else {
$('#tab-recordlist-incremental').show();
$('#tab-recordlist-incremental-label').show();
}
$('#tab-recordlist-incremental').change();
var selectedOption = $(this).find(':selected');
$('#tab-recordlist-simplequery').attr(
'placeholder', selectedOption.data('placeholder')
);
}).change();
$('#table-tab-recordlist-full-checkbox').change(function() {
if ($(this).attr('checked')) {
$('#table-tab-recordlist-form-simple').hide();
$('#table-tab-recordlist-form-full').show();
} else {
$('#table-tab-recordlist-form-simple').show();
$('#table-tab-recordlist-form-full').hide();
}
}).change();
$('#tab-recordlist-incremental').change(function() {
$('#tab-recordlist-simplequery').unbind('keyup');
if ($(this).attr('checked') &&
$('#tab-recordlist-simplequerytype').val() != 'scorer') {
$('#tab-recordlist-simplequery').keyup(function(e) {
that.recordlist_simple(
that.current_table,
$('#tab-recordlist-simplequery').val(),
$('#tab-recordlist-simplequerytype').val(),
1,
true);
});
}
}).change();
$('#createcolumn-type').change(function(e) {
var s = $('#createcolumn-type-table option:selected');
var cs = $('#createcolumn-source');
if (s.length > 0) {
cs.empty().removeAttr('disabled');
that.showloading(
$.ajax({
url: '/d/column_list',
data: {'table': s.val()},
dataType: 'json',
success: function(d) {
if(that.validateajax(d) < 0) { return; }
var idx;
var b = d[1];
$.each(b[0], function(i, val) {
if (val[0] == 'name') { idx = i; }
});
if (idx) {
b.shift();
$.each(b, function(i, val) {
cs.append($('').val(val[idx]).text(val[idx]));
});
}
that.hideloading();
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
that.errorloading(XMLHttpRequest);
}
})
);
} else {
cs.empty().attr('disabled', 'disabled');
}
});
this.recordlist_count = 30;
};
jQuery.extend(GroongaAdmin.prototype, {
SELECT_PARAMS: [
'match_columns', 'query', 'filter',
'scorer',
'output_columns',
'sortby', 'offset', 'limit',
'drilldown',
'drilldown_output_columns',
'drilldown_sortby', 'drilldown_offset', 'drilldown_limit'
],
_initializeTabs: function() {
this._initializeDatabaseTab();
this._initializeTableTab();
this._initializeSuggestTab();
this._selectTab("database");
},
_initializeDatabaseTab: function() {
var that = this;
this._$databaseTabs = $('#database-tabs').tabs({
show: function(e, ui) {
that.stop_status_timer();
if (ui.panel.id == 'database-tab-summary') {
that.start_status_timer();
}
}
});
},
_initializeTableTab: function() {
this._$tableTabs = $('#table-tabs').tabs({
show: function(e, ui) {
}
});
},
_initializeSuggestTab: function() {
var that = this;
this._$suggestTabs = $('#suggest-tabs').tabs({
show: function(e, ui) {
}
});
this._initializeSuggestDatasetComplete();
this._initializeSuggestQueryComplete();
this._initializeSuggestSubmit();
this._initializeSuggestResult();
},
_initializeSuggestDatasetComplete: function() {
var that = this;
var $dataset = $("#suggest-dataset");
this._$suggestDataset = $dataset;
$dataset.autocomplete({
minLength: 0,
source: function (request, response) {
var datasets = [];
$.each(that._tables, function(i, table_name) {
var suggestTableMatch = /^item_(.+)$/.exec(table_name);
if (suggestTableMatch) {
var dataset = suggestTableMatch[1];
datasets.push(dataset);
}
});
datasets = $.ui.autocomplete.filter(datasets, request.term);
response(datasets);
}
});
$dataset.focus(function (event) {
$dataset.autocomplete("search", $dataset.val());
});
},
_suggestParameters: function(query, dataset, type) {
var nItemsPerPage = 30;
return {
query: query,
types: type,
table: "item_" + dataset,
column: "kana",
offset: 0,
limit: nItemsPerPage,
};
},
_initializeSuggestQueryComplete: function() {
var that = this;
this._$suggestQuery = $("#suggest-query").autocomplete({
source: function (request, response) {
var $dataset = $("#suggest-dataset");
var dataset = $dataset.val();
$("#suggest-submit").click();
$.ajax({
url: "/d/suggest",
data: that._suggestParameters(request.term, dataset, "complete"),
dataType: "jsonp",
success: function (data, textStatus, jqXHR) {
var completions = data[1]["complete"];
var items = [];
if (completions && completions.length > 2) {
completions.shift();
completions.shift();
$.each(completions, function(i, item) {
var key = item[0];
items.push(key);
if (items.length >= 3) {
return false;
}
return true;
});
}
response(items);
},
error: function(jqXHR, textStatus, errorThrown) {
}
});
}
});
},
_initializeSuggestSubmit: function() {
var that = this;
$("#suggest-submit").click(function (event) {
var dataset = $("#suggest-dataset").val();
var query = $("#suggest-query").val();
var type = that._suggestResultType;
var parameters = that._suggestParameters(query, dataset, type);
$.ajax({
url: "/d/suggest",
data: parameters,
dataType: "jsonp",
success: function (data, textStatus, jqXHR) {
var response = data[1][type];
response.shift();
var $result = $("#suggest-result-tab-" + type);
$result
.empty()
.append($("
").append(that._createResultTable(response)));
},
error: function(jqXHR, textStatus, errorThrown) {
}
});
});
},
_initializeSuggestResult: function() {
var that = this;
$("#suggest-result-tabs").tabs({
show: function (event, ui) {
that._suggestResultType = ui.panel.id.replace(/^suggest-result-tab-/, "");
$("#suggest-submit").click();
}
});
},
_selectTab: function(name) {
this.stop_status_timer();
this._$databaseTabs.hide();
this._$tableTabs.hide();
this._$suggestTabs.hide();
switch (name) {
case "table":
this._$tableTabs.show();
break;
case "suggest":
this._$suggestTabs.show();
break;
case "database":
default:
this._$databaseTabs.show();
break;
}
},
_initializeSideMenu: function () {
var that = this;
$('#side-menu-summary').click(function() {
that.current_table = null;
that._selectTab("database");
that._$databaseTabs.tabs("select", "#database-tab-summary");
});
$('#side-menu-suggest').click(function() {
that.current_table = null;
that._selectTab("suggest");
});
},
start_status_timer: function() {
var that = this;
this.stop_status_timer();
this.status();
this.statusTimer = setInterval(function() {that.status()}, 1000);
},
change_status_timer: function(time) {
var that = this;
this.stop_status_timer();
this.statusTimer = setInterval(function() {that.status()}, time);
},
stop_status_timer: function() {
if (this.statusTimer) {
clearInterval(this.statusTimer);
this.statusTimer = null;
}
},
_createResultTable: function (result, options) {
var that = this;
if (!options) {
options = {};
}
var table = $('
');
if ($.isArray(result)) {
var nEntries = result.length;
if (nEntries >= 1) {
var thead = $('');
table.append(thead);
var line = result[0];
if ($.isArray(line)) {
var tr = $('
');
thead.append(tr);
var m = line.length;
if (options.check) {
tr.append($('
'));
}
}
var tbody = $('');
table.append(tbody);
for (var i = 1; i < nEntries; i++) {
line = result[i];
if ($.isArray(line)) {
var tr = $('
');
table.append(tr);
var m = line.length;
switch(options.check) {// チェックボックスの値を何にするか
case 1: // 1番目の要素(レコード一覧の_id等)
case 2: // 2番目の要素(テーブル・カラム一覧のname等)
var td = $('
');
tr.append(td);
td.append($('')
.attr("type", "checkbox")
.attr("value", line[options.check-1]));
break;
}
for (var j = 0; j < m; j++) {
var td = $('
');
tr.append(td);
td.append(prim2html(line[j], 128));
}
switch(options.button) {
case 1: // Edit record
var td = $('
');
tr.append(td);
td.append($('')
.attr("type", "button")
.attr("value", "詳細")
.attr("data-table-name", line[1])
.click(function () {
var tableName = $(this).attr("data-table-name");
$("#side-menu-tablelist-link-" + tableName).click();
}));
break;
}
}
}
}
}
return table;
},
show_edit_record: function(id) {
$('#table-tabs').tabs('select', 2);
this.update_createrecord(this.current_table, id);
},
format_unix_time: function(unix_time) {
var date = new Date();
date.setTime(unix_time * 1000);
return date.toLocaleString();
},
format_duration: function(duration_in_seconds) {
var duration = "";
var days = Math.floor(duration_in_seconds / 3600 / 24);
var hours = Math.floor(duration_in_seconds / 3600 % 24);
var minutes = Math.floor(duration_in_seconds / 60 % 60);
var seconds = Math.floor(duration_in_seconds % 60);
if (days > 0) {
duration += days;
if (days == 1) {
duration += " day, ";
} else {
duration += " days, ";
}
}
if (days > 0 || hours > 0) {
duration += hours + ":" + minutes + ":" + seconds;
} else if (minutes > 0) {
duration += minutes + ":" + seconds;
} else {
duration += seconds;
}
return duration;
},
maxThroughput: 0,
lastNQueries: -1,
keepLastNData: 100,
throughputData: [],
throughputChart: null,
updateThroughputChart: function(statusData) {
var maxThroughputUpdated = false;
if (this.lastNQueries >= 0) {
var throughput = statusData.n_queries - this.lastNQueries;
this.throughputData.push(throughput);
if (this.maxThroughput < throughput) {
this.maxThroughput = throughput;
maxThroughputUpdated = true;
}
}
if (this.throughputData.length > this.keepLastNData) {
this.throughputData.shift();
}
if (!this.throughputChart) {
this.throughputChart = $.plot($("#throughput-chart"),
[[]],
{xaxis: {min: -(this.keepLastNData - 1),
max: 0},
yaxis: {min: 0}});
}
var that = this;
var chartSeries = $.map(this.throughputData, function(n, i) {
return [[-(that.throughputData.length - i) + 1, n]];
});
this.throughputChart.setData([chartSeries]);
if (maxThroughputUpdated) {
this.throughputChart.setupGrid();
}
this.throughputChart.draw();
this.lastNQueries = statusData.n_queries;
},
status: function() {
if (this.current_status > 0) { return; }
this.current_status++;
var that = this;
$.ajax({
url: '/d/status',
data: {},
dataType: 'json',
success: function(b) {
that.current_status--;
if (!b) {
that.change_status_timer(10000);
return;
}
var d = b[1];
$('#status-starttime').text(that.format_unix_time(d.starttime));
$('#status-uptime').text(that.format_duration(d.uptime));
$('#status-n-queries').text(d.n_queries);
$('#status-cache-hit-rate').text(d.cache_hit_rate);
that.updateThroughputChart(d);
that.change_status_timer(1000);
},
error: function() {
that.current_status--;
that.change_status_timer(10000);
}
});
},
update_tablelist: function() {
var that = this;
this._tables = [];
this.showloading(
$.ajax({
url: '/d/table_list',
data: {},
dataType: 'json',
success: function(d) {
if (that.validateajax(d) < 0) { return; }
d.shift();
var tl = $('#side-menu-tablelist').empty();
var tt = $('#createtable-key-type-table').empty();
var vt = $('#createtable-value-type-table').empty();
var ct = $('#createcolumn-type-table').empty();
var b = d.shift();
b.shift();
$.each(b, function(i, val) {
var table_name = val[1];
that._tables.push(table_name);
tl.append(
$('').append(
$('')
.attr('id', 'side-menu-tablelist-link-' + table_name)
.attr('href', '#side-menu-tablelist-' + table_name)
.text(table_name)
.click(function() {
that.current_table = table_name;
$('#database-tabs').hide();
$('#suggest-tabs').hide();
that.stop_status_timer();
$('#table-tabs').show();
that.columnlist(table_name);
$('#tab-recordlist-simplequery').val('');
that.recordlist_simple(table_name, null, null, 1);
that.update_createrecord(that.current_table);
})
)
);
tt.append($('').val(val[1]).text(val[1]));
vt.append($('').val(val[1]).text(val[1]));
ct.append($('').val(val[1]).text(val[1]));
});
that.hideloading();
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
that.errorloading(XMLHttpRequest);
}
})
);
},
tablelist: function() {
$('#tab-tablelist-table').empty();
var that = this;
this.showloading(
$.ajax({
url: '/d/table_list',
data: {},
dataType: 'json',
success: function(d) {
if (that.validateajax(d) < 0) { return; }
var b = d[1];
var table = that._createResultTable(b, {check: 2, button: 2});
$('#tab-tablelist-table').append($('').text('テーブル一覧')).append(table);
that.hideloading();
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
that.errorloading(XMLHttpRequest);
}
})
);
},
recordlist_simple: function(table_name, simplequery, simplequery_type, page, hide_dialog) {
var d = {
'table': table_name,
'offset': (page - 1) * this.recordlist_count,
'limit': this.recordlist_count
}
switch (simplequery_type) {
case 'query':
case 'filter':
case null:
if (simplequery) {
d[simplequery_type] = simplequery;
}
this.recordlist(d, true, hide_dialog);
break;
}
},
recordlist: function(params, show_pager, hide_dialog) {
var that = this;
this.reload_record_func = function(){
that.recordlist(params, show_pager, hide_dialog);
};
this.showloading(
$.ajax({
url: '/d/select',
data: params,
dataType: 'json',
success: function(d) {
if (that.validateajax(d, hide_dialog) < 0) { return; }
var rc = d.shift();
if (rc[0] != 0) {
alert('error: ' + rc[3]);
that.hideloading();
return false;
}
var body = d.shift();
var recs = body.shift();
var all_count = recs.shift()[0];
var pager;
if (show_pager) {
var offset = params['offset'] || 0;
var rows = params['limit'] || 10;
if (rows < 0){
rows = all_count + parseInt(rows) + 1;
}
if (rows != '' && !parseInt(rows)) {
pager = $('');
} else {
pager = $("");
pager.paginate({
total: all_count,
nItemsPerPage: rows,
currentPage: Math.floor(offset/rows)+1,
callback: function(page) {
params['offset'] = page * rows;
that.recordlist(params, true, false);
return false;
}
});
}
} else {
pager = $('');
}
$('#tab-recordlist-table')
.empty()
.append($('').text('レコード一覧: ' + params['table']))
.append($('').text('総件数: ' + all_count))
.append(pager.clone(true))
.append($('').append(that._createResultTable(recs, {check: 1, button: 1})))
.append(pager);
that.hideloading();
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
that.errorloading(XMLHttpRequest, hide_dialog);
}
})
,hide_dialog);
},
columnlist: function(table_name) {
var that = this;
$('#tab-columnlist-table').empty();
this.showloading(
$.ajax({
url: '/d/column_list',
data: {'table': table_name},
dataType: 'json',
success: function(d) {
if (that.validateajax(d) < 0) { return; }
var b = d[1];
var table = that._createResultTable(b, {check: 2});
$('#tab-columnlist-table')
.append($('').text('カラム一覧: ' + table_name))
.append(table);
that.hideloading();
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
that.errorloading(XMLHttpRequest);
}
})
);
},
add_record_inputbox: function(type, value) {
var inputbox = null;
switch(type){
case "Bool":
inputbox = $('')
.attr("type","checkbox")
.attr("value","true");
if (value) {
inputbox.attr("checked","");
}
break;
case "UInt8":
case "UInt16":
case "UInt32":
case "UInt64":
case "Int8":
case "Int16":
case "Int32":
case "Int64":
case "Float":
inputbox = $('')
.attr("type", "text")
.val(isNaN(value) ? "" : value);
break;
case "Text":
case "ShortText":
case "LongText":
inputbox = $('')
.attr("cols", "50")
.attr("rows", "2")
.text(value ? value : "");
break;
case "TokyoGeoPoint":
case "WGS84GeoPoint":
case "Time":
inputbox = $('')
.attr("type", "text")
.attr("size", "40")
.val(value ? value : "");
break;
case "Object":
inputbox = $('')
.attr("type", "text")
.attr("disabled", "disabled");
break;
default:
inputbox = $('')
.attr("type", "text")
.val(value ? value : "");
}
inputbox.addClass('column_values');
return inputbox;
},
add_record_deletebutton: function(){
var ret =
$('')
.append("[×]")
.css('cursor', 'pointer')
.click(function() {
$(this).prev().remove();
$(this).next().remove();
$(this).remove();
});
return ret;
},
update_createrecord_loadcomplete: function(d_sel, d_col) {
var that = this;
var b = d_sel[1][0];
var columns = $('
');
var listofs = b[1].length - (d_col[1].length - 1);
for (var i = 1; i < b[1].length; i++) {
var line = b[1][i];
var value = null;
if (b[2]) value = b[2][i];
if ($.isArray(line)) {
var tr = $('