|
|
@@ -14,1907 +14,22 @@
|
|
|
## See the License for the specific language governing permissions and
|
|
|
## limitations under the License.
|
|
|
|
|
|
-<%!
|
|
|
-from desktop import conf
|
|
|
-from desktop.lib.i18n import smart_unicode
|
|
|
-
|
|
|
-from django.utils.translation import ugettext as _
|
|
|
-from desktop.views import _ko
|
|
|
-%>
|
|
|
+<%namespace name="koBreadCrumbs" file="/ko_components/ko_breadcrumbs.mako" />
|
|
|
+<%namespace name="koDropDown" file="/ko_components/ko_drop_down.mako" />
|
|
|
+<%namespace name="koFavoriteApp" file="/ko_components/ko_favorite_app.mako" />
|
|
|
+<%namespace name="koGlobalSearch" file="/ko_components/ko_global_search.mako" />
|
|
|
+<%namespace name="koHistoryPanel" file="/ko_components/ko_history_panel.mako" />
|
|
|
+<%namespace name="koInlineAutocomplete" file="/ko_components/ko_inline_autocomplete.mako" />
|
|
|
+<%namespace name="koJobBrowserLinks" file="/ko_components/ko_job_browser_links.mako" />
|
|
|
+<%namespace name="koSimpleAceEditor" file="/ko_components/ko_simple_ace_editor.mako" />
|
|
|
|
|
|
<%def name="all()">
|
|
|
-
|
|
|
- <script type="text/html" id="breadcrumbs-template">
|
|
|
- <div class="hue-breadcrumb-container">
|
|
|
- <!-- ko if: hiddenBreadcrumbs().length > 0 -->
|
|
|
- ...
|
|
|
- <!-- ko component: { name: 'hue-drop-down', params: { entries: hiddenBreadcrumbs, noLabel: true, searchable: false, value: hiddenValue } } --><!-- /ko -->
|
|
|
- <div class="hue-breadcrumb-divider" data-bind="text: divider"></div>
|
|
|
- <!-- /ko -->
|
|
|
- <!-- ko foreach: lastTwoBreadcrumbs -->
|
|
|
- <!-- ko if: $index() < $parent.lastTwoBreadcrumbs().length - 1 -->
|
|
|
- <div class="hue-breadcrumb pointer" data-bind="text: $data.label || $data, click: $parent.onSelect"></div>
|
|
|
- <div class="hue-breadcrumb-divider" data-bind="text: $parent.divider"></div>
|
|
|
- <!-- /ko -->
|
|
|
- <!-- ko if: $index() === $parent.lastTwoBreadcrumbs().length - 1 -->
|
|
|
- <div class="hue-breadcrumb pointer" data-bind="text: $data.label || $data"></div>
|
|
|
- <!-- /ko -->
|
|
|
- <!-- /ko -->
|
|
|
- </div>
|
|
|
- </script>
|
|
|
-
|
|
|
- <script type="text/javascript">
|
|
|
- (function () {
|
|
|
- function BreadcrumbViewModel(params) {
|
|
|
- var self = this;
|
|
|
- self.hiddenValue = ko.observable();
|
|
|
- self.onSelect = params.onSelect || function () {};
|
|
|
- self.hiddenValue.subscribe(function (newValue) {
|
|
|
- if (newValue) {
|
|
|
- self.onSelect(newValue);
|
|
|
- }
|
|
|
- });
|
|
|
- self.hiddenBreadcrumbs = ko.pureComputed(function () {
|
|
|
- if (params.breadcrumbs().length > 2) {
|
|
|
- return params.breadcrumbs().slice(0, params.breadcrumbs().length - 2);
|
|
|
- }
|
|
|
- return [];
|
|
|
- });
|
|
|
- self.lastTwoBreadcrumbs = ko.pureComputed(function () {
|
|
|
- return params.breadcrumbs().slice(params.breadcrumbs().length - 2, params.breadcrumbs().length);
|
|
|
- });
|
|
|
- self.divider = params.divider || '>';
|
|
|
- }
|
|
|
-
|
|
|
- ko.components.register('hue-breadcrumbs', {
|
|
|
- viewModel: BreadcrumbViewModel,
|
|
|
- template: { element: 'breadcrumbs-template' }
|
|
|
- });
|
|
|
- })();
|
|
|
- </script>
|
|
|
-
|
|
|
- <script type="text/html" id="hue-drop-down-template">
|
|
|
- <!-- ko if: !menuOnly && (!dropDownVisible() || !searchable) -->
|
|
|
- <a class="inactive-action hue-drop-down-active" href="javascript:void(0)" data-bind="toggle: dropDownVisible, css: { 'blue': dropDownVisible }">
|
|
|
- <!-- ko if: icon --><i class="fa" data-bind="css: icon"></i><!-- /ko -->
|
|
|
- <!-- ko if: !noLabel && value -->
|
|
|
- <span data-bind="text: typeof value().label !== 'undefined' ? value().label : value(), visible: ! dropDownVisible() || !searchable, attr: { 'title': linkTitle }" ></span>
|
|
|
- <!-- /ko -->
|
|
|
- <i class="fa fa-caret-down"></i>
|
|
|
- </a>
|
|
|
- <!-- /ko -->
|
|
|
- <!-- ko if: !menuOnly && (dropDownVisible() && searchable) -->
|
|
|
- <input class="hue-drop-down-input" type="text" data-bind="textInput: filter, attr: { 'placeHolder': value }, visible: dropDownVisible, style: { color: filterEdited() ? '#000' : '#AAA', 'min-height': '22px', 'margin-left': '10px' }"/>
|
|
|
- <i class="fa fa-caret-down"></i>
|
|
|
- <!-- /ko -->
|
|
|
- <div class="hue-drop-down-container" data-bind="css: { 'open' : dropDownVisible, 'hue-drop-down-fixed': fixedPosition }">
|
|
|
- <div class="dropdown-menu" data-bind="visible: filteredEntries().length > 0" style="min-width: 190px; max-width: 250px; min-height: 34px; max-height: 200px;">
|
|
|
- <!-- ko if: foreachVisible -->
|
|
|
- <ul class="hue-inner-drop-down" style="overflow-x: hidden;" data-bind="foreachVisible: { data: filteredEntries, minHeight: 34, container: '.dropdown-menu' }">
|
|
|
- <!-- ko if: typeof $data.divider !== 'undefined' && $data.divider -->
|
|
|
- <li class="divider"></li>
|
|
|
- <!-- /ko -->
|
|
|
- <!-- ko if: typeof $data.divider === 'undefined' || !$data.divider -->
|
|
|
- <li><a href="javascript:void(0)" data-bind="text: typeof $data.label !== 'undefined' ? $data.label : $data, click: function () { $parent.value($data); }"></a></li>
|
|
|
- <!-- /ko -->
|
|
|
- </ul>
|
|
|
- <!-- /ko -->
|
|
|
- <!-- ko ifnot: foreachVisible -->
|
|
|
- <ul class="hue-inner-drop-down" style="overflow-x: hidden;" data-bind="foreach: filteredEntries">
|
|
|
- <!-- ko if: typeof $data.divider !== 'undefined' && $data.divider -->
|
|
|
- <li class="divider"></li>
|
|
|
- <!-- /ko -->
|
|
|
- <!-- ko if: typeof $data.divider === 'undefined' || !$data.divider -->
|
|
|
- <li><a href="javascript:void(0)" data-bind="text: typeof $data.label !== 'undefined' ? $data.label : $data, click: function () { $parent.value($data); }"></a></li>
|
|
|
- <!-- /ko -->
|
|
|
- </ul>
|
|
|
- <!-- /ko -->
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </script>
|
|
|
-
|
|
|
- <script type="text/javascript">
|
|
|
- (function () {
|
|
|
- var HueDropDown = function (params, element) {
|
|
|
- var self = this;
|
|
|
- self.dropDownVisible = ko.observable(!!params.showOnInit);
|
|
|
- self.menuOnly = !!params.menuOnly;
|
|
|
- self.noLabel = !!params.noLabel;
|
|
|
- self.icon = params.icon;
|
|
|
- self.value = params.value;
|
|
|
- self.entries = params.entries;
|
|
|
- self.searchable = params.searchable || false;
|
|
|
- self.foreachVisible = params.foreachVisible || false;
|
|
|
- self.linkTitle = params.linkTitle || '${ _('Selected entry') }';
|
|
|
- self.fixedPosition = !!params.fixedPosition;
|
|
|
-
|
|
|
- var closeOnOutsideClick = function (e) {
|
|
|
- var $input = $(element).find('.hue-drop-down-input');
|
|
|
- if (!$input.is($(e.target))) {
|
|
|
- self.dropDownVisible(false);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- var inputKeyup = function (e) {
|
|
|
- var $currentActive = $(element).find('.hue-inner-drop-down > .active');
|
|
|
- var activeTop = $currentActive.length !== 0 ? $currentActive.position().top : 0;
|
|
|
- var activeHeight = $currentActive.length !== 0 ? $currentActive.outerHeight(true) : $(element).find('.hue-inner-drop-down li:first-child').outerHeight(true);
|
|
|
- var containerHeight = $(element).find('.dropdown-menu').innerHeight();
|
|
|
- var containerScrollTop = $(element).find('.dropdown-menu').scrollTop();
|
|
|
-
|
|
|
- $currentActive.removeClass('active');
|
|
|
- if (e.keyCode === 27) {
|
|
|
- // esc
|
|
|
- self.dropDownVisible(false);
|
|
|
- } else if (e.keyCode === 38) {
|
|
|
- // up
|
|
|
- if ($currentActive.length !== 0 && $currentActive.prev().length !== 0) {
|
|
|
- if (activeTop < containerScrollTop + activeHeight) {
|
|
|
- $(element).find('.dropdown-menu').scrollTop(activeTop - containerHeight/2);
|
|
|
- }
|
|
|
- $currentActive.prev().addClass('active');
|
|
|
- }
|
|
|
- } else if (e.keyCode === 40) {
|
|
|
- // down
|
|
|
- if ($currentActive.length === 0) {
|
|
|
- $(element).find('.hue-inner-drop-down li:first-child').addClass('active');
|
|
|
- } else if ($currentActive.next().length !== 0) {
|
|
|
- if ((activeTop + activeHeight *3) > containerHeight - containerScrollTop) {
|
|
|
- $(element).find('.dropdown-menu').scrollTop(activeTop - containerHeight/2);
|
|
|
- }
|
|
|
- $currentActive.next().addClass('active');
|
|
|
- } else {
|
|
|
- $currentActive.addClass('active');
|
|
|
- }
|
|
|
- } else if (e.keyCode === 13) {
|
|
|
- // enter
|
|
|
- if ($currentActive.length > 0) {
|
|
|
- self.value(ko.dataFor($currentActive[0]));
|
|
|
- self.dropDownVisible(false);
|
|
|
- $(element).find('.dropdown-menu').scrollTop(0)
|
|
|
- }
|
|
|
- } else {
|
|
|
- $(element).find('.dropdown-menu').scrollTop(0)
|
|
|
- }
|
|
|
- };
|
|
|
- self.filter = ko.observable('');
|
|
|
-
|
|
|
- self.value.subscribe(function () {
|
|
|
- self.dropDownVisible(false);
|
|
|
- });
|
|
|
-
|
|
|
- self.filterEdited = ko.observable(false);
|
|
|
-
|
|
|
- self.filter.subscribe(function () {
|
|
|
- self.filterEdited(true);
|
|
|
- $(element).find('.hue-inner-drop-down > .active').removeClass('.active');
|
|
|
- });
|
|
|
- self.dropDownVisible.subscribe(function (newValue) {
|
|
|
- self.filterEdited(false);
|
|
|
- if (newValue) {
|
|
|
- window.setTimeout(function () {
|
|
|
- self.filter('');
|
|
|
- $(window).on('click', closeOnOutsideClick);
|
|
|
- $(element).find('.hue-drop-down-input').on('keyup', inputKeyup);
|
|
|
- $(element).find('.hue-drop-down-input').focus();
|
|
|
- }, 0);
|
|
|
- } else {
|
|
|
- $(element).find('.hue-inner-drop-down > .active').removeClass('.active');
|
|
|
- $(window).off('click', closeOnOutsideClick);
|
|
|
- $(element).find('.hue-drop-down-input').off('keyup', inputKeyup);
|
|
|
- }
|
|
|
- });
|
|
|
- self.filteredEntries = ko.pureComputed(function () {
|
|
|
- if (self.filter() === '' || ! self.filterEdited()) {
|
|
|
- return self.entries();
|
|
|
- } else {
|
|
|
- var lowerFilter = self.filter().toLowerCase();
|
|
|
- return self.entries().filter(function (database) {
|
|
|
- return database.toLowerCase().indexOf(lowerFilter) !== -1;
|
|
|
- });
|
|
|
- }
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- ko.components.register('hue-drop-down', {
|
|
|
- viewModel: {
|
|
|
- createViewModel: function(params, componentInfo) {
|
|
|
- return new HueDropDown(params, componentInfo.element);
|
|
|
- }
|
|
|
- },
|
|
|
- template: { element: 'hue-drop-down-template' }
|
|
|
- });
|
|
|
- })();
|
|
|
- </script>
|
|
|
-
|
|
|
- <script type="text/html" id="hue-history-panel-template">
|
|
|
- <button class="btn btn-flat pull-right btn-toggle-jobs-panel" title="${_('Task history')}" data-bind="toggle: historyPanelVisible">
|
|
|
- <i class="fa fa-history"></i>
|
|
|
- <div class="jobs-badge" data-bind="text: historyRunningJobs().length, visible: historyRunningJobs().length > 0"></div>
|
|
|
- ## <div class="jobs-badge" data-bind="text: historyFinishedJobs().length, visible: historyFinishedJobs().length > 0"></div>
|
|
|
- </button>
|
|
|
-
|
|
|
- <div class="jobs-panel history-panel" data-bind="visible: historyPanelVisible" style="display: none;">
|
|
|
- <a class="pointer inactive-action pull-right" data-bind="click: function(){ historyPanelVisible(false); }"><i class="fa fa-fw fa-times"></i></a>
|
|
|
- <!-- ko ifnot: editorViewModel.selectedNotebook() && editorViewModel.selectedNotebook().history().length > 0 -->
|
|
|
- <span style="font-style: italic">${ _('No task history.') }</span>
|
|
|
- <!-- /ko -->
|
|
|
- <!-- ko if: editorViewModel.selectedNotebook() && editorViewModel.selectedNotebook().history().length > 0 -->
|
|
|
- <!-- ko with: editorViewModel.selectedNotebook() -->
|
|
|
- <div class="notification-history margin-bottom-10">
|
|
|
- <!-- ko if: onSuccessUrl() -->
|
|
|
- <div class="notification-history-title">
|
|
|
- <strong class="margin-left-5" data-bind="text: name"></strong>
|
|
|
- <!-- ko if: onSuccessUrl() !== 'assist.db.refresh' -->
|
|
|
- <a class="pull-right margin-right-10" href="javascript:void(0)" data-bind="hueLink: onSuccessUrl()">
|
|
|
- ${ _('Output') }
|
|
|
- </a>
|
|
|
- <!-- /ko -->
|
|
|
- <div class="clearfix"></div>
|
|
|
- </div>
|
|
|
- <!-- /ko -->
|
|
|
- <!-- ko if: snippets()[0] -->
|
|
|
- <!-- ko with: snippets()[0] -->
|
|
|
- <!-- ko if: progress -->
|
|
|
- <div class="snippet-progress-container">
|
|
|
- <div class="progress-snippet progress" data-bind="css: {
|
|
|
- 'progress-starting': progress() == 0 && status() == 'running',
|
|
|
- 'progress-warning': progress() > 0 && progress() < 100,
|
|
|
- 'progress-success': progress() == 100,
|
|
|
- 'progress-danger': progress() == 0 && errors().length > 0}" style="background-color: #FFF; width: 100%">
|
|
|
- <div class="bar" data-bind="style: {'width': (errors().length > 0 ? 100 : Math.max(2, progress())) + '%'}"></div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <!-- /ko -->
|
|
|
- <!-- ko if: result -->
|
|
|
- <pre data-bind="visible: result.logs() && result.logs().length == 0" class="logs logs-bigger">${ _('No logs available at this moment.') }</pre>
|
|
|
- <pre data-bind="visible: result.logs() && result.logs().length > 0, text: result.logs, logScroller: result.logs, logScrollerVisibilityEvent: showLogs, niceScroll" class="logs logs-bigger logs-populated" style="height: 120px"></pre>
|
|
|
- <!-- /ko -->
|
|
|
- <!-- /ko -->
|
|
|
- <!-- /ko -->
|
|
|
- </div>
|
|
|
- <!-- ko if: history -->
|
|
|
- <hr>
|
|
|
- <div class="notification-history margin-bottom-10" data-bind="niceScroll">
|
|
|
- <!-- ko if: history().length == 0 -->
|
|
|
- <span style="font-style: italic">${ _('No task history.') }</span>
|
|
|
- <!-- /ko -->
|
|
|
- <!-- ko if: history().length > 0 -->
|
|
|
- <div class="notification-history-title">
|
|
|
- <strong class="margin-left-5">${ _('Task History') }</strong>
|
|
|
- <div class="inactive-action pointer pull-right" title="${_('Clear the query history')}" data-target="#clearNotificationHistoryModal" data-toggle="modal" rel="tooltip">
|
|
|
- <i class="fa fa-calendar-times-o"></i>
|
|
|
- </div>
|
|
|
- <div class="clearfix"></div>
|
|
|
- </div>
|
|
|
- <ul class="unstyled notification-history-list">
|
|
|
- <!-- ko foreach: history -->
|
|
|
- <li data-bind="click: function() { $parents[1].editorViewModel.openNotebook(uuid()); }, css: {'active': $parents[1].editorViewModel.selectedNotebook() !== null && $parents[1].editorViewModel.selectedNotebook().uuid() === uuid() }">
|
|
|
- <div class="muted pull-left" data-bind="momentFromNow: {data: lastExecuted, interval: 10000, titleFormat: 'LLL'}"></div>
|
|
|
- <div class="pull-right muted">
|
|
|
- <!-- ko switch: status -->
|
|
|
- <!-- ko case: 'running' -->
|
|
|
- <div class="history-status" data-bind="tooltip: { title: '${ _ko("Query running") }', placement: 'bottom' }"><i class="fa fa-fighter-jet fa-fw"></i></div>
|
|
|
- <!-- /ko -->
|
|
|
- <!-- ko case: 'failed' -->
|
|
|
- <div class="history-status" data-bind="tooltip: { title: '${ _ko("Query failed") }', placement: 'bottom' }"><i class="fa fa-exclamation fa-fw"></i></div>
|
|
|
- <!-- /ko -->
|
|
|
- <!-- ko case: 'available' -->
|
|
|
- <div class="history-status" data-bind="tooltip: { title: '${ _ko("Result available") }', placement: 'bottom' }"><i class="fa fa-check fa-fw"></i></div>
|
|
|
- <!-- /ko -->
|
|
|
- <!-- ko case: 'expired' -->
|
|
|
- <div class="history-status" data-bind="tooltip: { title: '${ _ko("Result expired") }', placement: 'bottom' }"><i class="fa fa-unlink fa-fw"></i></div>
|
|
|
- <!-- /ko -->
|
|
|
- <!-- /ko -->
|
|
|
- </div>
|
|
|
- <div class="clearfix"></div>
|
|
|
- <strong data-bind="text: name, attr: { title: uuid }"></strong>
|
|
|
- <div data-bind="highlight: { value: query }"></div>
|
|
|
- </li>
|
|
|
- <!-- /ko -->
|
|
|
- </ul>
|
|
|
- <!-- /ko -->
|
|
|
- </div>
|
|
|
- <!-- /ko -->
|
|
|
- <!-- /ko -->
|
|
|
- <!-- /ko -->
|
|
|
- </div>
|
|
|
-
|
|
|
- <div id="clearNotificationHistoryModal" class="modal hide fade" data-backdrop="false">
|
|
|
- <div class="modal-header">
|
|
|
- <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">×</span></button>
|
|
|
- <h2 class="modal-title">${_('Confirm History Clearing')}</h2>
|
|
|
- </div>
|
|
|
- <div class="modal-body">
|
|
|
- <p>${_('Are you sure you want to clear the task history?')}</p>
|
|
|
- </div>
|
|
|
- <div class="modal-footer">
|
|
|
- <a class="btn" data-dismiss="modal">${_('No')}</a>
|
|
|
- <a class="btn btn-danger disable-feedback" data-bind="click: function() { editorViewModel.selectedNotebook().clearHistory(); editorViewModel.selectedNotebook(null); }">${_('Yes')}</a>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </script>
|
|
|
-
|
|
|
- <script type="text/javascript">
|
|
|
- (function () {
|
|
|
- var HistoryPanel = function (params) {
|
|
|
- var self = this;
|
|
|
- self.historyPanelVisible = ko.observable(false);
|
|
|
-
|
|
|
- self.historyPanelVisible.subscribe(function (newVal) {
|
|
|
- if (newVal) {
|
|
|
- huePubSub.publish('hide.jobs.panel');
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- huePubSub.subscribe('hide.history.panel', function () {
|
|
|
- self.historyPanelVisible(false);
|
|
|
- });
|
|
|
-
|
|
|
- self.editorViewModel = new EditorViewModel(null, '', {
|
|
|
- user: '${ user.username }',
|
|
|
- userId: ${ user.id },
|
|
|
- languages: [{name: "Java", type: "java"}, {name: "Hive SQL", type: "hive"}], // TODO reuse
|
|
|
- snippetViewSettings: {
|
|
|
- hive: {
|
|
|
- placeHolder: '${ _("Example: SELECT * FROM tablename, or press CTRL + space") }',
|
|
|
- aceMode: 'ace/mode/hive',
|
|
|
- snippetImage: '${ static("beeswax/art/icon_beeswax_48.png") }',
|
|
|
- sqlDialect: true
|
|
|
- },
|
|
|
- impala: {
|
|
|
- placeHolder: '${ _("Example: SELECT * FROM tablename, or press CTRL + space") }',
|
|
|
- aceMode: 'ace/mode/impala',
|
|
|
- snippetImage: '${ static("impala/art/icon_impala_48.png") }',
|
|
|
- sqlDialect: true
|
|
|
- },
|
|
|
- java : {
|
|
|
- snippetIcon: 'fa-file-code-o'
|
|
|
- },
|
|
|
- shell: {
|
|
|
- snippetIcon: 'fa-terminal'
|
|
|
- },
|
|
|
- }
|
|
|
- });
|
|
|
- self.editorViewModel.editorMode(true);
|
|
|
- self.editorViewModel.isNotificationManager(true);
|
|
|
- self.editorViewModel.newNotebook();
|
|
|
-
|
|
|
- self.historyRunningJobs = ko.computed(function() {
|
|
|
- if (self.editorViewModel.selectedNotebook()) {
|
|
|
- return $.grep(self.editorViewModel.selectedNotebook().history(), function(task) { return task.status() == 'running'; });
|
|
|
- } else {
|
|
|
- return [];
|
|
|
- }
|
|
|
- });
|
|
|
- self.historyFinishedJobs = ko.computed(function() {
|
|
|
- if (self.editorViewModel.selectedNotebook()) {
|
|
|
- return $.grep(self.editorViewModel.selectedNotebook().history(), function(task) { return task.status() == 'available' || task.status() == 'failed'; });
|
|
|
- } else {
|
|
|
- return [];
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
-
|
|
|
- huePubSub.subscribe("notebook.task.submitted", function (history_id) {
|
|
|
- self.editorViewModel.openNotebook(history_id, null, true, function(){
|
|
|
- var notebook = self.editorViewModel.selectedNotebook();
|
|
|
- notebook.snippets()[0].progress.subscribe(function(val){
|
|
|
- if (val == 100){
|
|
|
- //self.indexingStarted(false);
|
|
|
- //self.isIndexing(false);
|
|
|
- //self.indexingSuccess(true);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- notebook.snippets()[0].status.subscribe(function(val){
|
|
|
- if (val == 'failed'){
|
|
|
- //self.isIndexing(false);
|
|
|
- //self.indexingStarted(false);
|
|
|
- //self.indexingError(true);
|
|
|
- } else if (val == 'available') {
|
|
|
- var snippet = notebook.snippets()[0];
|
|
|
- if (! snippet.result.handle().has_more_statements) {
|
|
|
- // TODO: Show finish notification and clicking on it does onSuccessUrl
|
|
|
- // or if still on initial spinner we redirect automatically to onSuccessUrl
|
|
|
- if (notebook.onSuccessUrl() && notebook.onSuccessUrl() !== 'assist.db.refresh') { // TODO: Similar if in in FB directory, also refresh FB dir
|
|
|
- huePubSub.publish('open.link', notebook.onSuccessUrl());
|
|
|
- }
|
|
|
-
|
|
|
- if (notebook.onSuccessUrl() == 'assist.db.refresh') {
|
|
|
- huePubSub.publish('assist.db.refresh', {sourceTypes: [snippet.type()]});
|
|
|
- } else if (notebook.onSuccessUrl()) {
|
|
|
- huePubSub.publish(notebook.pubSubUrl());
|
|
|
- }
|
|
|
- } else { // Perform last DROP statement execute
|
|
|
- snippet.execute();
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- notebook.snippets()[0].checkStatus();
|
|
|
-
|
|
|
- // Add to history
|
|
|
- notebook.history.unshift(
|
|
|
- notebook._makeHistoryRecord(
|
|
|
- notebook.onSuccessUrl(),
|
|
|
- notebook.description(),
|
|
|
- (new Date()).getTime(),
|
|
|
- notebook.snippets()[0].status(),
|
|
|
- notebook.name(),
|
|
|
- notebook.uuid()
|
|
|
- )
|
|
|
- );
|
|
|
-
|
|
|
- self.historyPanelVisible(true);
|
|
|
- });
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- ko.components.register('hue-history-panel', {
|
|
|
- viewModel: HistoryPanel,
|
|
|
- template: { element: 'hue-history-panel-template' }
|
|
|
- });
|
|
|
- })();
|
|
|
- </script>
|
|
|
-
|
|
|
- <script type="text/html" id="hue-job-browser-links-template">
|
|
|
- <div class="btn-group pull-right">
|
|
|
- <a class="btn btn-flat" style="padding-right: 4px" title="${_('Job browser')}" data-bind="hueLink: '/jobbrowser#!jobs', click: function(){ huePubSub.publish('hide.jobs.panel'); }">
|
|
|
- <span>${ _('Jobs') }</span>
|
|
|
- </a>
|
|
|
- <button class="btn btn-flat btn-toggle-jobs-panel" title="${_('Jobs preview')}" data-bind="click: function() { huePubSub.publish('toggle.jobs.panel'); }, style: {'paddingLeft': jobCount() > 0 ? '0': '4px'}">
|
|
|
- <span class="jobs-badge" data-bind="visible: jobCount() > 0, text: jobCount"></span>
|
|
|
- <i class="fa fa-tasks"></i>
|
|
|
- </button>
|
|
|
- </div>
|
|
|
- </script>
|
|
|
-
|
|
|
- <script type="text/javascript">
|
|
|
- (function () {
|
|
|
- var JB_CHECK_INTERVAL_IN_MILLIS = 30000;
|
|
|
-
|
|
|
- var JobBrowserPanel = function (params) {
|
|
|
- var self = this;
|
|
|
-
|
|
|
- huePubSub.subscribe('hide.jobs.panel', function () {
|
|
|
- $('#jobsPanel').hide();
|
|
|
- });
|
|
|
-
|
|
|
- huePubSub.subscribe('show.jobs.panel', function (section) {
|
|
|
- huePubSub.publish('hide.history.panel');
|
|
|
- $('#jobsPanel').show();
|
|
|
- huePubSub.publish('mini.jb.navigate', section && section.interface ? section.interface : 'jobs');
|
|
|
- if (section && section.id) {
|
|
|
- huePubSub.publish('mini.jb.open.job', section.id);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- huePubSub.subscribe('toggle.jobs.panel', function () {
|
|
|
- if ($('#jobsPanel').is(':visible')){
|
|
|
- huePubSub.publish('hide.jobs.panel');
|
|
|
- }
|
|
|
- else {
|
|
|
- huePubSub.publish('show.jobs.panel');
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- self.jobCounts = ko.observable({'yarn': 0, 'schedules': 0});
|
|
|
- self.jobCount = ko.pureComputed(function() {
|
|
|
- var total = 0;
|
|
|
- Object.keys(self.jobCounts()).forEach(function (value) {
|
|
|
- total += self.jobCounts()[value];
|
|
|
- });
|
|
|
- return total;
|
|
|
- });
|
|
|
- self.onePageViewModel = params.onePageViewModel;
|
|
|
-
|
|
|
- var lastYarnBrowserRequest = null;
|
|
|
- var checkYarnBrowserStatus = function() {
|
|
|
- return $.post("/jobbrowser/jobs/", {
|
|
|
- "format": "json",
|
|
|
- "state": "running",
|
|
|
- "user": "${user.username}"
|
|
|
- },
|
|
|
- function(data) {
|
|
|
- if (data != null && data.jobs != null) {
|
|
|
- huePubSub.publish('jobbrowser.data', data.jobs);
|
|
|
- self.jobCounts()['yarn'] = data.jobs.length;
|
|
|
- self.jobCounts.valueHasMutated();
|
|
|
- }
|
|
|
- })
|
|
|
- };
|
|
|
- var lastScheduleBrowserRequest = null;
|
|
|
- var checkScheduleBrowserStatus = function() {
|
|
|
- return $.post("/jobbrowser/api/jobs", {
|
|
|
- interface: ko.mapping.toJSON("schedules"),
|
|
|
- filters: ko.mapping.toJSON([
|
|
|
- {"text": "user:${user.username}"},
|
|
|
- {"time": {"time_value": 7, "time_unit": "days"}},
|
|
|
- {"states": ["running"]},
|
|
|
- {"pagination": {"page": 1, "offset": 1, "limit": 1}}
|
|
|
- ])
|
|
|
- },
|
|
|
- function(data) {
|
|
|
- if (data != null && data.total != null) {
|
|
|
- huePubSub.publish('jobbrowser.schedule.data', data.apps);
|
|
|
- self.jobCounts()['schedules'] = data.total;
|
|
|
- self.jobCounts.valueHasMutated();
|
|
|
- }
|
|
|
- })
|
|
|
- };
|
|
|
-
|
|
|
- var checkJobBrowserStatus = function() {
|
|
|
- lastYarnBrowserRequest = checkYarnBrowserStatus();
|
|
|
- lastScheduleBrowserRequest = checkScheduleBrowserStatus();
|
|
|
-
|
|
|
- $.when.apply($, [lastYarnBrowserRequest, lastScheduleBrowserRequest])
|
|
|
- .done(function () {
|
|
|
- checkJobBrowserStatusIdx = window.setTimeout(checkJobBrowserStatus, JB_CHECK_INTERVAL_IN_MILLIS);
|
|
|
- })
|
|
|
- .fail(function () {
|
|
|
- window.clearTimeout(checkJobBrowserStatusIdx);
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
-
|
|
|
- // Load the mini jobbrowser
|
|
|
- $.ajax({
|
|
|
- url: '/jobbrowser/apps?is_embeddable=true&is_mini=true',
|
|
|
- beforeSend: function (xhr) {
|
|
|
- xhr.setRequestHeader('X-Requested-With', 'Hue');
|
|
|
- },
|
|
|
- dataType: 'html',
|
|
|
- success: function (response) {
|
|
|
- var r = params.onePageViewModel.processHeaders(response);
|
|
|
- $('#mini_jobbrowser').html(r);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- var checkJobBrowserStatusIdx = window.setTimeout(checkJobBrowserStatus, 10);
|
|
|
-
|
|
|
- huePubSub.subscribe('check.job.browser', checkYarnBrowserStatus);
|
|
|
- huePubSub.subscribe('check.schedules.browser', checkScheduleBrowserStatus);
|
|
|
- };
|
|
|
-
|
|
|
- ko.components.register('hue-job-browser-links', {
|
|
|
- viewModel: JobBrowserPanel,
|
|
|
- template: { element: 'hue-job-browser-links-template' }
|
|
|
- });
|
|
|
- })();
|
|
|
- </script>
|
|
|
-
|
|
|
- <script type="text/html" id="hue-favorite-app-template">
|
|
|
- <!-- ko if: isHue4 -->
|
|
|
- <div class="inline pointer favorite-app" data-bind="click: setAsFavoriteApp, tooltip: { placement: 'bottom', title: isFavorite() ? '${ _ko("Unset from default application") }' : '${ _ko("Set as default application") }' }">
|
|
|
- <i class="fa inactive-action" data-bind="css: { 'fa-star-o': !isFavorite(), 'fa-star': isFavorite }"></i>
|
|
|
- </div>
|
|
|
- <!-- /ko -->
|
|
|
- </script>
|
|
|
-
|
|
|
- <script type="text/javascript">
|
|
|
- (function () {
|
|
|
-
|
|
|
- var FavoriteApp = function (params) {
|
|
|
- var self = this;
|
|
|
- self.isHue4 = ko.observable(params.hue4);
|
|
|
- self.isFavorite = ko.observable(false);
|
|
|
- self.app = params.app;
|
|
|
- self.interpreter = params.interpreter;
|
|
|
-
|
|
|
- self.parseCurrentFavorite = function (data, announce) {
|
|
|
- self.isFavorite(false);
|
|
|
- if (data.status === 0 && data.data && data.data.default_app) {
|
|
|
- try {
|
|
|
- var defaultApp = JSON.parse(data.data.default_app);
|
|
|
- self.isFavorite(defaultApp.app === self.app && ((self.app === 'editor' && defaultApp.interpreter === self.interpreter) || self.app !== 'editor'));
|
|
|
- if (announce) {
|
|
|
- huePubSub.publish('hue.new.default.app', defaultApp);
|
|
|
- }
|
|
|
- } catch (e) {
|
|
|
- console.error('${ _ko("There was a problem decoding the default app setting.") }');
|
|
|
- }
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- self.setAsFavoriteApp = function (vm, e) {
|
|
|
- e.originalEvent.stopPropagation();
|
|
|
- e.originalEvent.stopImmediatePropagation();
|
|
|
- var postParams = {
|
|
|
- app: self.app
|
|
|
- };
|
|
|
- if (self.interpreter !== '') {
|
|
|
- postParams['interpreter'] = self.interpreter;
|
|
|
- }
|
|
|
- var post = {};
|
|
|
- if (self.isFavorite()) {
|
|
|
- post['delete'] = true;
|
|
|
- }
|
|
|
- else {
|
|
|
- post['set'] = ko.mapping.toJSON(postParams);
|
|
|
- }
|
|
|
- $.post('/desktop/api2/user_preferences/default_app', post, function (data) {
|
|
|
- self.parseCurrentFavorite(data, true);
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- if (self.isHue4()) {
|
|
|
- // Load the fav app status
|
|
|
- $.get('/desktop/api2/user_preferences/default_app', function (data) {
|
|
|
- self.parseCurrentFavorite(data);
|
|
|
- });
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- ko.components.register('hue-favorite-app', {
|
|
|
- viewModel: FavoriteApp,
|
|
|
- template: {element: 'hue-favorite-app-template'}
|
|
|
- });
|
|
|
- })();
|
|
|
- </script>
|
|
|
-
|
|
|
- <script type="text/html" id="inline-autocomplete-template">
|
|
|
- <div class="inline-autocomplete-container">
|
|
|
- <div>
|
|
|
- <input class="inline-autocomplete-input" autocorrect="off" autocapitalize="off" spellcheck="false" type="text" data-bind="attr: { 'placeHolder' : hasFocus() ? '' : placeHolder }, textInput: searchInput, hasFocus: hasFocus, clearable: { value: searchInput, onClear: onClear }">
|
|
|
- <input class="inline-autocomplete-autocomplete" disabled type="text" data-bind="value: inlineAutocomplete">
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </script>
|
|
|
-
|
|
|
- <script type="text/javascript">
|
|
|
- (function () {
|
|
|
-
|
|
|
- var getSortedFacets = function (facetIndex) {
|
|
|
- var result = Object.keys(facetIndex);
|
|
|
- result.sort(function (a, b) {
|
|
|
- if (facetIndex[a] > facetIndex[b]) {
|
|
|
- return -1;
|
|
|
- }
|
|
|
- if (facetIndex[b] > facetIndex[a]) {
|
|
|
- return 1;
|
|
|
- }
|
|
|
- return a.localeCompare(b);
|
|
|
- });
|
|
|
- return result;
|
|
|
- };
|
|
|
-
|
|
|
- var InlineAutocomplete = function (params) {
|
|
|
- var self = this;
|
|
|
- self.disposals = [];
|
|
|
-
|
|
|
- self.placeHolder = params.placeHolder || '${ _('Filter...') }';
|
|
|
- self.hasFocus = params.hasFocus || ko.observable();
|
|
|
- self.querySpec = params.querySpec;
|
|
|
- self.autocompleteFromEntries = params.autocompleteFromEntries || function () {};
|
|
|
- self.facets = params.facets || [];
|
|
|
- self.knownFacetValues = params.knownFacetValues || {};
|
|
|
- self.uniqueFacets = !!params.uniqueFacets;
|
|
|
-
|
|
|
- self.searchInput = ko.observable('');
|
|
|
- self.inlineAutocomplete = ko.observable('');
|
|
|
- self.lastResult = {};
|
|
|
-
|
|
|
- self.querySpec({
|
|
|
- query: '',
|
|
|
- facets: {},
|
|
|
- text: []
|
|
|
- });
|
|
|
-
|
|
|
- var querySpecSub = self.querySpec.subscribe(function (newVal) {
|
|
|
- if (!newVal || !newVal.query) {
|
|
|
- self.searchInput('');
|
|
|
- self.inlineAutocomplete('');
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- self.disposals.push(function () {
|
|
|
- querySpecSub.dispose();
|
|
|
- });
|
|
|
-
|
|
|
- self.onClear = function () {
|
|
|
- if (params.onClear) {
|
|
|
- params.onClear();
|
|
|
- }
|
|
|
- self.inlineAutocomplete('');
|
|
|
- };
|
|
|
-
|
|
|
- if (params.triggerObservable) {
|
|
|
- var triggerSub = params.triggerObservable.subscribe(function () {
|
|
|
- self.autocomplete(self.searchInput(), self.inlineAutocomplete);
|
|
|
- });
|
|
|
- self.disposals.push(function () {
|
|
|
- triggerSub.dispose();
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- var inputSub = self.searchInput.subscribe(function (newValue) {
|
|
|
- if (newValue === '' && self.querySpec() && self.querySpec().query === '') {
|
|
|
- self.inlineAutocomplete('');
|
|
|
- return;
|
|
|
- }
|
|
|
- if (newValue === '' && self.querySpec() && self.querySpec().query !== '') {
|
|
|
- self.querySpec({
|
|
|
- query: '',
|
|
|
- facets: {},
|
|
|
- text: []
|
|
|
- });
|
|
|
- } else {
|
|
|
- // TODO: Get cursor position and split to before and after
|
|
|
- self.lastResult = globalSearchParser.parseGlobalSearch(newValue, '');
|
|
|
- if (hueDebug && hueDebug.showGlobalSearchParseResults) {
|
|
|
- console.log(self.lastResult);
|
|
|
- }
|
|
|
- var querySpec = { query: newValue };
|
|
|
- if (self.lastResult.facets) {
|
|
|
-
|
|
|
- var knownFacetValues = ko.unwrap(self.knownFacetValues);
|
|
|
- var cleanFacets = {};
|
|
|
- Object.keys(self.lastResult.facets).forEach(function (facet) {
|
|
|
- if (!knownFacetValues[facet]) {
|
|
|
- cleanFacets[facet] = self.lastResult.facets[facet];
|
|
|
- } else {
|
|
|
- cleanFacets[facet] = {};
|
|
|
- Object.keys(self.lastResult.facets[facet]).forEach(function (value) {
|
|
|
- if (knownFacetValues[facet][value]) {
|
|
|
- cleanFacets[facet][value] = self.lastResult.facets[facet][value];
|
|
|
- } else {
|
|
|
- var found = false;
|
|
|
- // Find the closest match, i.e. type:s -> type: [string, smallint, ...]
|
|
|
- getSortedFacets(knownFacetValues[facet]).forEach(function (knownValue) {
|
|
|
- if (knownValue.toLowerCase().indexOf(value.toLowerCase()) === 0) {
|
|
|
- if (!cleanFacets[facet][knownValue]) {
|
|
|
- cleanFacets[facet][knownValue] = [];
|
|
|
- }
|
|
|
- cleanFacets[facet][knownValue] = true;
|
|
|
- found = true
|
|
|
- }
|
|
|
- });
|
|
|
- if (!found) {
|
|
|
- cleanFacets[facet][value] = self.lastResult.facets[facet][value];
|
|
|
- }
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
- });
|
|
|
- querySpec.facets = cleanFacets;
|
|
|
- }
|
|
|
- if (self.lastResult.text) {
|
|
|
- querySpec.text = self.lastResult.text;
|
|
|
- }
|
|
|
- self.querySpec(querySpec);
|
|
|
- }
|
|
|
-
|
|
|
- if (newValue === '') {
|
|
|
- self.inlineAutocomplete('');
|
|
|
- } else if (self.inlineAutocomplete() === newValue || self.inlineAutocomplete().indexOf(newValue) !== 0) {
|
|
|
- self.autocomplete(newValue, self.inlineAutocomplete);
|
|
|
- } else if (self.inlineAutocomplete().indexOf(newValue) === 0) {
|
|
|
- var newAutocomp = self.inlineAutocomplete();
|
|
|
- while (newAutocomp.lastIndexOf(' ') >= newValue.length) {
|
|
|
- newAutocomp = newAutocomp.substring(0, newAutocomp.lastIndexOf(' '));
|
|
|
- }
|
|
|
- if (newAutocomp !== self.inlineAutocomplete()) {
|
|
|
- self.inlineAutocomplete(newAutocomp);
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- self.disposals.push(function () {
|
|
|
- inputSub.dispose();
|
|
|
- });
|
|
|
-
|
|
|
- var onKeyDown = function (event) {
|
|
|
- if (!self.hasFocus()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (event.keyCode === 32 && event.ctrlKey) { // Ctrl-Space
|
|
|
- self.autocomplete(self.searchInput(), self.inlineAutocomplete);
|
|
|
- return;
|
|
|
- }
|
|
|
- if (event.keyCode === 39 && self.inlineAutocomplete() !== '' && self.inlineAutocomplete() !== self.searchInput()) { // Right arrow
|
|
|
- // TODO: Check that cursor is last
|
|
|
- self.searchInput(self.inlineAutocomplete());
|
|
|
- return;
|
|
|
- }
|
|
|
- if (event.keyCode === 9 && self.inlineAutocomplete() !== self.searchInput()) { // Tab
|
|
|
- self.searchInput(self.inlineAutocomplete());
|
|
|
- event.preventDefault();
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- self.disposals.push(function () {
|
|
|
- $(document).off('keydown', onKeyDown);
|
|
|
- });
|
|
|
-
|
|
|
- var focusSub = self.hasFocus.subscribe(function (newVal) {
|
|
|
- if (!newVal) {
|
|
|
- self.inlineAutocomplete('');
|
|
|
- $(document).off('keydown', onKeyDown);
|
|
|
- } else if (self.searchInput() !== '') {
|
|
|
- self.autocomplete(self.searchInput(), self.inlineAutocomplete);
|
|
|
- $(document).on('keydown', onKeyDown);
|
|
|
- } else {
|
|
|
- $(document).on('keydown', onKeyDown);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- self.disposals.push(function () {
|
|
|
- focusSub.dispose();
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- InlineAutocomplete.prototype.dispose = function () {
|
|
|
- var self = this;
|
|
|
- while (self.disposals.length) {
|
|
|
- self.disposals.pop()();
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- InlineAutocomplete.prototype.autocomplete = function (text, callback) {
|
|
|
- var self = this;
|
|
|
- if (!self.lastResult) {
|
|
|
- callback('');
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- var partial, nonPartial;
|
|
|
- var partialMatch = text.match(/([^:\s]+)$/i);
|
|
|
- if (partialMatch) {
|
|
|
- partial = partialMatch[0];
|
|
|
- nonPartial = text.substring(0, text.length - partial.length);
|
|
|
- } else {
|
|
|
- partial = '';
|
|
|
- nonPartial = text;
|
|
|
- }
|
|
|
-
|
|
|
- var newAutocomplete = '';
|
|
|
- var partialLower = partial.toLowerCase();
|
|
|
- if (self.lastResult.suggestFacets) {
|
|
|
- var existingFacetIndex = {};
|
|
|
- if (self.uniqueFacets && self.lastResult.facets) {
|
|
|
- Object.keys(self.lastResult.facets).forEach(function (facet) {
|
|
|
- existingFacetIndex[facet.toLowerCase()] = true;
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- var suggestion = nonPartial + partial;
|
|
|
- var isLowerCase = suggestion.length > 0 && suggestion.toLowerCase() === suggestion;
|
|
|
- self.facets.every(function (facet) {
|
|
|
- if (self.uniqueFacets && existingFacetIndex[facet]) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- if (partial.length === 0 || facet.indexOf(partialLower) === 0) {
|
|
|
- var remainder = facet.substring(partial.length);
|
|
|
- suggestion += isLowerCase ? remainder : remainder.toUpperCase();
|
|
|
- suggestion += ':';
|
|
|
- newAutocomplete = suggestion;
|
|
|
- return false;
|
|
|
- }
|
|
|
- return true;
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- if (self.lastResult.suggestFacetValues && !newAutocomplete) {
|
|
|
- var facetValues = ko.unwrap(self.knownFacetValues);
|
|
|
- if (facetValues[self.lastResult.suggestFacetValues.toLowerCase()]) {
|
|
|
- getSortedFacets(facetValues[self.lastResult.suggestFacetValues.toLowerCase()]).every(function (value) {
|
|
|
- if (value.toLowerCase().indexOf(partialLower) === 0) {
|
|
|
- newAutocomplete = nonPartial + partial + value.substring(partial.length, value.length);
|
|
|
- return false;
|
|
|
- }
|
|
|
- return true;
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (partial !== '' && self.lastResult.suggestResults && !newAutocomplete) {
|
|
|
- newAutocomplete = self.autocompleteFromEntries(nonPartial, partial);
|
|
|
- }
|
|
|
-
|
|
|
- if (!newAutocomplete) {
|
|
|
- callback('');
|
|
|
- } else if (newAutocomplete !== self.inlineAutocomplete()) {
|
|
|
- callback(newAutocomplete);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- ko.components.register('inline-autocomplete', {
|
|
|
- viewModel: InlineAutocomplete,
|
|
|
- template: {element: 'inline-autocomplete-template'}
|
|
|
- });
|
|
|
- })();
|
|
|
- </script>
|
|
|
-
|
|
|
- <script type="text/html" id="hue-global-search-template">
|
|
|
- <!-- ko component: {
|
|
|
- name: 'inline-autocomplete',
|
|
|
- params: {
|
|
|
- hasFocus: searchHasFocus,
|
|
|
- placeHolder: '${ _ko('Search data and saved documents...') }',
|
|
|
- querySpec: querySpec,
|
|
|
- onClear: function () { selectedIndex(null); searchResultVisible(false); },
|
|
|
- facets: ['type', 'tags'],
|
|
|
- knownFacetValues: knownFacetValues,
|
|
|
- autocompleteFromEntries: autocompleteFromEntries,
|
|
|
- triggerObservable: searchResultCategories
|
|
|
- }
|
|
|
- } --><!-- /ko -->
|
|
|
- <!-- ko if: searchResultVisible-->
|
|
|
- <div class="global-search-results" data-bind="onClickOutside: onResultClickOutside, css: { 'global-search-empty' : searchResultCategories().length === 0 }, style: { 'height' : heightWhenDragging }">
|
|
|
- <!-- ko hueSpinner: { spin: loading() && searchResultCategories().length === 0 , center: true, size: 'large' } --><!-- /ko -->
|
|
|
- <!-- ko if: !loading() && searchResultCategories().length === 0 -->
|
|
|
- <div>${ _('No results found.') }</div>
|
|
|
- <!-- /ko -->
|
|
|
- <!-- ko if: searchResultCategories().length > 0 -->
|
|
|
- <div class="global-search-alternatives" data-bind="foreach: searchResultCategories">
|
|
|
- <div class="global-search-category">
|
|
|
- <div class="global-search-category-header" data-bind="text: label"></div>
|
|
|
- <ul data-bind="foreach: result">
|
|
|
- <li data-bind="multiClick: {
|
|
|
- click: function () { $parents[1].resultSelected($parentContext.$index(), $index()) },
|
|
|
- dblClick: function () { $parents[1].resultSelected($parentContext.$index(), $index()); $parents[1].openResult(); }
|
|
|
- }, html: label, css: { 'selected': $parents[1].selectedResult() === $data }, draggableText: { text: draggable, meta: draggableMeta }"></li>
|
|
|
- </ul>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="global-search-preview" style="overflow: auto;">
|
|
|
- <!-- ko with: selectedResult -->
|
|
|
- <!-- ko switch: type -->
|
|
|
- <!-- ko case: ['database', 'document', 'field', 'table', 'view'] -->
|
|
|
- <!-- ko component: { name: 'context-popover-contents-global-search', params: { data: data, globalSearch: $parent } } --><!-- /ko -->
|
|
|
- <!-- /ko -->
|
|
|
- <!-- ko case: $default -->
|
|
|
- <pre data-bind="text: ko.mapping.toJSON($data)"></pre>
|
|
|
- <!-- /ko -->
|
|
|
- <!-- /ko -->
|
|
|
- <!--/ko -->
|
|
|
- </div>
|
|
|
- <!-- /ko -->
|
|
|
- </div>
|
|
|
- <!-- /ko -->
|
|
|
- </script>
|
|
|
-
|
|
|
- <script type="text/javascript">
|
|
|
- (function () {
|
|
|
-
|
|
|
- var GlobalSearch = function (params) {
|
|
|
- var self = this;
|
|
|
- self.apiHelper = ApiHelper.getInstance();
|
|
|
- self.knownFacetValues = ko.observable({});
|
|
|
-
|
|
|
- self.autocompleteThrottle = -1;
|
|
|
- self.fetchThrottle = -1;
|
|
|
-
|
|
|
- self.searchHasFocus = ko.observable(false);
|
|
|
- self.querySpec = ko.observable();
|
|
|
- self.searchActive = ko.observable(false);
|
|
|
- self.searchResultVisible = ko.observable(false);
|
|
|
- self.heightWhenDragging = ko.observable(null);
|
|
|
- self.searchResultCategories = ko.observableArray([]);
|
|
|
- self.selectedIndex = ko.observable();
|
|
|
- self.loading = ko.observable(false);
|
|
|
-
|
|
|
- self.selectedResult = ko.pureComputed(function () {
|
|
|
- if (self.selectedIndex()) {
|
|
|
- return self.searchResultCategories()[self.selectedIndex().categoryIndex].result[self.selectedIndex().resultIndex]
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- var deferredCloseIfVisible = function () {
|
|
|
- window.setTimeout(function () {
|
|
|
- if (self.searchResultVisible()) {
|
|
|
- self.close();
|
|
|
- }
|
|
|
- }, 0);
|
|
|
- };
|
|
|
-
|
|
|
- huePubSub.subscribe('context.popover.open.in.metastore', deferredCloseIfVisible);
|
|
|
- huePubSub.subscribe('context.popover.show.in.assist', deferredCloseIfVisible);
|
|
|
- huePubSub.subscribe('sample.error.insert.click', deferredCloseIfVisible);
|
|
|
-
|
|
|
- huePubSub.subscribe('draggable.text.started', function (meta) {
|
|
|
- // We have to set the height to 0 when dragging a text, just closing the results will break the
|
|
|
- // jQuery draggable plugin
|
|
|
- if (meta.source === 'globalSearch') {
|
|
|
- huePubSub.subscribeOnce('draggable.text.stopped', function () {
|
|
|
- self.heightWhenDragging(null);
|
|
|
- self.close();
|
|
|
- });
|
|
|
- self.heightWhenDragging(0);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- self.querySpec.subscribe(function (newValue) {
|
|
|
- if (newValue && newValue.query !== '') {
|
|
|
- window.clearTimeout(self.fetchThrottle);
|
|
|
- self.fetchThrottle = window.setTimeout(function () {
|
|
|
- self.fetchResults(newValue.query);
|
|
|
- }, 500);
|
|
|
- } else {
|
|
|
- self.selectedIndex(undefined);
|
|
|
- self.searchResultCategories([]);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- self.autocompleteFromEntries = function (lastNonPartial, partial) {
|
|
|
- var result;
|
|
|
- var partialLower = partial.toLowerCase();
|
|
|
- self.searchResultCategories().every(function (category) {
|
|
|
- return category.result.every(function (entry) {
|
|
|
- if (category.type === 'documents' && entry.data.originalName.toLowerCase().indexOf(partialLower) === 0) {
|
|
|
- result = lastNonPartial + partial + entry.data.originalName.substring(partial.length, entry.data.originalName.length);
|
|
|
- return false;
|
|
|
- } else if (entry.data.selectionName && entry.data.selectionName.toLowerCase().indexOf(partialLower) === 0) {
|
|
|
- result = lastNonPartial + partial + entry.data.selectionName.substring(partial.length, entry.data.selectionName.length);
|
|
|
- return false;
|
|
|
- }
|
|
|
- return true;
|
|
|
- });
|
|
|
- });
|
|
|
- return result;
|
|
|
- };
|
|
|
-
|
|
|
- self.searchHasFocus.subscribe(function (newVal) {
|
|
|
- if (newVal && self.querySpec() && self.querySpec().query !== '') {
|
|
|
- if (!self.searchResultVisible()) {
|
|
|
- self.searchResultVisible(true);
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- self.searchResultVisible.subscribe(function (newVal) {
|
|
|
- if (newVal) {
|
|
|
- self.heightWhenDragging(null);
|
|
|
- } else {
|
|
|
- self.selectedIndex(undefined);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // TODO: Consider attach/detach on focus
|
|
|
- $(document).keydown(function (event) {
|
|
|
- if (!self.searchHasFocus() && !self.searchResultVisible()) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (event.keyCode === 13 && self.searchHasFocus() && self.querySpec() && self.querySpec().query !== '') {
|
|
|
- window.clearTimeout(self.fetchThrottle);
|
|
|
- self.fetchResults(self.querySpec().query);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (self.searchResultVisible() && self.searchResultCategories().length > 0) {
|
|
|
- var currentIndex = self.selectedIndex();
|
|
|
- if (event.keyCode === 40) { // Down
|
|
|
- self.searchHasFocus(false);
|
|
|
- if (currentIndex && !(self.searchResultCategories()[currentIndex.categoryIndex].result.length <= currentIndex.resultIndex + 1 && self.searchResultCategories().length <= currentIndex.categoryIndex + 1)) {
|
|
|
- if (self.searchResultCategories()[currentIndex.categoryIndex].result.length <= currentIndex.resultIndex + 1) {
|
|
|
- self.selectedIndex({ categoryIndex: currentIndex.categoryIndex + 1, resultIndex: 0 });
|
|
|
- } else {
|
|
|
- self.selectedIndex({ categoryIndex: currentIndex.categoryIndex, resultIndex: currentIndex.resultIndex + 1})
|
|
|
- }
|
|
|
- } else {
|
|
|
- self.selectedIndex({ categoryIndex: 0, resultIndex: 0 });
|
|
|
- }
|
|
|
- event.preventDefault();
|
|
|
- } else if (event.keyCode === 38) { // Up
|
|
|
- self.searchHasFocus(false);
|
|
|
- if (currentIndex && !(currentIndex.categoryIndex === 0 && currentIndex.resultIndex === 0)) {
|
|
|
- if (currentIndex.resultIndex === 0) {
|
|
|
- self.selectedIndex({ categoryIndex: currentIndex.categoryIndex - 1, resultIndex: self.searchResultCategories()[currentIndex.categoryIndex - 1].result.length - 1 });
|
|
|
- } else {
|
|
|
- self.selectedIndex({ categoryIndex: currentIndex.categoryIndex, resultIndex: currentIndex.resultIndex - 1 });
|
|
|
- }
|
|
|
- } else {
|
|
|
- self.selectedIndex({ categoryIndex: self.searchResultCategories().length - 1, resultIndex: self.searchResultCategories()[self.searchResultCategories().length - 1].result.length - 1 });
|
|
|
- }
|
|
|
- event.preventDefault();
|
|
|
- } else if (event.keyCode === 13 && !self.searchHasFocus() && self.selectedIndex()) { // Enter
|
|
|
- self.openResult();
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- GlobalSearch.prototype.close = function () {
|
|
|
- var self = this;
|
|
|
- self.searchResultVisible(false);
|
|
|
- self.querySpec({
|
|
|
- query: '',
|
|
|
- facets: {},
|
|
|
- text: []
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- GlobalSearch.prototype.openResult = function () {
|
|
|
- var self = this;
|
|
|
- var selectedResult = self.selectedResult();
|
|
|
- if (['database', 'table', 'field', 'view'].indexOf(selectedResult.type) !== -1) {
|
|
|
- huePubSub.publish('context.popover.show.in.assist');
|
|
|
- } else if (selectedResult.type === 'document') {
|
|
|
- huePubSub.publish('open.link', '/hue' + selectedResult.data.link);
|
|
|
- }
|
|
|
- self.close();
|
|
|
- };
|
|
|
-
|
|
|
- GlobalSearch.prototype.resultSelected = function (categoryIndex, resultIndex) {
|
|
|
- var self = this;
|
|
|
- if (!self.selectedIndex() || !(self.selectedIndex().categoryIndex === categoryIndex && self.selectedIndex().resultIndex === resultIndex)) {
|
|
|
- self.selectedIndex({ categoryIndex: categoryIndex, resultIndex: resultIndex });
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- GlobalSearch.prototype.onResultClickOutside = function () {
|
|
|
- var self = this;
|
|
|
- if (!self.searchResultVisible() || self.searchHasFocus()) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- self.searchResultVisible(false);
|
|
|
- window.clearTimeout(self.fetchThrottle);
|
|
|
- window.clearTimeout(self.autocompleteThrottle);
|
|
|
- };
|
|
|
-
|
|
|
- GlobalSearch.prototype.mainSearchSelect = function (entry) {
|
|
|
- if (entry.data && entry.data.link) {
|
|
|
- huePubSub.publish('open.link', entry.data.link);
|
|
|
- } else if (!/:\s*$/.test(entry.value)) {
|
|
|
- huePubSub.publish('assist.show.sql');
|
|
|
- huePubSub.publish('assist.db.search', entry.value);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- var CATEGORIES = {
|
|
|
- 'table': '${ _('Tables') }',
|
|
|
- 'database': '${ _('Databases') }',
|
|
|
- 'field': '${ _('Columns') }',
|
|
|
- 'partition': '${ _('Partitions') }',
|
|
|
- 'view': '${ _('Views') }'
|
|
|
- };
|
|
|
-
|
|
|
- GlobalSearch.prototype.fetchResults = function (query) {
|
|
|
- var self = this;
|
|
|
- self.loading(true);
|
|
|
- self.searchResultVisible(true);
|
|
|
- var hueDocsPromise = self.apiHelper.fetchHueDocsInteractive(query);
|
|
|
- var navPromise = self.apiHelper.fetchNavEntitiesInteractive(query);
|
|
|
- hueDocsPromise.done(function (data) {
|
|
|
- var categories = self.searchResultCategories().filter(function (category) {
|
|
|
- return category.type !== 'documents';
|
|
|
- });
|
|
|
- var docCategory = {
|
|
|
- label: '${ _('Documents') }',
|
|
|
- result: [],
|
|
|
- type: 'documents'
|
|
|
- };
|
|
|
-
|
|
|
- data.results.forEach(function (doc) {
|
|
|
- docCategory.result.push({
|
|
|
- label: doc.hue_name,
|
|
|
- draggable: doc.originalName,
|
|
|
- draggableMeta: {
|
|
|
- source: 'globalSearch'
|
|
|
- },
|
|
|
- type: 'document',
|
|
|
- data: doc
|
|
|
- })
|
|
|
- });
|
|
|
- if (docCategory.result.length) {
|
|
|
- categories.unshift(docCategory);
|
|
|
- }
|
|
|
- self.selectedIndex(undefined);
|
|
|
- self.searchResultCategories(categories);
|
|
|
- });
|
|
|
-
|
|
|
- navPromise.done(function (data) {
|
|
|
- if (data.facets) {
|
|
|
- var facetValues = ko.unwrap(self.knownFacetValues);
|
|
|
- Object.keys(data.facets).forEach(function (facet) {
|
|
|
- if (!facetValues[facet] && Object.keys(data.facets[facet]).length > 0) {
|
|
|
- facetValues[facet] = {};
|
|
|
- }
|
|
|
- Object.keys(data.facets[facet]).forEach(function (facetKey) {
|
|
|
- facetValues[facet][facetKey] = data.facets[facet][facetKey];
|
|
|
- });
|
|
|
- })
|
|
|
- }
|
|
|
- var categories = self.searchResultCategories().length > 0 && self.searchResultCategories()[0].type === 'documents' ? [self.searchResultCategories()[0]] : [];
|
|
|
- var newCategories = {};
|
|
|
- data.results.forEach(function (result) {
|
|
|
- var typeLower = result.type.toLowerCase();
|
|
|
- if (CATEGORIES[typeLower]) {
|
|
|
- var category = newCategories[typeLower];
|
|
|
- if (!category) {
|
|
|
- category = {
|
|
|
- label: CATEGORIES[typeLower],
|
|
|
- result: []
|
|
|
- };
|
|
|
- newCategories[typeLower] = category;
|
|
|
- }
|
|
|
- var meta = {
|
|
|
- source: 'globalSearch'
|
|
|
- };
|
|
|
- if (result.type.toLowerCase() === 'database') {
|
|
|
- meta.type = 'sql';
|
|
|
- meta.database = result.originalName
|
|
|
- } else if (result.type.toLowerCase() === 'table') {
|
|
|
- meta.type = 'sql';
|
|
|
- var split = result.originalName.split('.');
|
|
|
- if (split.length == 2) {
|
|
|
- meta.database = split[0];
|
|
|
- meta.table = split[1];
|
|
|
- }
|
|
|
- } else if (result.type.toLowerCase() === 'field') {
|
|
|
- meta.type = 'sql';
|
|
|
- var split = result.originalName.split('.');
|
|
|
- if (split.length >= 3) {
|
|
|
- meta.database = split[0];
|
|
|
- meta.table = split[1];
|
|
|
- meta.column = split[2];
|
|
|
- }
|
|
|
- }
|
|
|
- category.result.push({
|
|
|
- label: result.hue_name || result.originalName,
|
|
|
- draggable: result.originalName,
|
|
|
- draggableMeta: meta,
|
|
|
- type: typeLower,
|
|
|
- data: result
|
|
|
- })
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- Object.keys(newCategories).forEach(function (key) {
|
|
|
- categories.push(newCategories[key]);
|
|
|
- });
|
|
|
- self.selectedIndex(undefined);
|
|
|
- self.searchResultCategories(categories);
|
|
|
- });
|
|
|
-
|
|
|
- $.when.apply($, [navPromise, hueDocsPromise]).always(function () {
|
|
|
- self.loading(false);
|
|
|
- })
|
|
|
- };
|
|
|
-
|
|
|
- ko.components.register('hue-global-search', {
|
|
|
- viewModel: GlobalSearch,
|
|
|
- template: {element: 'hue-global-search-template'}
|
|
|
- });
|
|
|
- })();
|
|
|
- </script>
|
|
|
-
|
|
|
- <script type="text/html" id="top-search-autocomp-item">
|
|
|
- <a href="javascript:void(0);">
|
|
|
- <div>
|
|
|
- <div><i class="fa fa-fw" data-bind="css: icon"></i></div>
|
|
|
- <div>
|
|
|
- <div data-bind="html: label, style: { 'padding-top': description ? 0 : '9px' }"></div>
|
|
|
- <!-- ko if: description -->
|
|
|
- <div data-bind="html: description"></div>
|
|
|
- <!-- /ko -->
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </a>
|
|
|
- </script>
|
|
|
-
|
|
|
- <script type="text/html" id="top-search-autocomp-no-match">
|
|
|
- <div style="height: 30px;">
|
|
|
- <div>${ _('No match found') }</div>
|
|
|
- </div>
|
|
|
- </script>
|
|
|
-
|
|
|
- <script type="text/html" id="hue-simple-ace-editor-template">
|
|
|
- <!-- ko if singleLine -->
|
|
|
- <div class="simple-ace-single-line">
|
|
|
- <div class="ace-editor"></div>
|
|
|
- </div>
|
|
|
- <!-- /ko -->
|
|
|
- <!-- ko ifnot: singleLine -->
|
|
|
- <div class="ace-editor"></div>
|
|
|
- <!-- /ko -->
|
|
|
- <!-- ko if: autocompleter !== null -->
|
|
|
- <!-- ko component: { name: 'hueAceAutocompleter', params: { editor: ace, autocompleter: autocompleter } } --><!-- /ko -->
|
|
|
- <!-- /ko -->
|
|
|
- </script>
|
|
|
-
|
|
|
- <script type="text/javascript">
|
|
|
- (function () {
|
|
|
- var normalizedColors = HueColors.getNormalizedColors();
|
|
|
-
|
|
|
- var SolrFormulaAutocompleter = (function () {
|
|
|
- var SOLR_FUNCTIONS = {
|
|
|
- abs: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}]],
|
|
|
- signature: 'abs(x)',
|
|
|
- draggable: 'abs()',
|
|
|
- description: 'Returns the absolute value of the specified value or function.'
|
|
|
- },
|
|
|
- and: {
|
|
|
- returnTypes: ['boolean'],
|
|
|
- arguments: [[{type: 'boolean', multiple: true}]],
|
|
|
- signature: 'and(x, y, ...)',
|
|
|
- draggable: 'and()',
|
|
|
- description: 'Returns a value of true if and only if all of its operands evaluate to true.'
|
|
|
- },
|
|
|
- childfield: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}]],
|
|
|
- signature: 'childfield(x)',
|
|
|
- draggable: 'childfield()',
|
|
|
- description: 'Returns the value of the given field for one of the matched child docs when searching by {!parent}. It can be used only in sort parameter.'
|
|
|
- },
|
|
|
- def: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}, {type: 'T'}]],
|
|
|
- signature: 'def(field, default)',
|
|
|
- draggable: 'def()',
|
|
|
- description: 'def is short for default. Returns the value of field "field", or if the field does not exist, returns the default value specified. and yields the first value where exists()==true.)'
|
|
|
- },
|
|
|
- div: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}, {type: 'T'}]],
|
|
|
- signature: 'div(x, y)',
|
|
|
- draggable: 'div()',
|
|
|
- description: 'Divides one value or function by another. div(x,y) divides x by y.'
|
|
|
- },
|
|
|
- dist: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T', multiple: true}]],
|
|
|
- signature: 'dist(power, x, y, 0, 0)',
|
|
|
- draggable: 'dist()',
|
|
|
- description: 'Return the distance between two vectors (points) in an n-dimensional space. Takes in the power, plus two or more ValueSource instances and calculates the distances between the two vectors. Each ValueSource must be a number. There must be an even number of ValueSource instances passed in and the method assumes that the first half represent the first vector and the second half represent the second vector.'
|
|
|
- },
|
|
|
- docfreq: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}, {type: 'T'}]],
|
|
|
- signature: 'docfreq(field, value)',
|
|
|
- draggable: 'docfreq()',
|
|
|
- description: 'Returns the number of documents that contain the term in the field. This is a constant (the same value for all documents in the index).\n\n You can quote the term if it’s more complex, or do parameter substitution for the term value.'
|
|
|
- },
|
|
|
- exists: {
|
|
|
- returnTypes: ['boolean'],
|
|
|
- arguments: [[{type: 'T'}]],
|
|
|
- signature: 'exists(x)',
|
|
|
- draggable: 'exists()',
|
|
|
- description: 'Returns TRUE if any member of the field exists.'
|
|
|
- },
|
|
|
- eq: {
|
|
|
- returnTypes: ['boolean'],
|
|
|
- arguments: [[{type: 'T'}, {type: 'T'}]],
|
|
|
- signature: 'eq(x, y)',
|
|
|
- draggable: 'eq()',
|
|
|
- description: 'Equal, returns TRUE if x is equal to y'
|
|
|
- },
|
|
|
- field: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}, {type: 'T', optional: true}]],
|
|
|
- signature: 'field(field, minOrMax)',
|
|
|
- draggable: 'field()',
|
|
|
- description: 'Returns the numeric docValues or indexed value of the field with the specified name. In its simplest (single argument) form, this function can only be used on single valued fields, and can be called using the name of the field as a string, or for most conventional field names simply use the field name by itself with out using the field(...) syntax.\n\nWhen using docValues, an optional 2nd argument can be specified to select the "min" or "max" value of multivalued fields.\n\n0 is returned for documents without a value in the field.'
|
|
|
- },
|
|
|
- gt: {
|
|
|
- returnTypes: ['boolean'],
|
|
|
- arguments: [[{type: 'T'}, {type: 'T'}]],
|
|
|
- signature: 'gt(x, y)',
|
|
|
- draggable: 'gt()',
|
|
|
- description: 'Greater than, returns TRUE if x is greater than y'
|
|
|
- },
|
|
|
- gte: {
|
|
|
- returnTypes: ['boolean'],
|
|
|
- arguments: [[{type: 'T'}, {type: 'T'}]],
|
|
|
- signature: 'gte(x, y)',
|
|
|
- draggable: 'gte()',
|
|
|
- description: 'Greater than or equal, returns TRUE if x is greater than or equal to y'
|
|
|
- },
|
|
|
- hsin: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T', multiple: true}]],
|
|
|
- signature: 'hsin(2, true, x, y, 0, 0)',
|
|
|
- draggable: 'hsin()',
|
|
|
- description: 'The Haversine distance calculates the distance between two points on a sphere when traveling along the sphere. The values must be in radians. hsin also take a Boolean argument to specify whether the function should convert its output to radians.'
|
|
|
- },
|
|
|
- idf: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}, {type: 'T'}]],
|
|
|
- signature: 'idf(field, str)',
|
|
|
- draggable: 'idf()',
|
|
|
- description: 'Inverse document frequency; a measure of whether the term is common or rare across all documents. Obtained by dividing the total number of documents by the number of documents containing the term, and then taking the logarithm of that quotient. See also tf.'
|
|
|
- },
|
|
|
- 'if': {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}, {type: 'T'}, {type: 'T'}]],
|
|
|
- signature: 'if(test, val1, val2)',
|
|
|
- draggable: 'if()',
|
|
|
- description: 'Enables conditional function queries. In if(test,value1,value2): \n- test is or refers to a logical value or expression that returns a logical value (TRUE or FALSE).\n- value1 is the value that is returned by the function if test yields TRUE.\n -value2 is the value that is returned by the function if test yields FALSE.\n\nAn expression can be any function which outputs boolean values, or even functions returning numeric values, in which case value 0 will be interpreted as false, or strings, in which case empty string is interpreted as false.'
|
|
|
- },
|
|
|
- linear: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}, {type: 'T'}, {type: 'T'}]],
|
|
|
- signature: 'linear(x, m, c)',
|
|
|
- draggable: 'linear()',
|
|
|
- description: 'Implements m*x+c where m and c are constants and x is an arbitrary function. This is equivalent to sum(product(m,x),c), but slightly more efficient as it is implemented as a single function.'
|
|
|
- },
|
|
|
- log: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}]],
|
|
|
- signature: 'log(x)',
|
|
|
- draggable: 'log()',
|
|
|
- description: 'Returns the log base 10 of the specified function.'
|
|
|
- },
|
|
|
- lt: {
|
|
|
- returnTypes: ['boolean'],
|
|
|
- arguments: [[{type: 'T'}, {type: 'T'}]],
|
|
|
- signature: 'lt(x, y)',
|
|
|
- draggable: 'lt()',
|
|
|
- description: 'Less than, returns TRUE if x is less than y'
|
|
|
- },
|
|
|
- lte: {
|
|
|
- returnTypes: ['boolean'],
|
|
|
- arguments: [[{type: 'T'}, {type: 'T'}]],
|
|
|
- signature: 'lte(x, y)',
|
|
|
- draggable: 'lte()',
|
|
|
- description: 'Less than or equal, returns TRUE if x is less than or equal to y'
|
|
|
- },
|
|
|
- map: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T', multiple: true}]],
|
|
|
- signature: 'map(x, min, max, target)',
|
|
|
- draggable: 'map()',
|
|
|
- description: 'Maps any values of an input function x that fall within min and max inclusive to the specified target. The arguments min and max must be constants. The arguments target and default can be constants or functions. If the value of x does not fall between min and max, then either the value of x is returned, or a default value is returned if specified as a 5th argument.'
|
|
|
- },
|
|
|
- max: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T', multiple: true}]],
|
|
|
- signature: 'max(x, y, ...)',
|
|
|
- draggable: 'max()',
|
|
|
- description: 'Returns the maximum numeric value of multiple nested functions or constants, which are specified as arguments: max(x,y,...). The max function can also be useful for "bottoming out" another function or field at some specified constant.'
|
|
|
- },
|
|
|
- maxdoc: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [],
|
|
|
- signature: 'maxdoc()',
|
|
|
- draggable: 'maxdoc()',
|
|
|
- description: 'Returns the number of documents in the index, including those that are marked as deleted but have not yet been purged. This is a constant (the same value for all documents in the index).'
|
|
|
- },
|
|
|
- min: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T', multiple: true}]],
|
|
|
- signature: 'min(x, y, ...)',
|
|
|
- draggable: 'min()',
|
|
|
- description: 'Returns the minimum numeric value of multiple nested functions of constants, which are specified as arguments: min(x,y,…...). The min function can also be useful for providing an "upper bound" on a function using a constant.'
|
|
|
- },
|
|
|
- ms: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T', multiple: true}]],
|
|
|
- signature: 'ms(x, y, ...)',
|
|
|
- draggable: 'ms()',
|
|
|
- description: 'Returns milliseconds of difference between its arguments. Dates are relative to the Unix or POSIX time epoch, midnight, January 1, 1970 UTC. Arguments may be the name of an indexed TrieDateField, or date math based on a constant date or NOW.\n\n- ms(): Equivalent to ms(NOW), number of milliseconds since the epoch.\n- ms(a): Returns the number of milliseconds since the epoch that the argument represents.\n- ms(a,b) : Returns the number of milliseconds that b occurs before a (that is, a - b)'
|
|
|
- },
|
|
|
- norm: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}]],
|
|
|
- signature: 'norm(field)',
|
|
|
- draggable: 'norm()',
|
|
|
- description: 'Returns the "norm" stored in the index for the specified field. This is the product of the index time boost and the length normalization factor, according to the Similarity for the field.'
|
|
|
- },
|
|
|
- not: {
|
|
|
- returnTypes: ['boolean'],
|
|
|
- arguments: [[{type: 'boolean'}]],
|
|
|
- signature: 'not(x)',
|
|
|
- draggable: 'not()',
|
|
|
- description: 'The logically negated value of the wrapped function.'
|
|
|
- },
|
|
|
- numdocs: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [],
|
|
|
- signature: 'numdocs()',
|
|
|
- draggable: 'numdocs()',
|
|
|
- description: 'Returns the number of documents in the index, not including those that are marked as deleted but have not yet been purged. This is a constant (the same value for all documents in the index).'
|
|
|
- },
|
|
|
- or: {
|
|
|
- returnTypes: ['boolean'],
|
|
|
- arguments: [[{type: 'boolean', multiple: true}]],
|
|
|
- signature: 'or(x, y)',
|
|
|
- draggable: 'or()',
|
|
|
- description: 'A logical disjunction.'
|
|
|
- },
|
|
|
- ord: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}]],
|
|
|
- signature: 'ord(field)',
|
|
|
- draggable: 'ord()',
|
|
|
- description: 'Returns the ordinal of the indexed field value within the indexed list of terms for that field in Lucene index order (lexicographically ordered by unicode value), starting at 1. In other words, for a given field, all values are ordered lexicographically; this function then returns the offset of a particular value in that ordering. The field must have a maximum of one value per document (not multi-valued). 0 is returned for documents without a value in the field.'
|
|
|
- },
|
|
|
- payload: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T', multiple: true}]],
|
|
|
- signature: 'payload(payloaded_field_dpf, term, 0.0, first)',
|
|
|
- draggable: 'payload()',
|
|
|
- description: 'Returns the float value computed from the decoded payloads of the term specified. The return value is computed using the min, max, or average of the decoded payloads. A special first function can be used instead of the others, to short-circuit term enumeration and return only the decoded payload of the first term. The field specified must have float or integer payload encoding capability (via DelimitedPayloadTokenFilter or NumericPayloadTokenFilter). If no payload is found for the term, the default value is returned.'
|
|
|
- },
|
|
|
- pow: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}, {type: 'T'}]],
|
|
|
- signature: 'pow(x, y)',
|
|
|
- draggable: 'pow()',
|
|
|
- description: 'Raises the specified base to the specified power. pow(x,y) raises x to the power of y.'
|
|
|
- },
|
|
|
- product: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T', multiple: true}]],
|
|
|
- signature: 'product(x, y, ...)',
|
|
|
- draggable: 'product()',
|
|
|
- description: 'Returns the product of multiple values or functions, which are specified in a comma-separated list. mul(...) may also be used as an alias for this function.'
|
|
|
- },
|
|
|
- query: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}, {type: 'T', optional: true}]],
|
|
|
- signature: 'query(subquery, default)',
|
|
|
- draggable: 'query()',
|
|
|
- description: 'Returns the score for the given subquery, or the default value for documents not matching the query. Any type of subquery is supported through either parameter de-referencing $otherparam or direct specification of the query string in the Local Parameters through the v key.'
|
|
|
- },
|
|
|
- recip: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}, {type: 'T'}, {type: 'T'}, {type: 'T'}]],
|
|
|
- signature: 'recip(x, m, a, b)',
|
|
|
- draggable: 'recip()',
|
|
|
- description: 'Performs a reciprocal function with recip(x,m,a,b) implementing a/(m*x+b) where m,a,b are constants, and x is any arbitrarily complex function.\n\nWhen a and b are equal, and x>=0, this function has a maximum value of 1 that drops as x increases. Increasing the value of a and b together results in a movement of the entire function to a flatter part of the curve. These properties can make this an ideal function for boosting more recent documents when x is rord(datefield).'
|
|
|
- },
|
|
|
- rord: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}]],
|
|
|
- signature: 'rord(x)',
|
|
|
- draggable: 'rord()',
|
|
|
- description: 'Returns the reverse ordering of that returned by ord.'
|
|
|
- },
|
|
|
- scale: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}, {type: 'T'}, {type: 'T'}]],
|
|
|
- signature: 'scale(x, minTarget, maxTarget)',
|
|
|
- draggable: 'scale()',
|
|
|
- description: 'Scales values of the function x such that they fall between the specified minTarget and maxTarget inclusive. The current implementation traverses all of the function values to obtain the min and max, so it can pick the correct scale.\n\nThe current implementation cannot distinguish when documents have been deleted or documents that have no value. It uses 0.0 values for these cases. This means that if values are normally all greater than 0.0, one can still end up with 0.0 as the min value to map from. In these cases, an appropriate map() function could be used as a workaround to change 0.0 to a value in the real range, as shown here: scale(map(x,0,0,5),1,2).'
|
|
|
- },
|
|
|
- sqedist: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T', multiple: true}]],
|
|
|
- signature: 'sqedist(x_td, y_td, 0, 0)',
|
|
|
- draggable: 'sqedist()',
|
|
|
- description: 'The Square Euclidean distance calculates the 2-norm (Euclidean distance) but does not take the square root, thus saving a fairly expensive operation. It is often the case that applications that care about Euclidean distance do not need the actual distance, but instead can use the square of the distance. There must be an even number of ValueSource instances passed in and the method assumes that the first half represent the first vector and the second half represent the second vector.'
|
|
|
- },
|
|
|
- sqrt: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}]],
|
|
|
- signature: 'sqrt(x)',
|
|
|
- draggable: 'sqrt()',
|
|
|
- description: 'Returns the square root of the specified value or function.'
|
|
|
- },
|
|
|
- strdist: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T', multiple: true}]],
|
|
|
- signature: 'strdist(string1, string2, distance_measure)',
|
|
|
- draggable: 'strdist()',
|
|
|
- description: 'Calculate the distance between two strings. Uses the Lucene spell checker StringDistance interface and supports all of the implementations available in that package, plus allows applications to plug in their own via Solr’s resource loading capabilities. strdist takes (string1, string2, distance measure).\n\nPossible values for distance measure are:\n- jw: Jaro-Winkler\n- edit: Levenstein or Edit distance\n- ngram: The NGramDistance, if specified, can optionally pass in the ngram size too. Default is 2.\n- FQN: Fully Qualified class Name for an implementation of the StringDistance interface. Must have a no-arg constructor.'
|
|
|
- },
|
|
|
- sub: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}, {type: 'T'}]],
|
|
|
- signature: 'sub(x, y)',
|
|
|
- draggable: 'sub()',
|
|
|
- description: 'Returns x-y from sub(x,y).'
|
|
|
- },
|
|
|
- sum: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T', multiple: true}]],
|
|
|
- signature: 'sum(x, y, ...)',
|
|
|
- draggable: 'sum()',
|
|
|
- description: 'Returns the sum of multiple values or functions, which are specified in a comma-separated list. add(...) may be used as an alias for this function'
|
|
|
- },
|
|
|
- sumtotaltermfreq: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}]],
|
|
|
- signature: 'sumtotaltermfreq(field)',
|
|
|
- draggable: 'sumtotaltermfreq()',
|
|
|
- description: 'Returns the sum of totaltermfreq values for all terms in the field in the entire index (i.e., the number of indexed tokens for that field). (Aliases sumtotaltermfreq to sttf.)'
|
|
|
- },
|
|
|
- termfreq: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}, {type: 'T'}]],
|
|
|
- signature: 'termfreq(field, term)',
|
|
|
- draggable: 'termfreq()',
|
|
|
- description: 'Returns the number of times the term appears in the field for that document.'
|
|
|
- },
|
|
|
- tf: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}, {type: 'T'}]],
|
|
|
- signature: 'tf(field, term)',
|
|
|
- draggable: 'tf()',
|
|
|
- description: 'Term frequency; returns the term frequency factor for the given term, using the Similarity for the field. The tf-idf value increases proportionally to the number of times a word appears in the document, but is offset by the frequency of the word in the document, which helps to control for the fact that some words are generally more common than others. See also idf.'
|
|
|
- },
|
|
|
- top: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}]],
|
|
|
- signature: 'top(x)',
|
|
|
- draggable: 'top()',
|
|
|
- description: 'Causes the function query argument to derive its values from the top-level IndexReader containing all parts of an index. For example, the ordinal of a value in a single segment will be different from the ordinal of that same value in the complete index.'
|
|
|
- },
|
|
|
- totaltermfreq: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}, {type: 'T'}]],
|
|
|
- signature: 'totaltermfreq(field, term)',
|
|
|
- draggable: 'totaltermfreq()',
|
|
|
- description: 'Returns the number of times the term appears in the field in the entire index. (Aliases totaltermfreq to ttf.)'
|
|
|
- },
|
|
|
- xor: {
|
|
|
- returnTypes: ['boolean'],
|
|
|
- arguments: [[{type: 'boolean', multiple: true}]],
|
|
|
- signature: 'xor(x, y)',
|
|
|
- draggable: 'xor()',
|
|
|
- description: 'Logical exclusive disjunction, or one or the other but not both.'
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- var SOLR_AGGREGATE_FUNCTIONS = {
|
|
|
- avg: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}]],
|
|
|
- signature: 'avg(x)',
|
|
|
- draggable: 'avg()',
|
|
|
- description: 'Average of numeric values.'
|
|
|
- },
|
|
|
- hll: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}]],
|
|
|
- signature: 'hll(x)',
|
|
|
- draggable: 'hll()',
|
|
|
- description: 'The number of unique values using the HyperLogLog algorithm.'
|
|
|
- },
|
|
|
- max: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}]],
|
|
|
- signature: 'max(x)',
|
|
|
- draggable: 'max()',
|
|
|
- description: 'The maximum value.'
|
|
|
- },
|
|
|
- min: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}]],
|
|
|
- signature: 'min(x)',
|
|
|
- draggable: 'min()',
|
|
|
- description: 'The minimum value.'
|
|
|
- },
|
|
|
- percentile: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T', multiple: true }]],
|
|
|
- signature: 'percentile(x, y, z, ...)',
|
|
|
- draggable: 'percentile()',
|
|
|
- description: 'Calculates the percentiles.'
|
|
|
- },
|
|
|
- stddev: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}]],
|
|
|
- signature: 'stddev(x)',
|
|
|
- draggable: 'stddev()',
|
|
|
- description: 'Calculates standard deviation (Solr 6.6+).'
|
|
|
- },
|
|
|
- sum: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}]],
|
|
|
- signature: 'sum(x)',
|
|
|
- draggable: 'sum()',
|
|
|
- description: 'The sum of numeric values.'
|
|
|
- },
|
|
|
- sumsq: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}]],
|
|
|
- signature: 'sumsq(x)',
|
|
|
- draggable: 'sumsq()',
|
|
|
- description: 'The sum of squares.'
|
|
|
- },
|
|
|
- unique: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}]],
|
|
|
- signature: 'unique(x)',
|
|
|
- draggable: 'unique()',
|
|
|
- description: 'The number of unique values (count distinct).'
|
|
|
- },
|
|
|
- variance: {
|
|
|
- returnTypes: ['T'],
|
|
|
- arguments: [[{type: 'T'}]],
|
|
|
- signature: 'variance(x)',
|
|
|
- draggable: 'variance()',
|
|
|
- description: 'Calculates variance (Solr 6.6+).'
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- var COLORS = {
|
|
|
- ALL: HueColors.BLUE,
|
|
|
- FIELD: normalizedColors['green'][2],
|
|
|
- FUNCTION: normalizedColors['purple-gray'][3]
|
|
|
- };
|
|
|
-
|
|
|
- var CATEGORIES = {
|
|
|
- ALL: { id: 'all', color: COLORS.ALL, label: AutocompleterGlobals.i18n.category.all },
|
|
|
- FIELD: { id: 'field', weight: 1000, color: COLORS.FIELD, label: AutocompleterGlobals.i18n.category.field, detailsTemplate: 'solr-field' },
|
|
|
- FUNCTION: { id: 'function', weight: 900, color: COLORS.FUNCTION, label: AutocompleterGlobals.i18n.category.function, detailsTemplate: 'udf' }
|
|
|
- };
|
|
|
-
|
|
|
- var SolrFormulaSuggestions = function () {
|
|
|
- var self = this;
|
|
|
- self.entries = ko.observableArray();
|
|
|
-
|
|
|
- self.filtered = ko.pureComputed(function () {
|
|
|
- var result = self.entries();
|
|
|
-
|
|
|
- if (self.filter()) {
|
|
|
- result = SqlUtils.autocompleteFilter(self.filter(), result);
|
|
|
- huePubSub.publish('hue.ace.autocompleter.match.updated');
|
|
|
- }
|
|
|
-
|
|
|
- SqlUtils.sortSuggestions(result, self.filter(), self.sortOverride);
|
|
|
-
|
|
|
- return result;
|
|
|
- });
|
|
|
-
|
|
|
- self.availableCategories = ko.pureComputed(function () {
|
|
|
- // TODO: Implement autocomplete logic
|
|
|
- return [CATEGORIES.ALL];
|
|
|
- });
|
|
|
-
|
|
|
- self.loading = ko.observable(false);
|
|
|
- self.filter = ko.observable();
|
|
|
- self.cancelRequests = function () {};
|
|
|
- };
|
|
|
-
|
|
|
- SolrFormulaSuggestions.prototype.update = function (parseResult) {
|
|
|
- var self = this;
|
|
|
- var syncEntries = [];
|
|
|
- if (parseResult.suggestFunctions) {
|
|
|
-
|
|
|
- Object.keys(SOLR_FUNCTIONS).forEach(function (name) {
|
|
|
- syncEntries.push({
|
|
|
- category: CATEGORIES.FUNCTION,
|
|
|
- value: name + '()',
|
|
|
- meta: SOLR_FUNCTIONS[name].returnTypes.join('|'),
|
|
|
- weightAdjust: 0, // Add when we type aware
|
|
|
- popular: ko.observable(false),
|
|
|
- details: SOLR_FUNCTIONS[name]
|
|
|
- })
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- self.entries(syncEntries);
|
|
|
- };
|
|
|
-
|
|
|
- /**
|
|
|
- * @param {Object} options
|
|
|
- * @param {Ace} options.editor
|
|
|
- * @constructor
|
|
|
- */
|
|
|
- var SolrFormulaAutocompleter = function (options) {
|
|
|
- var self = this;
|
|
|
- self.editor = options.editor;
|
|
|
- self.suggestions = new SolrFormulaSuggestions();
|
|
|
- };
|
|
|
-
|
|
|
- SolrFormulaAutocompleter.prototype.autocomplete = function () {
|
|
|
- var self = this;
|
|
|
- var parseResult = solrExpressionParser.parseSolrExpression(self.editor.getTextBeforeCursor(), self.editor.getTextAfterCursor());
|
|
|
- self.suggestions.update(parseResult);
|
|
|
- };
|
|
|
-
|
|
|
- return SolrFormulaAutocompleter;
|
|
|
- })();
|
|
|
-
|
|
|
- var AVAILABLE_AUTOCOMPLETERS = {
|
|
|
- 'solrFormula': SolrFormulaAutocompleter
|
|
|
- };
|
|
|
-
|
|
|
- var SimpleAceEditor = function (params, element) {
|
|
|
- var $element = $(element);
|
|
|
- var self = this;
|
|
|
- self.value = params.value;
|
|
|
- self.ace = ko.observable();
|
|
|
- self.disposeFunctions = [];
|
|
|
-
|
|
|
- self.singleLine = !!params.singleLine;
|
|
|
-
|
|
|
- var aceOptions = params.aceOptions || {};
|
|
|
-
|
|
|
- if (!$element.attr('id')) {
|
|
|
- $element.attr('id', UUID());
|
|
|
- }
|
|
|
-
|
|
|
- var editor = ace.edit($element.find('.ace-editor')[0]);
|
|
|
- editor.$blockScrolling = Infinity;
|
|
|
- self.ace(editor);
|
|
|
-
|
|
|
- if (params.autocomplete) {
|
|
|
- if (!AVAILABLE_AUTOCOMPLETERS[params.autocomplete]) {
|
|
|
- throw new Error('Could not find autocompleter for "' + params.autocomplete + '"');
|
|
|
- }
|
|
|
-
|
|
|
- self.autocompleter = new AVAILABLE_AUTOCOMPLETERS[params.autocomplete]({ editor: editor });
|
|
|
- } else {
|
|
|
- self.autocompleter = null;
|
|
|
- }
|
|
|
-
|
|
|
- if (self.value()) {
|
|
|
- editor.setValue(self.value());
|
|
|
- editor.clearSelection();
|
|
|
- }
|
|
|
-
|
|
|
- if (self.singleLine) {
|
|
|
- aceOptions = $.extend(aceOptions, {
|
|
|
- fontSize: '13px',
|
|
|
- maxLines: 1, // make it 1 line
|
|
|
- autoScrollEditorIntoView: true,
|
|
|
- highlightActiveLine: false,
|
|
|
- printMargin: false,
|
|
|
- showGutter: false
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- if (params.autocomplete) {
|
|
|
- aceOptions = $.extend(aceOptions, {
|
|
|
- enableLiveAutocompletion: true,
|
|
|
- enableBasicAutocompletion: params.autocomplete
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- editor.setOptions(aceOptions);
|
|
|
-
|
|
|
- if (params.singleLine) {
|
|
|
- editor.renderer.screenToTextCoordinates = function(x, y) {
|
|
|
- var pos = this.pixelToScreenCoordinates(x, y);
|
|
|
- return this.session.screenToDocumentPosition(
|
|
|
- Math.min(this.session.getScreenLength() - 1, Math.max(pos.row, 0)),
|
|
|
- Math.max(pos.column, 0)
|
|
|
- );
|
|
|
- };
|
|
|
-
|
|
|
- editor.commands.bindKey("Enter|Shift-Enter", "null");
|
|
|
-
|
|
|
- var pasteListener = editor.on("paste", function(e) {
|
|
|
- e.text = e.text.replace(/[\r\n]+/g, " ");
|
|
|
- });
|
|
|
-
|
|
|
- self.disposeFunctions.push(function () {
|
|
|
- editor.off('paste', pasteListener);
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- if (params.autocomplete) {
|
|
|
- var AceAutocomplete = ace.require("ace/autocomplete").Autocomplete;
|
|
|
-
|
|
|
- if (!editor.completer) {
|
|
|
- editor.completer = new AceAutocomplete();
|
|
|
- }
|
|
|
- editor.completer.exactMatch = false;
|
|
|
- editor.useHueAutocompleter = true;
|
|
|
- }
|
|
|
-
|
|
|
- var inputListener = editor.on('input', function () {
|
|
|
- self.value(editor.getValue());
|
|
|
- });
|
|
|
-
|
|
|
- self.disposeFunctions.push(function () {
|
|
|
- editor.off('input', inputListener);
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- SimpleAceEditor.prototype.dispose = function () {
|
|
|
- var self = this;
|
|
|
- self.disposeFunctions.forEach(function (dispose) {
|
|
|
- dispose();
|
|
|
- })
|
|
|
- };
|
|
|
-
|
|
|
- ko.components.register('hue-simple-ace-editor', {
|
|
|
- viewModel: {
|
|
|
- createViewModel: function(params, componentInfo) {
|
|
|
- return new SimpleAceEditor(params, componentInfo.element);
|
|
|
- }
|
|
|
- },
|
|
|
- template: {element: 'hue-simple-ace-editor-template'}
|
|
|
- });
|
|
|
- })();
|
|
|
- </script>
|
|
|
+ ${ koBreadCrumbs.breadCrumbs() }
|
|
|
+ ${ koDropDown.dropDown() }
|
|
|
+ ${ koFavoriteApp.favoriteApp() }
|
|
|
+ ${ koGlobalSearch.globalSearch() }
|
|
|
+ ${ koHistoryPanel.historyPanel() }
|
|
|
+ ${ koInlineAutocomplete.inlineAutocomplete() }
|
|
|
+ ${ koJobBrowserLinks.jobBrowserLinks() }
|
|
|
+ ${ koSimpleAceEditor.simpleAceEditor() }
|
|
|
</%def>
|