Эх сурвалжийг харах

HUE-3281 [editor] Modified mobile skeleton

Enrico Berti 9 жил өмнө
parent
commit
d7d10386da

+ 50 - 0
desktop/core/src/desktop/static/desktop/css/hue-mobile.css

@@ -0,0 +1,50 @@
+/*
+ Licensed to Cloudera, Inc. under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  Cloudera, Inc. licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+.navbar-inner {
+  background-color: #338BB8;
+}
+
+.navbar .btn-navbar {
+  background-color: #FFF;
+  background-image: -moz-linear-gradient(top,#FFF,#FFF);
+  background-image: -webkit-gradient(linear,0 0,0 100%,from(#FFF),to(#FFF));
+  background-image: -webkit-linear-gradient(top,#FFF,#FFF);
+  background-image: -o-linear-gradient(top,#FFF,#FFF);
+  background-image: linear-gradient(to bottom,#FFF,#FFF);
+  background-repeat: repeat-x;
+}
+
+.navbar .btn-navbar:hover,.navbar .btn-navbar:focus,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled] {
+  color: #fff;
+  background-color: #FFF;
+}
+
+.navbar .brand {
+  float: none;
+  text-align: center;
+  text-shadow: none;
+  color: #FFF;
+}
+
+.navbar .brand img {
+  width: 60px;
+  height: 16px;
+  float: left;
+}
+

+ 89 - 0
desktop/core/src/desktop/templates/common_footer_m.mako

@@ -0,0 +1,89 @@
+## Licensed to Cloudera, Inc. under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  Cloudera, Inc. licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License.  You may obtain a copy of the License at
+##
+##     http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+<%!
+from django.http import HttpRequest
+from django.utils.translation import ugettext as _
+from django.template.defaultfilters import escape, escapejs
+from desktop.lib.i18n import smart_unicode
+%>
+
+
+
+    </div> <!-- /container -->
+
+<script type="text/javascript">
+  $(document).ready(function () {
+    $(document).on("info", function (e, msg) {
+      $.jHueNotify.info(msg);
+    });
+    $(document).on("warn", function (e, msg) {
+      $.jHueNotify.warn(msg);
+    });
+    $(document).on("error", function (e, msg) {
+      $.jHueNotify.error(msg);
+    });
+
+    %if messages:
+      %for message in messages:
+        %if message.tags == 'error':
+          $(document).trigger('error', '${ escapejs(escape(message)) }');
+        %elif message.tags == 'warning':
+          $(document).trigger('warn', '${ escapejs(escape(message)) }');
+        %else:
+          $(document).trigger('info', '${ escapejs(escape(message)) }');
+        %endif
+      %endfor
+    %endif
+  });
+
+  if (typeof nv != "undefined"){
+    // hides all the nvd3 logs
+    nv.log = function() {};
+  }
+
+  %if collect_usage:
+
+    (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+    (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+    m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+    })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
+
+    ga('create', 'UA-40351920-1', 'auto');
+    ga('set', 'referrer', 'http://gethue.com'); // we force the referrer to prevent leaking sensitive information
+
+    // We collect only 2 path levels: not hostname, no IDs, no anchors...
+    var _pathName = location.pathname;
+    var _splits = _pathName.substr(1).split("/");
+    _pathName = _splits[0] + (_splits.length > 1 && $.trim(_splits[1]) != "" ? "/" + _splits[1] : "");
+
+    ga('send', 'pageview', {
+      'page': '/remote/${ version }/' + _pathName
+    });
+
+    function trackOnGA(path) {
+      if (typeof ga != "undefined" && ga != null) {
+        ga('set', 'referrer', 'http://gethue.com'); // we force the referrer to prevent leaking sensitive information
+        ga('send', 'pageview', {
+          'page': '/remote/${ version }/' + path
+        });
+      }
+    }
+
+  %endif
+
+</script>
+  </body>
+</html>

+ 1 - 6
desktop/core/src/desktop/templates/common_header.mako

@@ -13,7 +13,6 @@
 ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 ## 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
@@ -24,26 +23,22 @@ from desktop.conf import USE_NEW_EDITOR
 if USE_NEW_EDITOR.get():
   home_url = url('desktop.views.home2')
 %>
-
+<!DOCTYPE html>
 <%def name="is_selected(selected)">
   %if selected:
     class="active"
   %endif
 </%def>
-
 <%def name="get_nice_name(app, section)">
   % if app and section == app.display_name:
     - ${app.nice_name}
   % endif
 </%def>
-
 <%def name="get_title(title)">
   % if title:
     - ${smart_unicode(title)}
   % endif
 </%def>
-
-<!DOCTYPE html>
 <html lang="en">
 <head>
   <meta http-equiv="X-UA-Compatible" content="IE=edge">

+ 290 - 0
desktop/core/src/desktop/templates/common_header_m.mako

@@ -0,0 +1,290 @@
+## Licensed to Cloudera, Inc. under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  Cloudera, Inc. licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License.  You may obtain a copy of the License at
+##
+##     http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## 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 _
+
+home_url = url('desktop.views.home')
+from desktop.conf import USE_NEW_EDITOR
+if USE_NEW_EDITOR.get():
+  home_url = url('desktop.views.home2')
+%>
+<!DOCTYPE html>
+<%def name="is_selected(selected)">
+  %if selected:
+    class="active"
+  %endif
+</%def>
+<%def name="get_nice_name(app, section)">
+  % if app and section == app.display_name:
+    - ${app.nice_name}
+  % endif
+</%def>
+<%def name="get_title(title)">
+  % if title:
+    ${smart_unicode(title)}
+  % endif
+</%def>
+<html lang="en">
+<head>
+  <meta http-equiv="X-UA-Compatible" content="IE=edge">
+  <meta charset="utf-8">
+  <title>Hue ${get_nice_name(current_app, section)} - ${get_title(title)}</title>
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <link rel="icon" type="image/x-icon" href="${ static('desktop/art/favicon.ico') }" />
+  <meta name="description" content="">
+  <meta name="author" content="">
+
+  <link href="${ static('desktop/css/roboto.css') }" rel="stylesheet">
+  <link href="${ static('desktop/ext/css/bootplus.css') }" rel="stylesheet">
+  <link href="${ static('desktop/ext/css/bootplus-responsive.css') }" rel="stylesheet">
+  <link href="${ static('desktop/ext/css/font-awesome.min.css') }" rel="stylesheet">
+  <link href="${ static('desktop/css/hue-mobile.css') }" rel="stylesheet">
+
+  <style type="text/css">
+    body {
+      display: none;
+      visibility: hidden;
+    }
+  </style>
+
+  <script type="text/javascript" charset="utf-8">
+
+    var LOGGED_USERNAME = '${ user.username }';
+    var IS_S3_ENABLED = '${ is_s3_enabled }' === 'True';
+
+    // jHue plugins global configuration
+    jHueFileChooserGlobals = {
+      labels: {
+        BACK: "${_('Back')}",
+        SELECT_FOLDER: "${_('Select this folder')}",
+        CREATE_FOLDER: "${_('Create folder')}",
+        FOLDER_NAME: "${_('Folder name')}",
+        CANCEL: "${_('Cancel')}",
+        FILE_NOT_FOUND: "${_('The file has not been found')}",
+        UPLOAD_FILE: "${_('Upload a file')}",
+        FAILED: "${_('Failed')}"
+      },
+      user: "${ user.username }"
+    };
+
+    jHueHdfsTreeGlobals = {
+      labels: {
+        CREATE_FOLDER: "${_('Create folder')}",
+        FOLDER_NAME: "${_('Folder name')}",
+        CANCEL: "${_('Cancel')}"
+      }
+    };
+
+    jHueTableExtenderGlobals = {
+      labels: {
+        GO_TO_COLUMN: "${_('Go to column:')}",
+        PLACEHOLDER: "${_('column name...')}",
+        LOCK: "${_('Click to lock this row')}",
+        UNLOCK: "${_('Click to unlock this row')}"
+      }
+    };
+
+    jHueTourGlobals = {
+      labels: {
+        AVAILABLE_TOURS: "${_('Available tours')}",
+        NO_AVAILABLE_TOURS: "${_('None for this page.')}",
+        MORE_INFO: "${_('Read more about it...')}",
+        TOOLTIP_TITLE: "${_('Demo tutorials')}"
+      }
+    };
+
+    LeafletGlobals = {
+      layer: '${ leaflet['layer'] |n,unicode }',
+      attribution: '${ leaflet['attribution'] |n,unicode }'
+    };
+
+    ApiHelperGlobals = {
+      i18n: {
+        errorLoadingDatabases: '${ _('There was a problem loading the databases') }',
+        errorLoadingTablePreview: '${ _('There was a problem loading the preview') }'
+      },
+      user: '${ user.username }'
+    }
+  </script>
+
+  <!--[if lt IE 9]>
+  <script type="text/javascript">
+    if (document.documentMode && document.documentMode < 9){
+      location.href = "${ url('desktop.views.unsupported') }";
+    }
+  </script>
+  <![endif]-->
+
+  <script type="text/javascript">
+    // check if it's a Firefox < 7
+    var _UA = navigator.userAgent.toLowerCase();
+    for (var i = 1; i < 7; i++) {
+      if (_UA.indexOf("firefox/" + i + ".") > -1) {
+        location.href = "${ url('desktop.views.unsupported') }";
+      }
+    }
+
+    // check for IE document modes
+    if (document.documentMode && document.documentMode < 9){
+      location.href = "${ url('desktop.views.unsupported') }";
+    }
+  </script>
+
+  <script src="${ static('desktop/js/hue.utils.js') }"></script>
+  <script src="${ static('desktop/ext/js/jquery/jquery-2.1.1.min.js') }"></script>
+  <script src="${ static('desktop/js/jquery.migration.js') }"></script>
+  <script src="${ static('desktop/ext/js/jquery/plugins/jquery.cookie.js') }"></script>
+  <script src="${ static('desktop/ext/js/jquery/plugins/jquery.total-storage.min.js') }"></script>
+  <script src="${ static('desktop/ext/js/bootstrap.min.js') }"></script>
+  <script src="${ static('desktop/ext/js/bootstrap-better-typeahead.min.js') }"></script>
+  <script src="${ static('desktop/ext/js/moment-with-locales.min.js') }" type="text/javascript" charset="utf-8"></script>
+
+  <script type="text/javascript" charset="utf-8">
+
+    moment.locale(window.navigator.userLanguage || window.navigator.language);
+    localeFormat = function (time) {
+      var mTime = time;
+      if (typeof ko !== 'undefined' && ko.isObservable(time)) {
+        mTime = time();
+      }
+      if (moment(mTime).isValid()) {
+        return moment.utc(mTime).format("L LT");
+      }
+      return mTime;
+    }
+
+    //Add CSRF Token to all XHR Requests
+    var xrhsend = XMLHttpRequest.prototype.send;
+    XMLHttpRequest.prototype.send = function (data) {
+      this.setRequestHeader('X-CSRFToken', $.cookie('csrftoken'));
+      return xrhsend.apply(this, arguments);
+    }
+
+    // sets global apiHelper TTL
+    $.totalStorage('hue.cacheable.ttl', ${conf.CUSTOM.CACHEABLE_TTL.get()});
+
+    var IDLE_SESSION_TIMEOUT = -1;
+
+    $(document).ready(function () {
+      // forces IE's ajax calls not to cache
+      if ($.browser.msie) {
+        $.ajaxSetup({ cache: false });
+      }
+
+      // prevents framebusting and clickjacking
+      if (self == top){
+        $("body").css({
+          'display': 'block',
+          'visibility': 'visible'
+        });
+      }
+      else {
+        top.location = self.location;
+      }
+
+      %if conf.AUTH.IDLE_SESSION_TIMEOUT.get() > -1 and not skip_idle_timeout:
+      IDLE_SESSION_TIMEOUT = ${conf.AUTH.IDLE_SESSION_TIMEOUT.get()};
+      var idleTimer;
+      function resetIdleTimer() {
+        clearTimeout(idleTimer);
+        idleTimer = setTimeout(function () {
+          // Check if logged out
+          $.get('/desktop/debug/is_idle');
+        }, (IDLE_SESSION_TIMEOUT * 1000) + 1000);
+      }
+
+      $(document).on('mousemove', resetIdleTimer);
+      $(document).on('keydown', resetIdleTimer);
+      $(document).on('click', resetIdleTimer);
+      resetIdleTimer();
+      %endif
+
+      % if 'jobbrowser' in apps:
+      var JB_CHECK_INTERVAL_IN_MILLIS = 30000;
+      var checkJobBrowserStatusIdx = window.setTimeout(checkJobBrowserStatus, 10);
+
+      function checkJobBrowserStatus(){
+        $.post("/jobbrowser/jobs/", {
+            "format": "json",
+            "state": "running",
+            "user": "${user.username}"
+          },
+          function(data) {
+            if (data != null && data.jobs != null) {
+              huePubSub.publish('jobbrowser.data', data.jobs);
+              if (data.jobs.length > 0){
+                $("#jobBrowserCount").removeClass("hide").text(data.jobs.length);
+              }
+              else {
+                $("#jobBrowserCount").addClass("hide");
+              }
+            }
+          checkJobBrowserStatusIdx = window.setTimeout(checkJobBrowserStatus, JB_CHECK_INTERVAL_IN_MILLIS);
+        }).fail(function () {
+          window.clearTimeout(checkJobBrowserStatusIdx);
+        });
+      }
+      huePubSub.subscribe('check.job.browser', checkJobBrowserStatus);
+      % endif
+
+      window.hueDebug = {
+        viewModel: function () {
+          return ko.dataFor(document.body);
+        }
+      }
+    });
+  </script>
+</head>
+<body>
+
+<%
+  def count_apps(apps, app_list):
+    count = 0
+    found_app = ""
+    for app in app_list:
+      if app in apps:
+       found_app = app
+       count += 1
+    return found_app, count
+%>
+<div class="navbar navbar-fixed-top">
+      <div class="navbar-inner">
+        <div class="container">
+          <button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
+            <span class="icon-bar"></span>
+            <span class="icon-bar"></span>
+            <span class="icon-bar"></span>
+          </button>
+          <a class="brand" href="#">
+            <img src="${ static('desktop/art/hue-logo-mini-white.png') }" />
+            ${get_title(title)}
+          </a>
+          <div class="nav-collapse collapse">
+            <ul class="nav">
+              <li class="active"><a href="#">Home</a></li>
+              <li><a href="#about">About</a></li>
+              <li><a href="#contact">Contact</a></li>
+              <li><a title="${_('Sign out')}" href="/accounts/logout/">${ _("Sign out") }</li>
+            </ul>
+          </div><!--/.nav-collapse -->
+        </div>
+      </div>
+    </div>
+
+    <div class="container">
+

+ 18 - 4
desktop/core/src/desktop/views.py

@@ -377,7 +377,10 @@ def log_frontend_event(request):
   _LOG_FRONTEND_LOGGER.log(level, msg)
   return HttpResponse("")
 
-def commonheader(title, section, user, request=None, padding="90px", skip_topbar=False, skip_idle_timeout=False):
+def commonheader_m(title, section, user, request=None, padding="90px", skip_topbar=False, skip_idle_timeout=False):
+  return commonheader(title, section, user, request, padding, skip_topbar, skip_idle_timeout, True)
+
+def commonheader(title, section, user, request=None, padding="90px", skip_topbar=False, skip_idle_timeout=False, is_mobile=False):
   """
   Returns the rendered common header
   """
@@ -396,7 +399,11 @@ def commonheader(title, section, user, request=None, padding="90px", skip_topbar
   else:
     apps_list = []
 
-  return django_mako.render_to_string("common_header.mako", {
+  template = "common_header.mako"
+  if is_mobile:
+    template = "common_header_m.mako"
+
+  return django_mako.render_to_string(template, {
     'current_app': current_app,
     'apps': apps_list,
     'other_apps': other_apps,
@@ -431,7 +438,10 @@ def login_modal(request):
 def is_idle(request):
   return HttpResponse("no!")
 
-def commonfooter(request, messages=None):
+def commonfooter_m(request, messages=None):
+  return commonfooter(request, messages, True)
+
+def commonfooter(request, messages=None, is_mobile=False):
   """
   Returns the rendered common footer
   """
@@ -440,7 +450,11 @@ def commonfooter(request, messages=None):
 
   hue_settings = Settings.get_settings()
 
-  return django_mako.render_to_string("common_footer.mako", {
+  template = "common_footer.mako"
+  if is_mobile:
+    template = "common_footer_m.mako"
+
+  return django_mako.render_to_string(template, {
     'request': request,
     'messages': messages,
     'version': hue_version(),

+ 7 - 6
desktop/libs/notebook/src/notebook/static/notebook/js/notebook.ko.js

@@ -860,7 +860,7 @@ var EditorViewModel = (function() {
         stopLongOperationTimeout();
 
         if (vm.editorMode() && data.history_id) {
-          var url = '/notebook/editor?editor=' + data.history_id;
+          var url = '/notebook/editor' + (vm.isMobile() ? '_m' : '') + '?editor=' + data.history_id;
           hueUtils.changeURL(url);
           notebook.id(data.history_id);
           notebook.uuid(data.history_uuid);
@@ -1653,7 +1653,7 @@ var EditorViewModel = (function() {
               self.schedulerViewModel.coordinator.refreshParameters();
             }
 
-            hueUtils.changeURL('/notebook/editor?editor=' + data.id);
+            hueUtils.changeURL('/notebook/editor' + (self.isMobile() ? '_m' : '') + '?editor=' + data.id);
           }
           else {
             hueUtils.changeURL('/notebook/notebook?notebook=' + data.id);
@@ -1819,7 +1819,7 @@ var EditorViewModel = (function() {
         if (self.isHistory()) {
           self.id(null);
           self.uuid(UUID());
-          hueUtils.changeURL('/notebook/editor?type=' + vm.editorType());
+          hueUtils.changeURL('/notebook/editor' + (self.isMobile() ? '_m' : '') + '?type=' + vm.editorType());
         }
       }).fail(function (xhr) {
         $(document).trigger("error", xhr.responseText);
@@ -1978,6 +1978,7 @@ var EditorViewModel = (function() {
     var self = this;
     self.user = options.user;
     self.userId = options.userId;
+    self.isMobile = ko.observable(options.mobile);
     self.editorType = ko.observable(options.editor_type);
     self.editorType.subscribe(function(newVal) {
       console.log('Should happen on new query, or Hive --> Impala only: reload session, saved queries, history if in editor mode');
@@ -2212,7 +2213,7 @@ var EditorViewModel = (function() {
           self.loadNotebook(notebook, queryTab);
           if (typeof skipUrlChange === 'undefined'){
             if (self.editorMode()) {
-              hueUtils.changeURL('/notebook/editor?editor=' + data.document.id);
+              hueUtils.changeURL('/notebook/editor' + (self.isMobile() ? '_m' : '') + '?editor=' + data.document.id);
             }
             else {
               hueUtils.changeURL('/notebook/notebook?notebook=' + data.document.id);
@@ -2243,9 +2244,9 @@ var EditorViewModel = (function() {
             $.totalStorage('hue.notebook.lastWrittenSnippet.' + self.user +  '.' + window.location.getParameter('type'), '');
           }
           if (window.location.getParameter('type')) {
-            hueUtils.changeURL('/notebook/editor?type=' + window.location.getParameter('type'));
+            hueUtils.changeURL('/notebook/editor' + (self.isMobile() ? '_m' : '') + '?type=' + window.location.getParameter('type'));
           } else {
-            hueUtils.changeURL('/notebook/editor?type=' + self.editorType());
+            hueUtils.changeURL('/notebook/editor' + (self.isMobile() ? '_m' : '') + '?type=' + self.editorType());
           }
         } else {
           hueUtils.changeURL('/notebook/notebook');

+ 211 - 0
desktop/libs/notebook/src/notebook/templates/editor_m.mako

@@ -0,0 +1,211 @@
+## Licensed to Cloudera, Inc. under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  Cloudera, Inc. licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License.  You may obtain a copy of the License at
+##
+##     http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+<%!
+  from desktop.views import commonheader_m, commonfooter_m
+  from desktop import conf
+  from django.utils.translation import ugettext as _
+  from desktop.views import _ko
+%>
+
+${ commonheader_m(_('Editor'), editor_type, user, request, "68px") | n,unicode }
+
+
+
+<!-- ko with: selectedNotebook -->
+  <!-- ko if: history().length > 0 -->
+  <table class="table table-condensed margin-top-10 history-table">
+    <tbody data-bind="foreach: history">
+      <tr data-bind="click: function() { if (uuid() != $root.selectedNotebook().uuid()) { $root.openNotebook(uuid()); } }, css: { 'highlight': uuid() == $root.selectedNotebook().uuid(), 'pointer': uuid() != $root.selectedNotebook().uuid() }">
+        <td style="width: 100px" class="muted" data-bind="style: {'border-top-width': $index() == 0 ? '0' : ''}">
+          <span data-bind="momentFromNow: {data: lastExecuted, interval: 10000, titleFormat: 'LLL'}"></span>
+        </td>
+        <td style="width: 25px" class="muted" data-bind="style: {'border-top-width': $index() == 0 ? '0' : ''}">
+          <!-- 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 -->
+        </td>
+        <td style="width: 25px" class="muted" data-bind="ellipsis: {data: name(), length: 30}, style: {'border-top-width': $index() == 0 ? '0' : ''}"></td>
+        <td data-bind="style: {'border-top-width': $index() == 0 ? '0' : ''}"><div data-bind="highlight: query(), flavor: $parent.type" class="history-item"></div></td>
+      </tr>
+    </tbody>
+  </table>
+  <!-- /ko -->
+<!-- /ko -->
+
+
+
+<script src="${ static('desktop/ext/js/knockout.min.js') }"></script>
+<script src="${ static('desktop/ext/js/knockout-mapping.min.js') }"></script>
+<script src="${ static('desktop/js/apiHelper.js') }"></script>
+<script src="${ static('desktop/js/ko.charts.js') }"></script>
+<script src="${ static('desktop/js/ace/ace.js') }"></script>
+<script src="${ static('desktop/js/ace/mode-impala.js') }"></script>
+<script src="${ static('desktop/js/ace/mode-hive.js') }"></script>
+<script src="${ static('desktop/js/ace/ext-language_tools.js') }"></script>
+<script src="${ static('desktop/js/ace.extended.js') }"></script>
+<script src="${ static('desktop/js/ko.hue-bindings.js') }"></script>
+<script src="${ static('desktop/js/ko.switch-case.js') }"></script>
+<script src="${ static('desktop/js/sqlFunctions.js') }"></script>
+<script src="${ static('desktop/js/autocomplete/sql.js') }"></script>
+<script src="${ static('desktop/js/sqlAutocompleter.js') }"></script>
+<script src="${ static('desktop/js/sqlAutocompleter2.js') }"></script>
+<script src="${ static('desktop/js/hdfsAutocompleter.js') }"></script>
+<script src="${ static('desktop/js/autocompleter.js') }"></script>
+<script src="${ static('desktop/js/hue.json.js') }"></script>
+<script src="${ static('notebook/js/notebook.ko.js') }"></script>
+
+<script type="text/javascript">
+  ko.options.deferUpdates = true;
+
+  ace.config.set("basePath", "/static/desktop/js/ace");
+
+    var VIEW_MODEL_OPTIONS = $.extend(${ options_json | n,unicode }, {
+      user: '${ user.username }',
+      userId: ${ user.id },
+      assistAvailable: true,
+      % if conf.USE_NEW_AUTOCOMPLETER.get():
+      useNewAutocompleter: true,
+      % endif
+      autocompleteTimeout: ${ conf.EDITOR_AUTOCOMPLETE_TIMEOUT.get() },
+      snippetViewSettings: {
+        default: {
+          placeHolder: '${ _("Example: SELECT * FROM tablename, or press CTRL + space") }',
+          aceMode: 'ace/mode/sql',
+          snippetIcon: 'fa-database',
+          sqlDialect: true
+        },
+        code: {
+          placeHolder: '${ _("Example: 1 + 1, or press CTRL + space") }',
+          snippetIcon: 'fa-code'
+        },
+        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
+        },
+        jar : {
+          snippetIcon: 'fa-file-archive-o '
+        },
+        mysql: {
+          placeHolder: '${ _("Example: SELECT * FROM tablename, or press CTRL + space") }',
+          aceMode: 'ace/mode/mysql',
+          snippetIcon: 'fa-database',
+          sqlDialect: true
+        },
+        mysqljdbc: {
+          placeHolder: '${ _("Example: SELECT * FROM tablename, or press CTRL + space") }',
+          aceMode: 'ace/mode/mysql',
+          snippetIcon: 'fa-database',
+          sqlDialect: true
+        },
+        oracle: {
+          placeHolder: '${ _("Example: SELECT * FROM tablename, or press CTRL + space") }',
+          aceMode: 'ace/mode/oracle',
+          snippetIcon: 'fa-database',
+          sqlDialect: true
+        },
+        pig: {
+          placeHolder: '${ _("Example: 1 + 1, or press CTRL + space") }',
+          aceMode: 'ace/mode/pig',
+          snippetImage: '${ static("pig/art/icon_pig_48.png") }'
+        },
+        postgresql: {
+          placeHolder: '${ _("Example: SELECT * FROM tablename, or press CTRL + space") }',
+          aceMode: 'ace/mode/pgsql',
+          snippetIcon: 'fa-database',
+          sqlDialect: true
+        },
+        solr: {
+          placeHolder: '${ _("Example: SELECT fieldA, FieldB FROM collectionname, or press CTRL + space") }',
+          aceMode: 'ace/mode/mysql',
+          snippetIcon: 'fa-database',
+          sqlDialect: true
+        },
+        java : {
+          snippetIcon: 'fa-file-archive-o '
+        },
+        py : {
+          snippetIcon: 'fa-file-code-o'
+        },
+        pyspark: {
+          placeHolder: '${ _("Example: 1 + 1, or press CTRL + space") }',
+          aceMode: 'ace/mode/python',
+          snippetImage: '${ static("spark/art/icon_spark_48.png") }'
+        },
+        r: {
+          placeHolder: '${ _("Example: 1 + 1, or press CTRL + space") }',
+          aceMode: 'ace/mode/r',
+          snippetImage: '${ static("spark/art/icon_spark_48.png") }'
+        },
+        scala: {
+          placeHolder: '${ _("Example: 1 + 1, or press CTRL + space") }',
+          aceMode: 'ace/mode/scala',
+          snippetImage: '${ static("spark/art/icon_spark_48.png") }'
+        },
+        spark: {
+          placeHolder: '${ _("Example: 1 + 1, or press CTRL + space") }',
+          aceMode: 'ace/mode/scala',
+          snippetImage: '${ static("spark/art/icon_spark_48.png") }'
+        },
+        sqlite: {
+          placeHolder: '${ _("Example: SELECT * FROM tablename, or press CTRL + space") }',
+          aceMode: 'ace/mode/sqlite',
+          snippetIcon: 'fa-database',
+          sqlDialect: true
+        },
+        text: {
+          placeHolder: '${ _('Type your text here') }',
+          aceMode: 'ace/mode/text',
+          snippetIcon: 'fa-header'
+        },
+        markdown: {
+          placeHolder: '${ _('Type your markdown here') }',
+          aceMode: 'ace/mode/markdown',
+          snippetIcon: 'fa-header'
+        }
+      }
+    });
+
+    var viewModel;
+
+    $(document).ready(function () {
+      viewModel = new EditorViewModel(${ editor_id or 'null' }, ${ notebooks_json | n,unicode }, VIEW_MODEL_OPTIONS);
+      ko.applyBindings(viewModel);
+      viewModel.init();
+    });
+
+</script>
+
+
+${ commonfooter_m(request, messages) | n,unicode }

+ 0 - 47
desktop/libs/notebook/src/notebook/templates/editorm.mako

@@ -1,47 +0,0 @@
-## Licensed to Cloudera, Inc. under one
-## or more contributor license agreements.  See the NOTICE file
-## distributed with this work for additional information
-## regarding copyright ownership.  Cloudera, Inc. licenses this file
-## to you under the Apache License, Version 2.0 (the
-## "License"); you may not use this file except in compliance
-## with the License.  You may obtain a copy of the License at
-##
-##     http://www.apache.org/licenses/LICENSE-2.0
-##
-## Unless required by applicable law or agreed to in writing, software
-## distributed under the License is distributed on an "AS IS" BASIS,
-## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-## See the License for the specific language governing permissions and
-## limitations under the License.
-<%!
-  from desktop.views import commonheader, commonfooter
-  from desktop import conf
-  from django.utils.translation import ugettext as _
-%>
-
-<%namespace name="assist" file="/assist.mako" />
-<%namespace name="configKoComponents" file="/config_ko_components.mako" />
-<%namespace name="editorComponents" file="editor_components.mako" />
-<%namespace name="notebookKoComponents" file="notebook_ko_components.mako" />
-
-${ commonheader(_('Editor'), editor_type, user, "68px") | n,unicode }
-
-${ editorComponents.includes() }
-
-<style type="text/css">
-  .snippet {
-    margin-right: 10px;
-  }
-</style>
-
-${ editorComponents.topBar() }
-${ editorComponents.commonHTML() }
-
-${ assist.assistPanel() }
-${ configKoComponents.config() }
-${ notebookKoComponents.downloadSnippetResults() }
-${ notebookKoComponents.snippetDbSelection() }
-
-${ editorComponents.commonJS() }
-
-${ commonfooter(request, messages) | n,unicode }

+ 1 - 1
desktop/libs/notebook/src/notebook/urls.py

@@ -41,7 +41,7 @@ urlpatterns = patterns('notebook.views',
   url(r'^copy/?$', 'copy', name='copy'),
 
   url(r'^editor/?$', 'editor', name='editor'),
-  url(r'^editorm/?$', 'editor', name='editorm'),
+  url(r'^editor_m/?$', 'editor_m', name='editor_m'),
   url(r'^browse/(?P<database>\w+)/(?P<table>\w+)/?$', 'browse', name='browse'),
   url(r'^execute_and_watch/?$', 'execute_and_watch', name='execute_and_watch'),
 )

+ 14 - 12
desktop/libs/notebook/src/notebook/views.py

@@ -100,17 +100,18 @@ def editor(request):
       'editor_id': editor_id or None,
       'notebooks_json': '{}',
       'options_json': json.dumps({
-          'languages': get_interpreters(request.user),
-          'mode': 'editor',
-          'is_optimizer_enabled': has_optimizer(),
-          'is_navigator_enabled': has_navigator(request.user),
-          'editor_type': editor_type
+        'languages': get_interpreters(request.user),
+        'mode': 'editor',
+        'is_optimizer_enabled': has_optimizer(),
+        'is_navigator_enabled': has_navigator(request.user),
+        'editor_type': editor_type,
+        'mobile': False
       })
   })
 
 
 @check_document_access_permission()
-def editorm(request):
+def editor_m(request):
   editor_id = request.GET.get('editor')
   editor_type = request.GET.get('type', 'hive')
 
@@ -118,15 +119,16 @@ def editorm(request):
     document = Document2.objects.get(id=editor_id)
     editor_type = document.type.rsplit('-', 1)[-1]
 
-  return render('editorm.mako', request, {
+  return render('editor_m.mako', request, {
       'editor_id': editor_id or None,
       'notebooks_json': '{}',
       'options_json': json.dumps({
-          'languages': get_interpreters(request.user),
-          'mode': 'editor',
-          'is_optimizer_enabled': has_optimizer(),
-          'is_navigator_enabled': has_navigator(request.user),
-          'editor_type': editor_type
+        'languages': get_interpreters(request.user),
+        'mode': 'editor',
+        'is_optimizer_enabled': has_optimizer(),
+        'is_navigator_enabled': has_navigator(request.user),
+        'editor_type': editor_type,
+        'mobile': True
       })
   })