浏览代码

[core] Moved JS files out of Source/jhue folder
(cherry picked from commit 64a24ab03b04c2f2acac01f92f3350ee3e49b67d)

Enrico Berti 12 年之前
父节点
当前提交
aed634d

+ 4 - 4
apps/beeswax/src/beeswax/templates/execute.mako

@@ -378,13 +378,13 @@ ${layout.menubar(section='query')}
 
 <script src="/static/ext/js/codemirror-3.11.js"></script>
 <link rel="stylesheet" href="/static/ext/css/codemirror.css">
-<script src="/static/js/Source/jHue/codemirror-hql.js"></script>
+<script src="/static/js/codemirror-hql.js"></script>
 % if app_name == 'impala':
-  <script src="/static/js/Source/jHue/codemirror-isql-hint.js"></script>
+  <script src="/static/js/codemirror-isql-hint.js"></script>
 % else:
-  <script src="/static/js/Source/jHue/codemirror-hql-hint.js"></script>
+  <script src="/static/js/codemirror-hql-hint.js"></script>
 % endif
-<script src="/static/js/Source/jHue/codemirror-show-hint.js"></script>
+<script src="/static/js/codemirror-show-hint.js"></script>
 
 <link rel="stylesheet" href="/static/ext/css/codemirror-show-hint.css">
 

+ 3 - 3
apps/pig/src/pig/templates/app.mako

@@ -577,9 +577,9 @@ ${ commonheader(None, "pig", user, "100px") | n,unicode }
 <script src="/pig/static/js/pig.ko.js" type="text/javascript" charset="utf-8"></script>
 <script src="/static/ext/js/routie-0.3.0.min.js" type="text/javascript" charset="utf-8"></script>
 <script src="/static/ext/js/codemirror-3.11.js"></script>
-<script src="/static/js/Source/jHue/codemirror-pig.js"></script>
-<script src="/static/js/Source/jHue/codemirror-show-hint.js"></script>
-<script src="/static/js/Source/jHue/codemirror-pig-hint.js"></script>
+<script src="/static/js/codemirror-pig.js"></script>
+<script src="/static/js/codemirror-show-hint.js"></script>
+<script src="/static/js/codemirror-pig-hint.js"></script>
 <script src="/beeswax/static/js/autocomplete.utils.js" type="text/javascript" charset="utf-8"></script>
 
 <link rel="stylesheet" href="/pig/static/css/pig.css">

+ 1 - 1
desktop/core/src/desktop/templates/common_footer.mako

@@ -170,7 +170,7 @@ from django.template.defaultfilters import escape, escapejs
 
 </script>
 %if tours_and_tutorials:
-  <script src="/static/js/Source/jHue/available.tours.js"></script>
+  <script src="/static/js/available.tours.js"></script>
   <div id="jHueTourModal" class="modal hide fade">
     <div class="modal-header">
       <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>

+ 11 - 11
desktop/core/src/desktop/templates/common_header.mako

@@ -107,21 +107,21 @@ from django.utils.translation import ugettext as _
   </script>
 
   <script src="/static/ext/js/jquery/jquery-2.0.2.min.js"></script>
-  <script src="/static/js/Source/jHue/jquery.migration.js"></script>
-  <script src="/static/js/Source/jHue/jquery.filechooser.js"></script>
-  <script src="/static/js/Source/jHue/jquery.selector.js"></script>
-  <script src="/static/js/Source/jHue/jquery.alert.js"></script>
-  <script src="/static/js/Source/jHue/jquery.rowselector.js"></script>
-  <script src="/static/js/Source/jHue/jquery.notify.js"></script>
-  <script src="/static/js/Source/jHue/jquery.tablescroller.js"></script>
-  <script src="/static/js/Source/jHue/jquery.tableextender.js"></script>
-  <script src="/static/js/Source/jHue/jquery.scrollup.js"></script>
-  <script src="/static/js/Source/jHue/jquery.tour.js"></script>
+  <script src="/static/js/jquery.migration.js"></script>
+  <script src="/static/js/jquery.filechooser.js"></script>
+  <script src="/static/js/jquery.selector.js"></script>
+  <script src="/static/js/jquery.alert.js"></script>
+  <script src="/static/js/jquery.rowselector.js"></script>
+  <script src="/static/js/jquery.notify.js"></script>
+  <script src="/static/js/jquery.tablescroller.js"></script>
+  <script src="/static/js/jquery.tableextender.js"></script>
+  <script src="/static/js/jquery.scrollup.js"></script>
+  <script src="/static/js/jquery.tour.js"></script>
   <script src="/static/ext/js/jquery/plugins/jquery.cookie.js"></script>
   <script src="/static/ext/js/jquery/plugins/jquery.total-storage.min.js"></script>
   <script src="/static/ext/js/jquery/plugins/jquery.placeholder.min.js"></script>
   <script src="/static/ext/js/jquery/plugins/jquery.dataTables.1.8.2.min.js"></script>
-  <script src="/static/js/Source/jHue/jquery.datatables.sorting.js"></script>
+  <script src="/static/js/jquery.datatables.sorting.js"></script>
   <script src="/static/ext/js/bootstrap.min.js"></script>
   <script src="/static/ext/js/fileuploader.js"></script>
 

+ 1 - 1
desktop/core/src/desktop/templates/common_jasmine.mako

@@ -24,7 +24,7 @@
     <script type="text/javascript" src="/static/ext/js/jasmine/jasmine-1.2.0.js"></script>
     <script type="text/javascript" src="/static/ext/js/jasmine/jasmine-html-1.2.0.js"></script>
     <script type="text/javascript" src="/static/ext/js/jquery/jquery-2.0.2.min.js"></script>
-    <script type="text/javascript" src="/static/js/Source/jHue/jquery.migration.js"></script>
+    <script type="text/javascript" src="/static/js/jquery.migration.js"></script>
     <script type="text/javascript" src="/static/ext/js/jasmine/jasmine-jquery-1.3.1.js"></script>
     <script type="text/javascript" src="/static/ext/js/knockout-min.js"></script>
 

+ 3 - 3
desktop/core/src/desktop/templates/jasmine.mako

@@ -26,13 +26,13 @@
   <script src="/static/ext/js/jquery/plugins/jquery.dataTables.1.8.2.min.js"></script>
   <script src="/static/ext/js/bootstrap.min.js"></script>
 
-  <script src="/static/js/Source/jHue/jquery.selector.js"></script>
+  <script src="/static/js/jquery.selector.js"></script>
   <script src="static/jasmine/jHueSelectorSpec.js"></script>
 
-  <script src="/static/js/Source/jHue/jquery.tableextender.js"></script>
+  <script src="/static/js/jquery.tableextender.js"></script>
   <script src="static/jasmine/jHueTableExtenderSpec.js"></script>
 
-  <script src="/static/js/Source/jHue/jquery.tablescroller.js"></script>
+  <script src="/static/js/jquery.tablescroller.js"></script>
   <script src="static/jasmine/jHueTableScrollerSpec.js"></script>
 </%block>
 

+ 18 - 0
desktop/core/static/js/available.tours.js

@@ -0,0 +1,18 @@
+// 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.
+
+
+// put the tours here

+ 145 - 0
desktop/core/static/js/codemirror-hql-hint.js

@@ -0,0 +1,145 @@
+// 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.
+
+(function () {
+  function forEach(arr, f) {
+    for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
+  }
+
+  function arrayContains(arr, item) {
+    if (!Array.prototype.indexOf) {
+      var i = arr.length;
+      while (i--) {
+        if (arr[i] === item) {
+          return true;
+        }
+      }
+      return false;
+    }
+    return arr.indexOf(item) != -1;
+  }
+
+  function scriptHint(editor, _keywords, getToken) {
+    // Find the token at the cursor
+    var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
+
+    // If it's not a 'word-style' or dot token, ignore the token.
+    if (!/^[\.\w$_]*$/.test(token.string)) {
+      token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
+        className: token.string == "." ? "hiveql-type" : null};
+    }
+
+    if (!context) var context = [];
+    context.push(tprop);
+
+    var completionList = getCompletions(token, context);
+    //prevent autocomplete for last word, instead show dropdown with one word
+    if (completionList.length == 1) {
+      completionList.push(" ");
+    }
+
+    return {list: completionList,
+      from: CodeMirror.Pos(cur.line, token.start),
+      to: CodeMirror.Pos(cur.line, token.end)};
+  }
+
+  CodeMirror.catalogTables = "";
+  CodeMirror.catalogFields = "";
+  CodeMirror.possibleTable = false;
+  CodeMirror.possibleSoloField = false;
+  CodeMirror.tableFieldMagic = false;
+
+  CodeMirror.hiveQLHint = function (editor) {
+    return scriptHint(editor, hiveQLKeywordsU, function (e, cur) {
+      return e.getTokenAt(cur);
+    });
+  };
+
+  var hiveQLKeywords = "ADD AFTER ALL ALTER AND ARCHIVE AS ASC BETWEEN BUCKET BUCKETS BY CASCADE CHANGE CLI CLUSTER CLUSTERED COALESCE COLLECTION COLUMN COLUMNS COMMENT CREATE CROSS DATA DATABASE DATABASES DBPROPERTIES DEFERRED DELIMITED DEPENDENCY DESC DESCRIBE DIRECTORY DISABLE DISTINCT DISTRIBUTE DOT DROP ENABLE ESCAPED EXISTS EXPLAIN EXPORT EXTENDED EXTERNAL FIELDS FILEFORMAT FIRST FORMAT FORMATTED FROM FULL FUNCTION FUNCTIONS GRANT GROUP HAVING IDXPROPERTIES IF IGNORE IMPORT IN INDEX INDEXES INPATH INSERT INTO IS ITEMS JOIN KEYS LATERAL LEFT LIKE LIMIT LINES LOAD LOCAL LOCATION LOCKS MAP MAPJOIN MSCK NOT OF OFFLINE ON OPTION ORDER OUT OUTER OVERWRITE PARTITION PARTITIONED PARTITIONS PERCENT PRIVILEGES PROTECTION REBUILD RECORDREADER RECOVER REDUCE REGEXP RENAME REPAIR REPLACE RESTRICT REVOKE RIGHT RLIKE ROLE ROW SCHEMA SCHEMAS SELECT SEMI SEPARATED SERDE SERDEPROPERTIES SET SHOW SKEWED SORT SORTED STORED SUM TABLE TABLES TABLESAMPLE TBLPROPERTIES TEMPORARY TERMINATED TO TOUCH TRANSFORM TRUNCATE UNARCHIVE UNION US USER USING VIEW WHERE WITH";
+  var hiveQLKeywordsU = hiveQLKeywords.split(" ");
+  var hiveQLKeywordsL = hiveQLKeywords.toLowerCase().split(" ");
+
+  var hiveQLKeywordsAfterTables = "JOIN ON WHERE";
+  var hiveQLKeywordsAfterTablesU = hiveQLKeywordsAfterTables.split(" ");
+  var hiveQLKeywordsAfterTablesL = hiveQLKeywordsAfterTables.toLowerCase().split(" ");
+
+  var hiveQLTypes = "TINYINT SMALLINT INT BIGINT BOOLEAN FLOAT DOUBLE STRING BINARY TIMESTAMP DECIMAL ARRAY MAP STRUCT UNIONTYPE DELIMITED SERDE SEQUENCEFILE TEXTFILE RCFILE INPUTFORMAT OUTPUTFORMAT";
+  var hiveQLTypesU = hiveQLTypes.split(" ");
+  var hiveQLTypesL = hiveQLTypes.toLowerCase().split(" ");
+
+  var hiveQLBuiltins = "ROUND FLOOR CEIL CEILING RAND EXP LN LOG10 LOG2 LOG POW POWER SQRT BIN HEX UNHEX CONV ABS PMOD SIN ASIN COS ACOS TAN ATAN DEGREES RADIANS POSITIVE NEGATIVE SIGN E PI SIZE MAP_KEYS MAP_VALUES ARRAY_CONTAINS SORT_ARRAY BINARY CAST FROM_UNIXTIME UNIX_TIMESTAMP TO_DATE YEAR MONTH DAY HOUR MINUTE SECOND WEEKOFYEAR DATEDIFF DATE_ADD DATE_SUB FROM_UTC_TIMESTAMP TO_UTC_TIMESTAMP ASCII CONCAT CONTEXT_NGRAMS CONCAT_WS FIND_IN_SET FORMAT_NUMBER GET_JSON_OBJECT IN_FILE INSTR LENGTH LOCATE LOWER LCASE LPAD LTRIM NGRAMS PARSE_URL PRINTF REGEXP_EXTRACT REGEXP_REPLACE REPEAT REVERSE RPAD RTRIM SENTENCES SPACE SPLIT STR_TO_MAP SUBSTR SUBSTRING TRANSLATE TRIM UPPER UCASE JAVA_METHOD REFLECT XPATH XPATH_SHORT XPATH_INT XPATH_LONG XPATH_FLOAT XPATH_DOUBLE XPATH_NUMBER XPATH_STRING COUNT SUM AVG MIN MAX VARIANCE VAR_SAMP STDEV_POP STDEV_SAMP COVAR_POP COVAR_SAMP CORR PERCENTILE PERCENTILE_APPROX HISTOGRAM_NUMERIC COLLECT_SET INLINE EXPLODE JSON_TUPLE PARSE_URL_TUPLE GET_JSON_OBJECT";
+  var hiveQLBuiltinsU = hiveQLBuiltins.split(" ").join("() ").split(" ");
+  var hiveQLBuiltinsL = hiveQLBuiltins.toLowerCase().split(" ").join("() ").split(" ");
+
+  function getCompletions(token, context) {
+    var catalogTablesL = CodeMirror.catalogTables.toLowerCase().split(" ");
+    var catalogFieldsL = CodeMirror.catalogFields.toLowerCase().split(" ");
+
+    var found = [], start = token.string, extraFound = [];
+
+    function maybeAdd(str) {
+      if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str);
+    }
+
+    function maybeAddToExtra(str) {
+      if (str.indexOf(start) == 0 && !arrayContains(found, str)) extraFound.push(str);
+    }
+
+    function gatherCompletions(obj) {
+      if (obj.indexOf(".") == 0) {
+        forEach(catalogFieldsL, maybeAdd);
+      }
+      else {
+        if (!CodeMirror.possibleTable) {
+          if (CodeMirror.tableFieldMagic) {
+            var _specialCatalogTablesL = CodeMirror.catalogTables.toLowerCase().split(" ");
+            for (var i = 0; i < _specialCatalogTablesL.length; i++) {
+              _specialCatalogTablesL[i] = "<i class='icon-magic'></i> FROM " + _specialCatalogTablesL[i];
+            }
+            forEach(_specialCatalogTablesL, maybeAddToExtra);
+          }
+          if (CodeMirror.possibleSoloField) {
+            forEach(catalogFieldsL, maybeAddToExtra);
+          }
+          forEach(hiveQLBuiltinsU, maybeAdd);
+          forEach(hiveQLBuiltinsL, maybeAdd);
+          forEach(hiveQLTypesU, maybeAdd);
+          forEach(hiveQLTypesL, maybeAdd);
+          forEach(hiveQLKeywordsU, maybeAdd);
+          forEach(hiveQLKeywordsL, maybeAdd);
+
+        }
+        else {
+          forEach(catalogTablesL, maybeAddToExtra);
+          forEach(hiveQLKeywordsAfterTablesU, maybeAdd);
+          forEach(hiveQLKeywordsAfterTablesL, maybeAdd);
+        }
+      }
+    }
+
+    if (context) {
+      // If this is a property, see if it belongs to some object we can
+      // find in the current environment.
+      var obj = context.pop(), base;
+      base = obj.string;
+
+      while (base != null && context.length)
+        base = base[context.pop().string];
+      if (base != null) gatherCompletions(base);
+    }
+    return extraFound.sort().concat(found.sort());
+  }
+})();

文件差异内容过多而无法显示
+ 245 - 0
desktop/core/static/js/codemirror-hql.js


+ 144 - 0
desktop/core/static/js/codemirror-isql-hint.js

@@ -0,0 +1,144 @@
+// 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.
+
+(function () {
+  function forEach(arr, f) {
+    for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
+  }
+
+  function arrayContains(arr, item) {
+    if (!Array.prototype.indexOf) {
+      var i = arr.length;
+      while (i--) {
+        if (arr[i] === item) {
+          return true;
+        }
+      }
+      return false;
+    }
+    return arr.indexOf(item) != -1;
+  }
+
+  function scriptHint(editor, _keywords, getToken) {
+    // Find the token at the cursor
+    var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
+
+    // If it's not a 'word-style' or dot token, ignore the token.
+    if (!/^[\.\w$_]*$/.test(token.string)) {
+      token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
+        className: token.string == "." ? "impalasql-type" : null};
+    }
+
+    if (!context) var context = [];
+    context.push(tprop);
+
+    var completionList = getCompletions(token, context);
+    //prevent autocomplete for last word, instead show dropdown with one word
+    if (completionList.length == 1) {
+      completionList.push(" ");
+    }
+
+    return {list: completionList,
+      from: CodeMirror.Pos(cur.line, token.start),
+      to: CodeMirror.Pos(cur.line, token.end)};
+  }
+
+  CodeMirror.catalogTables = "";
+  CodeMirror.catalogFields = "";
+  CodeMirror.possibleTable = false;
+  CodeMirror.possibleSoloField = false;
+  CodeMirror.tableFieldMagic = false;
+
+  CodeMirror.impalaSQLHint = function (editor) {
+    return scriptHint(editor, impalaSQLKeywordsU, function (e, cur) {
+      return e.getTokenAt(cur);
+    });
+  };
+
+  var impalaSQLKeywords = "ALL AND AS BY COMMENT CREATE DATABASE DATABASES DELIMITED DESCRIBE DISTINCT DROP EXISTS EXPLAIN EXTERNAL FIELDS FORMAT FROM GROUP HAVING IF INSERT INTO JOIN LIKE LIMIT LINES LOCATION NOT OR ORDER OVERWRITE PARTITIONED REFRESH ROW SCHEMA SCHEMAS SELECT SHOW STORED TABLE TABLES TERMINATED UNION USE WHERE INVALIDATE METADATA";
+  var impalaSQLKeywordsU = impalaSQLKeywords.split(" ");
+  var impalaSQLKeywordsL = impalaSQLKeywords.toLowerCase().split(" ");
+
+  var impalaSQLKeywordsAfterTables = "JOIN ON WHERE";
+  var impalaSQLKeywordsAfterTablesU = impalaSQLKeywordsAfterTables.split(" ");
+  var impalaSQLKeywordsAfterTablesL = impalaSQLKeywordsAfterTables.toLowerCase().split(" ");
+
+  var impalaSQLTypes = "TINYINT SMALLINT INT BIGINT BOOLEAN FLOAT DOUBLE STRING TIMESTAMP PARQUETFILE SEQUENCEFILE TEXTFILE RCFILE";
+  var impalaSQLTypesU = impalaSQLTypes.split(" ");
+  var impalaSQLTypesL = impalaSQLTypes.toLowerCase().split(" ");
+
+  var impalaSQLBuiltins = "COUNT SUM CAST LIKE IN BETWEEN COALESCE";
+  var impalaSQLBuiltinsU = impalaSQLBuiltins.split(" ").join("() ").split(" ");
+  var impalaSQLBuiltinsL = impalaSQLBuiltins.toLowerCase().split(" ").join("() ").split(" ");
+
+  function getCompletions(token, context) {
+    var catalogTablesL = CodeMirror.catalogTables.toLowerCase().split(" ");
+    var catalogFieldsL = CodeMirror.catalogFields.toLowerCase().split(" ");
+
+    var found = [], start = token.string, extraFound = [];
+
+    function maybeAdd(str) {
+      if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str);
+    }
+
+    function maybeAddToExtra(str) {
+      if (str.indexOf(start) == 0 && !arrayContains(found, str)) extraFound.push(str);
+    }
+
+    function gatherCompletions(obj) {
+      if (obj.indexOf(".") == 0) {
+        forEach(catalogFieldsL, maybeAdd);
+      }
+      else {
+        if (!CodeMirror.possibleTable) {
+          if (CodeMirror.tableFieldMagic) {
+            var _specialCatalogTablesL = CodeMirror.catalogTables.toLowerCase().split(" ");
+            for (var i = 0; i < _specialCatalogTablesL.length; i++) {
+              _specialCatalogTablesL[i] = "<i class='icon-magic'></i> FROM " + _specialCatalogTablesL[i];
+            }
+            forEach(_specialCatalogTablesL, maybeAddToExtra);
+          }
+          if (CodeMirror.possibleSoloField) {
+            forEach(catalogFieldsL, maybeAddToExtra);
+          }
+          forEach(impalaSQLBuiltinsU, maybeAdd);
+          forEach(impalaSQLBuiltinsL, maybeAdd);
+          forEach(impalaSQLTypesU, maybeAdd);
+          forEach(impalaSQLTypesL, maybeAdd);
+          forEach(impalaSQLKeywordsU, maybeAdd);
+          forEach(impalaSQLKeywordsL, maybeAdd);
+        }
+        else {
+          forEach(catalogTablesL, maybeAddToExtra);
+          forEach(impalaSQLKeywordsAfterTablesU, maybeAdd);
+          forEach(impalaSQLKeywordsAfterTablesL, maybeAdd);
+        }
+      }
+    }
+
+    if (context) {
+      // If this is a property, see if it belongs to some object we can
+      // find in the current environment.
+      var obj = context.pop(), base;
+      base = obj.string;
+
+      while (base != null && context.length)
+        base = base[context.pop().string];
+      if (base != null) gatherCompletions(base);
+    }
+    return extraFound.sort().concat(found.sort());
+  }
+})();

+ 145 - 0
desktop/core/static/js/codemirror-pig-hint.js

@@ -0,0 +1,145 @@
+// 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.
+
+(function () {
+  function forEach(arr, f) {
+    for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
+  }
+
+  function arrayContains(arr, item) {
+    if (!Array.prototype.indexOf) {
+      var i = arr.length;
+      while (i--) {
+        if (arr[i] === item) {
+          return true;
+        }
+      }
+      return false;
+    }
+    return arr.indexOf(item) != -1;
+  }
+
+  function scriptHint(editor, _keywords, getToken) {
+    // Find the token at the cursor
+    var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
+    // If it's not a 'word-style' token, ignore the token.
+
+    if (token.string.indexOf("'") == 0){
+      CodeMirror.isPath = !CodeMirror.isTable;
+      token.string = token.string.substring(1, token.string.length);
+    }
+    if (token.string.indexOf("/") > -1){
+      token.string = token.string.substring(token.string.lastIndexOf("/") + 1);
+    }
+
+    if (!/^[\w$_]*$/.test(token.string)) {
+      token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
+        className: token.string == ":" ? "pig-type" : null};
+    }
+
+    if (!context) var context = [];
+    context.push(tprop);
+
+    var completionList = getCompletions(token, context);
+    //prevent autocomplete for last word, instead show dropdown with one word
+    if (completionList.length == 1) {
+      completionList.push(" ");
+    }
+
+    return {list: completionList,
+      from: CodeMirror.Pos(cur.line, token.start),
+      to: CodeMirror.Pos(cur.line, token.end)};
+  }
+
+  CodeMirror.isPath = false;
+  CodeMirror.isTable = false;
+  CodeMirror.isHCatHint = false;
+  CodeMirror.currentFiles = [];
+  CodeMirror.catalogTables = "";
+
+  CodeMirror.pigHint = function (editor) {
+    return scriptHint(editor, pigCaseInsensitive, function (e, cur) {
+      return e.getTokenAt(cur);
+    });
+  };
+
+  var pigCaseInsensitive = "and any all arrange as asc bag by bytearray boolean cache cat cd chararray cogroup cp cross " +
+      "datetime %declare %default define desc describe distinct double du dump e eval exec explain f filter " +
+      "flatten float foreach full generate group help if illustrate import inner input int into is join kill" +
+      "l left limit load long ls map matches mkdir mv not null onschema or order outer output parallel pig pwd" +
+      "quit register right rm rmf run sample set ship split stderr stdin stdout store stream through tuple union using";
+
+  var pigCaseInsensitiveU = pigCaseInsensitive.toUpperCase().split(" ");
+  var pigCaseInsensitiveL = pigCaseInsensitive.toLowerCase().split(" ");
+
+  var pigCaseSensitive = "AVG BinStorage CONCAT copyFromLocal copyToLocal COUNT DIFF MAX MIN  PigDump PigStorage SIZE SUM TextLoader TOKENIZE".split(" ");
+
+  function getCompletions(token, context) {
+    var catalogTablesL = CodeMirror.catalogTables.toLowerCase().split(" ");
+
+    var found = [], start = token.string, extraFound = [];
+
+    function maybeAdd(str) {
+      var stripped = strip(str).replace(/(?:(?:^|\n)\s+|\s+(?:$|\n))/g,'').replace(/\s+/g,' ');
+      if (stripped.toLowerCase().indexOf(start.toLowerCase()) == 0 && !arrayContains(found, str)) found.push(str);
+    }
+
+    function maybeAddToExtra(str) {
+      if (str.toLowerCase().indexOf(start.toLowerCase()) == 0 && !arrayContains(found, str)) extraFound.push(str);
+    }
+
+    function strip(html){
+      if (jQuery) {
+        return $("<div>").html(html).text();
+      }
+      else {
+        var tmp = document.createElement("DIV");
+        tmp.innerHTML = html;
+        return tmp.textContent || tmp.innerText;
+      }
+    }
+
+    function gatherCompletions(obj) {
+      if (CodeMirror.isPath || (obj.indexOf("'") == 0 && !CodeMirror.isTable)){
+        forEach(CodeMirror.currentFiles, maybeAdd);
+      }
+      else if (CodeMirror.isTable) {
+        forEach(catalogTablesL, maybeAddToExtra);
+      }
+      else {
+        forEach(pigCaseInsensitiveU, maybeAdd);
+        forEach(pigCaseInsensitiveL, maybeAdd);
+        forEach(pigCaseSensitive, maybeAdd);
+        forEach(CodeMirror.availableVariables, maybeAddToExtra);
+      }
+      if (CodeMirror.isHCatHint){
+        maybeAdd("<i class='icon-magic'></i> USING org.apache.hcatalog.pig.HCatLoader();");
+      }
+    }
+
+    if (context) {
+      // If this is a property, see if it belongs to some object we can
+      // find in the current environment.
+      var obj = context.pop(), base;
+      base = obj.string;
+
+      while (base != null && context.length)
+        base = base[context.pop().string];
+      if (base != null) gatherCompletions(base);
+    }
+    return extraFound.sort().concat(found.sort());
+  }
+})();

+ 172 - 0
desktop/core/static/js/codemirror-pig.js

@@ -0,0 +1,172 @@
+/*
+ *	Pig Latin Mode for CodeMirror 2
+ *	@author Prasanth Jayachandran
+ *	@link 	https://github.com/prasanthj/pig-codemirror-2
+ *  This implementation is adapted from PL/SQL mode in CodeMirror 2.
+*/
+CodeMirror.defineMode("pig", function(_config, parserConfig) {
+	var keywords = parserConfig.keywords,
+		builtins = parserConfig.builtins,
+		types = parserConfig.types,
+		multiLineStrings = parserConfig.multiLineStrings;
+
+	var isOperatorChar = /[*+\-%<>=&?:\/!|]/;
+
+	function chain(stream, state, f) {
+		state.tokenize = f;
+		return f(stream, state);
+	}
+
+	var type;
+	function ret(tp, style) {
+		type = tp;
+		return style;
+	}
+
+	function tokenComment(stream, state) {
+		var isEnd = false;
+		var ch;
+		while(ch = stream.next()) {
+			if(ch == "/" && isEnd) {
+				state.tokenize = tokenBase;
+				break;
+			}
+			isEnd = (ch == "*");
+		}
+		return ret("comment", "comment");
+	}
+
+	function tokenString(quote) {
+		return function(stream, state) {
+			var escaped = false, next, end = false;
+			while((next = stream.next()) != null) {
+				if (next == quote && !escaped) {
+					end = true; break;
+				}
+				escaped = !escaped && next == "\\";
+			}
+			if (end || !(escaped || multiLineStrings))
+				state.tokenize = tokenBase;
+			return ret("string", "error");
+		};
+	}
+
+	function tokenBase(stream, state) {
+		var ch = stream.next();
+
+		// is a start of string?
+		if (ch == '"' || ch == "'")
+			return chain(stream, state, tokenString(ch));
+		// is it one of the special chars
+		else if(/[\[\]{}\(\),;\.]/.test(ch))
+			return ret(ch);
+		// is it a number?
+		else if(/\d/.test(ch)) {
+			stream.eatWhile(/[\w\.]/);
+			return ret("number", "number");
+		}
+		// multi line comment or operator
+		else if (ch == "/") {
+			if (stream.eat("*")) {
+				return chain(stream, state, tokenComment);
+			}
+			else {
+				stream.eatWhile(isOperatorChar);
+				return ret("operator", "operator");
+			}
+		}
+		// single line comment or operator
+		else if (ch=="-") {
+			if(stream.eat("-")){
+				stream.skipToEnd();
+				return ret("comment", "comment");
+			}
+			else {
+				stream.eatWhile(isOperatorChar);
+				return ret("operator", "operator");
+			}
+		}
+		// is it an operator
+		else if (isOperatorChar.test(ch)) {
+			stream.eatWhile(isOperatorChar);
+			return ret("operator", "operator");
+		}
+		else {
+			// get the while word
+			stream.eatWhile(/[\w\$_]/);
+			// is it one of the listed keywords?
+			if (keywords && keywords.propertyIsEnumerable(stream.current().toUpperCase())) {
+				if (stream.eat(")") || stream.eat(".")) {
+					//keywords can be used as variables like flatten(group), group.$0 etc..
+				}
+				else {
+					return ("keyword", "keyword");
+				}
+			}
+			// is it one of the builtin functions?
+			if (builtins && builtins.propertyIsEnumerable(stream.current().toUpperCase()))
+			{
+				return ("keyword", "variable-2");
+			}
+			// is it one of the listed types?
+			if (types && types.propertyIsEnumerable(stream.current().toUpperCase()))
+				return ("keyword", "variable-3");
+			// default is a 'variable'
+			return ret("variable", "pig-word");
+		}
+	}
+
+	// Interface
+	return {
+		startState: function() {
+			return {
+				tokenize: tokenBase,
+				startOfLine: true
+			};
+		},
+
+		token: function(stream, state) {
+			if(stream.eatSpace()) return null;
+			var style = state.tokenize(stream, state);
+			return style;
+		}
+	};
+});
+
+(function() {
+	function keywords(str) {
+		var obj = {}, words = str.split(" ");
+		for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
+ 		return obj;
+ 	}
+
+
+	// builtin funcs taken from trunk revision 1303237
+	var pBuiltins = "ABS ACOS ARITY ASIN ATAN AVG BAGSIZE BINSTORAGE BLOOM BUILDBLOOM CBRT CEIL "
+	+ "CONCAT COPYFROMLOCAL COPYTOLOCAL CAT COR COS COSH COUNT COUNT_STAR COV CONSTANTSIZE CUBEDIMENSIONS DIFF DISTINCT DOUBLEABS "
+	+ "DOUBLEAVG DOUBLEBASE DOUBLEMAX DOUBLEMIN DOUBLEROUND DOUBLESUM EXP FLOOR FLOATABS FLOATAVG "
+	+ "FLOATMAX FLOATMIN FLOATROUND FLOATSUM GENERICINVOKER INDEXOF INTABS INTAVG INTMAX INTMIN "
+	+ "INTSUM INVOKEFORDOUBLE INVOKEFORFLOAT INVOKEFORINT INVOKEFORLONG INVOKEFORSTRING INVOKER "
+	+ "ISEMPTY JSONLOADER JSONMETADATA JSONSTORAGE LAST_INDEX_OF LCFIRST LOG LOG10 LOWER LONGABS "
+	+ "LONGAVG LONGMAX LONGMIN LONGSUM MAX MIN MAPSIZE MONITOREDUDF NONDETERMINISTIC OUTPUTSCHEMA  "
+	+ "PIGSTORAGE PIGSTREAMING RANDOM REGEX_EXTRACT REGEX_EXTRACT_ALL REPLACE ROUND SIN SINH SIZE "
+	+ "SQRT STRSPLIT SUBSTRING SUM STRINGCONCAT STRINGMAX STRINGMIN STRINGSIZE TAN TANH TOBAG "
+	+ "TOKENIZE TOMAP TOP TOTUPLE TRIM TEXTLOADER TUPLESIZE UCFIRST UPPER UTF8STORAGECONVERTER EVAL PIGDUMP PIGSTORAGE";
+
+	// taken from QueryLexer.g
+	var pKeywords = "%DECLARE %DEFAULT VOID IMPORT RETURNS ARRANGE DEFINE LOAD FILTER FOREACH ORDER CUBE DISTINCT COGROUP CP CD DU "
+	+ "JOIN CROSS UNION SPLIT INTO IF OTHERWISE ALL AS BY USING INNER OUTER ONSCHEMA PARALLEL "
+	+ "PARTITION GROUP AND ANY OR NOT GENERATE FLATTEN ASC DESC DESCRIBE EXPLAIN IS STREAM THROUGH STORE MAPREDUCE "
+	+ "SHIP CACHE INPUT OUTPUT STDERROR STDIN STDOUT LIMIT SAMPLE LEFT RIGHT FULL EQ GT LT GTE LTE "
+	+ "NEQ MATCHES TRUE FALSE REGISTER DUMP EXEC HELP ILLUSTRATE KILL LS MKDIR MV PIG PWD QUIT RM RMF RUN SET ";
+
+	// data types
+	var pTypes = "BOOLEAN INT LONG FLOAT DOUBLE CHARARRAY BYTEARRAY BAG TUPLE MAP DATETIME ";
+
+	CodeMirror.defineMIME("text/x-pig", {
+	  name: "pig",
+	  builtins: keywords(pBuiltins),
+	  keywords: keywords(pKeywords),
+	  types: keywords(pTypes)
+	});
+}());

+ 177 - 0
desktop/core/static/js/codemirror-show-hint.js

@@ -0,0 +1,177 @@
+CodeMirror.showHint = function(cm, getHints, options) {
+  if (!options) options = {};
+  var startCh = cm.getCursor().ch, continued = false;
+
+  function startHinting() {
+    // We want a single cursor position.
+    if (cm.somethingSelected()) return;
+
+    if (options.async)
+      getHints(cm, showHints, options);
+    else
+      return showHints(getHints(cm, options));
+  }
+
+  function showHints(data) {
+    if (!data || !data.list.length) return;
+    var completions = data.list;
+    // When there is only one completion, use it directly.
+    if (!continued && options.completeSingle !== false && completions.length == 1) {
+      cm.replaceRange(completions[0], data.from, data.to);
+      return true;
+    }
+
+    // Build the select widget
+    var hints = document.createElement("ul"), selectedHint = 0;
+    hints.className = "CodeMirror-hints";
+    for (var i = 0; i < completions.length; ++i) {
+      var elt = hints.appendChild(document.createElement("li"));
+      elt.className = "CodeMirror-hint" + (i ? "" : " CodeMirror-hint-active");
+      //elt.appendChild(document.createTextNode(completions[i]));
+      elt.innerHTML = completions[i];
+      elt.hintId = i;
+    }
+    var pos = cm.cursorCoords(options.alignWithWord !== false ? data.from : null);
+    var left = pos.left, top = pos.bottom, below = true;
+    hints.style.left = left + "px";
+    hints.style.top = top + "px";
+    document.body.appendChild(hints);
+
+    // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
+    var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
+    var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
+    var box = hints.getBoundingClientRect();
+    var overlapX = box.right - winW, overlapY = box.bottom - winH;
+    if (overlapX > 0) {
+      if (box.right - box.left > winW) {
+        hints.style.width = (winW - 5) + "px";
+        overlapX -= (box.right - box.left) - winW;
+      }
+      hints.style.left = (left = pos.left - overlapX) + "px";
+    }
+    if (overlapY > 0) {
+      var height = box.bottom - box.top;
+      if (box.top - (pos.bottom - pos.top) - height > 0) {
+        overlapY = height + (pos.bottom - pos.top);
+        below = false;
+      } else if (height > winH) {
+        hints.style.height = (winH - 5) + "px";
+        overlapY -= height - winH;
+      }
+      hints.style.top = (top = pos.bottom - overlapY) + "px";
+    }
+
+    function changeActive(i) {
+      i = Math.max(0, Math.min(i, completions.length - 1));
+      if (selectedHint == i) return;
+      hints.childNodes[selectedHint].className = "CodeMirror-hint";
+      var node = hints.childNodes[selectedHint = i];
+      node.className = "CodeMirror-hint CodeMirror-hint-active";
+      if (node.offsetTop < hints.scrollTop)
+        hints.scrollTop = node.offsetTop - 3;
+      else if (node.offsetTop + node.offsetHeight > hints.scrollTop + hints.clientHeight)
+        hints.scrollTop = node.offsetTop + node.offsetHeight - hints.clientHeight + 3;
+    }
+
+    function screenAmount() {
+      return Math.floor(hints.clientHeight / hints.firstChild.offsetHeight) || 1;
+    }
+
+    var ourMap = {
+      Up: function() {changeActive(selectedHint - 1);},
+      Down: function() {changeActive(selectedHint + 1);},
+      PageUp: function() {changeActive(selectedHint - screenAmount());},
+      PageDown: function() {changeActive(selectedHint + screenAmount());},
+      Home: function() {changeActive(0);},
+      End: function() {changeActive(completions.length - 1);},
+      Enter: pick,
+      Tab: pick,
+      Esc: close
+    };
+    if (options.customKeys) for (var key in options.customKeys) if (options.customKeys.hasOwnProperty(key)) {
+      var val = options.customKeys[key];
+      if (/^(Up|Down|Enter|Esc)$/.test(key)) val = ourMap[val];
+      ourMap[key] = val;
+    }
+
+    cm.addKeyMap(ourMap);
+    cm.on("cursorActivity", cursorActivity);
+    var closingOnBlur;
+    function onBlur(){ closingOnBlur = setTimeout(close, 100); };
+    function onFocus(){ clearTimeout(closingOnBlur); };
+    cm.on("blur", onBlur);
+    cm.on("focus", onFocus);
+    var startScroll = cm.getScrollInfo();
+    function onScroll() {
+      var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();
+      var newTop = top + startScroll.top - curScroll.top, point = newTop;
+      if (!below) point += hints.offsetHeight;
+      if (point <= editor.top || point >= editor.bottom) return close();
+      hints.style.top = newTop + "px";
+      hints.style.left = (left + startScroll.left - curScroll.left) + "px";
+    }
+    cm.on("scroll", onScroll);
+    CodeMirror.on(hints, "dblclick", function(e) {
+      var t = e.target || e.srcElement;
+      if (t.hintId != null) {selectedHint = t.hintId; pick();}
+      setTimeout(function(){cm.focus();}, 20);
+    });
+    CodeMirror.on(hints, "click", function(e) {
+      var t = e.target || e.srcElement;
+      if (t.hintId != null) changeActive(t.hintId);
+      setTimeout(function(){cm.focus();}, 20);
+    });
+
+    var done = false, once;
+    function close() {
+      if (done) return;
+      done = true;
+      clearTimeout(once);
+      hints.parentNode.removeChild(hints);
+      cm.removeKeyMap(ourMap);
+      cm.off("cursorActivity", cursorActivity);
+      cm.off("blur", onBlur);
+      cm.off("focus", onFocus);
+      cm.off("scroll", onScroll);
+    }
+    function pick() {
+      var _insertion = strip(completions[selectedHint]).replace(/(?:(?:^|\n)\s+|\s+(?:$|\n))/g,'').replace(/\s+/g,' ');
+      if (CodeMirror.isPath){
+        var _curRange = cm.getRange(data.from, data.to);
+        if (_curRange.indexOf("/") > -1){
+          _insertion = _curRange.substring(0, _curRange.lastIndexOf("/") + 1) + _insertion;
+        }
+        else {
+          if (_curRange != ""){
+            _insertion = "'" + _insertion;
+          }
+        }
+      }
+      cm.replaceRange(_insertion, data.from, data.to);
+      if (CodeMirror.onAutocomplete !== "undefined" && CodeMirror.onAutocomplete != null) {
+        CodeMirror.onAutocomplete(_insertion, data.from, data.to);
+      }
+      close();
+    }
+    function strip(html){
+      var tmp = document.createElement("DIV");
+      tmp.innerHTML = html;
+      return tmp.textContent||tmp.innerText;
+    }
+
+    var once, lastPos = cm.getCursor(), lastLen = cm.getLine(lastPos.line).length;
+    function cursorActivity() {
+      clearTimeout(once);
+
+      var pos = cm.getCursor(), len = cm.getLine(pos.line).length;
+      if (pos.line != lastPos.line || len - pos.ch != lastLen - lastPos.ch ||
+          pos.ch < startCh || cm.somethingSelected())
+        close();
+      else
+        once = setTimeout(function(){close(); continued = true; startHinting();}, 70);
+    }
+    return true;
+  }
+
+  return startHinting();
+};

+ 33 - 0
desktop/core/static/js/jquery.alert.js

@@ -0,0 +1,33 @@
+// 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.
+/*
+* jHue alert plugin
+*/
+var jHueAlert = function(message){
+
+		if ($("#jHueAlertDialog").length==0){
+			var mainDialog = $("<div>").attr("id", "jHueAlertDialog").addClass("modal").addClass("hide").addClass("fade");
+			mainDialog.html('<div class="modal-header"><a href="#" class="close">&times;</a></div><div class="modal-body"></div>');
+			mainDialog.appendTo("body");
+			mainDialog.modal({
+				backdrop: "static",
+				keyboard: true
+			});
+		}
+		var alertDialog= $("#jHueAlertDialog");
+		alertDialog.find(".modal-body").text(message);
+		alertDialog.modal("show");
+};

+ 23 - 0
desktop/core/static/js/jquery.datatables.sorting.js

@@ -0,0 +1,23 @@
+// 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.
+
+$.fn.dataTableExt.afnSortData['dom-sort-value'] = function (oSettings, iColumn) {
+  var aData = [];
+  $( 'td:eq('+iColumn+')', oSettings.oApi._fnGetTrNodes(oSettings) ).each( function () {
+    aData.push( $(this).attr('data-sort-value') );
+  } );
+  return aData;
+};

+ 318 - 0
desktop/core/static/js/jquery.filechooser.js

@@ -0,0 +1,318 @@
+// 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.
+/*
+ * jHue fileChooser plugin
+ */
+;
+(function ($, window, document, undefined) {
+
+    var pluginName = "jHueFileChooser",
+        // global variables (jHueFileChooserGlobals, useful for i18n) can be set on
+        // desktop/templates/common_header.mako
+        defaults = {
+            initialPath:"",
+            forceRefresh:false,
+            errorRedirectPath:"",
+            createFolder:true,
+            uploadFile:true,
+            selectFolder:false,
+            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: "",
+            onFileChoose:function () {
+            },
+            onFolderChoose:function () {
+            },
+            onFolderChange:function () {
+            },
+            onError:function () {
+            }
+        },
+        STORAGE_PREFIX = "hueFileBrowserLastPathForUser_";
+
+    function Plugin(element, options) {
+        this.element = element;
+        if (typeof jHueFileChooserGlobals != 'undefined') {
+            var extendedDefaults = $.extend({}, defaults, jHueFileChooserGlobals);
+            extendedDefaults.labels = $.extend({}, defaults.labels, jHueFileChooserGlobals.labels);
+            this.options = $.extend({}, extendedDefaults, options);
+            if (options != null){
+                this.options.labels = $.extend({}, extendedDefaults.labels, options.labels);
+            }
+        }
+        else {
+            this.options = $.extend({}, defaults, options);
+            if (options != null){
+                this.options.labels = $.extend({}, defaults.labels, options.labels);
+            }
+        }
+        this._defaults = defaults;
+        this._name = pluginName;
+        this.previousPath = "";
+        this.init();
+    }
+
+    Plugin.prototype.setOptions = function (options) {
+        this.options = $.extend({}, defaults, options);
+        if (this.options.forceRefresh){
+          if ($.trim(this.options.initialPath) != "") {
+            this.navigateTo(this.options.initialPath);
+          }
+          else if ($.totalStorage(STORAGE_PREFIX + this.options.user) != null) {
+            this.navigateTo($.totalStorage(STORAGE_PREFIX + this.options.user));
+          }
+          else {
+            this.navigateTo("/?default_to_home");
+          }
+        }
+        else {
+          if ($.trim(this.options.initialPath) != "") {
+            this.navigateTo(this.options.initialPath);
+          }
+        }
+    };
+
+    Plugin.prototype.navigateTo = function (path) {
+        var _parent = this;
+        $(_parent.element).empty();
+        $.getJSON("/filebrowser/chooser" + path, function (data) {
+            path = data.current_dir_path; // use real path.
+            var _flist = $("<ul>").addClass("unstyled");
+            if (data.title != null && data.title == "Error") {
+                var _errorMsg = $("<div>").addClass("alert").addClass("alert-error").text(data.message);
+                _errorMsg.appendTo($(_parent.element));
+                var _previousLink = $("<a>").addClass("btn").addClass("bnt-small").text(_parent.options.labels.BACK).click(function () {
+                    _parent.options.onFolderChange(_parent.previousPath);
+                    _parent.navigateTo(_parent.previousPath);
+                });
+                _previousLink.appendTo($(_parent.element));
+            }
+            else {
+                if (data.type == "file") {
+                    _parent.navigateTo(data.view.dirname);
+                    return;
+                }
+                $.totalStorage(STORAGE_PREFIX + _parent.options.user, path);
+                _parent.previousPath = path;
+                var _breadcrumbs = $("<ul>").addClass("hueBreadcrumb").css("padding", "0");
+                var _home = $("<li>");
+                var _homelink = $("<a>").addClass("nounderline").html('<i class="icon-home"></i> Home').css("cursor", "pointer").click(function () {
+                    _parent.navigateTo("/?default_to_home");
+                });
+                _homelink.appendTo(_home);
+                $("<span>").addClass("divider").css("margin-right", "20px").appendTo(_home);
+                _home.appendTo(_breadcrumbs);
+                var _bLength = data.breadcrumbs.length;
+                $(data.breadcrumbs).each(function (cnt, crumb) {
+                    var _crumb = $("<li>");
+                    var _crumbLink = $("<a>");
+                    var _crumbLabel = (crumb.label != null && crumb.label != "") ? crumb.label : "/";
+                    _crumbLink.attr("href", "javascript:void(0)").text(_crumbLabel).appendTo(_crumb);
+                    if (cnt < _bLength - 1) {
+                        if (cnt > 0) {
+                            $("<span>").addClass("divider").text("/").appendTo(_crumb);
+                        }
+                        else {
+                            $("<span>").html("&nbsp;").appendTo(_crumb);
+                        }
+                    }
+                    _crumb.click(function () {
+                        var _url = (crumb.url != null && crumb.url != "") ? crumb.url : "/";
+                        _parent.options.onFolderChange(_url);
+                        _parent.navigateTo(_url);
+                    });
+                    _crumb.appendTo(_breadcrumbs);
+                });
+                _breadcrumbs.appendTo($(_parent.element));
+
+                $(data.files).each(function (cnt, file) {
+                    var _f = $("<li>");
+                    var _flink = $("<a>");
+                    _flink.attr("href", "javascript:void(0)").text(" " + file.name).appendTo(_f);
+                    if (file.type == "dir") {
+                        $("<i class='icon-folder-close'></i>").prependTo(_flink);
+                        _f.click(function () {
+                            _parent.options.onFolderChange(file.path);
+                            _parent.navigateTo(file.path);
+                        });
+                    }
+                    if (file.type == "file") {
+                        $("<i class='icon-file-alt'></i>").prependTo(_flink);
+                        _f.click(function () {
+                            _parent.options.onFileChoose(file.path);
+                        });
+                    }
+                    _f.appendTo(_flist);
+                });
+                _flist.appendTo($(_parent.element));
+                var _actions = $("<div>").addClass("jHueFilechooserActions");
+                var _showActions = false;
+                var _uploadFileBtn;
+                var _createFolderBtn;
+                var _selectFolderBtn;
+                if (_parent.options.uploadFile) {
+                    _uploadFileBtn = $("<div>").attr("id", "file-uploader");
+                    _uploadFileBtn.appendTo(_actions);
+                    _showActions = true;
+                    initUploader(path, _parent, _uploadFileBtn, _parent.options.labels);
+                }
+                if (_parent.options.selectFolder) {
+                    _selectFolderBtn = $("<a>").addClass("btn").addClass("small").text(_parent.options.labels.SELECT_FOLDER);
+                    if (_parent.options.uploadFile){
+                        _selectFolderBtn.css("margin-top", "10px");
+                    }
+                    _selectFolderBtn.appendTo(_actions);
+                    _showActions = true;
+                    _selectFolderBtn.click(function () {
+                        _parent.options.onFolderChoose(path);
+                    });
+                }
+                $("<span> </span>").appendTo(_actions);
+                if (_parent.options.createFolder) {
+                    _createFolderBtn = $("<a>").addClass("btn").addClass("small").text(_parent.options.labels.CREATE_FOLDER);
+                    if (_parent.options.uploadFile){
+                        _createFolderBtn.css("margin-top", "10px");
+                    }
+                    _createFolderBtn.appendTo(_actions);
+                    _showActions = true;
+                    var _createFolderDetails = $("<form>").css("margin-top", "10px").addClass("well form-inline");
+                    _createFolderDetails.hide();
+                    var _folderName = $("<input>").attr("type", "text").attr("placeholder", _parent.options.labels.FOLDER_NAME).appendTo(_createFolderDetails);
+                    $("<span> </span>").appendTo(_createFolderDetails);
+                    var _folderBtn = $("<input>").attr("type", "button").attr("value", _parent.options.labels.CREATE_FOLDER).addClass("btn primary").appendTo(_createFolderDetails);
+                    $("<span> </span>").appendTo(_createFolderDetails);
+                    var _folderCancel = $("<input>").attr("type", "button").attr("value", _parent.options.labels.CANCEL).addClass("btn").appendTo(_createFolderDetails);
+                    _folderCancel.click(function () {
+                        if (_uploadFileBtn) {
+                            _uploadFileBtn.removeClass("disabled");
+                        }
+                        _createFolderBtn.removeClass("disabled");
+                        _createFolderDetails.slideUp();
+                    });
+                    _folderBtn.click(function () {
+                        $.ajax({
+                            type:"POST",
+                            url:"/filebrowser/mkdir",
+                            data:{
+                                name:_folderName.val(),
+                                path:path
+                            },
+                            beforeSend:function (xhr) {
+                                xhr.setRequestHeader("X-Requested-With", "Hue"); // need to override the default one because otherwise Django returns HTTP 500
+                            },
+                            success:function (xhr, status) {
+                                if (status == "success") {
+                                    _parent.navigateTo(path);
+                                    if (_uploadFileBtn) {
+                                        _uploadFileBtn.removeClass("disabled");
+                                    }
+                                    _createFolderBtn.removeClass("disabled");
+                                    _createFolderDetails.slideUp();
+                                }
+                            }
+                        });
+
+                    });
+
+                    _createFolderDetails.appendTo(_actions);
+
+                    _createFolderBtn.click(function () {
+                        if (_uploadFileBtn) {
+                            _uploadFileBtn.addClass("disabled");
+                        }
+                        _createFolderBtn.addClass("disabled");
+                        _createFolderDetails.slideDown();
+                    });
+                }
+                if (_showActions){
+                    _actions.appendTo($(_parent.element));
+                }
+            }
+        }).error(function(){
+            _parent.options.onError();
+            _parent.navigateTo(_parent.options.errorRedirectPath != "" ? _parent.options.errorRedirectPath : "/?default_to_home");
+            if ($.jHueNotify) {
+                $.jHueNotify.info(_parent.options.labels.FILE_NOT_FOUND);
+            }
+        });
+    };
+
+    var num_of_pending_uploads = 0;
+    function initUploader(path, _parent, el, labels) {
+        var uploader = new qq.FileUploader({
+            element:el[0],
+            action:'/filebrowser/upload/file',
+            params:{
+                dest:path,
+                fileFieldLabel:'hdfs_file'
+            },
+            onComplete:function (id, fileName, responseJSON) {
+                num_of_pending_uploads--;
+                if(num_of_pending_uploads == 0){
+                    _parent.navigateTo(path);
+                }
+            },
+            onSubmit: function(id, fileName){
+                num_of_pending_uploads++;
+            },
+            template: '<div class="qq-uploader">' +
+                '<div class="qq-upload-drop-area"><span></span></div>' +
+                '<div class="qq-upload-button">' + labels.UPLOAD_FILE + '</div>' +
+                '<ul class="qq-upload-list"></ul>' +
+                '</div>',
+            fileTemplate: '<li>' +
+                '<span class="qq-upload-file"></span>' +
+                '<span class="qq-upload-spinner"></span>' +
+                '<span class="qq-upload-size"></span>' +
+                '<a class="qq-upload-cancel" href="#">' + labels.CANCEL + '</a>' +
+                '<span class="qq-upload-failed-text">' + labels.FAILED + '</span>' +
+                '</li>',
+            debug:false
+        });
+    }
+
+    Plugin.prototype.init = function () {
+        if ($.trim(this.options.initialPath) != "") {
+            this.navigateTo(this.options.initialPath);
+        }
+        else if ($.totalStorage(STORAGE_PREFIX + this.options.user) != null) {
+            this.navigateTo($.totalStorage(STORAGE_PREFIX + this.options.user));
+        }
+        else {
+            this.navigateTo("/?default_to_home");
+        }
+    };
+
+    $.fn[pluginName] = function (options) {
+        return this.each(function () {
+            if (!$.data(this, 'plugin_' + pluginName)) {
+                $.data(this, 'plugin_' + pluginName, new Plugin(this, options));
+            }
+            else {
+                $.data(this, 'plugin_' + pluginName).setOptions(options);
+            }
+        });
+    }
+
+})(jQuery, window, document);

+ 111 - 0
desktop/core/static/js/jquery.migration.js

@@ -0,0 +1,111 @@
+// 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.
+/*
+ * Repo for missing jQuery 2 functions, this ease the transition without updating every other plugin in Hue
+ *
+ */
+
+jQuery.uaMatch = function (ua) {
+  ua = ua.toLowerCase();
+
+  var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
+    /(webkit)[ \/]([\w.]+)/.exec(ua) ||
+    /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
+    /(msie) ([\w.]+)/.exec(ua) ||
+    ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
+    [];
+
+  return {
+    browser: match[ 1 ] || "",
+    version: match[ 2 ] || "0"
+  };
+};
+
+// Don't clobber any existing jQuery.browser in case it's different
+if (!jQuery.browser) {
+  matched = jQuery.uaMatch(navigator.userAgent);
+  browser = {};
+
+  if (matched.browser) {
+    browser[ matched.browser ] = true;
+    browser.version = matched.version;
+  }
+
+  // Chrome is Webkit, but Webkit is also Safari.
+  if (browser.chrome) {
+    browser.webkit = true;
+  } else if (browser.webkit) {
+    browser.safari = true;
+  }
+
+  jQuery.browser = browser;
+}
+
+// Since jQuery.clean is used internally on older versions, we only shim if it's missing
+if (!jQuery.clean) {
+  jQuery.clean = function (elems, context, fragment, scripts) {
+    // Set context per 1.8 logic
+    context = context || document;
+    context = !context.nodeType && context[0] || context;
+    context = context.ownerDocument || context;
+
+    var i, elem, handleScript, jsTags,
+      ret = [];
+
+    jQuery.merge(ret, jQuery.buildFragment(elems, context).childNodes);
+
+    // Complex logic lifted directly from jQuery 1.8
+    if (fragment) {
+      // Special handling of each script element
+      handleScript = function (elem) {
+        // Check if we consider it executable
+        if (!elem.type || rscriptType.test(elem.type)) {
+          // Detach the script and store it in the scripts array (if provided) or the fragment
+          // Return truthy to indicate that it has been handled
+          return scripts ?
+            scripts.push(elem.parentNode ? elem.parentNode.removeChild(elem) : elem) :
+            fragment.appendChild(elem);
+        }
+      };
+
+      for (i = 0; (elem = ret[i]) != null; i++) {
+        // Check if we're done after handling an executable script
+        if (!( jQuery.nodeName(elem, "script") && handleScript(elem) )) {
+          // Append to fragment and handle embedded scripts
+          fragment.appendChild(elem);
+          if (typeof elem.getElementsByTagName !== "undefined") {
+            // handleScript alters the DOM, so use jQuery.merge to ensure snapshot iteration
+            jsTags = jQuery.grep(jQuery.merge([], elem.getElementsByTagName("script")), handleScript);
+
+            // Splice the scripts into ret after their former ancestor and advance our index beyond them
+            ret.splice.apply(ret, [i + 1, 0].concat(jsTags));
+            i += jsTags.length;
+          }
+        }
+      }
+    }
+
+    return ret;
+  };
+}
+
+var rscriptType = /\/(java|ecma)script/i,
+  oldSelf = jQuery.fn.andSelf || jQuery.fn.addBack;
+
+jQuery.fn.andSelf = function () {
+  return oldSelf.apply(this, arguments);
+};
+

+ 124 - 0
desktop/core/static/js/jquery.notify.js

@@ -0,0 +1,124 @@
+// 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.
+/*
+ * jHue notify plugin
+ *
+ */
+;(function ($, window, document, undefined) {
+
+    var pluginName = "jHueNotify",
+        TYPES = {
+            INFO: "INFO",
+            ERROR: "ERROR",
+            GENERAL: "GENERAL"
+        },
+        defaults = {
+            level: TYPES.GENERAL,
+            message: "",
+            sticky: false,
+            css: null
+        };
+
+    function Plugin(options) {
+        this.options = $.extend({}, defaults, options) ;
+        this._defaults = defaults;
+        this._name = pluginName;
+        this.show();
+    }
+
+    Plugin.prototype.setOptions = function(options) {
+        this.options = $.extend({}, defaults, options) ;
+    };
+
+    Plugin.prototype.show = function () {
+        var _this = this;
+        var MARGIN = 6;
+        var el = $("#jHueNotify").clone();
+        el.removeAttr("id");
+
+        // stops all the current animations and resets the style
+        el.stop(true);
+        el.attr("class", "alert jHueNotify");
+        el.find(".close").hide();
+
+        if ($(".jHueNotify").last().position() != null) {
+            el.css("top", $(".jHueNotify").last().position().top + $(".jHueNotify").last().outerHeight() + MARGIN - $(window).scrollTop());
+        }
+
+        if (_this.options.level == TYPES.ERROR){
+            el.addClass("alert-error");
+            el.find(".message").html("<i class='icon-warning-sign'></i> <strong>" + _this.options.message + "</strong>");
+        }
+        else if (_this.options.level == TYPES.INFO){
+            el.addClass("alert-info");
+            el.find(".message").html("<i class='icon-info-sign'></i> <strong>" + _this.options.message + "</strong>");
+        }
+        else {
+            el.find(".message").html(_this.options.message);
+        }
+
+        if (_this.options.css != null){
+            el.attr("style", _this.options.css);
+        }
+
+        if (_this.options.sticky){
+            el.find(".close").click(function(){
+                el.fadeOut();
+                el.nextAll(".jHueNotify").animate({
+                    top:'-=' + (el.outerHeight() + MARGIN)
+                }, 200);
+                el.remove();
+            }).show();
+            el.show();
+        }
+        else {
+            var t = window.setTimeout(function(){
+                el.fadeOut();
+                el.nextAll(".jHueNotify").animate({
+                    top:'-=' + (el.outerHeight() + MARGIN)
+                }, 200);
+                el.remove();
+
+            }, 3000);
+            el.click(function(){
+                window.clearTimeout(t);
+                $(this).stop(true);
+                $(this).fadeOut();
+                $(this).nextAll(".jHueNotify").animate({
+                    top:'-=' + ($(this).outerHeight() + MARGIN)
+                }, 200);
+            });
+            el.show();
+        }
+        el.appendTo($("body"));
+    };
+
+    $[pluginName] = function () {
+    };
+
+    $[pluginName].info = function (message) {
+        new Plugin({ level: TYPES.INFO, message: message});
+    };
+
+    $[pluginName].error = function (message) {
+        new Plugin({ level: TYPES.ERROR, message: message, sticky: true});
+    };
+
+    $[pluginName].notify = function (options) {
+        new Plugin(options);
+    };
+
+})(jQuery, window, document);

+ 68 - 0
desktop/core/static/js/jquery.rowselector.js

@@ -0,0 +1,68 @@
+// 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.
+/*
+ * jHue row selector plugin
+ *
+ */
+;
+(function ($, window, document, undefined) {
+
+    var pluginName = "jHueRowSelector",
+        defaults = {
+        };
+
+    function Plugin(element, options) {
+        this.element = element;
+        this.options = $.extend({}, defaults, options);
+        this._defaults = defaults;
+        this._name = pluginName;
+        this.init();
+    }
+
+    Plugin.prototype.setOptions = function (options) {
+        this.options = $.extend({}, defaults, options);
+    };
+
+    Plugin.prototype.init = function () {
+        var _this = this;
+        $(_this.element).closest("tr").click(function (e) {
+            if ($(e.target).data("row-selector-exclude")) {
+                return;
+            }
+            if (!$(e.target).is("a")) {
+                var href = $.trim($(_this.element).attr("href"));
+                if (href != "" && href != "#" && href.indexOf("void(0)") == -1) {
+                    location.href = $(_this.element).attr("href");
+                }
+                else {
+                    $(_this.element).click();
+                }
+            }
+        }).css("cursor", "pointer");
+    };
+
+    $.fn[pluginName] = function (options) {
+        return this.each(function () {
+            if (!$.data(this, 'plugin_' + pluginName)) {
+                $.data(this, 'plugin_' + pluginName, new Plugin(this, options));
+            }
+            else {
+                $.data(this, 'plugin_' + pluginName).setOptions(options);
+            }
+        });
+    }
+
+})(jQuery, window, document);

+ 91 - 0
desktop/core/static/js/jquery.scrollup.js

@@ -0,0 +1,91 @@
+// 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.
+/*
+ * jHue scroll to top plugin
+ * Can be used globally with
+ *   $.jHueScrollUp()
+ * or with a target for the scroll up
+ *   $(element).jHueScrollUp()
+ *
+ *   options:
+ *    - threshold: (default 100) value in pixels, scroll amount before the link appears
+ */
+
+(function ($, window, document, undefined) {
+
+  var pluginName = "jHueScrollUp",
+      defaults = {
+        threshold: 100 // it displays it after 100 px of scroll
+      };
+
+  function Plugin(element, options) {
+    this.element = element;
+    this.options = $.extend({}, defaults, options);
+    this._defaults = defaults;
+    this._name = pluginName;
+    this.init();
+  }
+
+  Plugin.prototype.setOptions = function (options) {
+    this.options = $.extend({}, defaults, options);
+  };
+
+  Plugin.prototype.init = function () {
+    var _this = this;
+
+    if ($("#jHueScrollUpAnchor").length > 0) { // just one scroll up per page
+      return;
+    }
+
+    var link = $("<a/>").attr("id", "jHueScrollUpAnchor").attr("href", "javascript:void(0)").html("<i class='icon-chevron-up'></i>").appendTo("body");
+
+    if ($(_this.element).is("body")) {
+      $(window).scroll(function () {
+        $(($(window).scrollTop() > _this.options.threshold) ? link.fadeIn(200) : link.fadeOut(200));
+      });
+      link.click(function (event) {
+        $("body, html").animate({scrollTop: $(_this.element).position().top}, 300);
+        return false;
+      });
+    }
+    else {
+      $(_this.element).scroll(function () {
+        $(($(_this.element).scrollTop() > _this.options.threshold) ? link.fadeIn(200) : link.fadeOut(200));
+      });
+      link.click(function (event) {
+        $(_this.element).animate({scrollTop: 0}, 300);
+        return false;
+      });
+    }
+
+  };
+
+  $.fn[pluginName] = function (options) {
+    return this.each(function () {
+      if (!$.data(this, 'plugin_' + pluginName)) {
+        $.data(this, 'plugin_' + pluginName, new Plugin(this, options));
+      }
+      else {
+        $.data(this, 'plugin_' + pluginName).setOptions(options);
+      }
+    });
+  }
+
+  $[pluginName] = function (options) {
+    new Plugin($("body"), options);
+  };
+
+})(jQuery, window, document);

+ 170 - 0
desktop/core/static/js/jquery.selector.js

@@ -0,0 +1,170 @@
+// 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.
+/*
+ * jHue selector plugin
+ * it tranforms a select multiple into a searchable/selectable alphabetical list
+ */
+;
+(function ($, window, document, undefined) {
+
+  var pluginName = "jHueSelector",
+      defaults = {
+        selectAllLabel: "Select all",
+        searchPlaceholder: "Search",
+        noChoicesFound: "No choices found for this element",
+        width: 300,
+        height: 200,
+        onChange: function () {
+        }
+      };
+
+  function Plugin(element, options) {
+    this.element = element;
+    this.options = $.extend({}, defaults, options);
+    this._defaults = defaults;
+    this._name = pluginName;
+    this.init();
+  }
+
+  Plugin.prototype.setOptions = function (options) {
+    this.options = $.extend({}, defaults, options);
+  };
+
+  Plugin.prototype.init = function () {
+    var _this = this;
+    var addressBook = [];
+    var selectorContainer = $("<div>");
+    if (this.options.width != 300) {
+      selectorContainer.width(this.options.width);
+    }
+    $(_this.element).hide();
+    $(_this.element).find("option").each(function (cnt, opt) {
+      var initial = $(opt).text().substr(0, 1).toLowerCase();
+      if (addressBook[initial] == null) {
+        addressBook[initial] = [];
+      }
+      addressBook[initial].push($(opt));
+    });
+    var sortedKeys = [];
+    for (var key in addressBook) {
+      sortedKeys.push(key);
+    }
+    sortedKeys.sort();
+
+    if (sortedKeys.length == 0) {
+      $(_this.element).after($("<div>").addClass("alert").css("margin-top", "-2px").css("float", "left").html(this.options.noChoicesFound));
+    }
+    else {
+      selectorContainer.addClass("jHueSelector");
+      var body = $("<div>").addClass("jHueSelectorBody");
+      body.appendTo(selectorContainer);
+
+      for (var i = 0; i < sortedKeys.length; i++) {
+        var key = sortedKeys[i];
+        var ul = $("<ul>");
+        var dividerLi = $("<li>").addClass("selectorDivider");
+        dividerLi.html("<strong>" + key.toUpperCase() + "</strong>");
+        dividerLi.appendTo(ul);
+        $.each(addressBook[key], function (cnt, opt) {
+          var li = $("<li>");
+          var lbl = $("<label>").text(opt.text());
+          var chk = $("<input>").attr("type", "checkbox").addClass("selector").change(function () {
+            if ($(this).is(":checked")) {
+              $(this).data("opt").attr("selected", "selected");
+            }
+            else {
+              $(this).data("opt").removeAttr("selected");
+            }
+            _this.options.onChange();
+          }).data("opt", opt).prependTo(lbl);
+          if (opt.is(":selected")) {
+            chk.attr("checked", "checked");
+          }
+          lbl.appendTo(li);
+          li.appendTo(ul);
+        });
+        ul.appendTo(body);
+      }
+
+      var header = $("<div>").addClass("jHueSelectorHeader");
+      header.prependTo(selectorContainer);
+
+      var selectAll = $("<label>").text(this.options.selectAllLabel);
+      $("<input>").attr("type", "checkbox").change(function () {
+        var isChecked = $(this).is(":checked");
+        selectorContainer.find("input.selector:visible").each(function () {
+          if (isChecked) {
+            $(this).attr("checked", "checked");
+            $(this).data("opt").attr("selected", "selected");
+          }
+          else {
+            $(this).removeAttr("checked");
+            $(this).data("opt").removeAttr("selected");
+          }
+        });
+        if (searchBox.val() != "") {
+          $(this).removeAttr("checked");
+        }
+        _this.options.onChange();
+      }).prependTo(selectAll);
+      selectAll.appendTo(header);
+
+      var searchBox = $("<input>").attr("type", "text").attr("placeholder", this.options.searchPlaceholder).keyup(function () {
+        body.find("ul").attr("show", true).show();
+        var q = $.trim($(this).val());
+        if (q != "") {
+          body.find("li.selectorDivider").hide();
+          body.find("label").each(function () {
+            if ($(this).text().toLowerCase().indexOf(q.toLowerCase()) > -1) {
+              $(this).parent().show();
+            }
+            else {
+              $(this).parent().hide();
+            }
+          });
+          body.find("ul").attr("show", false);
+          body.find("ul > *:visible").parent().attr("show", true).find("li.selectorDivider").show();
+        }
+        else {
+          body.find("li.selectorDivider").show();
+          body.find("label").parent().show();
+        }
+        body.find("ul[show=false]").hide();
+        body.find("ul[show=true]").show();
+      });
+      if (this.options.width != 300) {
+        searchBox.css("margin-left", this.options.width - 120 + "px");
+      }
+      searchBox.prependTo(header);
+
+      body.height(this.options.height - header.outerHeight());
+
+      $(_this.element).after(selectorContainer);
+    }
+  };
+
+  $.fn[pluginName] = function (options) {
+    return this.each(function () {
+      if (!$.data(this, 'plugin_' + pluginName)) {
+        $.data(this, 'plugin_' + pluginName, new Plugin(this, options));
+      }
+      else {
+        $.data(this, 'plugin_' + pluginName).setOptions(options);
+      }
+    });
+  }
+
+})(jQuery, window, document);

+ 209 - 0
desktop/core/static/js/jquery.tableextender.js

@@ -0,0 +1,209 @@
+// 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.
+/*
+ * jHue table extender plugin
+ *
+ */
+;
+(function ($, window, document, undefined) {
+
+  var pluginName = "jHueTableExtender",
+      defaults = {
+        fixedHeader:false,
+        firstColumnTooltip:false,
+        hintElement:null,
+        labels:{
+          GO_TO_COLUMN:"Go to column:",
+          PLACEHOLDER:"column name..."
+        }
+      };
+
+  function Plugin(element, options) {
+    this.element = element;
+    if (typeof jHueTableExtenderGlobals != 'undefined') {
+      var extendedDefaults = $.extend({}, defaults, jHueFileChooserGlobals);
+      extendedDefaults.labels = $.extend({}, defaults.labels, jHueFileChooserGlobals.labels);
+      this.options = $.extend({}, extendedDefaults, options);
+      if (options != null) {
+        this.options.labels = $.extend({}, extendedDefaults.labels, options.labels);
+      }
+    }
+    else {
+      this.options = $.extend({}, defaults, options);
+      if (options != null) {
+        this.options.labels = $.extend({}, defaults.labels, options.labels);
+      }
+    }
+    this._defaults = defaults;
+    this._name = pluginName;
+    this.previousPath = "";
+    this.init();
+  }
+
+  Plugin.prototype.setOptions = function (options) {
+    this.options = $.extend({}, defaults, options);
+  };
+
+  Plugin.prototype.init = function () {
+
+    $.expr[":"].econtains = function (obj, index, meta, stack) {
+      return (obj.textContent || obj.innerText || $(obj).text() || "").toLowerCase() == meta[3].toLowerCase();
+    }
+
+    var _this = this;
+
+    var jHueTableExtenderNavigator = $("<div>").attr("id", "jHueTableExtenderNavigator");
+    $("<a>").attr("href", "#").addClass("pull-right").html("&times;").click(function (e) {
+      e.preventDefault();
+      $(this).parent().hide();
+    }).appendTo(jHueTableExtenderNavigator);
+    $("<label>").html(_this.options.labels.GO_TO_COLUMN + " <input type=\"text\" placeholder=\"" + _this.options.labels.PLACEHOLDER + "\" />").appendTo(jHueTableExtenderNavigator);
+
+    jHueTableExtenderNavigator.appendTo($("body"));
+
+    $(_this.element).find("tbody").click(function (event) {
+      if ($.trim(getSelection()) == "") {
+        window.setTimeout(function () {
+          $(".rowSelected").removeClass("rowSelected");
+          $(".columnSelected").removeClass("columnSelected");
+          $(".cellSelected").removeClass("cellSelected");
+          $(event.target.parentNode).addClass("rowSelected");
+          $(event.target.parentNode).find("td").addClass("rowSelected");
+          jHueTableExtenderNavigator
+              .css("left", (event.clientX + jHueTableExtenderNavigator.width() > $(window).width() - 10 ? event.clientX - jHueTableExtenderNavigator.width() - 10 : event.clientX) + "px")
+              .css("top", (event.clientY + 10) + "px").show();
+          jHueTableExtenderNavigator.find("input").focus();
+        }, 100);
+      }
+    });
+
+    var source = [];
+    $(_this.element).find("th").each(function () {
+      source.push($(this).text());
+    });
+
+    jHueTableExtenderNavigator.find("input").typeahead({
+      source:source,
+      updater:function (item) {
+        jHueTableExtenderNavigator.hide();
+        $(_this.element).find("tr td:nth-child(" + ($(_this.element).find("th:econtains(" + item + ")").index() + 1) + ")").addClass("columnSelected");
+        if (_this.options.firstColumnTooltip) {
+          $(_this.element).find("tr td:nth-child(" + ($(_this.element).find("th:econtains(" + item + ")").index() + 1) + ")").each(function () {
+            $(this).attr("rel", "tooltip").attr("title", "#" + $(this).parent().find("td:first-child").text()).tooltip({
+              placement:'left'
+            });
+          });
+        }
+        $(_this.element).parent().animate({
+          scrollLeft:$(_this.element).find("th:econtains(" + item + ")").position().left + $(_this.element).parent().scrollLeft() - $(_this.element).parent().offset().left - 30
+        }, 300);
+        $(_this.element).find("tr.rowSelected td:nth-child(" + ($(_this.element).find("th:econtains(" + item + ")").index() + 1) + ")").addClass("cellSelected");
+      }
+    });
+
+    $(_this.element).parent().bind("mouseleave", function () {
+      jHueTableExtenderNavigator.hide();
+    });
+
+    jHueTableExtenderNavigator.bind("mouseenter", function (e) {
+      jHueTableExtenderNavigator.show();
+    });
+
+    if (_this.options.hintElement != null) {
+      var showAlertTimeout = -1;
+      $(_this.element).find("tbody").mousemove(function () {
+        window.clearTimeout(showAlertTimeout);
+        if ($(_this.options.hintElement).data("show") == null || $(_this.options.hintElement).data("show")) {
+          showAlertTimeout = window.setTimeout(function () {
+            $(_this.options.hintElement).fadeIn();
+          }, 1300);
+        }
+      });
+
+      $(_this.options.hintElement).find(".close").click(function () {
+        $(_this.options.hintElement).data("show", false);
+      });
+    }
+
+    if (_this.options.fixedHeader) {
+      var clonedTable = $(_this.element).clone();
+      clonedTable.css("margin-bottom", "0").css("table-layout", "fixed");
+      clonedTable.removeClass("resultTable").find("tbody").remove();
+      $(_this.element).find("thead>tr th").each(function (i) {
+        var originalTh = $(this);
+        clonedTable.find("thead>tr th:eq(" + i + ")").width(originalTh.width()).css("background-color", "#FFFFFF");
+        clonedTable.find("thead>tr th:eq(" + i + ")").click(function () {
+          originalTh.click();
+          clonedTable.find("thead>tr th").attr("class", "sorting");
+          $(this).attr("class", originalTh.attr("class"));
+        });
+      });
+      var clonedTableContainer = $("<div>").width($(_this.element).outerWidth());
+      clonedTable.appendTo(clonedTableContainer);
+
+      var clonedTableVisibleContainer = $("<div>").width($(_this.element).parent().width()).css("overflow-x", "hidden");
+      clonedTableVisibleContainer.css("position", "fixed");
+
+      clonedTableContainer.appendTo(clonedTableVisibleContainer);
+      clonedTableVisibleContainer.prependTo($(_this.element).parent());
+
+      $(_this.element).parent().scroll(function () {
+        clonedTableVisibleContainer.scrollLeft($(this).scrollLeft());
+      });
+
+      $(_this.element).parent().data("w", clonedTableVisibleContainer.width());
+
+      window.setInterval(function () {
+        if ($(_this.element).parent().width() != $(_this.element).parent().data("w")) {
+          clonedTableVisibleContainer.width($(_this.element).parent().width());
+          $(_this.element).parent().data("w", clonedTableVisibleContainer.width());
+          $(_this.element).find("thead>tr th").each(function (i) {
+            clonedTable.find("thead>tr th:eq(" + i + ")").width($(this).width()).css("background-color", "#FFFFFF");
+          });
+        }
+      }, 250);
+
+      $(_this.element).parent().resize(function () {
+        clonedTableVisibleContainer.width($(this).width());
+      });
+    }
+
+  };
+
+  function getSelection() {
+    var t = '';
+    if (window.getSelection) {
+      t = window.getSelection();
+    } else if (document.getSelection) {
+      t = document.getSelection();
+    } else if (document.selection) {
+      t = document.selection.createRange().text;
+    }
+    return t.toString();
+  }
+
+  $.fn[pluginName] = function (options) {
+    return this.each(function () {
+      if (!$.data(this, 'plugin_' + pluginName)) {
+        $.data(this, 'plugin_' + pluginName, new Plugin(this, options));
+      }
+      else {
+        $.data(this, 'plugin_' + pluginName).setOptions(options);
+      }
+    });
+  }
+
+})(jQuery, window, document);

+ 120 - 0
desktop/core/static/js/jquery.tablescroller.js

@@ -0,0 +1,120 @@
+// 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.
+/*
+ * jHue table scroller plugin
+ *
+ * Three data attributes can be set on the table to modify the default behavior of the plugin:
+ * - data-tablescroller-min-height="N" where N is a value in pixels: specifies a minimum height for the scrolling table,
+ *      overriding the default 500px set by the plugin
+ * - data-tablescroller-min-height-disable="true": disable enforcing a minimum height for the table
+ * - data-tablescroller-disable="true": disable the plugin for the specific table
+ * - data-tablescroller-enforce-height="true": displays the table at its maximum height accordingly to the page
+ */
+;
+(function ($, window, document, undefined) {
+
+  var pluginName = "jHueTableScroller",
+      defaults = {
+        minHeight: 400
+      };
+
+  function Plugin(element, options) {
+    this.element = element;
+    this.options = $.extend({}, defaults, options);
+    this._defaults = defaults;
+    this._name = pluginName;
+    this.init();
+  }
+
+  Plugin.prototype.setOptions = function (options) {
+    this.options = $.extend({}, defaults, options);
+  };
+
+  Plugin.prototype.init = function () {
+    var _this = this;
+
+    $(_this.element).data("original-height", $(_this.element).height());
+
+    var disableScrollingTable = $(_this.element).find("table").eq(0).data("tablescroller-disable");
+    if (disableScrollingTable == null || disableScrollingTable != true) {
+      resizeScrollingTable(_this.element);
+      var _resizeTimeout = -1;
+      var winWidth = $(window).width();
+      var winHeight = $(window).height();
+      $(window).resize(function () {
+        window.clearTimeout(_resizeTimeout);
+        _resizeTimeout = window.setTimeout(function(){
+          // prevents endless loop in IE8
+          if (winWidth != $(window).width() || winHeight != $(window).height()) {
+            resizeScrollingTable(_this.element);
+            winWidth = $(window).width();
+            winHeight = $(window).height();
+          }
+        }, 400);
+      });
+    }
+
+    function resizeScrollingTable(el) {
+      $(el).css("overflow-y", "").css("height", "");
+      var heightAfter = 0;
+      $(el).nextAll(":visible").each(function () {
+        heightAfter += $(this).outerHeight(true);
+      });
+
+      var heightCondition = $(el).height() > ($(window).height() - $(el).offset().top - heightAfter);
+      var enforceHeight = $(_this.element).find("table").eq(0).data("tablescroller-enforce-height");
+      if (enforceHeight !== undefined && enforceHeight == true) {
+        heightCondition = true;
+      }
+
+      if (heightCondition) {
+        var specificMinHeight = $(el).find("table").eq(0).data("tablescroller-min-height");
+        var minHeightVal = _this.options.minHeight;
+        if (!isNaN(parseFloat(specificMinHeight)) && isFinite(specificMinHeight)) {
+          minHeightVal = parseFloat(specificMinHeight);
+        }
+        var disableMinHeight = $(_this.element).find("table").eq(0).data("tablescroller-min-height-disable");
+        if (disableMinHeight != null && disableMinHeight == true) {
+          if (heightCondition) {
+            $(el).css("overflow-y", "auto").height($(window).height() - $(el).offset().top - heightAfter);
+          }
+        }
+        else {
+          if (($(window).height() - $(el).offset().top - heightAfter) > minHeightVal){
+            $(el).css("overflow-y", "auto").height($(window).height() - $(el).offset().top - heightAfter);
+          }
+          else {
+            if ($(el).data("original-height") > minHeightVal){
+              $(el).css("overflow-y", "auto").height(minHeightVal);
+            }
+          }
+        }
+      }
+    }
+  };
+
+  $.fn[pluginName] = function (options) {
+    return this.each(function () {
+      if (!$.data(this, 'plugin_' + pluginName)) {
+        $.data(this, 'plugin_' + pluginName, new Plugin(this, options));
+      }
+      else {
+        $.data(this, 'plugin_' + pluginName).setOptions(options);
+      }
+    });
+  }
+
+})(jQuery, window, document);

+ 480 - 0
desktop/core/static/js/jquery.tour.js

@@ -0,0 +1,480 @@
+// 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.
+/*
+ * jHue tour plugin
+ * Optionally depends on $.totalstorage for progress checking and $.jHueNotify for error notification
+ * Can be instantiated with
+ $.jHueTour({
+        tours: [  <-- array, the tours available for this page
+          {
+            name: "xxxx", <-- unique tour name  (location.pathname scope)
+            desc: "Desc yyyy", <-- the label shown on the question mark
+            path: "beeswax/*", <-- string for the path to show this tour on
+            steps: [ <-- array, steps of the tour
+              {
+                arrowOn: "a[href='/beeswax']", <-- the element relative to the popover is positioned
+                expose: ".navbar-fixed-top", <-- optional, the exposed object. if not present, arrowOn will be exposed
+                title: "Welcome to Beeswax!", <-- popover title
+                content: "This is a tour of the Beeswax app. <br/><b>HTML</b> is supported <em>too!</em>", <-- popover content, html enable
+                placement: "bottom", <-- popover placement
+                left: "100px", <-- popover absolute position (css string)
+                top: -20 <-- popover relative position (it adds that amount of pixels to the popover calculated position)
+                visitUrl: "blabla?tour=hello" <-- overrides everything, redirects to specific url
+              },
+              {
+                arrowOn: ".subnav-fixed",
+                title: "Beeswax sections",
+                content: "There are several sections in the Beeswax app",
+                placement: "bottom",
+                left: "100px"
+              }, ...
+            ]
+          }, ...
+        ]
+      });
+
+  Calling $.jHueTour({tours: [...]}) more than once will merge the tour data, so you can keep adding tours dinamically to the same page
+
+  You can interact with:
+  - $.jHueTour("start") / $.jHueTour("show") / $.jHueTour("play") : starts the first available tour
+  - $.jHueTour("stop") / $.jHueTour("close") / $.jHueTour("hide") / $.jHueTour("stop") : starts the first available tour
+  - $.jHueTour("reset") : removes stored tours and history
+  - $.jHueTour("clear") : removes current tours
+  - $.jHueTour("http://remote/hue/tour.hue") : loads a remote tour
+  - $.jHueTour("tourName", 1) : loads tour name and start at step 1
+  - $.jHueTour() : returns the available tours
+ */
+
+(function ($, window, document, undefined) {
+
+  var pluginName = "jHueTour",
+    defaults = {
+      labels: {
+        AVAILABLE_TOURS: "Available tours",
+        NO_AVAILABLE_TOURS: "None for this page"
+      },
+      tours: [],
+      showRemote: false,
+      questionMarkPlacement: "right",
+      hideIfNoneAvailable: true
+    };
+
+  function Plugin(element, options) {
+    this.element = element;
+    if (typeof jHueTourGlobals !== undefined) {
+      var extendedDefaults = $.extend({}, defaults, jHueTourGlobals);
+      extendedDefaults.labels = $.extend({}, defaults.labels, jHueTourGlobals.labels);
+      this.options = $.extend({}, extendedDefaults, options);
+      this.options = $.extend({}, defaults, this.options);
+    }
+    else {
+      this.options = $.extend({}, defaults, options);
+    }
+    this._defaults = defaults;
+    this._name = pluginName;
+    this.currentTour = {
+      name: "",
+      path: "",
+      remote: false,
+      steps: [],
+      shownStep: 0
+    };
+    this.init();
+  }
+
+  Plugin.prototype.init = function () {
+    var _this = this;
+    _this.initQuestionMark();
+    var _tourMask = $("<div>").attr("id", "jHueTourMask");
+    _tourMask.width($(document).width()).height($(document).height())
+    _tourMask.click(function () {
+      _this.closeCurtains();
+    });
+    _tourMask.appendTo($("body"));
+
+    $(document).on("keyup", function (e) {
+      var _code = (e.keyCode ? e.keyCode : e.which);
+      if ($("#jHueTourMask").is(":visible") && _code == 27) {
+        _this.performOperation("close");
+      }
+    });
+  };
+
+  Plugin.prototype.initQuestionMark = function () {
+    var _this = this;
+    $("#jHueTourQuestion").remove();
+    var _questionMark = $("<div>").attr("id", "jHueTourQuestion").html('<i class="icon-flag-checkered" style=""></i>').addClass("jHueTourBadge");
+    if (_this.options.questionMarkPlacement == "left"){
+      _questionMark.addClass("jHueTourBadgeLeft");
+    }
+    if ($.totalStorage("jHueTourExtras") != null) {
+      var _newTours = [];
+      $.each(_this.options.tours, function (cnt, tour) {
+        if (tour.remote == undefined || !tour.remote) {
+          _newTours.push(tour);
+        }
+      });
+      _this.options.tours = _newTours.concat($.totalStorage("jHueTourExtras"));
+    }
+
+    var _toursHtml = '<ul class="nav nav-pills nav-stacked">'
+      var _added = 0;
+      $.each(_this.options.tours, function (ctn, tour) {
+        if (tour.path === undefined || RegExp(tour.path).test(location.pathname)) {
+          var _tourDone = '';
+          var _removeTour = '';
+          if ($.totalStorage !== undefined) {
+            var _key = location.pathname;
+            if (tour.path !== undefined && tour.path != "") {
+              _key = tour.path;
+            }
+            _key += "_" + tour.name;
+            if ($.totalStorage("jHueTourHistory") != null && $.totalStorage("jHueTourHistory")[_key] == true) {
+              _tourDone = '<div style="color:green;float:right;margin:4px"><i class="icon-check-sign"></i></div>';
+            }
+          }
+          if (tour.remote) {
+            _removeTour = '<div style="color:red;float:right;margin:4px;cursor: pointer" onclick="javascript:$.jHueTour(\'remove_' + tour.name + '\')"><i class="icon-remove-sign"></i></div>';
+          }
+          _toursHtml += '<li>' + _removeTour + _tourDone + '<a href="javascript:$.jHueTour(\'' + tour.name + '\', 1)" style="padding-left:0">' + tour.desc + '</a></li>';
+          _added++;
+        }
+      });
+      if (_added == 0) {
+        if (_this.options.hideIfNoneAvailable){
+          _questionMark.css("display", "none");
+        }
+        else {
+          _toursHtml += '<li>' + _this.options.labels.NO_AVAILABLE_TOURS + '</li>';
+        }
+      }
+      if (_added > 0 && typeof $.totalStorage !== "undefined" && ($.totalStorage("jHueTourHideModal") == null || $.totalStorage("jHueTourHideModal") == false)) {
+        $(document).ready(function () {
+          $("#jHueTourModal").modal();
+          $.totalStorage("jHueTourHideModal", true);
+          $("#jHueTourModalChk").attr("checked", "checked");
+          $("#jHueTourModalChk").on("change", function () {
+            $.totalStorage("jHueTourHideModal", $(this).is(":checked"));
+          });
+        });
+      }
+      if (_this.options.showRemote){
+        _toursHtml += '<li>' +
+          ' <div class="input-append" style="margin-top: 10px">' +
+          '  <input id="jHueTourRemoteTutorial" style="width:70%" type="text" placeholder="URL">' +
+          '  <button id="jHueTourRemoteTutorialBtn" class="btn" type="button" onclick="javascript:$.jHueTour($(\'#jHueTourRemoteTutorial\').val())">' +
+          '  <i class="icon-cloud-download"></i></button>' +
+          ' </div>' +
+          '</li>';
+      }
+    _toursHtml += '</ul>';
+
+    _questionMark.click(function () {
+
+      var _closeBtn = $("<a>");
+      _closeBtn.addClass("btn").addClass("btn-mini").html('<i class="icon-remove"></i>').css("float", "right").css("margin-top", "-4px").css("margin-right", "-6px");
+      _closeBtn.click(function () {
+        $(".popover").remove();
+      });
+
+      _questionMark.popover("destroy").popover({
+        title: _this.options.labels.AVAILABLE_TOURS,
+        content: _toursHtml,
+        html: true,
+        trigger: "manual",
+        placement: _this.options.questionMarkPlacement == "left"?"right":"left"
+      }).popover("show");
+      if ($(".popover").position().top <= 0) {
+        $(".popover").css("top", "10px");
+      }
+      _closeBtn.prependTo($(".popover-title"));
+
+    });
+    _questionMark.appendTo($("body"));
+  };
+
+  Plugin.prototype.addTours = function (options) {
+    var _this = this;
+    var _addableTours = [];
+    if (options.tours != null) {
+      $.each(options.tours, function (cnt, tour) {
+        var _add = true;
+        if (_this.options.tours != null) {
+          $.each(_this.options.tours, function (icnt, itour) {
+            if (itour.name == tour.name) {
+              _add = false;
+            }
+          });
+        }
+        if (_add) {
+          _addableTours.push(tour);
+        }
+      });
+    }
+    _this.options.tours = _this.options.tours.concat(_addableTours);
+  };
+
+  Plugin.prototype.availableTours = function () {
+    return this.options.tours;
+  };
+
+  Plugin.prototype.performOperation = function (operation) {
+    var _this = this;
+    var _op = operation.toLowerCase();
+    if (_op.indexOf("http:") == 0) {
+      $("#jHueTourRemoteTutorial").attr("disabled", "disabled");
+      $("#jHueTourRemoteTutorialBtn").attr("disabled", "disabled");
+      $.ajax({
+        type: "GET",
+        url: operation + "?callback=?",
+        async: false,
+        jsonpCallback: "jHueRemoteTour",
+        contentType: "application/json",
+        dataType: "jsonp",
+        success: function (json) {
+          if ($.totalStorage !== undefined) {
+            if ($.totalStorage("jHueTourExtras") == null) {
+              $.totalStorage("jHueTourExtras", []);
+            }
+            var _newStoredArray = [];
+            if (json.tours != null) {
+              _newStoredArray = json.tours;
+              $.each($.totalStorage("jHueTourExtras"), function (cnt, tour) {
+                var _found = false;
+                $.each(json.tours, function (icnt, itour) {
+                  if (itour.name == tour.name) {
+                    _found = true;
+                  }
+                });
+                if (!_found) {
+                  _newStoredArray.push(tour);
+                }
+              });
+            }
+            $.totalStorage("jHueTourExtras", _newStoredArray);
+          }
+          $("#jHueTourQuestion").popover("destroy");
+          _this.initQuestionMark();
+          $("#jHueTourQuestion").click();
+        },
+        error: function (e) {
+          if ($.jHueNotify !== undefined) {
+            $.jHueNotify.error(e.message)
+          }
+          $("#jHueTourRemoteTutorial").removeAttr("disabled");
+          $("#jHueTourRemoteTutorialBtn").removeAttr("disabled");
+        }
+      });
+    }
+
+    if (_op.indexOf("remove_") == 0) {
+      var _tourName = _op.substr(7);
+      if ($.totalStorage !== undefined) {
+        var _newStoredArray = [];
+        $.each($.totalStorage("jHueTourExtras"), function (cnt, tour) {
+          if (tour.name != _tourName) {
+            _newStoredArray.push(tour);
+          }
+        });
+        $.totalStorage("jHueTourExtras", _newStoredArray);
+        $("#jHueTourQuestion").popover("destroy");
+        _this.initQuestionMark();
+        $("#jHueTourQuestion").click();
+      }
+    }
+
+    if (_op == "start" || _op == "show" || _op == "play") {
+      if (_this.options.tours.length > 0 && _this.currentTour.name == "") {
+        _this.currentTour.name = _this.options.tours[0].name;
+        _this.currentTour.path = _this.options.tours[0].path;
+        _this.currentTour.steps = _this.options.tours[0].steps;
+      }
+      this.showStep(1);
+    }
+
+    if (_op == "reset") {
+      if ($.totalStorage !== undefined) {
+        $.totalStorage("jHueTourHistory", null);
+        $.totalStorage("jHueTourExtras", null);
+      }
+    }
+
+    if (_op == "clear") {
+      _this.options.tours = [];
+    }
+
+    if (_op == "end" || _op == "hide" || _op == "close" || _op == "stop") {
+      _this.closeCurtains();
+    }
+  };
+
+  Plugin.prototype.closeCurtains = function () {
+    $(".popover").remove();
+    $(".jHueTourExposed").removeClass("jHueTourExposed");
+    $("#jHueTourMask").hide();
+  };
+
+  Plugin.prototype.showTour = function (tourName, stepNo) {
+    var _this = this;
+    if (_this.options.tours != null) {
+      $.each(_this.options.tours, function (cnt, tour) {
+        if (tour.name == tourName && (tour.path === undefined || RegExp(tour.path).test(location.pathname))) {
+          _this.currentTour.name = tour.name;
+          _this.currentTour.path = tour.path;
+          _this.currentTour.steps = tour.steps;
+          if (stepNo === undefined) {
+            _this.showStep(1);
+          }
+          else {
+            _this.showStep(stepNo);
+          }
+          return;
+        }
+      });
+    }
+  };
+
+  Plugin.prototype.showStep = function (stepNo) {
+    var _this = this;
+    if (_this.currentTour.steps[stepNo - 1] != null) {
+      var _step = _this.currentTour.steps[stepNo - 1];
+      _this.currentTour.shownStep = stepNo;
+      var _navigation = "";
+
+      if (_step.visitUrl != undefined) {
+        location.href = _step.visitUrl;
+      }
+
+      if (_step.onShown != undefined) {
+        window.setTimeout(_step.onShown, 10);
+      }
+
+      $(".popover").remove();
+      $(".jHueTourExposed").removeClass("jHueTourExposed");
+      if ($(".jHueTourExposed").css("position") == "relative") {
+        $(".jHueTourExposed").css("position", "relative");
+      }
+      $("#jHueTourMask").width($(document).width()).height($(document).height()).show();
+
+      var _closeBtn = $("<a>");
+      _closeBtn.addClass("btn").addClass("btn-mini").html('<i class="icon-remove"></i>').css("float", "right").css("margin-top", "-4px").css("margin-right", "-6px");
+      _closeBtn.click(function () {
+        _this.performOperation("close");
+      });
+
+      var _nextBtn = $("<a>");
+      _nextBtn.addClass("btn").addClass("btn-mini").html('<i class="icon-chevron-sign-right"></i>').css("margin-top", "10px");
+      _nextBtn.click(function () {
+        _this.showStep(_this.currentTour.shownStep + 1);
+      });
+
+      var _prevBtn = $("<a>");
+      _prevBtn.addClass("btn").addClass("btn-mini").html('<i class="icon-chevron-sign-left"></i>').css("margin-top", "10px").css("margin-right", "10px");
+      _prevBtn.click(function () {
+        _this.showStep(_this.currentTour.shownStep - 1);
+      });
+
+      var _arrowOn = _step.arrowOn;
+      var _additionalContent = "";
+      if ($(_arrowOn).length == 0 || !($(_arrowOn).is(":visible"))){
+        _arrowOn = "body";
+        _additionalContent = "<b>MISSING POINTER OF STEP "+ _this.currentTour.shownStep +"</b> ";
+      }
+      $(_arrowOn).popover('destroy').popover({
+        title: _step.title,
+        content: _additionalContent + _step.content + "<br/>",
+        html: true,
+        trigger: 'manual',
+        placement: (_step.placement != "" && _step.placement != undefined) ? _step.placement : "left"
+      }).popover('show');
+
+      if (_step.top != undefined) {
+        if ($.isNumeric(_step.top)) {
+          $(".popover").css("top", ($(".popover").position().top + _step.top) + "px");
+        }
+        else {
+          $(".popover").css("top", _step.top);
+        }
+      }
+
+      if (_step.left != undefined) {
+        if ($.isNumeric(_step.left)) {
+          $(".popover").css("left", ($(".popover").position().left + _step.left) + "px");
+        }
+        else {
+          $(".popover").css("left", _step.left);
+        }
+      }
+      $(".popover-title").html(_step.title);
+      _closeBtn.prependTo($(".popover-title"));
+
+      if (_this.currentTour.shownStep > 1) {
+        _prevBtn.appendTo($(".popover-content p"));
+      }
+      if (_this.currentTour.shownStep < _this.currentTour.steps.length && (_step.waitForAction == undefined || _step.waitForAction == false)) {
+        _nextBtn.appendTo($(".popover-content p"));
+      }
+
+      // last step, mark tour/tutorial as done
+      if ($.totalStorage !== undefined && _this.currentTour.shownStep == _this.currentTour.steps.length) {
+        var _key = location.pathname;
+        if (_this.currentTour.path !== undefined && _this.currentTour.path != "") {
+          _key = _this.currentTour.path;
+        }
+        _key += "_" + _this.currentTour.name;
+        var _history = $.totalStorage("jHueTourHistory");
+        if (_history == null) {
+          _history = {}
+        }
+        _history[_key] = true;
+        $.totalStorage("jHueTourHistory", _history);
+      }
+
+      var _exposedElement = $((_step.expose != undefined && _step.expose != "" ? _step.expose : _arrowOn));
+      if (_exposedElement.css("position") === undefined || _exposedElement.css("position") != "fixed") {
+        _exposedElement.css("position", "relative");
+      }
+      _exposedElement.addClass("jHueTourExposed");
+    }
+  };
+
+  $[pluginName] = function (options, stepNo) {
+    var _el = $("body");
+    if (!$("body").data('plugin_' + pluginName)) {
+      $("body").data('plugin_' + pluginName, new Plugin(_el, options));
+    }
+
+    if (options === undefined) {
+      return $("body").data('plugin_' + pluginName).availableTours();
+    }
+
+    if (typeof options == "string") {
+      if (stepNo === undefined) {
+        $("body").data('plugin_' + pluginName).performOperation(options);
+      }
+      else if ($.isNumeric(stepNo)) {
+        $("body").data('plugin_' + pluginName).showTour(options, stepNo);
+      }
+    }
+    else if ($.isNumeric(options)) {
+      $("body").data('plugin_' + pluginName).showStep(options);
+    }
+    else {
+      $("body").data('plugin_' + pluginName).addTours(options);
+    }
+  };
+
+})(jQuery, window, document);

+ 52 - 0
desktop/core/static/js/jquery.validation.js

@@ -0,0 +1,52 @@
+// 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.
+/*
+* jHue validation plugin
+*/
+;(function ($, window, document, undefined) {
+
+	var pluginName = "jHueValidation",
+	defaults = {
+	};
+
+	function Plugin(element, options) {
+		this.element = element;
+		this.options = $.extend({}, defaults, options) ;
+		this._defaults = defaults;
+		this._name = pluginName;
+		this.init();
+	}
+	
+	Plugin.prototype.setOptions = function(options) {
+		this.options = $.extend({}, defaults, options) ;
+	};
+
+	Plugin.prototype.init = function () {
+		var _this = this;		
+	};
+
+	$.fn[pluginName] = function (options) {
+		return this.each(function () {
+			if (!$.data(this, 'plugin_' + pluginName)) {
+				$.data(this, 'plugin_' + pluginName, new Plugin( this, options));
+			}
+			else {
+				$.data(this, 'plugin_' + pluginName).setOptions(options);
+			}
+		});
+	}
+
+})(jQuery, window, document);

部分文件因为文件数量过多而无法显示