controls.js 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018
  1. // Licensed to Cloudera, Inc. under one
  2. // or more contributor license agreements. See the NOTICE file
  3. // distributed with this work for additional information
  4. // regarding copyright ownership. Cloudera, Inc. licenses this file
  5. // to you under the Apache License, Version 2.0 (the
  6. // "License"); you may not use this file except in compliance
  7. // with the License. You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. var searchRenderers = {
  17. 'rowkey': { //class to tag selection
  18. select: /[^\,\{\[]+(([\{\[][^\}\]]+[\}\]])+|)([^\,]+|)/g, //select the substring to process, useful as JS has no lookbehinds old: ([^,]+\[([^,]+(,|)+)+\]|[^,]+)
  19. tag: /.+/g, //select the matches to wrap with tags
  20. nested: {
  21. 'scan': { select: /(([^\\]|\b)\+[0-9]+)/g, tag: /\+[0-9]+/g },
  22. 'columns': {
  23. select: /\[.+\]/g,
  24. tag: /[^,\[\]]+/g, //forced to do this select due to lack of lookbehinds /[\[\]]/g
  25. nested: {
  26. 'range': {
  27. select: /\sto\s/g,
  28. tag: /.+/g
  29. }
  30. }
  31. },
  32. 'prefix': { select: /[^\*\\]+\*/g, tag: /\*/g},
  33. 'filter': {
  34. select: /\{[^\{\}]+\}/,
  35. tag:/[^\{\}]+/g,
  36. nested: {
  37. 'linker': {
  38. select: /\ (AND|OR|SKIP|WHILE)\ /g,
  39. tag: /.+/g
  40. }/*,
  41. 'compare_op': {
  42. select: /[\<\=\!\>]{1,2}/g,
  43. tag: /.+/g
  44. }*/ //will be added eventually after html bug is figured out
  45. }
  46. }
  47. }
  48. }
  49. };
  50. var DataTableViewModel = function(options) {
  51. var self = this, _defaults = {
  52. name: '',
  53. columns: [],
  54. items: [],
  55. reload: function() {
  56. },
  57. el:''
  58. };
  59. options = ko.utils.extend(_defaults,options);
  60. ListViewModel.apply(this, [options]);
  61. self.name = ko.observable(options.name);
  62. self.searchQuery.subscribe(function(value) {
  63. self._table.fnFilter(value);
  64. });
  65. self.columns = ko.observableArray(options.columns);
  66. self._el = $('table[data-datasource="' + options.el + '"]');
  67. self._table = null;
  68. self._initTable = function() {
  69. if(!self._table) {
  70. self._table = self._el.dataTable({
  71. "aoColumnDefs": [ { "bSortable": false, "aTargets": [ 0 ] }
  72. ],
  73. "sDom": 'tr',//this has to be in, change to sDom so you can call filter()
  74. 'bAutoWidth':false,
  75. "iDisplayLength": -1});
  76. return self._table;
  77. }
  78. };
  79. self.sort = function(viewModel, event) {
  80. var el = $(event.currentTarget);
  81. };
  82. var _reload = self.reload;
  83. self.reload = function(callback) {
  84. if(self._table) {
  85. self._table.fnClearTable();
  86. self._table.fnDestroy();
  87. self._table = null;
  88. }
  89. _reload(function() {
  90. self._initTable();
  91. if(callback!=null)
  92. callback();
  93. });
  94. };
  95. self.canDrop = ko.computed(function() {
  96. var selected = self.selected();
  97. if(selected.length <= 0) return false;
  98. for(var i=0; i<selected.length; i++) {
  99. if(selected[i].enabled()) return false;
  100. }
  101. return true;
  102. });
  103. self.canDisable = ko.computed(function() {
  104. var selected = self.selected();
  105. if(selected.length <= 0) return false;
  106. for(var i=0; i<selected.length; i++) {
  107. if(!selected[i].enabled()) return false;
  108. }
  109. return true;
  110. });
  111. self.canEnable = ko.computed(function() {
  112. var selected = self.selected();
  113. if(selected.length <= 0) return false;
  114. for(var i=0; i<selected.length; i++) {
  115. if(selected[i].enabled()) return false;
  116. }
  117. return true;
  118. });
  119. };
  120. //a Listview of Listviews
  121. var SmartViewModel = function(options) {
  122. var self = this;
  123. options = ko.utils.extend({
  124. name: '',
  125. items: [],
  126. reload: function() {
  127. },
  128. el:'',
  129. sortFields: {
  130. 'Row Key': function(a, b) {
  131. return a.row.localeCompare(b.row);
  132. },
  133. 'Column Count': function(a, b) {
  134. a = a.items().length;
  135. b = b.items().length;
  136. if(a > b)
  137. return 1;
  138. if(a < b)
  139. return -1;
  140. return 0;
  141. },
  142. 'Row Key Length': function(a, b) {
  143. a = a.row.length;
  144. b = b.row.length;
  145. if(a > b)
  146. return 1;
  147. if(a < b)
  148. return -1;
  149. return 0;
  150. }
  151. },
  152. canWrite: false
  153. }, options);
  154. ListViewModel.apply(this, [options]); //items: [ListView.items[],ListView.items[]]
  155. self.columnFamilies = ko.observableArray();
  156. self.name = ko.observable(options.name);
  157. self.name.subscribe(function(val){
  158. if(!val) return;
  159. self.columnFamilies([]);
  160. self._reloadcfs();
  161. if(app.station() == 'table' && app.search.cur_input())
  162. return;
  163. self.querySet.removeAll();
  164. self.evaluateQuery();
  165. }); //fix and decouple
  166. self.lastReloadTime = ko.observable(1);
  167. self.searchQuery.subscribe(function(value) //make this as nice as the render function and split into two, also fire not down on keyup events
  168. {
  169. if(app.station() != 'table')
  170. return;
  171. if(value.replace(/\s/g, "") == '' || value == null)
  172. routie(app.cluster() + '/' + app.views.tabledata.name());
  173. var inputs = value.match(searchRenderers['rowkey']['select']);
  174. self.querySet.removeAll();
  175. if(inputs) {
  176. for(var i=0; i<inputs.length; i++) {
  177. if(inputs[i].trim() != "" && inputs[i].trim() != ',') {
  178. //pull out filters
  179. var filter = inputs[i].match(searchRenderers['rowkey']['nested']['filter']['select']) || "";
  180. filter = filter != null && filter.length > 0 ? escape(filter[0].trim().slice(1, -1)) : "";
  181. function filterPostfix(postfix) {
  182. return (filter != null && filter.length > 0 ? ' AND ' : ' ' ) + postfix;
  183. }
  184. //pull out columns
  185. var extract = inputs[i].match(searchRenderers['rowkey']['nested']['columns']['select']);
  186. var columns = extract != null ? extract[0].match(searchRenderers['rowkey']['nested']['columns']['tag']) : [];
  187. inputs[i] = inputs[i].replace(extract, '');
  188. //pull out scan
  189. var p = pullFromRenderer(inputs[i], searchRenderers['rowkey']['nested']['scan']);
  190. inputs[i] = inputs[i].replace(p, '');
  191. p = p.split('+');
  192. var scan = p.length > 1 ? parseInt(p[1].trim()) : 0;
  193. //pull out column filters
  194. var toRemove = [];
  195. var cfs = [];
  196. for(var n = 0; n < columns.length; n++) {
  197. var o = columns[n];
  198. if(columns[n].match(searchRenderers['rowkey']['nested']['columns']['nested']['range']['select'])) {
  199. var partitions = columns[n].split(searchRenderers['rowkey']['nested']['columns']['nested']['range']['select']);
  200. filter += filterPostfix("ColumnRangeFilter('" + partitions[0] + "', false, '" + partitions[1] + "', true)");
  201. toRemove.push(n);
  202. } else {
  203. if(o.indexOf(':') == -1) {
  204. toRemove.push(n);
  205. //for each column family push cf and then column
  206. $(self.columnFamilies()).each(function(i, item) {
  207. columns.push(item.name + o);
  208. });
  209. continue;
  210. } else {
  211. o = o.slice(o.indexOf(':') + 1);
  212. }
  213. var colscan = pullFromRenderer(o, searchRenderers['rowkey']['nested']['scan']);
  214. if(colscan) {
  215. colscan = parseInt(colscan.split('+')[1]) + 1;
  216. filter += filterPostfix("ColumnPaginationFilter(" + colscan + ", 0)");
  217. }
  218. var fc = o.replace(pullFromRenderer(o, searchRenderers['rowkey']['nested']['prefix']), '');
  219. if(fc != o) {
  220. filter += filterPostfix("ColumnPrefixFilter('" + o.match(/[^:*]+/g)[0] + "')");
  221. columns[n] = columns[n].split(':')[0] + ':';
  222. }
  223. }
  224. }
  225. for(var n = toRemove.length - 1; n>=0; n--) {
  226. columns.splice(toRemove[n], 1);
  227. };
  228. self.querySet.push(new QuerySetPiece({
  229. 'row_key': inputs[i].replace(/\\(\+|\*|\,)/g, '$1').replace(/[\[\{].+[\]\}]|\*/g,'').trim(), //clean up with column regex selectors instead
  230. 'scan_length': scan ? scan + 1 : 1,
  231. 'columns': columns,
  232. 'prefix': inputs[i].match(searchRenderers['rowkey']['nested']['prefix']['select']) != null,
  233. 'filter': filter.length > 0 ? filter : null
  234. }));
  235. }
  236. }
  237. }
  238. self.evaluateQuery();
  239. });
  240. self._reloadcfs = function(callback) {
  241. return API.queryTable("getColumnDescriptors").done(function(data) {
  242. self.columnFamilies.removeAll();
  243. var keys = Object.keys(data);
  244. for(var i=0;i<keys.length;i++) {
  245. self.columnFamilies.push(new ColumnFamily({name:keys[i], enabled:false}));
  246. }
  247. if(callback!=null)
  248. callback();
  249. });
  250. };
  251. self.columnQuery = ko.observable("");
  252. self.columnQuery.subscribe(function(query) {
  253. var dataRowFilter = function(index, data_row) {
  254. data_row.searchQuery(query);
  255. };
  256. if (self.selected().length > 0) {
  257. $.each(self.selected(), dataRowFilter);
  258. } else {
  259. $.each(self.items(), dataRowFilter);
  260. }
  261. });
  262. self.rows = ko.computed(function() {
  263. var a = [];
  264. var items = this.items();
  265. for(var i=0; i<items.length; i++) {
  266. a.push(items[i].row);
  267. }
  268. return a;
  269. }, self);
  270. self.querySet = ko.observableArray();
  271. self.validateQuery = function() {
  272. if(self.querySet().length == 0) {
  273. self.querySet.push(new QuerySetPiece({
  274. 'row_key': 'null',
  275. 'scan_length': 10,
  276. 'prefix': 'false'
  277. }));
  278. } else {
  279. $(self.querySet()).each(function() {
  280. this.validate();
  281. this.editing(false);
  282. });
  283. }
  284. };
  285. self.addQuery = function() {
  286. self.validateQuery();
  287. self.querySet.push(new QuerySetPiece({onValidate: function() {
  288. //self.reload();
  289. }}))
  290. };
  291. self.evaluateQuery = function(callback) {
  292. self.validateQuery();
  293. self.reload(callback);
  294. };
  295. var _reload = self.reload;
  296. self.reload = function(callback) {
  297. var queryStart = new Date();
  298. _reload(function() {
  299. self.lastReloadTime((new Date() - queryStart)/1000);
  300. if(callback!=null)
  301. callback();
  302. self.isLoading(false);
  303. });
  304. };
  305. self.showGrid = ko.observable(false);
  306. self.showGrid.subscribe(function(val) {
  307. if(val) {
  308. var rows = self.items();
  309. var columns = {};
  310. //build full lookup hash of columns
  311. for(var i=0; i<rows.length; i++) {
  312. var cols = rows[i].items();
  313. for(var q=0; q<cols.length; q++) {
  314. if(!columns[cols[q].name])
  315. columns[cols[q].name] = "";
  316. }
  317. }
  318. for(var i=0; i<rows.length; i++) {
  319. //clone blank template from hash
  320. var new_row = $.extend({}, columns);
  321. var cols = rows[i].items();
  322. var col_list = [];
  323. //set existing values
  324. for(var q=0; q<cols.length; q++) {
  325. new_row[cols[q].name] = cols[q];
  326. }
  327. //build actual row from hash
  328. var keys = Object.keys(new_row);
  329. for(var r=0; r<keys.length; r++) {
  330. if(!new_row[keys[r]]) new_row[keys[r]] = new ColumnRow({ name: keys[r], value: '', parent: rows[i] });
  331. col_list.push(new_row[keys[r]]);
  332. }
  333. //set and sort
  334. rows[i].items(col_list);
  335. rows[i].sortDropDown.sort();
  336. }
  337. } else {
  338. self.reload();
  339. }
  340. });
  341. self.truncateLimit = ko.observable(1500);
  342. self.reachedLimit = ko.computed(function() {
  343. var items = self.items();
  344. for(var i=0; i<items.length; i++) {
  345. if(self.truncateLimit() < items[i].items().length) return true;
  346. }
  347. return false;
  348. });
  349. };
  350. var SmartViewDataRow = function(options) {
  351. var self = this;
  352. options = ko.utils.extend({
  353. sortFields: {
  354. 'Column Family': function(a, b) {
  355. return a.name.localeCompare(b.name);
  356. },
  357. 'Column Name': function(a, b) {
  358. return a.name.split(':')[1].localeCompare(b.name.split(':')[1]);
  359. },
  360. 'Cell Size': function(a, b) {
  361. a = a.value().length;
  362. b = b.value().length;
  363. if(a > b)
  364. return 1;
  365. if(a < b)
  366. return -1;
  367. return 0;
  368. },
  369. 'Cell Value': function(a, b) {
  370. return a.value().localeCompare(b.value());
  371. },
  372. 'Timestamp': function(a, b) {
  373. a = parseInt(a.timestamp);
  374. b = parseInt(b.timestamp);
  375. if(a > b)
  376. return 1;
  377. if(a < b)
  378. return -1;
  379. return 0;
  380. },
  381. 'Column Name Length': function(a, b) {
  382. a = a.name.split(':')[1].length;
  383. b = b.name.split(':')[1].length;
  384. if(a > b)
  385. return 1;
  386. if(a < b)
  387. return -1;
  388. return 0;
  389. }
  390. },
  391. canWrite: false
  392. }, options);
  393. DataRow.apply(self,[options]);
  394. ListViewModel.apply(self,[options]);
  395. self.displayedItems = ko.observableArray();
  396. self.displayRangeStart = 0;
  397. self.displayRangeLength = 20;
  398. self.items.subscribe(function() {
  399. self.displayedItems([]);
  400. self.updateDisplayedItems();
  401. });
  402. self.searchQuery.subscribe(function(searchValue) {
  403. self.scrollLoadSource = ko.computed(function(){
  404. return self.items().filter(function(column) {
  405. return column.name.toLowerCase().indexOf(searchValue.toLowerCase()) != -1;
  406. });
  407. });
  408. self.displayRangeLength = 20;
  409. self.updateDisplayedItems();
  410. });
  411. self.scrollLoadSource = self.items;
  412. self.updateDisplayedItems = function() {
  413. var x = self.displayRangeStart;
  414. self.displayedItems(self.scrollLoadSource().slice(x, x + self.displayRangeLength));
  415. };
  416. self.resetScrollLoad = function() {
  417. self.scrollLoadSource = self.items;
  418. self.updateDisplayedItems();
  419. };
  420. self.isCollapsed = ko.observable(false);
  421. self.toggleSelectedCollapse = function() {
  422. if(!self.isCollapsed()) {
  423. self.displayedItems(self.displayedItems().filter(function(item) {
  424. return item.isSelected();
  425. }));
  426. self.scrollLoadSource = self.displayedItems;
  427. self.isCollapsed(true);
  428. }
  429. else {
  430. self.isCollapsed(false);
  431. self.resetScrollLoad();
  432. }
  433. };
  434. self.onScroll = function(target, ev) {
  435. var displayRangeDelta = 15;
  436. if($(ev.target).scrollLeft() == ev.target.scrollWidth - ev.target.clientWidth) {
  437. if(self.displayedItems().length < self.scrollLoadSource().length) {
  438. self.displayRangeLength += displayRangeDelta;
  439. self.updateDisplayedItems();
  440. } else {
  441. self.displayRangeLength = self.items().length + displayRangeDelta;
  442. var validate = self.items().length;
  443. API.queryTable('getRowPartial' , prepForTransport(self.row), self.items().length, 100).done(function(data) {
  444. if(self.items().length != validate) return false;
  445. var cols = data[0].columns;
  446. var keys = Object.keys(cols);
  447. var temp = [];
  448. for(var i=0; i<keys.length; i++) {
  449. var col = cols[keys[i]];
  450. temp.push(new ColumnRow({name: keys[i],
  451. timestamp: col.timestamp,
  452. value: col.value,
  453. parent: self}));
  454. }
  455. self.items(self.items().concat(temp));
  456. });
  457. }
  458. }
  459. };
  460. self.drop = function(cont) {
  461. function doDrop() {
  462. self.isLoading(true);
  463. return API.queryTable('deleteAllRow', self.row, "{}").complete(function() {
  464. app.views.tabledata.items.remove(self); //decouple later
  465. self.isLoading(false);
  466. });
  467. }
  468. (cont === true) ? doDrop() : confirm(i18n("Confirm Delete"), i18n('Delete row ') + self.row + i18n('? (This cannot be undone)'), doDrop);
  469. };
  470. self.setItems = function(cols) {
  471. var colKeys = Object.keys(cols);
  472. var items = [];
  473. for(var q=0; q<colKeys.length; q++) {
  474. items.push(new ColumnRow({name: colKeys[q],
  475. timestamp: cols[colKeys[q]].timestamp,
  476. value: cols[colKeys[q]].value,
  477. parent: self}));
  478. }
  479. self.items(items);
  480. return self.items();
  481. };
  482. self.selectAllVisible = function(){
  483. for(t=0; t<self.displayedItems().length; t++)
  484. self.displayedItems()[t].isSelected(true);
  485. return self;
  486. };
  487. self.deselectAllVisible = function(){
  488. for(t=0; t<self.displayedItems().length; t++)
  489. self.displayedItems()[t].isSelected(false);
  490. return self;
  491. };
  492. self.toggleSelectAllVisible = function() {
  493. if(self.selected().length != self.displayedItems().length)
  494. return self.selectAllVisible();
  495. return self.deselectAllVisible();
  496. };
  497. self.push = function(item) {
  498. var column = new ColumnRow(item);
  499. self.items.push(column);
  500. };
  501. var _reload = self.reload;
  502. self.reload = function(callback) {
  503. logGA('get_row');
  504. _reload(function() {
  505. if(callback!=null)
  506. callback();
  507. self.isLoading(false);
  508. });
  509. };
  510. };
  511. var ColumnRow = function(options) {
  512. var self = this;
  513. ko.utils.extend(self,options);
  514. DataRow.apply(self,[options]);
  515. self.value = ko.observable(self.value);
  516. self.history = new CellHistoryPage({row: self.parent.row, column: self.name, timestamp: self.timestamp, items: []});
  517. self.drop = function(cont) {
  518. function doDrop() {
  519. logGA('filter_columns');
  520. self.parent.isLoading(true);
  521. return API.queryTable('deleteColumn', prepForTransport(self.parent.row), prepForTransport(self.name)).done(function(data) {
  522. self.parent.items.remove(self);
  523. if(self.parent.items().length > 0)
  524. self.parent.reload(); //change later
  525. self.parent.isLoading(false);
  526. });
  527. }
  528. (cont === true) ? doDrop() : confirm(i18n("Confirm Delete"), i18n("Are you sure you want to drop this column?"), doDrop);
  529. };
  530. self.reload = function(callback, skipPut) {
  531. self.isLoading(true);
  532. API.queryTable('get', prepForTransport(self.parent.row), prepForTransport(self.name), 'null').done(function(data) {
  533. if(data.length > 0 && !skipPut)
  534. self.value(data[0].value);
  535. if(typeof callback !== "undefined" && callback != null)
  536. callback();
  537. self.isLoading(false);
  538. });
  539. };
  540. self.value.subscribe(function(value) {
  541. //change transport prep to object wrapper
  542. logGA('put_column');
  543. API.queryTable('putColumn', prepForTransport(self.parent.row), prepForTransport(self.name), "hbase-post-key-" + JSON.stringify(value)).done(function(data) {
  544. self.reload(function(){}, true);
  545. });
  546. self.editing(false);
  547. });
  548. self.editing = ko.observable(false);
  549. self.isLoading = ko.observable(false); //move to baseclass
  550. };
  551. var SortDropDownView = function(options) {
  552. var self = this;
  553. options = ko.utils.extend({
  554. sortFields: {},
  555. target: null
  556. }, options);
  557. BaseModel.apply(self,[options]);
  558. self.target = options.target;
  559. self.sortAsc = ko.observable(true);
  560. self.sortAsc.subscribe(function(){self.sort()});
  561. self.sortField = ko.observable("");
  562. self.sortField.subscribe(function(){self.sort()});
  563. self.sortFields = ko.observableArray(Object.keys(options.sortFields)); // change to ko.computed?
  564. self.sortFunctionHash = ko.observable(options.sortFields);
  565. self.toggleSortMode = function() {
  566. self.sortAsc(!self.sortAsc());
  567. };
  568. self.sort = function() {
  569. if (!self.target || !(self.sortFields().length > 0)) return;
  570. self.target.sort(function(a, b) {
  571. return (self.sortAsc() ? 1 : -1) * self.sortFunctionHash()[self.sortField() ? self.sortField() : self.sortFields()[0]](a,b); //all sort functions must sort by ASC for default
  572. });
  573. };
  574. };
  575. var TableDataRow = function(options) {
  576. var self = this;
  577. options = ko.utils.extend({
  578. name:"",
  579. enabled:true
  580. }, options);
  581. DataRow.apply(self,[options]);
  582. self.name = options['name'];
  583. self.enabled = ko.observable(options['enabled']);
  584. self.toggle = function(viewModel,event){
  585. var action = [i18n('enable'), i18n('disable')][self.enabled() << 0], el = $(event.currentTarget);
  586. confirm(i18n("Confirm") + " " + action, i18n("Are you sure you want to") + " " + action + " " + i18n("this table?"), function()
  587. {
  588. el.showIndicator();
  589. return self[action](el).complete(function() {
  590. el.hideIndicator();
  591. });
  592. });
  593. };
  594. self.enable = function(el) {
  595. return API.queryCluster('enableTable',self.name).complete(function() {
  596. self.enabled(true);
  597. });
  598. };
  599. self.disable = function(callback) {
  600. return API.queryCluster('disableTable',self.name).complete(function() {
  601. self.enabled(false);
  602. if($.isFunction(callback)) callback();
  603. });
  604. };
  605. self.drop = function(el) {
  606. return API.queryCluster('deleteTable',self.name);
  607. };
  608. };
  609. var QuerySetPiece = function(options) {
  610. var self = this;
  611. options = ko.utils.extend({
  612. row_key: "null",
  613. scan_length: 1,
  614. prefix: false,
  615. columns: [],
  616. filter: null,
  617. onValidate: function(){}
  618. }, options);
  619. BaseModel.apply(self,[options]);
  620. self.row_key = ko.observable(options.row_key);
  621. self.scan_length = ko.observable(options.scan_length);
  622. self.columns = ko.observableArray(options.columns);
  623. self.prefix = ko.observable(options.prefix);
  624. self.filter = ko.observable(options.filter);
  625. self.editing = ko.observable(true);
  626. self.validate = function() {
  627. if(self.scan_length() <= 0 || self.row_key() == "")
  628. return app.views.tabledata.querySet.remove(self); //change later
  629. return options.onValidate();
  630. };
  631. self.row_key.subscribe(self.validate.bind());
  632. self.scan_length.subscribe(self.validate.bind());
  633. };
  634. var ColumnFamily = function(options) {
  635. this.name = options.name;
  636. this.enabled = ko.observable(options.enabled);
  637. this.toggle = function() {
  638. this.enabled(!this.enabled());
  639. app.views.tabledata.reload();
  640. };
  641. }
  642. //tagsearch
  643. var tagsearch = function() {
  644. var self = this;
  645. self.tags = ko.observableArray();
  646. self.mode = ko.observable('idle');
  647. self.cur_input = ko.observable('');
  648. self.submitted = ko.observable(false);
  649. self.filters = ["KeyOnlyFilter ()", "FirstKeyOnlyFilter ()", "PrefixFilter ('row_prefix')", "ColumnPrefixFilter('column_prefix')", "MultipleColumnPrefixFilter('column_prefix', 'column_prefix', …, 'column_prefix')", "ColumnCountGetFilter (limit)", "PageFilter (page_size)", "ColumnPaginationFilter(limit, offset)", "InclusiveStopFilter('stop_row_key')", "TimeStampsFilter (timestamp, timestamp, ... ,timestamp)", "RowFilter (compareOp, 'row_comparator')", "QualifierFilter (compareOp, 'qualifier_comparator')", "QualifierFilter (compareOp,'qualifier_comparator')", "ValueFilter (compareOp,'value_comparator')", "DependentColumnFilter ('family', 'qualifier', boolean, compare operator, 'value comparator')", "DependentColumnFilter ('family', 'qualifier', boolean)", "DependentColumnFilter ('family', 'qualifier')", "SingleColumnValueFilter('family', 'qualifier', compare operator, 'comparator', filterIfColumnMissing_boolean, latest_version_boolean)", "SingleColumnValueFilter('family', 'qualifier', compare operator, 'comparator')", "SingleColumnValueExcludeFilter('family', 'qualifier', compare operator, 'comparator', latest_version_boolean, filterIfColumnMissing_boolean)", "SingleColumnValueExcludeFilter('family', 'qualifier', compare operator, 'comparator')", "ColumnRangeFilter ('minColumn', minColumnInclusive_bool, 'maxColumn', maxColumnInclusive_bool)"];
  650. self.hints = ko.observableArray([ {
  651. hint: i18n('End Query'),
  652. shortcut: ',',
  653. mode: ['rowkey', 'prefix', 'scan'],
  654. selected: false
  655. }, {
  656. hint: i18n('Prefix Scan'),
  657. shortcut: '*',
  658. mode: ['rowkey', 'columns'],
  659. selected: false
  660. }, {
  661. hint: i18n('Start Scan'),
  662. shortcut: '+',
  663. mode: ['rowkey', 'prefix', 'columns'],
  664. selected: false
  665. }, {
  666. hint: i18n('Start Select Columns'),
  667. shortcut: '[',
  668. mode: ['rowkey', 'prefix'],
  669. selected: false
  670. }, {
  671. hint: i18n('End Column/Family'),
  672. shortcut: ',',
  673. mode: ['columns'],
  674. selected: false
  675. }, {
  676. hint: i18n('End Select Columns'),
  677. shortcut: ']',
  678. mode: ['columns'],
  679. selected: false
  680. }, {
  681. hint: i18n('Column Range'),
  682. shortcut: ' to ',
  683. mode: ['columns'],
  684. selected: false
  685. },
  686. {
  687. hint: i18n('Start FilterString'),
  688. shortcut: '{',
  689. mode: ['rowkey'],
  690. selected: false
  691. },
  692. {
  693. hint: i18n('End FilterString'),
  694. shortcut: '}',
  695. mode: ['filter'],
  696. selected: false
  697. }
  698. ]);
  699. self.activeHints = ko.computed(function() {
  700. var ret = [];
  701. $(self.hints()).each(function(i, hint) {
  702. if (hint.mode.indexOf(self.mode()) > -1)
  703. ret.push(hint);
  704. });
  705. return ret;
  706. });
  707. self.activeHint = ko.observable(-1);
  708. self.modes = {
  709. 'rowkey': {
  710. hint: i18n('Row Key Value'),
  711. type: i18n('String')
  712. },
  713. 'scan': {
  714. hint: i18n('Length of Scan or Row Key'),
  715. type: i18n('Integer')
  716. },
  717. 'columns': {
  718. hint: i18n('Column Family: Column Name'),
  719. type: i18n('String')
  720. },
  721. 'prefix': {
  722. hint: i18n('Rows starting with'),
  723. type: i18n('String')
  724. },
  725. 'filter': {
  726. hint: i18n('Thrift FilterString'),
  727. type: i18n('String')
  728. }
  729. }
  730. self.modeQueue = ['idle'];
  731. self.focused = ko.observable(false);
  732. self.activeSuggestions = ko.observableArray();
  733. self.activeSuggestion = ko.observable(-1);
  734. self.insertTag = function(tag) {
  735. var mode = tag.indexOf('+') != -1 ? 'scan' : 'rowkey';
  736. var tag = {value: tag, tag: mode} //parse mode
  737. self.tags.push(tag);
  738. }
  739. self.render = function(input, renderers) {
  740. var keys = Object.keys(renderers);
  741. for(var i=0; i<keys.length; i++) {
  742. input = input.replace(renderers[keys[i]].select, function(selected) {
  743. var hasMatched = false;
  744. var processed = selected.replace(renderers[keys[i]].tag, function(tagged) {
  745. hasMatched = true;
  746. return "<span class='" + keys[i] + " tagsearchTag' title='" + keys[i] + "' data-toggle='tooltip'>" + ('nested' in renderers[keys[i]] ? self.render(tagged, renderers[keys[i]].nested) : tagged) + "</span>";
  747. });
  748. if(hasMatched && renderers[keys[i]]['strip'])
  749. processed = processed.replace(renderers[keys[i]].strip, '');
  750. return processed;
  751. });
  752. }
  753. return input;
  754. };
  755. self.updateMode = function(value) {
  756. self.submitted(false);
  757. var selection = value.slice(0, self.selectionEnd());
  758. var endindex = selection.slice(selection.lastIndexOf(',')).indexOf(',');
  759. if(endindex == -1) endindex = selection.length;
  760. var lastbit = value.substring(selection.lastIndexOf(','), endindex).trim();
  761. if(lastbit == "," || lastbit == "") {
  762. self.mode('idle');
  763. return;
  764. }
  765. var tokens = "[]+*{}";
  766. var m = 'rowkey';
  767. for(var i=selection.length - 1; i>=0; i--) {
  768. if(tokens.indexOf(selection[i]) != -1) {
  769. if(selection[i] == '{')
  770. m = 'filter';
  771. else if(selection[i] == '[')
  772. m = 'columns';
  773. else if(selection[i] == ']' || selection[i] == '}')
  774. m = 'rowkey';
  775. else if(selection[i] == '+')
  776. m = 'scan';
  777. else if(selection[i] == '-')
  778. m = 'prefix';
  779. break;
  780. }
  781. }
  782. self.mode(m.trim());
  783. };
  784. self.selectionStart = ko.observable(0);
  785. self.selectionEnd = ko.observable(0);
  786. self.hintText = ko.computed(function() {
  787. var pre, s, e;
  788. try {
  789. var r = window.getSelection().getRangeAt(0);
  790. pre = r.startContainer.nodeValue;
  791. s = r.startOffset;
  792. e = r.endOffset;
  793. return pre.slice(0, s) + "<span class='selection'>" + pre.slice(s, e) + "</span>" + pre.slice(e);
  794. } catch (e) {
  795. var value = self.cur_input();
  796. var selection = value.slice(0, self.selectionEnd());
  797. var index = selection.lastIndexOf(',') + 1;
  798. var endindex = value.slice(index).indexOf(',');
  799. endindex = endindex == -1 ? value.length : endindex;
  800. pre = value.substring(index, index + endindex);
  801. s = self.selectionStart() - index;
  802. e = self.selectionEnd() - index;
  803. if(s == e)
  804. e += 1;
  805. s = s < 0 ? 0 : s;
  806. e = e > pre.length ? pre.length : e;
  807. }
  808. return pre.slice(0, s) + "<span class='selection'>" + pre.slice(s, e) + "</span>" + pre.slice(e);
  809. });
  810. self.onKeyDown = function(target, ev) {
  811. if(ev.keyCode == 13 && self.cur_input().slice(self.cur_input().lastIndexOf(',')).trim() != ",") {
  812. if(self.activeSuggestion() > -1) {
  813. self.replaceFocusNode(self.activeSuggestions()[self.activeSuggestion()]);
  814. self.activeSuggestion(-1);
  815. setTimeout(function() {
  816. var s = self.cur_input(), r;
  817. if(s.lastIndexOf('(') != -1 && s.lastIndexOf(')') != -1) {
  818. r = setCursor($('#search-tags')[0], s.lastIndexOf('(') + 1);
  819. r.setEnd(r.endContainer, r.startOffset + (s.lastIndexOf(')') - s.lastIndexOf('(') - 1));
  820. } else {
  821. r = setCursor($('#search-tags')[0], s.length);
  822. }
  823. var sel = window.getSelection();
  824. sel.removeAllRanges();
  825. sel.addRange(r);
  826. }, 1);
  827. } else {
  828. self.evaluate();
  829. }
  830. return false;
  831. } else if (ev.keyCode == 219) {
  832. setTimeout(function() {
  833. var ep = getEditablePosition($('#search-tags')[0], true);
  834. self.cur_input(self.cur_input().slice(0, ep) + (ev.shiftKey ? '}' : ']') + self.cur_input().slice(ep));
  835. }, 1);
  836. } else if (ev.keyCode == 40) {
  837. if(self.activeSuggestion() < self.activeSuggestions().length - 1)
  838. self.activeSuggestion(self.activeSuggestion() + 1);
  839. return false;
  840. } else if(ev.keyCode == 38) {
  841. if(self.activeSuggestion() > 0)
  842. self.activeSuggestion(self.activeSuggestion() - 1);
  843. return false;
  844. }
  845. setTimeout(self.updateMenu, 1);
  846. return true;
  847. };
  848. self.updateMenu = function() {
  849. self.activeSuggestion(-1);
  850. try{
  851. var pos = getEditablePosition(document.getElementById('search-tags'));
  852. self.selectionStart(pos);
  853. self.selectionEnd(pos);
  854. } catch (err) {}
  855. self.updateMode(self.cur_input());
  856. self.updateSuggestions();
  857. };
  858. self.replaceFocusNode = function(text) {
  859. window.getSelection().getRangeAt(0).startContainer.nodeValue = text;
  860. };
  861. self.updateSuggestions = function() {
  862. var val = window.getSelection().getRangeAt(0).startContainer.nodeValue;
  863. switch(self.mode()) {
  864. case 'filter':
  865. var focus = val.replace(/\{|\}|\s|&[^;]+?;/g,"").split(searchRenderers.rowkey.nested.filter.nested.linker.select).slice(-1)[0];
  866. if(focus != "") {
  867. self.activeSuggestions(self.filters.filter(function(a) {
  868. return a.toLowerCase().replace(" ","").indexOf(focus.toLowerCase()) != -1;
  869. }));
  870. } else {
  871. self.activeSuggestions([]);
  872. }
  873. return;
  874. case 'rowkey':
  875. var validate = window.getSelection().getRangeAt(0).startContainer.nodeValue;
  876. function cancel() {
  877. return window.getSelection().getRangeAt(0).startContainer.nodeValue != validate;
  878. }
  879. function callback() {
  880. if(cancel()) return false;
  881. if(validate.trim() != "") {
  882. API.queryTable('getAutocompleteRows', 10, prepForTransport(validate.trim())).done(function(data) {
  883. if(cancel()) return false;
  884. self.activeSuggestions(data);
  885. });
  886. }
  887. }
  888. setTimeout(callback, 200);
  889. return;
  890. default:
  891. self.activeSuggestions([]);
  892. return;
  893. }
  894. }
  895. self.evaluate = function() {
  896. table_search(self.cur_input());
  897. self.submitted(true);
  898. self.mode('idle');
  899. };
  900. $('#search-tags').blur(function(){
  901. self.focused(false);
  902. });
  903. self.doBlur = function() {
  904. if(self.cur_input().trim() == "") {
  905. function doClick() {
  906. if(self.cur_input().trim() != "") return false;
  907. setTimeout(function() {
  908. $('#search-tags').focus();
  909. }, 1);
  910. }
  911. $('#search-tags').html('<small>' + $('#search-tags').data("placeholder") + '</small>').one('click', doClick).find('small').on('mousedown', doClick);
  912. }
  913. }
  914. $('#search-tags').focus(function(){
  915. self.focused(true);
  916. });
  917. };
  918. var CellHistoryPage = function(options) {
  919. var self = this;
  920. self.items = ko.observableArray(options.items);
  921. self.loading = ko.observable(false);
  922. self.reload = function(timestamp, append) {
  923. if(!timestamp)
  924. timestamp = options.timestamp
  925. API.queryTable("getVerTs", prepForTransport(options.row), prepForTransport(options.column), timestamp, 10, 'null').done(function(res) {
  926. self.loading = ko.observable(true);
  927. if(!append)
  928. self.items(res);
  929. else
  930. self.items(self.items() + res);
  931. self.loading = ko.observable(false);
  932. });
  933. };
  934. self.pickHistory = function(data) {
  935. data.history = self;
  936. if(!ko.isObservable(data.value))
  937. data.value = ko.observable(data.value);
  938. launchModal('cell_edit_modal',{ content: data, mime: detectMimeType(data.value()), readonly: true })
  939. };
  940. };