You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zeppelin.apache.org by ku...@apache.org on 2017/07/14 16:07:06 UTC

[1/5] zeppelin git commit: [ZEPPELIN-2749] Use scalable file structure for zeppelin web

Repository: zeppelin
Updated Branches:
  refs/heads/master ebb6591d8 -> 6bd6c7088


http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/websocket/websocket-message.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/websocket/websocket-message.service.js b/zeppelin-web/src/components/websocket/websocket-message.service.js
new file mode 100644
index 0000000..9ca631b
--- /dev/null
+++ b/zeppelin-web/src/components/websocket/websocket-message.service.js
@@ -0,0 +1,343 @@
+/*
+ * Licensed 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.
+ */
+
+angular.module('zeppelinWebApp').service('websocketMsgSrv', WebsocketMessageService)
+
+function WebsocketMessageService ($rootScope, websocketEvents) {
+  'ngInject'
+
+  return {
+
+    getHomeNote: function () {
+      websocketEvents.sendNewEvent({op: 'GET_HOME_NOTE'})
+    },
+
+    createNotebook: function (noteName, defaultInterpreterId) {
+      websocketEvents.sendNewEvent({
+        op: 'NEW_NOTE',
+        data: {
+          name: noteName,
+          defaultInterpreterId: defaultInterpreterId
+        }
+      })
+    },
+
+    moveNoteToTrash: function (noteId) {
+      websocketEvents.sendNewEvent({op: 'MOVE_NOTE_TO_TRASH', data: {id: noteId}})
+    },
+
+    moveFolderToTrash: function (folderId) {
+      websocketEvents.sendNewEvent({op: 'MOVE_FOLDER_TO_TRASH', data: {id: folderId}})
+    },
+
+    restoreNote: function (noteId) {
+      websocketEvents.sendNewEvent({op: 'RESTORE_NOTE', data: {id: noteId}})
+    },
+
+    restoreFolder: function (folderId) {
+      websocketEvents.sendNewEvent({op: 'RESTORE_FOLDER', data: {id: folderId}})
+    },
+
+    restoreAll: function () {
+      websocketEvents.sendNewEvent({op: 'RESTORE_ALL'})
+    },
+
+    deleteNote: function (noteId) {
+      websocketEvents.sendNewEvent({op: 'DEL_NOTE', data: {id: noteId}})
+    },
+
+    removeFolder: function (folderId) {
+      websocketEvents.sendNewEvent({op: 'REMOVE_FOLDER', data: {id: folderId}})
+    },
+
+    emptyTrash: function () {
+      websocketEvents.sendNewEvent({op: 'EMPTY_TRASH'})
+    },
+
+    cloneNote: function (noteIdToClone, newNoteName) {
+      websocketEvents.sendNewEvent({op: 'CLONE_NOTE', data: {id: noteIdToClone, name: newNoteName}})
+    },
+
+    getNoteList: function () {
+      websocketEvents.sendNewEvent({op: 'LIST_NOTES'})
+    },
+
+    reloadAllNotesFromRepo: function () {
+      websocketEvents.sendNewEvent({op: 'RELOAD_NOTES_FROM_REPO'})
+    },
+
+    getNote: function (noteId) {
+      websocketEvents.sendNewEvent({op: 'GET_NOTE', data: {id: noteId}})
+    },
+
+    updateNote: function (noteId, noteName, noteConfig) {
+      websocketEvents.sendNewEvent({op: 'NOTE_UPDATE', data: {id: noteId, name: noteName, config: noteConfig}})
+    },
+
+    updatePersonalizedMode: function (noteId, modeValue) {
+      websocketEvents.sendNewEvent({op: 'UPDATE_PERSONALIZED_MODE', data: {id: noteId, personalized: modeValue}})
+    },
+
+    renameNote: function (noteId, noteName) {
+      websocketEvents.sendNewEvent({op: 'NOTE_RENAME', data: {id: noteId, name: noteName}})
+    },
+
+    renameFolder: function (folderId, folderName) {
+      websocketEvents.sendNewEvent({op: 'FOLDER_RENAME', data: {id: folderId, name: folderName}})
+    },
+
+    moveParagraph: function (paragraphId, newIndex) {
+      websocketEvents.sendNewEvent({op: 'MOVE_PARAGRAPH', data: {id: paragraphId, index: newIndex}})
+    },
+
+    insertParagraph: function (newIndex) {
+      websocketEvents.sendNewEvent({op: 'INSERT_PARAGRAPH', data: {index: newIndex}})
+    },
+
+    copyParagraph: function (newIndex, paragraphTitle, paragraphData,
+                            paragraphConfig, paragraphParams) {
+      websocketEvents.sendNewEvent({
+        op: 'COPY_PARAGRAPH',
+        data: {
+          index: newIndex,
+          title: paragraphTitle,
+          paragraph: paragraphData,
+          config: paragraphConfig,
+          params: paragraphParams
+        }
+      })
+    },
+
+    updateAngularObject: function (noteId, paragraphId, name, value, interpreterGroupId) {
+      websocketEvents.sendNewEvent({
+        op: 'ANGULAR_OBJECT_UPDATED',
+        data: {
+          noteId: noteId,
+          paragraphId: paragraphId,
+          name: name,
+          value: value,
+          interpreterGroupId: interpreterGroupId
+        }
+      })
+    },
+
+    clientBindAngularObject: function (noteId, name, value, paragraphId) {
+      websocketEvents.sendNewEvent({
+        op: 'ANGULAR_OBJECT_CLIENT_BIND',
+        data: {
+          noteId: noteId,
+          name: name,
+          value: value,
+          paragraphId: paragraphId
+        }
+      })
+    },
+
+    clientUnbindAngularObject: function (noteId, name, paragraphId) {
+      websocketEvents.sendNewEvent({
+        op: 'ANGULAR_OBJECT_CLIENT_UNBIND',
+        data: {
+          noteId: noteId,
+          name: name,
+          paragraphId: paragraphId
+        }
+      })
+    },
+
+    cancelParagraphRun: function (paragraphId) {
+      websocketEvents.sendNewEvent({op: 'CANCEL_PARAGRAPH', data: {id: paragraphId}})
+    },
+
+    paragraphExecutedBySpell: function (paragraphId, paragraphTitle,
+                                        paragraphText, paragraphResultsMsg,
+                                        paragraphStatus, paragraphErrorMessage,
+                                        paragraphConfig, paragraphParams,
+                                        paragraphDateStarted, paragraphDateFinished) {
+      websocketEvents.sendNewEvent({
+        op: 'PARAGRAPH_EXECUTED_BY_SPELL',
+        data: {
+          id: paragraphId,
+          title: paragraphTitle,
+          paragraph: paragraphText,
+          results: {
+            code: paragraphStatus,
+            msg: paragraphResultsMsg.map(dataWithType => {
+              let serializedData = dataWithType.data
+              return { type: dataWithType.type, data: serializedData, }
+            })
+          },
+          status: paragraphStatus,
+          errorMessage: paragraphErrorMessage,
+          config: paragraphConfig,
+          params: paragraphParams,
+          dateStarted: paragraphDateStarted,
+          dateFinished: paragraphDateFinished,
+        }
+      })
+    },
+
+    runParagraph: function (paragraphId, paragraphTitle, paragraphData, paragraphConfig, paragraphParams) {
+      websocketEvents.sendNewEvent({
+        op: 'RUN_PARAGRAPH',
+        data: {
+          id: paragraphId,
+          title: paragraphTitle,
+          paragraph: paragraphData,
+          config: paragraphConfig,
+          params: paragraphParams
+        }
+      })
+    },
+
+    runAllParagraphs: function (noteId, paragraphs) {
+      websocketEvents.sendNewEvent({
+        op: 'RUN_ALL_PARAGRAPHS',
+        data: {
+          noteId: noteId,
+          paragraphs: JSON.stringify(paragraphs)
+        }
+      })
+    },
+
+    removeParagraph: function (paragraphId) {
+      websocketEvents.sendNewEvent({op: 'PARAGRAPH_REMOVE', data: {id: paragraphId}})
+    },
+
+    clearParagraphOutput: function (paragraphId) {
+      websocketEvents.sendNewEvent({op: 'PARAGRAPH_CLEAR_OUTPUT', data: {id: paragraphId}})
+    },
+
+    clearAllParagraphOutput: function (noteId) {
+      websocketEvents.sendNewEvent({op: 'PARAGRAPH_CLEAR_ALL_OUTPUT', data: {id: noteId}})
+    },
+
+    completion: function (paragraphId, buf, cursor) {
+      websocketEvents.sendNewEvent({
+        op: 'COMPLETION',
+        data: {
+          id: paragraphId,
+          buf: buf,
+          cursor: cursor
+        }
+      })
+    },
+
+    commitParagraph: function (paragraphId, paragraphTitle, paragraphData, paragraphConfig, paragraphParams) {
+      websocketEvents.sendNewEvent({
+        op: 'COMMIT_PARAGRAPH',
+        data: {
+          id: paragraphId,
+          title: paragraphTitle,
+          paragraph: paragraphData,
+          config: paragraphConfig,
+          params: paragraphParams
+        }
+      })
+    },
+
+    importNote: function (note) {
+      websocketEvents.sendNewEvent({
+        op: 'IMPORT_NOTE',
+        data: {
+          note: note
+        }
+      })
+    },
+
+    checkpointNote: function (noteId, commitMessage) {
+      websocketEvents.sendNewEvent({
+        op: 'CHECKPOINT_NOTE',
+        data: {
+          noteId: noteId,
+          commitMessage: commitMessage
+        }
+      })
+    },
+
+    setNoteRevision: function (noteId, revisionId) {
+      websocketEvents.sendNewEvent({
+        op: 'SET_NOTE_REVISION',
+        data: {
+          noteId: noteId,
+          revisionId: revisionId
+        }
+      })
+    },
+
+    listRevisionHistory: function (noteId) {
+      websocketEvents.sendNewEvent({
+        op: 'LIST_REVISION_HISTORY',
+        data: {
+          noteId: noteId
+        }
+      })
+    },
+
+    getNoteByRevision: function (noteId, revisionId) {
+      websocketEvents.sendNewEvent({
+        op: 'NOTE_REVISION',
+        data: {
+          noteId: noteId,
+          revisionId: revisionId
+        }
+      })
+    },
+
+    getEditorSetting: function (paragraphId, replName) {
+      websocketEvents.sendNewEvent({
+        op: 'EDITOR_SETTING',
+        data: {
+          paragraphId: paragraphId,
+          magic: replName
+        }
+      })
+    },
+
+    isConnected: function () {
+      return websocketEvents.isConnected()
+    },
+
+    getNoteJobsList: function () {
+      websocketEvents.sendNewEvent({op: 'LIST_NOTE_JOBS'})
+    },
+
+    getUpdateNoteJobsList: function (lastUpdateServerUnixTime) {
+      websocketEvents.sendNewEvent(
+        {op: 'LIST_UPDATE_NOTE_JOBS', data: {lastUpdateUnixTime: lastUpdateServerUnixTime * 1}}
+      )
+    },
+
+    unsubscribeJobManager: function () {
+      websocketEvents.sendNewEvent({op: 'UNSUBSCRIBE_UPDATE_NOTE_JOBS'})
+    },
+
+    getInterpreterBindings: function (noteId) {
+      websocketEvents.sendNewEvent({op: 'GET_INTERPRETER_BINDINGS', data: {noteId: noteId}})
+    },
+
+    saveInterpreterBindings: function (noteId, selectedSettingIds) {
+      websocketEvents.sendNewEvent({op: 'SAVE_INTERPRETER_BINDINGS',
+        data: {noteId: noteId, selectedSettingIds: selectedSettingIds}})
+    },
+
+    listConfigurations: function () {
+      websocketEvents.sendNewEvent({op: 'LIST_CONFIGURATIONS'})
+    },
+
+    getInterpreterSettings: function () {
+      websocketEvents.sendNewEvent({op: 'GET_INTERPRETER_SETTINGS'})
+    },
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js b/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js
deleted file mode 100644
index 16b3f7a..0000000
--- a/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Licensed 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.
- */
-
-angular.module('zeppelinWebApp').factory('websocketEvents', websocketEvents)
-
-function websocketEvents ($rootScope, $websocket, $location, baseUrlSrv) {
-  'ngInject'
-
-  let websocketCalls = {}
-  let pingIntervalId
-
-  websocketCalls.ws = $websocket(baseUrlSrv.getWebsocketUrl())
-  websocketCalls.ws.reconnectIfNotNormalClose = true
-
-  websocketCalls.ws.onOpen(function () {
-    console.log('Websocket created')
-    $rootScope.$broadcast('setConnectedStatus', true)
-    pingIntervalId = setInterval(function () {
-      websocketCalls.sendNewEvent({op: 'PING'})
-    }, 10000)
-  })
-
-  websocketCalls.sendNewEvent = function (data) {
-    if ($rootScope.ticket !== undefined) {
-      data.principal = $rootScope.ticket.principal
-      data.ticket = $rootScope.ticket.ticket
-      data.roles = $rootScope.ticket.roles
-    } else {
-      data.principal = ''
-      data.ticket = ''
-      data.roles = ''
-    }
-    console.log('Send >> %o, %o, %o, %o, %o', data.op, data.principal, data.ticket, data.roles, data)
-    websocketCalls.ws.send(JSON.stringify(data))
-  }
-
-  websocketCalls.isConnected = function () {
-    return (websocketCalls.ws.socket.readyState === 1)
-  }
-
-  websocketCalls.ws.onMessage(function (event) {
-    let payload
-    if (event.data) {
-      payload = angular.fromJson(event.data)
-    }
-
-    console.log('Receive << %o, %o', payload.op, payload)
-
-    let op = payload.op
-    let data = payload.data
-    if (op === 'NOTE') {
-      $rootScope.$broadcast('setNoteContent', data.note)
-    } else if (op === 'NEW_NOTE') {
-      $location.path('/notebook/' + data.note.id)
-    } else if (op === 'NOTES_INFO') {
-      $rootScope.$broadcast('setNoteMenu', data.notes)
-    } else if (op === 'LIST_NOTE_JOBS') {
-      $rootScope.$broadcast('setNoteJobs', data.noteJobs)
-    } else if (op === 'LIST_UPDATE_NOTE_JOBS') {
-      $rootScope.$broadcast('setUpdateNoteJobs', data.noteRunningJobs)
-    } else if (op === 'AUTH_INFO') {
-      let btn = []
-      if ($rootScope.ticket.roles === '[]') {
-        btn = [{
-          label: 'Close',
-          action: function (dialog) {
-            dialog.close()
-          }
-        }]
-      } else {
-        btn = [{
-          label: 'Login',
-          action: function (dialog) {
-            dialog.close()
-            angular.element('#loginModal').modal({
-              show: 'true'
-            })
-          }
-        }, {
-          label: 'Cancel',
-          action: function (dialog) {
-            dialog.close()
-            // using $rootScope.apply to trigger angular digest cycle
-            // changing $location.path inside bootstrap modal wont trigger digest
-            $rootScope.$apply(function () {
-              $location.path('/')
-            })
-          }
-        }]
-      }
-
-      BootstrapDialog.show({
-        closable: false,
-        closeByBackdrop: false,
-        closeByKeyboard: false,
-        title: 'Insufficient privileges',
-        message: data.info.toString(),
-        buttons: btn
-      })
-    } else if (op === 'PARAGRAPH') {
-      $rootScope.$broadcast('updateParagraph', data)
-    } else if (op === 'RUN_PARAGRAPH_USING_SPELL') {
-      $rootScope.$broadcast('runParagraphUsingSpell', data)
-    } else if (op === 'PARAGRAPH_APPEND_OUTPUT') {
-      $rootScope.$broadcast('appendParagraphOutput', data)
-    } else if (op === 'PARAGRAPH_UPDATE_OUTPUT') {
-      $rootScope.$broadcast('updateParagraphOutput', data)
-    } else if (op === 'PROGRESS') {
-      $rootScope.$broadcast('updateProgress', data)
-    } else if (op === 'COMPLETION_LIST') {
-      $rootScope.$broadcast('completionList', data)
-    } else if (op === 'EDITOR_SETTING') {
-      $rootScope.$broadcast('editorSetting', data)
-    } else if (op === 'ANGULAR_OBJECT_UPDATE') {
-      $rootScope.$broadcast('angularObjectUpdate', data)
-    } else if (op === 'ANGULAR_OBJECT_REMOVE') {
-      $rootScope.$broadcast('angularObjectRemove', data)
-    } else if (op === 'APP_APPEND_OUTPUT') {
-      $rootScope.$broadcast('appendAppOutput', data)
-    } else if (op === 'APP_UPDATE_OUTPUT') {
-      $rootScope.$broadcast('updateAppOutput', data)
-    } else if (op === 'APP_LOAD') {
-      $rootScope.$broadcast('appLoad', data)
-    } else if (op === 'APP_STATUS_CHANGE') {
-      $rootScope.$broadcast('appStatusChange', data)
-    } else if (op === 'LIST_REVISION_HISTORY') {
-      $rootScope.$broadcast('listRevisionHistory', data)
-    } else if (op === 'NOTE_REVISION') {
-      $rootScope.$broadcast('noteRevision', data)
-    } else if (op === 'INTERPRETER_BINDINGS') {
-      $rootScope.$broadcast('interpreterBindings', data)
-    } else if (op === 'ERROR_INFO') {
-      BootstrapDialog.show({
-        closable: false,
-        closeByBackdrop: false,
-        closeByKeyboard: false,
-        title: 'Details',
-        message: data.info.toString(),
-        buttons: [{
-          // close all the dialogs when there are error on running all paragraphs
-          label: 'Close',
-          action: function () {
-            BootstrapDialog.closeAll()
-          }
-        }]
-      })
-    } else if (op === 'SESSION_LOGOUT') {
-      $rootScope.$broadcast('session_logout', data)
-    } else if (op === 'CONFIGURATIONS_INFO') {
-      $rootScope.$broadcast('configurationsInfo', data)
-    } else if (op === 'INTERPRETER_SETTINGS') {
-      $rootScope.$broadcast('interpreterSettings', data)
-    } else if (op === 'PARAGRAPH_ADDED') {
-      $rootScope.$broadcast('addParagraph', data.paragraph, data.index)
-    } else if (op === 'PARAGRAPH_REMOVED') {
-      $rootScope.$broadcast('removeParagraph', data.id)
-    } else if (op === 'PARAGRAPH_MOVED') {
-      $rootScope.$broadcast('moveParagraph', data.id, data.index)
-    } else if (op === 'NOTE_UPDATED') {
-      $rootScope.$broadcast('updateNote', data.name, data.config, data.info)
-    } else if (op === 'SET_NOTE_REVISION') {
-      $rootScope.$broadcast('setNoteRevisionResult', data)
-    } else if (op === 'PARAS_INFO') {
-      $rootScope.$broadcast('updateParaInfos', data)
-    } else {
-      console.error(`unknown websocket op: ${op}`)
-    }
-  })
-
-  websocketCalls.ws.onError(function (event) {
-    console.log('error message: ', event)
-    $rootScope.$broadcast('setConnectedStatus', false)
-  })
-
-  websocketCalls.ws.onClose(function (event) {
-    console.log('close message: ', event)
-    if (pingIntervalId !== undefined) {
-      clearInterval(pingIntervalId)
-      pingIntervalId = undefined
-    }
-    $rootScope.$broadcast('setConnectedStatus', false)
-  })
-
-  return websocketCalls
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js b/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js
deleted file mode 100644
index 3fa56f3..0000000
--- a/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js
+++ /dev/null
@@ -1,343 +0,0 @@
-/*
- * Licensed 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.
- */
-
-angular.module('zeppelinWebApp').service('websocketMsgSrv', websocketMsgSrv)
-
-function websocketMsgSrv ($rootScope, websocketEvents) {
-  'ngInject'
-
-  return {
-
-    getHomeNote: function () {
-      websocketEvents.sendNewEvent({op: 'GET_HOME_NOTE'})
-    },
-
-    createNotebook: function (noteName, defaultInterpreterId) {
-      websocketEvents.sendNewEvent({
-        op: 'NEW_NOTE',
-        data: {
-          name: noteName,
-          defaultInterpreterId: defaultInterpreterId
-        }
-      })
-    },
-
-    moveNoteToTrash: function (noteId) {
-      websocketEvents.sendNewEvent({op: 'MOVE_NOTE_TO_TRASH', data: {id: noteId}})
-    },
-
-    moveFolderToTrash: function (folderId) {
-      websocketEvents.sendNewEvent({op: 'MOVE_FOLDER_TO_TRASH', data: {id: folderId}})
-    },
-
-    restoreNote: function (noteId) {
-      websocketEvents.sendNewEvent({op: 'RESTORE_NOTE', data: {id: noteId}})
-    },
-
-    restoreFolder: function (folderId) {
-      websocketEvents.sendNewEvent({op: 'RESTORE_FOLDER', data: {id: folderId}})
-    },
-
-    restoreAll: function () {
-      websocketEvents.sendNewEvent({op: 'RESTORE_ALL'})
-    },
-
-    deleteNote: function (noteId) {
-      websocketEvents.sendNewEvent({op: 'DEL_NOTE', data: {id: noteId}})
-    },
-
-    removeFolder: function (folderId) {
-      websocketEvents.sendNewEvent({op: 'REMOVE_FOLDER', data: {id: folderId}})
-    },
-
-    emptyTrash: function () {
-      websocketEvents.sendNewEvent({op: 'EMPTY_TRASH'})
-    },
-
-    cloneNote: function (noteIdToClone, newNoteName) {
-      websocketEvents.sendNewEvent({op: 'CLONE_NOTE', data: {id: noteIdToClone, name: newNoteName}})
-    },
-
-    getNoteList: function () {
-      websocketEvents.sendNewEvent({op: 'LIST_NOTES'})
-    },
-
-    reloadAllNotesFromRepo: function () {
-      websocketEvents.sendNewEvent({op: 'RELOAD_NOTES_FROM_REPO'})
-    },
-
-    getNote: function (noteId) {
-      websocketEvents.sendNewEvent({op: 'GET_NOTE', data: {id: noteId}})
-    },
-
-    updateNote: function (noteId, noteName, noteConfig) {
-      websocketEvents.sendNewEvent({op: 'NOTE_UPDATE', data: {id: noteId, name: noteName, config: noteConfig}})
-    },
-
-    updatePersonalizedMode: function (noteId, modeValue) {
-      websocketEvents.sendNewEvent({op: 'UPDATE_PERSONALIZED_MODE', data: {id: noteId, personalized: modeValue}})
-    },
-
-    renameNote: function (noteId, noteName) {
-      websocketEvents.sendNewEvent({op: 'NOTE_RENAME', data: {id: noteId, name: noteName}})
-    },
-
-    renameFolder: function (folderId, folderName) {
-      websocketEvents.sendNewEvent({op: 'FOLDER_RENAME', data: {id: folderId, name: folderName}})
-    },
-
-    moveParagraph: function (paragraphId, newIndex) {
-      websocketEvents.sendNewEvent({op: 'MOVE_PARAGRAPH', data: {id: paragraphId, index: newIndex}})
-    },
-
-    insertParagraph: function (newIndex) {
-      websocketEvents.sendNewEvent({op: 'INSERT_PARAGRAPH', data: {index: newIndex}})
-    },
-
-    copyParagraph: function (newIndex, paragraphTitle, paragraphData,
-                            paragraphConfig, paragraphParams) {
-      websocketEvents.sendNewEvent({
-        op: 'COPY_PARAGRAPH',
-        data: {
-          index: newIndex,
-          title: paragraphTitle,
-          paragraph: paragraphData,
-          config: paragraphConfig,
-          params: paragraphParams
-        }
-      })
-    },
-
-    updateAngularObject: function (noteId, paragraphId, name, value, interpreterGroupId) {
-      websocketEvents.sendNewEvent({
-        op: 'ANGULAR_OBJECT_UPDATED',
-        data: {
-          noteId: noteId,
-          paragraphId: paragraphId,
-          name: name,
-          value: value,
-          interpreterGroupId: interpreterGroupId
-        }
-      })
-    },
-
-    clientBindAngularObject: function (noteId, name, value, paragraphId) {
-      websocketEvents.sendNewEvent({
-        op: 'ANGULAR_OBJECT_CLIENT_BIND',
-        data: {
-          noteId: noteId,
-          name: name,
-          value: value,
-          paragraphId: paragraphId
-        }
-      })
-    },
-
-    clientUnbindAngularObject: function (noteId, name, paragraphId) {
-      websocketEvents.sendNewEvent({
-        op: 'ANGULAR_OBJECT_CLIENT_UNBIND',
-        data: {
-          noteId: noteId,
-          name: name,
-          paragraphId: paragraphId
-        }
-      })
-    },
-
-    cancelParagraphRun: function (paragraphId) {
-      websocketEvents.sendNewEvent({op: 'CANCEL_PARAGRAPH', data: {id: paragraphId}})
-    },
-
-    paragraphExecutedBySpell: function (paragraphId, paragraphTitle,
-                                        paragraphText, paragraphResultsMsg,
-                                        paragraphStatus, paragraphErrorMessage,
-                                        paragraphConfig, paragraphParams,
-                                        paragraphDateStarted, paragraphDateFinished) {
-      websocketEvents.sendNewEvent({
-        op: 'PARAGRAPH_EXECUTED_BY_SPELL',
-        data: {
-          id: paragraphId,
-          title: paragraphTitle,
-          paragraph: paragraphText,
-          results: {
-            code: paragraphStatus,
-            msg: paragraphResultsMsg.map(dataWithType => {
-              let serializedData = dataWithType.data
-              return { type: dataWithType.type, data: serializedData, }
-            })
-          },
-          status: paragraphStatus,
-          errorMessage: paragraphErrorMessage,
-          config: paragraphConfig,
-          params: paragraphParams,
-          dateStarted: paragraphDateStarted,
-          dateFinished: paragraphDateFinished,
-        }
-      })
-    },
-
-    runParagraph: function (paragraphId, paragraphTitle, paragraphData, paragraphConfig, paragraphParams) {
-      websocketEvents.sendNewEvent({
-        op: 'RUN_PARAGRAPH',
-        data: {
-          id: paragraphId,
-          title: paragraphTitle,
-          paragraph: paragraphData,
-          config: paragraphConfig,
-          params: paragraphParams
-        }
-      })
-    },
-
-    runAllParagraphs: function (noteId, paragraphs) {
-      websocketEvents.sendNewEvent({
-        op: 'RUN_ALL_PARAGRAPHS',
-        data: {
-          noteId: noteId,
-          paragraphs: JSON.stringify(paragraphs)
-        }
-      })
-    },
-
-    removeParagraph: function (paragraphId) {
-      websocketEvents.sendNewEvent({op: 'PARAGRAPH_REMOVE', data: {id: paragraphId}})
-    },
-
-    clearParagraphOutput: function (paragraphId) {
-      websocketEvents.sendNewEvent({op: 'PARAGRAPH_CLEAR_OUTPUT', data: {id: paragraphId}})
-    },
-
-    clearAllParagraphOutput: function (noteId) {
-      websocketEvents.sendNewEvent({op: 'PARAGRAPH_CLEAR_ALL_OUTPUT', data: {id: noteId}})
-    },
-
-    completion: function (paragraphId, buf, cursor) {
-      websocketEvents.sendNewEvent({
-        op: 'COMPLETION',
-        data: {
-          id: paragraphId,
-          buf: buf,
-          cursor: cursor
-        }
-      })
-    },
-
-    commitParagraph: function (paragraphId, paragraphTitle, paragraphData, paragraphConfig, paragraphParams) {
-      websocketEvents.sendNewEvent({
-        op: 'COMMIT_PARAGRAPH',
-        data: {
-          id: paragraphId,
-          title: paragraphTitle,
-          paragraph: paragraphData,
-          config: paragraphConfig,
-          params: paragraphParams
-        }
-      })
-    },
-
-    importNote: function (note) {
-      websocketEvents.sendNewEvent({
-        op: 'IMPORT_NOTE',
-        data: {
-          note: note
-        }
-      })
-    },
-
-    checkpointNote: function (noteId, commitMessage) {
-      websocketEvents.sendNewEvent({
-        op: 'CHECKPOINT_NOTE',
-        data: {
-          noteId: noteId,
-          commitMessage: commitMessage
-        }
-      })
-    },
-
-    setNoteRevision: function (noteId, revisionId) {
-      websocketEvents.sendNewEvent({
-        op: 'SET_NOTE_REVISION',
-        data: {
-          noteId: noteId,
-          revisionId: revisionId
-        }
-      })
-    },
-
-    listRevisionHistory: function (noteId) {
-      websocketEvents.sendNewEvent({
-        op: 'LIST_REVISION_HISTORY',
-        data: {
-          noteId: noteId
-        }
-      })
-    },
-
-    getNoteByRevision: function (noteId, revisionId) {
-      websocketEvents.sendNewEvent({
-        op: 'NOTE_REVISION',
-        data: {
-          noteId: noteId,
-          revisionId: revisionId
-        }
-      })
-    },
-
-    getEditorSetting: function (paragraphId, replName) {
-      websocketEvents.sendNewEvent({
-        op: 'EDITOR_SETTING',
-        data: {
-          paragraphId: paragraphId,
-          magic: replName
-        }
-      })
-    },
-
-    isConnected: function () {
-      return websocketEvents.isConnected()
-    },
-
-    getNoteJobsList: function () {
-      websocketEvents.sendNewEvent({op: 'LIST_NOTE_JOBS'})
-    },
-
-    getUpdateNoteJobsList: function (lastUpdateServerUnixTime) {
-      websocketEvents.sendNewEvent(
-        {op: 'LIST_UPDATE_NOTE_JOBS', data: {lastUpdateUnixTime: lastUpdateServerUnixTime * 1}}
-      )
-    },
-
-    unsubscribeJobManager: function () {
-      websocketEvents.sendNewEvent({op: 'UNSUBSCRIBE_UPDATE_NOTE_JOBS'})
-    },
-
-    getInterpreterBindings: function (noteId) {
-      websocketEvents.sendNewEvent({op: 'GET_INTERPRETER_BINDINGS', data: {noteId: noteId}})
-    },
-
-    saveInterpreterBindings: function (noteId, selectedSettingIds) {
-      websocketEvents.sendNewEvent({op: 'SAVE_INTERPRETER_BINDINGS',
-        data: {noteId: noteId, selectedSettingIds: selectedSettingIds}})
-    },
-
-    listConfigurations: function () {
-      websocketEvents.sendNewEvent({op: 'LIST_CONFIGURATIONS'})
-    },
-
-    getInterpreterSettings: function () {
-      websocketEvents.sendNewEvent({op: 'GET_INTERPRETER_SETTINGS'})
-    },
-
-  }
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/index.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/index.html b/zeppelin-web/src/index.html
index 547fd3a..90631eb 100644
--- a/zeppelin-web/src/index.html
+++ b/zeppelin-web/src/index.html
@@ -62,7 +62,6 @@ limitations under the License.
     <link rel="stylesheet" href="app/helium/helium.css" />
     <link rel="stylesheet" href="app/credential/credential.css" />
     <link rel="stylesheet" href="app/configuration/configuration.css" />
-    <link rel="stylesheet" href="components/expandCollapse/expandCollapse.css" />
     <link rel="stylesheet" href="fonts/font-awesome.min.css" />
     <link rel="stylesheet" href="fonts/simple-line-icons.css" />
     <link rel="stylesheet" href="fonts/custom-font.css" />
@@ -81,19 +80,19 @@ limitations under the License.
     <div id="main" class="container">
       <div ng-view></div>
     </div>
-    <!-- Modal ::  Keyboard shortcuts -->
-    <div ng-include src="'components/modal-shortcut/modal-shortcut.html'"></div>
-    <div ng-controller="NotenameCtrl as notenamectrl">
-      <div id="note-modal-container" ng-include src="'components/noteName-create/note-name-dialog.html'"></div>
+    <!-- global :: dialogs -->
+    <div ng-include src="'app/notebook/shortcut.html'"></div>
+    <div ng-controller="NoteCreateCtrl as noteCreateCtrl">
+      <div id="note-modal-container" ng-include src="'components/note-create/note-create.html'"></div>
     </div>
-    <div ng-controller="NoteImportCtrl as noteimportctrl">
-      <div id="note-import-container" ng-include src="'components/noteName-import/note-import-dialog.html'"></div>
+    <div ng-controller="NoteImportCtrl as noteImportCtrl">
+      <div id="note-import-container" ng-include src="'components/note-import/note-import.html'"></div>
     </div>
-    <div ng-controller="LoginCtrl as noteimportctrl">
+    <div ng-controller="LoginCtrl">
       <div id="login-container" ng-include src="'components/login/login.html'"></div>
     </div>
-    <div ng-controller="RenameCtrl">
-      <div ng-include src="'components/rename/rename.html'"></div>
+    <div ng-controller="NoteRenameCtrl">
+      <div ng-include src="'components/note-rename/note-rename.html'"></div>
     </div>
     <!-- build:js(.) scripts/oldieshim.js -->
     <!--[if lt IE 9]>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/index.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/index.js b/zeppelin-web/src/index.js
index de77686..75faca0 100644
--- a/zeppelin-web/src/index.js
+++ b/zeppelin-web/src/index.js
@@ -40,38 +40,38 @@ import './app/jobmanager/jobs/job.controller.js'
 import './app/jobmanager/jobmanager.filter.js'
 import './app/interpreter/interpreter.controller.js'
 import './app/interpreter/interpreter.filter.js'
+import './app/interpreter/interpreter-item.directive.js'
+import './app/interpreter/widget/number-widget.directive.js'
 import './app/credential/credential.controller.js'
 import './app/configuration/configuration.controller.js'
 import './app/notebook/paragraph/paragraph.controller.js'
+import './app/notebook/paragraph/clipboard.controller.js'
+import './app/notebook/paragraph/resizable.directive.js'
 import './app/notebook/paragraph/result/result.controller.js'
+import './app/notebook/paragraph/code-editor/code-editor.directive.js'
+import './app/notebook/save-as/save-as.service.js'
+import './app/notebook/save-as/browser-detect.service.js'
+import './app/notebook/elastic-input/elastic-input.controller.js'
+import './app/notebook/dropdown-input/dropdown-input.directive.js'
+import './app/notebook/note-var-share.service.js'
+import './app/notebook-repository/notebook-repository.controller.js'
 import './app/search/result-list.controller.js'
-import './app/notebookRepos/notebookRepos.controller.js'
+import './app/search/search.service.js'
 import './app/helium'
-import './components/arrayOrderingSrv/arrayOrdering.service.js'
-import './components/clipboard/clipboard.controller.js'
+import './app/helium/helium.service.js'
+import './components/array-ordering/array-ordering.service.js'
 import './components/navbar/navbar.controller.js'
-import './components/ngescape/ngescape.directive.js'
-import './components/interpreter/interpreter.directive.js'
-import './components/interpreter/widget/widget.number.directive.js'
-import './components/expandCollapse/expandCollapse.directive.js'
-import './components/noteName-create/notename.controller.js'
-import './components/noteName-import/notenameImport.controller.js'
-import './components/editor/codeEditor.directive.js'
-import './components/ngenter/ngenter.directive.js'
-import './components/dropdowninput/dropdowninput.directive.js'
-import './components/resizable/resizable.directive.js'
-import './components/noteName-create/visible.directive.js'
-import './components/websocketEvents/websocketMsg.service.js'
-import './components/websocketEvents/websocketEvents.factory.js'
-import './components/noteListDataFactory/noteList.datafactory.js'
-import './components/baseUrl/baseUrl.service.js'
-import './components/browser-detect/browserDetect.service.js'
-import './components/saveAs/saveAs.service.js'
-import './components/searchService/search.service.js'
+import './components/navbar/expand-collapse/expand-collapse.directive.js'
+import './components/note-create/note-create.controller.js'
+import './components/note-create/visible.directive.js'
+import './components/note-import/note-import.controller.js'
+import './components/ng-enter/ng-enter.directive.js'
+import './components/ng-escape/ng-escape.directive.js'
+import './components/websocket/websocket-message.service.js'
+import './components/websocket/websocket-event.factory.js'
+import './components/note-list/note-list.factory.js'
+import './components/base-url/base-url.service.js'
 import './components/login/login.controller.js'
-import './components/elasticInputCtrl/elasticInput.controller.js'
-import './components/noteAction/noteAction.service.js'
-import './components/notevarshareService/notevarshare.service.js'
-import './components/rename/rename.controller.js'
-import './components/rename/rename.service.js'
-import './components/helium/helium.service.js'
+import './components/note-action/note-action.service.js'
+import './components/note-rename/note-rename.controller.js'
+import './components/note-rename/note-rename.service.js'


[3/5] zeppelin git commit: [ZEPPELIN-2749] Use scalable file structure for zeppelin web

Posted by ku...@apache.org.
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/elasticInputCtrl/elasticInput.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/elasticInputCtrl/elasticInput.controller.js b/zeppelin-web/src/components/elasticInputCtrl/elasticInput.controller.js
deleted file mode 100644
index 507b2f6..0000000
--- a/zeppelin-web/src/components/elasticInputCtrl/elasticInput.controller.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Licensed 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.
- */
-
-angular.module('zeppelinWebApp').controller('ElasticInputCtrl', ElasticInputCtrl)
-
-function ElasticInputCtrl () {
-  let vm = this
-  vm.showEditor = false
-  vm.value = ''
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/expandCollapse/expandCollapse.css
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/expandCollapse/expandCollapse.css b/zeppelin-web/src/components/expandCollapse/expandCollapse.css
deleted file mode 100644
index b1a60d8..0000000
--- a/zeppelin-web/src/components/expandCollapse/expandCollapse.css
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Licensed 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.
- */
-
- .expandable {
-  display: none;
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/expandCollapse/expandCollapse.directive.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/expandCollapse/expandCollapse.directive.js b/zeppelin-web/src/components/expandCollapse/expandCollapse.directive.js
deleted file mode 100644
index c71fae0..0000000
--- a/zeppelin-web/src/components/expandCollapse/expandCollapse.directive.js
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Licensed 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.
- */
-
-angular.module('zeppelinWebApp').directive('expandCollapse', expandCollapse)
-
-function expandCollapse () {
-  return {
-    restrict: 'EA',
-    link: function (scope, element, attrs) {
-      angular.element(element).click(function (event) {
-        if (angular.element(element).find('.expandable:visible').length > 1) {
-          angular.element(element).find('.expandable:visible').slideUp('slow')
-          angular.element(element).find('i.fa-folder-open').toggleClass('fa-folder fa-folder-open')
-        } else {
-          angular.element(element).find('.expandable').first().slideToggle('200', function () {
-            // do not toggle trash folder
-            if (angular.element(element).find('.fa-trash-o').length === 0) {
-              angular.element(element).find('i').first().toggleClass('fa-folder fa-folder-open')
-            }
-          })
-        }
-
-        let target = event.target
-
-        // add note
-        if (target.classList !== undefined && target.classList.contains('fa-plus') &&
-          target.tagName.toLowerCase() === 'i') {
-          return
-        }
-
-        event.stopPropagation()
-      })
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/filterNoteNames/filter-note-names.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/filterNoteNames/filter-note-names.html b/zeppelin-web/src/components/filterNoteNames/filter-note-names.html
deleted file mode 100644
index 071cba4..0000000
--- a/zeppelin-web/src/components/filterNoteNames/filter-note-names.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<!--
-Licensed 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.
--->
-<input type="text"
-       class="note-name-query form-control"
-       ng-click="$event.stopPropagation()"
-       placeholder="&#xf002 Filter"
-       ng-model="$parent.query.q"
-       ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 300, 'blur': 0 } }" />

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/helium/helium-conf.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/helium/helium-conf.js b/zeppelin-web/src/components/helium/helium-conf.js
deleted file mode 100644
index 10ca18a..0000000
--- a/zeppelin-web/src/components/helium/helium-conf.js
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Licensed 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.
- */
-
-export const HeliumConfFieldType = {
-  NUMBER: 'number',
-  JSON: 'json',
-  STRING: 'string',
-}
-
-/**
- * @param persisted <Object> including `type`, `description`, `defaultValue` for each conf key
- * @param spec <Object> including `value` for each conf key
- */
-export function mergePersistedConfWithSpec (persisted, spec) {
-  const confs = []
-
-  for (let name in spec) {
-    const specField = spec[name]
-    const persistedValue = persisted[name]
-
-    const value = (persistedValue) ? persistedValue : specField.defaultValue
-    const merged = {
-      name: name,
-      type: specField.type,
-      description: specField.description,
-      value: value,
-      defaultValue: specField.defaultValue,
-    }
-
-    confs.push(merged)
-  }
-
-  return confs
-}
-
-export function createAllPackageConfigs (defaultPackages, persistedConfs) {
-  let packageConfs = {}
-
-  for (let name in defaultPackages) {
-    const pkgSearchResult = defaultPackages[name]
-
-    const spec = pkgSearchResult.pkg.config
-    if (!spec) { continue }
-
-    const artifact = pkgSearchResult.pkg.artifact
-    if (!artifact) { continue }
-
-    let persistedConf = {}
-    if (persistedConfs[artifact]) {
-      persistedConf = persistedConfs[artifact]
-    }
-
-    const confs = mergePersistedConfWithSpec(persistedConf, spec)
-    packageConfs[name] = confs
-  }
-
-  return packageConfs
-}
-
-export function parseConfigValue (type, stringified) {
-  let value = stringified
-
-  try {
-    if (HeliumConfFieldType.NUMBER === type) {
-      value = parseFloat(stringified)
-    } else if (HeliumConfFieldType.JSON === type) {
-      value = JSON.parse(stringified)
-    }
-  } catch (error) {
-    // return just the stringified one
-    console.error(`Failed to parse conf type ${type}, value ${value}`)
-  }
-
-  return value
-}
-
-/**
- * persist key-value only
- * since other info (e.g type, desc) can be provided by default config
- */
-export function createPersistableConfig (currentConfs) {
-  const filtered = currentConfs.reduce((acc, c) => {
-    acc[c.name] = parseConfigValue(c.type, c.value)
-    return acc
-  }, {})
-
-  return filtered
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/helium/helium-package.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/helium/helium-package.js b/zeppelin-web/src/components/helium/helium-package.js
deleted file mode 100644
index 88d191a..0000000
--- a/zeppelin-web/src/components/helium/helium-package.js
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Licensed 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.
- */
-
-export function createDefaultPackage (pkgSearchResult, sce) {
-  for (let pkgIdx in pkgSearchResult) {
-    const pkg = pkgSearchResult[pkgIdx]
-    pkg.pkg.icon = sce.trustAsHtml(pkg.pkg.icon)
-    if (pkg.enabled) {
-      pkgSearchResult.splice(pkgIdx, 1)
-      return pkg
-    }
-  }
-
-  // show first available version if package is not enabled
-  const result = pkgSearchResult[0]
-  pkgSearchResult.splice(0, 1)
-  return result
-}
-
-/**
- * create default packages based on `enabled` field and `latest` version.
- *
- * @param pkgSearchResults
- * @param sce angular `$sce` object
- * @returns {Object} including {name, pkgInfo}
- */
-export function createDefaultPackages (pkgSearchResults, sce) {
-  const defaultPackages = {}
-  // show enabled version if any version of package is enabled
-  for (let name in pkgSearchResults) {
-    const pkgSearchResult = pkgSearchResults[name]
-    defaultPackages[name] = createDefaultPackage(pkgSearchResult, sce)
-  }
-
-  return defaultPackages
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/helium/helium-type.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/helium/helium-type.js b/zeppelin-web/src/components/helium/helium-type.js
deleted file mode 100644
index 27b34fa..0000000
--- a/zeppelin-web/src/components/helium/helium-type.js
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Licensed 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.
- */
-
-export const HeliumType = {
-  VISUALIZATION: 'VISUALIZATION',
-  SPELL: 'SPELL',
-  INTERPRETER: 'INTERPRETER',
-  APPLICATION: 'APPLICATION',
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/helium/helium.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/helium/helium.service.js b/zeppelin-web/src/components/helium/helium.service.js
deleted file mode 100644
index 46fcc85..0000000
--- a/zeppelin-web/src/components/helium/helium.service.js
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Licensed 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.
- */
-
-import { HeliumType, } from './helium-type'
-import {
-  createAllPackageConfigs,
-  createPersistableConfig,
-  mergePersistedConfWithSpec,
-} from './helium-conf'
-import {
-  createDefaultPackages,
-} from './helium-package'
-
-angular.module('zeppelinWebApp').service('heliumService', heliumService)
-
-export default function heliumService ($http, $sce, baseUrlSrv) {
-  'ngInject'
-
-  let visualizationBundles = []
-  let visualizationPackageOrder = []
-  // name `heliumBundles` should be same as `HeliumBundleFactory.HELIUM_BUNDLES_VAR`
-  let heliumBundles = []
-  // map for `{ magic: interpreter }`
-  let spellPerMagic = {}
-  // map for `{ magic: package-name }`
-  let pkgNamePerMagic = {}
-
-  /**
-   * @param magic {string} e.g `%flowchart`
-   * @returns {SpellBase} undefined if magic is not registered
-   */
-  this.getSpellByMagic = function (magic) {
-    return spellPerMagic[magic]
-  }
-
-  this.executeSpell = function (magic, textWithoutMagic) {
-    const promisedConf = this.getSinglePackageConfigUsingMagic(magic)
-      .then(confs => createPersistableConfig(confs))
-
-    return promisedConf.then(conf => {
-      const spell = this.getSpellByMagic(magic)
-      const spellResult = spell.interpret(textWithoutMagic, conf)
-      const parsed = spellResult.getAllParsedDataWithTypes(
-        spellPerMagic, magic, textWithoutMagic)
-
-      return parsed
-    })
-  }
-
-  this.executeSpellAsDisplaySystem = function (magic, textWithoutMagic) {
-    const promisedConf = this.getSinglePackageConfigUsingMagic(magic)
-      .then(confs => createPersistableConfig(confs))
-
-    return promisedConf.then(conf => {
-      const spell = this.getSpellByMagic(magic)
-      const spellResult = spell.interpret(textWithoutMagic.trim(), conf)
-      const parsed = spellResult.getAllParsedDataWithTypes(spellPerMagic)
-
-      return parsed
-    })
-  }
-
-  this.getVisualizationCachedPackages = function () {
-    return visualizationBundles
-  }
-
-  this.getVisualizationCachedPackageOrder = function () {
-    return visualizationPackageOrder
-  }
-
-  /**
-   * @returns {Promise} which returns bundleOrder and cache it in `visualizationPackageOrder`
-   */
-  this.getVisualizationPackageOrder = function () {
-    return $http.get(baseUrlSrv.getRestApiBase() + '/helium/order/visualization')
-      .then(function (response, status) {
-        const order = response.data.body
-        visualizationPackageOrder = order
-        return order
-      })
-      .catch(function (error) {
-        console.error('Can not get bundle order', error)
-      })
-  }
-
-  this.setVisualizationPackageOrder = function (list) {
-    return $http.post(baseUrlSrv.getRestApiBase() + '/helium/order/visualization', list)
-  }
-
-  this.enable = function (name, artifact) {
-    return $http.post(baseUrlSrv.getRestApiBase() + '/helium/enable/' + name, artifact)
-  }
-
-  this.disable = function (name) {
-    return $http.post(baseUrlSrv.getRestApiBase() + '/helium/disable/' + name)
-  }
-
-  this.saveConfig = function (pkg, defaultPackageConfig, closeConfigPanelCallback) {
-    // in case of local package, it will include `/`
-    const pkgArtifact = encodeURIComponent(pkg.artifact)
-    const pkgName = pkg.name
-    const filtered = createPersistableConfig(defaultPackageConfig)
-
-    if (!pkgName || !pkgArtifact || !filtered) {
-      console.error(
-        `Can't save config for helium package '${pkgArtifact}'`, filtered)
-      return
-    }
-
-    const url = `${baseUrlSrv.getRestApiBase()}/helium/config/${pkgName}/${pkgArtifact}`
-    return $http.post(url, filtered)
-      .then(() => {
-        if (closeConfigPanelCallback) { closeConfigPanelCallback() }
-      }).catch((error) => {
-        console.error(`Failed to save config for ${pkgArtifact}`, error)
-      })
-  }
-
-  /**
-   * @returns {Promise<Object>} which including {name, Array<package info for artifact>}
-   */
-  this.getAllPackageInfo = function () {
-    return $http.get(`${baseUrlSrv.getRestApiBase()}/helium/package`)
-      .then(function (response, status) {
-        return response.data.body
-      })
-      .catch(function (error) {
-        console.error('Failed to get all package infos', error)
-      })
-  }
-
-  this.getAllEnabledPackages = function () {
-    return $http.get(`${baseUrlSrv.getRestApiBase()}/helium/enabledPackage`)
-      .then(function (response, status) {
-        return response.data.body
-      })
-      .catch(function (error) {
-        console.error('Failed to get all enabled package infos', error)
-      })
-  }
-
-  this.getSingleBundle = function (pkgName) {
-    let url = `${baseUrlSrv.getRestApiBase()}/helium/bundle/load/${pkgName}`
-    if (process.env.HELIUM_BUNDLE_DEV) {
-      url = url + '?refresh=true'
-    }
-
-    return $http.get(url)
-      .then(function (response, status) {
-        const bundle = response.data
-        if (bundle.substring(0, 'ERROR:'.length) === 'ERROR:') {
-          console.error(`Failed to get bundle: ${pkgName}`, bundle)
-          return '' // empty bundle will be filtered later
-        }
-
-        return bundle
-      })
-      .catch(function (error) {
-        console.error(`Failed to get single bundle: ${pkgName}`, error)
-      })
-  }
-
-  this.getDefaultPackages = function () {
-    return this.getAllPackageInfo()
-      .then(pkgSearchResults => {
-        return createDefaultPackages(pkgSearchResults, $sce)
-      })
-  }
-
-  this.getAllPackageInfoAndDefaultPackages = function () {
-    return this.getAllPackageInfo()
-      .then(pkgSearchResults => {
-        return {
-          pkgSearchResults: pkgSearchResults,
-          defaultPackages: createDefaultPackages(pkgSearchResults, $sce),
-        }
-      })
-  }
-
-  /**
-   * get all package configs.
-   * @return { Promise<{name, Array<Object>}> }
-   */
-  this.getAllPackageConfigs = function () {
-    const promisedDefaultPackages = this.getDefaultPackages()
-    const promisedPersistedConfs =
-      $http.get(`${baseUrlSrv.getRestApiBase()}/helium/config`)
-      .then(function (response, status) {
-        return response.data.body
-      })
-
-    return Promise.all([promisedDefaultPackages, promisedPersistedConfs])
-      .then(values => {
-        const defaultPackages = values[0]
-        const persistedConfs = values[1]
-
-        return createAllPackageConfigs(defaultPackages, persistedConfs)
-      })
-      .catch(function (error) {
-        console.error('Failed to get all package configs', error)
-      })
-  }
-
-  /**
-   * get the package config which is persisted in server.
-   * @return { Promise<Array<Object>> }
-   */
-  this.getSinglePackageConfigs = function (pkg) {
-    const pkgName = pkg.name
-    // in case of local package, it will include `/`
-    const pkgArtifact = encodeURIComponent(pkg.artifact)
-
-    if (!pkgName || !pkgArtifact) {
-      console.error('Failed to fetch config for\n', pkg)
-      return Promise.resolve([])
-    }
-
-    const confUrl = `${baseUrlSrv.getRestApiBase()}/helium/config/${pkgName}/${pkgArtifact}`
-    const promisedConf = $http.get(confUrl)
-      .then(function (response, status) {
-        return response.data.body
-      })
-
-    return promisedConf.then(({confSpec, confPersisted}) => {
-      const merged = mergePersistedConfWithSpec(confPersisted, confSpec)
-      return merged
-    })
-  }
-
-  this.getSinglePackageConfigUsingMagic = function (magic) {
-    const pkgName = pkgNamePerMagic[magic]
-
-    const confUrl = `${baseUrlSrv.getRestApiBase()}/helium/spell/config/${pkgName}`
-    const promisedConf = $http.get(confUrl)
-      .then(function (response, status) {
-        return response.data.body
-      })
-
-    return promisedConf.then(({confSpec, confPersisted}) => {
-      const merged = mergePersistedConfWithSpec(confPersisted, confSpec)
-      return merged
-    })
-  }
-
-  const p = this.getAllEnabledPackages()
-    .then(enabledPackageSearchResults => {
-      const promises = enabledPackageSearchResults.map(packageSearchResult => {
-        const pkgName = packageSearchResult.pkg.name
-        return this.getSingleBundle(pkgName)
-      })
-
-      return Promise.all(promises)
-    })
-    .then(bundles => {
-      return bundles.reduce((acc, b) => {
-        // filter out empty bundle
-        if (b === '') { return acc }
-        acc.push(b)
-        return acc
-      }, [])
-    })
-
-  // load should be promise
-  this.load = p.then(availableBundles => {
-    // evaluate bundles
-    availableBundles.map(b => {
-      // eslint-disable-next-line no-eval
-      eval(b)
-    })
-
-    // extract bundles by type
-    heliumBundles.map(b => {
-      if (b.type === HeliumType.SPELL) {
-        const spell = new b.class() // eslint-disable-line new-cap
-        const pkgName = b.id
-        spellPerMagic[spell.getMagic()] = spell
-        pkgNamePerMagic[spell.getMagic()] = pkgName
-      } else if (b.type === HeliumType.VISUALIZATION) {
-        visualizationBundles.push(b)
-      }
-    })
-  })
-
-  this.init = function() {
-    this.getVisualizationPackageOrder()
-  }
-
-  // init
-  this.init()
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/interpreter/interpreter.directive.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/interpreter/interpreter.directive.js b/zeppelin-web/src/components/interpreter/interpreter.directive.js
deleted file mode 100644
index 7080760..0000000
--- a/zeppelin-web/src/components/interpreter/interpreter.directive.js
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Licensed 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.
- */
-
-angular.module('zeppelinWebApp').directive('interpreterDirective', interpreterDirective)
-
-function interpreterDirective ($timeout) {
-  'ngInject'
-
-  return {
-    restrict: 'A',
-    link: function (scope, element, attr) {
-      if (scope.$last === true) {
-        $timeout(function () {
-          let id = 'ngRenderFinished'
-          scope.$emit(id)
-        })
-      }
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/interpreter/widget/widget.number.directive.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/interpreter/widget/widget.number.directive.js b/zeppelin-web/src/components/interpreter/widget/widget.number.directive.js
deleted file mode 100644
index 5d66cd1..0000000
--- a/zeppelin-web/src/components/interpreter/widget/widget.number.directive.js
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Licensed 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.
- */
-
-angular.module('zeppelinWebApp').directive('widgetNumber', numericOnly)
-
-function numericOnly() {
-  return {
-    require: 'ngModel',
-    link: function (scope, element, attrs, modelCtrl) {
-      modelCtrl.$parsers.push(function (inputValue) {
-        let transformedInput = inputValue ? inputValue.replace(/[^\d.-]/g, '') : null
-        if (transformedInput !== inputValue) {
-          modelCtrl.$setViewValue(transformedInput)
-          modelCtrl.$render()
-        }
-        return transformedInput
-      })
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/login/login.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/login/login.html b/zeppelin-web/src/components/login/login.html
index cd3b604..d4a94a6 100644
--- a/zeppelin-web/src/components/login/login.html
+++ b/zeppelin-web/src/components/login/login.html
@@ -17,7 +17,7 @@ limitations under the License.
     <div class="modal-dialog">
 
       <!-- Modal content-->
-      <div class="modal-content" id="NoteImportCtrl" ng-init="NoteImportInit">
+      <div id="loginModalContent" class="modal-content">
         <div class="modal-header">
           <button type="button" class="close" data-dismiss="modal">&times;</button>
           <h4 class="modal-title">Login</h4>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/modal-shortcut/modal-shortcut.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/modal-shortcut/modal-shortcut.html b/zeppelin-web/src/components/modal-shortcut/modal-shortcut.html
deleted file mode 100644
index 775da4f..0000000
--- a/zeppelin-web/src/components/modal-shortcut/modal-shortcut.html
+++ /dev/null
@@ -1,312 +0,0 @@
-<!--
-Licensed 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.
--->
-
-<div class="modal fade" id="shortcutModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
-  <div class="modal-dialog">
-    <div class="modal-content">
-      <div class="shortcut-modal-header">
-        <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
-        <h4 class="shortcut-modal-title" id="myModalLabel">Keyboard shortcuts</h4>
-      </div>
-      <div class="table-scroll">
-        <table class="table table-shortcut">
-          <tr>
-            <th style="width:70%">Note Keyboard Shortcuts</th>
-            <th></th>
-          </tr>
-
-          <tr>
-            <td>
-              <div class="col-md-8">Run paragraph</div>
-           </td>
-           <td>
-              <div class="keys">
-                <kbd class="kbd-default">Shift</kbd> + <kbd class="kbd-default">Enter</kbd>
-              </div>
-            </td>
-          </tr>
-
-          <tr>
-            <td>
-              <div class="col-md-8">Cancel</div>
-            </td>
-            <td>
-              <div class="keys">
-                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt'}}</kbd> + <kbd class="kbd-default">C</kbd>
-              </div>
-            </td>
-          </tr>
-
-          <tr>
-            <td>
-              <div class="col-md-8">Move cursor Up</div>
-            </td>
-            <td>
-              <div class="keys">
-                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">P</kbd>
-              </div>
-            </td>
-          </tr>
-
-          <tr>
-            <td>
-              <div class="col-md-8">Move cursor Down</div>
-            </td>
-            <td>
-              <div class="keys">
-                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">N</kbd>
-              </div>
-            </td>
-          </tr>
-
-        <tr>
-          <td>
-            <div class="col-md-8">Remove paragraph</div>
-          </td>
-          <td>
-            <div class="keys">
-              <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt'}}</kbd> + <kbd class="kbd-default">D</kbd>
-            </div>
-          </td>
-        </tr>
-
-        <tr>
-          <td>
-            <div class="col-md-8">Insert new paragraph above</div>
-          </td>
-          <td>
-            <div class="keys">
-              <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt'}}</kbd> + <kbd class="kbd-default">A</kbd>
-            </div>
-          </td>
-        </tr>
-
-        <tr>
-          <td>
-            <div class="col-md-8">Insert new paragraph below</div>
-          </td>
-          <td>
-            <div class="keys">
-              <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt'}}</kbd> + <kbd class="kbd-default">B</kbd>
-            </div>
-          </td>
-        </tr>
-
-        <tr>
-          <td>
-            <div class="col-md-8">Insert copy of paragraph below</div>
-          </td>
-          <td>
-            <div class="keys">
-              <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">Shift</kbd> + <kbd class="kbd-default">C</kbd>
-            </div>
-          </td>
-        </tr>
-
-        <tr>
-          <td>
-            <div class="col-md-8">Move paragraph Up</div>
-          </td>
-          <td>
-            <div class="keys">
-              <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt'}}</kbd> + <kbd class="kbd-default">K</kbd>
-            </div>
-          </td>
-        </tr>
-
-        <tr>
-          <td>
-            <div class="col-md-8">Move paragraph Down</div>
-          </td>
-          <td>
-            <div class="keys">
-              <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt'}}</kbd> + <kbd class="kbd-default">J</kbd>
-            </div>
-          </td>
-        </tr>
-
-        <tr>
-          <td>
-            <div class="col-md-8">Enable/Disable run paragraph</div>
-          </td>
-          <td>
-            <div class="keys">
-              <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt' }}</kbd> + <kbd class="kbd-default">R</kbd>
-            </div>
-          </td>
-        </tr>
-
-        <tr>
-          <td>
-            <div class="col-md-8">Toggle output</div>
-            </td>
-            <td>
-              <div class="keys">
-                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt'}}</kbd> + <kbd class="kbd-default">O</kbd>
-              </div>
-            </td>
-          </tr>
-
-          <tr>
-            <td>
-              <div class="col-md-8">Toggle editor</div>
-            </td>
-            <td>
-              <div class="keys">
-                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt'}}</kbd> + <kbd class="kbd-default">E</kbd>
-              </div>
-            </td>
-          </tr>
-
-          <tr>
-            <td>
-              <div class="col-md-8">Toggle line number</div>
-            </td>
-            <td>
-              <div class="keys">
-                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt'}}</kbd> + <kbd class="kbd-default">M</kbd>
-              </div>
-            </td>
-          </tr>
-
-          <tr>
-            <td>
-              <div class="col-md-8">Toggle title</div>
-            </td>
-            <td>
-              <div class="keys">
-                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt'}}</kbd> + <kbd class="kbd-default">T</kbd>
-              </div>
-            </td>
-          </tr>
-
-          <tr>
-            <td>
-              <div class="col-md-8">Clear output</div>
-            </td>
-            <td>
-              <div class="keys">
-                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt'}}</kbd> + <kbd class="kbd-default">L</kbd>
-              </div>
-            </td>
-          </tr>
-
-          <tr>
-            <td>
-              <div class="col-md-8">Link this paragraph</div>
-            </td>
-            <td>
-              <div class="keys">
-                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt'}}</kbd> + <kbd class="kbd-default">W</kbd>
-              </div>
-            </td>
-          </tr>
-
-          <tr>
-            <td>
-              <div class="col-md-8">Reduce paragraph width</div>
-            </td>
-            <td>
-              <div class="keys">
-                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">Shift</kbd> + <kbd class="kbd-default">-</kbd>
-              </div>
-            </td>
-          </tr>
-
-          <tr>
-            <td>
-              <div class="col-md-8">Increase paragraph width</div>
-            </td>
-            <td>
-              <div class="keys">
-                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">Shift</kbd> + <kbd class="kbd-default">+</kbd>
-              </div>
-            </td>
-          </tr>
-
-          <tr class="sub-title">
-            <th style="width:70%">Editor Keyboard Shortcuts</th>
-            <th></th>
-          </tr>
-
-          <tr>
-            <td>
-              <div class="col-md-8">Auto-completion</div>
-            </td>
-            <td>
-              <div class="keys">
-                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">.</kbd>
-              </div>
-            </td>
-          </tr>
-
-          <tr>
-            <td>
-              <div class="col-md-8">Cut the line</div>
-            </td>
-            <td>
-              <div class="keys">
-                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">K</kbd>
-              </div>
-            </td>
-          </tr>
-
-          <tr>
-            <td>
-              <div class="col-md-8">Paste the line</div>
-            </td>
-            <td>
-              <div class="keys">
-                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">Y</kbd>
-              </div>
-            </td>
-          </tr>
-
-          <tr>
-            <td>
-              <div class="col-md-8">Search inside the code</div>
-            </td>
-            <td>
-              <div class="keys">
-                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">S</kbd>
-              </div>
-            </td>
-          </tr>
-
-          <tr>
-            <td>
-              <div class="col-md-8">Move cursor to the beginning</div>
-            </td>
-            <td>
-              <div class="keys">
-                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">A</kbd>
-              </div>
-            </td>
-          </tr>
-
-          <tr>
-            <td>
-              <div class="col-md-8">Move cursor at the end</div>
-            </td>
-            <td>
-              <div class="keys">
-                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">E</kbd>
-              </div>
-            </td>
-          </tr>
-        </table>
-      </div>
-    </div>
-  </div>
-</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/navbar/expand-collapse/expand-collapse.css
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/navbar/expand-collapse/expand-collapse.css b/zeppelin-web/src/components/navbar/expand-collapse/expand-collapse.css
new file mode 100644
index 0000000..b1a60d8
--- /dev/null
+++ b/zeppelin-web/src/components/navbar/expand-collapse/expand-collapse.css
@@ -0,0 +1,17 @@
+/*
+ * Licensed 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.
+ */
+
+ .expandable {
+  display: none;
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/navbar/expand-collapse/expand-collapse.directive.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/navbar/expand-collapse/expand-collapse.directive.js b/zeppelin-web/src/components/navbar/expand-collapse/expand-collapse.directive.js
new file mode 100644
index 0000000..95e0681
--- /dev/null
+++ b/zeppelin-web/src/components/navbar/expand-collapse/expand-collapse.directive.js
@@ -0,0 +1,48 @@
+/*
+ * Licensed 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.
+ */
+
+import './expand-collapse.css'
+
+angular.module('zeppelinWebApp').directive('expandCollapse', expandCollapseDirective)
+
+function expandCollapseDirective() {
+  return {
+    restrict: 'EA',
+    link: function (scope, element, attrs) {
+      angular.element(element).click(function (event) {
+        if (angular.element(element).find('.expandable:visible').length > 1) {
+          angular.element(element).find('.expandable:visible').slideUp('slow')
+          angular.element(element).find('i.fa-folder-open').toggleClass('fa-folder fa-folder-open')
+        } else {
+          angular.element(element).find('.expandable').first().slideToggle('200', function () {
+            // do not toggle trash folder
+            if (angular.element(element).find('.fa-trash-o').length === 0) {
+              angular.element(element).find('i').first().toggleClass('fa-folder fa-folder-open')
+            }
+          })
+        }
+
+        let target = event.target
+
+        // add note
+        if (target.classList !== undefined && target.classList.contains('fa-plus') &&
+          target.tagName.toLowerCase() === 'i') {
+          return
+        }
+
+        event.stopPropagation()
+      })
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/navbar/navbar-note-list-elem.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/navbar/navbar-note-list-elem.html b/zeppelin-web/src/components/navbar/navbar-note-list-elem.html
new file mode 100644
index 0000000..cb36cfa
--- /dev/null
+++ b/zeppelin-web/src/components/navbar/navbar-note-list-elem.html
@@ -0,0 +1,50 @@
+<!--
+Licensed 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.
+-->
+
+<a class="notebook-list-item" ng-if="navbar.isFilterNote(node) && !node.children" href="#/notebook/{{node.id}}">
+  <i style="font-size: 10px; margin-right: 5px;" ng-class="query.q && node.isTrash ? 'fa fa-trash-o' : 'icon-doc'" ></i>
+  <span>{{noteName(node)}}</span>
+</a>
+
+<li ng-if="node.children">
+  <expand-collapse>
+      <div ng-mouseenter="showFolderButton=true" ng-mouseleave="showFolderButton=false">
+        <a class="notebook-list-item" href="javascript:void(0)">
+          <div ng-if="node.id !== navbar.TRASH_FOLDER_ID">
+            <i style="font-size: 10px; margin-right: 5px;" class="fa fa-folder"></i>
+            <span>{{noteName(node)}}</span>
+            <i data-toggle="modal" data-target="#noteCreateModal" ng-controller="NoteCreateCtrl as noteCreateCtrl"
+              ng-click="noteCreateCtrl.getInterpreterSettings()" data-path="{{node.id}}"
+              style="font-size: 12px; margin-left: 5px; margin-right: 5px;"
+              ng-show="showFolderButton" class="fa fa-plus"
+              uib-tooltip="Create new note"
+              tooltip-placement="{{calculateTooltipPlacement(node)}}">
+            </i>
+          </div>
+          <div ng-if="node.id === navbar.TRASH_FOLDER_ID">
+            <i style="font-size: 12px; margin-right: 5px;" class="fa fa-trash-o"></i>
+            <span>Trash</span>
+          </div>
+        </a>
+      </div>
+      <div class="expandable" style="color: black;">
+        <ul>
+          <li ng-repeat="node in node.children | orderBy:node:false:navbar.arrayOrderingSrv.noteComparator track by $index"
+              ng-class="{'active' : navbar.isActive(node.id)}"
+              ng-include="'components/navbar/navbar-note-list-elem.html'">
+          </li>
+        </ul>
+      </div>
+  </expand-collapse>
+</li>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/navbar/navbar-noteList-elem.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/navbar/navbar-noteList-elem.html b/zeppelin-web/src/components/navbar/navbar-noteList-elem.html
deleted file mode 100644
index 6cba04a..0000000
--- a/zeppelin-web/src/components/navbar/navbar-noteList-elem.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<!--
-Licensed 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.
--->
-
-<a class="notebook-list-item" ng-if="navbar.isFilterNote(node) && !node.children" href="#/notebook/{{node.id}}">
-  <i style="font-size: 10px; margin-right: 5px;" ng-class="query.q && node.isTrash ? 'fa fa-trash-o' : 'icon-doc'" ></i>
-  <span>{{noteName(node)}}</span>
-</a>
-
-<li ng-if="node.children">
-  <expand-collapse>
-      <div ng-mouseenter="showFolderButton=true" ng-mouseleave="showFolderButton=false">
-        <a class="notebook-list-item" href="javascript:void(0)">
-          <div ng-if="node.id !== navbar.TRASH_FOLDER_ID">
-            <i style="font-size: 10px; margin-right: 5px;" class="fa fa-folder"></i>
-            <span>{{noteName(node)}}</span>
-            <i data-toggle="modal" data-target="#noteNameModal" ng-controller="NotenameCtrl as notenamectrl"
-              ng-click="notenamectrl.getInterpreterSettings()" data-path="{{node.id}}"
-              style="font-size: 12px; margin-left: 5px; margin-right: 5px;"
-              ng-show="showFolderButton" class="fa fa-plus"
-              uib-tooltip="Create new note"
-              tooltip-placement="{{calculateTooltipPlacement(node)}}">
-            </i>
-          </div>
-          <div ng-if="node.id === navbar.TRASH_FOLDER_ID">
-            <i style="font-size: 12px; margin-right: 5px;" class="fa fa-trash-o"></i>
-            <span>Trash</span>
-          </div>
-        </a>
-      </div>
-      <div class="expandable" style="color: black;">
-        <ul>
-          <li ng-repeat="node in node.children | orderBy:node:false:navbar.arrayOrderingSrv.noteComparator track by $index"
-              ng-class="{'active' : navbar.isActive(node.id)}"
-              ng-include="'components/navbar/navbar-noteList-elem.html'">
-          </li>
-        </ul>
-      </div>
-  </expand-collapse>
-</li>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/navbar/navbar.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/navbar/navbar.controller.js b/zeppelin-web/src/components/navbar/navbar.controller.js
index 28a900e..0ac2f18 100644
--- a/zeppelin-web/src/components/navbar/navbar.controller.js
+++ b/zeppelin-web/src/components/navbar/navbar.controller.js
@@ -15,7 +15,7 @@
 angular.module('zeppelinWebApp').controller('NavCtrl', NavCtrl)
 
 function NavCtrl ($scope, $rootScope, $http, $routeParams, $location,
-                 noteListDataFactory, baseUrlSrv, websocketMsgSrv,
+                 noteListFactory, baseUrlSrv, websocketMsgSrv,
                  arrayOrderingSrv, searchService, TRASH_FOLDER_ID) {
   'ngInject'
 
@@ -24,7 +24,7 @@ function NavCtrl ($scope, $rootScope, $http, $routeParams, $location,
   vm.connected = websocketMsgSrv.isConnected()
   vm.isActive = isActive
   vm.logout = logout
-  vm.notes = noteListDataFactory
+  vm.notes = noteListFactory
   vm.search = search
   vm.searchForm = searchService
   vm.showLoginWindow = showLoginWindow
@@ -123,7 +123,7 @@ function NavCtrl ($scope, $rootScope, $http, $routeParams, $location,
    */
 
   $scope.$on('setNoteMenu', function (event, notes) {
-    noteListDataFactory.setNotes(notes)
+    noteListFactory.setNotes(notes)
     initNotebookListEventListener()
   })
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/navbar/navbar.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/navbar/navbar.html b/zeppelin-web/src/components/navbar/navbar.html
index d5e6668..597ed51 100644
--- a/zeppelin-web/src/components/navbar/navbar.html
+++ b/zeppelin-web/src/components/navbar/navbar.html
@@ -37,23 +37,23 @@ limitations under the License.
             <span>Notebook</span>
             <span class="caret"></span></a>
           <ul class="dropdown-menu" role="menu">
-            <li ng-controller="NotenameCtrl as notenamectrl">
-              <a href="" data-toggle="modal" data-target="#noteNameModal" ng-click="notenamectrl.getInterpreterSettings()">
+            <li ng-controller="NoteCreateCtrl as noteCreateCtrl">
+              <a href="" data-toggle="modal" data-target="#noteCreateModal" ng-click="noteCreateCtrl.getInterpreterSettings()">
                 <i class="fa fa-plus"></i>
                 Create new note
               </a>
             </li>
             <li class="divider hidden-xs"></li>
             <div id="notebook-list" class="scrollbar-container" ng-if="isDrawNavbarNoteList">
-              <li class="filter-names" ng-include="'components/filterNoteNames/filter-note-names.html'"></li>
+              <li class="filter-names" ng-include="'components/note-name-filter/note-name-filter.html'"></li>
               <div ng-if="!query.q || query.q === ''">
               <li ng-repeat="node in navbar.notes.root.children | orderBy:node:false:navbar.arrayOrderingSrv.noteComparator track by node.id"
-                  ng-class="{'active' : navbar.isActive(node.id)}" ng-include="'components/navbar/navbar-noteList-elem.html'">
+                  ng-class="{'active' : navbar.isActive(node.id)}" ng-include="'components/navbar/navbar-note-list-elem.html'">
               </li>
             </div>
             <div ng-if="query.q">
               <li ng-repeat="node in navbar.notes.flatList | filter : query.q | orderBy:navbar.arrayOrderingSrv.noteFlatListOrdering track by node.id"
-                  ng-class="{'active' : navbar.isActive(node.id)}" ng-include="'components/navbar/navbar-noteList-elem.html'">
+                  ng-class="{'active' : navbar.isActive(node.id)}" ng-include="'components/navbar/navbar-note-list-elem.html'">
               </li>
             </div>
             </div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/ng-enter/ng-enter.directive.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/ng-enter/ng-enter.directive.js b/zeppelin-web/src/components/ng-enter/ng-enter.directive.js
new file mode 100644
index 0000000..98bc067
--- /dev/null
+++ b/zeppelin-web/src/components/ng-enter/ng-enter.directive.js
@@ -0,0 +1,30 @@
+/*
+ * Licensed 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.
+ */
+
+angular.module('zeppelinWebApp').directive('ngEnter', NgEnterDirective)
+
+function NgEnterDirective() {
+  return function (scope, element, attrs) {
+    element.bind('keydown keypress', function (event) {
+      if (event.which === 13) {
+        if (!event.shiftKey) {
+          scope.$apply(function () {
+            scope.$eval(attrs.ngEnter)
+          })
+        }
+        event.preventDefault()
+      }
+    })
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/ng-enter/ng-enter.directive.test.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/ng-enter/ng-enter.directive.test.js b/zeppelin-web/src/components/ng-enter/ng-enter.directive.test.js
new file mode 100644
index 0000000..49f97cc
--- /dev/null
+++ b/zeppelin-web/src/components/ng-enter/ng-enter.directive.test.js
@@ -0,0 +1,24 @@
+describe('Directive: ngEnter', function () {
+  // load the directive's module
+  beforeEach(angular.mock.module('zeppelinWebApp'))
+
+  let element
+  let scope
+
+  beforeEach(inject(function ($rootScope) {
+    scope = $rootScope.$new()
+  }))
+
+  it('should be define', inject(function ($compile) {
+    element = angular.element('<ng-enter></ng-enter>')
+    element = $compile(element)(scope)
+    expect(element.text()).toBeDefined()
+  }))
+
+  // Test the rest of function in ngEnter
+  /*  it('should make hidden element visible', inject(function ($compile) {
+      element = angular.element('<ng-enter></ng-enter>');
+      element = $compile(element)(scope);
+      expect(element.text()).toBe('this is the ngEnter directive');
+    })); */
+})

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/ng-escape/ng-escape.directive.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/ng-escape/ng-escape.directive.js b/zeppelin-web/src/components/ng-escape/ng-escape.directive.js
new file mode 100644
index 0000000..a3d35ea
--- /dev/null
+++ b/zeppelin-web/src/components/ng-escape/ng-escape.directive.js
@@ -0,0 +1,28 @@
+/*
+ * Licensed 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.
+ */
+
+angular.module('zeppelinWebApp').directive('ngEscape', NgEscapeDirective)
+
+function NgEscapeDirective() {
+  return function (scope, element, attrs) {
+    element.bind('keydown keyup', function (event) {
+      if (event.which === 27) {
+        scope.$apply(function () {
+          scope.$eval(attrs.ngEscape)
+        })
+        event.preventDefault()
+      }
+    })
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/ngenter/ngenter.directive.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/ngenter/ngenter.directive.js b/zeppelin-web/src/components/ngenter/ngenter.directive.js
deleted file mode 100644
index 57ec01c..0000000
--- a/zeppelin-web/src/components/ngenter/ngenter.directive.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Licensed 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.
- */
-
-angular.module('zeppelinWebApp').directive('ngEnter', ngEnter)
-
-function ngEnter () {
-  return function (scope, element, attrs) {
-    element.bind('keydown keypress', function (event) {
-      if (event.which === 13) {
-        if (!event.shiftKey) {
-          scope.$apply(function () {
-            scope.$eval(attrs.ngEnter)
-          })
-        }
-        event.preventDefault()
-      }
-    })
-  }
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/ngenter/ngenter.directive.test.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/ngenter/ngenter.directive.test.js b/zeppelin-web/src/components/ngenter/ngenter.directive.test.js
deleted file mode 100644
index 49f97cc..0000000
--- a/zeppelin-web/src/components/ngenter/ngenter.directive.test.js
+++ /dev/null
@@ -1,24 +0,0 @@
-describe('Directive: ngEnter', function () {
-  // load the directive's module
-  beforeEach(angular.mock.module('zeppelinWebApp'))
-
-  let element
-  let scope
-
-  beforeEach(inject(function ($rootScope) {
-    scope = $rootScope.$new()
-  }))
-
-  it('should be define', inject(function ($compile) {
-    element = angular.element('<ng-enter></ng-enter>')
-    element = $compile(element)(scope)
-    expect(element.text()).toBeDefined()
-  }))
-
-  // Test the rest of function in ngEnter
-  /*  it('should make hidden element visible', inject(function ($compile) {
-      element = angular.element('<ng-enter></ng-enter>');
-      element = $compile(element)(scope);
-      expect(element.text()).toBe('this is the ngEnter directive');
-    })); */
-})

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/ngescape/ngescape.directive.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/ngescape/ngescape.directive.js b/zeppelin-web/src/components/ngescape/ngescape.directive.js
deleted file mode 100644
index b52a7a7..0000000
--- a/zeppelin-web/src/components/ngescape/ngescape.directive.js
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Licensed 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.
- */
-
-angular.module('zeppelinWebApp').directive('ngEscape', ngEscape)
-
-function ngEscape () {
-  return function (scope, element, attrs) {
-    element.bind('keydown keyup', function (event) {
-      if (event.which === 27) {
-        scope.$apply(function () {
-          scope.$eval(attrs.ngEscape)
-        })
-        event.preventDefault()
-      }
-    })
-  }
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/note-action/note-action.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/note-action/note-action.service.js b/zeppelin-web/src/components/note-action/note-action.service.js
new file mode 100644
index 0000000..8e00c0f
--- /dev/null
+++ b/zeppelin-web/src/components/note-action/note-action.service.js
@@ -0,0 +1,183 @@
+/*
+ * Licensed 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.
+ */
+
+angular.module('zeppelinWebApp').service('noteActionService', noteActionService)
+
+function noteActionService(websocketMsgSrv, $location, noteRenameService, noteListFactory) {
+  'ngInject'
+
+  this.moveNoteToTrash = function (noteId, redirectToHome) {
+    BootstrapDialog.confirm({
+      closable: true,
+      title: 'Move this note to trash?',
+      message: 'This note will be moved to <strong>trash</strong>.',
+      callback: function (result) {
+        if (result) {
+          websocketMsgSrv.moveNoteToTrash(noteId)
+          if (redirectToHome) {
+            $location.path('/')
+          }
+        }
+      }
+    })
+  }
+
+  this.moveFolderToTrash = function (folderId) {
+    BootstrapDialog.confirm({
+      closable: true,
+      title: 'Move this folder to trash?',
+      message: 'This folder will be moved to <strong>trash</strong>.',
+      callback: function (result) {
+        if (result) {
+          websocketMsgSrv.moveFolderToTrash(folderId)
+        }
+      }
+    })
+  }
+
+  this.removeNote = function (noteId, redirectToHome) {
+    BootstrapDialog.confirm({
+      type: BootstrapDialog.TYPE_WARNING,
+      closable: true,
+      title: 'WARNING! This note will be removed permanently',
+      message: 'This cannot be undone. Are you sure?',
+      callback: function (result) {
+        if (result) {
+          websocketMsgSrv.deleteNote(noteId)
+          if (redirectToHome) {
+            $location.path('/')
+          }
+        }
+      }
+    })
+  }
+
+  this.removeFolder = function (folderId) {
+    BootstrapDialog.confirm({
+      type: BootstrapDialog.TYPE_WARNING,
+      closable: true,
+      title: 'WARNING! This folder will be removed permanently',
+      message: 'This cannot be undone. Are you sure?',
+      callback: function (result) {
+        if (result) {
+          websocketMsgSrv.removeFolder(folderId)
+        }
+      }
+    })
+  }
+
+  this.restoreAll = function () {
+    BootstrapDialog.confirm({
+      closable: true,
+      title: 'Are you sure want to restore all notes in the trash?',
+      message: 'Folders and notes in the trash will be ' +
+      '<strong>merged</strong> into their original position.',
+      callback: function (result) {
+        if (result) {
+          websocketMsgSrv.restoreAll()
+        }
+      }
+    })
+  }
+
+  this.emptyTrash = function () {
+    BootstrapDialog.confirm({
+      type: BootstrapDialog.TYPE_WARNING,
+      closable: true,
+      title: 'WARNING! Notes under trash will be removed permanently',
+      message: 'This cannot be undone. Are you sure?',
+      callback: function (result) {
+        if (result) {
+          websocketMsgSrv.emptyTrash()
+        }
+      }
+    })
+  }
+
+  this.clearAllParagraphOutput = function (noteId) {
+    BootstrapDialog.confirm({
+      closable: true,
+      title: '',
+      message: 'Do you want to clear all output?',
+      callback: function (result) {
+        if (result) {
+          websocketMsgSrv.clearAllParagraphOutput(noteId)
+        }
+      }
+    })
+  }
+
+  this.renameNote = function (noteId, notePath) {
+    noteRenameService.openRenameModal({
+      title: 'Rename note',
+      oldName: notePath,
+      callback: function (newName) {
+        websocketMsgSrv.renameNote(noteId, newName)
+      }
+    })
+  }
+
+  this.renameFolder = function (folderId) {
+    noteRenameService.openRenameModal({
+      title: 'Rename folder',
+      oldName: folderId,
+      callback: function (newName) {
+        let newFolderId = normalizeFolderId(newName)
+        if (_.has(noteListFactory.flatFolderMap, newFolderId)) {
+          BootstrapDialog.confirm({
+            type: BootstrapDialog.TYPE_WARNING,
+            closable: true,
+            title: 'WARNING! The folder will be MERGED',
+            message: 'The folder will be merged into <strong>' + newFolderId + '</strong>. Are you sure?',
+            callback: function (result) {
+              if (result) {
+                websocketMsgSrv.renameFolder(folderId, newFolderId)
+              }
+            }
+          })
+        } else {
+          websocketMsgSrv.renameFolder(folderId, newFolderId)
+        }
+      }
+    })
+  }
+
+  function normalizeFolderId (folderId) {
+    folderId = folderId.trim()
+
+    while (folderId.indexOf('\\') > -1) {
+      folderId = folderId.replace('\\', '/')
+    }
+
+    while (folderId.indexOf('///') > -1) {
+      folderId = folderId.replace('///', '/')
+    }
+
+    folderId = folderId.replace('//', '/')
+
+    if (folderId === '/') {
+      return '/'
+    }
+
+    if (folderId[0] === '/') {
+      folderId = folderId.substring(1)
+    }
+
+    if (folderId.slice(-1) === '/') {
+      folderId = folderId.slice(0, -1)
+    }
+
+    return folderId
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/note-create/note-create.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/note-create/note-create.controller.js b/zeppelin-web/src/components/note-create/note-create.controller.js
new file mode 100644
index 0000000..c999c20
--- /dev/null
+++ b/zeppelin-web/src/components/note-create/note-create.controller.js
@@ -0,0 +1,106 @@
+/*
+ * Licensed 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.
+ */
+
+import './note-create.css'
+
+angular.module('zeppelinWebApp').controller('NoteCreateCtrl', NoteCreateCtrl)
+
+function NoteCreateCtrl ($scope, noteListFactory, $routeParams, websocketMsgSrv) {
+  'ngInject'
+
+  let vm = this
+  vm.clone = false
+  vm.notes = noteListFactory
+  vm.websocketMsgSrv = websocketMsgSrv
+  $scope.note = {}
+  $scope.interpreterSettings = {}
+  $scope.note.defaultInterpreter = null
+
+  vm.createNote = function () {
+    if (!vm.clone) {
+      let defaultInterpreterId = ''
+      if ($scope.note.defaultInterpreter !== null) {
+        defaultInterpreterId = $scope.note.defaultInterpreter.id
+      }
+      vm.websocketMsgSrv.createNotebook($scope.note.notename, defaultInterpreterId)
+      $scope.note.defaultInterpreter = $scope.interpreterSettings[0]
+    } else {
+      let noteId = $routeParams.noteId
+      vm.websocketMsgSrv.cloneNote(noteId, $scope.note.notename)
+    }
+  }
+
+  vm.handleNameEnter = function () {
+    angular.element('#noteCreateModal').modal('toggle')
+    vm.createNote()
+  }
+
+  vm.preVisible = function(clone, sourceNoteName, path) {
+    vm.clone = clone
+    vm.sourceNoteName = sourceNoteName
+    $scope.note.notename = vm.clone ? vm.cloneNoteName() : vm.newNoteName(path)
+    $scope.$apply()
+  }
+
+  vm.newNoteName = function(path) {
+    let newCount = 1
+    angular.forEach(vm.notes.flatList, function (noteName) {
+      noteName = noteName.name
+      if (noteName.match(/^Untitled Note [0-9]*$/)) {
+        let lastCount = noteName.substr(14) * 1
+        if (newCount <= lastCount) {
+          newCount = lastCount + 1
+        }
+      }
+    })
+    return (path ? path + '/' : '') + 'Untitled Note ' + newCount
+  }
+
+  vm.cloneNoteName = function () {
+    let copyCount = 1
+    let newCloneName = ''
+    let lastIndex = vm.sourceNoteName.lastIndexOf(' ')
+    let endsWithNumber = !!vm.sourceNoteName.match('^.+?\\s\\d$')
+    let noteNamePrefix = endsWithNumber ? vm.sourceNoteName.substr(0, lastIndex) : vm.sourceNoteName
+    let regexp = new RegExp('^' + noteNamePrefix + ' .+')
+
+    angular.forEach(vm.notes.flatList, function (noteName) {
+      noteName = noteName.name
+      if (noteName.match(regexp)) {
+        let lastCopyCount = noteName.substr(lastIndex).trim()
+        newCloneName = noteNamePrefix
+        lastCopyCount = parseInt(lastCopyCount)
+        if (copyCount <= lastCopyCount) {
+          copyCount = lastCopyCount + 1
+        }
+      }
+    })
+
+    if (!newCloneName) {
+      newCloneName = vm.sourceNoteName
+    }
+    return newCloneName + ' ' + copyCount
+  }
+
+  vm.getInterpreterSettings = function () {
+    vm.websocketMsgSrv.getInterpreterSettings()
+  }
+
+  $scope.$on('interpreterSettings', function (event, data) {
+    $scope.interpreterSettings = data.interpreterSettings
+
+    // initialize default interpreter with Spark interpreter
+    $scope.note.defaultInterpreter = data.interpreterSettings[0]
+  })
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/note-create/note-create.controller.test.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/note-create/note-create.controller.test.js b/zeppelin-web/src/components/note-create/note-create.controller.test.js
new file mode 100644
index 0000000..d409a14
--- /dev/null
+++ b/zeppelin-web/src/components/note-create/note-create.controller.test.js
@@ -0,0 +1,39 @@
+describe('Controller: NoteCreateCtrl', function () {
+  beforeEach(angular.mock.module('zeppelinWebApp'))
+
+  let scope
+  let ctrl
+  let noteList
+
+  beforeEach(inject(function ($injector, $rootScope, $controller) {
+    noteList = $injector.get('noteListFactory')
+    scope = $rootScope.$new()
+    ctrl = $controller('NoteCreateCtrl', {
+      $scope: scope,
+      noteListFactory: noteList
+    })
+  }))
+
+  it('should create a new name from current name when cloneNoteName is called', function () {
+    let notesList = [
+      {name: 'dsds 1', id: '1'},
+      {name: 'dsds 2', id: '2'},
+      {name: 'test name', id: '3'},
+      {name: 'aa bb cc', id: '4'},
+      {name: 'Untitled Note 6', id: '4'}
+    ]
+
+    noteList.setNotes(notesList)
+
+    ctrl.sourceNoteName = 'test name'
+    expect(ctrl.cloneNoteName()).toEqual('test name 1')
+    ctrl.sourceNoteName = 'aa bb cc'
+    expect(ctrl.cloneNoteName()).toEqual('aa bb cc 1')
+    ctrl.sourceNoteName = 'Untitled Note 6'
+    expect(ctrl.cloneNoteName()).toEqual('Untitled Note 7')
+    ctrl.sourceNoteName = 'My_note'
+    expect(ctrl.cloneNoteName()).toEqual('My_note 1')
+    ctrl.sourceNoteName = 'dsds 2'
+    expect(ctrl.cloneNoteName()).toEqual('dsds 3')
+  })
+})

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/note-create/note-create.css
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/note-create/note-create.css b/zeppelin-web/src/components/note-create/note-create.css
new file mode 100644
index 0000000..22249ad
--- /dev/null
+++ b/zeppelin-web/src/components/note-create/note-create.css
@@ -0,0 +1,49 @@
+/*
+ * Licensed 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.
+ */
+
+.modal-header-note-name {
+  background-color: #3071a9;
+  border: 2px solid #3071a9;
+  border-top-left-radius: 4px;
+  border-top-right-radius: 4px;
+}
+
+.modal-header-note-name > .modal-title {
+  font-weight: 300;
+  font-size: 20px;
+  color: white;
+}
+
+.modal-header-note-name > .close {
+  color: #cfcfcf;
+  opacity: 1;
+}
+
+.modal-body-note-name label {
+  font-size: 17px;
+  font-weight: 400;
+}
+
+.note-name-create-input {
+  margin-top: 5px;
+}
+
+.note-name-desc-panel {
+  margin-top: 20px;
+  margin-bottom: 4px;
+}
+
+.default-interpreter-select {
+  margin-top: 12px;
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/note-create/note-create.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/note-create/note-create.html b/zeppelin-web/src/components/note-create/note-create.html
new file mode 100644
index 0000000..dbfb9e2
--- /dev/null
+++ b/zeppelin-web/src/components/note-create/note-create.html
@@ -0,0 +1,66 @@
+<!--
+Licensed 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.
+-->
+<div id="noteCreateModal" class="modal fade" role="dialog"
+     modalvisible previsiblecallback="noteCreateCtrl.preVisible"
+     targetinput="noteName" tabindex="-1">
+  <div class="modal-dialog">
+
+    <!-- modal content-->
+    <div class="modal-content" id="NoteCreateCtrl">
+      <!-- modal header -->
+      <div class="modal-header modal-header-note-name">
+        <button type="button" class="close" data-dismiss="modal">&times;</button>
+        <h4 class="modal-title" ng-show="!noteCreateCtrl.clone">Create New Note</h4>
+        <h4 class="modal-title" ng-show="noteCreateCtrl.clone">Clone Note</h4>
+      </div>
+
+      <!-- modal body-->
+      <div class="modal-body modal-body-note-name">
+        <div class="form-group">
+          <!-- note name -->
+          <div>
+            <label for="noteName">Note Name</label>
+            <input placeholder="Insert Note Name" type="text"
+                   class="form-control note-name-create-input"
+                   id="noteName" ng-model="note.notename"
+                   ng-enter="noteCreateCtrl.handleNameEnter()" />
+          </div>
+          <!-- default interpreter -->
+          <div class="btn-group default-interpreter-select" ng-show="!noteCreateCtrl.clone">
+            <label for="defaultInterpreter">Default Interpreter</label>
+            <select id="defaultInterpreter"
+                    name="defaultInterpreter"
+                    class="form-control"
+                    ng-model="note.defaultInterpreter"
+                    ng-options="option.name for option in interpreterSettings">
+            </select>
+          </div>
+        </div> <!-- end: form-group -->
+        <div class="panel panel-default note-name-desc-panel">
+          <div class="panel-heading">
+            Use '/' to create folders. Example: /NoteDirA/Note1
+          </div>
+        </div>
+      </div>
+      <div class="modal-footer">
+        <button type="button" id="createNoteButton"
+                class="btn btn-primary"
+                data-dismiss="modal" ng-click="noteCreateCtrl.createNote()">
+          <span ng-show="!noteCreateCtrl.clone">Create</span>
+          <span ng-show="noteCreateCtrl.clone">Clone</span>
+        </button>
+      </div>
+    </div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/note-create/visible.directive.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/note-create/visible.directive.js b/zeppelin-web/src/components/note-create/visible.directive.js
new file mode 100644
index 0000000..48c170f
--- /dev/null
+++ b/zeppelin-web/src/components/note-create/visible.directive.js
@@ -0,0 +1,45 @@
+/*
+ * Licensed 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.
+ */
+
+angular.module('zeppelinWebApp').directive('modalvisible', modalvisible)
+
+function modalvisible () {
+  return {
+    restrict: 'A',
+    scope: {
+      preVisibleCallback: '&previsiblecallback',
+      postVisibleCallback: '&postvisiblecallback',
+      targetinput: '@targetinput'
+    },
+    link: function (scope, element, attrs) {
+      // Add some listeners
+      let previsibleMethod = scope.preVisibleCallback
+      let postVisibleMethod = scope.postVisibleCallback
+      element.on('show.bs.modal', function (e) {
+        let relatedTarget = angular.element(e.relatedTarget)
+        let clone = relatedTarget.data('clone')
+        let sourceNoteName = relatedTarget.data('source-note-name')
+        let path = relatedTarget.data('path')
+        let cloneNote = clone ? true : false
+        previsibleMethod()(cloneNote, sourceNoteName, path)
+      })
+      element.on('shown.bs.modal', function (e) {
+        if (scope.targetinput) {
+          angular.element(e.target).find('input#' + scope.targetinput).select()
+        }
+        postVisibleMethod()
+      })
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/note-import/note-import.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/note-import/note-import.controller.js b/zeppelin-web/src/components/note-import/note-import.controller.js
new file mode 100644
index 0000000..8cec890
--- /dev/null
+++ b/zeppelin-web/src/components/note-import/note-import.controller.js
@@ -0,0 +1,138 @@
+/*
+ * Licensed 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.
+ */
+
+import './note-import.css'
+
+angular.module('zeppelinWebApp').controller('NoteImportCtrl', NoteImportCtrl)
+
+function NoteImportCtrl ($scope, $timeout, websocketMsgSrv) {
+  'ngInject'
+
+  let vm = this
+  $scope.note = {}
+  $scope.note.step1 = true
+  $scope.note.step2 = false
+  $scope.maxLimit = ''
+  let limit = 0
+
+  websocketMsgSrv.listConfigurations()
+  $scope.$on('configurationsInfo', function (scope, event) {
+    limit = event.configurations['zeppelin.websocket.max.text.message.size']
+    $scope.maxLimit = Math.round(limit / 1048576)
+  })
+
+  vm.resetFlags = function () {
+    $scope.note = {}
+    $scope.note.step1 = true
+    $scope.note.step2 = false
+    angular.element('#noteImportFile').val('')
+  }
+
+  $scope.uploadFile = function () {
+    angular.element('#noteImportFile').click()
+  }
+
+  $scope.importFile = function (element) {
+    $scope.note.errorText = ''
+    $scope.note.importFile = element.files[0]
+    let file = $scope.note.importFile
+    let reader = new FileReader()
+
+    if (file.size > limit) {
+      $scope.note.errorText = 'File size limit Exceeded!'
+      $scope.$apply()
+      return
+    }
+
+    reader.onloadend = function () {
+      vm.processImportJson(reader.result)
+    }
+
+    if (file) {
+      reader.readAsText(file)
+    }
+  }
+
+  $scope.uploadURL = function () {
+    $scope.note.errorText = ''
+    $scope.note.step1 = false
+    $timeout(function () {
+      $scope.note.step2 = true
+    }, 400)
+  }
+
+  vm.importBack = function () {
+    $scope.note.errorText = ''
+    $timeout(function () {
+      $scope.note.step1 = true
+    }, 400)
+    $scope.note.step2 = false
+  }
+
+  vm.importNote = function () {
+    $scope.note.errorText = ''
+    if ($scope.note.importUrl) {
+      jQuery.ajax({
+        url: $scope.note.importUrl,
+        type: 'GET',
+        dataType: 'json',
+        jsonp: false,
+        xhrFields: {
+          withCredentials: false
+        },
+        error: function (xhr, ajaxOptions, thrownError) {
+          $scope.note.errorText = 'Unable to Fetch URL'
+          $scope.$apply()
+        }}).done(function (data) {
+          vm.processImportJson(data)
+        })
+    } else {
+      $scope.note.errorText = 'Enter URL'
+      $scope.$apply()
+    }
+  }
+
+  vm.processImportJson = function (result) {
+    if (typeof result !== 'object') {
+      try {
+        result = JSON.parse(result)
+      } catch (e) {
+        $scope.note.errorText = 'JSON parse exception'
+        $scope.$apply()
+        return
+      }
+    }
+    if (result.paragraphs && result.paragraphs.length > 0) {
+      if (!$scope.note.noteImportName) {
+        $scope.note.noteImportName = result.name
+      } else {
+        result.name = $scope.note.noteImportName
+      }
+      websocketMsgSrv.importNote(result)
+      // angular.element('#noteImportModal').modal('hide');
+    } else {
+      $scope.note.errorText = 'Invalid JSON'
+    }
+    $scope.$apply()
+  }
+
+  /*
+   ** $scope.$on functions below
+   */
+
+  $scope.$on('setNoteMenu', function (event, notes) {
+    vm.resetFlags()
+    angular.element('#noteImportModal').modal('hide')
+  })
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/note-import/note-import.css
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/note-import/note-import.css b/zeppelin-web/src/components/note-import/note-import.css
new file mode 100644
index 0000000..81e276a
--- /dev/null
+++ b/zeppelin-web/src/components/note-import/note-import.css
@@ -0,0 +1,91 @@
+/*
+ * Licensed 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.
+ */
+
+.modal-header-import {
+  background-color: #3071a9;
+  border: 2px solid #3071a9;
+  border-top-left-radius: 4px;
+  border-top-right-radius: 4px;
+}
+
+.modal-header-import .close {
+  color: #cfcfcf;
+  opacity: 1;
+}
+
+.modal-header-import .modal-title {
+  color: white;
+  font-size: 20px;
+  font-weight: 300;
+}
+
+.modal-body-import .note-name-input {
+  margin-left: 7px;
+  margin-right: 7px;
+}
+
+.modal-body-import label {
+  font-size: 15px;
+  font-weight: 400;
+  margin-bottom: 10px;
+  margin-top: 15px;
+}
+
+.modal-body-import {
+  min-height: 420px;
+  overflow: hidden;
+}
+
+.modal-body-import .import-btn-image-group {
+  margin-top: 4px;
+}
+
+.modal-body-import .import-btn-image {
+  display: inline-block;
+  margin-left: 8px;
+}
+
+.modal-body-import .import-btn-image a {
+  background: #fff;
+  border: 1px solid #e6e6e6;
+  /*border-radius: 20px;*/
+  border-radius: 20%;
+  color: #7c828e;
+  cursor: pointer;
+  display: block;
+  float: left;
+  font-size: 98px;
+  text-align: center;
+  text-decoration: none;
+  height: 240px;
+  padding-top: 60px;
+  margin: 0 10px 0px 10px;
+  width: 250px;
+}
+
+.modal-body-import .import-btn-image a:hover {
+  background: #eee;
+}
+
+.modal-body-import .modal-body-import-desc {
+  font-size: 15px;
+  font-weight: 400;
+  margin-top: 30px;
+  color: black;
+  font-family: 'Roboto', sans-serif;
+}
+
+.modal-footer-import {
+  min-height: 65px;
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/note-import/note-import.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/note-import/note-import.html b/zeppelin-web/src/components/note-import/note-import.html
new file mode 100644
index 0000000..848a93b
--- /dev/null
+++ b/zeppelin-web/src/components/note-import/note-import.html
@@ -0,0 +1,80 @@
+<!--
+Licensed 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.
+-->
+<div id="noteImportModal" class="modal fade" role="dialog" tabindex="-1"
+     data-backdrop="static" data-keyboard="false">
+
+  <div class="modal-dialog">
+    <div class="modal-content" id="NoteImportCtrl" ng-init="NoteImportInit">
+
+      <!-- modal-header -->
+      <div class="modal-header modal-header-import">
+        <button type="button" class="close" data-dismiss="modal"
+                ng-click="noteImportCtrl.resetFlags()">&times;</button>
+        <h4 class="modal-title">Import New Note</h4>
+      </div>
+
+      <!-- modal-body -->
+      <div class="modal-body modal-body-import">
+        <div class="form-group">
+          <label for="noteImportName">Import As</label>
+          <input class="form-control note-name-input" id="noteImportName"
+                 placeholder="Insert Note Name" type="text"
+                 ng-model="note.noteImportName" />
+        </div>
+
+        <div class="form-group" ng-show="note.errorText">
+          <div class="alert alert-danger">{{note.errorText}}</div>
+        </div>
+
+        <label>JSON file size cannot exceed {{maxLimit}} MB</label>
+
+        <div class="form-group slide-left import-btn-image-group" ng-show="note.step1">
+          <div class="import-btn-image">
+            <a class="fa fa-cloud-upload import-file-upload" ng-click="uploadFile()">
+              <p class="modal-body-import-desc">Select JSON File </p>
+            </a>
+            <div style="display: none">
+              <input class="form-control note-name-input" id="noteImportFile"
+                     placeholder="Note name" type="file"
+                     ng-model="note.importFile" onchange="angular.element(this).scope().importFile(this)" />
+            </div>
+          </div>
+          <div class="import-btn-image">
+            <a href="javascript:void(0);" ng-click="uploadURL()" class="fa fa-link">
+              <p class="modal-body-import-desc">Add from URL</p>
+            </a>
+          </div>
+        </div>
+
+        <div class="form-group slide-right" ng-show="note.step2">
+
+          <label for="noteImportUrl">URL</label>
+          <input placeholder="Note URL" type="text" class="form-control" id="noteImportUrl"
+                 ng-model="note.importUrl" />
+        </div>
+
+      </div>
+      <div class="modal-footer modal-footer-import" ng-show="note.step2">
+        <button type="button" id="importBackButton"
+                class="btn btn-default"
+                ng-click="noteImportCtrl.importBack()">Back
+        </button>
+        <button type="button" id="importNoteButton"
+                class="btn btn-default"
+                ng-click="noteImportCtrl.importNote()">Import Note
+        </button>
+      </div>
+    </div>
+  </div>
+</div>


[2/5] zeppelin git commit: [ZEPPELIN-2749] Use scalable file structure for zeppelin web

Posted by ku...@apache.org.
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/note-list/note-list.factory.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/note-list/note-list.factory.js b/zeppelin-web/src/components/note-list/note-list.factory.js
new file mode 100644
index 0000000..21abbc0
--- /dev/null
+++ b/zeppelin-web/src/components/note-list/note-list.factory.js
@@ -0,0 +1,81 @@
+/*
+ * Licensed 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.
+ */
+
+angular.module('zeppelinWebApp').factory('noteListFactory', NoteListFactory)
+
+function NoteListFactory(TRASH_FOLDER_ID) {
+  'ngInject'
+
+  const notes = {
+    root: {children: []},
+    flatList: [],
+    flatFolderMap: {},
+
+    setNotes: function (notesList) {
+      // a flat list to boost searching
+      notes.flatList = _.map(notesList, (note) => {
+        note.isTrash = note.name
+          ? note.name.split('/')[0] === TRASH_FOLDER_ID : false
+        return note
+      })
+
+      // construct the folder-based tree
+      notes.root = {children: []}
+      notes.flatFolderMap = {}
+      _.reduce(notesList, function (root, note) {
+        let noteName = note.name || note.id
+        let nodes = noteName.match(/([^\/][^\/]*)/g)
+
+        // recursively add nodes
+        addNode(root, nodes, note.id)
+
+        return root
+      }, notes.root)
+    }
+  }
+
+  const addNode = function (curDir, nodes, noteId) {
+    if (nodes.length === 1) {  // the leaf
+      curDir.children.push({
+        name: nodes[0],
+        id: noteId,
+        path: curDir.id ? curDir.id + '/' + nodes[0] : nodes[0],
+        isTrash: curDir.id ? curDir.id.split('/')[0] === TRASH_FOLDER_ID : false
+      })
+    } else {  // a folder node
+      let node = nodes.shift()
+      let dir = _.find(curDir.children,
+        function (c) { return c.name === node && c.children !== undefined })
+      if (dir !== undefined) { // found an existing dir
+        addNode(dir, nodes, noteId)
+      } else {
+        let newDir = {
+          id: curDir.id ? curDir.id + '/' + node : node,
+          name: node,
+          hidden: true,
+          children: [],
+          isTrash: curDir.id ? curDir.id.split('/')[0] === TRASH_FOLDER_ID : false
+        }
+
+        // add the folder to flat folder map
+        notes.flatFolderMap[newDir.id] = newDir
+
+        curDir.children.push(newDir)
+        addNode(newDir, nodes, noteId)
+      }
+    }
+  }
+
+  return notes
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/note-list/note-list.factory.test.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/note-list/note-list.factory.test.js b/zeppelin-web/src/components/note-list/note-list.factory.test.js
new file mode 100644
index 0000000..58d5d42
--- /dev/null
+++ b/zeppelin-web/src/components/note-list/note-list.factory.test.js
@@ -0,0 +1,75 @@
+describe('Factory: NoteList', function () {
+  let noteList
+
+  beforeEach(function () {
+    angular.mock.module('zeppelinWebApp')
+
+    inject(function ($injector) {
+      noteList = $injector.get('noteListFactory')
+    })
+  })
+
+  it('should generate both flat list and folder-based list properly', function () {
+    let notesList = [
+      {name: 'A', id: '000001'},
+      {name: 'B', id: '000002'},
+      {id: '000003'},                     // note without name
+      {name: '/C/CA', id: '000004'},
+      {name: '/C/CB', id: '000005'},
+      {name: '/C/CB/CBA', id: '000006'},  // same name with a dir
+      {name: '/C/CB/CBA', id: '000007'},  // same name with another note
+      {name: 'C///CB//CBB', id: '000008'},
+      {name: 'D/D[A/DA]B', id: '000009'}   // check if '[' and ']' considered as folder seperator
+    ]
+    noteList.setNotes(notesList)
+
+    let flatList = noteList.flatList
+    expect(flatList.length).toBe(9)
+    expect(flatList[0].name).toBe('A')
+    expect(flatList[0].id).toBe('000001')
+    expect(flatList[1].name).toBe('B')
+    expect(flatList[2].name).toBeUndefined()
+    expect(flatList[3].name).toBe('/C/CA')
+    expect(flatList[4].name).toBe('/C/CB')
+    expect(flatList[5].name).toBe('/C/CB/CBA')
+    expect(flatList[6].name).toBe('/C/CB/CBA')
+    expect(flatList[7].name).toBe('C///CB//CBB')
+    expect(flatList[8].name).toBe('D/D[A/DA]B')
+
+    let folderList = noteList.root.children
+    expect(folderList.length).toBe(5)
+    expect(folderList[0].name).toBe('A')
+    expect(folderList[0].id).toBe('000001')
+    expect(folderList[1].name).toBe('B')
+    expect(folderList[2].name).toBe('000003')
+    expect(folderList[3].name).toBe('C')
+    expect(folderList[3].id).toBe('C')
+    expect(folderList[3].children.length).toBe(3)
+    expect(folderList[3].children[0].name).toBe('CA')
+    expect(folderList[3].children[0].id).toBe('000004')
+    expect(folderList[3].children[0].children).toBeUndefined()
+    expect(folderList[3].children[1].name).toBe('CB')
+    expect(folderList[3].children[1].id).toBe('000005')
+    expect(folderList[3].children[1].children).toBeUndefined()
+    expect(folderList[3].children[2].name).toBe('CB')
+    expect(folderList[3].children[2].id).toBe('C/CB')
+    expect(folderList[3].children[2].children.length).toBe(3)
+    expect(folderList[3].children[2].children[0].name).toBe('CBA')
+    expect(folderList[3].children[2].children[0].id).toBe('000006')
+    expect(folderList[3].children[2].children[0].children).toBeUndefined()
+    expect(folderList[3].children[2].children[1].name).toBe('CBA')
+    expect(folderList[3].children[2].children[1].id).toBe('000007')
+    expect(folderList[3].children[2].children[1].children).toBeUndefined()
+    expect(folderList[3].children[2].children[2].name).toBe('CBB')
+    expect(folderList[3].children[2].children[2].id).toBe('000008')
+    expect(folderList[3].children[2].children[2].children).toBeUndefined()
+    expect(folderList[4].name).toBe('D')
+    expect(folderList[4].id).toBe('D')
+    expect(folderList[4].children.length).toBe(1)
+    expect(folderList[4].children[0].name).toBe('D[A')
+    expect(folderList[4].children[0].id).toBe('D/D[A')
+    expect(folderList[4].children[0].children[0].name).toBe('DA]B')
+    expect(folderList[4].children[0].children[0].id).toBe('000009')
+    expect(folderList[4].children[0].children[0].children).toBeUndefined()
+  })
+})

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/note-name-filter/note-name-filter.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/note-name-filter/note-name-filter.html b/zeppelin-web/src/components/note-name-filter/note-name-filter.html
new file mode 100644
index 0000000..071cba4
--- /dev/null
+++ b/zeppelin-web/src/components/note-name-filter/note-name-filter.html
@@ -0,0 +1,19 @@
+<!--
+Licensed 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.
+-->
+<input type="text"
+       class="note-name-query form-control"
+       ng-click="$event.stopPropagation()"
+       placeholder="&#xf002 Filter"
+       ng-model="$parent.query.q"
+       ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 300, 'blur': 0 } }" />

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/note-rename/note-rename.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/note-rename/note-rename.controller.js b/zeppelin-web/src/components/note-rename/note-rename.controller.js
new file mode 100644
index 0000000..b950d2b
--- /dev/null
+++ b/zeppelin-web/src/components/note-rename/note-rename.controller.js
@@ -0,0 +1,48 @@
+/*
+ * Licensed 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.
+ */
+
+import './note-rename.css'
+
+angular.module('zeppelinWebApp').controller('NoteRenameCtrl', NoteRenameController)
+
+function NoteRenameController($scope) {
+  'ngInject'
+
+  let self = this
+
+  $scope.params = {newName: ''}
+  $scope.isValid = true
+
+  $scope.rename = function () {
+    angular.element('#noteRenameModal').modal('hide')
+    self.callback($scope.params.newName)
+  }
+
+  $scope.$on('openRenameModal', function (event, options) {
+    self.validator = options.validator || defaultValidator
+    self.callback = options.callback || function () {}
+
+    $scope.title = options.title || 'Rename'
+    $scope.params.newName = options.oldName || ''
+    $scope.validate = function () {
+      $scope.isValid = self.validator($scope.params.newName)
+    }
+
+    angular.element('#noteRenameModal').modal('show')
+  })
+
+  function defaultValidator (str) {
+    return !!str.trim()
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/note-rename/note-rename.css
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/note-rename/note-rename.css b/zeppelin-web/src/components/note-rename/note-rename.css
new file mode 100644
index 0000000..45d4710
--- /dev/null
+++ b/zeppelin-web/src/components/note-rename/note-rename.css
@@ -0,0 +1,38 @@
+/*
+ * Licensed 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.
+ */
+
+.modal-header-rename {
+  background-color: #3071a9;
+  border: 2px solid #3071a9;
+  border-top-left-radius: 4px;
+  border-top-right-radius: 4px;
+}
+
+.modal-header-rename .close {
+  color: #cfcfcf;
+  opacity: 1;
+}
+
+.modal-header-rename .modal-title {
+  color: white;
+  font-size: 20px;
+  font-weight: 300;
+}
+
+.modal-body-rename label {
+  font-size: 17px;
+  font-weight: 400;
+  margin-top: 5px;
+  margin-bottom: 15px;
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/note-rename/note-rename.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/note-rename/note-rename.html b/zeppelin-web/src/components/note-rename/note-rename.html
new file mode 100644
index 0000000..2f3a8c1
--- /dev/null
+++ b/zeppelin-web/src/components/note-rename/note-rename.html
@@ -0,0 +1,43 @@
+<!--
+Licensed 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.
+-->
+<div id="noteRenameModal" class="modal fade" role="dialog" tabindex="-1">
+  <div class="modal-dialog">
+    <div class="modal-content">
+      <!-- modal-header -->
+      <div class="modal-header modal-header-rename">
+        <button type="button" class="close" data-dismiss="modal">&times;</button>
+        <h4 class="modal-title">{{title}}</h4>
+      </div>
+
+      <!-- modal-body -->
+      <div class="modal-body modal-body-rename">
+        <label ng-if="isValid">Please enter a new name</label>
+        <label ng-if="!isValid" class="text-danger">Please enter a valid name</label>
+        <div class="form-group" ng-class="{'has-error': !isValid}">
+          <input type="text" class="form-control"
+                 ng-model="params.newName" ng-change="validate()"
+                 ng-enter="isValid && rename()" />
+        </div>
+      </div>
+      <div class="modal-footer">
+        <div>
+          <button type="button" class="btn btn-default btn-primary"
+                  ng-click="rename()" ng-class="{'disabled': !isValid}">
+            Rename
+          </button>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/note-rename/note-rename.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/note-rename/note-rename.service.js b/zeppelin-web/src/components/note-rename/note-rename.service.js
new file mode 100644
index 0000000..64df82f
--- /dev/null
+++ b/zeppelin-web/src/components/note-rename/note-rename.service.js
@@ -0,0 +1,32 @@
+/*
+ * Licensed 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.
+ */
+
+angular.module('zeppelinWebApp').service('noteRenameService', NoteRenameService)
+
+function NoteRenameService($rootScope) {
+  'ngInject'
+
+  let self = this
+
+  /**
+   * <options schema>
+   * title: string - Modal title
+   * oldName: string - name to initialize input
+   * callback: (newName: string)=>void - callback onButtonClick
+   * validator: (str: string)=>boolean - input validator
+   */
+  self.openRenameModal = function (options) {
+    $rootScope.$broadcast('openRenameModal', options)
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/noteAction/noteAction.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/noteAction/noteAction.service.js b/zeppelin-web/src/components/noteAction/noteAction.service.js
deleted file mode 100644
index f925975..0000000
--- a/zeppelin-web/src/components/noteAction/noteAction.service.js
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Licensed 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.
- */
-
-angular.module('zeppelinWebApp').service('noteActionSrv', noteActionSrv)
-
-function noteActionSrv (websocketMsgSrv, $location, renameSrv, noteListDataFactory) {
-  'ngInject'
-
-  this.moveNoteToTrash = function (noteId, redirectToHome) {
-    BootstrapDialog.confirm({
-      closable: true,
-      title: 'Move this note to trash?',
-      message: 'This note will be moved to <strong>trash</strong>.',
-      callback: function (result) {
-        if (result) {
-          websocketMsgSrv.moveNoteToTrash(noteId)
-          if (redirectToHome) {
-            $location.path('/')
-          }
-        }
-      }
-    })
-  }
-
-  this.moveFolderToTrash = function (folderId) {
-    BootstrapDialog.confirm({
-      closable: true,
-      title: 'Move this folder to trash?',
-      message: 'This folder will be moved to <strong>trash</strong>.',
-      callback: function (result) {
-        if (result) {
-          websocketMsgSrv.moveFolderToTrash(folderId)
-        }
-      }
-    })
-  }
-
-  this.removeNote = function (noteId, redirectToHome) {
-    BootstrapDialog.confirm({
-      type: BootstrapDialog.TYPE_WARNING,
-      closable: true,
-      title: 'WARNING! This note will be removed permanently',
-      message: 'This cannot be undone. Are you sure?',
-      callback: function (result) {
-        if (result) {
-          websocketMsgSrv.deleteNote(noteId)
-          if (redirectToHome) {
-            $location.path('/')
-          }
-        }
-      }
-    })
-  }
-
-  this.removeFolder = function (folderId) {
-    BootstrapDialog.confirm({
-      type: BootstrapDialog.TYPE_WARNING,
-      closable: true,
-      title: 'WARNING! This folder will be removed permanently',
-      message: 'This cannot be undone. Are you sure?',
-      callback: function (result) {
-        if (result) {
-          websocketMsgSrv.removeFolder(folderId)
-        }
-      }
-    })
-  }
-
-  this.restoreAll = function () {
-    BootstrapDialog.confirm({
-      closable: true,
-      title: 'Are you sure want to restore all notes in the trash?',
-      message: 'Folders and notes in the trash will be ' +
-      '<strong>merged</strong> into their original position.',
-      callback: function (result) {
-        if (result) {
-          websocketMsgSrv.restoreAll()
-        }
-      }
-    })
-  }
-
-  this.emptyTrash = function () {
-    BootstrapDialog.confirm({
-      type: BootstrapDialog.TYPE_WARNING,
-      closable: true,
-      title: 'WARNING! Notes under trash will be removed permanently',
-      message: 'This cannot be undone. Are you sure?',
-      callback: function (result) {
-        if (result) {
-          websocketMsgSrv.emptyTrash()
-        }
-      }
-    })
-  }
-
-  this.clearAllParagraphOutput = function (noteId) {
-    BootstrapDialog.confirm({
-      closable: true,
-      title: '',
-      message: 'Do you want to clear all output?',
-      callback: function (result) {
-        if (result) {
-          websocketMsgSrv.clearAllParagraphOutput(noteId)
-        }
-      }
-    })
-  }
-
-  this.renameNote = function (noteId, notePath) {
-    renameSrv.openRenameModal({
-      title: 'Rename note',
-      oldName: notePath,
-      callback: function (newName) {
-        websocketMsgSrv.renameNote(noteId, newName)
-      }
-    })
-  }
-
-  this.renameFolder = function (folderId) {
-    renameSrv.openRenameModal({
-      title: 'Rename folder',
-      oldName: folderId,
-      callback: function (newName) {
-        let newFolderId = normalizeFolderId(newName)
-        if (_.has(noteListDataFactory.flatFolderMap, newFolderId)) {
-          BootstrapDialog.confirm({
-            type: BootstrapDialog.TYPE_WARNING,
-            closable: true,
-            title: 'WARNING! The folder will be MERGED',
-            message: 'The folder will be merged into <strong>' + newFolderId + '</strong>. Are you sure?',
-            callback: function (result) {
-              if (result) {
-                websocketMsgSrv.renameFolder(folderId, newFolderId)
-              }
-            }
-          })
-        } else {
-          websocketMsgSrv.renameFolder(folderId, newFolderId)
-        }
-      }
-    })
-  }
-
-  function normalizeFolderId (folderId) {
-    folderId = folderId.trim()
-
-    while (folderId.indexOf('\\') > -1) {
-      folderId = folderId.replace('\\', '/')
-    }
-
-    while (folderId.indexOf('///') > -1) {
-      folderId = folderId.replace('///', '/')
-    }
-
-    folderId = folderId.replace('//', '/')
-
-    if (folderId === '/') {
-      return '/'
-    }
-
-    if (folderId[0] === '/') {
-      folderId = folderId.substring(1)
-    }
-
-    if (folderId.slice(-1) === '/') {
-      folderId = folderId.slice(0, -1)
-    }
-
-    return folderId
-  }
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/noteListDataFactory/noteList.datafactory.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/noteListDataFactory/noteList.datafactory.js b/zeppelin-web/src/components/noteListDataFactory/noteList.datafactory.js
deleted file mode 100644
index ab6956a..0000000
--- a/zeppelin-web/src/components/noteListDataFactory/noteList.datafactory.js
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Licensed 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.
- */
-
-angular.module('zeppelinWebApp').factory('noteListDataFactory', noteListDataFactory)
-
-function noteListDataFactory (TRASH_FOLDER_ID) {
-  'ngInject'
-
-  const notes = {
-    root: {children: []},
-    flatList: [],
-    flatFolderMap: {},
-
-    setNotes: function (notesList) {
-      // a flat list to boost searching
-      notes.flatList = _.map(notesList, (note) => {
-        note.isTrash = note.name
-          ? note.name.split('/')[0] === TRASH_FOLDER_ID : false
-        return note
-      })
-
-      // construct the folder-based tree
-      notes.root = {children: []}
-      notes.flatFolderMap = {}
-      _.reduce(notesList, function (root, note) {
-        let noteName = note.name || note.id
-        let nodes = noteName.match(/([^\/][^\/]*)/g)
-
-        // recursively add nodes
-        addNode(root, nodes, note.id)
-
-        return root
-      }, notes.root)
-    }
-  }
-
-  const addNode = function (curDir, nodes, noteId) {
-    if (nodes.length === 1) {  // the leaf
-      curDir.children.push({
-        name: nodes[0],
-        id: noteId,
-        path: curDir.id ? curDir.id + '/' + nodes[0] : nodes[0],
-        isTrash: curDir.id ? curDir.id.split('/')[0] === TRASH_FOLDER_ID : false
-      })
-    } else {  // a folder node
-      let node = nodes.shift()
-      let dir = _.find(curDir.children,
-        function (c) { return c.name === node && c.children !== undefined })
-      if (dir !== undefined) { // found an existing dir
-        addNode(dir, nodes, noteId)
-      } else {
-        let newDir = {
-          id: curDir.id ? curDir.id + '/' + node : node,
-          name: node,
-          hidden: true,
-          children: [],
-          isTrash: curDir.id ? curDir.id.split('/')[0] === TRASH_FOLDER_ID : false
-        }
-
-        // add the folder to flat folder map
-        notes.flatFolderMap[newDir.id] = newDir
-
-        curDir.children.push(newDir)
-        addNode(newDir, nodes, noteId)
-      }
-    }
-  }
-
-  return notes
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/noteListDataFactory/noteList.datafactory.test.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/noteListDataFactory/noteList.datafactory.test.js b/zeppelin-web/src/components/noteListDataFactory/noteList.datafactory.test.js
deleted file mode 100644
index d1acc5d..0000000
--- a/zeppelin-web/src/components/noteListDataFactory/noteList.datafactory.test.js
+++ /dev/null
@@ -1,75 +0,0 @@
-describe('Factory: NoteList', function () {
-  let noteList
-
-  beforeEach(function () {
-    angular.mock.module('zeppelinWebApp')
-
-    inject(function ($injector) {
-      noteList = $injector.get('noteListDataFactory')
-    })
-  })
-
-  it('should generate both flat list and folder-based list properly', function () {
-    let notesList = [
-      {name: 'A', id: '000001'},
-      {name: 'B', id: '000002'},
-      {id: '000003'},                     // note without name
-      {name: '/C/CA', id: '000004'},
-      {name: '/C/CB', id: '000005'},
-      {name: '/C/CB/CBA', id: '000006'},  // same name with a dir
-      {name: '/C/CB/CBA', id: '000007'},  // same name with another note
-      {name: 'C///CB//CBB', id: '000008'},
-      {name: 'D/D[A/DA]B', id: '000009'}   // check if '[' and ']' considered as folder seperator
-    ]
-    noteList.setNotes(notesList)
-
-    let flatList = noteList.flatList
-    expect(flatList.length).toBe(9)
-    expect(flatList[0].name).toBe('A')
-    expect(flatList[0].id).toBe('000001')
-    expect(flatList[1].name).toBe('B')
-    expect(flatList[2].name).toBeUndefined()
-    expect(flatList[3].name).toBe('/C/CA')
-    expect(flatList[4].name).toBe('/C/CB')
-    expect(flatList[5].name).toBe('/C/CB/CBA')
-    expect(flatList[6].name).toBe('/C/CB/CBA')
-    expect(flatList[7].name).toBe('C///CB//CBB')
-    expect(flatList[8].name).toBe('D/D[A/DA]B')
-
-    let folderList = noteList.root.children
-    expect(folderList.length).toBe(5)
-    expect(folderList[0].name).toBe('A')
-    expect(folderList[0].id).toBe('000001')
-    expect(folderList[1].name).toBe('B')
-    expect(folderList[2].name).toBe('000003')
-    expect(folderList[3].name).toBe('C')
-    expect(folderList[3].id).toBe('C')
-    expect(folderList[3].children.length).toBe(3)
-    expect(folderList[3].children[0].name).toBe('CA')
-    expect(folderList[3].children[0].id).toBe('000004')
-    expect(folderList[3].children[0].children).toBeUndefined()
-    expect(folderList[3].children[1].name).toBe('CB')
-    expect(folderList[3].children[1].id).toBe('000005')
-    expect(folderList[3].children[1].children).toBeUndefined()
-    expect(folderList[3].children[2].name).toBe('CB')
-    expect(folderList[3].children[2].id).toBe('C/CB')
-    expect(folderList[3].children[2].children.length).toBe(3)
-    expect(folderList[3].children[2].children[0].name).toBe('CBA')
-    expect(folderList[3].children[2].children[0].id).toBe('000006')
-    expect(folderList[3].children[2].children[0].children).toBeUndefined()
-    expect(folderList[3].children[2].children[1].name).toBe('CBA')
-    expect(folderList[3].children[2].children[1].id).toBe('000007')
-    expect(folderList[3].children[2].children[1].children).toBeUndefined()
-    expect(folderList[3].children[2].children[2].name).toBe('CBB')
-    expect(folderList[3].children[2].children[2].id).toBe('000008')
-    expect(folderList[3].children[2].children[2].children).toBeUndefined()
-    expect(folderList[4].name).toBe('D')
-    expect(folderList[4].id).toBe('D')
-    expect(folderList[4].children.length).toBe(1)
-    expect(folderList[4].children[0].name).toBe('D[A')
-    expect(folderList[4].children[0].id).toBe('D/D[A')
-    expect(folderList[4].children[0].children[0].name).toBe('DA]B')
-    expect(folderList[4].children[0].children[0].id).toBe('000009')
-    expect(folderList[4].children[0].children[0].children).toBeUndefined()
-  })
-})

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/noteName-create/note-name-dialog.css
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/noteName-create/note-name-dialog.css b/zeppelin-web/src/components/noteName-create/note-name-dialog.css
deleted file mode 100644
index 22249ad..0000000
--- a/zeppelin-web/src/components/noteName-create/note-name-dialog.css
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Licensed 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.
- */
-
-.modal-header-note-name {
-  background-color: #3071a9;
-  border: 2px solid #3071a9;
-  border-top-left-radius: 4px;
-  border-top-right-radius: 4px;
-}
-
-.modal-header-note-name > .modal-title {
-  font-weight: 300;
-  font-size: 20px;
-  color: white;
-}
-
-.modal-header-note-name > .close {
-  color: #cfcfcf;
-  opacity: 1;
-}
-
-.modal-body-note-name label {
-  font-size: 17px;
-  font-weight: 400;
-}
-
-.note-name-create-input {
-  margin-top: 5px;
-}
-
-.note-name-desc-panel {
-  margin-top: 20px;
-  margin-bottom: 4px;
-}
-
-.default-interpreter-select {
-  margin-top: 12px;
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/noteName-create/note-name-dialog.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/noteName-create/note-name-dialog.html b/zeppelin-web/src/components/noteName-create/note-name-dialog.html
deleted file mode 100644
index 6b20d3a..0000000
--- a/zeppelin-web/src/components/noteName-create/note-name-dialog.html
+++ /dev/null
@@ -1,66 +0,0 @@
-<!--
-Licensed 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.
--->
-<div id="noteNameModal" class="modal fade" role="dialog"
-     modalvisible previsiblecallback="notenamectrl.preVisible"
-     targetinput="noteName" tabindex="-1">
-  <div class="modal-dialog">
-
-    <!-- modal content-->
-    <div class="modal-content" id="NotenameCtrl">
-      <!-- modal header -->
-      <div class="modal-header modal-header-note-name">
-        <button type="button" class="close" data-dismiss="modal">&times;</button>
-        <h4 class="modal-title" ng-show="!notenamectrl.clone">Create New Note</h4>
-        <h4 class="modal-title" ng-show="notenamectrl.clone">Clone Note</h4>
-      </div>
-
-      <!-- modal body-->
-      <div class="modal-body modal-body-note-name">
-        <div class="form-group">
-          <!-- note name -->
-          <div>
-            <label for="noteName">Note Name</label>
-            <input placeholder="Insert Note Name" type="text"
-                   class="form-control note-name-create-input"
-                   id="noteName" ng-model="note.notename"
-                   ng-enter="notenamectrl.handleNameEnter()" />
-          </div>
-          <!-- default interpreter -->
-          <div class="btn-group default-interpreter-select" ng-show="!notenamectrl.clone">
-            <label for="defaultInterpreter">Default Interpreter</label>
-            <select id="defaultInterpreter"
-                    name="defaultInterpreter"
-                    class="form-control"
-                    ng-model="note.defaultInterpreter"
-                    ng-options="option.name for option in interpreterSettings">
-            </select>
-          </div>
-        </div> <!-- end: form-group -->
-        <div class="panel panel-default note-name-desc-panel">
-          <div class="panel-heading">
-            Use '/' to create folders. Example: /NoteDirA/Note1
-          </div>
-        </div>
-      </div>
-      <div class="modal-footer">
-        <button type="button" id="createNoteButton"
-                class="btn btn-primary"
-                data-dismiss="modal" ng-click="notenamectrl.createNote()">
-          <span ng-show="!notenamectrl.clone">Create</span>
-          <span ng-show="notenamectrl.clone">Clone</span>
-        </button>
-      </div>
-    </div>
-  </div>
-</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/noteName-create/notename.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/noteName-create/notename.controller.js b/zeppelin-web/src/components/noteName-create/notename.controller.js
deleted file mode 100644
index 80bceb8..0000000
--- a/zeppelin-web/src/components/noteName-create/notename.controller.js
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Licensed 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.
- */
-
-import './note-name-dialog.css'
-
-angular.module('zeppelinWebApp').controller('NotenameCtrl', NotenameCtrl)
-
-function NotenameCtrl ($scope, noteListDataFactory, $routeParams, websocketMsgSrv) {
-  'ngInject'
-
-  let vm = this
-  vm.clone = false
-  vm.notes = noteListDataFactory
-  vm.websocketMsgSrv = websocketMsgSrv
-  $scope.note = {}
-  $scope.interpreterSettings = {}
-  $scope.note.defaultInterpreter = null
-
-  vm.createNote = function () {
-    if (!vm.clone) {
-      let defaultInterpreterId = ''
-      if ($scope.note.defaultInterpreter !== null) {
-        defaultInterpreterId = $scope.note.defaultInterpreter.id
-      }
-      vm.websocketMsgSrv.createNotebook($scope.note.notename, defaultInterpreterId)
-      $scope.note.defaultInterpreter = $scope.interpreterSettings[0]
-    } else {
-      let noteId = $routeParams.noteId
-      vm.websocketMsgSrv.cloneNote(noteId, $scope.note.notename)
-    }
-  }
-
-  vm.handleNameEnter = function () {
-    angular.element('#noteNameModal').modal('toggle')
-    vm.createNote()
-  }
-
-  vm.preVisible = function(clone, sourceNoteName, path) {
-    vm.clone = clone
-    vm.sourceNoteName = sourceNoteName
-    $scope.note.notename = vm.clone ? vm.cloneNoteName() : vm.newNoteName(path)
-    $scope.$apply()
-  }
-
-  vm.newNoteName = function(path) {
-    let newCount = 1
-    angular.forEach(vm.notes.flatList, function (noteName) {
-      noteName = noteName.name
-      if (noteName.match(/^Untitled Note [0-9]*$/)) {
-        let lastCount = noteName.substr(14) * 1
-        if (newCount <= lastCount) {
-          newCount = lastCount + 1
-        }
-      }
-    })
-    return (path ? path + '/' : '') + 'Untitled Note ' + newCount
-  }
-
-  vm.cloneNoteName = function () {
-    let copyCount = 1
-    let newCloneName = ''
-    let lastIndex = vm.sourceNoteName.lastIndexOf(' ')
-    let endsWithNumber = !!vm.sourceNoteName.match('^.+?\\s\\d$')
-    let noteNamePrefix = endsWithNumber ? vm.sourceNoteName.substr(0, lastIndex) : vm.sourceNoteName
-    let regexp = new RegExp('^' + noteNamePrefix + ' .+')
-
-    angular.forEach(vm.notes.flatList, function (noteName) {
-      noteName = noteName.name
-      if (noteName.match(regexp)) {
-        let lastCopyCount = noteName.substr(lastIndex).trim()
-        newCloneName = noteNamePrefix
-        lastCopyCount = parseInt(lastCopyCount)
-        if (copyCount <= lastCopyCount) {
-          copyCount = lastCopyCount + 1
-        }
-      }
-    })
-
-    if (!newCloneName) {
-      newCloneName = vm.sourceNoteName
-    }
-    return newCloneName + ' ' + copyCount
-  }
-
-  vm.getInterpreterSettings = function () {
-    vm.websocketMsgSrv.getInterpreterSettings()
-  }
-
-  $scope.$on('interpreterSettings', function (event, data) {
-    $scope.interpreterSettings = data.interpreterSettings
-
-    // initialize default interpreter with Spark interpreter
-    $scope.note.defaultInterpreter = data.interpreterSettings[0]
-  })
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/noteName-create/notename.controller.test.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/noteName-create/notename.controller.test.js b/zeppelin-web/src/components/noteName-create/notename.controller.test.js
deleted file mode 100644
index 61bce14..0000000
--- a/zeppelin-web/src/components/noteName-create/notename.controller.test.js
+++ /dev/null
@@ -1,39 +0,0 @@
-describe('Controller: NotenameCtrl', function () {
-  beforeEach(angular.mock.module('zeppelinWebApp'))
-
-  let scope
-  let ctrl
-  let noteList
-
-  beforeEach(inject(function ($injector, $rootScope, $controller) {
-    noteList = $injector.get('noteListDataFactory')
-    scope = $rootScope.$new()
-    ctrl = $controller('NotenameCtrl', {
-      $scope: scope,
-      noteListDataFactory: noteList
-    })
-  }))
-
-  it('should create a new name from current name when cloneNoteName is called', function () {
-    let notesList = [
-      {name: 'dsds 1', id: '1'},
-      {name: 'dsds 2', id: '2'},
-      {name: 'test name', id: '3'},
-      {name: 'aa bb cc', id: '4'},
-      {name: 'Untitled Note 6', id: '4'}
-    ]
-
-    noteList.setNotes(notesList)
-
-    ctrl.sourceNoteName = 'test name'
-    expect(ctrl.cloneNoteName()).toEqual('test name 1')
-    ctrl.sourceNoteName = 'aa bb cc'
-    expect(ctrl.cloneNoteName()).toEqual('aa bb cc 1')
-    ctrl.sourceNoteName = 'Untitled Note 6'
-    expect(ctrl.cloneNoteName()).toEqual('Untitled Note 7')
-    ctrl.sourceNoteName = 'My_note'
-    expect(ctrl.cloneNoteName()).toEqual('My_note 1')
-    ctrl.sourceNoteName = 'dsds 2'
-    expect(ctrl.cloneNoteName()).toEqual('dsds 3')
-  })
-})

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/noteName-create/visible.directive.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/noteName-create/visible.directive.js b/zeppelin-web/src/components/noteName-create/visible.directive.js
deleted file mode 100644
index 48c170f..0000000
--- a/zeppelin-web/src/components/noteName-create/visible.directive.js
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed 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.
- */
-
-angular.module('zeppelinWebApp').directive('modalvisible', modalvisible)
-
-function modalvisible () {
-  return {
-    restrict: 'A',
-    scope: {
-      preVisibleCallback: '&previsiblecallback',
-      postVisibleCallback: '&postvisiblecallback',
-      targetinput: '@targetinput'
-    },
-    link: function (scope, element, attrs) {
-      // Add some listeners
-      let previsibleMethod = scope.preVisibleCallback
-      let postVisibleMethod = scope.postVisibleCallback
-      element.on('show.bs.modal', function (e) {
-        let relatedTarget = angular.element(e.relatedTarget)
-        let clone = relatedTarget.data('clone')
-        let sourceNoteName = relatedTarget.data('source-note-name')
-        let path = relatedTarget.data('path')
-        let cloneNote = clone ? true : false
-        previsibleMethod()(cloneNote, sourceNoteName, path)
-      })
-      element.on('shown.bs.modal', function (e) {
-        if (scope.targetinput) {
-          angular.element(e.target).find('input#' + scope.targetinput).select()
-        }
-        postVisibleMethod()
-      })
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/noteName-import/note-import-dialog.css
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/noteName-import/note-import-dialog.css b/zeppelin-web/src/components/noteName-import/note-import-dialog.css
deleted file mode 100644
index 81e276a..0000000
--- a/zeppelin-web/src/components/noteName-import/note-import-dialog.css
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Licensed 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.
- */
-
-.modal-header-import {
-  background-color: #3071a9;
-  border: 2px solid #3071a9;
-  border-top-left-radius: 4px;
-  border-top-right-radius: 4px;
-}
-
-.modal-header-import .close {
-  color: #cfcfcf;
-  opacity: 1;
-}
-
-.modal-header-import .modal-title {
-  color: white;
-  font-size: 20px;
-  font-weight: 300;
-}
-
-.modal-body-import .note-name-input {
-  margin-left: 7px;
-  margin-right: 7px;
-}
-
-.modal-body-import label {
-  font-size: 15px;
-  font-weight: 400;
-  margin-bottom: 10px;
-  margin-top: 15px;
-}
-
-.modal-body-import {
-  min-height: 420px;
-  overflow: hidden;
-}
-
-.modal-body-import .import-btn-image-group {
-  margin-top: 4px;
-}
-
-.modal-body-import .import-btn-image {
-  display: inline-block;
-  margin-left: 8px;
-}
-
-.modal-body-import .import-btn-image a {
-  background: #fff;
-  border: 1px solid #e6e6e6;
-  /*border-radius: 20px;*/
-  border-radius: 20%;
-  color: #7c828e;
-  cursor: pointer;
-  display: block;
-  float: left;
-  font-size: 98px;
-  text-align: center;
-  text-decoration: none;
-  height: 240px;
-  padding-top: 60px;
-  margin: 0 10px 0px 10px;
-  width: 250px;
-}
-
-.modal-body-import .import-btn-image a:hover {
-  background: #eee;
-}
-
-.modal-body-import .modal-body-import-desc {
-  font-size: 15px;
-  font-weight: 400;
-  margin-top: 30px;
-  color: black;
-  font-family: 'Roboto', sans-serif;
-}
-
-.modal-footer-import {
-  min-height: 65px;
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/noteName-import/note-import-dialog.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/noteName-import/note-import-dialog.html b/zeppelin-web/src/components/noteName-import/note-import-dialog.html
deleted file mode 100644
index c640ca1..0000000
--- a/zeppelin-web/src/components/noteName-import/note-import-dialog.html
+++ /dev/null
@@ -1,80 +0,0 @@
-<!--
-Licensed 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.
--->
-<div id="noteImportModal" class="modal fade" role="dialog" tabindex="-1"
-     data-backdrop="static" data-keyboard="false">
-
-  <div class="modal-dialog">
-    <div class="modal-content" id="NoteImportCtrl" ng-init="NoteImportInit">
-
-      <!-- modal-header -->
-      <div class="modal-header modal-header-import">
-        <button type="button" class="close" data-dismiss="modal"
-                ng-click="noteimportctrl.resetFlags()">&times;</button>
-        <h4 class="modal-title">Import New Note</h4>
-      </div>
-
-      <!-- modal-body -->
-      <div class="modal-body modal-body-import">
-        <div class="form-group">
-          <label for="noteImportName">Import As</label>
-          <input class="form-control note-name-input" id="noteImportName"
-                 placeholder="Insert Note Name" type="text"
-                 ng-model="note.noteImportName" />
-        </div>
-
-        <div class="form-group" ng-show="note.errorText">
-          <div class="alert alert-danger">{{note.errorText}}</div>
-        </div>
-
-        <label>JSON file size cannot exceed {{maxLimit}} MB</label>
-
-        <div class="form-group slide-left import-btn-image-group" ng-show="note.step1">
-          <div class="import-btn-image">
-            <a class="fa fa-cloud-upload import-file-upload" ng-click="uploadFile()">
-              <p class="modal-body-import-desc">Select JSON File </p>
-            </a>
-            <div style="display: none">
-              <input class="form-control note-name-input" id="noteImportFile"
-                     placeholder="Note name" type="file"
-                     ng-model="note.importFile" onchange="angular.element(this).scope().importFile(this)" />
-            </div>
-          </div>
-          <div class="import-btn-image">
-            <a href="javascript:void(0);" ng-click="uploadURL()" class="fa fa-link">
-              <p class="modal-body-import-desc">Add from URL</p>
-            </a>
-          </div>
-        </div>
-
-        <div class="form-group slide-right" ng-show="note.step2">
-
-          <label for="noteImportUrl">URL</label>
-          <input placeholder="Note URL" type="text" class="form-control" id="noteImportUrl"
-                 ng-model="note.importUrl" />
-        </div>
-
-      </div>
-      <div class="modal-footer modal-footer-import" ng-show="note.step2">
-        <button type="button" id="importBackButton"
-                class="btn btn-default"
-                ng-click="noteimportctrl.importBack()">Back
-        </button>
-        <button type="button" id="importNoteButton"
-                class="btn btn-default"
-                ng-click="noteimportctrl.importNote()">Import Note
-        </button>
-      </div>
-    </div>
-  </div>
-</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/noteName-import/notenameImport.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/noteName-import/notenameImport.controller.js b/zeppelin-web/src/components/noteName-import/notenameImport.controller.js
deleted file mode 100644
index 0959244..0000000
--- a/zeppelin-web/src/components/noteName-import/notenameImport.controller.js
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Licensed 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.
- */
-
-import './note-import-dialog.css'
-
-angular.module('zeppelinWebApp').controller('NoteImportCtrl', NoteImportCtrl)
-
-function NoteImportCtrl ($scope, $timeout, websocketMsgSrv) {
-  'ngInject'
-
-  let vm = this
-  $scope.note = {}
-  $scope.note.step1 = true
-  $scope.note.step2 = false
-  $scope.maxLimit = ''
-  let limit = 0
-
-  websocketMsgSrv.listConfigurations()
-  $scope.$on('configurationsInfo', function (scope, event) {
-    limit = event.configurations['zeppelin.websocket.max.text.message.size']
-    $scope.maxLimit = Math.round(limit / 1048576)
-  })
-
-  vm.resetFlags = function () {
-    $scope.note = {}
-    $scope.note.step1 = true
-    $scope.note.step2 = false
-    angular.element('#noteImportFile').val('')
-  }
-
-  $scope.uploadFile = function () {
-    angular.element('#noteImportFile').click()
-  }
-
-  $scope.importFile = function (element) {
-    $scope.note.errorText = ''
-    $scope.note.importFile = element.files[0]
-    let file = $scope.note.importFile
-    let reader = new FileReader()
-
-    if (file.size > limit) {
-      $scope.note.errorText = 'File size limit Exceeded!'
-      $scope.$apply()
-      return
-    }
-
-    reader.onloadend = function () {
-      vm.processImportJson(reader.result)
-    }
-
-    if (file) {
-      reader.readAsText(file)
-    }
-  }
-
-  $scope.uploadURL = function () {
-    $scope.note.errorText = ''
-    $scope.note.step1 = false
-    $timeout(function () {
-      $scope.note.step2 = true
-    }, 400)
-  }
-
-  vm.importBack = function () {
-    $scope.note.errorText = ''
-    $timeout(function () {
-      $scope.note.step1 = true
-    }, 400)
-    $scope.note.step2 = false
-  }
-
-  vm.importNote = function () {
-    $scope.note.errorText = ''
-    if ($scope.note.importUrl) {
-      jQuery.ajax({
-        url: $scope.note.importUrl,
-        type: 'GET',
-        dataType: 'json',
-        jsonp: false,
-        xhrFields: {
-          withCredentials: false
-        },
-        error: function (xhr, ajaxOptions, thrownError) {
-          $scope.note.errorText = 'Unable to Fetch URL'
-          $scope.$apply()
-        }}).done(function (data) {
-          vm.processImportJson(data)
-        })
-    } else {
-      $scope.note.errorText = 'Enter URL'
-      $scope.$apply()
-    }
-  }
-
-  vm.processImportJson = function (result) {
-    if (typeof result !== 'object') {
-      try {
-        result = JSON.parse(result)
-      } catch (e) {
-        $scope.note.errorText = 'JSON parse exception'
-        $scope.$apply()
-        return
-      }
-    }
-    if (result.paragraphs && result.paragraphs.length > 0) {
-      if (!$scope.note.noteImportName) {
-        $scope.note.noteImportName = result.name
-      } else {
-        result.name = $scope.note.noteImportName
-      }
-      websocketMsgSrv.importNote(result)
-      // angular.element('#noteImportModal').modal('hide');
-    } else {
-      $scope.note.errorText = 'Invalid JSON'
-    }
-    $scope.$apply()
-  }
-
-  /*
-   ** $scope.$on functions below
-   */
-
-  $scope.$on('setNoteMenu', function (event, notes) {
-    vm.resetFlags()
-    angular.element('#noteImportModal').modal('hide')
-  })
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/notevarshareService/notevarshare.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/notevarshareService/notevarshare.service.js b/zeppelin-web/src/components/notevarshareService/notevarshare.service.js
deleted file mode 100644
index 04981bf..0000000
--- a/zeppelin-web/src/components/notevarshareService/notevarshare.service.js
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Licensed 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.
- */
-
-angular.module('zeppelinWebApp').service('noteVarShareService', noteVarShareService)
-
-function noteVarShareService () {
-  'ngInject'
-
-  let store = {}
-
-  this.clear = function () {
-    store = {}
-  }
-
-  this.put = function (key, value) {
-    store[key] = value
-  }
-
-  this.get = function (key) {
-    return store[key]
-  }
-
-  this.del = function (key) {
-    let v = store[key]
-    delete store[key]
-    return v
-  }
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/rename/rename.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/rename/rename.controller.js b/zeppelin-web/src/components/rename/rename.controller.js
deleted file mode 100644
index 2682b6e..0000000
--- a/zeppelin-web/src/components/rename/rename.controller.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Licensed 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.
- */
-
-import './rename.css'
-
-angular.module('zeppelinWebApp').controller('RenameCtrl', RenameCtrl)
-
-function RenameCtrl ($scope) {
-  'ngInject'
-
-  let self = this
-
-  $scope.params = {newName: ''}
-  $scope.isValid = true
-
-  $scope.rename = function () {
-    angular.element('#renameModal').modal('hide')
-    self.callback($scope.params.newName)
-  }
-
-  $scope.$on('openRenameModal', function (event, options) {
-    self.validator = options.validator || defaultValidator
-    self.callback = options.callback || function () {}
-
-    $scope.title = options.title || 'Rename'
-    $scope.params.newName = options.oldName || ''
-    $scope.validate = function () {
-      $scope.isValid = self.validator($scope.params.newName)
-    }
-
-    angular.element('#renameModal').modal('show')
-  })
-
-  function defaultValidator (str) {
-    return !!str.trim()
-  }
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/rename/rename.css
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/rename/rename.css b/zeppelin-web/src/components/rename/rename.css
deleted file mode 100644
index 45d4710..0000000
--- a/zeppelin-web/src/components/rename/rename.css
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Licensed 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.
- */
-
-.modal-header-rename {
-  background-color: #3071a9;
-  border: 2px solid #3071a9;
-  border-top-left-radius: 4px;
-  border-top-right-radius: 4px;
-}
-
-.modal-header-rename .close {
-  color: #cfcfcf;
-  opacity: 1;
-}
-
-.modal-header-rename .modal-title {
-  color: white;
-  font-size: 20px;
-  font-weight: 300;
-}
-
-.modal-body-rename label {
-  font-size: 17px;
-  font-weight: 400;
-  margin-top: 5px;
-  margin-bottom: 15px;
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/rename/rename.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/rename/rename.html b/zeppelin-web/src/components/rename/rename.html
deleted file mode 100644
index 71caa76..0000000
--- a/zeppelin-web/src/components/rename/rename.html
+++ /dev/null
@@ -1,43 +0,0 @@
-<!--
-Licensed 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.
--->
-<div id="renameModal" class="modal fade" role="dialog" tabindex="-1">
-  <div class="modal-dialog">
-    <div class="modal-content">
-      <!-- modal-header -->
-      <div class="modal-header modal-header-rename">
-        <button type="button" class="close" data-dismiss="modal">&times;</button>
-        <h4 class="modal-title">{{title}}</h4>
-      </div>
-
-      <!-- modal-body -->
-      <div class="modal-body modal-body-rename">
-        <label ng-if="isValid">Please enter a new name</label>
-        <label ng-if="!isValid" class="text-danger">Please enter a valid name</label>
-        <div class="form-group" ng-class="{'has-error': !isValid}">
-          <input type="text" class="form-control"
-                 ng-model="params.newName" ng-change="validate()"
-                 ng-enter="isValid && rename()" />
-        </div>
-      </div>
-      <div class="modal-footer">
-        <div>
-          <button type="button" class="btn btn-default btn-primary"
-                  ng-click="rename()" ng-class="{'disabled': !isValid}">
-            Rename
-          </button>
-        </div>
-      </div>
-    </div>
-  </div>
-</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/rename/rename.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/rename/rename.service.js b/zeppelin-web/src/components/rename/rename.service.js
deleted file mode 100644
index 135f85d..0000000
--- a/zeppelin-web/src/components/rename/rename.service.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Licensed 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.
- */
-
-angular.module('zeppelinWebApp').service('renameSrv', renameSrv)
-
-function renameSrv ($rootScope) {
-  'ngInject'
-
-  let self = this
-
-  /**
-   * <options schema>
-   * title: string - Modal title
-   * oldName: string - name to initialize input
-   * callback: (newName: string)=>void - callback onButtonClick
-   * validator: (str: string)=>boolean - input validator
-   */
-  self.openRenameModal = function (options) {
-    $rootScope.$broadcast('openRenameModal', options)
-  }
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/repository-create/repository-dialog.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/repository-create/repository-dialog.html b/zeppelin-web/src/components/repository-create/repository-dialog.html
deleted file mode 100644
index ab678b7..0000000
--- a/zeppelin-web/src/components/repository-create/repository-dialog.html
+++ /dev/null
@@ -1,115 +0,0 @@
-<!--
-Licensed 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.
--->
-
-<div id="repoModal" class="modal fade" role="dialog"
-     tabindex="-1">
-  <div class="modal-dialog">
-
-    <!-- Modal content-->
-    <div class="modal-content">
-      <div class="modal-header">
-        <button type="button" class="close" data-dismiss="modal">&times;</button>
-        <h4 class="modal-title">Add New Repository</h4>
-      </div>
-
-      <form class="form-horizontal" ng-submit="addNewRepository()">
-        <div class="modal-body">
-            <div class="form-group">
-              <label class="control-label col-sm-2" for="repoId">ID</label>
-              <div class="col-sm-10">
-                <input type="text" class="form-control" id="repoId" ng-model="newRepoSetting.id"
-                       placeholder="Repository id" required />
-              </div>
-            </div>
-            <div class="form-group">
-              <label class="control-label col-sm-2" for="repoUrl">URL</label>
-              <div class="col-sm-10">
-                <input type="text" pattern="(http|https|file)://.*" title="Please enter URL starts with http://, https:// or file://"
-                       class="form-control" id="repoUrl" ng-model="newRepoSetting.url"
-                       placeholder="Repository url" required />
-              </div>
-            </div>
-            <div class="form-group">
-              <label class="control-label col-sm-2" for="repoSnapshot">Snapshot</label>
-              <div class="col-sm-10">
-                <select class="form-control"
-                        id="repoSnapshot"
-                        ng-model="newRepoSetting.snapshot"
-                        ng-options="col for col in [false,true]">
-                </select>
-              </div>
-            </div>
-            <div class="form-group">
-              <label class="control-label col-sm-2" for="repoUsername">Username</label>
-              <div class="col-sm-10">
-                <input type="text" class="form-control" id="repoUsername" ng-model="newRepoSetting.username" />
-              </div>
-            </div>
-            <div class="form-group">
-              <label class="control-label col-sm-2" for="repoPassword">Password</label>
-              <div class="col-sm-10">
-                <input type="password" class="form-control" id="repoPassword" ng-model="newRepoSetting.password" />
-              </div>
-            </div>
-            <hr/>
-            <div class="center-block"><h4>Proxy Settings (optional)</h4></div>
-            <br/>
-            <div class="form-group">
-              <div class="col-sm-10">
-                <label class="control-label col-sm-2" for="proxyProtocol1">Protocol</label>
-                <label class="radio-inline">
-                    <input type="radio" name="proxyProtocol" id="proxyProtocol1" value="HTTP" ng-model="newRepoSetting.proxyProtocol"/> HTTP
-                </label>
-                <label class="radio-inline">
-                    <input type="radio" name="proxyProtocol" id="proxyProtocol2" value="HTTPS" ng-model="newRepoSetting.proxyProtocol"/> HTTPS
-                </label>
-              </div>
-            </div>
-            <div class="form-group">
-              <label class="control-label col-sm-2" for="proxyHost">Host</label>
-              <div class="col-sm-10">
-                <input type="text" class="form-control" id="proxyHost" ng-model="newRepoSetting.proxyHost" />
-              </div>
-            </div>
-            <div class="form-group">
-              <label class="control-label col-sm-2" for="proxyPort">Port</label>
-              <div class="col-sm-10">
-                <input type="text" class="form-control" id="proxyPort" ng-model="newRepoSetting.proxyPort" />
-              </div>
-            </div>
-            <div class="form-group">
-                <label class="control-label col-sm-2" for="proxyLogin">Login</label>
-                <div class="col-sm-10">
-                    <input type="text" class="form-control" id="proxyLogin" ng-model="newRepoSetting.proxyLogin" />
-                </div>
-            </div>
-            <div class="form-group">
-                <label class="control-label col-sm-2" for="proxyPassword">Password</label>
-                <div class="col-sm-10">
-                    <input type="text" class="form-control" id="proxyPassword" ng-model="newRepoSetting.proxyPassword" />
-                </div>
-            </div>
-        </div>
-        <div class="modal-footer">
-          <button type="submit"
-                  class="btn btn-default">Add
-          </button>
-          <button type="button" data-dismiss="modal"
-                  class="btn btn-default" ng-click="resetNewRepositorySetting()">Cancel
-          </button>
-        </div>
-      </form>
-    </div>
-  </div>
-</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/resizable/resizable.directive.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/resizable/resizable.directive.js b/zeppelin-web/src/components/resizable/resizable.directive.js
deleted file mode 100644
index 7bf8477..0000000
--- a/zeppelin-web/src/components/resizable/resizable.directive.js
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Licensed 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.
- */
-
-angular.module('zeppelinWebApp').directive('resizable', resizable)
-
-function resizable () {
-  let resizableConfig = {
-    autoHide: true,
-    handles: 'se',
-    helper: 'resizable-helper',
-    stop: function () {
-      angular.element(this).css({'width': '100%', 'height': '100%'})
-    }
-  }
-
-  return {
-    restrict: 'A',
-    scope: {
-      callback: '&onResize'
-    },
-    link: function postLink (scope, elem, attrs) {
-      attrs.$observe('resize', function (resize) {
-        let resetResize = function (elem, resize) {
-          let colStep = window.innerWidth / 12
-          elem.off('resizestop')
-          let conf = angular.copy(resizableConfig)
-          if (resize.graphType === 'TABLE' || resize.graphType === 'NETWORK' || resize.graphType === 'TEXT') {
-            conf.grid = [colStep, 10]
-            conf.minHeight = 100
-          } else {
-            conf.grid = [colStep, 10000]
-            conf.minHeight = 0
-          }
-          conf.maxWidth = window.innerWidth
-
-          elem.resizable(conf)
-          elem.on('resizestop', function () {
-            if (scope.callback) {
-              let height = elem.height()
-              if (height < 50) {
-                height = 300
-              }
-              scope.callback({width: Math.ceil(elem.width() / colStep), height: height})
-            }
-          })
-        }
-
-        resize = JSON.parse(resize)
-        if (resize.allowresize === 'true') {
-          resetResize(elem, resize)
-          angular.element(window).resize(function () {
-            resetResize(elem, resize)
-          })
-        }
-      })
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/saveAs/saveAs.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/saveAs/saveAs.service.js b/zeppelin-web/src/components/saveAs/saveAs.service.js
deleted file mode 100644
index b9cb7c8..0000000
--- a/zeppelin-web/src/components/saveAs/saveAs.service.js
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Licensed 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.
- */
-
-angular.module('zeppelinWebApp').service('saveAsService', saveAsService)
-
-function saveAsService (browserDetectService) {
-  'ngInject'
-
-  this.saveAs = function (content, filename, extension) {
-    let BOM = '\uFEFF'
-    if (browserDetectService.detectIE()) {
-      angular.element('body').append('<iframe id="SaveAsId" style="display: none"></iframe>')
-      let frameSaveAs = angular.element('body > iframe#SaveAsId')[0].contentWindow
-      content = BOM + content
-      frameSaveAs.document.open('text/json', 'replace')
-      frameSaveAs.document.write(content)
-      frameSaveAs.document.close()
-      frameSaveAs.focus()
-      let t1 = Date.now()
-      frameSaveAs.document.execCommand('SaveAs', false, filename + '.' + extension)
-      let t2 = Date.now()
-
-      // This means, this version of IE dosen't support auto download of a file with extension provided in param
-      // falling back to ".txt"
-      if (t1 === t2) {
-        frameSaveAs.document.execCommand('SaveAs', true, filename + '.txt')
-      }
-      angular.element('body > iframe#SaveAsId').remove()
-    } else {
-      content = 'data:image/svg;charset=utf-8,' + BOM + encodeURIComponent(content)
-      angular.element('body').append('<a id="SaveAsId"></a>')
-      let saveAsElement = angular.element('body > a#SaveAsId')
-      saveAsElement.attr('href', content)
-      saveAsElement.attr('download', filename + '.' + extension)
-      saveAsElement.attr('target', '_blank')
-      saveAsElement[0].click()
-      saveAsElement.remove()
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/searchService/search.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/searchService/search.service.js b/zeppelin-web/src/components/searchService/search.service.js
deleted file mode 100644
index a1c8640..0000000
--- a/zeppelin-web/src/components/searchService/search.service.js
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Licensed 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.
- */
-
-angular.module('zeppelinWebApp').service('searchService', searchService)
-
-function searchService ($resource, baseUrlSrv) {
-  'ngInject'
-
-  this.search = function (term) {
-    this.searchTerm = term.q
-    console.log('Searching for: %o', term.q)
-    if (!term.q) { // TODO(bzz): empty string check
-      return
-    }
-    let encQuery = window.encodeURIComponent(term.q)
-    return $resource(baseUrlSrv.getRestApiBase() + '/notebook/search?q=' + encQuery, {}, {
-      query: {method: 'GET'}
-    })
-  }
-
-  this.searchTerm = ''
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/websocket/websocket-event.factory.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/websocket/websocket-event.factory.js b/zeppelin-web/src/components/websocket/websocket-event.factory.js
new file mode 100644
index 0000000..f8391ab
--- /dev/null
+++ b/zeppelin-web/src/components/websocket/websocket-event.factory.js
@@ -0,0 +1,196 @@
+/*
+ * Licensed 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.
+ */
+
+angular.module('zeppelinWebApp').factory('websocketEvents', WebsocketEventFactory)
+
+function WebsocketEventFactory ($rootScope, $websocket, $location, baseUrlSrv) {
+  'ngInject'
+
+  let websocketCalls = {}
+  let pingIntervalId
+
+  websocketCalls.ws = $websocket(baseUrlSrv.getWebsocketUrl())
+  websocketCalls.ws.reconnectIfNotNormalClose = true
+
+  websocketCalls.ws.onOpen(function () {
+    console.log('Websocket created')
+    $rootScope.$broadcast('setConnectedStatus', true)
+    pingIntervalId = setInterval(function () {
+      websocketCalls.sendNewEvent({op: 'PING'})
+    }, 10000)
+  })
+
+  websocketCalls.sendNewEvent = function (data) {
+    if ($rootScope.ticket !== undefined) {
+      data.principal = $rootScope.ticket.principal
+      data.ticket = $rootScope.ticket.ticket
+      data.roles = $rootScope.ticket.roles
+    } else {
+      data.principal = ''
+      data.ticket = ''
+      data.roles = ''
+    }
+    console.log('Send >> %o, %o, %o, %o, %o', data.op, data.principal, data.ticket, data.roles, data)
+    websocketCalls.ws.send(JSON.stringify(data))
+  }
+
+  websocketCalls.isConnected = function () {
+    return (websocketCalls.ws.socket.readyState === 1)
+  }
+
+  websocketCalls.ws.onMessage(function (event) {
+    let payload
+    if (event.data) {
+      payload = angular.fromJson(event.data)
+    }
+
+    console.log('Receive << %o, %o', payload.op, payload)
+
+    let op = payload.op
+    let data = payload.data
+    if (op === 'NOTE') {
+      $rootScope.$broadcast('setNoteContent', data.note)
+    } else if (op === 'NEW_NOTE') {
+      $location.path('/notebook/' + data.note.id)
+    } else if (op === 'NOTES_INFO') {
+      $rootScope.$broadcast('setNoteMenu', data.notes)
+    } else if (op === 'LIST_NOTE_JOBS') {
+      $rootScope.$broadcast('setNoteJobs', data.noteJobs)
+    } else if (op === 'LIST_UPDATE_NOTE_JOBS') {
+      $rootScope.$broadcast('setUpdateNoteJobs', data.noteRunningJobs)
+    } else if (op === 'AUTH_INFO') {
+      let btn = []
+      if ($rootScope.ticket.roles === '[]') {
+        btn = [{
+          label: 'Close',
+          action: function (dialog) {
+            dialog.close()
+          }
+        }]
+      } else {
+        btn = [{
+          label: 'Login',
+          action: function (dialog) {
+            dialog.close()
+            angular.element('#loginModal').modal({
+              show: 'true'
+            })
+          }
+        }, {
+          label: 'Cancel',
+          action: function (dialog) {
+            dialog.close()
+            // using $rootScope.apply to trigger angular digest cycle
+            // changing $location.path inside bootstrap modal wont trigger digest
+            $rootScope.$apply(function () {
+              $location.path('/')
+            })
+          }
+        }]
+      }
+
+      BootstrapDialog.show({
+        closable: false,
+        closeByBackdrop: false,
+        closeByKeyboard: false,
+        title: 'Insufficient privileges',
+        message: data.info.toString(),
+        buttons: btn
+      })
+    } else if (op === 'PARAGRAPH') {
+      $rootScope.$broadcast('updateParagraph', data)
+    } else if (op === 'RUN_PARAGRAPH_USING_SPELL') {
+      $rootScope.$broadcast('runParagraphUsingSpell', data)
+    } else if (op === 'PARAGRAPH_APPEND_OUTPUT') {
+      $rootScope.$broadcast('appendParagraphOutput', data)
+    } else if (op === 'PARAGRAPH_UPDATE_OUTPUT') {
+      $rootScope.$broadcast('updateParagraphOutput', data)
+    } else if (op === 'PROGRESS') {
+      $rootScope.$broadcast('updateProgress', data)
+    } else if (op === 'COMPLETION_LIST') {
+      $rootScope.$broadcast('completionList', data)
+    } else if (op === 'EDITOR_SETTING') {
+      $rootScope.$broadcast('editorSetting', data)
+    } else if (op === 'ANGULAR_OBJECT_UPDATE') {
+      $rootScope.$broadcast('angularObjectUpdate', data)
+    } else if (op === 'ANGULAR_OBJECT_REMOVE') {
+      $rootScope.$broadcast('angularObjectRemove', data)
+    } else if (op === 'APP_APPEND_OUTPUT') {
+      $rootScope.$broadcast('appendAppOutput', data)
+    } else if (op === 'APP_UPDATE_OUTPUT') {
+      $rootScope.$broadcast('updateAppOutput', data)
+    } else if (op === 'APP_LOAD') {
+      $rootScope.$broadcast('appLoad', data)
+    } else if (op === 'APP_STATUS_CHANGE') {
+      $rootScope.$broadcast('appStatusChange', data)
+    } else if (op === 'LIST_REVISION_HISTORY') {
+      $rootScope.$broadcast('listRevisionHistory', data)
+    } else if (op === 'NOTE_REVISION') {
+      $rootScope.$broadcast('noteRevision', data)
+    } else if (op === 'INTERPRETER_BINDINGS') {
+      $rootScope.$broadcast('interpreterBindings', data)
+    } else if (op === 'ERROR_INFO') {
+      BootstrapDialog.show({
+        closable: false,
+        closeByBackdrop: false,
+        closeByKeyboard: false,
+        title: 'Details',
+        message: data.info.toString(),
+        buttons: [{
+          // close all the dialogs when there are error on running all paragraphs
+          label: 'Close',
+          action: function () {
+            BootstrapDialog.closeAll()
+          }
+        }]
+      })
+    } else if (op === 'SESSION_LOGOUT') {
+      $rootScope.$broadcast('session_logout', data)
+    } else if (op === 'CONFIGURATIONS_INFO') {
+      $rootScope.$broadcast('configurationsInfo', data)
+    } else if (op === 'INTERPRETER_SETTINGS') {
+      $rootScope.$broadcast('interpreterSettings', data)
+    } else if (op === 'PARAGRAPH_ADDED') {
+      $rootScope.$broadcast('addParagraph', data.paragraph, data.index)
+    } else if (op === 'PARAGRAPH_REMOVED') {
+      $rootScope.$broadcast('removeParagraph', data.id)
+    } else if (op === 'PARAGRAPH_MOVED') {
+      $rootScope.$broadcast('moveParagraph', data.id, data.index)
+    } else if (op === 'NOTE_UPDATED') {
+      $rootScope.$broadcast('updateNote', data.name, data.config, data.info)
+    } else if (op === 'SET_NOTE_REVISION') {
+      $rootScope.$broadcast('setNoteRevisionResult', data)
+    } else if (op === 'PARAS_INFO') {
+      $rootScope.$broadcast('updateParaInfos', data)
+    } else {
+      console.error(`unknown websocket op: ${op}`)
+    }
+  })
+
+  websocketCalls.ws.onError(function (event) {
+    console.log('error message: ', event)
+    $rootScope.$broadcast('setConnectedStatus', false)
+  })
+
+  websocketCalls.ws.onClose(function (event) {
+    console.log('close message: ', event)
+    if (pingIntervalId !== undefined) {
+      clearInterval(pingIntervalId)
+      pingIntervalId = undefined
+    }
+    $rootScope.$broadcast('setConnectedStatus', false)
+  })
+
+  return websocketCalls
+}


[4/5] zeppelin git commit: [ZEPPELIN-2749] Use scalable file structure for zeppelin web

Posted by ku...@apache.org.
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/interpreter/repository-create.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/interpreter/repository-create.html b/zeppelin-web/src/app/interpreter/repository-create.html
new file mode 100644
index 0000000..ab678b7
--- /dev/null
+++ b/zeppelin-web/src/app/interpreter/repository-create.html
@@ -0,0 +1,115 @@
+<!--
+Licensed 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.
+-->
+
+<div id="repoModal" class="modal fade" role="dialog"
+     tabindex="-1">
+  <div class="modal-dialog">
+
+    <!-- Modal content-->
+    <div class="modal-content">
+      <div class="modal-header">
+        <button type="button" class="close" data-dismiss="modal">&times;</button>
+        <h4 class="modal-title">Add New Repository</h4>
+      </div>
+
+      <form class="form-horizontal" ng-submit="addNewRepository()">
+        <div class="modal-body">
+            <div class="form-group">
+              <label class="control-label col-sm-2" for="repoId">ID</label>
+              <div class="col-sm-10">
+                <input type="text" class="form-control" id="repoId" ng-model="newRepoSetting.id"
+                       placeholder="Repository id" required />
+              </div>
+            </div>
+            <div class="form-group">
+              <label class="control-label col-sm-2" for="repoUrl">URL</label>
+              <div class="col-sm-10">
+                <input type="text" pattern="(http|https|file)://.*" title="Please enter URL starts with http://, https:// or file://"
+                       class="form-control" id="repoUrl" ng-model="newRepoSetting.url"
+                       placeholder="Repository url" required />
+              </div>
+            </div>
+            <div class="form-group">
+              <label class="control-label col-sm-2" for="repoSnapshot">Snapshot</label>
+              <div class="col-sm-10">
+                <select class="form-control"
+                        id="repoSnapshot"
+                        ng-model="newRepoSetting.snapshot"
+                        ng-options="col for col in [false,true]">
+                </select>
+              </div>
+            </div>
+            <div class="form-group">
+              <label class="control-label col-sm-2" for="repoUsername">Username</label>
+              <div class="col-sm-10">
+                <input type="text" class="form-control" id="repoUsername" ng-model="newRepoSetting.username" />
+              </div>
+            </div>
+            <div class="form-group">
+              <label class="control-label col-sm-2" for="repoPassword">Password</label>
+              <div class="col-sm-10">
+                <input type="password" class="form-control" id="repoPassword" ng-model="newRepoSetting.password" />
+              </div>
+            </div>
+            <hr/>
+            <div class="center-block"><h4>Proxy Settings (optional)</h4></div>
+            <br/>
+            <div class="form-group">
+              <div class="col-sm-10">
+                <label class="control-label col-sm-2" for="proxyProtocol1">Protocol</label>
+                <label class="radio-inline">
+                    <input type="radio" name="proxyProtocol" id="proxyProtocol1" value="HTTP" ng-model="newRepoSetting.proxyProtocol"/> HTTP
+                </label>
+                <label class="radio-inline">
+                    <input type="radio" name="proxyProtocol" id="proxyProtocol2" value="HTTPS" ng-model="newRepoSetting.proxyProtocol"/> HTTPS
+                </label>
+              </div>
+            </div>
+            <div class="form-group">
+              <label class="control-label col-sm-2" for="proxyHost">Host</label>
+              <div class="col-sm-10">
+                <input type="text" class="form-control" id="proxyHost" ng-model="newRepoSetting.proxyHost" />
+              </div>
+            </div>
+            <div class="form-group">
+              <label class="control-label col-sm-2" for="proxyPort">Port</label>
+              <div class="col-sm-10">
+                <input type="text" class="form-control" id="proxyPort" ng-model="newRepoSetting.proxyPort" />
+              </div>
+            </div>
+            <div class="form-group">
+                <label class="control-label col-sm-2" for="proxyLogin">Login</label>
+                <div class="col-sm-10">
+                    <input type="text" class="form-control" id="proxyLogin" ng-model="newRepoSetting.proxyLogin" />
+                </div>
+            </div>
+            <div class="form-group">
+                <label class="control-label col-sm-2" for="proxyPassword">Password</label>
+                <div class="col-sm-10">
+                    <input type="text" class="form-control" id="proxyPassword" ng-model="newRepoSetting.proxyPassword" />
+                </div>
+            </div>
+        </div>
+        <div class="modal-footer">
+          <button type="submit"
+                  class="btn btn-default">Add
+          </button>
+          <button type="button" data-dismiss="modal"
+                  class="btn btn-default" ng-click="resetNewRepositorySetting()">Cancel
+          </button>
+        </div>
+      </form>
+    </div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/interpreter/widget/number-widget.directive.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/interpreter/widget/number-widget.directive.js b/zeppelin-web/src/app/interpreter/widget/number-widget.directive.js
new file mode 100644
index 0000000..2046b94
--- /dev/null
+++ b/zeppelin-web/src/app/interpreter/widget/number-widget.directive.js
@@ -0,0 +1,31 @@
+/*
+ * Licensed 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.
+ */
+
+angular.module('zeppelinWebApp').directive('numberWidget', InterpreterNumberDirective)
+
+function InterpreterNumberDirective() {
+  return {
+    require: 'ngModel',
+    link: function (scope, element, attrs, modelCtrl) {
+      modelCtrl.$parsers.push(function (inputValue) {
+        let transformedInput = inputValue ? inputValue.replace(/[^\d.-]/g, '') : null
+        if (transformedInput !== inputValue) {
+          modelCtrl.$setViewValue(transformedInput)
+          modelCtrl.$render()
+        }
+        return transformedInput
+      })
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/jobmanager/jobs/job-progress-bar.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/jobmanager/jobs/job-progress-bar.html b/zeppelin-web/src/app/jobmanager/jobs/job-progress-bar.html
new file mode 100644
index 0000000..00b290b
--- /dev/null
+++ b/zeppelin-web/src/app/jobmanager/jobs/job-progress-bar.html
@@ -0,0 +1,22 @@
+<!--
+Licensed 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.
+-->
+
+<div id="{{notebookJob.noteId}}_runControl" class="runControl">
+  <div id="{{notebookJob.noteId}}_progress" class="progress" ng-if="notebookJob.isRunningJob === true">
+      <div ng-if="getProgress()>0 && getProgress()<100 && notebookJob.isRunningJob === true"
+        class="progress-bar" role="progressbar" ng-style="{width:getProgress()+'%'}"></div>
+      <div ng-if="(getProgress()<=0 || getProgress()>=100) && (notebookJob.isRunningJob === true)"
+          class="progress-bar progress-bar-striped active" role="progressbar" style="width:100%;"></div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/jobmanager/jobs/job-progressBar.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/jobmanager/jobs/job-progressBar.html b/zeppelin-web/src/app/jobmanager/jobs/job-progressBar.html
deleted file mode 100644
index 00b290b..0000000
--- a/zeppelin-web/src/app/jobmanager/jobs/job-progressBar.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Licensed 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.
--->
-
-<div id="{{notebookJob.noteId}}_runControl" class="runControl">
-  <div id="{{notebookJob.noteId}}_progress" class="progress" ng-if="notebookJob.isRunningJob === true">
-      <div ng-if="getProgress()>0 && getProgress()<100 && notebookJob.isRunningJob === true"
-        class="progress-bar" role="progressbar" ng-style="{width:getProgress()+'%'}"></div>
-      <div ng-if="(getProgress()<=0 || getProgress()>=100) && (notebookJob.isRunningJob === true)"
-          class="progress-bar progress-bar-striped active" role="progressbar" style="width:100%;"></div>
-  </div>
-</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/jobmanager/jobs/job.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/jobmanager/jobs/job.html b/zeppelin-web/src/app/jobmanager/jobs/job.html
index e555771..b6dd6be 100644
--- a/zeppelin-web/src/app/jobmanager/jobs/job.html
+++ b/zeppelin-web/src/app/jobmanager/jobs/job.html
@@ -28,7 +28,7 @@ limitations under the License.
       <span ng-if="notebookJob.interpreter !== undefined" style="color: black;">
         {{notebookJob.interpreter}}</span>
     </a>
-    <div ng-include src="'app/jobmanager/jobs/job-progressBar.html'"></div>
+    <div ng-include src="'app/jobmanager/jobs/job-progress-bar.html'"></div>
   </div>
 
   <div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/notebook-repository/notebook-repository.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook-repository/notebook-repository.controller.js b/zeppelin-web/src/app/notebook-repository/notebook-repository.controller.js
new file mode 100644
index 0000000..0f62bc0
--- /dev/null
+++ b/zeppelin-web/src/app/notebook-repository/notebook-repository.controller.js
@@ -0,0 +1,87 @@
+/*
+ * Licensed 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.
+ */
+
+angular.module('zeppelinWebApp').controller('NotebookRepositoryCtrl', NotebookRepositoryCtrl)
+
+function NotebookRepositoryCtrl($http, baseUrlSrv, ngToast) {
+  'ngInject'
+
+  let vm = this
+  vm.notebookRepos = []
+  vm.showDropdownSelected = showDropdownSelected
+  vm.saveNotebookRepo = saveNotebookRepo
+
+  _init()
+
+  // Public functions
+
+  function saveNotebookRepo (valueform, repo, data) {
+    console.log('data %o', data)
+    $http.put(baseUrlSrv.getRestApiBase() + '/notebook-repositories', {
+      'name': repo.className,
+      'settings': data
+    }).success(function (data) {
+      let index = _.findIndex(vm.notebookRepos, {'className': repo.className})
+      if (index >= 0) {
+        vm.notebookRepos[index] = data.body
+        console.log('repos %o, data %o', vm.notebookRepos, data.body)
+      }
+      valueform.$show()
+    }).error(function () {
+      ngToast.danger({
+        content: 'We couldn\'t save that NotebookRepo\'s settings',
+        verticalPosition: 'bottom',
+        timeout: '3000'
+      })
+      valueform.$show()
+    })
+
+    return 'manual'
+  }
+
+  function showDropdownSelected (setting) {
+    let index = _.findIndex(setting.value, {'value': setting.selected})
+    if (index < 0) {
+      return 'No value'
+    } else {
+      return setting.value[index].name
+    }
+  }
+
+  // Private functions
+
+  function _getInterpreterSettings () {
+    $http.get(baseUrlSrv.getRestApiBase() + '/notebook-repositories')
+      .success(function (data, status, headers, config) {
+        vm.notebookRepos = data.body
+        console.log('ya notebookRepos %o', vm.notebookRepos)
+      }).error(function (data, status, headers, config) {
+        if (status === 401) {
+          ngToast.danger({
+            content: 'You don\'t have permission on this page',
+            verticalPosition: 'bottom',
+            timeout: '3000'
+          })
+          setTimeout(function () {
+            window.location = baseUrlSrv.getBase()
+          }, 3000)
+        }
+        console.log('Error %o %o', status, data.message)
+      })
+  }
+
+  function _init () {
+    _getInterpreterSettings()
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/notebook-repository/notebook-repository.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook-repository/notebook-repository.html b/zeppelin-web/src/app/notebook-repository/notebook-repository.html
new file mode 100644
index 0000000..1e30df3
--- /dev/null
+++ b/zeppelin-web/src/app/notebook-repository/notebook-repository.html
@@ -0,0 +1,98 @@
+<!--
+Licensed 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.
+-->
+<div class="interpreterHead">
+  <div class="header">
+    <div class="row">
+      <div class="col-md-12">
+        <h3 class="new_h3">
+          Notebook Repository
+        </h3>
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-md-12">
+        Manage your Notebook Repositories' settings.
+      </div>
+    </div>
+  </div>
+</div>
+
+<div class="box width-full"
+     ng-repeat="repo in noterepo.notebookRepos | orderBy: 'name'">
+  <div id="{{repo.name | lowercase}}">
+    <div class="row interpreter">
+
+      <div class="col-md-12">
+        <h3 class="interpreter-title">{{repo.name}}</h3>
+        <span style="float:right" ng-show="repo.settings.length > 0">
+          <button class="btn btn-default btn-xs"
+                  ng-click="valueform.$show();">
+            <span class="fa fa-pencil"></span> edit</button>
+        </span>
+      </div>
+    </div>
+    <div class="row interpreter">
+      <div class="col-md-12" ng-show="repo.settings.length > 0">
+        <h5>Settings</h5>
+        <table class="table table-striped">
+          <thead>
+            <tr>
+              <th style="width:40%">name</th>
+              <th style="width:60%">value</th>
+            </tr>
+          </thead>
+          <tr ng-repeat="setting in repo.settings" >
+            <td ng-bind="setting.name"></td>
+            <td>
+              <span class="btn-group">
+                <span ng-show="setting.type === 'DROPDOWN'">
+                  <span editable-select="setting.selected"
+                          e-name="{{setting.name}}"
+                          e-ng-options="s.value as s.name for s in setting.value"
+                          class="selectpicker" ng-disabled="!valueform.$visible" e-form="valueform">
+                          {{noterepo.showDropdownSelected(setting)}}
+                  </span>
+                </span>
+                <span ng-show="setting.type === 'INPUT'">
+                  <span editable-textarea="setting.selected" e-name="{{setting.name}}" e-form="valueform" e-msd-elastic e-cols="100">
+                    {{setting.selected | breakFilter}}
+                  </span>
+                </span>
+              </span>
+            </td>
+          </tr>
+        </table>
+      </div>
+    </div>
+    <span style="float:right" ng-show="valueform.$visible">
+      <form editable-form name="valueform"
+            onbeforesave="noterepo.saveNotebookRepo(valueform, repo, $data)"
+            ng-show="valueform.$visible">
+        <button type="submit" class="btn btn-primary btn-xs">
+          <span class="fa fa-check"></span> Save
+        </button>
+        <button type="button" class="btn btn-default btn-xs"
+                ng-disabled="valueform.$waiting"
+                ng-click="valueform.$cancel();">
+          <span class="fa fa-remove"></span> Cancel
+        </button>
+      </form>
+    </span>
+    <div class="row interpreter">
+      <div ng-show="repo.settings.length === 0 || valueform.$hidden" class="col-md-12 gray40-message">
+        <em>Currently there are no settings for this Notebook Repository</em>
+      </div>
+    </div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/notebook/dropdown-input/dropdown-input.directive.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/dropdown-input/dropdown-input.directive.js b/zeppelin-web/src/app/notebook/dropdown-input/dropdown-input.directive.js
new file mode 100644
index 0000000..a64204a
--- /dev/null
+++ b/zeppelin-web/src/app/notebook/dropdown-input/dropdown-input.directive.js
@@ -0,0 +1,26 @@
+/*
+ * Licensed 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.
+ */
+
+angular.module('zeppelinWebApp').directive('dropdownInput', dropdownInputDirective)
+
+function dropdownInputDirective() {
+  return {
+    restrict: 'A',
+    link: function (scope, element) {
+      element.bind('click', function (event) {
+        event.stopPropagation()
+      })
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/notebook/elastic-input/elastic-input.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/elastic-input/elastic-input.controller.js b/zeppelin-web/src/app/notebook/elastic-input/elastic-input.controller.js
new file mode 100644
index 0000000..507b2f6
--- /dev/null
+++ b/zeppelin-web/src/app/notebook/elastic-input/elastic-input.controller.js
@@ -0,0 +1,21 @@
+/*
+ * Licensed 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.
+ */
+
+angular.module('zeppelinWebApp').controller('ElasticInputCtrl', ElasticInputCtrl)
+
+function ElasticInputCtrl () {
+  let vm = this
+  vm.showEditor = false
+  vm.value = ''
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/notebook/note-var-share.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/note-var-share.service.js b/zeppelin-web/src/app/notebook/note-var-share.service.js
new file mode 100644
index 0000000..e79f389
--- /dev/null
+++ b/zeppelin-web/src/app/notebook/note-var-share.service.js
@@ -0,0 +1,39 @@
+/*
+ * Licensed 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.
+ */
+
+angular.module('zeppelinWebApp').service('noteVarShareService', NoteVarShareService)
+
+function NoteVarShareService () {
+  'ngInject'
+
+  let store = {}
+
+  this.clear = function () {
+    store = {}
+  }
+
+  this.put = function (key, value) {
+    store[key] = value
+  }
+
+  this.get = function (key) {
+    return store[key]
+  }
+
+  this.del = function (key) {
+    let v = store[key]
+    delete store[key]
+    return v
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/notebook/notebook-actionBar.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/notebook-actionBar.html b/zeppelin-web/src/app/notebook/notebook-actionBar.html
index 5b600f2..919ddbc 100644
--- a/zeppelin-web/src/app/notebook/notebook-actionBar.html
+++ b/zeppelin-web/src/app/notebook/notebook-actionBar.html
@@ -60,7 +60,7 @@ limitations under the License.
               class="btn btn-default btn-xs"
               ng-hide="viewOnly"
               tooltip-placement="bottom" uib-tooltip="Clone this note" data-source-note-name="{{note.name}}"
-              data-toggle="modal" data-target="#noteNameModal" data-clone="true"
+              data-toggle="modal" data-target="#noteCreateModal" data-clone="true"
               ng-disabled="revisionView">
         <i class="fa fa-copy"></i>
       </button>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/notebook/notebook.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/notebook.controller.js b/zeppelin-web/src/app/notebook/notebook.controller.js
index 8a3a97b..7504807 100644
--- a/zeppelin-web/src/app/notebook/notebook.controller.js
+++ b/zeppelin-web/src/app/notebook/notebook.controller.js
@@ -20,7 +20,7 @@ angular.module('zeppelinWebApp').controller('NotebookCtrl', NotebookCtrl)
 
 function NotebookCtrl ($scope, $route, $routeParams, $location, $rootScope,
                       $http, websocketMsgSrv, baseUrlSrv, $timeout, saveAsService,
-                      ngToast, noteActionSrv, noteVarShareService, TRASH_FOLDER_ID,
+                      ngToast, noteActionService, noteVarShareService, TRASH_FOLDER_ID,
                       heliumService) {
   'ngInject'
 
@@ -165,12 +165,12 @@ function NotebookCtrl ($scope, $route, $routeParams, $location, $rootScope,
 
   // Move the note to trash and go back to the main page
   $scope.moveNoteToTrash = function (noteId) {
-    noteActionSrv.moveNoteToTrash(noteId, true)
+    noteActionService.moveNoteToTrash(noteId, true)
   }
 
   // Remove the note permanently if it's in the trash
   $scope.removeNote = function (noteId) {
-    noteActionSrv.removeNote(noteId, true)
+    noteActionService.removeNote(noteId, true)
   }
 
   $scope.isTrash = function (note) {
@@ -309,7 +309,7 @@ function NotebookCtrl ($scope, $route, $routeParams, $location, $rootScope,
   }
 
   $scope.clearAllParagraphOutput = function (noteId) {
-    noteActionSrv.clearAllParagraphOutput(noteId)
+    noteActionService.clearAllParagraphOutput(noteId)
   }
 
   $scope.toggleAllEditor = function () {

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/notebook/paragraph/clipboard.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/clipboard.controller.js b/zeppelin-web/src/app/notebook/paragraph/clipboard.controller.js
new file mode 100644
index 0000000..0eb78e3
--- /dev/null
+++ b/zeppelin-web/src/app/notebook/paragraph/clipboard.controller.js
@@ -0,0 +1,34 @@
+/*
+ * Licensed 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.
+ */
+angular.module('zeppelinWebApp').controller('clipboardCtrl', ClipboardController)
+
+function ClipboardController ($scope) {
+  'ngInject'
+
+  $scope.complete = function (e) {
+    $scope.copied = true
+    $scope.tooltip = 'Copied!'
+    setTimeout(function () {
+      $scope.tooltip = 'Copy to clipboard'
+    }, 400)
+  }
+  $scope.$watch('input', function () {
+    $scope.copied = false
+    $scope.tooltip = 'Copy to clipboard'
+  })
+  $scope.clipError = function (e) {
+    console.log('Error: ' + e.name + ' - ' + e.message)
+    $scope.tooltip = 'Not supported browser'
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/notebook/paragraph/code-editor/code-editor.directive.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/code-editor/code-editor.directive.html b/zeppelin-web/src/app/notebook/paragraph/code-editor/code-editor.directive.html
new file mode 100644
index 0000000..1c0a1f0
--- /dev/null
+++ b/zeppelin-web/src/app/notebook/paragraph/code-editor/code-editor.directive.html
@@ -0,0 +1,21 @@
+<!--
+Licensed 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.
+-->
+
+<div class="editor"
+     ui-ace="{ onLoad : onLoad, require : ['ace/ext/language_tools'] }"
+     ng-model="paragraph.text"
+     ng-class="{'paragraph-disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' || revisionView === true,
+           'paragraph-text--dirty' : dirtyText !== originalText && dirtyText !== undefined}">
+
+</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/notebook/paragraph/code-editor/code-editor.directive.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/code-editor/code-editor.directive.js b/zeppelin-web/src/app/notebook/paragraph/code-editor/code-editor.directive.js
new file mode 100644
index 0000000..944f05d
--- /dev/null
+++ b/zeppelin-web/src/app/notebook/paragraph/code-editor/code-editor.directive.js
@@ -0,0 +1,38 @@
+/*
+ * Licensed 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.
+ */
+
+angular.module('zeppelinWebApp').directive('codeEditor', CodeEditorDirective)
+
+function CodeEditorDirective($templateRequest, $compile) {
+  return {
+    restrict: 'AE',
+    scope: {
+      paragraphId: '=paragraphId',
+      paragraph: '=paragraphContext',
+      dirtyText: '=dirtyText',
+      originalText: '=originalText',
+      onLoad: '=onLoad',
+      revisionView: '=revisionView'
+    },
+    link: function (scope, element, attrs, controller) {
+      $templateRequest('app/notebook/paragraph/code-editor/code-editor.directive.html').then(function (editorHtml) {
+        let editor = angular.element(editorHtml)
+        editor.attr('id', scope.paragraphId + '_editor')
+        element.append(editor)
+        $compile(editor)(scope)
+        console.debug('codeEditor directive revision view is ' + scope.revisionView)
+      })
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/notebook/paragraph/paragraph-parameterized-query-form.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph-parameterized-query-form.html b/zeppelin-web/src/app/notebook/paragraph/paragraph-parameterized-query-form.html
new file mode 100644
index 0000000..249e7c1
--- /dev/null
+++ b/zeppelin-web/src/app/notebook/paragraph/paragraph-parameterized-query-form.html
@@ -0,0 +1,71 @@
+<!--
+Licensed 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.
+-->
+<form id="{{paragraph.id}}_form" role="form"
+      ng-show="!paragraph.config.tableHide"
+      class=" paragraphForm form-horizontal row">
+  <div class="form-group col-sm-6 col-md-6 col-lg-4"
+       ng-repeat="formulaire in paragraph.settings.forms | toArray"
+       ng-init="loadForm(formulaire, paragraph.settings.params)">
+    <label class="control-label input-sm" ng-class="{'disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' }">{{formulaire.name}}</label>
+    <div>
+      <input class="form-control input-sm"
+             ng-if="paragraph.settings.forms[formulaire.name].type == 'TextBox'"
+             ng-enter="runParagraphFromButton(getEditorValue())"
+             ng-model="paragraph.settings.params[formulaire.name]"
+             ng-class="{'disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' }"
+             name="{{formulaire.name}}" />
+    </div>
+    <div ng-if="paragraph.config.runOnSelectionChange == true">
+      <select class="form-control input-sm"
+             ng-if="paragraph.settings.forms[formulaire.name].type == 'Select'"
+             ng-change="runParagraphFromButton(getEditorValue())"
+             ng-model="paragraph.settings.params[formulaire.name]"
+             ng-class="{'disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' }"
+             name="{{formulaire.name}}"
+             ng-options="option.value as (option.displayName||option.value) for option in paragraph.settings.forms[formulaire.name].options">
+      </select>
+    </div>
+    <div ng-if="paragraph.config.runOnSelectionChange == false">
+      <select class="form-control input-sm"
+             ng-if="paragraph.settings.forms[formulaire.name].type == 'Select'"
+             ng-enter="runParagraphFromButton(getEditorValue())"
+             ng-model="paragraph.settings.params[formulaire.name]"
+             ng-class="{'disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' }"
+             name="{{formulaire.name}}"
+             ng-options="option.value as (option.displayName||option.value) for option in paragraph.settings.forms[formulaire.name].options">
+      </select>
+    </div>
+    <div ng-if="paragraph.config.runOnSelectionChange == true &&
+                paragraph.settings.forms[formulaire.name].type == 'CheckBox'">
+      <label ng-repeat="option in paragraph.settings.forms[formulaire.name].options"
+             class="checkbox-item input-sm">
+        <input type="checkbox"
+               ng-class="{'disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' }"
+               ng-checked="paragraph.settings.params[formulaire.name].indexOf(option.value) > -1"
+               ng-click="toggleCheckbox(formulaire, option, false); runParagraphFromButton(getEditorValue())"/> {{option.displayName||option.value}}
+      </label>
+    </div>
+    <div ng-if="paragraph.config.runOnSelectionChange == false &&
+                paragraph.settings.forms[formulaire.name].type == 'CheckBox'">
+      <label ng-repeat="option in paragraph.settings.forms[formulaire.name].options"
+             class="checkbox-item input-sm">
+        <input type="checkbox"
+               ng-class="{'disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' }"
+               ng-checked="paragraph.settings.params[formulaire.name].indexOf(option.value) > -1"
+               ng-enter="runParagraphFromButton(getEditorValue())"
+               ng-click="toggleCheckbox(formulaire, option, false)"/> {{option.displayName||option.value}}
+      </label>
+    </div>
+  </div>
+</form>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/notebook/paragraph/paragraph-parameterizedQueryForm.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph-parameterizedQueryForm.html b/zeppelin-web/src/app/notebook/paragraph/paragraph-parameterizedQueryForm.html
deleted file mode 100644
index 249e7c1..0000000
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph-parameterizedQueryForm.html
+++ /dev/null
@@ -1,71 +0,0 @@
-<!--
-Licensed 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.
--->
-<form id="{{paragraph.id}}_form" role="form"
-      ng-show="!paragraph.config.tableHide"
-      class=" paragraphForm form-horizontal row">
-  <div class="form-group col-sm-6 col-md-6 col-lg-4"
-       ng-repeat="formulaire in paragraph.settings.forms | toArray"
-       ng-init="loadForm(formulaire, paragraph.settings.params)">
-    <label class="control-label input-sm" ng-class="{'disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' }">{{formulaire.name}}</label>
-    <div>
-      <input class="form-control input-sm"
-             ng-if="paragraph.settings.forms[formulaire.name].type == 'TextBox'"
-             ng-enter="runParagraphFromButton(getEditorValue())"
-             ng-model="paragraph.settings.params[formulaire.name]"
-             ng-class="{'disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' }"
-             name="{{formulaire.name}}" />
-    </div>
-    <div ng-if="paragraph.config.runOnSelectionChange == true">
-      <select class="form-control input-sm"
-             ng-if="paragraph.settings.forms[formulaire.name].type == 'Select'"
-             ng-change="runParagraphFromButton(getEditorValue())"
-             ng-model="paragraph.settings.params[formulaire.name]"
-             ng-class="{'disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' }"
-             name="{{formulaire.name}}"
-             ng-options="option.value as (option.displayName||option.value) for option in paragraph.settings.forms[formulaire.name].options">
-      </select>
-    </div>
-    <div ng-if="paragraph.config.runOnSelectionChange == false">
-      <select class="form-control input-sm"
-             ng-if="paragraph.settings.forms[formulaire.name].type == 'Select'"
-             ng-enter="runParagraphFromButton(getEditorValue())"
-             ng-model="paragraph.settings.params[formulaire.name]"
-             ng-class="{'disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' }"
-             name="{{formulaire.name}}"
-             ng-options="option.value as (option.displayName||option.value) for option in paragraph.settings.forms[formulaire.name].options">
-      </select>
-    </div>
-    <div ng-if="paragraph.config.runOnSelectionChange == true &&
-                paragraph.settings.forms[formulaire.name].type == 'CheckBox'">
-      <label ng-repeat="option in paragraph.settings.forms[formulaire.name].options"
-             class="checkbox-item input-sm">
-        <input type="checkbox"
-               ng-class="{'disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' }"
-               ng-checked="paragraph.settings.params[formulaire.name].indexOf(option.value) > -1"
-               ng-click="toggleCheckbox(formulaire, option, false); runParagraphFromButton(getEditorValue())"/> {{option.displayName||option.value}}
-      </label>
-    </div>
-    <div ng-if="paragraph.config.runOnSelectionChange == false &&
-                paragraph.settings.forms[formulaire.name].type == 'CheckBox'">
-      <label ng-repeat="option in paragraph.settings.forms[formulaire.name].options"
-             class="checkbox-item input-sm">
-        <input type="checkbox"
-               ng-class="{'disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' }"
-               ng-checked="paragraph.settings.params[formulaire.name].indexOf(option.value) > -1"
-               ng-enter="runParagraphFromButton(getEditorValue())"
-               ng-click="toggleCheckbox(formulaire, option, false)"/> {{option.displayName||option.value}}
-      </label>
-    </div>
-  </div>
-</form>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/notebook/paragraph/paragraph-progress-bar.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph-progress-bar.html b/zeppelin-web/src/app/notebook/paragraph/paragraph-progress-bar.html
new file mode 100644
index 0000000..b344176
--- /dev/null
+++ b/zeppelin-web/src/app/notebook/paragraph/paragraph-progress-bar.html
@@ -0,0 +1,22 @@
+<!--
+Licensed 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.
+-->
+
+<div id="{{paragraph.id}}_runControl" class="runControl">
+  <div id="{{paragraph.id}}_progress" class="progress" ng-if="paragraph.status=='RUNNING'">
+      <div ng-if="getProgress()>0 && getProgress()<100 && paragraph.status=='RUNNING'"
+        class="progress-bar" role="progressbar" style="width:{{getProgress()}}%;"></div>
+      <div ng-if="(getProgress()<=0 || getProgress()>=100) && (paragraph.status=='RUNNING' )"
+          class="progress-bar progress-bar-striped active" role="progressbar" style="width:100%;"></div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/notebook/paragraph/paragraph-progressBar.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph-progressBar.html b/zeppelin-web/src/app/notebook/paragraph/paragraph-progressBar.html
deleted file mode 100644
index b344176..0000000
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph-progressBar.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Licensed 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.
--->
-
-<div id="{{paragraph.id}}_runControl" class="runControl">
-  <div id="{{paragraph.id}}_progress" class="progress" ng-if="paragraph.status=='RUNNING'">
-      <div ng-if="getProgress()>0 && getProgress()<100 && paragraph.status=='RUNNING'"
-        class="progress-bar" role="progressbar" style="width:{{getProgress()}}%;"></div>
-      <div ng-if="(getProgress()<=0 || getProgress()>=100) && (paragraph.status=='RUNNING' )"
-          class="progress-bar progress-bar-striped active" role="progressbar" style="width:100%;"></div>
-  </div>
-</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
index ef61d08..8f55d1f 100644
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
+++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
@@ -30,7 +30,7 @@ angular.module('zeppelinWebApp').controller('ParagraphCtrl', ParagraphCtrl)
 
 function ParagraphCtrl ($scope, $rootScope, $route, $window, $routeParams, $location,
                        $timeout, $compile, $http, $q, websocketMsgSrv,
-                       baseUrlSrv, ngToast, saveAsService, noteVarShareService,
+                       baseUrlSrv, ngToast, noteVarShareService,
                        heliumService) {
   'ngInject'
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/notebook/paragraph/paragraph.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.html b/zeppelin-web/src/app/notebook/paragraph/paragraph.html
index 0de5e64..754a452 100644
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph.html
+++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.html
@@ -47,8 +47,8 @@ limitations under the License.
       ></code-editor>
     </div>
 
-    <div ng-include src="'app/notebook/paragraph/paragraph-progressBar.html'"></div>
-    <div ng-include src="'app/notebook/paragraph/paragraph-parameterizedQueryForm.html'"></div>
+    <div ng-include src="'app/notebook/paragraph/paragraph-progress-bar.html'"></div>
+    <div ng-include src="'app/notebook/paragraph/paragraph-parameterized-query-form.html'"></div>
 
     <!-- Rendering -->
     <div class="tableDisplay"

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/notebook/paragraph/resizable.directive.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/resizable.directive.js b/zeppelin-web/src/app/notebook/paragraph/resizable.directive.js
new file mode 100644
index 0000000..2893cd5
--- /dev/null
+++ b/zeppelin-web/src/app/notebook/paragraph/resizable.directive.js
@@ -0,0 +1,69 @@
+/*
+ * Licensed 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.
+ */
+
+angular.module('zeppelinWebApp').directive('resizable', ResizableDirective)
+
+function ResizableDirective () {
+  let resizableConfig = {
+    autoHide: true,
+    handles: 'se',
+    helper: 'resizable-helper',
+    stop: function () {
+      angular.element(this).css({'width': '100%', 'height': '100%'})
+    }
+  }
+
+  return {
+    restrict: 'A',
+    scope: {
+      callback: '&onResize'
+    },
+    link: function postLink (scope, elem, attrs) {
+      attrs.$observe('resize', function (resize) {
+        let resetResize = function (elem, resize) {
+          let colStep = window.innerWidth / 12
+          elem.off('resizestop')
+          let conf = angular.copy(resizableConfig)
+          if (resize.graphType === 'TABLE' || resize.graphType === 'NETWORK' || resize.graphType === 'TEXT') {
+            conf.grid = [colStep, 10]
+            conf.minHeight = 100
+          } else {
+            conf.grid = [colStep, 10000]
+            conf.minHeight = 0
+          }
+          conf.maxWidth = window.innerWidth
+
+          elem.resizable(conf)
+          elem.on('resizestop', function () {
+            if (scope.callback) {
+              let height = elem.height()
+              if (height < 50) {
+                height = 300
+              }
+              scope.callback({width: Math.ceil(elem.width() / colStep), height: height})
+            }
+          })
+        }
+
+        resize = JSON.parse(resize)
+        if (resize.allowresize === 'true') {
+          resetResize(elem, resize)
+          angular.element(window).resize(function () {
+            resetResize(elem, resize)
+          })
+        }
+      })
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/notebook/save-as/browser-detect.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/save-as/browser-detect.service.js b/zeppelin-web/src/app/notebook/save-as/browser-detect.service.js
new file mode 100644
index 0000000..a31e9af
--- /dev/null
+++ b/zeppelin-web/src/app/notebook/save-as/browser-detect.service.js
@@ -0,0 +1,39 @@
+/*
+ * Licensed 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.
+ */
+
+angular.module('zeppelinWebApp').service('browserDetectService', BrowserDetectService)
+
+function BrowserDetectService () {
+  this.detectIE = function () {
+    let ua = window.navigator.userAgent
+    let msie = ua.indexOf('MSIE ')
+    if (msie > 0) {
+      // IE 10 or older => return version number
+      return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10)
+    }
+    let trident = ua.indexOf('Trident/')
+    if (trident > 0) {
+      // IE 11 => return version number
+      let rv = ua.indexOf('rv:')
+      return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10)
+    }
+    let edge = ua.indexOf('Edge/')
+    if (edge > 0) {
+      // IE 12 (aka Edge) => return version number
+      return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10)
+    }
+    // other browser
+    return false
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/notebook/save-as/save-as.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/save-as/save-as.service.js b/zeppelin-web/src/app/notebook/save-as/save-as.service.js
new file mode 100644
index 0000000..c71c0f7
--- /dev/null
+++ b/zeppelin-web/src/app/notebook/save-as/save-as.service.js
@@ -0,0 +1,51 @@
+/*
+ * Licensed 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.
+ */
+
+angular.module('zeppelinWebApp').service('saveAsService', SaveAsService)
+
+function SaveAsService (browserDetectService) {
+  'ngInject'
+
+  this.saveAs = function (content, filename, extension) {
+    let BOM = '\uFEFF'
+    if (browserDetectService.detectIE()) {
+      angular.element('body').append('<iframe id="SaveAsId" style="display: none"></iframe>')
+      let frameSaveAs = angular.element('body > iframe#SaveAsId')[0].contentWindow
+      content = BOM + content
+      frameSaveAs.document.open('text/json', 'replace')
+      frameSaveAs.document.write(content)
+      frameSaveAs.document.close()
+      frameSaveAs.focus()
+      let t1 = Date.now()
+      frameSaveAs.document.execCommand('SaveAs', false, filename + '.' + extension)
+      let t2 = Date.now()
+
+      // This means, this version of IE dosen't support auto download of a file with extension provided in param
+      // falling back to ".txt"
+      if (t1 === t2) {
+        frameSaveAs.document.execCommand('SaveAs', true, filename + '.txt')
+      }
+      angular.element('body > iframe#SaveAsId').remove()
+    } else {
+      content = 'data:image/svg;charset=utf-8,' + BOM + encodeURIComponent(content)
+      angular.element('body').append('<a id="SaveAsId"></a>')
+      let saveAsElement = angular.element('body > a#SaveAsId')
+      saveAsElement.attr('href', content)
+      saveAsElement.attr('download', filename + '.' + extension)
+      saveAsElement.attr('target', '_blank')
+      saveAsElement[0].click()
+      saveAsElement.remove()
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/notebook/shortcut.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/shortcut.html b/zeppelin-web/src/app/notebook/shortcut.html
new file mode 100644
index 0000000..775da4f
--- /dev/null
+++ b/zeppelin-web/src/app/notebook/shortcut.html
@@ -0,0 +1,312 @@
+<!--
+Licensed 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.
+-->
+
+<div class="modal fade" id="shortcutModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
+  <div class="modal-dialog">
+    <div class="modal-content">
+      <div class="shortcut-modal-header">
+        <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
+        <h4 class="shortcut-modal-title" id="myModalLabel">Keyboard shortcuts</h4>
+      </div>
+      <div class="table-scroll">
+        <table class="table table-shortcut">
+          <tr>
+            <th style="width:70%">Note Keyboard Shortcuts</th>
+            <th></th>
+          </tr>
+
+          <tr>
+            <td>
+              <div class="col-md-8">Run paragraph</div>
+           </td>
+           <td>
+              <div class="keys">
+                <kbd class="kbd-default">Shift</kbd> + <kbd class="kbd-default">Enter</kbd>
+              </div>
+            </td>
+          </tr>
+
+          <tr>
+            <td>
+              <div class="col-md-8">Cancel</div>
+            </td>
+            <td>
+              <div class="keys">
+                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt'}}</kbd> + <kbd class="kbd-default">C</kbd>
+              </div>
+            </td>
+          </tr>
+
+          <tr>
+            <td>
+              <div class="col-md-8">Move cursor Up</div>
+            </td>
+            <td>
+              <div class="keys">
+                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">P</kbd>
+              </div>
+            </td>
+          </tr>
+
+          <tr>
+            <td>
+              <div class="col-md-8">Move cursor Down</div>
+            </td>
+            <td>
+              <div class="keys">
+                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">N</kbd>
+              </div>
+            </td>
+          </tr>
+
+        <tr>
+          <td>
+            <div class="col-md-8">Remove paragraph</div>
+          </td>
+          <td>
+            <div class="keys">
+              <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt'}}</kbd> + <kbd class="kbd-default">D</kbd>
+            </div>
+          </td>
+        </tr>
+
+        <tr>
+          <td>
+            <div class="col-md-8">Insert new paragraph above</div>
+          </td>
+          <td>
+            <div class="keys">
+              <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt'}}</kbd> + <kbd class="kbd-default">A</kbd>
+            </div>
+          </td>
+        </tr>
+
+        <tr>
+          <td>
+            <div class="col-md-8">Insert new paragraph below</div>
+          </td>
+          <td>
+            <div class="keys">
+              <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt'}}</kbd> + <kbd class="kbd-default">B</kbd>
+            </div>
+          </td>
+        </tr>
+
+        <tr>
+          <td>
+            <div class="col-md-8">Insert copy of paragraph below</div>
+          </td>
+          <td>
+            <div class="keys">
+              <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">Shift</kbd> + <kbd class="kbd-default">C</kbd>
+            </div>
+          </td>
+        </tr>
+
+        <tr>
+          <td>
+            <div class="col-md-8">Move paragraph Up</div>
+          </td>
+          <td>
+            <div class="keys">
+              <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt'}}</kbd> + <kbd class="kbd-default">K</kbd>
+            </div>
+          </td>
+        </tr>
+
+        <tr>
+          <td>
+            <div class="col-md-8">Move paragraph Down</div>
+          </td>
+          <td>
+            <div class="keys">
+              <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt'}}</kbd> + <kbd class="kbd-default">J</kbd>
+            </div>
+          </td>
+        </tr>
+
+        <tr>
+          <td>
+            <div class="col-md-8">Enable/Disable run paragraph</div>
+          </td>
+          <td>
+            <div class="keys">
+              <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt' }}</kbd> + <kbd class="kbd-default">R</kbd>
+            </div>
+          </td>
+        </tr>
+
+        <tr>
+          <td>
+            <div class="col-md-8">Toggle output</div>
+            </td>
+            <td>
+              <div class="keys">
+                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt'}}</kbd> + <kbd class="kbd-default">O</kbd>
+              </div>
+            </td>
+          </tr>
+
+          <tr>
+            <td>
+              <div class="col-md-8">Toggle editor</div>
+            </td>
+            <td>
+              <div class="keys">
+                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt'}}</kbd> + <kbd class="kbd-default">E</kbd>
+              </div>
+            </td>
+          </tr>
+
+          <tr>
+            <td>
+              <div class="col-md-8">Toggle line number</div>
+            </td>
+            <td>
+              <div class="keys">
+                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt'}}</kbd> + <kbd class="kbd-default">M</kbd>
+              </div>
+            </td>
+          </tr>
+
+          <tr>
+            <td>
+              <div class="col-md-8">Toggle title</div>
+            </td>
+            <td>
+              <div class="keys">
+                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt'}}</kbd> + <kbd class="kbd-default">T</kbd>
+              </div>
+            </td>
+          </tr>
+
+          <tr>
+            <td>
+              <div class="col-md-8">Clear output</div>
+            </td>
+            <td>
+              <div class="keys">
+                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt'}}</kbd> + <kbd class="kbd-default">L</kbd>
+              </div>
+            </td>
+          </tr>
+
+          <tr>
+            <td>
+              <div class="col-md-8">Link this paragraph</div>
+            </td>
+            <td>
+              <div class="keys">
+                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">{{ isMac ? 'Option' : 'Alt'}}</kbd> + <kbd class="kbd-default">W</kbd>
+              </div>
+            </td>
+          </tr>
+
+          <tr>
+            <td>
+              <div class="col-md-8">Reduce paragraph width</div>
+            </td>
+            <td>
+              <div class="keys">
+                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">Shift</kbd> + <kbd class="kbd-default">-</kbd>
+              </div>
+            </td>
+          </tr>
+
+          <tr>
+            <td>
+              <div class="col-md-8">Increase paragraph width</div>
+            </td>
+            <td>
+              <div class="keys">
+                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">Shift</kbd> + <kbd class="kbd-default">+</kbd>
+              </div>
+            </td>
+          </tr>
+
+          <tr class="sub-title">
+            <th style="width:70%">Editor Keyboard Shortcuts</th>
+            <th></th>
+          </tr>
+
+          <tr>
+            <td>
+              <div class="col-md-8">Auto-completion</div>
+            </td>
+            <td>
+              <div class="keys">
+                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">.</kbd>
+              </div>
+            </td>
+          </tr>
+
+          <tr>
+            <td>
+              <div class="col-md-8">Cut the line</div>
+            </td>
+            <td>
+              <div class="keys">
+                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">K</kbd>
+              </div>
+            </td>
+          </tr>
+
+          <tr>
+            <td>
+              <div class="col-md-8">Paste the line</div>
+            </td>
+            <td>
+              <div class="keys">
+                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">Y</kbd>
+              </div>
+            </td>
+          </tr>
+
+          <tr>
+            <td>
+              <div class="col-md-8">Search inside the code</div>
+            </td>
+            <td>
+              <div class="keys">
+                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">S</kbd>
+              </div>
+            </td>
+          </tr>
+
+          <tr>
+            <td>
+              <div class="col-md-8">Move cursor to the beginning</div>
+            </td>
+            <td>
+              <div class="keys">
+                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">A</kbd>
+              </div>
+            </td>
+          </tr>
+
+          <tr>
+            <td>
+              <div class="col-md-8">Move cursor at the end</div>
+            </td>
+            <td>
+              <div class="keys">
+                <kbd class="kbd-default">Ctrl</kbd> + <kbd class="kbd-default">E</kbd>
+              </div>
+            </td>
+          </tr>
+        </table>
+      </div>
+    </div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/notebookRepos/notebookRepos.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebookRepos/notebookRepos.controller.js b/zeppelin-web/src/app/notebookRepos/notebookRepos.controller.js
deleted file mode 100644
index 9ae12e4..0000000
--- a/zeppelin-web/src/app/notebookRepos/notebookRepos.controller.js
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Licensed 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.
- */
-
-angular.module('zeppelinWebApp').controller('NotebookReposCtrl', NotebookReposCtrl)
-
-function NotebookReposCtrl ($http, baseUrlSrv, ngToast) {
-  'ngInject'
-
-  let vm = this
-  vm.notebookRepos = []
-  vm.showDropdownSelected = showDropdownSelected
-  vm.saveNotebookRepo = saveNotebookRepo
-
-  _init()
-
-  // Public functions
-
-  function saveNotebookRepo (valueform, repo, data) {
-    console.log('data %o', data)
-    $http.put(baseUrlSrv.getRestApiBase() + '/notebook-repositories', {
-      'name': repo.className,
-      'settings': data
-    }).success(function (data) {
-      let index = _.findIndex(vm.notebookRepos, {'className': repo.className})
-      if (index >= 0) {
-        vm.notebookRepos[index] = data.body
-        console.log('repos %o, data %o', vm.notebookRepos, data.body)
-      }
-      valueform.$show()
-    }).error(function () {
-      ngToast.danger({
-        content: 'We couldn\'t save that NotebookRepo\'s settings',
-        verticalPosition: 'bottom',
-        timeout: '3000'
-      })
-      valueform.$show()
-    })
-
-    return 'manual'
-  }
-
-  function showDropdownSelected (setting) {
-    let index = _.findIndex(setting.value, {'value': setting.selected})
-    if (index < 0) {
-      return 'No value'
-    } else {
-      return setting.value[index].name
-    }
-  }
-
-  // Private functions
-
-  function _getInterpreterSettings () {
-    $http.get(baseUrlSrv.getRestApiBase() + '/notebook-repositories')
-      .success(function (data, status, headers, config) {
-        vm.notebookRepos = data.body
-        console.log('ya notebookRepos %o', vm.notebookRepos)
-      }).error(function (data, status, headers, config) {
-        if (status === 401) {
-          ngToast.danger({
-            content: 'You don\'t have permission on this page',
-            verticalPosition: 'bottom',
-            timeout: '3000'
-          })
-          setTimeout(function () {
-            window.location = baseUrlSrv.getBase()
-          }, 3000)
-        }
-        console.log('Error %o %o', status, data.message)
-      })
-  }
-
-  function _init () {
-    _getInterpreterSettings()
-  }
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/notebookRepos/notebookRepos.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebookRepos/notebookRepos.html b/zeppelin-web/src/app/notebookRepos/notebookRepos.html
deleted file mode 100644
index 65ca372..0000000
--- a/zeppelin-web/src/app/notebookRepos/notebookRepos.html
+++ /dev/null
@@ -1,98 +0,0 @@
-<!--
-Licensed 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.
--->
-<div class="interpreterHead">
-  <div class="header">
-    <div class="row">
-      <div class="col-md-12">
-        <h3 class="new_h3">
-          Notebook Repos
-        </h3>
-      </div>
-    </div>
-    <div class="row">
-      <div class="col-md-12">
-        Manage your Notebook Repositories' settings.
-      </div>
-    </div>
-  </div>
-</div>
-
-<div class="box width-full"
-     ng-repeat="repo in noterepo.notebookRepos | orderBy: 'name'">
-  <div id="{{repo.name | lowercase}}">
-    <div class="row interpreter">
-
-      <div class="col-md-12">
-        <h3 class="interpreter-title">{{repo.name}}</h3>
-        <span style="float:right" ng-show="repo.settings.length > 0">
-          <button class="btn btn-default btn-xs"
-                  ng-click="valueform.$show();">
-            <span class="fa fa-pencil"></span> edit</button>
-        </span>
-      </div>
-    </div>
-    <div class="row interpreter">
-      <div class="col-md-12" ng-show="repo.settings.length > 0">
-        <h5>Settings</h5>
-        <table class="table table-striped">
-          <thead>
-            <tr>
-              <th style="width:40%">name</th>
-              <th style="width:60%">value</th>
-            </tr>
-          </thead>
-          <tr ng-repeat="setting in repo.settings" >
-            <td ng-bind="setting.name"></td>
-            <td>
-              <span class="btn-group">
-                <span ng-show="setting.type === 'DROPDOWN'">
-                  <span editable-select="setting.selected"
-                          e-name="{{setting.name}}"
-                          e-ng-options="s.value as s.name for s in setting.value"
-                          class="selectpicker" ng-disabled="!valueform.$visible" e-form="valueform">
-                          {{noterepo.showDropdownSelected(setting)}}
-                  </span>
-                </span>
-                <span ng-show="setting.type === 'INPUT'">
-                  <span editable-textarea="setting.selected" e-name="{{setting.name}}" e-form="valueform" e-msd-elastic e-cols="100">
-                    {{setting.selected | breakFilter}}
-                  </span>
-                </span>
-              </span>
-            </td>
-          </tr>
-        </table>
-      </div>
-    </div>
-    <span style="float:right" ng-show="valueform.$visible">
-      <form editable-form name="valueform"
-            onbeforesave="noterepo.saveNotebookRepo(valueform, repo, $data)"
-            ng-show="valueform.$visible">
-        <button type="submit" class="btn btn-primary btn-xs">
-          <span class="fa fa-check"></span> Save
-        </button>
-        <button type="button" class="btn btn-default btn-xs"
-                ng-disabled="valueform.$waiting"
-                ng-click="valueform.$cancel();">
-          <span class="fa fa-remove"></span> Cancel
-        </button>
-      </form>
-    </span>
-    <div class="row interpreter">
-      <div ng-show="repo.settings.length === 0 || valueform.$hidden" class="col-md-12 gray40-message">
-        <em>Currently there are no settings for this Notebook Repository</em>
-      </div>
-    </div>
-  </div>
-</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/search/search.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/search/search.service.js b/zeppelin-web/src/app/search/search.service.js
new file mode 100644
index 0000000..fe4b666
--- /dev/null
+++ b/zeppelin-web/src/app/search/search.service.js
@@ -0,0 +1,33 @@
+/*
+ * Licensed 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.
+ */
+
+angular.module('zeppelinWebApp').service('searchService', SearchService)
+
+function SearchService ($resource, baseUrlSrv) {
+  'ngInject'
+
+  this.search = function (term) {
+    this.searchTerm = term.q
+    console.log('Searching for: %o', term.q)
+    if (!term.q) { // TODO(bzz): empty string check
+      return
+    }
+    let encQuery = window.encodeURIComponent(term.q)
+    return $resource(baseUrlSrv.getRestApiBase() + '/notebook/search?q=' + encQuery, {}, {
+      query: {method: 'GET'}
+    })
+  }
+
+  this.searchTerm = ''
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/array-ordering/array-ordering.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/array-ordering/array-ordering.service.js b/zeppelin-web/src/components/array-ordering/array-ordering.service.js
new file mode 100644
index 0000000..850a5da
--- /dev/null
+++ b/zeppelin-web/src/components/array-ordering/array-ordering.service.js
@@ -0,0 +1,62 @@
+/*
+ * Licensed 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.
+ */
+
+angular.module('zeppelinWebApp').service('arrayOrderingSrv', ArrayOrderingService)
+
+function ArrayOrderingService(TRASH_FOLDER_ID) {
+  'ngInject'
+
+  let arrayOrderingSrv = this
+
+  this.noteListOrdering = function (note) {
+    if (note.id === TRASH_FOLDER_ID) {
+      return '\uFFFF'
+    }
+    return arrayOrderingSrv.getNoteName(note)
+  }
+
+  this.getNoteName = function (note) {
+    if (note.name === undefined || note.name.trim() === '') {
+      return 'Note ' + note.id
+    } else {
+      return note.name
+    }
+  }
+
+  this.noteComparator = function (v1, v2) {
+    let note1 = v1.value
+    let note2 = v2.value
+
+    if (note1.id === TRASH_FOLDER_ID) {
+      return 1
+    }
+
+    if (note2.id === TRASH_FOLDER_ID) {
+      return -1
+    }
+
+    if (note1.children === undefined && note2.children !== undefined) {
+      return 1
+    }
+
+    if (note1.children !== undefined && note2.children === undefined) {
+      return -1
+    }
+
+    let noteName1 = arrayOrderingSrv.getNoteName(note1)
+    let noteName2 = arrayOrderingSrv.getNoteName(note2)
+
+    return noteName1.localeCompare(noteName2)
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/arrayOrderingSrv/arrayOrdering.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/arrayOrderingSrv/arrayOrdering.service.js b/zeppelin-web/src/components/arrayOrderingSrv/arrayOrdering.service.js
deleted file mode 100644
index ead2824..0000000
--- a/zeppelin-web/src/components/arrayOrderingSrv/arrayOrdering.service.js
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Licensed 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.
- */
-
-angular.module('zeppelinWebApp').service('arrayOrderingSrv', arrayOrderingSrv)
-
-function arrayOrderingSrv (TRASH_FOLDER_ID) {
-  'ngInject'
-
-  let arrayOrderingSrv = this
-
-  this.noteListOrdering = function (note) {
-    if (note.id === TRASH_FOLDER_ID) {
-      return '\uFFFF'
-    }
-    return arrayOrderingSrv.getNoteName(note)
-  }
-
-  this.getNoteName = function (note) {
-    if (note.name === undefined || note.name.trim() === '') {
-      return 'Note ' + note.id
-    } else {
-      return note.name
-    }
-  }
-
-  this.noteComparator = function (v1, v2) {
-    let note1 = v1.value
-    let note2 = v2.value
-
-    if (note1.id === TRASH_FOLDER_ID) {
-      return 1
-    }
-
-    if (note2.id === TRASH_FOLDER_ID) {
-      return -1
-    }
-
-    if (note1.children === undefined && note2.children !== undefined) {
-      return 1
-    }
-
-    if (note1.children !== undefined && note2.children === undefined) {
-      return -1
-    }
-
-    let noteName1 = arrayOrderingSrv.getNoteName(note1)
-    let noteName2 = arrayOrderingSrv.getNoteName(note2)
-
-    return noteName1.localeCompare(noteName2)
-  }
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/base-url/base-url.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/base-url/base-url.service.js b/zeppelin-web/src/components/base-url/base-url.service.js
new file mode 100644
index 0000000..6ef55b9
--- /dev/null
+++ b/zeppelin-web/src/components/base-url/base-url.service.js
@@ -0,0 +1,50 @@
+/*
+ * Licensed 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.
+ */
+
+angular.module('zeppelinWebApp').service('baseUrlSrv', BaseUrlService)
+
+function BaseUrlService() {
+  this.getPort = function () {
+    let port = Number(location.port)
+    if (!port) {
+      port = 80
+      if (location.protocol === 'https:') {
+        port = 443
+      }
+    }
+    // Exception for when running locally via grunt
+    if (port === process.env.WEB_PORT) {
+      port = process.env.SERVER_PORT
+    }
+    return port
+  }
+
+  this.getWebsocketUrl = function () {
+    let wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:'
+    return wsProtocol + '//' + location.hostname + ':' + this.getPort() +
+      skipTrailingSlash(location.pathname) + '/ws'
+  }
+
+  this.getBase = function() {
+    return location.protocol + '//' + location.hostname + ':' + this.getPort() + location.pathname
+  }
+
+  this.getRestApiBase = function () {
+    return skipTrailingSlash(this.getBase()) + '/api'
+  }
+
+  const skipTrailingSlash = function (path) {
+    return path.replace(/\/$/, '')
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/baseUrl/baseUrl.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/baseUrl/baseUrl.service.js b/zeppelin-web/src/components/baseUrl/baseUrl.service.js
deleted file mode 100644
index 5b8824a..0000000
--- a/zeppelin-web/src/components/baseUrl/baseUrl.service.js
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed 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.
- */
-
-angular.module('zeppelinWebApp').service('baseUrlSrv', baseUrlSrv)
-
-function baseUrlSrv () {
-  this.getPort = function () {
-    let port = Number(location.port)
-    if (!port) {
-      port = 80
-      if (location.protocol === 'https:') {
-        port = 443
-      }
-    }
-    // Exception for when running locally via grunt
-    if (port === process.env.WEB_PORT) {
-      port = process.env.SERVER_PORT
-    }
-    return port
-  }
-
-  this.getWebsocketUrl = function () {
-    let wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:'
-    return wsProtocol + '//' + location.hostname + ':' + this.getPort() +
-      skipTrailingSlash(location.pathname) + '/ws'
-  }
-
-  this.getBase = function() {
-    return location.protocol + '//' + location.hostname + ':' + this.getPort() + location.pathname
-  }
-
-  this.getRestApiBase = function () {
-    return skipTrailingSlash(this.getBase()) + '/api'
-  }
-
-  const skipTrailingSlash = function (path) {
-    return path.replace(/\/$/, '')
-  }
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/browser-detect/browserDetect.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/browser-detect/browserDetect.service.js b/zeppelin-web/src/components/browser-detect/browserDetect.service.js
deleted file mode 100644
index 2223d5f..0000000
--- a/zeppelin-web/src/components/browser-detect/browserDetect.service.js
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Licensed 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.
- */
-
-angular.module('zeppelinWebApp').service('browserDetectService', browserDetectService)
-
-function browserDetectService () {
-  this.detectIE = function () {
-    let ua = window.navigator.userAgent
-    let msie = ua.indexOf('MSIE ')
-    if (msie > 0) {
-      // IE 10 or older => return version number
-      return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10)
-    }
-    let trident = ua.indexOf('Trident/')
-    if (trident > 0) {
-      // IE 11 => return version number
-      let rv = ua.indexOf('rv:')
-      return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10)
-    }
-    let edge = ua.indexOf('Edge/')
-    if (edge > 0) {
-      // IE 12 (aka Edge) => return version number
-      return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10)
-    }
-    // other browser
-    return false
-  }
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/clipboard/clipboard.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/clipboard/clipboard.controller.js b/zeppelin-web/src/components/clipboard/clipboard.controller.js
deleted file mode 100644
index c4a9e42..0000000
--- a/zeppelin-web/src/components/clipboard/clipboard.controller.js
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Licensed 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.
- */
-angular.module('zeppelinWebApp').controller('clipboardCtrl', clipboardCtrl)
-
-function clipboardCtrl ($scope) {
-  'ngInject'
-
-  $scope.complete = function (e) {
-    $scope.copied = true
-    $scope.tooltip = 'Copied!'
-    setTimeout(function () {
-      $scope.tooltip = 'Copy to clipboard'
-    }, 400)
-  }
-  $scope.$watch('input', function () {
-    $scope.copied = false
-    $scope.tooltip = 'Copy to clipboard'
-  })
-  $scope.clipError = function (e) {
-    console.log('Error: ' + e.name + ' - ' + e.message)
-    $scope.tooltip = 'Not supported browser'
-  }
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/dropdowninput/dropdowninput.directive.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/dropdowninput/dropdowninput.directive.js b/zeppelin-web/src/components/dropdowninput/dropdowninput.directive.js
deleted file mode 100644
index cdc74f3..0000000
--- a/zeppelin-web/src/components/dropdowninput/dropdowninput.directive.js
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Licensed 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.
- */
-
-angular.module('zeppelinWebApp').directive('dropdownInput', dropdownInput)
-
-function dropdownInput () {
-  return {
-    restrict: 'A',
-    link: function (scope, element) {
-      element.bind('click', function (event) {
-        event.stopPropagation()
-      })
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/editor/ace.editor.directive.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/editor/ace.editor.directive.html b/zeppelin-web/src/components/editor/ace.editor.directive.html
deleted file mode 100644
index 1c0a1f0..0000000
--- a/zeppelin-web/src/components/editor/ace.editor.directive.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<!--
-Licensed 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.
--->
-
-<div class="editor"
-     ui-ace="{ onLoad : onLoad, require : ['ace/ext/language_tools'] }"
-     ng-model="paragraph.text"
-     ng-class="{'paragraph-disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' || revisionView === true,
-           'paragraph-text--dirty' : dirtyText !== originalText && dirtyText !== undefined}">
-
-</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/components/editor/codeEditor.directive.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/editor/codeEditor.directive.js b/zeppelin-web/src/components/editor/codeEditor.directive.js
deleted file mode 100644
index d8cb73f..0000000
--- a/zeppelin-web/src/components/editor/codeEditor.directive.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Licensed 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.
- */
-
-angular.module('zeppelinWebApp').directive('codeEditor', codeEditor)
-
-function codeEditor ($templateRequest, $compile) {
-  return {
-    restrict: 'AE',
-    scope: {
-      paragraphId: '=paragraphId',
-      paragraph: '=paragraphContext',
-      dirtyText: '=dirtyText',
-      originalText: '=originalText',
-      onLoad: '=onLoad',
-      revisionView: '=revisionView'
-    },
-    link: function (scope, element, attrs, controller) {
-      $templateRequest('components/editor/ace.editor.directive.html').then(function (editorHtml) {
-        let editor = angular.element(editorHtml)
-        editor.attr('id', scope.paragraphId + '_editor')
-        element.append(editor)
-        $compile(editor)(scope)
-        console.debug('codeEditor directive revision view is ' + scope.revisionView)
-      })
-    }
-  }
-}


[5/5] zeppelin git commit: [ZEPPELIN-2749] Use scalable file structure for zeppelin web

Posted by ku...@apache.org.
[ZEPPELIN-2749] Use scalable file structure for zeppelin web

### What is this PR for?

We have improved zeppelin-web, but some parts are still messy. As part of keeping zeppelin-web module healthy ([ZEPPELIN-2725](https://issues.apache.org/jira/browse/ZEPPELIN-2725)), I suggest having these file structure. (Refer the screenshot section)

Here are few reasons.

- unified directory, file name helps us to recognize, find which part we should modify / fix
  * Let's say we need to modify the resize feature of paragraph, where the developer can find it? currently, it's under `component/resizable` not under `paragraph/resizeable` and also it's not the shareable component. There is no reason to keep that files in `component/**`
- [this structure](https://github.com/toddmotto/angularjs-styleguide#file-naming-conventions) is what the angularjs community has verified for few years. so newly joined developers can feel more comfortable.
- this is necessary for [Modular archiecture](https://issues.apache.org/jira/browse/ZEPPELIN-2750) and it eventually helps us to make a smooth transition toward next technologies (even whatever we will use)

Additionally,

- This is not the meaningless refactoring PR and doesn't block developing new features / fixes (Please refer the `Some Details` section)
- I will handle conflicts for few days would be brought by other WIPs

For your information,

- https://github.com/toddmotto/angularjs-styleguide#file-naming-conventions
- https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#naming

#### How to Review This PR?

Please follow the commits. I modified submodules by splitting commits. Thus commit message includes what has been done in that PR. For example,

![image](https://user-images.githubusercontent.com/4968473/27993114-d8ac45e6-64dd-11e7-8130-f3fa887054a1.png)

#### Some Details

- Didn't change the widely used variable names not to make many conflicts. For example, `websocketMsgSrv`, `arrayOrderingSrv`
- Since there are helium packages already published, didn't change the HTML file names like `pivot_setting.html` (it's better to use `pivot-setting.html` if we following the rule)

### What type of PR is it?
[Improvement | Refactoring]

### Todos

Please refer the commit message.

### What is the Jira issue?

[ZEPPELIN-2749](https://issues.apache.org/jira/browse/ZEPPELIN-2749)

### How should this be tested?

**All functionalities must work** as like before, CI will test it.

### Screenshots (if appropriate)

#### Before: messy, mixed directory structure

![image](https://user-images.githubusercontent.com/4968473/27993126-0a94aca6-64de-11e7-93db-548b6fcc6913.png)

#### After: only the shared components will be placed under `components/`

![image](https://user-images.githubusercontent.com/4968473/27993118-ee1bd2d4-64dd-11e7-95b6-f71dc628a94e.png)

### Questions:
* Does the licenses files need update? - NO
* Is there breaking changes for older versions? - NO
* Does this needs documentation? - NO

Author: 1ambda <1a...@gmail.com>

Closes #2472 from 1ambda/ZEPPELIN-2749/use-scalable-file-structure-for-zeppelin-web and squashes the following commits:

22e6bf95 [1ambda] fix: AuthenticationIT, InterpreterModeActionsIT
e31f0bc8 [1ambda] fix: AbstractZeppelinIT
102e5443 [1ambda] paragraph-parameterized-query-form: Rename
74e0af41 [1ambda] paragraph-progress-bar: Rename
0029d6b7 [1ambda] job-progress-bar: Rename
af420a41 [1ambda] helium: Move helium related files into app/helium
285a6462 [1ambda] notebook-repository: Rename injected ctrl name
147572f1 [1ambda] notebook-repository: Rename files, funcs
4f66b642 [1ambda] ng-*: Rename files, funcs
f25d981e [1ambda] interpreter: Move interpreter specific directives into interpreter/
4a0602f7 [1ambda] array-ordering: Rename files
1e4ba709 [1ambda] code-editor: Move paragraph specific directive into paragraph/code-editor
31dcf6a3 [1ambda] base-url: Rename file
1afb2d03 [1ambda] note-name-filter: Rename files
ec046683 [1ambda] dropdown-input: Move notebook specific directive into notebook/dropdown-input
81fa2d44 [1ambda] elastic-input: Move notebook specific ctrl into notebook/elastic-input
657d0638 [1ambda] note-rename: Add prefix note-
47ac45d1 [1ambda] expand-collapse: Move navbar specific directive into navbar/
8bc78124 [1ambda] login: Remove invalid attr in login
b2bf91b0 [1ambda] note-import: Rename to noteImportCtrl
07b1f3ff [1ambda] note-create: Rename to noteCreateModal
568149ed [1ambda] note-create: Rename injected controller
c3402449 [1ambda] note-create: Rename files
e17c7ee7 [1ambda] note-create: Remove useless dialog postfix
b0a36a7e [1ambda] note-import: Remove meaningless postfix dialog
abf6869d [1ambda] note-import: Rename files
a40ea23c [1ambda] search: Move search specific service into serach/
7a094841 [1ambda] browser-detect: move save-as specific service into save-as/
40f62e3c [1ambda] rename: Modify injected service name
e993133a [1ambda] rename: Rename funcs
1f950943 [1ambda] note-action: Rename injected service name
d3da2d45 [1ambda] note-action: Rename files, funcs
b9f3c178 [1ambda] note-list-elem: Rename
d3132e9a [1ambda] note-list: Remove useless middle word Data
b5dff142 [1ambda] resizable: Move para specific directive into paragraph/
bfcd6b85 [1ambda] resizable: Rename funcs
40643997 [1ambda] fix: Remove useless postfix and rename files, funcs
0611aff5 [1ambda] websocket: Rename funcs
a2527530 [1ambda] fix: Remove meaningless postfix from searchService
c21a1237 [1ambda] move: paragraph specific ctrl into paragraph/
ad99c04b [1ambda] move: note specific dialog into notebook/
701f4432 [1ambda] move: note specific service into notebook/
3f02c503 [1ambda] fix: Remove unused saveAsService from paragraph ctrl
d7d7434a [1ambda] move: note specific service saveAsService into notebook/
4e7f6d9c [1ambda] fix: Move repository-create into interpreter/
a9a6368a [1ambda] fix: remove useless dir interpreter-create
c1c210de [1ambda] rename: repository-dialog -> repository-create


Project: http://git-wip-us.apache.org/repos/asf/zeppelin/repo
Commit: http://git-wip-us.apache.org/repos/asf/zeppelin/commit/6bd6c708
Tree: http://git-wip-us.apache.org/repos/asf/zeppelin/tree/6bd6c708
Diff: http://git-wip-us.apache.org/repos/asf/zeppelin/diff/6bd6c708

Branch: refs/heads/master
Commit: 6bd6c70881e8515a5b0679e5a8d6c235cfb1adf6
Parents: ebb6591
Author: 1ambda <1a...@gmail.com>
Authored: Mon Jul 10 15:52:37 2017 +0900
Committer: 1ambda <1a...@gmail.com>
Committed: Sat Jul 15 01:06:54 2017 +0900

----------------------------------------------------------------------
 .../org/apache/zeppelin/AbstractZeppelinIT.java |   2 +-
 .../zeppelin/integration/AuthenticationIT.java  |   2 +-
 .../integration/InterpreterModeActionsIT.java   |   2 +-
 zeppelin-web/src/app/app.js                     |   4 +-
 zeppelin-web/src/app/helium/helium-conf.js      |  99 +++++
 zeppelin-web/src/app/helium/helium-package.js   |  47 +++
 zeppelin-web/src/app/helium/helium-type.js      |  20 +
 zeppelin-web/src/app/helium/helium.config.js    | 101 -----
 .../src/app/helium/helium.controller.js         |   2 +-
 zeppelin-web/src/app/helium/helium.service.js   | 301 +++++++++++++++
 zeppelin-web/src/app/home/home.controller.js    |  25 +-
 zeppelin-web/src/app/home/home.html             |   4 +-
 .../src/app/home/notebook-template.html         |   4 +-
 zeppelin-web/src/app/home/notebook.html         |   4 +-
 .../src/app/interpreter/interpreter-create.html | 383 +++++++++++++++++++
 .../interpreter-create/interpreter-create.html  | 383 -------------------
 .../interpreter/interpreter-item.directive.js   |  31 ++
 .../src/app/interpreter/interpreter.html        |  10 +-
 .../src/app/interpreter/repository-create.html  | 115 ++++++
 .../widget/number-widget.directive.js           |  31 ++
 .../app/jobmanager/jobs/job-progress-bar.html   |  22 ++
 .../app/jobmanager/jobs/job-progressBar.html    |  22 --
 zeppelin-web/src/app/jobmanager/jobs/job.html   |   2 +-
 .../notebook-repository.controller.js           |  87 +++++
 .../notebook-repository.html                    |  98 +++++
 .../dropdown-input/dropdown-input.directive.js  |  26 ++
 .../elastic-input/elastic-input.controller.js   |  21 +
 .../src/app/notebook/note-var-share.service.js  |  39 ++
 .../src/app/notebook/notebook-actionBar.html    |   2 +-
 .../src/app/notebook/notebook.controller.js     |   8 +-
 .../notebook/paragraph/clipboard.controller.js  |  34 ++
 .../code-editor/code-editor.directive.html      |  21 +
 .../code-editor/code-editor.directive.js        |  38 ++
 .../paragraph-parameterized-query-form.html     |  71 ++++
 .../paragraph-parameterizedQueryForm.html       |  71 ----
 .../paragraph/paragraph-progress-bar.html       |  22 ++
 .../paragraph/paragraph-progressBar.html        |  22 --
 .../notebook/paragraph/paragraph.controller.js  |   2 +-
 .../src/app/notebook/paragraph/paragraph.html   |   4 +-
 .../notebook/paragraph/resizable.directive.js   |  69 ++++
 .../notebook/save-as/browser-detect.service.js  |  39 ++
 .../src/app/notebook/save-as/save-as.service.js |  51 +++
 zeppelin-web/src/app/notebook/shortcut.html     | 312 +++++++++++++++
 .../notebookRepos/notebookRepos.controller.js   |  87 -----
 .../src/app/notebookRepos/notebookRepos.html    |  98 -----
 zeppelin-web/src/app/search/search.service.js   |  33 ++
 .../array-ordering/array-ordering.service.js    |  62 +++
 .../arrayOrderingSrv/arrayOrdering.service.js   |  62 ---
 .../src/components/base-url/base-url.service.js |  50 +++
 .../src/components/baseUrl/baseUrl.service.js   |  50 ---
 .../browser-detect/browserDetect.service.js     |  39 --
 .../clipboard/clipboard.controller.js           |  34 --
 .../dropdowninput/dropdowninput.directive.js    |  26 --
 .../components/editor/ace.editor.directive.html |  21 -
 .../components/editor/codeEditor.directive.js   |  38 --
 .../elasticInputCtrl/elasticInput.controller.js |  21 -
 .../expandCollapse/expandCollapse.css           |  17 -
 .../expandCollapse/expandCollapse.directive.js  |  46 ---
 .../filterNoteNames/filter-note-names.html      |  19 -
 .../src/components/helium/helium-conf.js        |  99 -----
 .../src/components/helium/helium-package.js     |  47 ---
 .../src/components/helium/helium-type.js        |  20 -
 .../src/components/helium/helium.service.js     | 301 ---------------
 .../interpreter/interpreter.directive.js        |  31 --
 .../widget/widget.number.directive.js           |  31 --
 zeppelin-web/src/components/login/login.html    |   2 +-
 .../modal-shortcut/modal-shortcut.html          | 312 ---------------
 .../navbar/expand-collapse/expand-collapse.css  |  17 +
 .../expand-collapse.directive.js                |  48 +++
 .../navbar/navbar-note-list-elem.html           |  50 +++
 .../components/navbar/navbar-noteList-elem.html |  50 ---
 .../src/components/navbar/navbar.controller.js  |   6 +-
 zeppelin-web/src/components/navbar/navbar.html  |  10 +-
 .../components/ng-enter/ng-enter.directive.js   |  30 ++
 .../ng-enter/ng-enter.directive.test.js         |  24 ++
 .../components/ng-escape/ng-escape.directive.js |  28 ++
 .../src/components/ngenter/ngenter.directive.js |  30 --
 .../ngenter/ngenter.directive.test.js           |  24 --
 .../components/ngescape/ngescape.directive.js   |  28 --
 .../note-action/note-action.service.js          | 183 +++++++++
 .../note-create/note-create.controller.js       | 106 +++++
 .../note-create/note-create.controller.test.js  |  39 ++
 .../src/components/note-create/note-create.css  |  49 +++
 .../src/components/note-create/note-create.html |  66 ++++
 .../components/note-create/visible.directive.js |  45 +++
 .../note-import/note-import.controller.js       | 138 +++++++
 .../src/components/note-import/note-import.css  |  91 +++++
 .../src/components/note-import/note-import.html |  80 ++++
 .../components/note-list/note-list.factory.js   |  81 ++++
 .../note-list/note-list.factory.test.js         |  75 ++++
 .../note-name-filter/note-name-filter.html      |  19 +
 .../note-rename/note-rename.controller.js       |  48 +++
 .../src/components/note-rename/note-rename.css  |  38 ++
 .../src/components/note-rename/note-rename.html |  43 +++
 .../note-rename/note-rename.service.js          |  32 ++
 .../components/noteAction/noteAction.service.js | 183 ---------
 .../noteListDataFactory/noteList.datafactory.js |  81 ----
 .../noteList.datafactory.test.js                |  75 ----
 .../noteName-create/note-name-dialog.css        |  49 ---
 .../noteName-create/note-name-dialog.html       |  66 ----
 .../noteName-create/notename.controller.js      | 106 -----
 .../noteName-create/notename.controller.test.js |  39 --
 .../noteName-create/visible.directive.js        |  45 ---
 .../noteName-import/note-import-dialog.css      |  91 -----
 .../noteName-import/note-import-dialog.html     |  80 ----
 .../notenameImport.controller.js                | 138 -------
 .../notevarshareService/notevarshare.service.js |  39 --
 .../src/components/rename/rename.controller.js  |  48 ---
 zeppelin-web/src/components/rename/rename.css   |  38 --
 zeppelin-web/src/components/rename/rename.html  |  43 ---
 .../src/components/rename/rename.service.js     |  32 --
 .../repository-create/repository-dialog.html    | 115 ------
 .../components/resizable/resizable.directive.js |  69 ----
 .../src/components/saveAs/saveAs.service.js     |  51 ---
 .../components/searchService/search.service.js  |  33 --
 .../websocket/websocket-event.factory.js        | 196 ++++++++++
 .../websocket/websocket-message.service.js      | 343 +++++++++++++++++
 .../websocketEvents/websocketEvents.factory.js  | 196 ----------
 .../websocketEvents/websocketMsg.service.js     | 343 -----------------
 zeppelin-web/src/index.html                     |  19 +-
 zeppelin-web/src/index.js                       |  54 +--
 121 files changed, 4075 insertions(+), 4174 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-server/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java b/zeppelin-server/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java
index e16bf1a..475be50 100644
--- a/zeppelin-server/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java
+++ b/zeppelin-server/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java
@@ -107,7 +107,7 @@ abstract public class AbstractZeppelinIT {
         " note')]"));
 
     WebDriverWait block = new WebDriverWait(driver, MAX_BROWSER_TIMEOUT_SEC);
-    block.until(ExpectedConditions.visibilityOfElementLocated(By.id("noteNameModal")));
+    block.until(ExpectedConditions.visibilityOfElementLocated(By.id("noteCreateModal")));
     clickAndWait(By.id("createNoteButton"));
     block.until(ExpectedConditions.invisibilityOfElementLocated(By.className("pull-right")));
   }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-server/src/test/java/org/apache/zeppelin/integration/AuthenticationIT.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/AuthenticationIT.java b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/AuthenticationIT.java
index 523b8f8..f87bff2 100644
--- a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/AuthenticationIT.java
+++ b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/AuthenticationIT.java
@@ -125,7 +125,7 @@ public class AuthenticationIT extends AbstractZeppelinIT {
     ZeppelinITUtils.sleep(1000, false);
     pollingWait(By.xpath("//*[@id='userName']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys(userName);
     pollingWait(By.xpath("//*[@id='password']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys(password);
-    pollingWait(By.xpath("//*[@id='NoteImportCtrl']//button[contains(.,'Login')]"),
+    pollingWait(By.xpath("//*[@id='loginModalContent']//button[contains(.,'Login')]"),
         MAX_BROWSER_TIMEOUT_SEC).click();
     ZeppelinITUtils.sleep(1000, false);
   }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-server/src/test/java/org/apache/zeppelin/integration/InterpreterModeActionsIT.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/InterpreterModeActionsIT.java b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/InterpreterModeActionsIT.java
index 212b04b..9bfeae0 100644
--- a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/InterpreterModeActionsIT.java
+++ b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/InterpreterModeActionsIT.java
@@ -139,7 +139,7 @@ public class InterpreterModeActionsIT extends AbstractZeppelinIT {
     ZeppelinITUtils.sleep(500, false);
     pollingWait(By.xpath("//*[@id='userName']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys(userName);
     pollingWait(By.xpath("//*[@id='password']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys(password);
-    pollingWait(By.xpath("//*[@id='NoteImportCtrl']//button[contains(.,'Login')]"),
+    pollingWait(By.xpath("//*[@id='loginModalContent']//button[contains(.,'Login')]"),
         MAX_BROWSER_TIMEOUT_SEC).click();
     ZeppelinITUtils.sleep(1000, false);
   }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/app.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/app.js b/zeppelin-web/src/app/app.js
index f645d3b..9878d56 100644
--- a/zeppelin-web/src/app/app.js
+++ b/zeppelin-web/src/app/app.js
@@ -113,8 +113,8 @@ let zeppelinWebApp = angular.module('zeppelinWebApp', requiredModules)
         controller: 'InterpreterCtrl'
       })
       .when('/notebookRepos', {
-        templateUrl: 'app/notebookRepos/notebookRepos.html',
-        controller: 'NotebookReposCtrl',
+        templateUrl: 'app/notebook-repository/notebook-repository.html',
+        controller: 'NotebookRepositoryCtrl',
         controllerAs: 'noterepo'
       })
       .when('/credential', {

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/helium/helium-conf.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/helium/helium-conf.js b/zeppelin-web/src/app/helium/helium-conf.js
new file mode 100644
index 0000000..10ca18a
--- /dev/null
+++ b/zeppelin-web/src/app/helium/helium-conf.js
@@ -0,0 +1,99 @@
+/*
+ * Licensed 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.
+ */
+
+export const HeliumConfFieldType = {
+  NUMBER: 'number',
+  JSON: 'json',
+  STRING: 'string',
+}
+
+/**
+ * @param persisted <Object> including `type`, `description`, `defaultValue` for each conf key
+ * @param spec <Object> including `value` for each conf key
+ */
+export function mergePersistedConfWithSpec (persisted, spec) {
+  const confs = []
+
+  for (let name in spec) {
+    const specField = spec[name]
+    const persistedValue = persisted[name]
+
+    const value = (persistedValue) ? persistedValue : specField.defaultValue
+    const merged = {
+      name: name,
+      type: specField.type,
+      description: specField.description,
+      value: value,
+      defaultValue: specField.defaultValue,
+    }
+
+    confs.push(merged)
+  }
+
+  return confs
+}
+
+export function createAllPackageConfigs (defaultPackages, persistedConfs) {
+  let packageConfs = {}
+
+  for (let name in defaultPackages) {
+    const pkgSearchResult = defaultPackages[name]
+
+    const spec = pkgSearchResult.pkg.config
+    if (!spec) { continue }
+
+    const artifact = pkgSearchResult.pkg.artifact
+    if (!artifact) { continue }
+
+    let persistedConf = {}
+    if (persistedConfs[artifact]) {
+      persistedConf = persistedConfs[artifact]
+    }
+
+    const confs = mergePersistedConfWithSpec(persistedConf, spec)
+    packageConfs[name] = confs
+  }
+
+  return packageConfs
+}
+
+export function parseConfigValue (type, stringified) {
+  let value = stringified
+
+  try {
+    if (HeliumConfFieldType.NUMBER === type) {
+      value = parseFloat(stringified)
+    } else if (HeliumConfFieldType.JSON === type) {
+      value = JSON.parse(stringified)
+    }
+  } catch (error) {
+    // return just the stringified one
+    console.error(`Failed to parse conf type ${type}, value ${value}`)
+  }
+
+  return value
+}
+
+/**
+ * persist key-value only
+ * since other info (e.g type, desc) can be provided by default config
+ */
+export function createPersistableConfig (currentConfs) {
+  const filtered = currentConfs.reduce((acc, c) => {
+    acc[c.name] = parseConfigValue(c.type, c.value)
+    return acc
+  }, {})
+
+  return filtered
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/helium/helium-package.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/helium/helium-package.js b/zeppelin-web/src/app/helium/helium-package.js
new file mode 100644
index 0000000..88d191a
--- /dev/null
+++ b/zeppelin-web/src/app/helium/helium-package.js
@@ -0,0 +1,47 @@
+/*
+ * Licensed 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.
+ */
+
+export function createDefaultPackage (pkgSearchResult, sce) {
+  for (let pkgIdx in pkgSearchResult) {
+    const pkg = pkgSearchResult[pkgIdx]
+    pkg.pkg.icon = sce.trustAsHtml(pkg.pkg.icon)
+    if (pkg.enabled) {
+      pkgSearchResult.splice(pkgIdx, 1)
+      return pkg
+    }
+  }
+
+  // show first available version if package is not enabled
+  const result = pkgSearchResult[0]
+  pkgSearchResult.splice(0, 1)
+  return result
+}
+
+/**
+ * create default packages based on `enabled` field and `latest` version.
+ *
+ * @param pkgSearchResults
+ * @param sce angular `$sce` object
+ * @returns {Object} including {name, pkgInfo}
+ */
+export function createDefaultPackages (pkgSearchResults, sce) {
+  const defaultPackages = {}
+  // show enabled version if any version of package is enabled
+  for (let name in pkgSearchResults) {
+    const pkgSearchResult = pkgSearchResults[name]
+    defaultPackages[name] = createDefaultPackage(pkgSearchResult, sce)
+  }
+
+  return defaultPackages
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/helium/helium-type.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/helium/helium-type.js b/zeppelin-web/src/app/helium/helium-type.js
new file mode 100644
index 0000000..27b34fa
--- /dev/null
+++ b/zeppelin-web/src/app/helium/helium-type.js
@@ -0,0 +1,20 @@
+/*
+ * Licensed 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.
+ */
+
+export const HeliumType = {
+  VISUALIZATION: 'VISUALIZATION',
+  SPELL: 'SPELL',
+  INTERPRETER: 'INTERPRETER',
+  APPLICATION: 'APPLICATION',
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/helium/helium.config.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/helium/helium.config.js b/zeppelin-web/src/app/helium/helium.config.js
deleted file mode 100644
index ace7136..0000000
--- a/zeppelin-web/src/app/helium/helium.config.js
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Licensed 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.
- */
-
-export const HeliumConfFieldType = {
-  NUMBER: 'number',
-  JSON: 'json',
-  STRING: 'string',
-}
-
-/**
- * @param persisted <Object> including `type`, `description`, `defaultValue` for each conf key
- * @param spec <Object> including `value` for each conf key
- */
-export function mergePersistedConfWithSpec (persisted, spec) {
-  const confs = []
-
-  for (let name in spec) {
-    const specField = spec[name]
-    const persistedValue = persisted[name]
-
-    const value = (persistedValue) ? persistedValue : specField.defaultValue
-    const merged = {
-      name: name,
-      type: specField.type,
-      description: specField.description,
-      value: value,
-      defaultValue: specField.defaultValue,
-    }
-
-    confs.push(merged)
-  }
-
-  return confs
-}
-
-export function createPackageConf (defaultPackages, persistedPackacgeConfs) {
-  let packageConfs = {}
-
-  for (let name in defaultPackages) {
-    const pkgInfo = defaultPackages[name]
-
-    const configSpec = pkgInfo.pkg.config
-    if (!configSpec) { continue }
-
-    const version = pkgInfo.pkg.version
-    if (!version) { continue }
-
-    let config = {}
-    if (persistedPackacgeConfs[name] && persistedPackacgeConfs[name][version]) {
-      config = persistedPackacgeConfs[name][version]
-    }
-
-    const confs = mergePersistedConfWithSpec(config, configSpec)
-    packageConfs[name] = confs
-  }
-
-  return packageConfs
-}
-
-export function parseConfigValue (type, stringified) {
-  let value = stringified
-
-  try {
-    if (HeliumConfFieldType.NUMBER === type) {
-      value = parseFloat(stringified)
-    } else if (HeliumConfFieldType.JSON === type) {
-      value = JSON.parse(stringified)
-    }
-  } catch (error) {
-    // return just the stringified one
-    console.error(`Failed to parse conf type ${type}, value ${value}`)
-  }
-
-  return value
-}
-
-/**
- * create persistable config object
- */
-export function createPersistableConfig (currentConf) {
-  // persist key-value only
-  // since other info (e.g type, desc) can be provided by default config
-  const filtered = currentConf.reduce((acc, c) => {
-    let value = parseConfigValue(c.type, c.value)
-    acc[c.name] = value
-    return acc
-  }, {})
-
-  return filtered
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/helium/helium.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/helium/helium.controller.js b/zeppelin-web/src/app/helium/helium.controller.js
index a610fb6..a397ace 100644
--- a/zeppelin-web/src/app/helium/helium.controller.js
+++ b/zeppelin-web/src/app/helium/helium.controller.js
@@ -12,7 +12,7 @@
  * limitations under the License.
  */
 
-import { HeliumType, } from '../../components/helium/helium-type'
+import { HeliumType, } from './helium-type'
 
 export default function HeliumCtrl ($scope, $rootScope, $sce,
                                    baseUrlSrv, ngToast, heliumService) {

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/helium/helium.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/helium/helium.service.js b/zeppelin-web/src/app/helium/helium.service.js
new file mode 100644
index 0000000..d2054b3
--- /dev/null
+++ b/zeppelin-web/src/app/helium/helium.service.js
@@ -0,0 +1,301 @@
+/*
+ * Licensed 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.
+ */
+
+import { HeliumType, } from './helium-type'
+import {
+  createAllPackageConfigs,
+  createPersistableConfig,
+  mergePersistedConfWithSpec,
+} from './helium-conf'
+import {
+  createDefaultPackages,
+} from './helium-package'
+
+angular.module('zeppelinWebApp').service('heliumService', HeliumService)
+
+export default function HeliumService($http, $sce, baseUrlSrv) {
+  'ngInject'
+
+  let visualizationBundles = []
+  let visualizationPackageOrder = []
+  // name `heliumBundles` should be same as `HeliumBundleFactory.HELIUM_BUNDLES_VAR`
+  let heliumBundles = []
+  // map for `{ magic: interpreter }`
+  let spellPerMagic = {}
+  // map for `{ magic: package-name }`
+  let pkgNamePerMagic = {}
+
+  /**
+   * @param magic {string} e.g `%flowchart`
+   * @returns {SpellBase} undefined if magic is not registered
+   */
+  this.getSpellByMagic = function (magic) {
+    return spellPerMagic[magic]
+  }
+
+  this.executeSpell = function (magic, textWithoutMagic) {
+    const promisedConf = this.getSinglePackageConfigUsingMagic(magic)
+      .then(confs => createPersistableConfig(confs))
+
+    return promisedConf.then(conf => {
+      const spell = this.getSpellByMagic(magic)
+      const spellResult = spell.interpret(textWithoutMagic, conf)
+      const parsed = spellResult.getAllParsedDataWithTypes(
+        spellPerMagic, magic, textWithoutMagic)
+
+      return parsed
+    })
+  }
+
+  this.executeSpellAsDisplaySystem = function (magic, textWithoutMagic) {
+    const promisedConf = this.getSinglePackageConfigUsingMagic(magic)
+      .then(confs => createPersistableConfig(confs))
+
+    return promisedConf.then(conf => {
+      const spell = this.getSpellByMagic(magic)
+      const spellResult = spell.interpret(textWithoutMagic.trim(), conf)
+      const parsed = spellResult.getAllParsedDataWithTypes(spellPerMagic)
+
+      return parsed
+    })
+  }
+
+  this.getVisualizationCachedPackages = function () {
+    return visualizationBundles
+  }
+
+  this.getVisualizationCachedPackageOrder = function () {
+    return visualizationPackageOrder
+  }
+
+  /**
+   * @returns {Promise} which returns bundleOrder and cache it in `visualizationPackageOrder`
+   */
+  this.getVisualizationPackageOrder = function () {
+    return $http.get(baseUrlSrv.getRestApiBase() + '/helium/order/visualization')
+      .then(function (response, status) {
+        const order = response.data.body
+        visualizationPackageOrder = order
+        return order
+      })
+      .catch(function (error) {
+        console.error('Can not get bundle order', error)
+      })
+  }
+
+  this.setVisualizationPackageOrder = function (list) {
+    return $http.post(baseUrlSrv.getRestApiBase() + '/helium/order/visualization', list)
+  }
+
+  this.enable = function (name, artifact) {
+    return $http.post(baseUrlSrv.getRestApiBase() + '/helium/enable/' + name, artifact)
+  }
+
+  this.disable = function (name) {
+    return $http.post(baseUrlSrv.getRestApiBase() + '/helium/disable/' + name)
+  }
+
+  this.saveConfig = function (pkg, defaultPackageConfig, closeConfigPanelCallback) {
+    // in case of local package, it will include `/`
+    const pkgArtifact = encodeURIComponent(pkg.artifact)
+    const pkgName = pkg.name
+    const filtered = createPersistableConfig(defaultPackageConfig)
+
+    if (!pkgName || !pkgArtifact || !filtered) {
+      console.error(
+        `Can't save config for helium package '${pkgArtifact}'`, filtered)
+      return
+    }
+
+    const url = `${baseUrlSrv.getRestApiBase()}/helium/config/${pkgName}/${pkgArtifact}`
+    return $http.post(url, filtered)
+      .then(() => {
+        if (closeConfigPanelCallback) { closeConfigPanelCallback() }
+      }).catch((error) => {
+        console.error(`Failed to save config for ${pkgArtifact}`, error)
+      })
+  }
+
+  /**
+   * @returns {Promise<Object>} which including {name, Array<package info for artifact>}
+   */
+  this.getAllPackageInfo = function () {
+    return $http.get(`${baseUrlSrv.getRestApiBase()}/helium/package`)
+      .then(function (response, status) {
+        return response.data.body
+      })
+      .catch(function (error) {
+        console.error('Failed to get all package infos', error)
+      })
+  }
+
+  this.getAllEnabledPackages = function () {
+    return $http.get(`${baseUrlSrv.getRestApiBase()}/helium/enabledPackage`)
+      .then(function (response, status) {
+        return response.data.body
+      })
+      .catch(function (error) {
+        console.error('Failed to get all enabled package infos', error)
+      })
+  }
+
+  this.getSingleBundle = function (pkgName) {
+    let url = `${baseUrlSrv.getRestApiBase()}/helium/bundle/load/${pkgName}`
+    if (process.env.HELIUM_BUNDLE_DEV) {
+      url = url + '?refresh=true'
+    }
+
+    return $http.get(url)
+      .then(function (response, status) {
+        const bundle = response.data
+        if (bundle.substring(0, 'ERROR:'.length) === 'ERROR:') {
+          console.error(`Failed to get bundle: ${pkgName}`, bundle)
+          return '' // empty bundle will be filtered later
+        }
+
+        return bundle
+      })
+      .catch(function (error) {
+        console.error(`Failed to get single bundle: ${pkgName}`, error)
+      })
+  }
+
+  this.getDefaultPackages = function () {
+    return this.getAllPackageInfo()
+      .then(pkgSearchResults => {
+        return createDefaultPackages(pkgSearchResults, $sce)
+      })
+  }
+
+  this.getAllPackageInfoAndDefaultPackages = function () {
+    return this.getAllPackageInfo()
+      .then(pkgSearchResults => {
+        return {
+          pkgSearchResults: pkgSearchResults,
+          defaultPackages: createDefaultPackages(pkgSearchResults, $sce),
+        }
+      })
+  }
+
+  /**
+   * get all package configs.
+   * @return { Promise<{name, Array<Object>}> }
+   */
+  this.getAllPackageConfigs = function () {
+    const promisedDefaultPackages = this.getDefaultPackages()
+    const promisedPersistedConfs =
+      $http.get(`${baseUrlSrv.getRestApiBase()}/helium/config`)
+      .then(function (response, status) {
+        return response.data.body
+      })
+
+    return Promise.all([promisedDefaultPackages, promisedPersistedConfs])
+      .then(values => {
+        const defaultPackages = values[0]
+        const persistedConfs = values[1]
+
+        return createAllPackageConfigs(defaultPackages, persistedConfs)
+      })
+      .catch(function (error) {
+        console.error('Failed to get all package configs', error)
+      })
+  }
+
+  /**
+   * get the package config which is persisted in server.
+   * @return { Promise<Array<Object>> }
+   */
+  this.getSinglePackageConfigs = function (pkg) {
+    const pkgName = pkg.name
+    // in case of local package, it will include `/`
+    const pkgArtifact = encodeURIComponent(pkg.artifact)
+
+    if (!pkgName || !pkgArtifact) {
+      console.error('Failed to fetch config for\n', pkg)
+      return Promise.resolve([])
+    }
+
+    const confUrl = `${baseUrlSrv.getRestApiBase()}/helium/config/${pkgName}/${pkgArtifact}`
+    const promisedConf = $http.get(confUrl)
+      .then(function (response, status) {
+        return response.data.body
+      })
+
+    return promisedConf.then(({confSpec, confPersisted}) => {
+      const merged = mergePersistedConfWithSpec(confPersisted, confSpec)
+      return merged
+    })
+  }
+
+  this.getSinglePackageConfigUsingMagic = function (magic) {
+    const pkgName = pkgNamePerMagic[magic]
+
+    const confUrl = `${baseUrlSrv.getRestApiBase()}/helium/spell/config/${pkgName}`
+    const promisedConf = $http.get(confUrl)
+      .then(function (response, status) {
+        return response.data.body
+      })
+
+    return promisedConf.then(({confSpec, confPersisted}) => {
+      const merged = mergePersistedConfWithSpec(confPersisted, confSpec)
+      return merged
+    })
+  }
+
+  const p = this.getAllEnabledPackages()
+    .then(enabledPackageSearchResults => {
+      const promises = enabledPackageSearchResults.map(packageSearchResult => {
+        const pkgName = packageSearchResult.pkg.name
+        return this.getSingleBundle(pkgName)
+      })
+
+      return Promise.all(promises)
+    })
+    .then(bundles => {
+      return bundles.reduce((acc, b) => {
+        // filter out empty bundle
+        if (b === '') { return acc }
+        acc.push(b)
+        return acc
+      }, [])
+    })
+
+  // load should be promise
+  this.load = p.then(availableBundles => {
+    // evaluate bundles
+    availableBundles.map(b => {
+      // eslint-disable-next-line no-eval
+      eval(b)
+    })
+
+    // extract bundles by type
+    heliumBundles.map(b => {
+      if (b.type === HeliumType.SPELL) {
+        const spell = new b.class() // eslint-disable-line new-cap
+        const pkgName = b.id
+        spellPerMagic[spell.getMagic()] = spell
+        pkgNamePerMagic[spell.getMagic()] = pkgName
+      } else if (b.type === HeliumType.VISUALIZATION) {
+        visualizationBundles.push(b)
+      }
+    })
+  })
+
+  this.init = function() {
+    this.getVisualizationPackageOrder()
+  }
+
+  // init
+  this.init()
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/home/home.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/home/home.controller.js b/zeppelin-web/src/app/home/home.controller.js
index 83f9681..2cf8439 100644
--- a/zeppelin-web/src/app/home/home.controller.js
+++ b/zeppelin-web/src/app/home/home.controller.js
@@ -14,15 +14,16 @@
 
 angular.module('zeppelinWebApp').controller('HomeCtrl', HomeCtrl)
 
-function HomeCtrl ($scope, noteListDataFactory, websocketMsgSrv, $rootScope, arrayOrderingSrv,
-                  ngToast, noteActionSrv, TRASH_FOLDER_ID) {
+function HomeCtrl ($scope, noteListFactory, websocketMsgSrv, $rootScope, arrayOrderingSrv,
+                  ngToast, noteActionService, TRASH_FOLDER_ID) {
   'ngInject'
 
   ngToast.dismiss()
   let vm = this
-  vm.notes = noteListDataFactory
+  vm.notes = noteListFactory
   vm.websocketMsgSrv = websocketMsgSrv
   vm.arrayOrderingSrv = arrayOrderingSrv
+  vm.noteActionService = noteActionService
 
   vm.notebookHome = false
   vm.noteCustomHome = true
@@ -85,15 +86,15 @@ function HomeCtrl ($scope, noteListDataFactory, websocketMsgSrv, $rootScope, arr
   })
 
   $scope.renameNote = function (nodeId, nodePath) {
-    noteActionSrv.renameNote(nodeId, nodePath)
+    vm.noteActionService.renameNote(nodeId, nodePath)
   }
 
   $scope.moveNoteToTrash = function (noteId) {
-    noteActionSrv.moveNoteToTrash(noteId, false)
+    vm.noteActionService.moveNoteToTrash(noteId, false)
   }
 
   $scope.moveFolderToTrash = function (folderId) {
-    noteActionSrv.moveFolderToTrash(folderId)
+    vm.noteActionService.moveFolderToTrash(folderId)
   }
 
   $scope.restoreNote = function (noteId) {
@@ -105,27 +106,27 @@ function HomeCtrl ($scope, noteListDataFactory, websocketMsgSrv, $rootScope, arr
   }
 
   $scope.restoreAll = function () {
-    noteActionSrv.restoreAll()
+    vm.noteActionService.restoreAll()
   }
 
   $scope.renameFolder = function (node) {
-    noteActionSrv.renameFolder(node.id)
+    vm.noteActionService.renameFolder(node.id)
   }
 
   $scope.removeNote = function (noteId) {
-    noteActionSrv.removeNote(noteId, false)
+    vm.noteActionService.removeNote(noteId, false)
   }
 
   $scope.removeFolder = function (folderId) {
-    noteActionSrv.removeFolder(folderId)
+    vm.noteActionService.removeFolder(folderId)
   }
 
   $scope.emptyTrash = function () {
-    noteActionSrv.emptyTrash()
+    vm.noteActionService.emptyTrash()
   }
 
   $scope.clearAllParagraphOutput = function (noteId) {
-    noteActionSrv.clearAllParagraphOutput(noteId)
+    vm.noteActionService.clearAllParagraphOutput(noteId)
   }
 
   $scope.isFilterNote = function (note) {

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/home/home.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/home/home.html b/zeppelin-web/src/app/home/home.html
index 7c1154f..1ab9718 100644
--- a/zeppelin-web/src/app/home/home.html
+++ b/zeppelin-web/src/app/home/home.html
@@ -36,10 +36,10 @@ limitations under the License.
           <div>
             <h5><a href="" data-toggle="modal" data-target="#noteImportModal" style="text-decoration: none;">
               <i style="font-size: 15px;" class="fa fa-upload"></i> Import note</a></h5>
-            <h5 ng-controller="NotenameCtrl as notenamectrl"><a href="" data-toggle="modal" data-target="#noteNameModal" style="text-decoration: none;" ng-click="notenamectrl.getInterpreterSettings()">
+            <h5 ng-controller="NoteCreateCtrl as noteCreateCtrl"><a href="" data-toggle="modal" data-target="#noteCreateModal" style="text-decoration: none;" ng-click="noteCreateCtrl.getInterpreterSettings()">
               <i style="font-size: 15px;" class="icon-notebook"></i> Create new note</a></h5>
             <ul id="notebook-names">
-              <li class="filter-names" ng-include="'components/filterNoteNames/filter-note-names.html'"></li>
+              <li class="filter-names" ng-include="'components/note-name-filter/note-name-filter.html'"></li>
               <li ng-repeat="note in home.notes.list | filter:query.q | orderBy:node:false:home.arrayOrderingSrv.noteComparator track by $index">
                 <i style="font-size: 10px;" class="icon-doc"></i>
                 <a style="text-decoration: none;" href="#/notebook/{{note.id}}">{{noteName(note)}}</a>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/home/notebook-template.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/home/notebook-template.html b/zeppelin-web/src/app/home/notebook-template.html
index 4c287de..76602aa 100644
--- a/zeppelin-web/src/app/home/notebook-template.html
+++ b/zeppelin-web/src/app/home/notebook-template.html
@@ -58,8 +58,8 @@ limitations under the License.
       <a style="text-decoration: none; cursor: pointer;" ng-click="toggleFolderNode(node)">
         <i style="font-size: 10px;" ng-class="node.hidden ? 'fa fa-folder' : 'fa fa-folder-open'"></i> {{getNoteName(node)}}
       </a>
-      <a ng-if="!node.isTrash" href="" data-toggle="modal" data-target="#noteNameModal" style="text-decoration: none;"
-         ng-controller="NotenameCtrl as notenamectrl" ng-click="notenamectrl.getInterpreterSettings()" data-path="{{node.id}}">
+      <a ng-if="!node.isTrash" href="" data-toggle="modal" data-target="#noteCreateModal" style="text-decoration: none;"
+         ng-controller="NoteCreateCtrl as noteCreateCtrl" ng-click="noteCreateCtrl.getInterpreterSettings()" data-path="{{node.id}}">
         <i style="margin-left: 10px;"
            class="fa fa-plus notebook-list-btn" ng-show="showFolderButton"
            tooltip-placement="bottom" uib-tooltip="Create new note">

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/home/notebook.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/home/notebook.html b/zeppelin-web/src/app/home/notebook.html
index 76214e9..f276a22 100644
--- a/zeppelin-web/src/app/home/notebook.html
+++ b/zeppelin-web/src/app/home/notebook.html
@@ -23,10 +23,10 @@ limitations under the License.
       </h4>
         <h5><a href="" data-toggle="modal" data-target="#noteImportModal" style="text-decoration: none;">
            <i style="font-size: 15px;" class="fa fa-upload"></i> Import note</a></h5>
-         <h5><a href="" data-toggle="modal" data-target="#noteNameModal" style="text-decoration: none;">
+         <h5><a href="" data-toggle="modal" data-target="#noteCreateModal" style="text-decoration: none;">
            <i style="font-size: 15px;" class="icon-notebook"></i> Create new note</a></h5>
        <ul id="notebook-names">
-         <li class="filter-names" ng-include="'components/filterNoteNames/filter-note-names.html'"></li>
+         <li class="filter-names" ng-include="'components/note-name-filter/note-name-filter.html'"></li>
          <div ng-if="!query.q || query.q === ''">
            <li ng-repeat="node in home.notes.root.children | orderBy:node:false:home.arrayOrderingSrv.noteComparator track by $index"
                ng-include src="'app/home/notebook-template.html'" ng-class="note_folder_renderer"></li>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/interpreter/interpreter-create.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/interpreter/interpreter-create.html b/zeppelin-web/src/app/interpreter/interpreter-create.html
new file mode 100644
index 0000000..b37f160
--- /dev/null
+++ b/zeppelin-web/src/app/interpreter/interpreter-create.html
@@ -0,0 +1,383 @@
+<!--
+Licensed 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.
+-->
+<div>
+  <div class="row interpreter">
+    <div class="col-md-12" ng-show="showAddNewSetting">
+      <hr />
+      <div class="interpreterSettingAdd">
+        <h4>Create new interpreter</h4>
+
+        <div class="form-group" style="width:200px">
+          <b>Interpreter Name</b>
+          <input id="newInterpreterSettingName" input pu-elastic-input
+                 pu-elastic-input-minwidth="180px" ng-model="newInterpreterSetting.name" />
+        </div>
+
+        <b>Interpreter group</b>
+        <div class="form-group"
+             style="width:180px">
+          <select class="form-control input-sm" ng-model="newInterpreterSetting.group"
+                  ng-change="newInterpreterGroupChange()">
+            <option ng-repeat="availableInterpreter in availableInterpreters | unique: 'name'| orderBy: 'name'" value="{{availableInterpreter.name}}">
+              {{availableInterpreter.name}}
+            </option>
+          </select>
+        </div>
+
+        <div>
+          <h5>Option</h5>
+          <div class="row interpreter" style="margin-top: 5px;">
+            <div class="col-md-6">
+              The interpreter will be instantiated
+              <span class="btn-group">
+              <button type="button" class="btn btn-default btn-xs dropdown-toggle"
+                      data-toggle="dropdown">
+                {{getInterpreterRunningOption(setting.id)}} <span class="caret"></span>
+              </button>
+              <ul class="dropdown-menu" role="menu">
+                <li>
+                  <a style="cursor:pointer"
+                     ng-click="setInterpreterRunningOption(setting.id, 'shared', 'shared')">
+                    Globally
+                  </a>
+                </li>
+                <li>
+                  <a style="cursor:pointer"
+                     ng-click="setInterpreterRunningOption(setting.id, 'scoped', '')">
+                    Per Note
+                  </a>
+                </li>
+                <li ng-if="ticket.principal !== 'anonymous'">
+                  <a style="cursor:pointer"
+                     ng-click="setInterpreterRunningOption(setting.id, 'shared', 'scoped')">
+                    Per User
+                  </a>
+                </li>
+              </ul>
+            </span>
+              in
+              <span class="btn-group">
+              <button type="button" class="btn btn-default btn-xs dropdown-toggle"
+                      data-toggle="dropdown"
+                      ng-disabled="getInterpreterRunningOption(setting.id) === 'Globally'">
+                <span ng-if="getInterpreterRunningOption(setting.id) !== 'Per User'">
+                  {{getPerNoteOption(setting.id)}}
+                </span>
+                <span ng-if="getInterpreterRunningOption(setting.id) === 'Per User'">
+                  {{getPerUserOption(setting.id)}}
+                </span>
+                  <span class="caret"></span>
+              </button>
+              <ul class="dropdown-menu" role="menu">
+                <li
+                  ng-if="getInterpreterRunningOption(setting.id) === 'Globally'">
+                  <a style="cursor:pointer"
+                     uib-tooltip="Single interpreter instance are shared across notes"
+                     ng-click="setPerNoteOption(setting.id, 'shared')">
+                    shared per note
+                  </a>
+                </li>
+
+                <li>
+                  <a style="cursor:pointer"
+                     ng-if="getInterpreterRunningOption(setting.id) === 'Per Note'"
+                     uib-tooltip="Separate Interpreter instance for each note"
+                     ng-click="setPerNoteOption(setting.id, 'scoped')">
+                    scoped per note
+                  </a>
+                </li>
+                <li>
+                  <a style="cursor:pointer"
+                     ng-if="getInterpreterRunningOption(setting.id) === 'Per User'"
+                     uib-tooltip="Separate Interpreter instance for each note"
+                     ng-click="setPerUserOption(setting.id, 'scoped')">
+                    scoped per user
+                  </a>
+                </li>
+
+                <li>
+                  <a style="cursor:pointer"
+                     ng-if="getInterpreterRunningOption(setting.id) === 'Per Note'"
+                     uib-tooltip="Separate Interpreter process for each note"
+                     ng-click="setPerNoteOption(setting.id, 'isolated')">
+                    isolated per note
+                  </a>
+                </li>
+                <li>
+                  <a style="cursor:pointer"
+                     ng-if="getInterpreterRunningOption(setting.id) === 'Per User'"
+                     uib-tooltip="Separate Interpreter process for each note"
+                     ng-click="setPerUserOption(setting.id, 'isolated')">
+                    isolated per user
+                  </a>
+                </li>
+              </ul>
+            </span>
+              process
+              <a class="fa fa-info-circle interpreter-binding-mode-info-link"
+                 aria-hidden="true"
+                 uib-tooltip="Can manage interpreter sessions differently by setting this option. Click this button to learn more"
+                 ng-href="{{getInterpreterBindingModeDocsLink()}}" target="_blank"></a>
+            <span ng-if="getInterpreterRunningOption(setting.id) === 'Per User' && ticket.principal !== 'anonymous'">
+              <span ng-if="getPerNoteOption(setting.id) === 'shared'">
+                <button type="button" class="btn btn-default btn-xs"
+                        ng-click="setPerNoteOption(setting.id, 'scoped')"
+                        data-toggle="dropdown">
+                  <i class="fa fa-plus"></i>
+                </button>
+              </span>
+            </span>
+            </div>
+          </div>
+          <div class="row interpreter"
+               style="margin-top: 6px;"
+               ng-if="getInterpreterRunningOption(setting.id) === 'Per User'
+                      && ticket.principal !== 'anonymous'
+                      && getPerNoteOption(setting.id) !== 'shared'">
+            <div class="col-md-12">
+              <span>
+                <span class="hidden-xs" style="padding-left: 190px;">And </span>
+                <span class="visible-xs" style="padding-left: 0px;">And </span>
+                <span class="btn-group">
+                  <button type="button" class="btn btn-default btn-xs dropdown-toggle"
+                          data-toggle="dropdown"
+                          ng-disabled="true">
+                    <span>
+                      Per Note
+                    </span>
+                    <span class="caret"></span>
+                  </button>
+                </span>
+                in
+                <span class="btn-group">
+                  <button type="button" class="btn btn-default btn-xs dropdown-toggle"
+                          data-toggle="dropdown">
+                    <span>
+                      {{getPerNoteOption(setting.id)}}
+                    </span>
+                    <span class="caret"></span>
+                  </button>
+                  <ul class="dropdown-menu" role="menu">
+                    <li>
+                      <a style="cursor:pointer"
+                         uib-tooltip="Separate Interpreter instance for each note"
+                         ng-click="setPerNoteOption(setting.id, 'scoped')">
+                        scoped per note
+                      </a>
+                    </li>
+                    <li>
+                      <a style="cursor:pointer"
+                         uib-tooltip="Separate Interpreter process for each note"
+                         ng-click="setPerNoteOption(setting.id, 'isolated')">
+                        isolated per note
+                      </a>
+                    </li>
+                  </ul>
+                </span>
+                process.
+                <button type="button" class="btn btn-default btn-xs"
+                        ng-click="setPerNoteOption(setting.id, 'shared')"
+                        data-toggle="dropdown">
+                  <i class="fa fa-minus"></i>
+                </button>
+              </span>
+            </div>
+          </div>
+        </div>
+        <div class="row interpreter" style="margin-top: 5px;"
+             ng-show="getInterpreterRunningOption(setting.id)=='Per User' && getPerUserOption(setting.id)=='isolated'">
+          <div class="col-md-12">
+            <div class="checkbox remove-margin-top-bottom">
+          <span class="input-group" style="line-height:30px;">
+            <label>
+              <input type="checkbox" style="width:20px" ng-model="newInterpreterSetting.option.isUserImpersonate" />
+                User Impersonate
+            </label>
+          </span>
+            </div>
+          </div>
+        </div>
+        <div class="row interpreter">
+          <div class="col-md-12">
+            <div class="checkbox remove-margin-top-bottom">
+          <span class="input-group" style="line-height:30px;">
+            <label>
+              <input type="checkbox" style="width:20px" id="isExistingProcess" ng-model="newInterpreterSetting.option.isExistingProcess"/>
+              Connect to existing process
+            </label>
+          </span>
+            </div>
+          </div>
+        </div>
+        <div class="row interpreter" ng-show="newInterpreterSetting.option.isExistingProcess" >
+          <div class="col-md-12">
+            <b>Host</b>
+            <input id="newInterpreterSettingHost" input pu-elastic-input
+                   pu-elastic-input-minwidth="180px" ng-model="newInterpreterSetting.option.host"/>
+          </div>
+          <div class="col-md-12">
+            <b>Port</b>
+            <input id="newInterpreterSettingPort" input pu-elastic-input
+                   pu-elastic-input-minwidth="180px" ng-model="newInterpreterSetting.option.port"/>
+          </div>
+        </div>
+        <div class="row interpreter">
+          <div class="col-md-12">
+            <div class="checkbox remove-margin-top-bottom">
+          <span class="input-group" style="line-height:30px;">
+            <label>
+              <input type="checkbox" style="width:20px !important" id="idShowPermission" ng-click="togglePermissions('newInterpreter')" ng-model="newInterpreterSetting.option.setPermission"/>
+               Set permission
+            </label>
+          </span>
+            </div>
+          </div>
+        </div>
+        <br/>
+
+        <div class="row interpreter">
+          <div class="col-md-12">
+            <!-- permissions -->
+            <div ng-show="newInterpreterSetting.option.setPermission" class="permissionsForm">
+              <div>
+                <p>
+                  Enter comma separated users and groups in the fields. <br />
+                  Empty field (*) implies anyone can run this interpreter.
+                </p>
+                <div>
+                  <span class="owners">Owners </span>
+                  <select id="newInterpreterOwners" class="form-control" multiple="multiple">
+                    <option ng-repeat="owner in newInterpreterSetting.option.owners" selected="selected">{{owner}}</option>
+                  </select>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <div>
+          <h5>Properties</h5>
+          <table class="table table-striped properties">
+            <tr>
+              <th>name</th>
+              <th>value</th>
+              <th>action</th>
+              <th>description</th>
+            </tr>
+            <tr ng-repeat="key in newInterpreterSetting.properties | sortByKey">
+              <td>{{key}}</td>
+              <td style="vertical-align: middle;">
+                <textarea ng-if="newInterpreterSetting.properties[key].type === 'textarea'"
+                          msd-elastic ng-model="newInterpreterSetting.properties[key].value"></textarea>
+                <input ng-if="newInterpreterSetting.properties[key].type === 'string'"
+                       type="text" msd-elastic ng-model="newInterpreterSetting.properties[key].value" />
+                <input ng-if="newInterpreterSetting.properties[key].type === 'number'"
+                       type="text" number-widget msd-elastic ng-model="newInterpreterSetting.properties[key].value" />
+                <input ng-if="newInterpreterSetting.properties[key].type === 'url'"
+                       type="text" msd-elastic ng-model="newInterpreterSetting.properties[key].value" />
+                <input ng-if="newInterpreterSetting.properties[key].type === 'password'"
+                       type="password" msd-elastic ng-model="newInterpreterSetting.properties[key].value" />
+                <input ng-if="newInterpreterSetting.properties[key].type === 'boolean'"
+                       type="checkbox" msd-elastic ng-model="newInterpreterSetting.properties[key].value" />
+              </td>
+              <td style="vertical-align: middle;">
+                <button class="btn btn-default btn-sm fa fa-remove" ng-click="removeInterpreterProperty(key)">
+                </button>
+              </td>
+              <td style="vertical-align: middle;">
+                {{newInterpreterSetting.properties[key].description}}
+              </td>
+            </tr>
+
+            <tr>
+              <td style="vertical-align: middle;">
+                <input pu-elastic-input pu-elastic-input-minwidth="180px"
+                       ng-model="newInterpreterSetting.propertyKey" />
+              </td>
+              <td style="vertical-align: middle;" ng-switch on="newInterpreterSetting.propertyType">
+                <textarea ng-switch-default msd-elastic ng-model="newInterpreterSetting.propertyValue"></textarea>
+                <input ng-switch-when="string" type="text" msd-elastic ng-model="newInterpreterSetting.propertyValue" />
+                <input ng-switch-when="number" type="text" number-widget msd-elastic ng-model="newInterpreterSetting.propertyValue" />
+                <input ng-switch-when="url" type="text" msd-elastic ng-model="newInterpreterSetting.propertyValue" />
+                <input ng-switch-when="password" type="password" msd-elastic ng-model="newInterpreterSetting.propertyValue" />
+                <input ng-switch-when="checkbox" type="checkbox" msd-elastic ng-model="newInterpreterSetting.propertyValue" />
+              </td>
+              <td style="vertical-align: middle;">
+                <select msd-elastic ng-model="newInterpreterSetting.propertyType" ng-init="newInterpreterSetting.propertyType=interpreterPropertyTypes[0]"
+                        ng-options="item for item in interpreterPropertyTypes" ng-change="defaultValueByType(newInterpreterSetting)">
+                </select>
+                <button class="btn btn-default btn-sm fa fa-plus" ng-click="addNewInterpreterProperty()">
+                </button>
+              </td>
+              <td></td>
+            </tr>
+          </table>
+        </div>
+
+        <div>
+          <h5>Dependencies</h5>
+          <table class="table table-striped properties">
+            <tr>
+              <th>artifact</th>
+              <th>exclude</th>
+              <th>action</th>
+            </tr>
+
+            <tr ng-repeat="dep in newInterpreterSetting.dependencies">
+              <td>
+                <input ng-model="dep.groupArtifactVersion" style="width:100%" />
+              </td>
+              <td>
+                <textarea msd-elastic ng-model="dep.exclusions"
+                          ng-list
+                          placeholder="(Optional) comma separated groupId:artifactId list">
+                </textarea>
+              </td>
+              <td>
+                <button class="btn btn-default btn-sm fa fa-remove"
+                     ng-click="removeInterpreterDependency(dep.groupArtifactVersion)">
+                </button>
+              </td>
+            </tr>
+
+            <tr>
+              <td>
+                <input ng-model="newInterpreterSetting.depArtifact"
+                       placeholder="groupId:artifactId:version or local file path"
+                       style="width: 100%" />
+              </td>
+              <td>
+                <textarea msd-elastic ng-model="newInterpreterSetting.depExclude"
+                          ng-list
+                          placeholder="(Optional) comma separated groupId:artifactId list">
+                </textarea>
+              </td>
+              <td>
+                <button class="btn btn-default btn-sm fa fa-plus" ng-click="addNewInterpreterDependency()">
+                </button>
+              </td>
+            </tr>
+          </table>
+        </div>
+
+        <span class="btn btn-primary" ng-click="addNewInterpreterSetting()">
+          Save
+        </span>
+        <span class="btn btn-default" ng-click="cancelInterpreterSetting()">
+          Cancel
+        </span>
+      </div>
+    </div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/interpreter/interpreter-create/interpreter-create.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/interpreter/interpreter-create/interpreter-create.html b/zeppelin-web/src/app/interpreter/interpreter-create/interpreter-create.html
deleted file mode 100644
index 1fa60f8..0000000
--- a/zeppelin-web/src/app/interpreter/interpreter-create/interpreter-create.html
+++ /dev/null
@@ -1,383 +0,0 @@
-<!--
-Licensed 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.
--->
-<div>
-  <div class="row interpreter">
-    <div class="col-md-12" ng-show="showAddNewSetting">
-      <hr />
-      <div class="interpreterSettingAdd">
-        <h4>Create new interpreter</h4>
-
-        <div class="form-group" style="width:200px">
-          <b>Interpreter Name</b>
-          <input id="newInterpreterSettingName" input pu-elastic-input
-                 pu-elastic-input-minwidth="180px" ng-model="newInterpreterSetting.name" />
-        </div>
-
-        <b>Interpreter group</b>
-        <div class="form-group"
-             style="width:180px">
-          <select class="form-control input-sm" ng-model="newInterpreterSetting.group"
-                  ng-change="newInterpreterGroupChange()">
-            <option ng-repeat="availableInterpreter in availableInterpreters | unique: 'name'| orderBy: 'name'" value="{{availableInterpreter.name}}">
-              {{availableInterpreter.name}}
-            </option>
-          </select>
-        </div>
-
-        <div>
-          <h5>Option</h5>
-          <div class="row interpreter" style="margin-top: 5px;">
-            <div class="col-md-6">
-              The interpreter will be instantiated
-              <span class="btn-group">
-              <button type="button" class="btn btn-default btn-xs dropdown-toggle"
-                      data-toggle="dropdown">
-                {{getInterpreterRunningOption(setting.id)}} <span class="caret"></span>
-              </button>
-              <ul class="dropdown-menu" role="menu">
-                <li>
-                  <a style="cursor:pointer"
-                     ng-click="setInterpreterRunningOption(setting.id, 'shared', 'shared')">
-                    Globally
-                  </a>
-                </li>
-                <li>
-                  <a style="cursor:pointer"
-                     ng-click="setInterpreterRunningOption(setting.id, 'scoped', '')">
-                    Per Note
-                  </a>
-                </li>
-                <li ng-if="ticket.principal !== 'anonymous'">
-                  <a style="cursor:pointer"
-                     ng-click="setInterpreterRunningOption(setting.id, 'shared', 'scoped')">
-                    Per User
-                  </a>
-                </li>
-              </ul>
-            </span>
-              in
-              <span class="btn-group">
-              <button type="button" class="btn btn-default btn-xs dropdown-toggle"
-                      data-toggle="dropdown"
-                      ng-disabled="getInterpreterRunningOption(setting.id) === 'Globally'">
-                <span ng-if="getInterpreterRunningOption(setting.id) !== 'Per User'">
-                  {{getPerNoteOption(setting.id)}}
-                </span>
-                <span ng-if="getInterpreterRunningOption(setting.id) === 'Per User'">
-                  {{getPerUserOption(setting.id)}}
-                </span>
-                  <span class="caret"></span>
-              </button>
-              <ul class="dropdown-menu" role="menu">
-                <li
-                  ng-if="getInterpreterRunningOption(setting.id) === 'Globally'">
-                  <a style="cursor:pointer"
-                     uib-tooltip="Single interpreter instance are shared across notes"
-                     ng-click="setPerNoteOption(setting.id, 'shared')">
-                    shared per note
-                  </a>
-                </li>
-
-                <li>
-                  <a style="cursor:pointer"
-                     ng-if="getInterpreterRunningOption(setting.id) === 'Per Note'"
-                     uib-tooltip="Separate Interpreter instance for each note"
-                     ng-click="setPerNoteOption(setting.id, 'scoped')">
-                    scoped per note
-                  </a>
-                </li>
-                <li>
-                  <a style="cursor:pointer"
-                     ng-if="getInterpreterRunningOption(setting.id) === 'Per User'"
-                     uib-tooltip="Separate Interpreter instance for each note"
-                     ng-click="setPerUserOption(setting.id, 'scoped')">
-                    scoped per user
-                  </a>
-                </li>
-
-                <li>
-                  <a style="cursor:pointer"
-                     ng-if="getInterpreterRunningOption(setting.id) === 'Per Note'"
-                     uib-tooltip="Separate Interpreter process for each note"
-                     ng-click="setPerNoteOption(setting.id, 'isolated')">
-                    isolated per note
-                  </a>
-                </li>
-                <li>
-                  <a style="cursor:pointer"
-                     ng-if="getInterpreterRunningOption(setting.id) === 'Per User'"
-                     uib-tooltip="Separate Interpreter process for each note"
-                     ng-click="setPerUserOption(setting.id, 'isolated')">
-                    isolated per user
-                  </a>
-                </li>
-              </ul>
-            </span>
-              process
-              <a class="fa fa-info-circle interpreter-binding-mode-info-link"
-                 aria-hidden="true"
-                 uib-tooltip="Can manage interpreter sessions differently by setting this option. Click this button to learn more"
-                 ng-href="{{getInterpreterBindingModeDocsLink()}}" target="_blank"></a>
-            <span ng-if="getInterpreterRunningOption(setting.id) === 'Per User' && ticket.principal !== 'anonymous'">
-              <span ng-if="getPerNoteOption(setting.id) === 'shared'">
-                <button type="button" class="btn btn-default btn-xs"
-                        ng-click="setPerNoteOption(setting.id, 'scoped')"
-                        data-toggle="dropdown">
-                  <i class="fa fa-plus"></i>
-                </button>
-              </span>
-            </span>
-            </div>
-          </div>
-          <div class="row interpreter"
-               style="margin-top: 6px;"
-               ng-if="getInterpreterRunningOption(setting.id) === 'Per User'
-                      && ticket.principal !== 'anonymous'
-                      && getPerNoteOption(setting.id) !== 'shared'">
-            <div class="col-md-12">
-              <span>
-                <span class="hidden-xs" style="padding-left: 190px;">And </span>
-                <span class="visible-xs" style="padding-left: 0px;">And </span>
-                <span class="btn-group">
-                  <button type="button" class="btn btn-default btn-xs dropdown-toggle"
-                          data-toggle="dropdown"
-                          ng-disabled="true">
-                    <span>
-                      Per Note
-                    </span>
-                    <span class="caret"></span>
-                  </button>
-                </span>
-                in
-                <span class="btn-group">
-                  <button type="button" class="btn btn-default btn-xs dropdown-toggle"
-                          data-toggle="dropdown">
-                    <span>
-                      {{getPerNoteOption(setting.id)}}
-                    </span>
-                    <span class="caret"></span>
-                  </button>
-                  <ul class="dropdown-menu" role="menu">
-                    <li>
-                      <a style="cursor:pointer"
-                         uib-tooltip="Separate Interpreter instance for each note"
-                         ng-click="setPerNoteOption(setting.id, 'scoped')">
-                        scoped per note
-                      </a>
-                    </li>
-                    <li>
-                      <a style="cursor:pointer"
-                         uib-tooltip="Separate Interpreter process for each note"
-                         ng-click="setPerNoteOption(setting.id, 'isolated')">
-                        isolated per note
-                      </a>
-                    </li>
-                  </ul>
-                </span>
-                process.
-                <button type="button" class="btn btn-default btn-xs"
-                        ng-click="setPerNoteOption(setting.id, 'shared')"
-                        data-toggle="dropdown">
-                  <i class="fa fa-minus"></i>
-                </button>
-              </span>
-            </div>
-          </div>
-        </div>
-        <div class="row interpreter" style="margin-top: 5px;"
-             ng-show="getInterpreterRunningOption(setting.id)=='Per User' && getPerUserOption(setting.id)=='isolated'">
-          <div class="col-md-12">
-            <div class="checkbox remove-margin-top-bottom">
-          <span class="input-group" style="line-height:30px;">
-            <label>
-              <input type="checkbox" style="width:20px" ng-model="newInterpreterSetting.option.isUserImpersonate" />
-                User Impersonate
-            </label>
-          </span>
-            </div>
-          </div>
-        </div>
-        <div class="row interpreter">
-          <div class="col-md-12">
-            <div class="checkbox remove-margin-top-bottom">
-          <span class="input-group" style="line-height:30px;">
-            <label>
-              <input type="checkbox" style="width:20px" id="isExistingProcess" ng-model="newInterpreterSetting.option.isExistingProcess"/>
-              Connect to existing process
-            </label>
-          </span>
-            </div>
-          </div>
-        </div>
-        <div class="row interpreter" ng-show="newInterpreterSetting.option.isExistingProcess" >
-          <div class="col-md-12">
-            <b>Host</b>
-            <input id="newInterpreterSettingHost" input pu-elastic-input
-                   pu-elastic-input-minwidth="180px" ng-model="newInterpreterSetting.option.host"/>
-          </div>
-          <div class="col-md-12">
-            <b>Port</b>
-            <input id="newInterpreterSettingPort" input pu-elastic-input
-                   pu-elastic-input-minwidth="180px" ng-model="newInterpreterSetting.option.port"/>
-          </div>
-        </div>
-        <div class="row interpreter">
-          <div class="col-md-12">
-            <div class="checkbox remove-margin-top-bottom">
-          <span class="input-group" style="line-height:30px;">
-            <label>
-              <input type="checkbox" style="width:20px !important" id="idShowPermission" ng-click="togglePermissions('newInterpreter')" ng-model="newInterpreterSetting.option.setPermission"/>
-               Set permission
-            </label>
-          </span>
-            </div>
-          </div>
-        </div>
-        <br/>
-
-        <div class="row interpreter">
-          <div class="col-md-12">
-            <!-- permissions -->
-            <div ng-show="newInterpreterSetting.option.setPermission" class="permissionsForm">
-              <div>
-                <p>
-                  Enter comma separated users and groups in the fields. <br />
-                  Empty field (*) implies anyone can run this interpreter.
-                </p>
-                <div>
-                  <span class="owners">Owners </span>
-                  <select id="newInterpreterOwners" class="form-control" multiple="multiple">
-                    <option ng-repeat="owner in newInterpreterSetting.option.owners" selected="selected">{{owner}}</option>
-                  </select>
-                </div>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <div>
-          <h5>Properties</h5>
-          <table class="table table-striped properties">
-            <tr>
-              <th>name</th>
-              <th>value</th>
-              <th>action</th>
-              <th>description</th>
-            </tr>
-            <tr ng-repeat="key in newInterpreterSetting.properties | sortByKey">
-              <td>{{key}}</td>
-              <td style="vertical-align: middle;">
-                <textarea ng-if="newInterpreterSetting.properties[key].type === 'textarea'"
-                          msd-elastic ng-model="newInterpreterSetting.properties[key].value"></textarea>
-                <input ng-if="newInterpreterSetting.properties[key].type === 'string'"
-                       type="text" msd-elastic ng-model="newInterpreterSetting.properties[key].value" />
-                <input ng-if="newInterpreterSetting.properties[key].type === 'number'"
-                       type="text" widget-number msd-elastic ng-model="newInterpreterSetting.properties[key].value" />
-                <input ng-if="newInterpreterSetting.properties[key].type === 'url'"
-                       type="text" msd-elastic ng-model="newInterpreterSetting.properties[key].value" />
-                <input ng-if="newInterpreterSetting.properties[key].type === 'password'"
-                       type="password" msd-elastic ng-model="newInterpreterSetting.properties[key].value" />
-                <input ng-if="newInterpreterSetting.properties[key].type === 'boolean'"
-                       type="checkbox" msd-elastic ng-model="newInterpreterSetting.properties[key].value" />
-              </td>
-              <td style="vertical-align: middle;">
-                <button class="btn btn-default btn-sm fa fa-remove" ng-click="removeInterpreterProperty(key)">
-                </button>
-              </td>
-              <td style="vertical-align: middle;">
-                {{newInterpreterSetting.properties[key].description}}
-              </td>
-            </tr>
-
-            <tr>
-              <td style="vertical-align: middle;">
-                <input pu-elastic-input pu-elastic-input-minwidth="180px"
-                       ng-model="newInterpreterSetting.propertyKey" />
-              </td>
-              <td style="vertical-align: middle;" ng-switch on="newInterpreterSetting.propertyType">
-                <textarea ng-switch-default msd-elastic ng-model="newInterpreterSetting.propertyValue"></textarea>
-                <input ng-switch-when="string" type="text" msd-elastic ng-model="newInterpreterSetting.propertyValue" />
-                <input ng-switch-when="number" type="text" widget-number msd-elastic ng-model="newInterpreterSetting.propertyValue" />
-                <input ng-switch-when="url" type="text" msd-elastic ng-model="newInterpreterSetting.propertyValue" />
-                <input ng-switch-when="password" type="password" msd-elastic ng-model="newInterpreterSetting.propertyValue" />
-                <input ng-switch-when="checkbox" type="checkbox" msd-elastic ng-model="newInterpreterSetting.propertyValue" />
-              </td>
-              <td style="vertical-align: middle;">
-                <select msd-elastic ng-model="newInterpreterSetting.propertyType" ng-init="newInterpreterSetting.propertyType=interpreterPropertyTypes[0]"
-                        ng-options="item for item in interpreterPropertyTypes" ng-change="defaultValueByType(newInterpreterSetting)">
-                </select>
-                <button class="btn btn-default btn-sm fa fa-plus" ng-click="addNewInterpreterProperty()">
-                </button>
-              </td>
-              <td></td>
-            </tr>
-          </table>
-        </div>
-
-        <div>
-          <h5>Dependencies</h5>
-          <table class="table table-striped properties">
-            <tr>
-              <th>artifact</th>
-              <th>exclude</th>
-              <th>action</th>
-            </tr>
-
-            <tr ng-repeat="dep in newInterpreterSetting.dependencies">
-              <td>
-                <input ng-model="dep.groupArtifactVersion" style="width:100%" />
-              </td>
-              <td>
-                <textarea msd-elastic ng-model="dep.exclusions"
-                          ng-list
-                          placeholder="(Optional) comma separated groupId:artifactId list">
-                </textarea>
-              </td>
-              <td>
-                <button class="btn btn-default btn-sm fa fa-remove"
-                     ng-click="removeInterpreterDependency(dep.groupArtifactVersion)">
-                </button>
-              </td>
-            </tr>
-
-            <tr>
-              <td>
-                <input ng-model="newInterpreterSetting.depArtifact"
-                       placeholder="groupId:artifactId:version or local file path"
-                       style="width: 100%" />
-              </td>
-              <td>
-                <textarea msd-elastic ng-model="newInterpreterSetting.depExclude"
-                          ng-list
-                          placeholder="(Optional) comma separated groupId:artifactId list">
-                </textarea>
-              </td>
-              <td>
-                <button class="btn btn-default btn-sm fa fa-plus" ng-click="addNewInterpreterDependency()">
-                </button>
-              </td>
-            </tr>
-          </table>
-        </div>
-
-        <span class="btn btn-primary" ng-click="addNewInterpreterSetting()">
-          Save
-        </span>
-        <span class="btn btn-default" ng-click="cancelInterpreterSetting()">
-          Cancel
-        </span>
-      </div>
-    </div>
-  </div>
-</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/interpreter/interpreter-item.directive.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/interpreter/interpreter-item.directive.js b/zeppelin-web/src/app/interpreter/interpreter-item.directive.js
new file mode 100644
index 0000000..4bde44d
--- /dev/null
+++ b/zeppelin-web/src/app/interpreter/interpreter-item.directive.js
@@ -0,0 +1,31 @@
+/*
+ * Licensed 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.
+ */
+
+angular.module('zeppelinWebApp').directive('interpreterItem', InterpreterItemDirective)
+
+function InterpreterItemDirective ($timeout) {
+  'ngInject'
+
+  return {
+    restrict: 'A',
+    link: function (scope, element, attr) {
+      if (scope.$last === true) {
+        $timeout(function () {
+          let id = 'ngRenderFinished'
+          scope.$emit(id)
+        })
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6bd6c708/zeppelin-web/src/app/interpreter/interpreter.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/interpreter/interpreter.html b/zeppelin-web/src/app/interpreter/interpreter.html
index 617ce38..c1d90cc 100644
--- a/zeppelin-web/src/app/interpreter/interpreter.html
+++ b/zeppelin-web/src/app/interpreter/interpreter.html
@@ -72,7 +72,7 @@ limitations under the License.
           </a>
         </li>
         <li class="liVertical">
-          <div ng-include src="'components/repository-create/repository-dialog.html'"></div>
+          <div ng-include src="'app/interpreter/repository-create.html'"></div>
           <div class="btn btn-default"
                data-toggle="modal"
                data-target="#repoModal">
@@ -83,11 +83,11 @@ limitations under the License.
     </div>
   </div>
 
-  <div ng-include src="'app/interpreter/interpreter-create/interpreter-create.html'"></div>
+  <div ng-include src="'app/interpreter/interpreter-create.html'"></div>
 </div>
 
-<div class="box width-full"
-     ng-repeat="setting in interpreterSettings | orderBy: 'name' | filter: {name:searchInterpreter} " interpreter-directive>
+<div interpreter-item class="box width-full"
+     ng-repeat="setting in interpreterSettings | orderBy: 'name' | filter: {name:searchInterpreter}" >
   <div id="{{setting.name | lowercase}}">
     <div class="row interpreter">
 
@@ -443,7 +443,7 @@ limitations under the License.
             <td style="vertical-align: middle;" ng-switch on="setting.propertyType">
               <textarea ng-switch-default msd-elastic ng-model="setting.propertyValue"></textarea>
               <input ng-switch-when="string" type="text" msd-elastic ng-model="setting.propertyValue"/>
-              <input ng-switch-when="number" type="text" msd-elastic ng-model="setting.propertyValue" widget-number />
+              <input ng-switch-when="number" type="text" msd-elastic ng-model="setting.propertyValue" number-widget />
               <input ng-switch-when="url" type="text" msd-elastic ng-model="setting.propertyValue" />
               <input ng-switch-when="password" type="password" msd-elastic ng-model="setting.propertyValue" />
               <input ng-switch-when="checkbox" type="checkbox" msd-elastic ng-model="setting.propertyValue" />