HAYASHI Kentaro
null+****@clear*****
Wed Jul 16 21:37:11 JST 2014
HAYASHI Kentaro 2014-07-16 21:37:11 +0900 (Wed, 16 Jul 2014) New Revision: bba9a94980f222779a6a4119acbfbb0e0243f8fd https://github.com/groonga/groonga/commit/bba9a94980f222779a6a4119acbfbb0e0243f8fd Merged a4a867f: Merge pull request #182 from kenhys/use-english-as-default-lauguage Message: admin: add Japanese admin interface It is very silly way to localize, but it is better than none at all. Improving admin interface task is just began, so it will be replaced by more sophisticated way in the future. Added files: data/html/admin/index.ja.html data/html/admin/js/groonga-admin.ja.js Added: data/html/admin/index.ja.html (+297 -0) 100644 =================================================================== --- /dev/null +++ data/html/admin/index.ja.html 2014-07-16 21:37:11 +0900 (c49de2b) @@ -0,0 +1,297 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html lang="ja"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> +<meta http-equiv="Content-Style-Type" content="text/css"> +<meta http-equiv="Content-Script-Type" content="text/javascript"> +<meta name="robots" content="noindex,nofollow,noarchive"> +<title>groonga admin</title> +<link rel="shortcut icon" href="favicon.ico"> +<link rel="icon" href="favicon.png"> +<link rel="stylesheet" type="text/css" href="css/groonga-admin.css"> +<link rel="stylesheet" type="text/css" href="css/redmond/jquery-ui-1.8.18.custom.css"> +<script type="text/javascript" src="js/jquery-1.7.2.min.js"></script> +<script type="text/javascript" src="js/jquery-ui-1.8.18.custom.min.js"></script> +<script type="text/javascript" src="js/jquery.flot-0.7.min.js"></script> +<script type="text/javascript" src="js/groonga-admin.js"></script> +</head> +<body> +<div id="header"> +</div> +<div id="body"> + <table> + <tr> + <td id="left-column"> + <div id="side-menu"> + <h2>画面一覧</h2> + <ul id="side-menu-view-list"> + <li><a href="#side-menu-summary" id="side-menu-summary">サマリー</a></li> + <li><a href="#side-menu-suggest" id="side-menu-suggest">サジェスト</a></li> + </ul> + <h2>テーブル一覧</h2> + <ul id="side-menu-tablelist" /> + </div> + </td> + + <td id="right-column"> + <!-- database view --> + <div id="database-tabs"> + <ul> + <li><a href="#database-tab-summary">サマリー</a></li> + <li><a href="#database-tab-tablelist" id="tab-tablelist-link">テーブル一覧</a></li> + <li><a href="#database-tab-createtable">テーブル作成</a></li> + </ul> + <div id="database-tab-summary"> + <p> + groongaの管理ツールです。 + </p> + <ul> + <li>開始時間: <span id="status-starttime"></span></li> + <li>uptime: <span id="status-uptime"></span></li> + <li>クエリ数: <span id="status-n-queries"></span></li> + <li>キャッシュヒット率: <span id="status-cache-hit-rate"></span></li> + </ul> + <div id="throughput-chart" style="height: 300px; max-width: 500px;"> + </div> + </div> + <div id="database-tab-tablelist"> + <div id="tab-tablelist-table"> + </div> + <input type="button" id="tablelist-remove-table" value="選択テーブル削除"> + </div> + <div id="database-tab-createtable"> + <table> + <tr> + <td> + <label for="createtable-name">テーブル名</label> + </td> + <td> + <input type="text" id="createtable-name"> + </td> + </tr> + <tr> + <td> + 主キー + </td> + <td> + <label for="createtable-key-type">keyの型:</label> + <select id="createtable-key-type"> + <optgroup label="組み込み型" id="createtable-key-type-builtin"> + </optgroup> + <optgroup label="テーブル" id="createtable-key-type-table"> + </optgroup> + </select> + <label for="createtable-key-index">keyのインデックス種類:</label> + <select id="createtable-key-index"> + <option value="GRN_OBJ_TABLE_PAT_KEY">パトリシア木</option> + <option value="GRN_OBJ_TABLE_HASH_KEY">ハッシュテーブル</option> + <option value="GRN_OBJ_TABLE_NO_KEY">キーなし</option> + </select> + </td> + </tr> + <tr> + <td> + フラグ + </td> + <td id="createtable-flags"> + <input type="checkbox" value="GRN_OBJ_PERSISTENT" checked>永続化</input> + <input type="checkbox" value="GRN_OBJ_KEY_NORMALIZE">key文字列の正規化</input> + <input type="checkbox" value="GRN_OBJ_KEY_WITH_SIS">key文字列のsuffix登録</input> + </td> + </tr> + <tr> + <td> + valueの型 + </td> + <td> + <select id="createtable-value-type"> + <optgroup label="組み込み型" id="createtable-value-type-builtin"> + </optgroup> + <optgroup label="テーブル" id="createtable-value-type-table"> + </optgroup> + </select> + </td> + </tr> + <tr> + <td> + デフォルトトークナイザ + </td> + <td> + <select id="createtable-default-tokenizer"> + <optgroup label="組み込み" id="createtable-default-tokenizer-builtin"> + </optgroup> + </select> + </td> + </tr> + </table> + <input type="button" id="createtable-add-table" value="テーブル追加"> + </div> + </div> + + <!-- table view --> + <div id="table-tabs"> + <ul> + <li><a href="#table-tab-recordlist" id="tab-recordlist-link">レコード一覧</a></li> + <li><a href="#table-tab-columnlist" id="tab-columnlist-link">カラム一覧</a></li> + <li><a href="#table-tab-createrecord" id="tab-createrecord-link">レコード作成</a></li> + <li><a href="#table-tab-createcolumn">カラム作成</a></li> + </ul> + <div id="table-tab-recordlist"> + <input type="checkbox" id="table-tab-recordlist-full-checkbox" /><label for="table-tab-recordlist-full-checkbox">管理モード</label> + <form id="tab-recordlist-form"> + <div id="table-tab-recordlist-form-simple"> + <label for="tab-recordlist-simplequery">検索クエリ: </label><input type="text" id="tab-recordlist-simplequery"> + <select id="tab-recordlist-simplequerytype"> + <option value="query" data-placeholder="例)column:@value">query</option> + <option value="filter" data-placeholder="例)column == "value"">filter</option> + </select> + <input type="checkbox" id="tab-recordlist-incremental" /><label for="tab-recordlist-incremental" id="tab-recordlist-incremental-label">インクリメンタル検索</label> + </div> + <div id="table-tab-recordlist-form-full"> + <table> + <tr><td>match_columns</td><td><input type="text" id="tab-recordlist-match_columns" /></td></tr> + <tr><td>query</td><td><input type="text" id="tab-recordlist-query" /></td></tr> + <tr><td>filter</td><td><input type="text" id="tab-recordlist-filter" /></td></tr> + <tr><td>scorer</td><td><input type="text" id="tab-recordlist-scorer" /></td></tr> + <tr><td>sortby</td><td><input type="text" id="tab-recordlist-sortby" /></td></tr> + <tr><td>output_columns</td><td><input type="text" id="tab-recordlist-output_columns" /></td></tr> + <tr><td>offset</td><td><input type="text" id="tab-recordlist-offset" /></td></tr> + <tr><td>limit</td><td><input type="text" id="tab-recordlist-limit" /></td></tr> + <tr><td>drilldown</td><td><input type="text" id="tab-recordlist-drilldown" /></td></tr> + <tr><td>drilldown_sortby</td><td><input type="text" id="tab-recordlist-drilldown_sortby" /></td></tr> + <tr><td>drilldown_output_columns</td><td><input type="text" id="tab-recordlist-drilldown_output_columns" /></td></tr> + <tr><td>drilldown_offset</td><td><input type="text" id="tab-recordlist-drilldown_offset" /></td></tr> + <tr><td>drilldown_limit</td><td><input type="text" id="tab-recordlist-drilldown_limit" /></td></tr> + </table> + </div> + <input type="submit" id="tab-recordlist-submit" value="検索"/> + </form> + <div id="tab-recordlist-table"> + </div> + <input type="button" id="recordlist-remove-record" value="選択レコード削除"> + </div> + <div id="table-tab-columnlist"> + <div id="tab-columnlist-table"> + </div> + <input type="button" id="columnlist-remove-column" value="選択カラム削除"> + </div> + <div id="table-tab-createrecord"> + <table id="table-createrecord"> + </table> + <input type="button" id="createrecord-add-record" value="レコード追加"> + </div> + <div id="table-tab-createcolumn"> + <table> + <tr> + <td> + <label for="createcolumn-name">カラム名</label> + </td> + <td> + <input type="text" id="createcolumn-name"> + </td> + </tr> + <tr> + <td> + 設定 + </td> + <td> + <label for="createcolumn-type">型:</label> + <select id="createcolumn-type"> + <optgroup label="組み込み型" id="createcolumn-type-builtin"> + </optgroup> + <optgroup label="テーブル" id="createcolumn-type-table"> + </optgroup> + </select> + + <label for="createcolumn-source">テーブル型の場合カラム</label> + <select id="createcolumn-source" disabled> + </select> + + <label for="createcolumn-column-type">カラム種別:</label> + <select id="createcolumn-column-type"> + <option value="GRN_OBJ_COLUMN_SCALAR">スカラ</option> + <option value="GRN_OBJ_COLUMN_VECTOR">ベクタ</option> + <option value="GRN_OBJ_COLUMN_INDEX">転置インデックス</option> + </select> + + <label for="createcolumn-compress">圧縮:</label> + <select id="createcolumn-compress"> + <option value="GRN_OBJ_COMPRESS_NONE">圧縮なし</option> + <option value="GRN_OBJ_COMPRESS_ZLIB">zlib</option> + <option value="GRN_OBJ_COMPRESS_LZO">lzo</option> + </select> + </td> + </tr> + <tr> + <td> + フラグ + </td> + <td id="createcolumn-flags"> + <input type="checkbox" value="GRN_OBJ_PERSISTENT" checked>永続化</input> + </td> + </tr> + <tr> + <td> + 転置インデックス用フラグ + </td> + <td id="createcolumn-ii-flags"> + <input type="checkbox" value="GRN_OBJ_WITH_SECTION">段落情報を含める</input> + <input type="checkbox" value="GRN_OBJ_WITH_WEIGHT">重みを含める</input> + <input type="checkbox" value="GRN_OBJ_WITH_POSITION">位置情報を含める</input> + </td> + </tr> + </table> + <input type="button" id="createcolumn-add-column" value="カラム追加"> + </div> + </div> + + <!-- suggest view --> + <div id="suggest-tabs"> + <ul> + <li><a href="#suggest-tab-search">検索</a></li> + </ul> + <div id="suggest-tab-search"> + <form id="suggest-tab-search-form"> + <p> + <label for="suggest-dataset">データセット: </label> + <input type="text" id="suggest-dataset"> + </p> + <label for="suggest-query">検索クエリ: </label> + <input type="text" id="suggest-query"> + <input type="button" id="suggest-submit" value="検索" /> + </form> + <div id="suggest-result-tabs"> + <ul> + <li><a href="#suggest-result-tab-suggest">提案</a></li> + <li><a href="#suggest-result-tab-complete">補完</a></li> + <li><a href="#suggest-result-tab-correct">補正</a></li> + </ul> + <div id="suggest-result-tab-suggest"> + </div> + <div id="suggest-result-tab-complete"> + </div> + <div id="suggest-result-tab-correct"> + </div> + </div> + </div> + </div> + </td> + </tr> + </table> +</div> +<div id="footer"> +Powered by <a href="http://jquery.com/">jQuery</a> and <a href="http://jqueryui.com/">jQuery UI</a>. +</div> +<script type="text/javascript"> +$(function() { + var admin = new GroongaAdmin(); + $(location.hash).click(); + $.ajaxSetup({ + timeout: 10000, + cache: false + }); +}); +</script> +</body> +</html> Added: data/html/admin/js/groonga-admin.ja.js (+1372 -0) 100644 =================================================================== --- /dev/null +++ data/html/admin/js/groonga-admin.ja.js 2014-07-16 21:37:11 +0900 (c9d11f5) @@ -0,0 +1,1372 @@ +// -*- 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, "<") + .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($('<span />') + .addClass('pager') + .append($('<a />') + .attr('href', '#') + .text('1') + .click(function () {callback(0)}))); + element.append($('<span />').text('....')); + } + for (var i = start; i <= end; i++) { + var page = $('<span />').append($('<a />') + .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($('<span />') + .text('....')); + element.append($('<span />') + .addClass('pager') + .append($('<a />') + .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($('<option />').val(val).text(val)); + }); + + e1 = $('#createtable-value-type-builtin'); + e1.append($('<option />').val('').text('なし')); + $.each(Groonga.value_type_list, function(i, val) { + e1.append($('<option />').val(val).text(val)); + }); + + e1 = $('#createtable-default-tokenizer-builtin'); + e1.append($('<option />').val('').text('なし')); + $.each(Groonga.tokenizer_list, function(i, val) { + e1.append($('<option />').val(val).text(val)); + }); + + e1 = $('#createcolumn-type-builtin'); + $.each(Groonga.column_type_list, function(i, val) { + e1.append($('<option />').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($('<option />').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($("<div/>").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 = $('<table class="records"/>'); + if ($.isArray(result)) { + var nEntries = result.length; + if (nEntries >= 1) { + var thead = $('<thead/>'); + table.append(thead); + var line = result[0]; + if ($.isArray(line)) { + var tr = $('<tr/>'); + thead.append(tr); + var m = line.length; + if (options.check) { + tr.append($('<th/>')); + } + for (var j = 0; j < m; j++) { + var th = $('<th/>'); + tr.append(th); + th.append(prim2html(line[j][0], 128)); + th.append($('<br />')); + th.append(prim2html(line[j][1], 128)); + } + if (options.button) { + tr.append($('<th/>')); + } + } + var tbody = $('<tbody>'); + table.append(tbody); + for (var i = 1; i < nEntries; i++) { + line = result[i]; + if ($.isArray(line)) { + var tr = $('<tr>'); + table.append(tr); + var m = line.length; + switch(options.check) {// チェックボックスの値を何にするか + case 1: // 1番目の要素(レコード一覧の_id等) + case 2: // 2番目の要素(テーブル・カラム一覧のname等) + var td = $('<td/>'); + tr.append(td); + td.append($('<input/>') + .attr("type", "checkbox") + .attr("value", line[options.check-1])); + break; + } + for (var j = 0; j < m; j++) { + var td = $('<td/>'); + tr.append(td); + td.append(prim2html(line[j], 128)); + } + switch(options.button) { + case 1: // Edit record + var td = $('<td/>'); + tr.append(td); + td.append($('<input/>') + .attr("type", "button") + .attr("value", "編集") + .attr("data-record-id", line[0]) + .click(function () { + that.show_edit_record($(this).attr("data-record-id")); + })); + break; + case 2: // Table + var td = $('<td/>'); + tr.append(td); + td.append($('<input/>') + .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( + $('<li />').append( + $('<a />') + .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($('<option />').val(val[1]).text(val[1])); + vt.append($('<option />').val(val[1]).text(val[1])); + ct.append($('<option />').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($('<h1 />').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 = $('<span />'); + } else { + pager = $("<div/>"); + 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 = $('<span />'); + } + $('#tab-recordlist-table') + .empty() + .append($('<h1 />').text('レコード一覧: ' + params['table'])) + .append($('<p />').text('総件数: ' + all_count)) + .append(pager.clone(true)) + .append($('<div />').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($('<h1 />').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 = $('<input />') + .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 = $('<input />') + .attr("type", "text") + .val(isNaN(value) ? "" : value); + break; + case "Text": + case "ShortText": + case "LongText": + inputbox = $('<textarea />') + .attr("cols", "50") + .attr("rows", "2") + .text(value ? value : ""); + break; + case "TokyoGeoPoint": + case "WGS84GeoPoint": + case "Time": + inputbox = $('<input />') + .attr("type", "text") + .attr("size", "40") + .val(value ? value : ""); + break; + case "Object": + inputbox = $('<input />') + .attr("type", "text") + .attr("disabled", "disabled"); + break; + default: + inputbox = $('<input />') + .attr("type", "text") + .val(value ? value : ""); + } + inputbox.addClass('column_values'); + return inputbox; + }, + add_record_deletebutton: function(){ + var ret = + $('<span />') + .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 = $('<tbody />'); + 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 = $('<tr/ >') + .addClass('create-record-columns') + .append( + $('<td />') + .addClass('columnname') + .append(prim2html(line[0], 128)) + ) + .append( + $('<td />') + .addClass('columntype') + .append("(") + .append($('<span />') + .append(prim2html(line[1], 128)) + ) + .append(")") + ); + var inputtd = $('<td />').addClass('columnval'); + if (i >= listofs && d_col[1][i - listofs + 1][4].indexOf("COLUMN_VECTOR") >= 0){ + var type = line[1]; + if (value != null) { + for (var j = 0; j < value.length; j++) { + inputtd + .append(this.add_record_inputbox(line[1], value[j])) + .append(this.add_record_deletebutton()) + .append('<br />'); + } + } + inputtd + .append($('<span />') + .append("[値を追加]") + .css('cursor', 'pointer') + .click(function() { + var target = $(this).parent(); + target + .append(that.add_record_inputbox($(this).parent().prev().children().text())) + .append(that.add_record_deletebutton()) + .append("<br />"); + $(this).appendTo(target); + }) + ); + } else { + inputtd.append(this.add_record_inputbox(line[1], value)); + if (line[0] == "_key" && value != null) { + inputtd.children().attr("disabled", "disabled"); + } + } + tr.append(inputtd); + columns.append(tr); + } + } + $("#table-createrecord").append(columns); + this.hideloading(); + }, + update_createrecord: function(table_name, id) { + var that = this; + var d_sel = null; + var d_col = null; + $('#table-createrecord').empty(); + this.showloading( + $.ajax({ + url: '/d/select', + data: { + 'table' : table_name, + 'limit' : 1, + 'query' : '_id:' + id + }, + dataType: 'json', + success: function(d) { + if (that.validateajax(d) < 0) { return; } + d_sel = d; + if (d_col) { + that.update_createrecord_loadcomplete(d_sel, d_col); + } + }, + error: function(XMLHttpRequest, textStatus, errorThrown) { + that.errorloading(XMLHttpRequest); + } + }) + ); + this.showloading( + $.ajax({ + url: '/d/column_list', + data: { + 'table' : table_name + }, + dataType: 'json', + success: function(d) { + if (that.validateajax(d) < 0) { return; } + d_col = d; + if (d_sel) { + that.update_createrecord_loadcomplete(d_sel, d_col); + } + }, + error: function(XMLHttpRequest, textStatus, errorThrown) { + that.errorloading(XMLHttpRequest); + } + }) + ); + }, + createtable: function() { + var that = this; + var flags = 0; + $('#createtable-flags>input:checked').each(function() { + flags |= Groonga[$(this).val()]; + }); + flags |= Groonga[$('#createtable-key-index').val()]; + this.showloading( + $.ajax({ + url: '/d/table_create', + data: { + name: $('#createtable-name').val(), + 'flags': flags, + key_type: $('#createtable-key-type').val(), + value_type: $('#createtable-value-type').val(), + default_tokenizer: $('#createtable-default-tokenizer').val() + }, + dataType: 'json', + success: function(d) { + if (that.validateajax(d) < 0) { return; } + that.hideloading(); + alert('テーブルを作成しました。'); + that.update_tablelist(); + }, + error: function(XMLHttpRequest, textStatus, errorThrown) { + that.errorloading(XMLHttpRequest); + } + }) + ); + }, + createcolumn: function() { + var that = this; + var flags = 0; + $('#createcolumn-flags>input:checked').each(function() { + flags |= Groonga[$(this).val()]; + }); + $('#createcolumn-ii-flags>input:checked').each(function() { + flags |= Groonga[$(this).val()]; + }); + flags |= Groonga[$('#createcolumn-column-type').val()]; + flags |= Groonga[$('#createcolumn-column-compress').val()]; + var d = { + table: this.current_table, + name: $('#createcolumn-name').val(), + 'flags': flags, + type: $('#createcolumn-type').val() + }; + if ($('#createcolumn-source').val()) { + d['source'] = $('#createcolumn-source').val(); + } + this.showloading( + $.ajax({ + url: '/d/column_create', + data: d, + dataType: 'json', + success: function(d) { + if (that.validateajax(d) < 0) { return; } + that.hideloading(); + alert('カラムを作成しました。'); + }, + error: function(XMLHttpRequest, textStatus, errorThrown) { + that.errorloading(XMLHttpRequest); + } + }) + ); + }, + createrecord_getvalue: function(type, inputbox) { + switch(type){ + case "Bool": + if (inputbox.is('input:checked')) { + return true; + } else { + return false; + } + default: + return inputbox.val(); + } + }, + createrecord: function() { + var that = this; + var d = {}; + $('.create-record-columns').each(function() { + if (!$(this).children('.columnval').children().attr('disabled') + || $(this).children('.columnname').text() == "_key") { + var type = $(this).children('.columntype').children().text(); + if ($(this).children('.columnval').children('span').length) { + var arr = []; + $(this).children('.columnval').children('.column_values').each(function() { + arr.push(that.createrecord_getvalue(type, $(this))); + }); + d[$(this).children('.columnname').text()] = arr; + } else { + d[$(this).children('.columnname').text()] = + that.createrecord_getvalue(type, $(this).children('.columnval').children()); + } + } + }); + this.showloading( + $.ajax({ + url: '/d/load', + data: { + "table" : this.current_table, + "input_type" : "json", + "output_type" : "json", + "values" : JSON.stringify([d]) + }, + dataType: 'json', + success: function(d) { + if (that.validateajax(d) < 0) { return; } + that.hideloading(); + alert('レコードを作成しました。'); + }, + error: function(XMLHttpRequest, textStatus, errorThrown) { + that.errorloading(XMLHttpRequest); + } + }) + ); + }, + removerecord: function() { + var that = this; + var checklist = $("#tab-recordlist-table").find("input:checked"); + var completecount = checklist.length; + if (completecount > 0) { + $('<div />') + .append("選択した" + completecount + "件のレコードを削除しますか?") + .dialog({ + modal: true, + buttons: { + 'いいえ': function() { + $(this).dialog('close'); + }, + 'はい': function() { + $(this).dialog('close'); + checklist.each(function(i, val) { + that.showloading( + $.ajax({ + url: '/d/delete', + data: { + "table" : that.current_table, + "id" : val.value + }, + dataType: 'json', + success: function() { + if (--completecount == 0) { + $('#tab-recordlist-form').submit(); + alert('レコードを削除しました。'); + } else if (completecount < 0){ + that.hideloading(); + } + }, + error: function(XMLHttpRequest, textStatus, errorThrown) { + completecount = 0; + that.errorloading(XMLHttpRequest); + } + }) + ); + }); + } + } + }); + } + }, + removecolumn: function() { + var that = this; + var checklist = $("#tab-columnlist-table").find("input:checked"); + var completecount = checklist.length; + if (completecount) { + $('<div />') + .append("選択した" + completecount + "件のカラムを削除しますか?") + .dialog({ + modal: true, + buttons: { + 'いいえ': function() { + $(this).dialog('close'); + }, + 'はい': function() { + $(this).dialog('close'); + checklist.each(function(i, val) { + that.showloading( + $.ajax({ + url: '/d/column_remove', + data: { + "table" : that.current_table, + "name" : val.value + }, + dataType: 'json', + success: function() { + if (!(--completecount)) { + that.columnlist(that.current_table); + alert('カラムを削除しました。'); + } else if (completecount < 0){ + that.hideloading(); + } + }, + error: function(XMLHttpRequest, textStatus, errorThrown) { + completecount = 0; + that.errorloading(XMLHttpRequest); + } + }) + ); + }); + } + } + }); + } + }, + removetable: function() { + var that = this; + var checklist = $("#tab-tablelist-table").find("input:checked"); + var completecount = checklist.length; + if (completecount > 0) { + $('<div />') + .append("選択した" + completecount + "件のテーブルを削除しますか?") + .dialog({ + modal: true, + buttons: { + 'いいえ': function() { + $(this).dialog('close'); + }, + 'はい': function() { + $(this).dialog('close'); + checklist.each(function(i, val) { + that.showloading( + $.ajax({ + url: '/d/table_remove', + data: { + "name" : val.value + }, + dataType: 'json', + success: function() { + if (--completecount == 0) { + that.tablelist(); + that.update_tablelist(); + alert('テーブルを削除しました。'); + } else if (completecount < 0){ + that.hideloading(); + } + }, + error: function(XMLHttpRequest, textStatus, errorThrown) { + completecount = 0; + that.errorloading(XMLHttpRequest); + } + }) + ); + }); + } + } + }); + } + }, + showloading: function(obj, hide_dialog) { + var that = this; + if (obj == null) { return; } + this.semaphore[this.semaphore.length] = obj; + if ( $("#loadingdialog").size() > 0 || hide_dialog) { return; } + $("<div />") + .attr("id", "loadingdialog") + .attr("style", "text-align: center;") + .append($("<img />").attr("src", "images/loading.gif")) + .append(" Loading...") + .dialog({ + title: "", + width: 200, + height: 110, + minHeight: 110, + modal: true, + resizable: false, + draggable: false, + position: ["right", "bottom"], + autoOpen: false, + buttons: { + '中止': function() { + if (obj) { obj.abort(); } + that.hideloading(); + } + } + }); + $("#loadingdialog").parents(".ui-dialog").children(".ui-dialog-titlebar").remove(); + $("#loadingdialog").dialog("open"); + $(".ui-widget-overlay").css("opacity", "0.0"); + }, + hideloading: function() { + for (var i = 0; i < this.semaphore.length; i++) { + if ( this.semaphore[i].readyState == 4) { + this.semaphore.splice(i, 1); + i--; + } + } + if ( this.semaphore.length == 0) { + $("#loadingdialog").dialog("close"); + $("#loadingdialog").remove(); + } + }, + errorloading: function(ajax, hide_dialog) { + var that = this; + var json = null; + if (ajax) { + json = JSON.parse(ajax.responseText); + } + this.hideloading(); + for (var i = 0; i < this.semaphore.length; i++) { + this.semaphore[i].abort(); + this.semaphore.splice(i, 1); + i--; + } + if ( $("#loadingdialog").size() == 0 && !hide_dialog) { + var error_label; + var error_message; + if (json){ + error_label = "groongaでエラーが発生しました。"; + error_message = json[0][3] + "(" + json[0][0] + ")"; + } else if (ajax) { + error_label = "通信エラーが発生しました。"; + error_message = "" + ajax.status + ": " + ajax.statusText; + } else { + error_label = "通信エラーが発生しました。"; + error_message = ""; + } + $("<div />") + .append($("<div />").text(error_label)) + .append($("<div />").text(error_message)) + .attr("id", "loadingdialog") + .dialog({ + title: "", + width: 340, + height: 160, + minHeight: 160, + modal: true, + resizable: false, + draggable: false, + open: function() { + $(this).parents(".ui-dialog").children(".ui-dialog-titlebar").remove(); + }, + buttons: { OK: function() { that.hideloading(); } } + }); + } + }, + validateajax: function(d, hide_dialog) { + if (!d) { + this.errorloading(null, hide_dialog); + return -1; + } + return 0; + } +}); -------------- next part -------------- HTML����������������������������...Download