Merge branch 'master' of https://github.com/apache/zeppelin into ZEPPELIN-1935

This commit is contained in:
Renjith Kamath 2017-01-11 22:39:53 +05:30
commit dfabe3ac3d
88 changed files with 6814 additions and 6517 deletions

1
.gitignore vendored
View file

@ -45,6 +45,7 @@ zeppelin-web/src/fonts/patua-one*
zeppelin-web/src/fonts/google-fonts.css
zeppelin-web/.sass-cache
zeppelin-web/npm-debug.log
zeppelin-web/yarn-error.log
zeppelin-web/bower_components
zeppelin-web/yarn.lock
**nbproject/

View file

@ -42,19 +42,19 @@ matrix:
# Test all modules with spark 2.0.2 and scala 2.11
- jdk: "oraclejdk7"
env: SCALA_VER="2.11" SPARK_VER="2.0.2" HADOOP_VER="2.6" PROFILE="-Pspark-2.0 -Phadoop-2.6 -Ppyspark -Psparkr -Pscalding -Pexamples -Pscala-2.11" BUILD_FLAG="package -Pbuild-distr -DskipRat" TEST_FLAG="verify -Pusing-packaged-distr -DskipRat" TEST_PROJECTS=""
env: SCALA_VER="2.11" SPARK_VER="2.0.2" HADOOP_VER="2.6" PROFILE="-Pspark-2.0 -Phadoop-2.6 -Ppyspark -Psparkr -Pscalding -Phelium-dev -Pexamples -Pscala-2.11" BUILD_FLAG="package -Pbuild-distr -DskipRat" TEST_FLAG="verify -Pusing-packaged-distr -DskipRat" TEST_PROJECTS=""
# Test all modules with spark 2.1.0 and scala 2.11
- jdk: "oraclejdk7"
env: SCALA_VER="2.11" SPARK_VER="2.1.0" HADOOP_VER="2.6" PROFILE="-Pspark-2.0 -Phadoop-2.6 -Ppyspark -Psparkr -Pscalding -Pexamples -Pscala-2.11" BUILD_FLAG="package -Pbuild-distr -DskipRat" TEST_FLAG="verify -Pusing-packaged-distr -DskipRat" TEST_PROJECTS=""
env: SCALA_VER="2.11" SPARK_VER="2.1.0" HADOOP_VER="2.6" PROFILE="-Pspark-2.0 -Phadoop-2.6 -Ppyspark -Psparkr -Pscalding -Phelium-dev -Pexamples -Pscala-2.11" BUILD_FLAG="package -Pbuild-distr -DskipRat" TEST_FLAG="verify -Pusing-packaged-distr -DskipRat" TEST_PROJECTS=""
# Test all modules with scala 2.10
- jdk: "oraclejdk7"
env: SCALA_VER="2.10" SPARK_VER="1.6.3" HADOOP_VER="2.6" PROFILE="-Pspark-1.6 -Pr -Phadoop-2.6 -Ppyspark -Psparkr -Pscalding -Pbeam -Pexamples -Pscala-2.10" BUILD_FLAG="package -Pbuild-distr -DskipRat" TEST_FLAG="verify -Pusing-packaged-distr -DskipRat" TEST_PROJECTS=""
env: SCALA_VER="2.10" SPARK_VER="1.6.3" HADOOP_VER="2.6" PROFILE="-Pspark-1.6 -Pr -Phadoop-2.6 -Ppyspark -Psparkr -Pscalding -Pbeam -Phelium-dev -Pexamples -Pscala-2.10" BUILD_FLAG="package -Pbuild-distr -DskipRat" TEST_FLAG="verify -Pusing-packaged-distr -DskipRat" TEST_PROJECTS=""
# Test all modules with scala 2.11
- jdk: "oraclejdk7"
env: SCALA_VER="2.11" SPARK_VER="1.6.3" HADOOP_VER="2.6" PROFILE="-Pspark-1.6 -Pr -Phadoop-2.6 -Ppyspark -Psparkr -Pscalding -Pexamples -Pscala-2.11" BUILD_FLAG="package -Pbuild-distr -DskipRat" TEST_FLAG="verify -Pusing-packaged-distr -DskipRat" TEST_PROJECTS=""
env: SCALA_VER="2.11" SPARK_VER="1.6.3" HADOOP_VER="2.6" PROFILE="-Pspark-1.6 -Pr -Phadoop-2.6 -Ppyspark -Psparkr -Pscalding -Phelium-dev -Pexamples -Pscala-2.11" BUILD_FLAG="package -Pbuild-distr -DskipRat" TEST_FLAG="verify -Pusing-packaged-distr -DskipRat" TEST_PROJECTS=""
# Test spark module for 1.5.2
- jdk: "oraclejdk7"
@ -66,7 +66,7 @@ matrix:
# Test selenium with spark module for 1.6.3
- jdk: "oraclejdk7"
env: TEST_SELENIUM="true" SCALA_VER="2.10" SPARK_VER="1.6.3" HADOOP_VER="2.6" PROFILE="-Pspark-1.6 -Phadoop-2.6 -Ppyspark -Pexamples" BUILD_FLAG="package -DskipTests -DskipRat" TEST_FLAG="verify -DskipRat" TEST_PROJECTS="-pl zeppelin-interpreter,zeppelin-zengine,zeppelin-server,zeppelin-display,spark-dependencies,spark -Dtest=org.apache.zeppelin.AbstractFunctionalSuite -DfailIfNoTests=false"
env: TEST_SELENIUM="true" SCALA_VER="2.10" SPARK_VER="1.6.3" HADOOP_VER="2.6" PROFILE="-Pspark-1.6 -Phadoop-2.6 -Ppyspark -Phelium-dev -Pexamples" BUILD_FLAG="package -DskipTests -DskipRat" TEST_FLAG="verify -DskipRat" TEST_PROJECTS="-pl zeppelin-interpreter,zeppelin-zengine,zeppelin-server,zeppelin-display,spark-dependencies,spark -Dtest=org.apache.zeppelin.AbstractFunctionalSuite -DfailIfNoTests=false"
# Test python/pyspark with python 2
- jdk: "oraclejdk7"

View file

@ -31,6 +31,9 @@ REM set ZEPPELIN_NOTEBOOK_HOMESCREEN REM Id of notebook to be displayed in home
REM set ZEPPELIN_NOTEBOOK_HOMESCREEN_HIDE REM hide homescreen notebook from list when this value set to "true". default "false"
REM set ZEPPELIN_NOTEBOOK_S3_BUCKET REM Bucket where notebook saved
REM set ZEPPELIN_NOTEBOOK_S3_USER REM User in bucket where notebook saved. For example bucket/user/notebook/2A94M5J1Z/note.json
REM set ZEPPELIN_NOTEBOOK_S3_ENDPOINT REM Endpoint of the bucket
REM set ZEPPELIN_NOTEBOOK_S3_KMS_KEY_ID REM AWS KMS key ID
REM set ZEPPELIN_NOTEBOOK_S3_KMS_KEY_REGION REM AWS KMS key region
REM set ZEPPELIN_IDENT_STRING REM A string representing this instance of zeppelin. $USER by default.
REM set ZEPPELIN_NICENESS REM The scheduling priority for daemons. Defaults to 0.
REM set ZEPPELIN_INTERPRETER_LOCALREPO REM Local repository for interpreter's additional dependency loading

View file

@ -33,6 +33,8 @@
# export ZEPPELIN_NOTEBOOK_S3_BUCKET # Bucket where notebook saved
# export ZEPPELIN_NOTEBOOK_S3_ENDPOINT # Endpoint of the bucket
# export ZEPPELIN_NOTEBOOK_S3_USER # User in bucket where notebook saved. For example bucket/user/notebook/2A94M5J1Z/note.json
# export ZEPPELIN_NOTEBOOK_S3_KMS_KEY_ID # AWS KMS key ID
# export ZEPPELIN_NOTEBOOK_S3_KMS_KEY_REGION # AWS KMS key region
# export ZEPPELIN_IDENT_STRING # A string representing this instance of zeppelin. $USER by default.
# export ZEPPELIN_NICENESS # The scheduling priority for daemons. Defaults to 0.
# export ZEPPELIN_INTERPRETER_LOCALREPO # Local repository for interpreter's additional dependency loading

View file

@ -108,6 +108,16 @@
</property>
-->
<!-- provide region of your KMS key -->
<!-- See http://docs.aws.amazon.com/general/latest/gr/rande.html#kms_region for region codes names -->
<!--
<property>
<name>zeppelin.notebook.s3.kmsKeyRegion</name>
<value>us-east-1</value>
<description>AWS KMS key region in your AWS account</description>
</property>
-->
<!-- Use a custom encryption materials provider to encrypt data -->
<!-- No configuration is given to the provider, so you must use system properties or another means to configure -->
<!-- See https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/s3/model/EncryptionMaterialsProvider.html -->

View file

@ -60,7 +60,7 @@ You can check example applications under [./zeppelin-examples](https://github.co
In the development mode, you can run your Application in your IDE as a normal java application and see the result inside of Zeppelin notebook.
`org.apache.zeppelin.interpreter.dev.ZeppelinApplicationDevServer` can run Zeppelin Application in development mode.
`org.apache.zeppelin.helium.ZeppelinApplicationDevServer` can run Zeppelin Application in development mode.
```java
@ -73,7 +73,7 @@ public static void main(String[] args) throws Exception {
// run application in devlopment mode with given resource
// in this case, Clock.class.getName() will be the application class name
ZeppelinApplicationDevServer devServer = new ZeppelinApplicationDevServer(
org.apache.zeppelin.helium.ZeppelinApplicationDevServer devServer = new org.apache.zeppelin.helium.ZeppelinApplicationDevServer(
Clock.class.getName(), pool.getAll());
// start development mode

File diff suppressed because it is too large Load diff

View file

@ -130,6 +130,23 @@ Or using the following setting in **zeppelin-site.xml**:
</property>
```
In order to set custom KMS key region, set the following environment variable in the file **zeppelin-env.sh**:
```
export ZEPPELIN_NOTEBOOK_S3_KMS_KEY_REGION = kms-key-region
```
Or using the following setting in **zeppelin-site.xml**:
```
<property>
<name>zeppelin.notebook.s3.kmsKeyRegion</name>
<value>target-region</value>
<description>AWS KMS key region in your AWS account</description>
</property>
```
Format of `target-region` is described in more details [here](http://docs.aws.amazon.com/general/latest/gr/rande.html#kms_region) in second `Region` column (e.g. `us-east-1`).
#### Custom Encryption Materials Provider class
You may use a custom [``EncryptionMaterialsProvider``](https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/s3/model/EncryptionMaterialsProvider.html) class as long as it is available in the classpath and able to initialize itself from system properties or another mechanism. To use this, set the following environment variable in the file **zeppelin-env.sh**:
@ -238,4 +255,4 @@ export ZEPPELINHUB_API_TOKEN = ZeppelinHub token
export ZEPPELINHUB_API_ADDRESS = address of ZeppelinHub service (e.g. https://www.zeppelinhub.com)
```
You can get more information on generating `token` and using authentication on the corresponding [help page](http://help.zeppelinhub.com/zeppelin_integration/#add-a-new-zeppelin-instance-and-generate-a-token).
You can get more information on generating `token` and using authentication on the corresponding [help page](http://help.zeppelinhub.com/zeppelin_integration/#add-a-new-zeppelin-instance-and-generate-a-token).

89
helium-dev/pom.xml Normal file
View file

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You under the Apache License, Version 2.0
~ (the "License"); you may not use this file except in compliance with
~ the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.zeppelin</groupId>
<artifactId>zeppelin</artifactId>
<version>0.7.0-SNAPSHOT</version>
</parent>
<groupId>org.apache.zeppelin</groupId>
<artifactId>helium-dev</artifactId>
<version>0.7.0-SNAPSHOT</version>
<name>Zeppelin: Helium development interpreter</name>
<dependencies>
<dependency>
<groupId>org.apache.zeppelin</groupId>
<artifactId>zeppelin-interpreter</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/../../interpreter/helium-dev</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>false</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
<includeScope>runtime</includeScope>
</configuration>
</execution>
<execution>
<id>copy-artifact</id>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/../../interpreter/helium-dev</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>false</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
<includeScope>runtime</includeScope>
<artifactItems>
<artifactItem>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
<type>${project.packaging}</type>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View file

@ -14,7 +14,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.zeppelin.interpreter.dev;
package org.apache.zeppelin.helium;
import java.util.LinkedList;
import java.util.List;
@ -48,7 +49,7 @@ public class DevInterpreter extends Interpreter {
}
/**
* event handler for ZeppelinApplicationDevServer
* event handler for org.apache.zeppelin.helium.ZeppelinApplicationDevServer
*/
public static interface InterpreterEvent {
public InterpreterResult interpret(String st, InterpreterContext context);

View file

@ -14,23 +14,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.zeppelin.interpreter.dev;
package org.apache.zeppelin.helium;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import com.google.gson.Gson;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
import org.apache.log4j.PatternLayout;
import org.apache.zeppelin.helium.*;
import org.apache.zeppelin.interpreter.*;
import org.apache.zeppelin.interpreter.InterpreterResult.Code;
import org.apache.zeppelin.interpreter.remote.RemoteInterpreterEventClient;
import org.apache.zeppelin.resource.ResourceSet;
import org.apache.zeppelin.resource.WellKnownResourceName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -47,7 +44,7 @@ public class ZeppelinApplicationDevServer extends ZeppelinDevServer {
public ZeppelinApplicationDevServer(final String className, ResourceSet resourceSet) throws
Exception {
this(ZeppelinDevServer.DEFAULT_TEST_INTERPRETER_PORT, className, resourceSet);
this(Constants.ZEPPELIN_INTERPRETER_DEFAUlT_PORT, className, resourceSet);
}
public ZeppelinApplicationDevServer(int port, String className, ResourceSet resourceSet) throws

View file

@ -14,16 +14,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.zeppelin.interpreter.dev;
package org.apache.zeppelin.helium;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Properties;
import org.apache.thrift.TException;
import org.apache.zeppelin.helium.DevInterpreter.InterpreterEvent;
import org.apache.zeppelin.interpreter.*;
import org.apache.zeppelin.interpreter.dev.DevInterpreter.InterpreterEvent;
import org.apache.zeppelin.interpreter.remote.RemoteInterpreterEventClient;
import org.apache.zeppelin.interpreter.remote.RemoteInterpreterServer;
import org.slf4j.Logger;
@ -34,11 +34,10 @@ import org.slf4j.LoggerFactory;
*/
public class ZeppelinDevServer extends
RemoteInterpreterServer implements InterpreterEvent, InterpreterOutputChangeListener {
final Logger logger = LoggerFactory.getLogger(ZeppelinDevServer.class);
public static final int DEFAULT_TEST_INTERPRETER_PORT = 29914;
private static final Logger logger = LoggerFactory.getLogger(ZeppelinDevServer.class);
DevInterpreter interpreter = null;
InterpreterOutput out;
private DevInterpreter interpreter = null;
private InterpreterOutput out;
public ZeppelinDevServer(int port) throws TException {
super(port);
}
@ -47,21 +46,21 @@ public class ZeppelinDevServer extends
protected Interpreter getInterpreter(String sessionKey, String className) throws TException {
synchronized (this) {
InterpreterGroup interpreterGroup = getInterpreterGroup();
if (interpreterGroup == null) {
if (interpreterGroup == null || interpreterGroup.isEmpty()) {
createInterpreter(
"dev",
sessionKey,
DevInterpreter.class.getName(),
new HashMap<String, String>(),
"anonymous");
Interpreter intp = super.getInterpreter(sessionKey, className);
interpreter = (DevInterpreter) (
((LazyOpenInterpreter) intp).getInnerInterpreter());
interpreter.setInterpreterEvent(this);
notify();
}
}
Interpreter intp = super.getInterpreter(sessionKey, className);
interpreter = (DevInterpreter) (
((LazyOpenInterpreter) intp).getInnerInterpreter());
interpreter.setInterpreterEvent(this);
return super.getInterpreter(sessionKey, className);
}

View file

@ -0,0 +1,19 @@
[
{
"group": "dev",
"name": "dev",
"className": "org.apache.zeppelin.helium.DevInterpreter",
"properties": {
"port": {
"envName": "PORT",
"propertyName": "port",
"defaultValue": "jdbc:postgresql://localhost:5432/",
"description": "The URL for JDBC."
}
},
"editor": {
"language": "helium",
"editOnDblClick": false
}
}
]

View file

@ -19,7 +19,6 @@ import static org.apache.commons.lang.StringUtils.isEmpty;
import static org.apache.commons.lang.StringUtils.isNotEmpty;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.io.IOException;
import java.security.PrivilegedExceptionAction;
import java.sql.*;
import java.util.*;
@ -34,10 +33,7 @@ import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.alias.CredentialProvider;
import org.apache.hadoop.security.alias.CredentialProviderFactory;
import org.apache.zeppelin.interpreter.Interpreter;
import org.apache.zeppelin.interpreter.InterpreterContext;
import org.apache.zeppelin.interpreter.InterpreterException;
import org.apache.zeppelin.interpreter.InterpreterResult;
import org.apache.zeppelin.interpreter.*;
import org.apache.zeppelin.interpreter.InterpreterResult.Code;
import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion;
import org.apache.zeppelin.jdbc.security.JDBCSecurityImpl;
@ -481,6 +477,57 @@ public class JDBCInterpreter extends Interpreter {
return updatedCount < 0 && columnCount <= 0 ? true : false;
}
/*
inspired from https://github.com/postgres/pgadmin3/blob/794527d97e2e3b01399954f3b79c8e2585b908dd/
pgadmin/dlg/dlgProperty.cpp#L999-L1045
*/
protected ArrayList<String> splitSqlQueries(String sql) {
ArrayList<String> queries = new ArrayList<>();
StringBuilder query = new StringBuilder();
Character character;
Boolean antiSlash = false;
Boolean quoteString = false;
Boolean doubleQuoteString = false;
for (int item = 0; item < sql.length(); item++) {
character = sql.charAt(item);
if (character.equals('\\')) {
antiSlash = true;
}
if (character.equals('\'')) {
if (antiSlash) {
antiSlash = false;
} else if (quoteString) {
quoteString = false;
} else if (!doubleQuoteString) {
quoteString = true;
}
}
if (character.equals('"')) {
if (antiSlash) {
antiSlash = false;
} else if (doubleQuoteString) {
doubleQuoteString = false;
} else if (!quoteString) {
doubleQuoteString = true;
}
}
if (character.equals(';') && !antiSlash && !quoteString && !doubleQuoteString) {
queries.add(query.toString());
query = new StringBuilder();
} else if (item == sql.length() - 1) {
query.append(character);
queries.add(query.toString());
} else {
query.append(character);
}
}
return queries;
}
private InterpreterResult executeSql(String propertyKey, String sql,
InterpreterContext interpreterContext) {
Connection connection;
@ -489,60 +536,68 @@ public class JDBCInterpreter extends Interpreter {
String paragraphId = interpreterContext.getParagraphId();
String user = interpreterContext.getAuthenticationInfo().getUser();
try {
String results = null;
connection = getConnection(propertyKey, interpreterContext);
InterpreterResult interpreterResult = new InterpreterResult(InterpreterResult.Code.SUCCESS);
try {
connection = getConnection(propertyKey, interpreterContext);
if (connection == null) {
return new InterpreterResult(Code.ERROR, "Prefix not found.");
}
statement = connection.createStatement();
if (statement == null) {
return new InterpreterResult(Code.ERROR, "Prefix not found.");
}
ArrayList<String> multipleSqlArray = splitSqlQueries(sql);
for (int i = 0; i < multipleSqlArray.size(); i++) {
String sqlToExecute = multipleSqlArray.get(i);
statement = connection.createStatement();
if (statement == null) {
return new InterpreterResult(Code.ERROR, "Prefix not found.");
}
try {
getJDBCConfiguration(user).saveStatement(paragraphId, statement);
try {
getJDBCConfiguration(user).saveStatement(paragraphId, statement);
boolean isResultSetAvailable = statement.execute(sql);
if (isResultSetAvailable) {
resultSet = statement.getResultSet();
boolean isResultSetAvailable = statement.execute(sqlToExecute);
if (isResultSetAvailable) {
resultSet = statement.getResultSet();
// Regards that the command is DDL.
if (isDDLCommand(statement.getUpdateCount(), resultSet.getMetaData().getColumnCount())) {
results = "Query executed successfully.";
// Regards that the command is DDL.
if (isDDLCommand(statement.getUpdateCount(),
resultSet.getMetaData().getColumnCount())) {
interpreterResult.add(InterpreterResult.Type.TEXT,
"Query executed successfully.");
} else {
interpreterResult.add(
getResults(resultSet, !containsIgnoreCase(sqlToExecute, EXPLAIN_PREDICATE)));
}
} else {
results = getResults(resultSet, !containsIgnoreCase(sql, EXPLAIN_PREDICATE));
// Response contains either an update count or there are no results.
int updateCount = statement.getUpdateCount();
interpreterResult.add(InterpreterResult.Type.TEXT,
"Query executed successfully. Affected rows : " +
updateCount);
}
} finally {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) { /*ignored*/ }
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) { /*ignored*/ }
}
} else {
// Response contains either an update count or there are no results.
int updateCount = statement.getUpdateCount();
results = "Query executed successfully. Affected rows : " + updateCount;
}
//In case user ran an insert/update/upsert statement
if (connection.getAutoCommit() != true) connection.commit();
} finally {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) { /*ignored*/ }
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) { /*ignored*/ }
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) { /*ignored*/ }
}
getJDBCConfiguration(user).removeStatement(paragraphId);
}
return new InterpreterResult(Code.SUCCESS, results);
//In case user ran an insert/update/upsert statement
if (connection != null) {
try {
if (!connection.getAutoCommit()) {
connection.commit();
}
connection.close();
} catch (SQLException e) { /*ignored*/ }
}
getJDBCConfiguration(user).removeStatement(paragraphId);
} catch (Exception e) {
logger.error("Cannot run " + sql, e);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@ -555,9 +610,10 @@ public class JDBCInterpreter extends Interpreter {
} catch (SQLException e1) {
e1.printStackTrace();
}
return new InterpreterResult(Code.ERROR, errorMsg);
interpreterResult.add(errorMsg);
return new InterpreterResult(Code.ERROR, interpreterResult.message());
}
return interpreterResult;
}
/**

View file

@ -15,9 +15,6 @@
package org.apache.zeppelin.jdbc;
import static java.lang.String.format;
import static org.apache.zeppelin.interpreter.Interpreter.logger;
import static org.apache.zeppelin.interpreter.Interpreter.register;
import static org.apache.zeppelin.jdbc.JDBCInterpreter.DEFAULT_KEY;
import static org.apache.zeppelin.jdbc.JDBCInterpreter.DEFAULT_DRIVER;
import static org.apache.zeppelin.jdbc.JDBCInterpreter.DEFAULT_PASSWORD;
import static org.apache.zeppelin.jdbc.JDBCInterpreter.DEFAULT_USER;
@ -29,19 +26,17 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.*;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.apache.zeppelin.interpreter.InterpreterContext;
import org.apache.zeppelin.interpreter.InterpreterResult;
import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion;
import org.apache.zeppelin.jdbc.JDBCInterpreter;
import org.apache.zeppelin.scheduler.FIFOScheduler;
import org.apache.zeppelin.scheduler.ParallelScheduler;
import org.apache.zeppelin.scheduler.Scheduler;
import org.apache.zeppelin.user.AuthenticationInfo;
import org.apache.zeppelin.user.Credentials;
import org.apache.zeppelin.user.UserCredentials;
import org.apache.zeppelin.user.UsernamePassword;
import org.junit.Before;
@ -171,6 +166,49 @@ public class JDBCInterpreterTest extends BasicJDBCTestCaseAdapter {
assertEquals("ID\tNAME\na\ta_name\nb\tb_name\n", interpreterResult.message().get(0).getData());
}
@Test
public void testSplitSqlQuery() throws SQLException, IOException {
String sqlQuery = "insert into test_table(id, name) values ('a', ';\"');" +
"select * from test_table;" +
"select * from test_table WHERE ID = \";'\";" +
"select * from test_table WHERE ID = ';'";
Properties properties = new Properties();
JDBCInterpreter t = new JDBCInterpreter(properties);
t.open();
ArrayList<String> multipleSqlArray = t.splitSqlQueries(sqlQuery);
assertEquals(4, multipleSqlArray.size());
assertEquals("insert into test_table(id, name) values ('a', ';\"')", multipleSqlArray.get(0));
assertEquals("select * from test_table", multipleSqlArray.get(1));
assertEquals("select * from test_table WHERE ID = \";'\"", multipleSqlArray.get(2));
assertEquals("select * from test_table WHERE ID = ';'", multipleSqlArray.get(3));
}
@Test
public void testSelectMultipleQuries() throws SQLException, IOException {
Properties properties = new Properties();
properties.setProperty("common.max_count", "1000");
properties.setProperty("common.max_retry", "3");
properties.setProperty("default.driver", "org.h2.Driver");
properties.setProperty("default.url", getJdbcConnection());
properties.setProperty("default.user", "");
properties.setProperty("default.password", "");
JDBCInterpreter t = new JDBCInterpreter(properties);
t.open();
String sqlQuery = "select * from test_table;" +
"select * from test_table WHERE ID = ';';";
InterpreterResult interpreterResult = t.interpret(sqlQuery, interpreterContext);
assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code());
assertEquals(2, interpreterResult.message().size());
assertEquals(InterpreterResult.Type.TABLE, interpreterResult.message().get(0).getType());
assertEquals("ID\tNAME\na\ta_name\nb\tb_name\nc\tnull\n", interpreterResult.message().get(0).getData());
assertEquals(InterpreterResult.Type.TABLE, interpreterResult.message().get(1).getType());
assertEquals("ID\tNAME\n", interpreterResult.message().get(1).getData());
}
@Test
public void testSelectQueryWithNull() throws SQLException, IOException {
Properties properties = new Properties();

View file

@ -726,6 +726,13 @@
</modules>
</profile>
<profile>
<id>helium-dev</id>
<modules>
<module>helium-dev</module>
</modules>
</profile>
<profile>
<id>build-distr</id>
<activation>

View file

@ -72,9 +72,9 @@ public class PythonInterpreter extends Interpreter {
// Add matplotlib display hook
InterpreterGroup intpGroup = getInterpreterGroup();
if (intpGroup != null && intpGroup.getInterpreterHookRegistry() != null) {
registerHook(HookType.POST_EXEC_DEV, "z._displayhook()");
registerHook(HookType.POST_EXEC_DEV, "\nz._displayhook()");
}
// Add zeppelin-bundled libs to PYTHONPATH
setPythonPath("../interpreter/lib/python:$PYTHONPATH");
LOG.info("Starting Python interpreter ---->");

View file

@ -45,6 +45,7 @@ import org.apache.zeppelin.interpreter.ClassloaderInterpreter;
import org.apache.zeppelin.interpreter.Interpreter;
import org.apache.zeppelin.interpreter.InterpreterGroup;
import org.apache.zeppelin.interpreter.InterpreterResult;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
@ -62,6 +63,7 @@ import org.slf4j.LoggerFactory;
public class PythonInterpreterTest {
private static final Logger LOG = LoggerFactory.getLogger(PythonProcess.class);
PythonInterpreter zeppelinPythonInterpreter = null;
PythonInterpreter pythonInterpreter = null;
PythonProcess mockPythonProcess;
String cmdHistory;
@ -88,6 +90,7 @@ public class PythonInterpreterTest {
// python interpreter
pythonInterpreter = spy(new PythonInterpreter(getPythonTestProperties()));
zeppelinPythonInterpreter = new PythonInterpreter(getPythonTestProperties());
// create interpreter group
InterpreterGroup group = new InterpreterGroup();
@ -99,6 +102,12 @@ public class PythonInterpreterTest {
when(mockPythonProcess.sendAndGetResult(eq("\n\nimport py4j\n"))).thenReturn("ImportError");
}
@After
public void afterTest() throws IOException {
pythonInterpreter.close();
zeppelinPythonInterpreter.close();
}
@Test
public void testOpenInterpreter() {
pythonInterpreter.open();
@ -172,6 +181,18 @@ public class PythonInterpreterTest {
assertEquals("%text print a", result.message().get(0).toString());
}
@Test
public void testInterpretInvalidSyntax() {
zeppelinPythonInterpreter.open();
InterpreterResult result = zeppelinPythonInterpreter.interpret("for x in range(0,3): print (\"hi\")\n\nz._displayhook()", null);
assertEquals(InterpreterResult.Code.SUCCESS, result.code());
assertTrue(result.message().get(0).toString().contains("hi\nhi\nhi"));
result = zeppelinPythonInterpreter.interpret("for x in range(0,3): print (\"hi\")\nz._displayhook()", null);
assertEquals(InterpreterResult.Code.ERROR, result.code());
assertTrue(result.message().get(0).toString().contains("SyntaxError: invalid syntax"));
}
/**
* Checks if given port is open on 'localhost'
* @param port

View file

@ -230,5 +230,4 @@ public class SparkRInterpreter extends Interpreter {
return false;
}
}
}

View file

@ -57,7 +57,6 @@ public class ZeppelinR implements ExecuteResultHandler {
boolean rScriptInitialized = false;
Integer rScriptInitializeNotifier = new Integer(0);
/**
* Request to R repl
*/
@ -103,8 +102,6 @@ public class ZeppelinR implements ExecuteResultHandler {
boolean rResponseError = false;
Integer rResponseNotifier = new Integer(0);
/**
* Create ZeppelinR instance
* @param rCmdPath R repl commandline path
@ -216,7 +213,6 @@ public class ZeppelinR implements ExecuteResultHandler {
}
}
/**
* Send request to r repl and return response
* @return responseValue
@ -257,7 +253,6 @@ public class ZeppelinR implements ExecuteResultHandler {
}
}
/**
* Wait until src/main/resources/R/zeppelin_sparkr.R is initialized
* and call onScriptInitialized()
@ -286,14 +281,11 @@ public class ZeppelinR implements ExecuteResultHandler {
e.printStackTrace();
}
if (rScriptInitialized == false) {
throw new InterpreterException("sparkr is not responding " + errorMessage);
}
}
/**
* invoked by src/main/resources/R/zeppelin_sparkr.R
* @return
@ -337,7 +329,6 @@ public class ZeppelinR implements ExecuteResultHandler {
}
}
/**
* Create R script in tmp dir
*/
@ -381,7 +372,6 @@ public class ZeppelinR implements ExecuteResultHandler {
return zeppelinR.get(hashcode);
}
/**
* Pass InterpreterOutput to capture the repl output
* @param out
@ -390,8 +380,6 @@ public class ZeppelinR implements ExecuteResultHandler {
outputStream.setInterpreterOutput(out);
}
@Override
public void onProcessComplete(int i) {
logger.info("process complete {}", i);
@ -403,6 +391,4 @@ public class ZeppelinR implements ExecuteResultHandler {
logger.error(e.getMessage(), e);
rScriptRunning = false;
}
}

View file

@ -88,7 +88,6 @@ object ZeppelinRDisplay {
}
private def htmlDisplay(body: Element, imageWidth: String): RDisplay = {
var div = new String()
for (element <- body.children) {
@ -101,7 +100,6 @@ object ZeppelinRDisplay {
val r = (pattern findFirstIn eHtml).getOrElse("")
div = div + eOuterHtml.replace(r, "")
}
val content = div
@ -115,7 +113,5 @@ object ZeppelinRDisplay {
}
RDisplay(body.html, HTML, SUCCESS)
}
}

View file

@ -34,10 +34,16 @@
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<groupId>org.apache.zeppelin</groupId>
<artifactId>zeppelin-interpreter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.zeppelin</groupId>
<artifactId>helium-dev</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>

View file

@ -19,7 +19,7 @@ package org.apache.zeppelin.example.app.clock;
import org.apache.zeppelin.helium.Application;
import org.apache.zeppelin.helium.ApplicationContext;
import org.apache.zeppelin.helium.ApplicationException;
import org.apache.zeppelin.interpreter.dev.ZeppelinApplicationDevServer;
import org.apache.zeppelin.helium.ZeppelinApplicationDevServer;
import org.apache.zeppelin.resource.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View file

@ -38,6 +38,12 @@
<artifactId>zeppelin-interpreter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>helium-dev</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>

View file

@ -20,8 +20,8 @@ import org.apache.commons.io.IOUtils;
import org.apache.zeppelin.helium.Application;
import org.apache.zeppelin.helium.ApplicationContext;
import org.apache.zeppelin.helium.ApplicationException;
import org.apache.zeppelin.helium.ZeppelinApplicationDevServer;
import org.apache.zeppelin.interpreter.InterpreterResult;
import org.apache.zeppelin.interpreter.dev.ZeppelinApplicationDevServer;
import org.apache.zeppelin.resource.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View file

@ -28,4 +28,6 @@ public class Constants {
public static final String EXISTING_PROCESS = "existing_process";
public static final int ZEPPELIN_INTERPRETER_DEFAUlT_PORT = 29914;
}

View file

@ -17,16 +17,12 @@
package org.apache.zeppelin.interpreter.remote;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.rmi.server.RemoteServer;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeoutException;
import org.apache.thrift.TException;
import org.apache.thrift.server.TThreadPoolServer;
@ -39,7 +35,6 @@ import org.apache.zeppelin.interpreter.*;
import org.apache.zeppelin.interpreter.InterpreterHookRegistry.HookType;
import org.apache.zeppelin.interpreter.InterpreterHookListener;
import org.apache.zeppelin.interpreter.InterpreterResult.Code;
import org.apache.zeppelin.interpreter.dev.ZeppelinDevServer;
import org.apache.zeppelin.interpreter.thrift.*;
import org.apache.zeppelin.resource.*;
import org.apache.zeppelin.scheduler.Job;
@ -144,7 +139,7 @@ public class RemoteInterpreterServer
public static void main(String[] args)
throws TTransportException, InterruptedException {
int port = ZeppelinDevServer.DEFAULT_TEST_INTERPRETER_PORT;
int port = Constants.ZEPPELIN_INTERPRETER_DEFAUlT_PORT;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
}
@ -442,7 +437,7 @@ public class RemoteInterpreterServer
public void onPreExecute(String script) {
String cmdDev = interpreter.getHook(noteId, HookType.PRE_EXEC_DEV);
String cmdUser = interpreter.getHook(noteId, HookType.PRE_EXEC);
// User defined hook should be executed before dev hook
List<String> cmds = Arrays.asList(cmdDev, cmdUser);
for (String cmd : cmds) {
@ -450,15 +445,15 @@ public class RemoteInterpreterServer
script = cmd + '\n' + script;
}
}
InterpretJob.this.script = script;
}
@Override
public void onPostExecute(String script) {
String cmdDev = interpreter.getHook(noteId, HookType.POST_EXEC_DEV);
String cmdUser = interpreter.getHook(noteId, HookType.POST_EXEC);
// User defined hook should be executed after dev hook
List<String> cmds = Arrays.asList(cmdUser, cmdDev);
for (String cmd : cmds) {
@ -466,7 +461,7 @@ public class RemoteInterpreterServer
script += '\n' + cmd;
}
}
InterpretJob.this.script = script;
}
};
@ -478,7 +473,7 @@ public class RemoteInterpreterServer
protected Object jobRun() throws Throwable {
try {
InterpreterContext.set(context);
// Open the interpreter instance prior to calling interpret().
// This is necessary because the earliest we can register a hook
// is from within the open() method.
@ -486,7 +481,7 @@ public class RemoteInterpreterServer
if (!lazy.isOpen()) {
lazy.open();
}
// Add hooks to script from registry.
// Global scope first, followed by notebook scope
processInterpreterHooks(null);

View file

@ -36,6 +36,7 @@ import javax.servlet.http.HttpServletRequest;
import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import com.google.gson.*;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.vfs2.FileSystemException;
import org.apache.zeppelin.conf.ZeppelinConfiguration;
@ -85,8 +86,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Queues;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
/**
@ -263,6 +262,9 @@ public class NotebookServer extends WebSocketServlet
case RUN_PARAGRAPH:
runParagraph(conn, userAndRoles, notebook, messagereceived);
break;
case RUN_ALL_PARAGRAPHS:
runAllParagraphs(conn, userAndRoles, notebook, messagereceived);
break;
case CANCEL_PARAGRAPH:
cancelParagraph(conn, userAndRoles, notebook, messagereceived);
break;
@ -1534,8 +1536,46 @@ public class NotebookServer extends WebSocketServlet
p.abort();
}
private void runParagraph(NotebookSocket conn, HashSet<String> userAndRoles, Notebook notebook,
private void runAllParagraphs(NotebookSocket conn, HashSet<String> userAndRoles,
Notebook notebook,
Message fromMessage) throws IOException {
final String noteId = (String) fromMessage.get("noteId");
if (StringUtils.isBlank(noteId)) {
return;
}
Note note = notebook.getNote(noteId);
NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
if (!notebookAuthorization.isWriter(noteId, userAndRoles)) {
permissionError(conn, "run all paragraphs", fromMessage.principal, userAndRoles,
notebookAuthorization.getOwners(noteId));
return;
}
List<Map<String, Object>> paragraphs =
gson.fromJson(String.valueOf(fromMessage.data.get("paragraphs")),
new TypeToken<List<Map<String, Object>>>() {}.getType());
for (Map<String, Object> raw : paragraphs) {
String paragraphId = (String) raw.get("id");
if (paragraphId == null) {
continue;
}
String text = (String) raw.get("paragraph");
String title = (String) raw.get("title");
Map<String, Object> params = (Map<String, Object>) raw.get("params");
Map<String, Object> config = (Map<String, Object>) raw.get("config");
Paragraph p = setParagraphUsingMessage(note, fromMessage,
paragraphId, text, title, params, config);
persistAndExecuteSingleParagraph(conn, note, p);
}
}
private void runParagraph(NotebookSocket conn, HashSet<String> userAndRoles, Notebook notebook,
Message fromMessage) throws IOException {
final String paragraphId = (String) fromMessage.get("id");
if (paragraphId == null) {
return;
@ -1550,30 +1590,29 @@ public class NotebookServer extends WebSocketServlet
return;
}
Paragraph p = note.getParagraph(paragraphId);
String text = (String) fromMessage.get("paragraph");
p.setText(text);
p.setTitle((String) fromMessage.get("title"));
AuthenticationInfo subject =
new AuthenticationInfo(fromMessage.principal, fromMessage.ticket);
p.setAuthenticationInfo(subject);
String title = (String) fromMessage.get("title");
Map<String, Object> params = (Map<String, Object>) fromMessage.get("params");
p.settings.setParams(params);
Map<String, Object> config = (Map<String, Object>) fromMessage.get("config");
p.setConfig(config);
Paragraph p = setParagraphUsingMessage(note, fromMessage, paragraphId,
text, title, params, config);
persistAndExecuteSingleParagraph(conn, note, p);
}
private void persistAndExecuteSingleParagraph(NotebookSocket conn,
Note note, Paragraph p) throws IOException {
// if it's the last paragraph and empty, let's add a new one
boolean isTheLastParagraph = note.isLastParagraph(p.getId());
if (!(text.trim().equals(p.getMagic()) ||
Strings.isNullOrEmpty(text)) &&
if (!(p.getText().trim().equals(p.getMagic()) ||
Strings.isNullOrEmpty(p.getText())) &&
isTheLastParagraph) {
Paragraph newPara = note.addParagraph(subject);
Paragraph newPara = note.addParagraph(p.getAuthenticationInfo());
broadcastNewParagraph(note, newPara);
}
try {
note.persist(subject);
note.persist(p.getAuthenticationInfo());
} catch (FileSystemException ex) {
LOG.error("Exception from run", ex);
conn.send(serializeMessage(new Message(OP.ERROR_INFO).put("info",
@ -1584,7 +1623,7 @@ public class NotebookServer extends WebSocketServlet
}
try {
note.run(paragraphId);
note.run(p.getId());
} catch (Exception ex) {
LOG.error("Exception from run", ex);
if (p != null) {
@ -1595,6 +1634,21 @@ public class NotebookServer extends WebSocketServlet
}
}
private Paragraph setParagraphUsingMessage(Note note, Message fromMessage, String paragraphId,
String text, String title, Map<String, Object> params,
Map<String, Object> config) {
Paragraph p = note.getParagraph(paragraphId);
p.setText(text);
p.setTitle(title);
AuthenticationInfo subject =
new AuthenticationInfo(fromMessage.principal, fromMessage.ticket);
p.setAuthenticationInfo(subject);
p.settings.setParams(params);
p.setConfig(config);
return p;
}
private void sendAllConfigurations(NotebookSocket conn, HashSet<String> userAndRoles,
Notebook notebook) throws IOException {
ZeppelinConfiguration conf = notebook.getConf();

View file

@ -241,7 +241,7 @@ public class ParagraphActionsIT extends AbstractZeppelinIT {
driver.findElement(By.xpath(getParagraphXPath(1) + "//span[@class='icon-control-play shortcut-icon']")).isDisplayed(), CoreMatchers.equalTo(false)
);
driver.findElement(By.xpath(".//*[@id='main']//button[@ng-click='runNote()']")).sendKeys(Keys.ENTER);
driver.findElement(By.xpath(".//*[@id='main']//button[contains(@ng-click, 'runAllParagraphs')]")).sendKeys(Keys.ENTER);
ZeppelinITUtils.sleep(1000, true);
driver.findElement(By.xpath("//div[@class='modal-dialog'][contains(.,'Run all paragraphs?')]" +
"//div[@class='modal-footer']//button[contains(.,'OK')]")).click();

View file

@ -11,52 +11,50 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').controller('MainCtrl', MainCtrl);
angular.module('zeppelinWebApp').controller('MainCtrl', MainCtrl);
MainCtrl.$inject = ['$scope', '$rootScope', '$window', 'arrayOrderingSrv'];
MainCtrl.$inject = ['$scope', '$rootScope', '$window', 'arrayOrderingSrv'];
function MainCtrl($scope, $rootScope, $window, arrayOrderingSrv) {
$scope.looknfeel = 'default';
function MainCtrl($scope, $rootScope, $window, arrayOrderingSrv) {
$scope.looknfeel = 'default';
var init = function() {
$scope.asIframe = (($window.location.href.indexOf('asIframe') > -1) ? true : false);
};
var init = function() {
$scope.asIframe = (($window.location.href.indexOf('asIframe') > -1) ? true : false);
};
init();
init();
$rootScope.$on('setIframe', function(event, data) {
if (!event.defaultPrevented) {
$scope.asIframe = data;
event.preventDefault();
}
});
$rootScope.$on('setIframe', function(event, data) {
if (!event.defaultPrevented) {
$scope.asIframe = data;
event.preventDefault();
}
});
$rootScope.$on('setLookAndFeel', function(event, data) {
if (!event.defaultPrevented && data && data !== '' && data !== $scope.looknfeel) {
$scope.looknfeel = data;
event.preventDefault();
}
});
$rootScope.$on('setLookAndFeel', function(event, data) {
if (!event.defaultPrevented && data && data !== '' && data !== $scope.looknfeel) {
$scope.looknfeel = data;
event.preventDefault();
}
});
// Set The lookAndFeel to default on every page
$rootScope.$on('$routeChangeStart', function(event, next, current) {
$rootScope.$broadcast('setLookAndFeel', 'default');
});
// Set The lookAndFeel to default on every page
$rootScope.$on('$routeChangeStart', function(event, next, current) {
$rootScope.$broadcast('setLookAndFeel', 'default');
});
$rootScope.noteName = function(note) {
if (!_.isEmpty(note)) {
return arrayOrderingSrv.getNoteName(note);
}
};
$rootScope.noteName = function(note) {
if (!_.isEmpty(note)) {
return arrayOrderingSrv.getNoteName(note);
}
};
BootstrapDialog.defaultOptions.onshown = function() {
angular.element('#' + this.id).find('.btn:last').focus();
};
BootstrapDialog.defaultOptions.onshown = function() {
angular.element('#' + this.id).find('.btn:last').focus();
};
// Remove BootstrapDialog animation
BootstrapDialog.configDefaultOptions({animate: false});
}
// Remove BootstrapDialog animation
BootstrapDialog.configDefaultOptions({animate: false});
}
})();

View file

@ -14,129 +14,128 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
var zeppelinWebApp = angular.module('zeppelinWebApp', [
'ngCookies',
'ngAnimate',
'ngRoute',
'ngSanitize',
'angular-websocket',
'ui.ace',
'ui.bootstrap',
'as.sortable',
'ngTouch',
'ngDragDrop',
'angular.filter',
'monospaced.elastic',
'puElasticInput',
'xeditable',
'ngToast',
'focus-if',
'ngResource',
'ngclipboard'
])
.filter('breakFilter', function() {
return function(text) {
if (!!text) {
return text.replace(/\n/g, '<br />');
}
};
})
.config(function($httpProvider, $routeProvider, ngToastProvider) {
// withCredentials when running locally via grunt
$httpProvider.defaults.withCredentials = true;
$routeProvider
.when('/', {
templateUrl: 'app/home/home.html'
})
.when('/notebook/:noteId', {
templateUrl: 'app/notebook/notebook.html',
controller: 'NotebookCtrl'
})
.when('/notebook/:noteId/paragraph?=:paragraphId', {
templateUrl: 'app/notebook/notebook.html',
controller: 'NotebookCtrl'
})
.when('/notebook/:noteId/paragraph/:paragraphId?', {
templateUrl: 'app/notebook/notebook.html',
controller: 'NotebookCtrl'
})
.when('/notebook/:noteId/revision/:revisionId', {
templateUrl: 'app/notebook/notebook.html',
controller: 'NotebookCtrl'
})
.when('/jobmanager', {
templateUrl: 'app/jobmanager/jobmanager.html',
controller: 'JobmanagerCtrl'
})
.when('/interpreter', {
templateUrl: 'app/interpreter/interpreter.html',
controller: 'InterpreterCtrl'
})
.when('/notebookRepos', {
templateUrl: 'app/notebookRepos/notebookRepos.html',
controller: 'NotebookReposCtrl',
controllerAs: 'noterepo'
})
.when('/credential', {
templateUrl: 'app/credential/credential.html',
controller: 'CredentialCtrl'
})
.when('/configuration', {
templateUrl: 'app/configuration/configuration.html',
controller: 'ConfigurationCtrl'
})
.when('/search/:searchTerm', {
templateUrl: 'app/search/result-list.html',
controller: 'SearchResultCtrl'
})
.otherwise({
redirectTo: '/'
});
ngToastProvider.configure({
dismissButton: true,
dismissOnClick: false,
combineDuplications: true,
timeout: 6000
});
})
.constant('TRASH_FOLDER_ID', '~Trash');
function auth() {
var $http = angular.injector(['ng']).get('$http');
var baseUrlSrv = angular.injector(['zeppelinWebApp']).get('baseUrlSrv');
var zeppelinWebApp = angular.module('zeppelinWebApp', [
'ngCookies',
'ngAnimate',
'ngRoute',
'ngSanitize',
'angular-websocket',
'ui.ace',
'ui.bootstrap',
'as.sortable',
'ngTouch',
'ngDragDrop',
'angular.filter',
'monospaced.elastic',
'puElasticInput',
'xeditable',
'ngToast',
'focus-if',
'ngResource',
'ngclipboard'
])
.filter('breakFilter', function() {
return function(text) {
if (!!text) {
return text.replace(/\n/g, '<br />');
}
};
})
.config(function($httpProvider, $routeProvider, ngToastProvider) {
// withCredentials when running locally via grunt
$http.defaults.withCredentials = true;
jQuery.ajaxSetup({
dataType: 'json',
xhrFields: {
withCredentials: true
},
crossDomain: true
});
return $http.get(baseUrlSrv.getRestApiBase() + '/security/ticket').then(function(response) {
zeppelinWebApp.run(function($rootScope) {
$rootScope.ticket = angular.fromJson(response.data).body;
});
}, function(errorResponse) {
// Handle error case
});
}
$httpProvider.defaults.withCredentials = true;
function bootstrapApplication() {
zeppelinWebApp.run(function($rootScope, $location) {
$rootScope.$on('$routeChangeStart', function(event, next, current) {
if (!$rootScope.ticket && next.$$route && !next.$$route.publicAccess) {
$location.path('/');
}
$routeProvider
.when('/', {
templateUrl: 'app/home/home.html'
})
.when('/notebook/:noteId', {
templateUrl: 'app/notebook/notebook.html',
controller: 'NotebookCtrl'
})
.when('/notebook/:noteId/paragraph?=:paragraphId', {
templateUrl: 'app/notebook/notebook.html',
controller: 'NotebookCtrl'
})
.when('/notebook/:noteId/paragraph/:paragraphId?', {
templateUrl: 'app/notebook/notebook.html',
controller: 'NotebookCtrl'
})
.when('/notebook/:noteId/revision/:revisionId', {
templateUrl: 'app/notebook/notebook.html',
controller: 'NotebookCtrl'
})
.when('/jobmanager', {
templateUrl: 'app/jobmanager/jobmanager.html',
controller: 'JobmanagerCtrl'
})
.when('/interpreter', {
templateUrl: 'app/interpreter/interpreter.html',
controller: 'InterpreterCtrl'
})
.when('/notebookRepos', {
templateUrl: 'app/notebookRepos/notebookRepos.html',
controller: 'NotebookReposCtrl',
controllerAs: 'noterepo'
})
.when('/credential', {
templateUrl: 'app/credential/credential.html',
controller: 'CredentialCtrl'
})
.when('/configuration', {
templateUrl: 'app/configuration/configuration.html',
controller: 'ConfigurationCtrl'
})
.when('/search/:searchTerm', {
templateUrl: 'app/search/result-list.html',
controller: 'SearchResultCtrl'
})
.otherwise({
redirectTo: '/'
});
});
angular.bootstrap(document, ['zeppelinWebApp']);
}
angular.element(document).ready(function() {
auth().then(bootstrapApplication);
ngToastProvider.configure({
dismissButton: true,
dismissOnClick: false,
combineDuplications: true,
timeout: 6000
});
})
.constant('TRASH_FOLDER_ID', '~Trash');
function auth() {
var $http = angular.injector(['ng']).get('$http');
var baseUrlSrv = angular.injector(['zeppelinWebApp']).get('baseUrlSrv');
// withCredentials when running locally via grunt
$http.defaults.withCredentials = true;
jQuery.ajaxSetup({
dataType: 'json',
xhrFields: {
withCredentials: true
},
crossDomain: true
});
}());
return $http.get(baseUrlSrv.getRestApiBase() + '/security/ticket').then(function(response) {
zeppelinWebApp.run(function($rootScope) {
$rootScope.ticket = angular.fromJson(response.data).body;
});
}, function(errorResponse) {
// Handle error case
});
}
function bootstrapApplication() {
zeppelinWebApp.run(function($rootScope, $location) {
$rootScope.$on('$routeChangeStart', function(event, next, current) {
if (!$rootScope.ticket && next.$$route && !next.$$route.publicAccess) {
$location.path('/');
}
});
});
angular.bootstrap(document, ['zeppelinWebApp']);
}
angular.element(document).ready(function() {
auth().then(bootstrapApplication);
});

View file

@ -11,41 +11,39 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').controller('ConfigurationCtrl', ConfigurationCtrl);
angular.module('zeppelinWebApp').controller('ConfigurationCtrl', ConfigurationCtrl);
ConfigurationCtrl.$inject = ['$scope', '$rootScope', '$http', 'baseUrlSrv', 'ngToast'];
ConfigurationCtrl.$inject = ['$scope', '$rootScope', '$http', 'baseUrlSrv', 'ngToast'];
function ConfigurationCtrl($scope, $rootScope, $http, baseUrlSrv, ngToast) {
$scope.configrations = [];
$scope._ = _;
ngToast.dismiss();
function ConfigurationCtrl($scope, $rootScope, $http, baseUrlSrv, ngToast) {
$scope.configrations = [];
$scope._ = _;
ngToast.dismiss();
var getConfigurations = function() {
$http.get(baseUrlSrv.getRestApiBase() + '/configurations/all').
success(function(data, status, headers, config) {
$scope.configurations = data.body;
}).
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.replace('/');
}, 3000);
}
console.log('Error %o %o', status, data.message);
});
};
var getConfigurations = function() {
$http.get(baseUrlSrv.getRestApiBase() + '/configurations/all').
success(function(data, status, headers, config) {
$scope.configurations = data.body;
}).
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.replace('/');
}, 3000);
}
console.log('Error %o %o', status, data.message);
});
};
var init = function() {
getConfigurations();
};
var init = function() {
getConfigurations();
};
init();
}
})();
init();
}

View file

@ -11,88 +11,87 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').controller('CredentialCtrl', CredentialCtrl);
angular.module('zeppelinWebApp').controller('CredentialCtrl', CredentialCtrl);
CredentialCtrl.$inject = ['$scope', '$rootScope', '$http', 'baseUrlSrv', 'ngToast'];
CredentialCtrl.$inject = ['$scope', '$rootScope', '$http', 'baseUrlSrv', 'ngToast'];
function CredentialCtrl($scope, $rootScope, $http, baseUrlSrv, ngToast) {
$scope._ = _;
ngToast.dismiss();
function CredentialCtrl($scope, $rootScope, $http, baseUrlSrv, ngToast) {
$scope._ = _;
ngToast.dismiss();
$scope.credentialInfo = [];
$scope.showAddNewCredentialInfo = false;
$scope.availableInterpreters = [];
$scope.credentialInfo = [];
$scope.showAddNewCredentialInfo = false;
$scope.availableInterpreters = [];
var getCredentialInfo = function() {
$http.get(baseUrlSrv.getRestApiBase() + '/credential').
success(function(data, status, headers, config) {
$scope.credentialInfo = _.map(data.body.userCredentials, function(value, prop) {
return {entity: prop, password: value.password, username: value.username};
});
console.log('Success %o %o', status, $scope.credentialInfo);
}).
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.replace('/');
}, 3000);
}
console.log('Error %o %o', status, data.message);
var getCredentialInfo = function() {
$http.get(baseUrlSrv.getRestApiBase() + '/credential').
success(function(data, status, headers, config) {
$scope.credentialInfo = _.map(data.body.userCredentials, function(value, prop) {
return {entity: prop, password: value.password, username: value.username};
});
};
$scope.addNewCredentialInfo = function() {
if ($scope.entity && _.isEmpty($scope.entity.trim()) &&
$scope.username && _.isEmpty($scope.username.trim())) {
console.log('Success %o %o', status, $scope.credentialInfo);
}).
error(function(data, status, headers, config) {
if (status === 401) {
ngToast.danger({
content: 'Username \\ Entity can not be empty.',
content: 'You don\'t have permission on this page',
verticalPosition: 'bottom',
timeout: '3000'
});
return;
setTimeout(function() {
window.location.replace('/');
}, 3000);
}
console.log('Error %o %o', status, data.message);
});
};
var newCredential = {
'entity': $scope.entity,
'username': $scope.username,
'password': $scope.password
};
$http.put(baseUrlSrv.getRestApiBase() + '/credential', newCredential).
success(function(data, status, headers, config) {
ngToast.success({
content: 'Successfully saved credentials.',
verticalPosition: 'bottom',
timeout: '3000'
});
$scope.credentialInfo.push(newCredential);
resetCredentialInfo();
$scope.showAddNewCredentialInfo = false;
console.log('Success %o %o', status, data.message);
}).
error(function(data, status, headers, config) {
ngToast.danger({
content: 'Error saving credentials',
verticalPosition: 'bottom',
timeout: '3000'
});
console.log('Error %o %o', status, data.message);
$scope.addNewCredentialInfo = function() {
if ($scope.entity && _.isEmpty($scope.entity.trim()) &&
$scope.username && _.isEmpty($scope.username.trim())) {
ngToast.danger({
content: 'Username \\ Entity can not be empty.',
verticalPosition: 'bottom',
timeout: '3000'
});
return;
}
var newCredential = {
'entity': $scope.entity,
'username': $scope.username,
'password': $scope.password
};
var getAvailableInterpreters = function() {
$http.get(baseUrlSrv.getRestApiBase() + '/interpreter/setting')
$http.put(baseUrlSrv.getRestApiBase() + '/credential', newCredential).
success(function(data, status, headers, config) {
ngToast.success({
content: 'Successfully saved credentials.',
verticalPosition: 'bottom',
timeout: '3000'
});
$scope.credentialInfo.push(newCredential);
resetCredentialInfo();
$scope.showAddNewCredentialInfo = false;
console.log('Success %o %o', status, data.message);
}).
error(function(data, status, headers, config) {
ngToast.danger({
content: 'Error saving credentials',
verticalPosition: 'bottom',
timeout: '3000'
});
console.log('Error %o %o', status, data.message);
});
};
var getAvailableInterpreters = function() {
$http.get(baseUrlSrv.getRestApiBase() + '/interpreter/setting')
.success(function(data, status, headers, config) {
for (var setting = 0; setting < data.body.length; setting++) {
$scope.availableInterpreters.push(
data.body[setting].group + '.' + data.body[setting].name);
data.body[setting].group + '.' + data.body[setting].name);
}
angular.element('#entityname').autocomplete({
source: $scope.availableInterpreters,
@ -102,91 +101,90 @@
}
});
}).error(function(data, status, headers, config) {
console.log('Error %o %o', status, data.message);
});
};
console.log('Error %o %o', status, data.message);
});
};
$scope.toggleAddNewCredentialInfo = function() {
if ($scope.showAddNewCredentialInfo) {
$scope.showAddNewCredentialInfo = false;
} else {
$scope.showAddNewCredentialInfo = true;
}
};
$scope.cancelCredentialInfo = function() {
$scope.toggleAddNewCredentialInfo = function() {
if ($scope.showAddNewCredentialInfo) {
$scope.showAddNewCredentialInfo = false;
resetCredentialInfo();
} else {
$scope.showAddNewCredentialInfo = true;
}
};
$scope.cancelCredentialInfo = function() {
$scope.showAddNewCredentialInfo = false;
resetCredentialInfo();
};
var resetCredentialInfo = function() {
$scope.entity = '';
$scope.username = '';
$scope.password = '';
};
$scope.copyOriginCredentialsInfo = function() {
ngToast.info({
content: 'Since entity is a unique key, you can edit only username & password',
verticalPosition: 'bottom',
timeout: '3000'
});
};
$scope.updateCredentialInfo = function(form, data, entity) {
var request = {
entity: entity,
username: data.username,
password: data.password
};
var resetCredentialInfo = function() {
$scope.entity = '';
$scope.username = '';
$scope.password = '';
};
$scope.copyOriginCredentialsInfo = function() {
ngToast.info({
content: 'Since entity is a unique key, you can edit only username & password',
$http.put(baseUrlSrv.getRestApiBase() + '/credential/', request).
success(function(data, status, headers, config) {
var index = _.findIndex($scope.credentialInfo, {'entity': entity});
$scope.credentialInfo[index] = request;
return true;
}).
error(function(data, status, headers, config) {
console.log('Error %o %o', status, data.message);
ngToast.danger({
content: 'We couldn\'t save the credential',
verticalPosition: 'bottom',
timeout: '3000'
});
};
form.$show();
});
return false;
};
$scope.updateCredentialInfo = function(form, data, entity) {
var request = {
entity: entity,
username: data.username,
password: data.password
};
$http.put(baseUrlSrv.getRestApiBase() + '/credential/', request).
success(function(data, status, headers, config) {
var index = _.findIndex($scope.credentialInfo, {'entity': entity});
$scope.credentialInfo[index] = request;
return true;
}).
error(function(data, status, headers, config) {
console.log('Error %o %o', status, data.message);
ngToast.danger({
content: 'We couldn\'t save the credential',
verticalPosition: 'bottom',
timeout: '3000'
});
form.$show();
});
return false;
};
$scope.removeCredentialInfo = function(entity) {
BootstrapDialog.confirm({
closable: false,
closeByBackdrop: false,
closeByKeyboard: false,
title: '',
message: 'Do you want to delete this credential information?',
callback: function(result) {
if (result) {
$http.delete(baseUrlSrv.getRestApiBase() + '/credential/' + entity).
success(function(data, status, headers, config) {
var index = _.findIndex($scope.credentialInfo, {'entity': entity});
$scope.credentialInfo.splice(index, 1);
console.log('Success %o %o', status, data.message);
}).
error(function(data, status, headers, config) {
console.log('Error %o %o', status, data.message);
});
}
$scope.removeCredentialInfo = function(entity) {
BootstrapDialog.confirm({
closable: false,
closeByBackdrop: false,
closeByKeyboard: false,
title: '',
message: 'Do you want to delete this credential information?',
callback: function(result) {
if (result) {
$http.delete(baseUrlSrv.getRestApiBase() + '/credential/' + entity).
success(function(data, status, headers, config) {
var index = _.findIndex($scope.credentialInfo, {'entity': entity});
$scope.credentialInfo.splice(index, 1);
console.log('Success %o %o', status, data.message);
}).
error(function(data, status, headers, config) {
console.log('Error %o %o', status, data.message);
});
}
});
};
}
});
};
var init = function() {
getAvailableInterpreters();
getCredentialInfo();
};
var init = function() {
getAvailableInterpreters();
getCredentialInfo();
};
init();
}
init();
}
})();

View file

@ -11,126 +11,124 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').controller('HomeCtrl', HomeCtrl);
angular.module('zeppelinWebApp').controller('HomeCtrl', HomeCtrl);
HomeCtrl.$inject = [
'$scope',
'noteListDataFactory',
'websocketMsgSrv',
'$rootScope',
'arrayOrderingSrv',
'ngToast',
'noteActionSrv',
'TRASH_FOLDER_ID'
];
HomeCtrl.$inject = [
'$scope',
'noteListDataFactory',
'websocketMsgSrv',
'$rootScope',
'arrayOrderingSrv',
'ngToast',
'noteActionSrv',
'TRASH_FOLDER_ID'
];
function HomeCtrl($scope, noteListDataFactory, websocketMsgSrv, $rootScope, arrayOrderingSrv,
ngToast, noteActionSrv, TRASH_FOLDER_ID) {
ngToast.dismiss();
var vm = this;
vm.notes = noteListDataFactory;
vm.websocketMsgSrv = websocketMsgSrv;
vm.arrayOrderingSrv = arrayOrderingSrv;
function HomeCtrl($scope, noteListDataFactory, websocketMsgSrv, $rootScope, arrayOrderingSrv,
ngToast, noteActionSrv, TRASH_FOLDER_ID) {
ngToast.dismiss();
var vm = this;
vm.notes = noteListDataFactory;
vm.websocketMsgSrv = websocketMsgSrv;
vm.arrayOrderingSrv = arrayOrderingSrv;
vm.notebookHome = false;
if ($rootScope.ticket !== undefined) {
vm.notebookHome = false;
if ($rootScope.ticket !== undefined) {
vm.staticHome = false;
} else {
vm.staticHome = true;
}
$scope.isReloading = false;
$scope.TRASH_FOLDER_ID = TRASH_FOLDER_ID;
var initHome = function() {
websocketMsgSrv.getHomeNote();
};
initHome();
$scope.reloadNoteList = function() {
websocketMsgSrv.reloadAllNotesFromRepo();
$scope.isReloadingNotes = true;
};
$scope.toggleFolderNode = function(node) {
node.hidden = !node.hidden;
};
angular.element('#loginModal').on('hidden.bs.modal', function(e) {
$rootScope.$broadcast('initLoginValues');
});
/*
** $scope.$on functions below
*/
$scope.$on('setNoteMenu', function(event, notes) {
$scope.isReloadingNotes = false;
});
$scope.$on('setNoteContent', function(event, note) {
if (note) {
vm.note = note;
// initialize look And Feel
$rootScope.$broadcast('setLookAndFeel', 'home');
// make it read only
vm.viewOnly = true;
vm.notebookHome = true;
vm.staticHome = false;
} else {
vm.staticHome = true;
vm.notebookHome = false;
}
});
$scope.isReloading = false;
$scope.TRASH_FOLDER_ID = TRASH_FOLDER_ID;
$scope.renameNote = function(node) {
noteActionSrv.renameNote(node.id, node.path);
};
var initHome = function() {
websocketMsgSrv.getHomeNote();
};
$scope.moveNoteToTrash = function(noteId) {
noteActionSrv.moveNoteToTrash(noteId, false);
};
initHome();
$scope.moveFolderToTrash = function(folderId) {
noteActionSrv.moveFolderToTrash(folderId);
};
$scope.reloadNoteList = function() {
websocketMsgSrv.reloadAllNotesFromRepo();
$scope.isReloadingNotes = true;
};
$scope.restoreNote = function(noteId) {
websocketMsgSrv.restoreNote(noteId);
};
$scope.toggleFolderNode = function(node) {
node.hidden = !node.hidden;
};
$scope.restoreFolder = function(folderId) {
websocketMsgSrv.restoreFolder(folderId);
};
angular.element('#loginModal').on('hidden.bs.modal', function(e) {
$rootScope.$broadcast('initLoginValues');
});
$scope.restoreAll = function() {
noteActionSrv.restoreAll();
};
/*
** $scope.$on functions below
*/
$scope.renameFolder = function(node) {
noteActionSrv.renameFolder(node.id);
};
$scope.$on('setNoteMenu', function(event, notes) {
$scope.isReloadingNotes = false;
});
$scope.removeNote = function(noteId) {
noteActionSrv.removeNote(noteId, false);
};
$scope.$on('setNoteContent', function(event, note) {
if (note) {
vm.note = note;
$scope.removeFolder = function(folderId) {
noteActionSrv.removeFolder(folderId);
};
// initialize look And Feel
$rootScope.$broadcast('setLookAndFeel', 'home');
$scope.emptyTrash = function() {
noteActionSrv.emptyTrash();
};
// make it read only
vm.viewOnly = true;
vm.notebookHome = true;
vm.staticHome = false;
} else {
vm.staticHome = true;
vm.notebookHome = false;
}
});
$scope.renameNote = function(node) {
noteActionSrv.renameNote(node.id, node.path);
};
$scope.moveNoteToTrash = function(noteId) {
noteActionSrv.moveNoteToTrash(noteId, false);
};
$scope.moveFolderToTrash = function(folderId) {
noteActionSrv.moveFolderToTrash(folderId);
};
$scope.restoreNote = function(noteId) {
websocketMsgSrv.restoreNote(noteId);
};
$scope.restoreFolder = function(folderId) {
websocketMsgSrv.restoreFolder(folderId);
};
$scope.restoreAll = function() {
noteActionSrv.restoreAll();
};
$scope.renameFolder = function(node) {
noteActionSrv.renameFolder(node.id);
};
$scope.removeNote = function(noteId) {
noteActionSrv.removeNote(noteId, false);
};
$scope.removeFolder = function(folderId) {
noteActionSrv.removeFolder(folderId);
};
$scope.emptyTrash = function() {
noteActionSrv.emptyTrash();
};
$scope.clearAllParagraphOutput = function(noteId) {
noteActionSrv.clearAllParagraphOutput(noteId);
};
}
})();
$scope.clearAllParagraphOutput = function(noteId) {
noteActionSrv.clearAllParagraphOutput(noteId);
};
}

View file

@ -381,13 +381,13 @@ a.navbar-brand:hover {
}
.zeppelin {
background: url('/assets/images/zepLogo.png') no-repeat right;
background: url('../assets/images/zepLogo.png') no-repeat right;
height: 380px;
opacity: 0.2;
}
.zeppelin2 {
background: url('/assets/images/zepLogo.png') no-repeat right;
background: url('../assets/images/zepLogo.png') no-repeat right;
background-position-y: 12px;
height: 380px;
opacity: 0.2;

File diff suppressed because it is too large Load diff

View file

@ -11,15 +11,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').filter('sortByKey', sortByKey);
angular.module('zeppelinWebApp').filter('sortByKey', sortByKey);
function sortByKey() {
return function(properties) {
var sortedKeys = properties ? Object.keys(properties) : [];
return sortedKeys.sort();
};
}
})();
function sortByKey() {
return function(properties) {
var sortedKeys = properties ? Object.keys(properties) : [];
return sortedKeys.sort();
};
}

View file

@ -11,167 +11,165 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp')
.controller('JobmanagerCtrl', JobmanagerCtrl);
angular.module('zeppelinWebApp')
.controller('JobmanagerCtrl', JobmanagerCtrl);
JobmanagerCtrl.$inject = ['$scope', 'websocketMsgSrv', '$interval', 'ngToast', '$q', '$timeout', 'jobManagerFilter'];
JobmanagerCtrl.$inject = ['$scope', 'websocketMsgSrv', '$interval', 'ngToast', '$q', '$timeout', 'jobManagerFilter'];
function JobmanagerCtrl($scope, websocketMsgSrv, $interval, ngToast, $q, $timeout, jobManagerFilter) {
ngToast.dismiss();
var asyncNotebookJobFilter = function(jobInfomations, filterConfig) {
return $q(function(resolve, reject) {
$scope.JobInfomationsByFilter = $scope.jobTypeFilter(jobInfomations, filterConfig);
resolve($scope.JobInfomationsByFilter);
function JobmanagerCtrl($scope, websocketMsgSrv, $interval, ngToast, $q, $timeout, jobManagerFilter) {
ngToast.dismiss();
var asyncNotebookJobFilter = function(jobInfomations, filterConfig) {
return $q(function(resolve, reject) {
$scope.JobInfomationsByFilter = $scope.jobTypeFilter(jobInfomations, filterConfig);
resolve($scope.JobInfomationsByFilter);
});
};
$scope.doFiltering = function(jobInfomations, filterConfig) {
asyncNotebookJobFilter(jobInfomations, filterConfig).then(
function() {
// success
$scope.isLoadingFilter = false;
},
function() {
// failed
});
};
};
$scope.doFiltering = function(jobInfomations, filterConfig) {
asyncNotebookJobFilter(jobInfomations, filterConfig).then(
function() {
// success
$scope.isLoadingFilter = false;
},
function() {
// failed
});
};
$scope.filterValueToName = function(filterValue, maxStringLength) {
if ($scope.activeInterpreters === undefined) {
return;
$scope.filterValueToName = function(filterValue, maxStringLength) {
if ($scope.activeInterpreters === undefined) {
return;
}
var index = _.findIndex($scope.activeInterpreters, {value: filterValue});
if ($scope.activeInterpreters[index].name !== undefined) {
if (maxStringLength !== undefined && maxStringLength > $scope.activeInterpreters[index].name) {
return $scope.activeInterpreters[index].name.substr(0, maxStringLength - 3) + '...';
}
var index = _.findIndex($scope.activeInterpreters, {value: filterValue});
if ($scope.activeInterpreters[index].name !== undefined) {
if (maxStringLength !== undefined && maxStringLength > $scope.activeInterpreters[index].name) {
return $scope.activeInterpreters[index].name.substr(0, maxStringLength - 3) + '...';
}
return $scope.activeInterpreters[index].name;
} else {
return 'Interpreter is not set';
}
};
return $scope.activeInterpreters[index].name;
} else {
return 'Interpreter is not set';
}
};
$scope.setFilterValue = function(filterValue) {
$scope.filterConfig.filterValueInterpreter = filterValue;
$scope.doFiltering($scope.jobInfomations, $scope.filterConfig);
};
$scope.setFilterValue = function(filterValue) {
$scope.filterConfig.filterValueInterpreter = filterValue;
$scope.doFiltering($scope.jobInfomations, $scope.filterConfig);
};
$scope.onChangeRunJobToAlwaysTopToggle = function() {
$scope.filterConfig.isRunningAlwaysTop = !$scope.filterConfig.isRunningAlwaysTop;
$scope.doFiltering($scope.jobInfomations, $scope.filterConfig);
};
$scope.onChangeRunJobToAlwaysTopToggle = function() {
$scope.filterConfig.isRunningAlwaysTop = !$scope.filterConfig.isRunningAlwaysTop;
$scope.doFiltering($scope.jobInfomations, $scope.filterConfig);
};
$scope.onChangeSortAsc = function() {
$scope.filterConfig.isSortByAsc = !$scope.filterConfig.isSortByAsc;
$scope.doFiltering($scope.jobInfomations, $scope.filterConfig);
};
$scope.onChangeSortAsc = function() {
$scope.filterConfig.isSortByAsc = !$scope.filterConfig.isSortByAsc;
$scope.doFiltering($scope.jobInfomations, $scope.filterConfig);
};
$scope.doFilterInputTyping = function(keyEvent, jobInfomations, filterConfig) {
var RETURN_KEY_CODE = 13;
$timeout.cancel($scope.dofilterTimeoutObject);
$scope.isActiveSearchTimer = true;
$scope.dofilterTimeoutObject = $timeout(function() {
$scope.doFiltering(jobInfomations, filterConfig);
$scope.isActiveSearchTimer = false;
}, 10000);
if (keyEvent.which === RETURN_KEY_CODE) {
$timeout.cancel($scope.dofilterTimeoutObject);
$scope.doFiltering(jobInfomations, filterConfig);
$scope.isActiveSearchTimer = false;
}
};
$scope.doForceFilterInputTyping = function(keyEvent, jobInfomations, filterConfig) {
$scope.doFilterInputTyping = function(keyEvent, jobInfomations, filterConfig) {
var RETURN_KEY_CODE = 13;
$timeout.cancel($scope.dofilterTimeoutObject);
$scope.isActiveSearchTimer = true;
$scope.dofilterTimeoutObject = $timeout(function() {
$scope.doFiltering(jobInfomations, filterConfig);
$scope.isActiveSearchTimer = false;
}, 10000);
if (keyEvent.which === RETURN_KEY_CODE) {
$timeout.cancel($scope.dofilterTimeoutObject);
$scope.doFiltering(jobInfomations, filterConfig);
$scope.isActiveSearchTimer = false;
}
};
$scope.doForceFilterInputTyping = function(keyEvent, jobInfomations, filterConfig) {
$timeout.cancel($scope.dofilterTimeoutObject);
$scope.doFiltering(jobInfomations, filterConfig);
$scope.isActiveSearchTimer = false;
};
$scope.init = function() {
$scope.isLoadingFilter = true;
$scope.jobInfomations = [];
$scope.JobInfomationsByFilter = $scope.jobInfomations;
$scope.filterConfig = {
isRunningAlwaysTop: true,
filterValueNotebookName: '',
filterValueInterpreter: '*',
isSortByAsc: true
};
$scope.jobTypeFilter = jobManagerFilter;
$scope.init = function() {
$scope.isLoadingFilter = true;
$scope.jobInfomations = [];
$scope.JobInfomationsByFilter = $scope.jobInfomations;
$scope.filterConfig = {
isRunningAlwaysTop: true,
filterValueNotebookName: '',
filterValueInterpreter: '*',
isSortByAsc: true
};
$scope.jobTypeFilter = jobManagerFilter;
websocketMsgSrv.getNoteJobsList();
websocketMsgSrv.getNoteJobsList();
$scope.$on('$destroy', function() {
websocketMsgSrv.unsubscribeJobManager();
});
};
$scope.$on('$destroy', function() {
websocketMsgSrv.unsubscribeJobManager();
});
};
/*
** $scope.$on functions below
*/
/*
** $scope.$on functions below
*/
$scope.$on('setNoteJobs', function(event, responseData) {
$scope.lastJobServerUnixTime = responseData.lastResponseUnixTime;
$scope.jobInfomations = responseData.jobs;
$scope.jobInfomationsIndexs = $scope.jobInfomations ? _.indexBy($scope.jobInfomations, 'noteId') : {};
$scope.jobTypeFilter($scope.jobInfomations, $scope.filterConfig);
$scope.activeInterpreters = [
{
name: 'ALL',
value: '*'
}
];
var interpreterLists = _.uniq(_.pluck($scope.jobInfomations, 'interpreter'), false);
for (var index = 0, length = interpreterLists.length; index < length; index++) {
$scope.activeInterpreters.push({
name: interpreterLists[index],
value: interpreterLists[index]
});
$scope.$on('setNoteJobs', function(event, responseData) {
$scope.lastJobServerUnixTime = responseData.lastResponseUnixTime;
$scope.jobInfomations = responseData.jobs;
$scope.jobInfomationsIndexs = $scope.jobInfomations ? _.indexBy($scope.jobInfomations, 'noteId') : {};
$scope.jobTypeFilter($scope.jobInfomations, $scope.filterConfig);
$scope.activeInterpreters = [
{
name: 'ALL',
value: '*'
}
$scope.doFiltering($scope.jobInfomations, $scope.filterConfig);
});
$scope.$on('setUpdateNoteJobs', function(event, responseData) {
var jobInfomations = $scope.jobInfomations;
var indexStore = $scope.jobInfomationsIndexs;
$scope.lastJobServerUnixTime = responseData.lastResponseUnixTime;
var notes = responseData.jobs;
notes.map(function(changedItem) {
if (indexStore[changedItem.noteId] === undefined) {
var newItem = angular.copy(changedItem);
jobInfomations.push(newItem);
indexStore[changedItem.noteId] = newItem;
} else {
var changeOriginTarget = indexStore[changedItem.noteId];
if (changedItem.isRemoved !== undefined && changedItem.isRemoved === true) {
// remove Item.
var removeIndex = _.findIndex(indexStore, changedItem.noteId);
if (removeIndex > -1) {
indexStore.splice(removeIndex, 1);
}
removeIndex = _.findIndex(jobInfomations, {'noteId': changedItem.noteId});
if (removeIndex) {
jobInfomations.splice(removeIndex, 1);
}
} else {
// change value for item.
changeOriginTarget.isRunningJob = changedItem.isRunningJob;
changeOriginTarget.noteName = changedItem.noteName;
changeOriginTarget.noteType = changedItem.noteType;
changeOriginTarget.interpreter = changedItem.interpreter;
changeOriginTarget.unixTimeLastRun = changedItem.unixTimeLastRun;
changeOriginTarget.paragraphs = changedItem.paragraphs;
}
}
];
var interpreterLists = _.uniq(_.pluck($scope.jobInfomations, 'interpreter'), false);
for (var index = 0, length = interpreterLists.length; index < length; index++) {
$scope.activeInterpreters.push({
name: interpreterLists[index],
value: interpreterLists[index]
});
$scope.doFiltering(jobInfomations, $scope.filterConfig);
});
}
}
$scope.doFiltering($scope.jobInfomations, $scope.filterConfig);
});
$scope.$on('setUpdateNoteJobs', function(event, responseData) {
var jobInfomations = $scope.jobInfomations;
var indexStore = $scope.jobInfomationsIndexs;
$scope.lastJobServerUnixTime = responseData.lastResponseUnixTime;
var notes = responseData.jobs;
notes.map(function(changedItem) {
if (indexStore[changedItem.noteId] === undefined) {
var newItem = angular.copy(changedItem);
jobInfomations.push(newItem);
indexStore[changedItem.noteId] = newItem;
} else {
var changeOriginTarget = indexStore[changedItem.noteId];
if (changedItem.isRemoved !== undefined && changedItem.isRemoved === true) {
// remove Item.
var removeIndex = _.findIndex(indexStore, changedItem.noteId);
if (removeIndex > -1) {
indexStore.splice(removeIndex, 1);
}
removeIndex = _.findIndex(jobInfomations, {'noteId': changedItem.noteId});
if (removeIndex) {
jobInfomations.splice(removeIndex, 1);
}
} else {
// change value for item.
changeOriginTarget.isRunningJob = changedItem.isRunningJob;
changeOriginTarget.noteName = changedItem.noteName;
changeOriginTarget.noteType = changedItem.noteType;
changeOriginTarget.interpreter = changedItem.interpreter;
changeOriginTarget.unixTimeLastRun = changedItem.unixTimeLastRun;
changeOriginTarget.paragraphs = changedItem.paragraphs;
}
}
});
$scope.doFiltering(jobInfomations, $scope.filterConfig);
});
}
})();

View file

@ -11,35 +11,33 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').filter('jobManager', jobManagerFilter);
angular.module('zeppelinWebApp').filter('jobManager', jobManagerFilter);
function jobManagerFilter() {
function filterContext(jobItems, filterConfig) {
var filterValueInterpreter = filterConfig.filterValueInterpreter;
var filterValueNotebookName = filterConfig.filterValueNotebookName;
var filterItems = jobItems;
function jobManagerFilter() {
function filterContext(jobItems, filterConfig) {
var filterValueInterpreter = filterConfig.filterValueInterpreter;
var filterValueNotebookName = filterConfig.filterValueNotebookName;
var filterItems = jobItems;
if (filterValueInterpreter === undefined) {
filterItems = _.filter(filterItems, function(jobItem) {
return jobItem.interpreter === undefined ? true : false;
});
} else if (filterValueInterpreter !== '*') {
filterItems = _.where(filterItems, {interpreter: filterValueInterpreter});
}
if (filterValueNotebookName !== '') {
filterItems = _.filter(filterItems, function(jobItem) {
var lowerFilterValue = filterValueNotebookName.toLocaleLowerCase();
var lowerNotebookName = jobItem.noteName.toLocaleLowerCase();
return lowerNotebookName.match(new RegExp('.*' + lowerFilterValue + '.*'));
});
}
return filterItems;
if (filterValueInterpreter === undefined) {
filterItems = _.filter(filterItems, function(jobItem) {
return jobItem.interpreter === undefined ? true : false;
});
} else if (filterValueInterpreter !== '*') {
filterItems = _.where(filterItems, {interpreter: filterValueInterpreter});
}
return filterContext;
}
})();
if (filterValueNotebookName !== '') {
filterItems = _.filter(filterItems, function(jobItem) {
var lowerFilterValue = filterValueNotebookName.toLocaleLowerCase();
var lowerNotebookName = jobItem.noteName.toLocaleLowerCase();
return lowerNotebookName.match(new RegExp('.*' + lowerFilterValue + '.*'));
});
}
return filterItems;
}
return filterContext;
}

View file

@ -11,99 +11,97 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').controller('JobCtrl', JobCtrl);
angular.module('zeppelinWebApp').controller('JobCtrl', JobCtrl);
JobCtrl.$inject = ['$scope', '$http', 'baseUrlSrv'];
JobCtrl.$inject = ['$scope', '$http', 'baseUrlSrv'];
function JobCtrl($scope, $http, baseUrlSrv) {
$scope.init = function(jobInformation) {
$scope.progressValue = 0;
};
function JobCtrl($scope, $http, baseUrlSrv) {
$scope.init = function(jobInformation) {
$scope.progressValue = 0;
};
$scope.getProgress = function() {
var statusList = _.pluck($scope.notebookJob.paragraphs, 'status');
var runningJob = _.countBy(statusList, function(status) {
if (status === 'FINISHED' || status === 'RUNNING') {
return 'matchCount';
} else {
return 'none';
}
});
var totalCount = statusList.length;
var runningJobCount = runningJob.matchCount;
var result = Math.ceil(runningJobCount / totalCount * 100);
return isNaN(result) ? 0 : result;
};
$scope.getProgress = function() {
var statusList = _.pluck($scope.notebookJob.paragraphs, 'status');
var runningJob = _.countBy(statusList, function(status) {
if (status === 'FINISHED' || status === 'RUNNING') {
return 'matchCount';
} else {
return 'none';
}
});
var totalCount = statusList.length;
var runningJobCount = runningJob.matchCount;
var result = Math.ceil(runningJobCount / totalCount * 100);
return isNaN(result) ? 0 : result;
};
$scope.runNotebookJob = function(notebookId) {
BootstrapDialog.confirm({
closable: true,
title: '',
message: 'Run all paragraphs?',
callback: function(result) {
if (result) {
$http({
method: 'POST',
url: baseUrlSrv.getRestApiBase() + '/notebook/job/' + notebookId,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then(function successCallback(response) {
// success
}, function errorCallback(errorResponse) {
var errorText = 'SERVER ERROR';
if (!!errorResponse.data.message) {
errorText = errorResponse.data.message;
}
BootstrapDialog.alert({
closable: true,
title: 'Execution Failure',
message: errorText
});
$scope.runNotebookJob = function(notebookId) {
BootstrapDialog.confirm({
closable: true,
title: '',
message: 'Run all paragraphs?',
callback: function(result) {
if (result) {
$http({
method: 'POST',
url: baseUrlSrv.getRestApiBase() + '/notebook/job/' + notebookId,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then(function successCallback(response) {
// success
}, function errorCallback(errorResponse) {
var errorText = 'SERVER ERROR';
if (!!errorResponse.data.message) {
errorText = errorResponse.data.message;
}
BootstrapDialog.alert({
closable: true,
title: 'Execution Failure',
message: errorText
});
}
});
}
});
};
}
});
};
$scope.stopNotebookJob = function(notebookId) {
BootstrapDialog.confirm({
closable: true,
title: '',
message: 'Stop all paragraphs?',
callback: function(result) {
if (result) {
$http({
method: 'DELETE',
url: baseUrlSrv.getRestApiBase() + '/notebook/job/' + notebookId,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then(function successCallback(response) {
// success
}, function errorCallback(errorResponse) {
var errorText = 'SERVER ERROR';
if (!!errorResponse.data.message) {
$scope.stopNotebookJob = function(notebookId) {
BootstrapDialog.confirm({
closable: true,
title: '',
message: 'Stop all paragraphs?',
callback: function(result) {
if (result) {
$http({
method: 'DELETE',
url: baseUrlSrv.getRestApiBase() + '/notebook/job/' + notebookId,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then(function successCallback(response) {
// success
}, function errorCallback(errorResponse) {
var errorText = 'SERVER ERROR';
if (!!errorResponse.data.message) {
errorText = errorResponse.data.message;
}
BootstrapDialog.alert({
closable: true,
title: 'Stop Failure',
message: errorText
});
errorText = errorResponse.data.message;
}
BootstrapDialog.alert({
closable: true,
title: 'Stop Failure',
message: errorText
});
}
});
}
});
};
}
});
};
$scope.lastExecuteTime = function(unixtime) {
return moment.unix(unixtime / 1000).fromNow();
};
$scope.lastExecuteTime = function(unixtime) {
return moment.unix(unixtime / 1000).fromNow();
};
}
}
})();

View file

@ -24,7 +24,7 @@ limitations under the License.
<span class="labelBtn btn-group">
<button type="button"
class="btn btn-default btn-xs"
ng-click="runNote()"
ng-click="runAllParagraphs(note.id)"
ng-class="{'disabled':isNoteRunning()}"
tooltip-placement="bottom" tooltip="Run all paragraphs"
ng-disabled="revisionView">

File diff suppressed because it is too large Load diff

View file

@ -13,7 +13,7 @@ limitations under the License.
-->
<div id="{{paragraph.id}}_runControl" class="runControl">
<div id="{{paragraph.id}}_progress" class="progress" ng-show="paragraph.status=='RUNNING'">
<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' )"

File diff suppressed because it is too large Load diff

View file

@ -72,6 +72,10 @@ table.dataTable.table-condensed .sorting_desc:after {
font-size: 12px !important;
}
.plainTextContent{
white-space:pre-wrap; /** to preserve white-space and newlines of result */
}
.graphContainer {
position: relative;
margin-bottom: 5px;

View file

@ -56,8 +56,7 @@ limitations under the License.
ng-controller="ResultCtrl"
ng-repeat="result in paragraph.results.msg track by $index"
ng-init="init(result, paragraph.config.results[$index], paragraph, $index)"
ng-include src="'app/notebook/paragraph/result/result.html'"
>
ng-include src="'app/notebook/paragraph/result/result.html'">
</div>
<div id="{{paragraph.id}}_error"
class="error text"

View file

@ -14,30 +14,32 @@ limitations under the License.
<div id="{{id}}_switch"
ng-if="(type == 'TABLE' || apps.length > 0 || suggestion.available && suggestion.available.length > 0) && !asIframe && !viewOnly"
class="btn-group"
style="margin-bottom: 10px;">
class="chart-selector">
<button type="button" class="btn btn-default btn-sm"
ng-if="type == 'TABLE'"
ng-repeat="viz in builtInTableDataVisualizationList track by $index"
ng-class="{'active' : viz.id == graphMode && !config.helium.activeApp}"
ng-click="switchViz(viz.id)"><i ng-class="viz.icon"></i>
</button>
<div ng-if="type == 'TABLE'" class="btn-group">
<button type="button" class="btn btn-default btn-sm"
ng-repeat="viz in builtInTableDataVisualizationList track by $index"
ng-class="{'active' : viz.id == graphMode && !config.helium.activeApp}"
ng-click="switchViz(viz.id)"><i ng-class="viz.icon"></i>
</button>
</div>
<button type="button"
ng-if="type != 'TABLE'"
ng-click="switchApp()"
ng-class="{'active' : !config.helium.activeApp}"
class="btn btn-default btn-sm"><i class="fa fa-terminal"></i>
</button>
<div class="btn-group">
<button type="button"
ng-if="type != 'TABLE'"
ng-click="switchApp()"
ng-class="{'active' : !config.helium.activeApp}"
class="btn btn-default btn-sm"><i class="fa fa-terminal"></i>
</button>
<button type="button"
class="btn btn-default btn-sm"
ng-repeat="app in apps"
ng-click="switchApp(app.id)"
ng-class="{'active' : app.id == config.helium.activeApp}"
ng-bind-html="app.pkg.icon">
</button>
<button type="button"
class="btn btn-default btn-sm"
ng-repeat="app in apps"
ng-click="switchApp(app.id)"
ng-class="{'active' : app.id == config.helium.activeApp}"
ng-bind-html="app.pkg.icon">
</button>
</div>
</div>
<div id="{{paragraph.id}}_helium"
@ -46,7 +48,6 @@ limitations under the License.
style="margin-bottom: 10px;">
<button type="button"
class="btn btn-default btn-sm dropdown-toggle"
ng-if="suggestion.available && suggestion.available.length > 0"
data-toggle="dropdown"
style="font-weight:bold; background-color:#ffdf96; border: 1px solid #FED233">
He

View file

@ -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.
-->
<div
id="p{{id}}_resize"
ng-if="!config.helium.activeApp"
style="padding-bottom: 5px;"
resize='{"allowresize": "{{!asIframe && !viewOnly}}", "graphType": "{{type}}"}'
resizable on-resize="resize(width, height);">
<div ng-include src="'app/notebook/paragraph/result/result-graph.html'"></div>
<div id="{{id}}_comment"
class="text"
ng-if="type == 'TABLE' && tableDataComment"
ng-bind-html="tableDataComment">
</div>
<div id="p{{id}}_text"
ng-if="type == 'TEXT'">
<div class="fa fa-level-down scroll-paragraph-down"
ng-show="showScrollDownIcon(id)"
ng-click="scrollParagraphDown(id)"
tooltip="Follow Output"></div>
<div id="p{{id}}_text"
style="max-height: {{config.graph.height}}px; overflow: auto"
class="text"></div>
<div class="fa fa-chevron-up scroll-paragraph-up"
ng-show="showScrollUpIcon(id)"
ng-click="scrollParagraphUp(id)"
tooltip="Scroll Top"></div>
</div>
<div id="p{{id}}_html"
class="resultContained"
ng-if="type == 'HTML'">
</div>
<div id="p{{id}}_angular"
class="resultContained"
ng-if="type == 'ANGULAR'">
</div>
<img id="{{id}}_img"
ng-if="type == 'IMG'"
ng-src="{{getBase64ImageSrc(result.data)}}" />
</div>
<div ng-repeat="app in apps">
<div id="p{{app.id}}"
ng-show="config.helium.activeApp == app.id">
</div>
</div>

View file

@ -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.
*/
.chart-selector {
margin-bottom: 10px;
position: relative;
display: inline-block;
vertical-align: middle;
}

View file

@ -21,38 +21,38 @@ limitations under the License.
resize='{"allowresize": "{{!asIframe && !viewOnly}}", "graphType": "{{type}}"}'
resizable on-resize="resize(width, height);">
<!-- graph setting -->
<div class="option lightBold" style="overflow: visible;"
ng-show="type=='TABLE' && graphMode!='table'
&& config.graph.optionOpen && !asIframe && !viewOnly">
<div ng-repeat="viz in builtInTableDataVisualizationList track by $index"
id="trsetting{{id}}_{{viz.id}}"
ng-show="graphMode == viz.id"></div>
<div ng-repeat="viz in builtInTableDataVisualizationList track by $index"
id="vizsetting{{id}}_{{viz.id}}"
ng-show="graphMode == viz.id"></div>
</div>
<div ng-if="type=='TABLE'">
<!-- graph setting -->
<div class="option lightBold" style="overflow: visible;"
ng-if="graphMode!='table'
&& config.graph.optionOpen && !asIframe && !viewOnly">
<div ng-repeat="viz in builtInTableDataVisualizationList track by $index"
id="trsetting{{id}}_{{viz.id}}"
ng-show="graphMode == viz.id"></div>
<div ng-repeat="viz in builtInTableDataVisualizationList track by $index"
id="vizsetting{{id}}_{{viz.id}}"
ng-show="graphMode == viz.id"></div>
</div>
<!-- graph -->
<div id="p{{id}}_graph"
class="graphContainer"
ng-class="{'noOverflow': graphMode=='table'}"
ng-show="type =='TABLE'"
>
<div ng-repeat="viz in builtInTableDataVisualizationList track by $index"
id="p{{id}}_{{viz.id}}"
ng-show="graphMode == viz.id">
<!-- graph -->
<div id="p{{id}}_graph"
class="graphContainer"
ng-class="{'noOverflow': graphMode=='table'}"
>
<div ng-repeat="viz in builtInTableDataVisualizationList track by $index"
id="p{{id}}_{{viz.id}}"
ng-show="graphMode == viz.id">
</div>
</div>
<div id="{{id}}_comment"
class="text"
ng-if="tableDataComment"
ng-bind-html="tableDataComment">
</div>
</div>
<div id="{{id}}_comment"
class="text"
ng-if="type == 'TABLE' && tableDataComment"
ng-bind-html="tableDataComment">
</div>
<div id="p{{id}}_text"
ng-if="type == 'TEXT'"
<div ng-if="type == 'TEXT'"
class="plainTextContainer">
<div class="fa fa-level-down scroll-paragraph-down"
ng-show="showScrollDownIcon()"
@ -60,7 +60,7 @@ limitations under the License.
tooltip="Follow Output"></div>
<div id="p{{id}}_text"
style="max-height: {{config.graph.height}}px; overflow: auto"
class="text"></div>
class="text plainTextContent"></div>
<div class="fa fa-chevron-up scroll-paragraph-up"
ng-show="showScrollUpIcon()"
ng-click="scrollParagraphUp()"

View file

@ -11,80 +11,78 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').controller('NotebookReposCtrl', NotebookReposCtrl);
angular.module('zeppelinWebApp').controller('NotebookReposCtrl', NotebookReposCtrl);
NotebookReposCtrl.$inject = ['$http', 'baseUrlSrv', 'ngToast'];
NotebookReposCtrl.$inject = ['$http', 'baseUrlSrv', 'ngToast'];
function NotebookReposCtrl($http, baseUrlSrv, ngToast) {
var vm = this;
vm.notebookRepos = [];
vm.showDropdownSelected = showDropdownSelected;
vm.saveNotebookRepo = saveNotebookRepo;
function NotebookReposCtrl($http, baseUrlSrv, ngToast) {
var vm = this;
vm.notebookRepos = [];
vm.showDropdownSelected = showDropdownSelected;
vm.saveNotebookRepo = saveNotebookRepo;
_init();
_init();
// Public functions
// 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) {
var 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) {
var index = _.findIndex(setting.value, {'value': setting.selected});
if (index < 0) {
return 'No value';
} else {
return setting.value[index].name;
function saveNotebookRepo(valueform, repo, data) {
console.log('data %o', data);
$http.put(baseUrlSrv.getRestApiBase() + '/notebook-repositories', {
'name': repo.className,
'settings': data
}).success(function(data) {
var 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) {
var index = _.findIndex(setting.value, {'value': setting.selected});
if (index < 0) {
return 'No value';
} else {
return setting.value[index].name;
}
}
// Private functions
// Private functions
function _getInterpreterSettings() {
$http.get(baseUrlSrv.getRestApiBase() + '/notebook-repositories')
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.replace('/');
}, 3000);
}
console.log('Error %o %o', status, data.message);
});
}
function _init() {
_getInterpreterSettings();
};
if (status === 401) {
ngToast.danger({
content: 'You don\'t have permission on this page',
verticalPosition: 'bottom',
timeout: '3000'
});
setTimeout(function() {
window.location.replace('/');
}, 3000);
}
console.log('Error %o %o', status, data.message);
});
}
})();
function _init() {
_getInterpreterSettings();
};
}

View file

@ -11,148 +11,146 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').controller('SearchResultCtrl', SearchResultCtrl);
angular.module('zeppelinWebApp').controller('SearchResultCtrl', SearchResultCtrl);
SearchResultCtrl.$inject = ['$scope', '$routeParams', 'searchService'];
SearchResultCtrl.$inject = ['$scope', '$routeParams', 'searchService'];
function SearchResultCtrl($scope, $routeParams, searchService) {
$scope.isResult = true ;
$scope.searchTerm = $routeParams.searchTerm;
var results = searchService.search({'q': $routeParams.searchTerm}).query();
results.$promise.then(function(result) {
$scope.notes = result.body.map(function(note) {
// redirect to notebook when search result is a notebook itself,
// not a paragraph
if (!/\/paragraph\//.test(note.id)) {
return note;
}
note.id = note.id.replace('paragraph/', '?paragraph=') +
'&term=' + $routeParams.searchTerm;
function SearchResultCtrl($scope, $routeParams, searchService) {
$scope.isResult = true ;
$scope.searchTerm = $routeParams.searchTerm;
var results = searchService.search({'q': $routeParams.searchTerm}).query();
results.$promise.then(function(result) {
$scope.notes = result.body.map(function(note) {
// redirect to notebook when search result is a notebook itself,
// not a paragraph
if (!/\/paragraph\//.test(note.id)) {
return note;
});
if ($scope.notes.length === 0) {
$scope.isResult = false;
} else {
$scope.isResult = true;
}
$scope.$on('$routeChangeStart', function(event, next, current) {
if (next.originalPath !== '/search/:searchTerm') {
searchService.searchTerm = '';
}
});
note.id = note.id.replace('paragraph/', '?paragraph=') +
'&term=' + $routeParams.searchTerm;
return note;
});
if ($scope.notes.length === 0) {
$scope.isResult = false;
} else {
$scope.isResult = true;
}
$scope.page = 0;
$scope.allResults = false;
$scope.$on('$routeChangeStart', function(event, next, current) {
if (next.originalPath !== '/search/:searchTerm') {
searchService.searchTerm = '';
}
});
});
$scope.highlightSearchResults = function(note) {
return function(_editor) {
function getEditorMode(text) {
var editorModes = {
'ace/mode/scala': /^%(\w*\.)?spark/,
'ace/mode/python': /^%(\w*\.)?(pyspark|python)/,
'ace/mode/r': /^%(\w*\.)?(r|sparkr|knitr)/,
'ace/mode/sql': /^%(\w*\.)?\wql/,
'ace/mode/markdown': /^%md/,
'ace/mode/sh': /^%sh/
};
$scope.page = 0;
$scope.allResults = false;
return Object.keys(editorModes).reduce(function(res, mode) {
return editorModes[mode].test(text) ? mode : res;
}, 'ace/mode/scala');
}
$scope.highlightSearchResults = function(note) {
return function(_editor) {
function getEditorMode(text) {
var editorModes = {
'ace/mode/scala': /^%(\w*\.)?spark/,
'ace/mode/python': /^%(\w*\.)?(pyspark|python)/,
'ace/mode/r': /^%(\w*\.)?(r|sparkr|knitr)/,
'ace/mode/sql': /^%(\w*\.)?\wql/,
'ace/mode/markdown': /^%md/,
'ace/mode/sh': /^%sh/
};
var Range = ace.require('ace/range').Range;
return Object.keys(editorModes).reduce(function(res, mode) {
return editorModes[mode].test(text) ? mode : res;
}, 'ace/mode/scala');
}
_editor.setOption('highlightActiveLine', false);
_editor.$blockScrolling = Infinity;
_editor.setReadOnly(true);
_editor.renderer.setShowGutter(false);
_editor.setTheme('ace/theme/chrome');
_editor.getSession().setMode(getEditorMode(note.text));
var Range = ace.require('ace/range').Range;
function getIndeces(term) {
return function(str) {
var indeces = [];
var i = -1;
while ((i = str.indexOf(term, i + 1)) >= 0) {
indeces.push(i);
_editor.setOption('highlightActiveLine', false);
_editor.$blockScrolling = Infinity;
_editor.setReadOnly(true);
_editor.renderer.setShowGutter(false);
_editor.setTheme('ace/theme/chrome');
_editor.getSession().setMode(getEditorMode(note.text));
function getIndeces(term) {
return function(str) {
var indeces = [];
var i = -1;
while ((i = str.indexOf(term, i + 1)) >= 0) {
indeces.push(i);
}
return indeces;
};
}
var result = '';
if (note.header !== '') {
result = note.header + '\n\n' + note.snippet;
} else {
result = note.snippet;
}
var lines = result
.split('\n')
.map(function(line, row) {
var match = line.match(/<B>(.+?)<\/B>/);
// return early if nothing to highlight
if (!match) {
return line;
}
var term = match[1];
var __line = line
.replace(/<B>/g, '')
.replace(/<\/B>/g, '');
var indeces = getIndeces(term)(__line);
indeces.forEach(function(start) {
var end = start + term.length;
if (note.header !== '' && row === 0) {
_editor
.getSession()
.addMarker(
new Range(row, 0, row, line.length),
'search-results-highlight-header',
'background'
);
_editor
.getSession()
.addMarker(
new Range(row, start, row, end),
'search-results-highlight',
'line'
);
} else {
_editor
.getSession()
.addMarker(
new Range(row, start, row, end),
'search-results-highlight',
'line'
);
}
return indeces;
};
}
var result = '';
if (note.header !== '') {
result = note.header + '\n\n' + note.snippet;
} else {
result = note.snippet;
}
var lines = result
.split('\n')
.map(function(line, row) {
var match = line.match(/<B>(.+?)<\/B>/);
// return early if nothing to highlight
if (!match) {
return line;
}
var term = match[1];
var __line = line
.replace(/<B>/g, '')
.replace(/<\/B>/g, '');
var indeces = getIndeces(term)(__line);
indeces.forEach(function(start) {
var end = start + term.length;
if (note.header !== '' && row === 0) {
_editor
.getSession()
.addMarker(
new Range(row, 0, row, line.length),
'search-results-highlight-header',
'background'
);
_editor
.getSession()
.addMarker(
new Range(row, start, row, end),
'search-results-highlight',
'line'
);
} else {
_editor
.getSession()
.addMarker(
new Range(row, start, row, end),
'search-results-highlight',
'line'
);
}
});
return __line;
});
return __line;
});
// resize editor based on content length
_editor.setOption(
'maxLines',
lines.reduce(function(len, line) {return len + line.length;}, 0)
);
// resize editor based on content length
_editor.setOption(
'maxLines',
lines.reduce(function(len, line) {return len + line.length;}, 0)
);
_editor.getSession().setValue(lines.join('\n'));
_editor.getSession().setValue(lines.join('\n'));
};
};
}
};
}
})();

View file

@ -11,29 +11,27 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').service('arrayOrderingSrv', arrayOrderingSrv);
angular.module('zeppelinWebApp').service('arrayOrderingSrv', arrayOrderingSrv);
arrayOrderingSrv.$inject = ['TRASH_FOLDER_ID'];
arrayOrderingSrv.$inject = ['TRASH_FOLDER_ID'];
function arrayOrderingSrv(TRASH_FOLDER_ID) {
var arrayOrderingSrv = this;
function arrayOrderingSrv(TRASH_FOLDER_ID) {
var arrayOrderingSrv = this;
this.noteListOrdering = function(note) {
if (note.id === TRASH_FOLDER_ID) {
return '\uFFFF';
}
return arrayOrderingSrv.getNoteName(note);
};
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.getNoteName = function(note) {
if (note.name === undefined || note.name.trim() === '') {
return 'Note ' + note.id;
} else {
return note.name;
}
};
}
})();

View file

@ -11,41 +11,39 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').service('baseUrlSrv', baseUrlSrv);
angular.module('zeppelinWebApp').service('baseUrlSrv', baseUrlSrv);
function baseUrlSrv() {
this.getPort = function() {
var port = Number(location.port);
if (!port) {
port = 80;
if (location.protocol === 'https:') {
port = 443;
}
function baseUrlSrv() {
this.getPort = function() {
var port = Number(location.port);
if (!port) {
port = 80;
if (location.protocol === 'https:') {
port = 443;
}
//Exception for when running locally via grunt
if (port === 3333 || port === 9000) {
port = 8080;
}
return port;
};
}
//Exception for when running locally via grunt
if (port === 3333 || port === 9000) {
port = 8080;
}
return port;
};
this.getWebsocketUrl = function() {
var wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
return wsProtocol + '//' + location.hostname + ':' + this.getPort() +
this.getWebsocketUrl = function() {
var wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
return wsProtocol + '//' + location.hostname + ':' + this.getPort() +
skipTrailingSlash(location.pathname) + '/ws';
};
};
this.getRestApiBase = function() {
return location.protocol + '//' + location.hostname + ':' +
this.getRestApiBase = function() {
return location.protocol + '//' + location.hostname + ':' +
this.getPort() + skipTrailingSlash(location.pathname) +
'/api';
};
'/api';
};
var skipTrailingSlash = function(path) {
return path.replace(/\/$/, '');
};
}
var skipTrailingSlash = function(path) {
return path.replace(/\/$/, '');
};
}
})();

View file

@ -11,32 +11,30 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').service('browserDetectService', browserDetectService);
angular.module('zeppelinWebApp').service('browserDetectService', browserDetectService);
function browserDetectService() {
this.detectIE = function() {
var ua = window.navigator.userAgent;
var msie = ua.indexOf('MSIE ');
if (msie > 0) {
// IE 10 or older => return version number
return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
}
var trident = ua.indexOf('Trident/');
if (trident > 0) {
// IE 11 => return version number
var rv = ua.indexOf('rv:');
return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
}
var 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;
};
}
function browserDetectService() {
this.detectIE = function() {
var ua = window.navigator.userAgent;
var msie = ua.indexOf('MSIE ');
if (msie > 0) {
// IE 10 or older => return version number
return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
}
var trident = ua.indexOf('Trident/');
if (trident > 0) {
// IE 11 => return version number
var rv = ua.indexOf('rv:');
return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
}
var 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;
};
}
})();

View file

@ -11,25 +11,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').controller('clipboardCtrl', clipboardCtrl);
clipboardCtrl.$inject = ['$scope'];
angular.module('zeppelinWebApp').controller('clipboardCtrl', clipboardCtrl);
clipboardCtrl.$inject = ['$scope'];
function clipboardCtrl($scope) {
$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;
function clipboardCtrl($scope) {
$scope.complete = function(e) {
$scope.copied = true;
$scope.tooltip = 'Copied!';
setTimeout(function() {
$scope.tooltip = 'Copy to clipboard';
});
$scope.clipError = function(e) {
console.log('Error: ' + e.name + ' - ' + e.message);
$scope.tooltip = 'Not supported browser';
};
}
})();
}, 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';
};
}

View file

@ -11,19 +11,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').directive('dropdownInput', dropdownInput);
angular.module('zeppelinWebApp').directive('dropdownInput', dropdownInput);
function dropdownInput() {
return {
restrict: 'A',
link: function(scope, element) {
element.bind('click', function(event) {
event.stopPropagation();
});
}
};
}
function dropdownInput() {
return {
restrict: 'A',
link: function(scope, element) {
element.bind('click', function(event) {
event.stopPropagation();
});
}
};
}
})();

View file

@ -11,31 +11,29 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').directive('codeEditor', codeEditor);
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) {
var editor = angular.element(editorHtml);
editor.attr('id', scope.paragraphId + '_editor');
element.append(editor);
$compile(editor)(scope);
console.log('codeEditor directive revision view is ' + scope.revisionView);
});
}
};
}
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) {
var editor = angular.element(editorHtml);
editor.attr('id', scope.paragraphId + '_editor');
element.append(editor);
$compile(editor)(scope);
console.log('codeEditor directive revision view is ' + scope.revisionView);
});
}
};
}
})();

View file

@ -11,14 +11,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').controller('ElasticInputCtrl', ElasticInputCtrl);
angular.module('zeppelinWebApp').controller('ElasticInputCtrl', ElasticInputCtrl);
function ElasticInputCtrl() {
var vm = this;
vm.showEditor = false;
vm.value = '';
}
function ElasticInputCtrl() {
var vm = this;
vm.showEditor = false;
vm.value = '';
}
})();

View file

@ -11,30 +11,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').directive('expandCollapse', expandCollapse);
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.icon-folder-alt').toggleClass('icon-folder icon-folder-alt');
} 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('icon-folder icon-folder-alt');
}
});
}
event.stopPropagation();
});
}
};
}
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.icon-folder-alt').toggleClass('icon-folder icon-folder-alt');
} 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('icon-folder icon-folder-alt');
}
});
}
event.stopPropagation();
});
}
};
}
})();

View file

@ -11,24 +11,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').directive('interpreterDirective', interpreterDirective);
angular.module('zeppelinWebApp').directive('interpreterDirective', interpreterDirective);
interpreterDirective.$inject = ['$timeout'];
interpreterDirective.$inject = ['$timeout'];
function interpreterDirective($timeout) {
return {
restrict: 'A',
link: function(scope, element, attr) {
if (scope.$last === true) {
$timeout(function() {
var id = 'ngRenderFinished';
scope.$emit(id);
});
}
function interpreterDirective($timeout) {
return {
restrict: 'A',
link: function(scope, element, attr) {
if (scope.$last === true) {
$timeout(function() {
var id = 'ngRenderFinished';
scope.$emit(id);
});
}
};
}
}
};
}
})();

View file

@ -11,53 +11,51 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').controller('LoginCtrl', LoginCtrl);
angular.module('zeppelinWebApp').controller('LoginCtrl', LoginCtrl);
LoginCtrl.$inject = ['$scope', '$rootScope', '$http', '$httpParamSerializer', 'baseUrlSrv'];
function LoginCtrl($scope, $rootScope, $http, $httpParamSerializer, baseUrlSrv) {
$scope.SigningIn = false;
$scope.loginParams = {};
$scope.login = function() {
LoginCtrl.$inject = ['$scope', '$rootScope', '$http', '$httpParamSerializer', 'baseUrlSrv'];
function LoginCtrl($scope, $rootScope, $http, $httpParamSerializer, baseUrlSrv) {
$scope.SigningIn = false;
$scope.loginParams = {};
$scope.login = function() {
$scope.SigningIn = true;
$http({
method: 'POST',
url: baseUrlSrv.getRestApiBase() + '/login',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: $httpParamSerializer({
'userName': $scope.loginParams.userName,
'password': $scope.loginParams.password
})
}).then(function successCallback(response) {
$rootScope.ticket = response.data.body;
angular.element('#loginModal').modal('toggle');
$rootScope.$broadcast('loginSuccess', true);
$rootScope.userName = $scope.loginParams.userName;
}, function errorCallback(errorResponse) {
$scope.loginParams.errorText = 'The username and password that you entered don\'t match.';
$scope.SigningIn = false;
});
};
var initValues = function() {
$scope.loginParams = {
userName: '',
password: ''
};
};
/*
** $scope.$on functions below
*/
$scope.$on('initLoginValues', function() {
initValues();
$scope.SigningIn = true;
$http({
method: 'POST',
url: baseUrlSrv.getRestApiBase() + '/login',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: $httpParamSerializer({
'userName': $scope.loginParams.userName,
'password': $scope.loginParams.password
})
}).then(function successCallback(response) {
$rootScope.ticket = response.data.body;
angular.element('#loginModal').modal('toggle');
$rootScope.$broadcast('loginSuccess', true);
$rootScope.userName = $scope.loginParams.userName;
}, function errorCallback(errorResponse) {
$scope.loginParams.errorText = 'The username and password that you entered don\'t match.';
$scope.SigningIn = false;
});
}
})();
};
var initValues = function() {
$scope.loginParams = {
userName: '',
password: ''
};
};
/*
** $scope.$on functions below
*/
$scope.$on('initLoginValues', function() {
initValues();
});
}

View file

@ -11,135 +11,133 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').controller('NavCtrl', NavCtrl);
angular.module('zeppelinWebApp').controller('NavCtrl', NavCtrl);
NavCtrl.$inject = [
'$scope',
'$rootScope',
'$http',
'$routeParams',
'$location',
'noteListDataFactory',
'baseUrlSrv',
'websocketMsgSrv',
'arrayOrderingSrv',
'searchService',
'TRASH_FOLDER_ID'
];
NavCtrl.$inject = [
'$scope',
'$rootScope',
'$http',
'$routeParams',
'$location',
'noteListDataFactory',
'baseUrlSrv',
'websocketMsgSrv',
'arrayOrderingSrv',
'searchService',
'TRASH_FOLDER_ID'
];
function NavCtrl($scope, $rootScope, $http, $routeParams, $location,
noteListDataFactory, baseUrlSrv, websocketMsgSrv,
arrayOrderingSrv, searchService, TRASH_FOLDER_ID) {
var vm = this;
vm.arrayOrderingSrv = arrayOrderingSrv;
vm.connected = websocketMsgSrv.isConnected();
vm.isActive = isActive;
vm.logout = logout;
vm.notes = noteListDataFactory;
vm.search = search;
vm.searchForm = searchService;
vm.showLoginWindow = showLoginWindow;
vm.TRASH_FOLDER_ID = TRASH_FOLDER_ID;
function NavCtrl($scope, $rootScope, $http, $routeParams, $location,
noteListDataFactory, baseUrlSrv, websocketMsgSrv,
arrayOrderingSrv, searchService, TRASH_FOLDER_ID) {
var vm = this;
vm.arrayOrderingSrv = arrayOrderingSrv;
vm.connected = websocketMsgSrv.isConnected();
vm.isActive = isActive;
vm.logout = logout;
vm.notes = noteListDataFactory;
vm.search = search;
vm.searchForm = searchService;
vm.showLoginWindow = showLoginWindow;
vm.TRASH_FOLDER_ID = TRASH_FOLDER_ID;
$scope.query = {q: ''};
$scope.query = {q: ''};
initController();
initController();
function getZeppelinVersion() {
$http.get(baseUrlSrv.getRestApiBase() + '/version').success(
function(data, status, headers, config) {
$rootScope.zeppelinVersion = data.body;
}).error(
function(data, status, headers, config) {
console.log('Error %o %o', status, data.message);
});
}
function initController() {
$scope.isDrawNavbarNoteList = false;
angular.element('#notebook-list').perfectScrollbar({suppressScrollX: true});
angular.element(document).click(function() {
$scope.query.q = '';
function getZeppelinVersion() {
$http.get(baseUrlSrv.getRestApiBase() + '/version').success(
function(data, status, headers, config) {
$rootScope.zeppelinVersion = data.body;
}).error(
function(data, status, headers, config) {
console.log('Error %o %o', status, data.message);
});
getZeppelinVersion();
loadNotes();
}
function isActive(noteId) {
return ($routeParams.noteId === noteId);
}
function loadNotes() {
websocketMsgSrv.getNoteList();
}
function logout() {
var logoutURL = baseUrlSrv.getRestApiBase() + '/login/logout';
//for firefox and safari
logoutURL = logoutURL.replace('//', '//false:false@');
$http.post(logoutURL).error(function() {
//force authcBasic (if configured) to logout
$http.post(logoutURL).error(function() {
$rootScope.userName = '';
$rootScope.ticket.principal = '';
$rootScope.ticket.ticket = '';
$rootScope.ticket.roles = '';
BootstrapDialog.show({
message: 'Logout Success'
});
setTimeout(function() {
window.location.replace('/');
}, 1000);
});
});
}
function search(searchTerm) {
$location.path('/search/' + searchTerm);
}
function showLoginWindow() {
setTimeout(function() {
angular.element('#userName').focus();
}, 500);
}
/*
** $scope.$on functions below
*/
$scope.$on('setNoteMenu', function(event, notes) {
noteListDataFactory.setNotes(notes);
initNotebookListEventListener();
});
$scope.$on('setConnectedStatus', function(event, param) {
vm.connected = param;
});
$scope.$on('loginSuccess', function(event, param) {
loadNotes();
});
/*
** Performance optimization for Browser Render.
*/
function initNotebookListEventListener() {
angular.element(document).ready(function() {
angular.element('.notebook-list-dropdown').on('show.bs.dropdown', function() {
$scope.isDrawNavbarNoteList = true;
});
angular.element('.notebook-list-dropdown').on('hide.bs.dropdown', function() {
$scope.isDrawNavbarNoteList = false;
});
});
}
}
})();
function initController() {
$scope.isDrawNavbarNoteList = false;
angular.element('#notebook-list').perfectScrollbar({suppressScrollX: true});
angular.element(document).click(function() {
$scope.query.q = '';
});
getZeppelinVersion();
loadNotes();
}
function isActive(noteId) {
return ($routeParams.noteId === noteId);
}
function loadNotes() {
websocketMsgSrv.getNoteList();
}
function logout() {
var logoutURL = baseUrlSrv.getRestApiBase() + '/login/logout';
//for firefox and safari
logoutURL = logoutURL.replace('//', '//false:false@');
$http.post(logoutURL).error(function() {
//force authcBasic (if configured) to logout
$http.post(logoutURL).error(function() {
$rootScope.userName = '';
$rootScope.ticket.principal = '';
$rootScope.ticket.ticket = '';
$rootScope.ticket.roles = '';
BootstrapDialog.show({
message: 'Logout Success'
});
setTimeout(function() {
window.location.replace('/');
}, 1000);
});
});
}
function search(searchTerm) {
$location.path('/search/' + searchTerm);
}
function showLoginWindow() {
setTimeout(function() {
angular.element('#userName').focus();
}, 500);
}
/*
** $scope.$on functions below
*/
$scope.$on('setNoteMenu', function(event, notes) {
noteListDataFactory.setNotes(notes);
initNotebookListEventListener();
});
$scope.$on('setConnectedStatus', function(event, param) {
vm.connected = param;
});
$scope.$on('loginSuccess', function(event, param) {
loadNotes();
});
/*
** Performance optimization for Browser Render.
*/
function initNotebookListEventListener() {
angular.element(document).ready(function() {
angular.element('.notebook-list-dropdown').on('show.bs.dropdown', function() {
$scope.isDrawNavbarNoteList = true;
});
angular.element('.notebook-list-dropdown').on('hide.bs.dropdown', function() {
$scope.isDrawNavbarNoteList = false;
});
});
}
}

View file

@ -11,23 +11,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').directive('ngEnter', ngEnter);
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();
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();
}
});
};
}
})();

View file

@ -11,21 +11,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').directive('ngEscape', ngEscape);
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();
}
});
};
}
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();
}
});
};
}
})();

View file

@ -11,175 +11,173 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').service('noteActionSrv', noteActionSrv);
angular.module('zeppelinWebApp').service('noteActionSrv', noteActionSrv);
noteActionSrv.$inject = ['websocketMsgSrv', '$location', 'renameSrv', 'noteListDataFactory'];
noteActionSrv.$inject = ['websocketMsgSrv', '$location', 'renameSrv', 'noteListDataFactory'];
function noteActionSrv(websocketMsgSrv, $location, renameSrv, noteListDataFactory) {
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('/');
}
function noteActionSrv(websocketMsgSrv, $location, renameSrv, noteListDataFactory) {
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.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.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.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.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.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.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.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) {
var 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);
}
this.renameFolder = function(folderId) {
renameSrv.openRenameModal({
title: 'Rename folder',
oldName: folderId,
callback: function(newName) {
var 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);
}
}
});
} 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('///', '/');
}
function normalizeFolderId(folderId) {
folderId = folderId.trim();
folderId = folderId.replace('//', '/');
if (folderId === '/') {
return '/';
}
if (folderId[0] === '/') {
folderId = folderId.substring(1);
}
if (folderId.slice(-1) === '/') {
folderId = folderId.slice(0, -1);
}
return folderId;
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;
}
})();
}

View file

@ -11,74 +11,72 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').factory('noteListDataFactory', noteListDataFactory);
angular.module('zeppelinWebApp').factory('noteListDataFactory', noteListDataFactory);
noteListDataFactory.$inject = ['TRASH_FOLDER_ID'];
noteListDataFactory.$inject = ['TRASH_FOLDER_ID'];
function noteListDataFactory(TRASH_FOLDER_ID) {
var notes = {
root: {children: []},
flatList: [],
flatFolderMap: {},
function noteListDataFactory(TRASH_FOLDER_ID) {
var 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;
});
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) {
var noteName = note.name || note.id;
var nodes = noteName.match(/([^\/][^\/]*)/g);
// construct the folder-based tree
notes.root = {children: []};
notes.flatFolderMap = {};
_.reduce(notesList, function(root, note) {
var noteName = note.name || note.id;
var nodes = noteName.match(/([^\/][^\/]*)/g);
// recursively add nodes
addNode(root, nodes, note.id);
// recursively add nodes
addNode(root, nodes, note.id);
return root;
}, notes.root);
}
};
return root;
}, notes.root);
}
};
var 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],
var 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
var node = nodes.shift();
var 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 {
var newDir = {
id: curDir.id ? curDir.id + '/' + node : node,
name: node,
hidden: true,
children: [],
isTrash: curDir.id ? curDir.id.split('/')[0] === TRASH_FOLDER_ID : false
});
} else { // a folder node
var node = nodes.shift();
var 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 {
var 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;
// add the folder to flat folder map
notes.flatFolderMap[newDir.id] = newDir;
curDir.children.push(newDir);
addNode(newDir, nodes, noteId);
}
curDir.children.push(newDir);
addNode(newDir, nodes, noteId);
}
};
}
};
return notes;
}
return notes;
}
})();

View file

@ -11,104 +11,102 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').controller('NotenameCtrl', NotenameCtrl);
angular.module('zeppelinWebApp').controller('NotenameCtrl', NotenameCtrl);
NotenameCtrl.$inject = [
'$scope',
'noteListDataFactory',
'$routeParams',
'websocketMsgSrv'
];
NotenameCtrl.$inject = [
'$scope',
'noteListDataFactory',
'$routeParams',
'websocketMsgSrv'
];
function NotenameCtrl($scope, noteListDataFactory, $routeParams, websocketMsgSrv) {
var vm = this;
vm.clone = false;
vm.notes = noteListDataFactory;
vm.websocketMsgSrv = websocketMsgSrv;
$scope.note = {};
$scope.interpreterSettings = {};
$scope.note.defaultInterpreter = null;
function NotenameCtrl($scope, noteListDataFactory, $routeParams, websocketMsgSrv) {
var 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) {
var 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 {
var noteId = $routeParams.noteId;
vm.websocketMsgSrv.cloneNote(noteId, $scope.note.notename);
vm.createNote = function() {
if (!vm.clone) {
var 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 {
var noteId = $routeParams.noteId;
vm.websocketMsgSrv.cloneNote(noteId, $scope.note.notename);
}
};
vm.handleNameEnter = function() {
angular.element('#noteNameModal').modal('toggle');
vm.createNote();
};
vm.handleNameEnter = function() {
angular.element('#noteNameModal').modal('toggle');
vm.createNote();
};
vm.preVisible = function(clone, sourceNoteName) {
vm.clone = clone;
vm.sourceNoteName = sourceNoteName;
$scope.note.notename = vm.clone ? vm.cloneNoteName() : vm.newNoteName();
$scope.$apply();
};
vm.preVisible = function(clone, sourceNoteName) {
vm.clone = clone;
vm.sourceNoteName = sourceNoteName;
$scope.note.notename = vm.clone ? vm.cloneNoteName() : vm.newNoteName();
$scope.$apply();
};
vm.newNoteName = function() {
var newCount = 1;
angular.forEach(vm.notes.flatList, function(noteName) {
noteName = noteName.name;
if (noteName.match(/^Untitled Note [0-9]*$/)) {
var lastCount = noteName.substr(14) * 1;
if (newCount <= lastCount) {
newCount = lastCount + 1;
}
vm.newNoteName = function() {
var newCount = 1;
angular.forEach(vm.notes.flatList, function(noteName) {
noteName = noteName.name;
if (noteName.match(/^Untitled Note [0-9]*$/)) {
var lastCount = noteName.substr(14) * 1;
if (newCount <= lastCount) {
newCount = lastCount + 1;
}
});
return 'Untitled Note ' + newCount;
};
vm.cloneNoteName = function() {
var copyCount = 1;
var newCloneName = '';
var lastIndex = vm.sourceNoteName.lastIndexOf(' ');
var endsWithNumber = !!vm.sourceNoteName.match('^.+?\\s\\d$');
var noteNamePrefix = endsWithNumber ? vm.sourceNoteName.substr(0, lastIndex) : vm.sourceNoteName;
var regexp = new RegExp('^' + noteNamePrefix + ' .+');
angular.forEach(vm.notes.flatList, function(noteName) {
noteName = noteName.name;
if (noteName.match(regexp)) {
var lastCopyCount = noteName.substr(lastIndex).trim();
newCloneName = noteNamePrefix;
lastCopyCount = parseInt(lastCopyCount);
if (copyCount <= lastCopyCount) {
copyCount = lastCopyCount + 1;
}
}
});
if (!newCloneName) {
newCloneName = vm.sourceNoteName;
}
return newCloneName + ' ' + copyCount;
};
});
return 'Untitled Note ' + newCount;
};
vm.getInterpreterSettings = function() {
vm.websocketMsgSrv.getInterpreterSettings();
};
vm.cloneNoteName = function() {
var copyCount = 1;
var newCloneName = '';
var lastIndex = vm.sourceNoteName.lastIndexOf(' ');
var endsWithNumber = !!vm.sourceNoteName.match('^.+?\\s\\d$');
var noteNamePrefix = endsWithNumber ? vm.sourceNoteName.substr(0, lastIndex) : vm.sourceNoteName;
var regexp = new RegExp('^' + noteNamePrefix + ' .+');
$scope.$on('interpreterSettings', function(event, data) {
$scope.interpreterSettings = data.interpreterSettings;
//initialize default interpreter with Spark interpreter
$scope.note.defaultInterpreter = data.interpreterSettings[0];
angular.forEach(vm.notes.flatList, function(noteName) {
noteName = noteName.name;
if (noteName.match(regexp)) {
var 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];
});
}
})();

View file

@ -11,37 +11,35 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').directive('modalvisible', modalvisible);
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
var previsibleMethod = scope.preVisibleCallback;
var postVisibleMethod = scope.postVisibleCallback;
element.on('show.bs.modal',function(e) {
var relatedTarget = angular.element(e.relatedTarget);
var clone = relatedTarget.data('clone');
var sourceNoteName = relatedTarget.data('source-note-name');
var cloneNote = clone ? true : false;
previsibleMethod()(cloneNote, sourceNoteName);
});
element.on('shown.bs.modal', function(e) {
if (scope.targetinput) {
angular.element(e.target).find('input#' + scope.targetinput).select();
}
postVisibleMethod();
});
}
};
}
function modalvisible() {
return {
restrict: 'A',
scope: {
preVisibleCallback: '&previsiblecallback',
postVisibleCallback: '&postvisiblecallback',
targetinput: '@targetinput'
},
link: function(scope, element, attrs) {
// Add some listeners
var previsibleMethod = scope.preVisibleCallback;
var postVisibleMethod = scope.postVisibleCallback;
element.on('show.bs.modal',function(e) {
var relatedTarget = angular.element(e.relatedTarget);
var clone = relatedTarget.data('clone');
var sourceNoteName = relatedTarget.data('source-note-name');
var cloneNote = clone ? true : false;
previsibleMethod()(cloneNote, sourceNoteName);
});
element.on('shown.bs.modal', function(e) {
if (scope.targetinput) {
angular.element(e.target).find('input#' + scope.targetinput).select();
}
postVisibleMethod();
});
}
};
}
})();

View file

@ -1,140 +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.
*/
(function() {
* 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('NoteImportCtrl', NoteImportCtrl);
angular.module('zeppelinWebApp').controller('NoteImportCtrl', NoteImportCtrl);
NoteImportCtrl.$inject = ['$scope', '$timeout', 'websocketMsgSrv'];
NoteImportCtrl.$inject = ['$scope', '$timeout', 'websocketMsgSrv'];
function NoteImportCtrl($scope, $timeout, websocketMsgSrv) {
var vm = this;
function NoteImportCtrl($scope, $timeout, websocketMsgSrv) {
var vm = this;
$scope.note = {};
$scope.note.step1 = true;
$scope.note.step2 = false;
$scope.maxLimit = '';
var 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;
$scope.maxLimit = '';
var limit = 0;
angular.element('#noteImportFile').val('');
};
websocketMsgSrv.listConfigurations();
$scope.$on('configurationsInfo', function(scope, event) {
limit = event.configurations['zeppelin.websocket.max.text.message.size'];
$scope.maxLimit = Math.round(limit / 1048576);
});
$scope.uploadFile = function() {
angular.element('#noteImportFile').click();
};
vm.resetFlags = function() {
$scope.note = {};
$scope.importFile = function(element) {
$scope.note.errorText = '';
$scope.note.importFile = element.files[0];
var file = $scope.note.importFile;
var 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;
$scope.note.step2 = false;
angular.element('#noteImportFile').val('');
};
}, 400);
$scope.note.step2 = false;
};
$scope.uploadFile = function() {
angular.element('#noteImportFile').click();
};
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();
}
};
$scope.importFile = function(element) {
$scope.note.errorText = '';
$scope.note.importFile = element.files[0];
var file = $scope.note.importFile;
var reader = new FileReader();
if (file.size > limit) {
$scope.note.errorText = 'File size limit Exceeded!';
vm.processImportJson = function(result) {
if (typeof result !== 'object') {
try {
result = JSON.parse(result);
} catch (e) {
$scope.note.errorText = 'JSON parse exception';
$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);
});
}
if (result.paragraphs && result.paragraphs.length > 0) {
if (!$scope.note.noteImportName) {
$scope.note.noteImportName = result.name;
} else {
$scope.note.errorText = 'Enter URL';
$scope.$apply();
result.name = $scope.note.noteImportName;
}
};
websocketMsgSrv.importNote(result);
//angular.element('#noteImportModal').modal('hide');
} else {
$scope.note.errorText = 'Invalid JSON';
}
$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;
}
/*
** $scope.$on functions below
*/
}
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('setNoteMenu', function(event, notes) {
vm.resetFlags();
angular.element('#noteImportModal').modal('hide');
});
}
/*
** $scope.$on functions below
*/
$scope.$on('setNoteMenu', function(event, notes) {
vm.resetFlags();
angular.element('#noteImportModal').modal('hide');
});
}
})();

View file

@ -11,31 +11,29 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').service('noteVarShareService', noteVarShareService);
angular.module('zeppelinWebApp').service('noteVarShareService', noteVarShareService);
noteVarShareService.$inject = [];
noteVarShareService.$inject = [];
function noteVarShareService() {
var store = {};
function noteVarShareService() {
var store = {};
this.clear = function() {
store = {};
};
this.put = function(key, value) {
store[key] = value;
};
this.get = function(key) {
return store[key];
};
this.del = function(key) {
var v = store[key];
delete store[key];
return v;
};
this.clear = function() {
store = {};
};
})();
this.put = function(key, value) {
store[key] = value;
};
this.get = function(key) {
return store[key];
};
this.del = function(key) {
var v = store[key];
delete store[key];
return v;
};
}

View file

@ -11,17 +11,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').directive('popoverHtmlUnsafePopup', popoverHtmlUnsafePopup);
angular.module('zeppelinWebApp').directive('popoverHtmlUnsafePopup', popoverHtmlUnsafePopup);
function popoverHtmlUnsafePopup() {
return {
restrict: 'EA',
replace: true,
scope: {title: '@', content: '@', placement: '@', animation: '&', isOpen: '&'},
templateUrl: 'components/popover-html-unsafe/popover-html-unsafe-popup.html'
};
}
function popoverHtmlUnsafePopup() {
return {
restrict: 'EA',
replace: true,
scope: {title: '@', content: '@', placement: '@', animation: '&', isOpen: '&'},
templateUrl: 'components/popover-html-unsafe/popover-html-unsafe-popup.html'
};
}
})();

View file

@ -11,14 +11,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').directive('popoverHtmlUnsafe', popoverHtmlUnsafe);
angular.module('zeppelinWebApp').directive('popoverHtmlUnsafe', popoverHtmlUnsafe);
popoverHtmlUnsafe.$inject = ['$tooltip'];
popoverHtmlUnsafe.$inject = ['$tooltip'];
function popoverHtmlUnsafe($tooltip) {
return $tooltip('popoverHtmlUnsafe', 'popover', 'click');
}
function popoverHtmlUnsafe($tooltip) {
return $tooltip('popoverHtmlUnsafe', 'popover', 'click');
}
})();

View file

@ -11,39 +11,37 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').controller('RenameCtrl', RenameCtrl);
angular.module('zeppelinWebApp').controller('RenameCtrl', RenameCtrl);
RenameCtrl.$inject = ['$scope'];
RenameCtrl.$inject = ['$scope'];
function RenameCtrl($scope) {
var self = this;
function RenameCtrl($scope) {
var self = this;
$scope.params = {newName: ''};
$scope.isValid = true;
$scope.params = {newName: ''};
$scope.isValid = true;
$scope.rename = function() {
angular.element('#renameModal').modal('hide');
self.callback($scope.params.newName);
$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);
};
$scope.$on('openRenameModal', function(event, options) {
self.validator = options.validator || defaultValidator;
self.callback = options.callback || function() {};
angular.element('#renameModal').modal('show');
});
$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();
}
function defaultValidator(str) {
return !!str.trim();
}
}
})();

View file

@ -11,24 +11,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').service('renameSrv', renameSrv);
angular.module('zeppelinWebApp').service('renameSrv', renameSrv);
renameSrv.$inject = ['$rootScope'];
renameSrv.$inject = ['$rootScope'];
function renameSrv($rootScope) {
var self = this;
function renameSrv($rootScope) {
var 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);
};
}
})();
/**
* <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);
};
}

View file

@ -11,62 +11,60 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').directive('resizable', resizable);
angular.module('zeppelinWebApp').directive('resizable', resizable);
function resizable() {
var resizableConfig = {
autoHide: true,
handles: 'se',
helper: 'resizable-helper',
stop: function() {
angular.element(this).css({'width': '100%', 'height': '100%'});
}
};
function resizable() {
var 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) {
var resetResize = function(elem, resize) {
var colStep = window.innerWidth / 12;
elem.off('resizestop');
var conf = angular.copy(resizableConfig);
if (resize.graphType === 'TABLE' || 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) {
var 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);
});
return {
restrict: 'A',
scope: {
callback: '&onResize'
},
link: function postLink(scope, elem, attrs) {
attrs.$observe('resize', function(resize) {
var resetResize = function(elem, resize) {
var colStep = window.innerWidth / 12;
elem.off('resizestop');
var conf = angular.copy(resizableConfig);
if (resize.graphType === 'TABLE' || 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) {
var 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);
});
}
});
}
};
}
})();

View file

@ -11,44 +11,42 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').service('saveAsService', saveAsService);
angular.module('zeppelinWebApp').service('saveAsService', saveAsService);
saveAsService.$inject = ['browserDetectService'];
saveAsService.$inject = ['browserDetectService'];
function saveAsService(browserDetectService) {
this.saveAs = function(content, filename, extension) {
var BOM = '\uFEFF';
if (browserDetectService.detectIE()) {
angular.element('body').append('<iframe id="SaveAsId" style="display: none"></iframe>');
var 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();
var t1 = Date.now();
frameSaveAs.document.execCommand('SaveAs', false, filename + '.' + extension);
var t2 = Date.now();
function saveAsService(browserDetectService) {
this.saveAs = function(content, filename, extension) {
var BOM = '\uFEFF';
if (browserDetectService.detectIE()) {
angular.element('body').append('<iframe id="SaveAsId" style="display: none"></iframe>');
var 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();
var t1 = Date.now();
frameSaveAs.document.execCommand('SaveAs', false, filename + '.' + extension);
var 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>');
var saveAsElement = angular.element('body > a#SaveAsId');
saveAsElement.attr('href', content);
saveAsElement.attr('download', filename + '.' + extension);
saveAsElement.attr('target', '_blank');
saveAsElement[0].click();
saveAsElement.remove();
//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>');
var saveAsElement = angular.element('body > a#SaveAsId');
saveAsElement.attr('href', content);
saveAsElement.attr('download', filename + '.' + extension);
saveAsElement.attr('target', '_blank');
saveAsElement[0].click();
saveAsElement.remove();
}
};
}
})();

View file

@ -11,26 +11,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').service('searchService', searchService);
angular.module('zeppelinWebApp').service('searchService', searchService);
searchService.$inject = ['$resource', 'baseUrlSrv'];
searchService.$inject = ['$resource', 'baseUrlSrv'];
function searchService($resource, baseUrlSrv) {
this.search = function(term) {
this.searchTerm = term.q;
console.log('Searching for: %o', term.q);
if (!term.q) { //TODO(bzz): empty string check
return;
}
var encQuery = window.encodeURIComponent(term.q);
return $resource(baseUrlSrv.getRestApiBase() + '/notebook/search?q=' + encQuery, {}, {
query: {method: 'GET'}
});
};
function searchService($resource, baseUrlSrv) {
this.search = function(term) {
this.searchTerm = term.q;
console.log('Searching for: %o', term.q);
if (!term.q) { //TODO(bzz): empty string check
return;
}
var encQuery = window.encodeURIComponent(term.q);
return $resource(baseUrlSrv.getRestApiBase() + '/notebook/search?q=' + encQuery, {}, {
query: {method: 'GET'}
});
};
this.searchTerm = '';
}
this.searchTerm = '';
}
})();

View file

@ -11,176 +11,174 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').factory('websocketEvents', websocketEvents);
angular.module('zeppelinWebApp').factory('websocketEvents', websocketEvents);
websocketEvents.$inject = ['$rootScope', '$websocket', '$location', 'baseUrlSrv'];
websocketEvents.$inject = ['$rootScope', '$websocket', '$location', 'baseUrlSrv'];
function websocketEvents($rootScope, $websocket, $location, baseUrlSrv) {
var websocketCalls = {};
var pingIntervalId;
function websocketEvents($rootScope, $websocket, $location, baseUrlSrv) {
var websocketCalls = {};
var pingIntervalId;
websocketCalls.ws = $websocket(baseUrlSrv.getWebsocketUrl());
websocketCalls.ws.reconnectIfNotNormalClose = true;
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.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;
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) {
var payload;
if (event.data) {
payload = angular.fromJson(event.data);
}
console.log('Receive << %o, %o', payload.op, payload);
var op = payload.op;
var 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') {
var btn = [];
if ($rootScope.ticket.roles === '[]') {
btn = [{
label: 'Close',
action: function(dialog) {
dialog.close();
}
}];
} else {
data.principal = '';
data.ticket = '';
data.roles = '';
btn = [{
label: 'Login',
action: function(dialog) {
dialog.close();
angular.element('#loginModal').modal({
show: 'true'
});
}
}, {
label: 'Cancel',
action: function(dialog) {
dialog.close();
$location.path('/');
}
}];
}
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);
};
BootstrapDialog.show({
closable: false,
closeByBackdrop: false,
closeByKeyboard: false,
title: 'Insufficient privileges',
message: data.info.toString(),
buttons: btn
});
websocketCalls.ws.onMessage(function(event) {
var payload;
if (event.data) {
payload = angular.fromJson(event.data);
}
console.log('Receive << %o, %o', payload.op, payload);
var op = payload.op;
var 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') {
var 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();
$location.path('/');
}
}];
}
} else if (op === 'PARAGRAPH') {
$rootScope.$broadcast('updateParagraph', 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 === '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);
}
});
BootstrapDialog.show({
closable: false,
closeByBackdrop: false,
closeByKeyboard: false,
title: 'Insufficient privileges',
message: data.info.toString(),
buttons: btn
});
websocketCalls.ws.onError(function(event) {
console.log('error message: ', event);
$rootScope.$broadcast('setConnectedStatus', false);
});
} else if (op === 'PARAGRAPH') {
$rootScope.$broadcast('updateParagraph', 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 === '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);
}
});
websocketCalls.ws.onClose(function(event) {
console.log('close message: ', event);
if (pingIntervalId !== undefined) {
clearInterval(pingIntervalId);
pingIntervalId = undefined;
}
$rootScope.$broadcast('setConnectedStatus', false);
});
websocketCalls.ws.onError(function(event) {
console.log('error message: ', event);
$rootScope.$broadcast('setConnectedStatus', false);
});
return websocketCalls;
}
websocketCalls.ws.onClose(function(event) {
console.log('close message: ', event);
if (pingIntervalId !== undefined) {
clearInterval(pingIntervalId);
pingIntervalId = undefined;
}
$rootScope.$broadcast('setConnectedStatus', false);
});
return websocketCalls;
}
})();

View file

@ -11,298 +11,306 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
angular.module('zeppelinWebApp').service('websocketMsgSrv', websocketMsgSrv);
angular.module('zeppelinWebApp').service('websocketMsgSrv', websocketMsgSrv);
websocketMsgSrv.$inject = ['$rootScope', 'websocketEvents'];
websocketMsgSrv.$inject = ['$rootScope', 'websocketEvents'];
function websocketMsgSrv($rootScope, websocketEvents) {
return {
function websocketMsgSrv($rootScope, websocketEvents) {
return {
getHomeNote: function() {
websocketEvents.sendNewEvent({op: 'GET_HOME_NOTE'});
},
getHomeNote: function() {
websocketEvents.sendNewEvent({op: 'GET_HOME_NOTE'});
},
createNotebook: function(noteName, defaultInterpreterId) {
websocketEvents.sendNewEvent({
op: 'NEW_NOTE',
data: {
name: noteName,
defaultInterpreterId: defaultInterpreterId
}
});
},
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}});
},
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}});
},
moveFolderToTrash: function(folderId) {
websocketEvents.sendNewEvent({op: 'MOVE_FOLDER_TO_TRASH', data: {id: folderId}});
},
restoreNote: function(noteId) {
websocketEvents.sendNewEvent({op: 'RESTORE_NOTE', data: {id: noteId}});
},
restoreNote: function(noteId) {
websocketEvents.sendNewEvent({op: 'RESTORE_NOTE', data: {id: noteId}});
},
restoreFolder: function(folderId) {
websocketEvents.sendNewEvent({op: 'RESTORE_FOLDER', data: {id: folderId}});
},
restoreFolder: function(folderId) {
websocketEvents.sendNewEvent({op: 'RESTORE_FOLDER', data: {id: folderId}});
},
restoreAll: function() {
websocketEvents.sendNewEvent({op: 'RESTORE_ALL'});
},
restoreAll: function() {
websocketEvents.sendNewEvent({op: 'RESTORE_ALL'});
},
deleteNote: function(noteId) {
websocketEvents.sendNewEvent({op: 'DEL_NOTE', data: {id: noteId}});
},
deleteNote: function(noteId) {
websocketEvents.sendNewEvent({op: 'DEL_NOTE', data: {id: noteId}});
},
removeFolder: function(folderId) {
websocketEvents.sendNewEvent({op: 'REMOVE_FOLDER', data: {id: folderId}});
},
removeFolder: function(folderId) {
websocketEvents.sendNewEvent({op: 'REMOVE_FOLDER', data: {id: folderId}});
},
emptyTrash: function() {
websocketEvents.sendNewEvent({op: 'EMPTY_TRASH'});
},
emptyTrash: function() {
websocketEvents.sendNewEvent({op: 'EMPTY_TRASH'});
},
cloneNote: function(noteIdToClone, newNoteName) {
websocketEvents.sendNewEvent({op: 'CLONE_NOTE', data: {id: noteIdToClone, name: newNoteName}});
},
cloneNote: function(noteIdToClone, newNoteName) {
websocketEvents.sendNewEvent({op: 'CLONE_NOTE', data: {id: noteIdToClone, name: newNoteName}});
},
getNoteList: function() {
websocketEvents.sendNewEvent({op: 'LIST_NOTES'});
},
getNoteList: function() {
websocketEvents.sendNewEvent({op: 'LIST_NOTES'});
},
reloadAllNotesFromRepo: function() {
websocketEvents.sendNewEvent({op: 'RELOAD_NOTES_FROM_REPO'});
},
reloadAllNotesFromRepo: function() {
websocketEvents.sendNewEvent({op: 'RELOAD_NOTES_FROM_REPO'});
},
getNote: function(noteId) {
websocketEvents.sendNewEvent({op: 'GET_NOTE', data: {id: noteId}});
},
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}});
},
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}});
},
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}});
},
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}});
},
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}});
},
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}});
},
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
}
});
},
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
}
});
},
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
}
});
},
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
}
});
},
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}});
},
cancelParagraphRun: function(paragraphId) {
websocketEvents.sendNewEvent({op: 'CANCEL_PARAGRAPH', data: {id: paragraphId}});
},
runParagraph: function(paragraphId, paragraphTitle, paragraphData, paragraphConfig, paragraphParams) {
websocketEvents.sendNewEvent({
op: 'RUN_PARAGRAPH',
data: {
id: paragraphId,
title: paragraphTitle,
paragraph: paragraphData,
config: paragraphConfig,
params: paragraphParams
}
});
},
runParagraph: function(paragraphId, paragraphTitle, paragraphData, paragraphConfig, paragraphParams) {
websocketEvents.sendNewEvent({
op: 'RUN_PARAGRAPH',
data: {
id: paragraphId,
title: paragraphTitle,
paragraph: paragraphData,
config: paragraphConfig,
params: paragraphParams
}
});
},
removeParagraph: function(paragraphId) {
websocketEvents.sendNewEvent({op: 'PARAGRAPH_REMOVE', data: {id: paragraphId}});
},
runAllParagraphs: function(noteId, paragraphs) {
websocketEvents.sendNewEvent({
op: 'RUN_ALL_PARAGRAPHS',
data: {
noteId: noteId,
paragraphs: JSON.stringify(paragraphs)
}
});
},
clearParagraphOutput: function(paragraphId) {
websocketEvents.sendNewEvent({op: 'PARAGRAPH_CLEAR_OUTPUT', data: {id: paragraphId}});
},
removeParagraph: function(paragraphId) {
websocketEvents.sendNewEvent({op: 'PARAGRAPH_REMOVE', data: {id: paragraphId}});
},
clearAllParagraphOutput: function(noteId) {
websocketEvents.sendNewEvent({op: 'PARAGRAPH_CLEAR_ALL_OUTPUT', data: {id: noteId}});
},
clearParagraphOutput: function(paragraphId) {
websocketEvents.sendNewEvent({op: 'PARAGRAPH_CLEAR_OUTPUT', data: {id: paragraphId}});
},
completion: function(paragraphId, buf, cursor) {
websocketEvents.sendNewEvent({
op: 'COMPLETION',
data: {
id: paragraphId,
buf: buf,
cursor: cursor
}
});
},
clearAllParagraphOutput: function(noteId) {
websocketEvents.sendNewEvent({op: 'PARAGRAPH_CLEAR_ALL_OUTPUT', data: {id: noteId}});
},
commitParagraph: function(paragraphId, paragraphTitle, paragraphData, paragraphConfig, paragraphParams) {
websocketEvents.sendNewEvent({
op: 'COMMIT_PARAGRAPH',
data: {
id: paragraphId,
title: paragraphTitle,
paragraph: paragraphData,
config: paragraphConfig,
params: paragraphParams
}
});
},
completion: function(paragraphId, buf, cursor) {
websocketEvents.sendNewEvent({
op: 'COMPLETION',
data: {
id: paragraphId,
buf: buf,
cursor: cursor
}
});
},
importNote: function(note) {
websocketEvents.sendNewEvent({
op: 'IMPORT_NOTE',
data: {
note: note
}
});
},
commitParagraph: function(paragraphId, paragraphTitle, paragraphData, paragraphConfig, paragraphParams) {
websocketEvents.sendNewEvent({
op: 'COMMIT_PARAGRAPH',
data: {
id: paragraphId,
title: paragraphTitle,
paragraph: paragraphData,
config: paragraphConfig,
params: paragraphParams
}
});
},
checkpointNote: function(noteId, commitMessage) {
websocketEvents.sendNewEvent({
op: 'CHECKPOINT_NOTE',
data: {
noteId: noteId,
commitMessage: commitMessage
}
});
},
importNote: function(note) {
websocketEvents.sendNewEvent({
op: 'IMPORT_NOTE',
data: {
note: note
}
});
},
setNoteRevision: function(noteId, revisionId) {
websocketEvents.sendNewEvent({
op: 'SET_NOTE_REVISION',
data: {
noteId: noteId,
revisionId: revisionId
}
});
},
checkpointNote: function(noteId, commitMessage) {
websocketEvents.sendNewEvent({
op: 'CHECKPOINT_NOTE',
data: {
noteId: noteId,
commitMessage: commitMessage
}
});
},
listRevisionHistory: function(noteId) {
websocketEvents.sendNewEvent({
op: 'LIST_REVISION_HISTORY',
data: {
noteId: noteId
}
});
},
setNoteRevision: function(noteId, revisionId) {
websocketEvents.sendNewEvent({
op: 'SET_NOTE_REVISION',
data: {
noteId: noteId,
revisionId: revisionId
}
});
},
getNoteByRevision: function(noteId, revisionId) {
websocketEvents.sendNewEvent({
op: 'NOTE_REVISION',
data: {
noteId: noteId,
revisionId: revisionId
}
});
},
listRevisionHistory: function(noteId) {
websocketEvents.sendNewEvent({
op: 'LIST_REVISION_HISTORY',
data: {
noteId: noteId
}
});
},
getEditorSetting: function(paragraphId, replName) {
websocketEvents.sendNewEvent({
op: 'EDITOR_SETTING',
data: {
paragraphId: paragraphId,
magic: replName
}
});
},
getNoteByRevision: function(noteId, revisionId) {
websocketEvents.sendNewEvent({
op: 'NOTE_REVISION',
data: {
noteId: noteId,
revisionId: revisionId
}
});
},
isConnected: function() {
return websocketEvents.isConnected();
},
getEditorSetting: function(paragraphId, replName) {
websocketEvents.sendNewEvent({
op: 'EDITOR_SETTING',
data: {
paragraphId: paragraphId,
magic: replName
}
});
},
getNoteJobsList: function() {
websocketEvents.sendNewEvent({op: 'LIST_NOTE_JOBS'});
},
isConnected: function() {
return websocketEvents.isConnected();
},
getUpdateNoteJobsList: function(lastUpdateServerUnixTime) {
websocketEvents.sendNewEvent(
{op: 'LIST_UPDATE_NOTE_JOBS', data: {lastUpdateUnixTime: lastUpdateServerUnixTime * 1}}
);
},
getNoteJobsList: function() {
websocketEvents.sendNewEvent({op: 'LIST_NOTE_JOBS'});
},
unsubscribeJobManager: function() {
websocketEvents.sendNewEvent({op: 'UNSUBSCRIBE_UPDATE_NOTE_JOBS'});
},
getUpdateNoteJobsList: function(lastUpdateServerUnixTime) {
websocketEvents.sendNewEvent(
{op: 'LIST_UPDATE_NOTE_JOBS', data: {lastUpdateUnixTime: lastUpdateServerUnixTime * 1}}
);
},
getInterpreterBindings: function(noteId) {
websocketEvents.sendNewEvent({op: 'GET_INTERPRETER_BINDINGS', data: {noteId: noteId}});
},
unsubscribeJobManager: function() {
websocketEvents.sendNewEvent({op: 'UNSUBSCRIBE_UPDATE_NOTE_JOBS'});
},
saveInterpreterBindings: function(noteId, selectedSettingIds) {
websocketEvents.sendNewEvent({op: 'SAVE_INTERPRETER_BINDINGS',
data: {noteId: noteId, selectedSettingIds: selectedSettingIds}});
},
getInterpreterBindings: function(noteId) {
websocketEvents.sendNewEvent({op: 'GET_INTERPRETER_BINDINGS', data: {noteId: noteId}});
},
listConfigurations: function() {
websocketEvents.sendNewEvent({op: 'LIST_CONFIGURATIONS'});
},
saveInterpreterBindings: function(noteId, selectedSettingIds) {
websocketEvents.sendNewEvent({op: 'SAVE_INTERPRETER_BINDINGS',
data: {noteId: noteId, selectedSettingIds: selectedSettingIds}});
},
getInterpreterSettings: function() {
websocketEvents.sendNewEvent({op: 'GET_INTERPRETER_SETTINGS'});
}
listConfigurations: function() {
websocketEvents.sendNewEvent({op: 'LIST_CONFIGURATIONS'});
},
};
}
getInterpreterSettings: function() {
websocketEvents.sendNewEvent({op: 'GET_INTERPRETER_SETTINGS'});
}
};
}
})();

View file

@ -57,6 +57,7 @@ limitations under the License.
<link rel="stylesheet" href="app/search/search.css" />
<link rel="stylesheet" href="app/notebook/notebook.css" />
<link rel="stylesheet" href="app/notebook/paragraph/paragraph.css" />
<link rel="stylesheet" href="app/notebook/paragraph/result/result.css" />
<link rel="stylesheet" href="app/jobmanager/jobmanager.css" />
<link rel="stylesheet" href="app/jobmanager/jobs/job.css" />
<link rel="stylesheet" href="app/interpreter/interpreter.css" />

View file

@ -35,7 +35,7 @@ describe('Controller: NotebookCtrl', function() {
scope.note = noteMock;
});
var functions = ['getCronOptionNameFromValue', 'removeNote', 'runNote', 'saveNote', 'toggleAllEditor',
var functions = ['getCronOptionNameFromValue', 'removeNote', 'runAllParagraphs', 'saveNote', 'toggleAllEditor',
'showAllEditor', 'hideAllEditor', 'toggleAllTable', 'hideAllTable', 'showAllTable', 'isNoteRunning',
'killSaveTimer', 'startSaveTimer', 'setLookAndFeel', 'setCronScheduler', 'setConfig', 'updateNoteName',
'openSetting', 'closeSetting', 'saveSetting', 'toggleSetting'];

View file

@ -98,7 +98,7 @@ module.exports = function makeWebpackConfig () {
// Output path from the view of the page
// Uses webpack-dev-server in development
publicPath: isProd ? '/' : 'http://localhost:9000/',
publicPath: isProd ? '' : 'http://localhost:9000/',
// Filename for entry points
// Only adds hash in build mode

View file

@ -369,6 +369,10 @@ public class ZeppelinConfiguration extends XMLConfiguration {
return getString(ConfVars.ZEPPELIN_NOTEBOOK_S3_KMS_KEY_ID);
}
public String getS3KMSKeyRegion() {
return getString(ConfVars.ZEPPELIN_NOTEBOOK_S3_KMS_KEY_REGION);
}
public String getS3EncryptionMaterialsProviderClass() {
return getString(ConfVars.ZEPPELIN_NOTEBOOK_S3_EMP);
}
@ -579,6 +583,7 @@ public class ZeppelinConfiguration extends XMLConfiguration {
ZEPPELIN_NOTEBOOK_S3_USER("zeppelin.notebook.s3.user", "user"),
ZEPPELIN_NOTEBOOK_S3_EMP("zeppelin.notebook.s3.encryptionMaterialsProvider", null),
ZEPPELIN_NOTEBOOK_S3_KMS_KEY_ID("zeppelin.notebook.s3.kmsKeyID", null),
ZEPPELIN_NOTEBOOK_S3_KMS_KEY_REGION("zeppelin.notebook.s3.kmsKeyRegion", null),
ZEPPELIN_NOTEBOOK_AZURE_CONNECTION_STRING("zeppelin.notebook.azure.connectionString", null),
ZEPPELIN_NOTEBOOK_AZURE_SHARE("zeppelin.notebook.azure.share", "zeppelin"),
ZEPPELIN_NOTEBOOK_AZURE_USER("zeppelin.notebook.azure.user", "user"),

View file

@ -74,8 +74,6 @@ import org.apache.zeppelin.display.AngularObjectRegistry;
import org.apache.zeppelin.display.AngularObjectRegistryListener;
import org.apache.zeppelin.helium.ApplicationEventListener;
import org.apache.zeppelin.interpreter.Interpreter.RegisteredInterpreter;
import org.apache.zeppelin.interpreter.dev.DevInterpreter;
import org.apache.zeppelin.interpreter.dev.ZeppelinDevServer;
import org.apache.zeppelin.interpreter.remote.RemoteAngularObjectRegistry;
import org.apache.zeppelin.interpreter.remote.RemoteInterpreter;
import org.apache.zeppelin.interpreter.remote.RemoteInterpreterProcessListener;
@ -1372,11 +1370,6 @@ public class InterpreterFactory implements InterpreterGroupFactory {
}
}
// dev interpreter
if (DevInterpreter.isInterpreterName(replName)) {
return getDevInterpreter();
}
return null;
}
@ -1453,24 +1446,4 @@ public class InterpreterFactory implements InterpreterGroupFactory {
}
return editor;
}
private Interpreter getDevInterpreter() {
if (devInterpreter == null) {
InterpreterOption option = new InterpreterOption();
option.setRemote(true);
InterpreterGroup interpreterGroup = createInterpreterGroup("dev", option);
devInterpreter = connectToRemoteRepl("dev", DevInterpreter.class.getName(), "localhost",
ZeppelinDevServer.DEFAULT_TEST_INTERPRETER_PORT, new Properties(), "dev", "anonymous",
false);
LinkedList<Interpreter> intpList = new LinkedList<>();
intpList.add(devInterpreter);
interpreterGroup.put("dev", intpList);
devInterpreter.setInterpreterGroup(interpreterGroup);
}
return devInterpreter;
}
}

View file

@ -31,6 +31,7 @@ import java.util.Map;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.zeppelin.conf.ZeppelinConfiguration;
import org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars;
import org.apache.zeppelin.notebook.Note;
@ -48,12 +49,15 @@ import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.AmazonS3EncryptionClient;
import com.amazonaws.services.s3.model.CryptoConfiguration;
import com.amazonaws.services.s3.model.EncryptionMaterialsProvider;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.KMSEncryptionMaterialsProvider;
import com.amazonaws.services.s3.model.ListObjectsRequest;
import com.amazonaws.services.s3.model.ObjectListing;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectSummary;
import com.google.gson.Gson;
@ -91,13 +95,24 @@ public class S3NotebookRepo implements NotebookRepo {
// always use the default provider chain
AWSCredentialsProvider credentialsProvider = new DefaultAWSCredentialsProviderChain();
CryptoConfiguration cryptoConf = null;
String keyRegion = conf.getS3KMSKeyRegion();
if (StringUtils.isNotBlank(keyRegion)) {
cryptoConf = new CryptoConfiguration();
cryptoConf.setAwsKmsRegion(Region.getRegion(Regions.fromName(keyRegion)));
}
// see if we should be encrypting data in S3
String kmsKeyID = conf.getS3KMSKeyID();
if (kmsKeyID != null) {
// use the AWS KMS to encrypt data
KMSEncryptionMaterialsProvider emp = new KMSEncryptionMaterialsProvider(kmsKeyID);
this.s3client = new AmazonS3EncryptionClient(credentialsProvider, emp);
if (cryptoConf != null) {
this.s3client = new AmazonS3EncryptionClient(credentialsProvider, emp, cryptoConf);
} else {
this.s3client = new AmazonS3EncryptionClient(credentialsProvider, emp);
}
}
else if (conf.getS3EncryptionMaterialsProviderClass() != null) {
// use a custom encryption materials provider class

View file

@ -171,7 +171,8 @@ public class Message {
PARAGRAPH_ADDED, // [s-c] paragraph is added
PARAGRAPH_REMOVED, // [s-c] paragraph deleted
PARAGRAPH_MOVED, // [s-c] paragraph moved
NOTE_UPDATED // [s-c] paragraph updated(name, config)
NOTE_UPDATED, // [s-c] paragraph updated(name, config)
RUN_ALL_PARAGRAPHS // [c-s] run all paragraphs
}
public static final Message EMPTY = new Message(null);