├── .gitignore ├── CHANGE.md ├── LICENSE ├── README.md ├── TODO.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ ├── main.js │ └── paste.js │ ├── java │ └── github │ │ └── hotstu │ │ └── lu │ │ ├── App.java │ │ └── MainActivity.java │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ └── ic_launcher_background.xml │ ├── layout │ └── activity_main.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── base ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── aidl │ └── github │ │ └── hotstu │ │ └── lu │ │ └── base │ │ ├── IPlayCallback.aidl │ │ └── IPlayService.aidl │ └── java │ └── github │ └── hotstu │ └── lu │ └── base │ └── IModule.java ├── build.gradle ├── demo.gif ├── engine ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ └── init.js │ ├── java │ └── github │ │ └── hotstu │ │ └── lu │ │ └── engine │ │ ├── AssetAndUrlModuleSourceProvider.java │ │ ├── ConsoleImpl.java │ │ ├── DefaultLuContext.java │ │ ├── LuContext.java │ │ ├── LuEngine.java │ │ ├── LuEngineThread.java │ │ ├── ModuleApp.java │ │ └── ScriptTask.java │ └── resources │ └── META-INF │ └── services │ └── github.hotstu.lu.base.IModule ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── play ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ ├── form.html │ └── web │ │ ├── addon │ │ ├── comment │ │ │ ├── comment.js │ │ │ └── continuecomment.js │ │ ├── dialog │ │ │ ├── dialog.css │ │ │ └── dialog.js │ │ ├── display │ │ │ ├── autorefresh.js │ │ │ ├── fullscreen.css │ │ │ ├── fullscreen.js │ │ │ ├── panel.js │ │ │ ├── placeholder.js │ │ │ └── rulers.js │ │ ├── edit │ │ │ ├── closebrackets.js │ │ │ ├── closetag.js │ │ │ ├── continuelist.js │ │ │ ├── matchbrackets.js │ │ │ ├── matchtags.js │ │ │ └── trailingspace.js │ │ ├── fold │ │ │ ├── brace-fold.js │ │ │ ├── comment-fold.js │ │ │ ├── foldcode.js │ │ │ ├── foldgutter.css │ │ │ ├── foldgutter.js │ │ │ ├── indent-fold.js │ │ │ ├── markdown-fold.js │ │ │ └── xml-fold.js │ │ ├── hint │ │ │ ├── anyword-hint.js │ │ │ ├── css-hint.js │ │ │ ├── html-hint.js │ │ │ ├── javascript-hint.js │ │ │ ├── show-hint.css │ │ │ ├── show-hint.js │ │ │ ├── sql-hint.js │ │ │ └── xml-hint.js │ │ ├── lint │ │ │ ├── coffeescript-lint.js │ │ │ ├── css-lint.js │ │ │ ├── html-lint.js │ │ │ ├── javascript-lint.js │ │ │ ├── json-lint.js │ │ │ ├── lint.css │ │ │ ├── lint.js │ │ │ └── yaml-lint.js │ │ ├── merge │ │ │ ├── merge.css │ │ │ └── merge.js │ │ ├── mode │ │ │ ├── loadmode.js │ │ │ ├── multiplex.js │ │ │ ├── multiplex_test.js │ │ │ ├── overlay.js │ │ │ └── simple.js │ │ ├── runmode │ │ │ ├── colorize.js │ │ │ ├── runmode-standalone.js │ │ │ ├── runmode.js │ │ │ └── runmode.node.js │ │ ├── scroll │ │ │ ├── annotatescrollbar.js │ │ │ ├── scrollpastend.js │ │ │ ├── simplescrollbars.css │ │ │ └── simplescrollbars.js │ │ ├── search │ │ │ ├── jump-to-line.js │ │ │ ├── match-highlighter.js │ │ │ ├── matchesonscrollbar.css │ │ │ ├── matchesonscrollbar.js │ │ │ ├── search.js │ │ │ └── searchcursor.js │ │ ├── selection │ │ │ ├── active-line.js │ │ │ ├── mark-selection.js │ │ │ └── selection-pointer.js │ │ ├── tern │ │ │ ├── tern.css │ │ │ ├── tern.js │ │ │ └── worker.js │ │ └── wrap │ │ │ └── hardwrap.js │ │ ├── lib │ │ ├── codemirror.css │ │ └── codemirror.js │ │ ├── mode │ │ ├── index.html │ │ ├── javascript │ │ │ ├── index.html │ │ │ ├── javascript.js │ │ │ ├── json-ld.html │ │ │ ├── test.js │ │ │ └── typescript.html │ │ └── meta.js │ │ └── theme │ │ ├── darcula.css │ │ └── monokai.css │ ├── java │ └── github │ │ └── hotstu │ │ └── lu │ │ └── play │ │ ├── Function.java │ │ ├── HttpDaemonService.java │ │ ├── ModuleApp.java │ │ ├── ServerApp.java │ │ └── StaticHandler.java │ ├── res │ └── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ └── resources │ └── META-INF │ └── services │ └── github.hotstu.lu.base.IModule ├── render ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ └── modules │ │ ├── ShadowModule.js │ │ ├── h.js │ │ ├── phatomDomAPI.js │ │ ├── snabbdom-attributes.js │ │ ├── snabbdom-class.js │ │ ├── snabbdom-dataset.js │ │ ├── snabbdom-eventlisteners.js │ │ ├── snabbdom-patch.js │ │ ├── snabbdom-props.js │ │ ├── snabbdom-style.js │ │ ├── snabbdom.js │ │ └── tovnode.js │ ├── java │ └── github │ │ └── hotstu │ │ └── lu │ │ └── render │ │ ├── NativeComponent.java │ │ ├── NativeRenderAPI.java │ │ ├── RenderContext.java │ │ ├── component │ │ ├── Comment.java │ │ ├── Frame.java │ │ └── Text.java │ │ ├── event │ │ ├── Event.java │ │ └── LuClickHandler.java │ │ ├── module │ │ └── ClassManager.java │ │ ├── style │ │ └── SimpleStyleEngine.java │ │ └── widget │ │ └── LuLinearLayout.java │ └── res │ └── values │ └── ids.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | .cxx 10 | -------------------------------------------------------------------------------- /CHANGE.md: -------------------------------------------------------------------------------- 1 | ### 1.0.0 2 | 3 | * init -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Lu-Another 2 | ====================== 3 | 4 | [Lu-Another]是一款我用业余时间开发的的 5 | 6 | 基于Virtual DOM的原生UI渲染框架 7 | 8 | 类似[Weex]、[ReactNative], 9 | 10 | 但不是对他们的复制,而是独立开发从底层写起 11 | 12 | 设计开发理念是实现一个极简的, 界面渲染各种业务逻辑全部在JS中, 实现了App的动态化。 13 | 14 | 15 | 虽然无法和大公司的成熟框架相比,但他内部的原理是相通的 16 | 17 | 研究学习探索的过程也是不断自我成长的过程 18 | 19 | 在这个这个项目开发过程中不仅实现了virtualDom的渲染引擎 20 | 21 | 还实现了基于web的原生界面编辑器(可以在浏览器中编辑界面在手机中实时看到渲染效果) 22 | 23 | 封装了Rhino Js引擎 24 | 25 | 掌握了Android中动态加载动态Class的技术 26 | 27 | 基于AIDL的模块化技术 28 | 29 | 30 | # demo使用方式 31 | 32 | 手机、PC连接在同一wifi下, 启动App, PC访问`http://手机ip:8080/` 33 | 34 | (如果手机为模拟器, 需要先执行`adb -e forward tcp:8080 tcp:8080`, 然后访问`http://127.0.0.1:8080/`) 35 | 36 | 37 | # 运行效果 38 | 39 | 40 | 41 | 42 | 43 | ## more about me 44 | 45 | |简书| 掘金|JCenter | dockerHub| 46 | | ------------- |------------- |------------- |------------- | 47 | | [简书](https://www.jianshu.com/u/ca2207af2001) | [掘金](https://juejin.im/user/5bee320651882516be2ebbfe) |[JCenter ](https://bintray.com/hglf/maven) | [dockerHub](https://hub.docker.com/u/hglf)| 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TODO: 5 | 6 | 7 | [x] 线程优化(工作线程、渲染线程、ui线程) 8 | 9 | [x] 支持Class (需要同时支持setAttribute 和classList.add, 可能需要重写module) 10 | 11 | [] 支持property(使用注解预编译) 12 | 13 | [] 多媒体支持(图片视频) 14 | 15 | [] 列表渲染 16 | 17 | [] 表单, 双向绑定 18 | 19 | [] 支持Style(参考jss, 支持css的子集) 20 | 21 | [] 支持v8引擎 22 | 23 | [] 支持自定义view 24 | 25 | [] 使用Litho 作为组件基础库 26 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 29 5 | buildToolsVersion "30.0.2" 6 | compileOptions { 7 | sourceCompatibility 1.8 8 | targetCompatibility 1.8 9 | } 10 | defaultConfig { 11 | applicationId "github.hotstu.lu" 12 | minSdkVersion 22 13 | targetSdkVersion 29 14 | versionCode 1 15 | versionName "1.0" 16 | 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | } 19 | 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | } 27 | 28 | dependencies { 29 | implementation fileTree(dir: "libs", include: ["*.jar"]) 30 | implementation 'androidx.appcompat:appcompat:1.2.0' 31 | implementation project(path: ':base') 32 | implementation project(path: ':play') 33 | implementation 'androidx.constraintlayout:constraintlayout:2.0.1' 34 | implementation project(path: ':engine') 35 | implementation project(path: ':render') 36 | implementation("com.squareup.okio:okio:2.8.0") 37 | 38 | testImplementation 'junit:junit:4.12' 39 | androidTestImplementation 'androidx.test.ext:junit:1.1.2' 40 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 41 | 42 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/github/hotstu/lu/App.java: -------------------------------------------------------------------------------- 1 | package github.hotstu.lu; 2 | 3 | import android.app.Application; 4 | 5 | import java.util.Iterator; 6 | import java.util.ServiceLoader; 7 | 8 | import github.hotstu.lu.base.IModule; 9 | 10 | /** 11 | * @author hglf [hglf](https://github.com/hotstu) 12 | * @desc 13 | * @since 9/16/20 14 | */ 15 | public class App extends Application { 16 | @Override 17 | public void onCreate() { 18 | super.onCreate(); 19 | ServiceLoader load = ServiceLoader.load(IModule.class); 20 | Iterator iterator = load.iterator(); 21 | while (iterator.hasNext()) { 22 | try { 23 | IModule next = iterator.next(); 24 | next.onCreate(this); 25 | } catch (Exception e) { 26 | e.printStackTrace(); 27 | } 28 | } 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/github/hotstu/lu/MainActivity.java: -------------------------------------------------------------------------------- 1 | package github.hotstu.lu; 2 | 3 | import android.content.ComponentName; 4 | import android.content.Intent; 5 | import android.content.ServiceConnection; 6 | import android.os.Bundle; 7 | import android.os.IBinder; 8 | import android.os.RemoteException; 9 | import android.util.Log; 10 | 11 | import androidx.appcompat.app.AppCompatActivity; 12 | 13 | import java.io.BufferedInputStream; 14 | import java.io.IOException; 15 | import java.io.InputStream; 16 | 17 | import github.hotstu.lu.base.IPlayCallback; 18 | import github.hotstu.lu.base.IPlayService; 19 | import github.hotstu.lu.engine.LuEngine; 20 | import github.hotstu.lu.play.HttpDaemonService; 21 | import github.hotstu.lu.render.RenderContext; 22 | import okio.ByteString; 23 | 24 | public class MainActivity extends AppCompatActivity { 25 | 26 | private ServiceConnection conn; 27 | private EventHandler mEventHandler = new EventHandler(); 28 | private IPlayService engineService; 29 | private RenderContext defaultContext; 30 | 31 | @Override 32 | protected void onCreate(Bundle savedInstanceState) { 33 | super.onCreate(savedInstanceState); 34 | setContentView(R.layout.activity_main); 35 | LuEngine.getInstance().start(); 36 | defaultContext = new RenderContext(this, "root"); 37 | try { 38 | defaultContext.resetContextScope().await(); 39 | } catch (InterruptedException e) { 40 | e.printStackTrace(); 41 | } 42 | defaultContext.wrap(findViewById(R.id.root)); 43 | InputStream open = null; 44 | try { 45 | open = new BufferedInputStream(getAssets().open("main.js")); 46 | ByteString read = ByteString.read(open, open.available()); 47 | defaultContext.exec(read.utf8()); 48 | } catch (IOException e) { 49 | e.printStackTrace(); 50 | } finally { 51 | if (open != null) { 52 | try { 53 | open.close(); 54 | } catch (IOException e) { 55 | e.printStackTrace(); 56 | } 57 | } 58 | } 59 | 60 | bindService(); 61 | } 62 | 63 | public class EventHandler extends IPlayCallback.Stub { 64 | public EventHandler() { 65 | } 66 | 67 | @Override 68 | public Bundle onEvent(int what, String msg) throws RemoteException { 69 | Log.d("MainActivity", Thread.currentThread().getName() + "event=>" + msg); 70 | return defaultContext.exec(msg); 71 | } 72 | } 73 | 74 | 75 | private void bindService() { 76 | Intent i = new Intent(this, HttpDaemonService.class); 77 | conn = new ServiceConnection() { 78 | @Override 79 | public void onServiceConnected(ComponentName name, IBinder service) { 80 | engineService = IPlayService.Stub.asInterface(service); 81 | try { 82 | engineService.setCallback(mEventHandler); 83 | } catch (RemoteException e) { 84 | e.printStackTrace(); 85 | } 86 | } 87 | 88 | @Override 89 | public void onServiceDisconnected(ComponentName name) { 90 | 91 | } 92 | 93 | @Override 94 | public void onBindingDied(ComponentName name) { 95 | unbindService(this); 96 | 97 | bindService(); 98 | } 99 | }; 100 | bindService(i, conn, BIND_AUTO_CREATE); 101 | } 102 | 103 | @Override 104 | protected void onDestroy() { 105 | if (engineService != null) { 106 | try { 107 | engineService.setCallback(null); 108 | } catch (RemoteException e) { 109 | e.printStackTrace(); 110 | } 111 | } 112 | unbindService(conn); 113 | LuEngine.getInstance().shutDown(); 114 | super.onDestroy(); 115 | } 116 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hotstu/lu/f2c33abaf58bd569570c86c0d77d7e408a637770/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hotstu/lu/f2c33abaf58bd569570c86c0d77d7e408a637770/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hotstu/lu/f2c33abaf58bd569570c86c0d77d7e408a637770/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hotstu/lu/f2c33abaf58bd569570c86c0d77d7e408a637770/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hotstu/lu/f2c33abaf58bd569570c86c0d77d7e408a637770/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hotstu/lu/f2c33abaf58bd569570c86c0d77d7e408a637770/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hotstu/lu/f2c33abaf58bd569570c86c0d77d7e408a637770/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hotstu/lu/f2c33abaf58bd569570c86c0d77d7e408a637770/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hotstu/lu/f2c33abaf58bd569570c86c0d77d7e408a637770/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hotstu/lu/f2c33abaf58bd569570c86c0d77d7e408a637770/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #6200EE 4 | #3700B3 5 | #03DAC5 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | lu 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /base/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /base/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 29 5 | buildToolsVersion "30.0.2" 6 | compileOptions { 7 | sourceCompatibility 1.8 8 | targetCompatibility 1.8 9 | } 10 | defaultConfig { 11 | minSdkVersion 22 12 | targetSdkVersion 29 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | consumerProguardFiles "consumer-rules.pro" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | } 26 | 27 | dependencies { 28 | implementation fileTree(dir: "libs", include: ["*.jar"]) 29 | implementation 'androidx.appcompat:appcompat:1.2.0' 30 | 31 | } -------------------------------------------------------------------------------- /base/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hotstu/lu/f2c33abaf58bd569570c86c0d77d7e408a637770/base/consumer-rules.pro -------------------------------------------------------------------------------- /base/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /base/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /base/src/main/aidl/github/hotstu/lu/base/IPlayCallback.aidl: -------------------------------------------------------------------------------- 1 | // ICallback.aidl 2 | package github.hotstu.lu.base; 3 | 4 | // Declare any non-default types here with import statements 5 | 6 | interface IPlayCallback { 7 | /** 8 | * Demonstrates some basic types that you can use as parameters 9 | * and return values in AIDL. 10 | */ 11 | Bundle onEvent(int what, String msg); 12 | } 13 | -------------------------------------------------------------------------------- /base/src/main/aidl/github/hotstu/lu/base/IPlayService.aidl: -------------------------------------------------------------------------------- 1 | // IEngineService.aidl 2 | package github.hotstu.lu.base; 3 | 4 | import github.hotstu.lu.base.IPlayCallback; 5 | 6 | interface IPlayService { 7 | 8 | void setCallback(IPlayCallback callback); 9 | } 10 | -------------------------------------------------------------------------------- /base/src/main/java/github/hotstu/lu/base/IModule.java: -------------------------------------------------------------------------------- 1 | package github.hotstu.lu.base; 2 | 3 | import android.content.Context; 4 | 5 | /** 6 | * @author hglf [hglf](https://github.com/hotstu) 7 | * @desc 8 | * @since 9/16/20 9 | */ 10 | public interface IModule { 11 | void onCreate(Context context); 12 | } 13 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath "com.android.tools.build:gradle:4.0.1" 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | task clean(type: Delete) { 20 | delete rootProject.buildDir 21 | } -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hotstu/lu/f2c33abaf58bd569570c86c0d77d7e408a637770/demo.gif -------------------------------------------------------------------------------- /engine/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /engine/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 29 5 | buildToolsVersion "30.0.2" 6 | compileOptions { 7 | sourceCompatibility 1.8 8 | targetCompatibility 1.8 9 | } 10 | defaultConfig { 11 | minSdkVersion 22 12 | targetSdkVersion 29 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | consumerProguardFiles "consumer-rules.pro" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | } 26 | 27 | dependencies { 28 | implementation fileTree(dir: "libs", include: ["*.jar"]) 29 | implementation 'androidx.appcompat:appcompat:1.2.0' 30 | implementation 'com.faendir.rhino:rhino-android:1.5.2' 31 | implementation group: 'org.mozilla', name: 'rhino', version: '1.7.12' 32 | implementation project(path: ':base') 33 | 34 | } -------------------------------------------------------------------------------- /engine/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hotstu/lu/f2c33abaf58bd569570c86c0d77d7e408a637770/engine/consumer-rules.pro -------------------------------------------------------------------------------- /engine/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /engine/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | / 5 | -------------------------------------------------------------------------------- /engine/src/main/assets/init.js: -------------------------------------------------------------------------------- 1 | var global = this; 2 | 3 | 4 | (function () { 5 | //重定向importClass使得其支持字符串参数 6 | global.importClass = 7 | (function () { 8 | var __importClass__ = importClass; 9 | return function (pack) { 10 | if (typeof (pack) == "string") { 11 | __importClass__(Packages[pack]); 12 | } else { 13 | __importClass__(pack); 14 | } 15 | } 16 | })(); 17 | })(); 18 | 19 | 20 | -------------------------------------------------------------------------------- /engine/src/main/java/github/hotstu/lu/engine/AssetAndUrlModuleSourceProvider.java: -------------------------------------------------------------------------------- 1 | package github.hotstu.lu.engine; 2 | 3 | import android.content.res.AssetManager; 4 | 5 | import org.mozilla.javascript.commonjs.module.provider.ModuleSource; 6 | import org.mozilla.javascript.commonjs.module.provider.UrlModuleSourceProvider; 7 | 8 | import java.io.FileNotFoundException; 9 | import java.io.IOException; 10 | import java.io.InputStreamReader; 11 | import java.net.URI; 12 | import java.net.URISyntaxException; 13 | import java.util.List; 14 | 15 | 16 | public class AssetAndUrlModuleSourceProvider extends UrlModuleSourceProvider { 17 | 18 | private final URI mBaseURI; 19 | private final String mAssetDirPath; 20 | private final AssetManager mAssetManager; 21 | 22 | public AssetAndUrlModuleSourceProvider(String assetDirPath, List list) { 23 | super(list, null); 24 | mAssetDirPath = assetDirPath; 25 | mBaseURI = URI.create("file:///android_asset/" + assetDirPath); 26 | mAssetManager = ModuleApp.application.getAssets(); 27 | } 28 | 29 | @Override 30 | protected ModuleSource loadFromPrivilegedLocations(String moduleId, Object validator) throws IOException, URISyntaxException { 31 | String moduleIdWithExtension = moduleId; 32 | if (!moduleIdWithExtension.endsWith(".js")) { 33 | moduleIdWithExtension += ".js"; 34 | } 35 | try { 36 | return new ModuleSource(new InputStreamReader(mAssetManager.open(mAssetDirPath + "/" + moduleIdWithExtension)), null, 37 | new URI(mBaseURI.toString() + "/" + moduleIdWithExtension), mBaseURI, validator); 38 | } catch (FileNotFoundException e) { 39 | return super.loadFromPrivilegedLocations(moduleId, validator); 40 | } 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /engine/src/main/java/github/hotstu/lu/engine/ConsoleImpl.java: -------------------------------------------------------------------------------- 1 | package github.hotstu.lu.engine; 2 | 3 | import android.util.Log; 4 | 5 | /** 6 | * @author hglf [hglf](https://github.com/hotstu) 7 | * @desc 8 | * @since 9/10/20 9 | */ 10 | public class ConsoleImpl { 11 | public void log(Object... objects) { 12 | StringBuilder sb = new StringBuilder(); 13 | for (Object object : objects) { 14 | sb.append(object); 15 | sb.append(','); 16 | } 17 | sb.delete(sb.length() - 1, sb.length()); 18 | Log.d("Console", sb.toString()); 19 | } 20 | 21 | public void error(Object... objects) { 22 | StringBuilder sb = new StringBuilder(); 23 | for (Object object : objects) { 24 | sb.append(object); 25 | sb.append(','); 26 | } 27 | sb.delete(sb.length() - 1, sb.length()); 28 | Log.e("Console", sb.toString()); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /engine/src/main/java/github/hotstu/lu/engine/DefaultLuContext.java: -------------------------------------------------------------------------------- 1 | package github.hotstu.lu.engine; 2 | 3 | import org.mozilla.javascript.Scriptable; 4 | 5 | /** 6 | * @author hglf [hglf](https://github.com/hotstu) 7 | * @desc 8 | * @since 9/18/20 9 | */ 10 | public class DefaultLuContext extends LuContext { 11 | public DefaultLuContext(Scriptable sharedScope) { 12 | super(sharedScope); 13 | } 14 | 15 | @Override 16 | protected void onContextScopeCreated(Scriptable scope) { 17 | LuEngine.getInstance().putProperty(scope, "$app", ModuleApp.application); 18 | LuEngine.getInstance().putProperty(scope, "$context", this); 19 | 20 | } 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /engine/src/main/java/github/hotstu/lu/engine/LuContext.java: -------------------------------------------------------------------------------- 1 | package github.hotstu.lu.engine; 2 | 3 | import android.os.Bundle; 4 | 5 | import org.mozilla.javascript.Context; 6 | import org.mozilla.javascript.Scriptable; 7 | 8 | import java.util.concurrent.CountDownLatch; 9 | 10 | /** 11 | * @author hglf [hglf](https://github.com/hotstu) 12 | * @desc 13 | * @since 9/16/20 14 | */ 15 | public abstract class LuContext { 16 | 17 | private Scriptable mContextScope; 18 | private final Scriptable mShared; 19 | 20 | public LuContext() { 21 | this.mShared = LuEngine.getInstance().getSharedScope(); 22 | } 23 | public LuContext(Scriptable sharedScope) { 24 | this.mShared = sharedScope; 25 | } 26 | 27 | public Scriptable getContextScope() { 28 | return mContextScope; 29 | } 30 | 31 | 32 | public CountDownLatch resetContextScope() { 33 | CountDownLatch countDownLatch = new CountDownLatch(1); 34 | LuEngine.getInstance().getWorkerHandler().post(() -> { 35 | this.mContextScope = resetContextScope(mShared); 36 | this.onContextScopeCreated(this.mContextScope); 37 | countDownLatch.countDown(); 38 | }); 39 | return countDownLatch; 40 | } 41 | 42 | 43 | protected abstract void onContextScopeCreated(Scriptable scope); 44 | 45 | 46 | public Bundle exec(String script) { 47 | ScriptTask task = new ScriptTask(this, script); 48 | LuEngine.getInstance().enqueueTask(task); 49 | task.await(3*1000); 50 | Bundle bundle = new Bundle(); 51 | bundle.putBoolean("success", task.err == null); 52 | bundle.putString("data", task.output); 53 | bundle.putString("err", task.err); 54 | return bundle; 55 | } 56 | 57 | private Scriptable resetContextScope(Scriptable shared) { 58 | LuEngine.getInstance().enforceWorkerThread(); 59 | // We can share the scope. 60 | Scriptable threadScope = Context.getCurrentContext().newObject(shared); 61 | threadScope.setPrototype(shared); 62 | 63 | // We want "threadScope" to be a new top-level 64 | // scope, so set its parent scope to null. This 65 | // means that any variables created by assignments 66 | // will be properties of "threadScope". 67 | threadScope.setParentScope(null); 68 | return threadScope; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /engine/src/main/java/github/hotstu/lu/engine/LuEngineThread.java: -------------------------------------------------------------------------------- 1 | package github.hotstu.lu.engine; 2 | 3 | import android.os.HandlerThread; 4 | 5 | import com.faendir.rhino_android.RhinoAndroidHelper; 6 | 7 | import org.mozilla.javascript.Context; 8 | import org.mozilla.javascript.Scriptable; 9 | import org.mozilla.javascript.WrapFactory; 10 | 11 | import java.util.Locale; 12 | 13 | public class LuEngineThread extends HandlerThread { 14 | 15 | public LuEngineThread(String name) { 16 | super(name); 17 | } 18 | 19 | public LuEngineThread(String name, int priority) { 20 | super(name, priority); 21 | } 22 | 23 | @Override 24 | protected void onLooperPrepared() { 25 | super.onLooperPrepared(); 26 | init(); 27 | } 28 | 29 | 30 | public void init() { 31 | if (Context.getCurrentContext() == null) { 32 | Context context = RhinoAndroidHelper.prepareContext(); 33 | context.setOptimizationLevel(1);//TODO 大于0 == JIT 34 | context.setLanguageVersion(Context.VERSION_ES6); 35 | context.setLocale(Locale.getDefault()); 36 | context.setWrapFactory(new WrapFactory() { 37 | @Override 38 | public Object wrap(Context cx, Scriptable scope, Object obj, Class staticType) { 39 | return super.wrap(cx, scope, obj, staticType); 40 | } 41 | 42 | @Override 43 | public Scriptable wrapNewObject(Context cx, Scriptable scope, Object obj) { 44 | return super.wrapNewObject(cx, scope, obj); 45 | } 46 | 47 | @Override 48 | public Scriptable wrapAsJavaObject(Context cx, Scriptable scope, Object javaObject, Class staticType) { 49 | return super.wrapAsJavaObject(cx, scope, javaObject, staticType); 50 | } 51 | 52 | @Override 53 | public Scriptable wrapJavaClass(Context cx, Scriptable scope, Class javaClass) { 54 | return super.wrapJavaClass(cx, scope, javaClass); 55 | } 56 | }); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /engine/src/main/java/github/hotstu/lu/engine/ModuleApp.java: -------------------------------------------------------------------------------- 1 | package github.hotstu.lu.engine; 2 | 3 | import android.content.Context; 4 | 5 | import github.hotstu.lu.base.IModule; 6 | 7 | /** 8 | * @author hglf [hglf](https://github.com/hotstu) 9 | * @desc 10 | * @since 9/16/20 11 | */ 12 | public class ModuleApp implements IModule { 13 | public static Context application; 14 | @Override 15 | public void onCreate(Context context) { 16 | application = context; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /engine/src/main/java/github/hotstu/lu/engine/ScriptTask.java: -------------------------------------------------------------------------------- 1 | package github.hotstu.lu.engine; 2 | 3 | import java.util.concurrent.CountDownLatch; 4 | import java.util.concurrent.TimeUnit; 5 | 6 | public class ScriptTask { 7 | public final String input; 8 | public final LuContext context; 9 | private final CountDownLatch lock; 10 | public String output = null; 11 | public String err = null; 12 | 13 | 14 | public ScriptTask(LuContext context, String input) { 15 | this.context = context; 16 | this.input = input; 17 | this.lock = new CountDownLatch(1); 18 | } 19 | 20 | public void await(long timeout) { 21 | try { 22 | lock.await(timeout, TimeUnit.MILLISECONDS); 23 | } catch (InterruptedException e) { 24 | e.printStackTrace(); 25 | } 26 | } 27 | 28 | public void sendBack() { 29 | lock.countDown(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /engine/src/main/resources/META-INF/services/github.hotstu.lu.base.IModule: -------------------------------------------------------------------------------- 1 | github.hotstu.lu.engine.ModuleApp -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hotstu/lu/f2c33abaf58bd569570c86c0d77d7e408a637770/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Sep 16 14:13:00 CST 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /play/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /play/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 29 5 | buildToolsVersion "30.0.2" 6 | compileOptions { 7 | sourceCompatibility 1.8 8 | targetCompatibility 1.8 9 | } 10 | defaultConfig { 11 | minSdkVersion 22 12 | targetSdkVersion 29 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | consumerProguardFiles "consumer-rules.pro" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | } 26 | 27 | dependencies { 28 | implementation fileTree(dir: "libs", include: ["*.jar"]) 29 | implementation 'androidx.appcompat:appcompat:1.2.0' 30 | implementation 'org.nanohttpd:nanohttpd:2.3.1' 31 | implementation 'com.google.code.gson:gson:2.8.6' 32 | implementation("com.squareup.okio:okio:2.8.0") 33 | implementation project(path: ':base') 34 | implementation project(path: ':engine') 35 | 36 | } -------------------------------------------------------------------------------- /play/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hotstu/lu/f2c33abaf58bd569570c86c0d77d7e408a637770/play/consumer-rules.pro -------------------------------------------------------------------------------- /play/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /play/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /play/src/main/assets/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Lu Play Groud 6 | 7 | 8 | 9 | 10 | 11 | 12 | 20 | 21 | 22 |
23 |
24 | 26 |
27 |
28 | 29 | 30 | 31 |
32 |
33 |
    34 |
    35 |
    36 | 37 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/comment/continuecomment.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | var nonspace = /\S/g; 13 | var repeat = String.prototype.repeat || function (n) { return Array(n + 1).join(this); }; 14 | function continueComment(cm) { 15 | if (cm.getOption("disableInput")) return CodeMirror.Pass; 16 | var ranges = cm.listSelections(), mode, inserts = []; 17 | for (var i = 0; i < ranges.length; i++) { 18 | var pos = ranges[i].head 19 | if (!/\bcomment\b/.test(cm.getTokenTypeAt(pos))) return CodeMirror.Pass; 20 | var modeHere = cm.getModeAt(pos) 21 | if (!mode) mode = modeHere; 22 | else if (mode != modeHere) return CodeMirror.Pass; 23 | 24 | var insert = null, line, found; 25 | var blockStart = mode.blockCommentStart, lineCmt = mode.lineComment; 26 | if (blockStart && mode.blockCommentContinue) { 27 | line = cm.getLine(pos.line); 28 | var end = line.lastIndexOf(mode.blockCommentEnd, pos.ch - mode.blockCommentEnd.length); 29 | // 1. if this block comment ended 30 | // 2. if this is actually inside a line comment 31 | if (end != -1 && end == pos.ch - mode.blockCommentEnd.length || 32 | lineCmt && (found = line.lastIndexOf(lineCmt, pos.ch - 1)) > -1 && 33 | /\bcomment\b/.test(cm.getTokenTypeAt({line: pos.line, ch: found + 1}))) { 34 | // ...then don't continue it 35 | } else if (pos.ch >= blockStart.length && 36 | (found = line.lastIndexOf(blockStart, pos.ch - blockStart.length)) > -1 && 37 | found > end) { 38 | // reuse the existing leading spaces/tabs/mixed 39 | // or build the correct indent using CM's tab/indent options 40 | if (nonspaceAfter(0, line) >= found) { 41 | insert = line.slice(0, found); 42 | } else { 43 | var tabSize = cm.options.tabSize, numTabs; 44 | found = CodeMirror.countColumn(line, found, tabSize); 45 | insert = !cm.options.indentWithTabs ? repeat.call(" ", found) : 46 | repeat.call("\t", (numTabs = Math.floor(found / tabSize))) + 47 | repeat.call(" ", found - tabSize * numTabs); 48 | } 49 | } else if ((found = line.indexOf(mode.blockCommentContinue)) > -1 && 50 | found <= pos.ch && 51 | found <= nonspaceAfter(0, line)) { 52 | insert = line.slice(0, found); 53 | } 54 | if (insert != null) insert += mode.blockCommentContinue 55 | } 56 | if (insert == null && lineCmt && continueLineCommentEnabled(cm)) { 57 | if (line == null) line = cm.getLine(pos.line); 58 | found = line.indexOf(lineCmt); 59 | // cursor at pos 0, line comment also at pos 0 => shift it down, don't continue 60 | if (!pos.ch && !found) insert = ""; 61 | // continue only if the line starts with an optional space + line comment 62 | else if (found > -1 && nonspaceAfter(0, line) >= found) { 63 | // don't continue if there's only space(s) after cursor or the end of the line 64 | insert = nonspaceAfter(pos.ch, line) > -1; 65 | // but always continue if the next line starts with a line comment too 66 | if (!insert) { 67 | var next = cm.getLine(pos.line + 1) || '', 68 | nextFound = next.indexOf(lineCmt); 69 | insert = nextFound > -1 && nonspaceAfter(0, next) >= nextFound || null; 70 | } 71 | if (insert) { 72 | insert = line.slice(0, found) + lineCmt + 73 | line.slice(found + lineCmt.length).match(/^\s*/)[0]; 74 | } 75 | } 76 | } 77 | if (insert == null) return CodeMirror.Pass; 78 | inserts[i] = "\n" + insert; 79 | } 80 | 81 | cm.operation(function() { 82 | for (var i = ranges.length - 1; i >= 0; i--) 83 | cm.replaceRange(inserts[i], ranges[i].from(), ranges[i].to(), "+insert"); 84 | }); 85 | } 86 | 87 | function nonspaceAfter(ch, str) { 88 | nonspace.lastIndex = ch; 89 | var m = nonspace.exec(str); 90 | return m ? m.index : -1; 91 | } 92 | 93 | function continueLineCommentEnabled(cm) { 94 | var opt = cm.getOption("continueComments"); 95 | if (opt && typeof opt == "object") 96 | return opt.continueLineComment !== false; 97 | return true; 98 | } 99 | 100 | CodeMirror.defineOption("continueComments", null, function(cm, val, prev) { 101 | if (prev && prev != CodeMirror.Init) 102 | cm.removeKeyMap("continueComment"); 103 | if (val) { 104 | var key = "Enter"; 105 | if (typeof val == "string") 106 | key = val; 107 | else if (typeof val == "object" && val.key) 108 | key = val.key; 109 | var map = {name: "continueComment"}; 110 | map[key] = continueComment; 111 | cm.addKeyMap(map); 112 | } 113 | }); 114 | }); 115 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/dialog/dialog.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-dialog { 2 | position: absolute; 3 | left: 0; right: 0; 4 | background: inherit; 5 | z-index: 15; 6 | padding: .1em .8em; 7 | overflow: hidden; 8 | color: inherit; 9 | } 10 | 11 | .CodeMirror-dialog-top { 12 | border-bottom: 1px solid #eee; 13 | top: 0; 14 | } 15 | 16 | .CodeMirror-dialog-bottom { 17 | border-top: 1px solid #eee; 18 | bottom: 0; 19 | } 20 | 21 | .CodeMirror-dialog input { 22 | border: none; 23 | outline: none; 24 | background: transparent; 25 | width: 20em; 26 | color: inherit; 27 | font-family: monospace; 28 | } 29 | 30 | .CodeMirror-dialog button { 31 | font-size: 70%; 32 | } 33 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/display/autorefresh.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")) 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod) 9 | else // Plain browser env 10 | mod(CodeMirror) 11 | })(function(CodeMirror) { 12 | "use strict" 13 | 14 | CodeMirror.defineOption("autoRefresh", false, function(cm, val) { 15 | if (cm.state.autoRefresh) { 16 | stopListening(cm, cm.state.autoRefresh) 17 | cm.state.autoRefresh = null 18 | } 19 | if (val && cm.display.wrapper.offsetHeight == 0) 20 | startListening(cm, cm.state.autoRefresh = {delay: val.delay || 250}) 21 | }) 22 | 23 | function startListening(cm, state) { 24 | function check() { 25 | if (cm.display.wrapper.offsetHeight) { 26 | stopListening(cm, state) 27 | if (cm.display.lastWrapHeight != cm.display.wrapper.clientHeight) 28 | cm.refresh() 29 | } else { 30 | state.timeout = setTimeout(check, state.delay) 31 | } 32 | } 33 | state.timeout = setTimeout(check, state.delay) 34 | state.hurry = function() { 35 | clearTimeout(state.timeout) 36 | state.timeout = setTimeout(check, 50) 37 | } 38 | CodeMirror.on(window, "mouseup", state.hurry) 39 | CodeMirror.on(window, "keyup", state.hurry) 40 | } 41 | 42 | function stopListening(_cm, state) { 43 | clearTimeout(state.timeout) 44 | CodeMirror.off(window, "mouseup", state.hurry) 45 | CodeMirror.off(window, "keyup", state.hurry) 46 | } 47 | }); 48 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/display/fullscreen.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-fullscreen { 2 | position: fixed; 3 | top: 0; left: 0; right: 0; bottom: 0; 4 | height: auto; 5 | z-index: 9; 6 | } 7 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/display/fullscreen.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.defineOption("fullScreen", false, function(cm, val, old) { 15 | if (old == CodeMirror.Init) old = false; 16 | if (!old == !val) return; 17 | if (val) setFullscreen(cm); 18 | else setNormal(cm); 19 | }); 20 | 21 | function setFullscreen(cm) { 22 | var wrap = cm.getWrapperElement(); 23 | cm.state.fullScreenRestore = {scrollTop: window.pageYOffset, scrollLeft: window.pageXOffset, 24 | width: wrap.style.width, height: wrap.style.height}; 25 | wrap.style.width = ""; 26 | wrap.style.height = "auto"; 27 | wrap.className += " CodeMirror-fullscreen"; 28 | document.documentElement.style.overflow = "hidden"; 29 | cm.refresh(); 30 | } 31 | 32 | function setNormal(cm) { 33 | var wrap = cm.getWrapperElement(); 34 | wrap.className = wrap.className.replace(/\s*CodeMirror-fullscreen\b/, ""); 35 | document.documentElement.style.overflow = ""; 36 | var info = cm.state.fullScreenRestore; 37 | wrap.style.width = info.width; wrap.style.height = info.height; 38 | window.scrollTo(info.scrollLeft, info.scrollTop); 39 | cm.refresh(); 40 | } 41 | }); 42 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/display/panel.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function (mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function (CodeMirror) { 12 | CodeMirror.defineExtension("addPanel", function (node, options) { 13 | options = options || {}; 14 | 15 | if (!this.state.panels) initPanels(this); 16 | 17 | var info = this.state.panels; 18 | var wrapper = info.wrapper; 19 | var cmWrapper = this.getWrapperElement(); 20 | var replace = options.replace instanceof Panel && !options.replace.cleared; 21 | 22 | if (options.after instanceof Panel && !options.after.cleared) { 23 | wrapper.insertBefore(node, options.before.node.nextSibling); 24 | } else if (options.before instanceof Panel && !options.before.cleared) { 25 | wrapper.insertBefore(node, options.before.node); 26 | } else if (replace) { 27 | wrapper.insertBefore(node, options.replace.node); 28 | options.replace.clear(true); 29 | } else if (options.position == "bottom") { 30 | wrapper.appendChild(node); 31 | } else if (options.position == "before-bottom") { 32 | wrapper.insertBefore(node, cmWrapper.nextSibling); 33 | } else if (options.position == "after-top") { 34 | wrapper.insertBefore(node, cmWrapper); 35 | } else { 36 | wrapper.insertBefore(node, wrapper.firstChild); 37 | } 38 | 39 | var height = (options && options.height) || node.offsetHeight; 40 | 41 | var panel = new Panel(this, node, options, height); 42 | info.panels.push(panel); 43 | 44 | this.setSize(); 45 | if (options.stable && isAtTop(this, node)) 46 | this.scrollTo(null, this.getScrollInfo().top + height); 47 | 48 | return panel; 49 | }); 50 | 51 | function Panel(cm, node, options, height) { 52 | this.cm = cm; 53 | this.node = node; 54 | this.options = options; 55 | this.height = height; 56 | this.cleared = false; 57 | } 58 | 59 | /* when skipRemove is true, clear() was called from addPanel(). 60 | * Thus removePanels() should not be called (issue 5518) */ 61 | Panel.prototype.clear = function (skipRemove) { 62 | if (this.cleared) return; 63 | this.cleared = true; 64 | var info = this.cm.state.panels; 65 | info.panels.splice(info.panels.indexOf(this), 1); 66 | this.cm.setSize(); 67 | if (this.options.stable && isAtTop(this.cm, this.node)) 68 | this.cm.scrollTo(null, this.cm.getScrollInfo().top - this.height) 69 | info.wrapper.removeChild(this.node); 70 | if (info.panels.length == 0 && !skipRemove) removePanels(this.cm); 71 | }; 72 | 73 | Panel.prototype.changed = function () { 74 | this.height = this.node.getBoundingClientRect().height; 75 | this.cm.setSize(); 76 | }; 77 | 78 | function initPanels(cm) { 79 | var wrap = cm.getWrapperElement(); 80 | var style = window.getComputedStyle ? window.getComputedStyle(wrap) : wrap.currentStyle; 81 | var height = parseInt(style.height); 82 | var info = cm.state.panels = { 83 | setHeight: wrap.style.height, 84 | panels: [], 85 | wrapper: document.createElement("div") 86 | }; 87 | wrap.parentNode.insertBefore(info.wrapper, wrap); 88 | var hasFocus = cm.hasFocus(); 89 | info.wrapper.appendChild(wrap); 90 | if (hasFocus) cm.focus(); 91 | 92 | cm._setSize = cm.setSize; 93 | if (height != null) cm.setSize = function (width, newHeight) { 94 | if (!newHeight) newHeight = info.wrapper.offsetHeight; 95 | info.setHeight = newHeight; 96 | if (typeof newHeight != "number") { 97 | var px = /^(\d+\.?\d*)px$/.exec(newHeight); 98 | if (px) { 99 | newHeight = Number(px[1]); 100 | } else { 101 | info.wrapper.style.height = newHeight; 102 | newHeight = info.wrapper.offsetHeight; 103 | } 104 | } 105 | var editorheight = newHeight - info.panels 106 | .map(function (p) { return p.node.getBoundingClientRect().height; }) 107 | .reduce(function (a, b) { return a + b; }, 0); 108 | cm._setSize(width, editorheight); 109 | height = newHeight; 110 | }; 111 | } 112 | 113 | function removePanels(cm) { 114 | var info = cm.state.panels; 115 | cm.state.panels = null; 116 | 117 | var wrap = cm.getWrapperElement(); 118 | info.wrapper.parentNode.replaceChild(wrap, info.wrapper); 119 | wrap.style.height = info.setHeight; 120 | cm.setSize = cm._setSize; 121 | cm.setSize(); 122 | } 123 | 124 | function isAtTop(cm, dom) { 125 | for (var sibling = dom.nextSibling; sibling; sibling = sibling.nextSibling) 126 | if (sibling == cm.getWrapperElement()) return true 127 | return false 128 | } 129 | }); 130 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/display/placeholder.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | CodeMirror.defineOption("placeholder", "", function(cm, val, old) { 13 | var prev = old && old != CodeMirror.Init; 14 | if (val && !prev) { 15 | cm.on("blur", onBlur); 16 | cm.on("change", onChange); 17 | cm.on("swapDoc", onChange); 18 | onChange(cm); 19 | } else if (!val && prev) { 20 | cm.off("blur", onBlur); 21 | cm.off("change", onChange); 22 | cm.off("swapDoc", onChange); 23 | clearPlaceholder(cm); 24 | var wrapper = cm.getWrapperElement(); 25 | wrapper.className = wrapper.className.replace(" CodeMirror-empty", ""); 26 | } 27 | 28 | if (val && !cm.hasFocus()) onBlur(cm); 29 | }); 30 | 31 | function clearPlaceholder(cm) { 32 | if (cm.state.placeholder) { 33 | cm.state.placeholder.parentNode.removeChild(cm.state.placeholder); 34 | cm.state.placeholder = null; 35 | } 36 | } 37 | function setPlaceholder(cm) { 38 | clearPlaceholder(cm); 39 | var elt = cm.state.placeholder = document.createElement("pre"); 40 | elt.style.cssText = "height: 0; overflow: visible"; 41 | elt.style.direction = cm.getOption("direction"); 42 | elt.className = "CodeMirror-placeholder CodeMirror-line-like"; 43 | var placeHolder = cm.getOption("placeholder") 44 | if (typeof placeHolder == "string") placeHolder = document.createTextNode(placeHolder) 45 | elt.appendChild(placeHolder) 46 | cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild); 47 | } 48 | 49 | function onBlur(cm) { 50 | if (isEmpty(cm)) setPlaceholder(cm); 51 | } 52 | function onChange(cm) { 53 | var wrapper = cm.getWrapperElement(), empty = isEmpty(cm); 54 | wrapper.className = wrapper.className.replace(" CodeMirror-empty", "") + (empty ? " CodeMirror-empty" : ""); 55 | 56 | if (empty) setPlaceholder(cm); 57 | else clearPlaceholder(cm); 58 | } 59 | 60 | function isEmpty(cm) { 61 | return (cm.lineCount() === 1) && (cm.getLine(0) === ""); 62 | } 63 | }); 64 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/display/rulers.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.defineOption("rulers", false, function(cm, val) { 15 | if (cm.state.rulerDiv) { 16 | cm.state.rulerDiv.parentElement.removeChild(cm.state.rulerDiv) 17 | cm.state.rulerDiv = null 18 | cm.off("refresh", drawRulers) 19 | } 20 | if (val && val.length) { 21 | cm.state.rulerDiv = cm.display.lineSpace.parentElement.insertBefore(document.createElement("div"), cm.display.lineSpace) 22 | cm.state.rulerDiv.className = "CodeMirror-rulers" 23 | drawRulers(cm) 24 | cm.on("refresh", drawRulers) 25 | } 26 | }); 27 | 28 | function drawRulers(cm) { 29 | cm.state.rulerDiv.textContent = "" 30 | var val = cm.getOption("rulers"); 31 | var cw = cm.defaultCharWidth(); 32 | var left = cm.charCoords(CodeMirror.Pos(cm.firstLine(), 0), "div").left; 33 | cm.state.rulerDiv.style.minHeight = (cm.display.scroller.offsetHeight + 30) + "px"; 34 | for (var i = 0; i < val.length; i++) { 35 | var elt = document.createElement("div"); 36 | elt.className = "CodeMirror-ruler"; 37 | var col, conf = val[i]; 38 | if (typeof conf == "number") { 39 | col = conf; 40 | } else { 41 | col = conf.column; 42 | if (conf.className) elt.className += " " + conf.className; 43 | if (conf.color) elt.style.borderColor = conf.color; 44 | if (conf.lineStyle) elt.style.borderLeftStyle = conf.lineStyle; 45 | if (conf.width) elt.style.borderLeftWidth = conf.width; 46 | } 47 | elt.style.left = (left + col * cw) + "px"; 48 | cm.state.rulerDiv.appendChild(elt) 49 | } 50 | } 51 | }); 52 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/edit/continuelist.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | var listRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]\s|[*+-]\s|(\d+)([.)]))(\s*)/, 15 | emptyListRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]|[*+-]|(\d+)[.)])(\s*)$/, 16 | unorderedListRE = /[*+-]\s/; 17 | 18 | CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) { 19 | if (cm.getOption("disableInput")) return CodeMirror.Pass; 20 | var ranges = cm.listSelections(), replacements = []; 21 | for (var i = 0; i < ranges.length; i++) { 22 | var pos = ranges[i].head; 23 | 24 | // If we're not in Markdown mode, fall back to normal newlineAndIndent 25 | var eolState = cm.getStateAfter(pos.line); 26 | var inner = CodeMirror.innerMode(cm.getMode(), eolState); 27 | if (inner.mode.name !== "markdown") { 28 | cm.execCommand("newlineAndIndent"); 29 | return; 30 | } else { 31 | eolState = inner.state; 32 | } 33 | 34 | var inList = eolState.list !== false; 35 | var inQuote = eolState.quote !== 0; 36 | 37 | var line = cm.getLine(pos.line), match = listRE.exec(line); 38 | var cursorBeforeBullet = /^\s*$/.test(line.slice(0, pos.ch)); 39 | if (!ranges[i].empty() || (!inList && !inQuote) || !match || cursorBeforeBullet) { 40 | cm.execCommand("newlineAndIndent"); 41 | return; 42 | } 43 | if (emptyListRE.test(line)) { 44 | var endOfQuote = inQuote && />\s*$/.test(line) 45 | var endOfList = !/>\s*$/.test(line) 46 | if (endOfQuote || endOfList) cm.replaceRange("", { 47 | line: pos.line, ch: 0 48 | }, { 49 | line: pos.line, ch: pos.ch + 1 50 | }); 51 | replacements[i] = "\n"; 52 | } else { 53 | var indent = match[1], after = match[5]; 54 | var numbered = !(unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0); 55 | var bullet = numbered ? (parseInt(match[3], 10) + 1) + match[4] : match[2].replace("x", " "); 56 | replacements[i] = "\n" + indent + bullet + after; 57 | 58 | if (numbered) incrementRemainingMarkdownListNumbers(cm, pos); 59 | } 60 | } 61 | 62 | cm.replaceSelections(replacements); 63 | }; 64 | 65 | // Auto-updating Markdown list numbers when a new item is added to the 66 | // middle of a list 67 | function incrementRemainingMarkdownListNumbers(cm, pos) { 68 | var startLine = pos.line, lookAhead = 0, skipCount = 0; 69 | var startItem = listRE.exec(cm.getLine(startLine)), startIndent = startItem[1]; 70 | 71 | do { 72 | lookAhead += 1; 73 | var nextLineNumber = startLine + lookAhead; 74 | var nextLine = cm.getLine(nextLineNumber), nextItem = listRE.exec(nextLine); 75 | 76 | if (nextItem) { 77 | var nextIndent = nextItem[1]; 78 | var newNumber = (parseInt(startItem[3], 10) + lookAhead - skipCount); 79 | var nextNumber = (parseInt(nextItem[3], 10)), itemNumber = nextNumber; 80 | 81 | if (startIndent === nextIndent && !isNaN(nextNumber)) { 82 | if (newNumber === nextNumber) itemNumber = nextNumber + 1; 83 | if (newNumber > nextNumber) itemNumber = newNumber + 1; 84 | cm.replaceRange( 85 | nextLine.replace(listRE, nextIndent + itemNumber + nextItem[4] + nextItem[5]), 86 | { 87 | line: nextLineNumber, ch: 0 88 | }, { 89 | line: nextLineNumber, ch: nextLine.length 90 | }); 91 | } else { 92 | if (startIndent.length > nextIndent.length) return; 93 | // This doesn't run if the next line immediatley indents, as it is 94 | // not clear of the users intention (new indented item or same level) 95 | if ((startIndent.length < nextIndent.length) && (lookAhead === 1)) return; 96 | skipCount += 1; 97 | } 98 | } 99 | } while (nextItem); 100 | } 101 | }); 102 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/edit/matchtags.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror"), require("../fold/xml-fold")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror", "../fold/xml-fold"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.defineOption("matchTags", false, function(cm, val, old) { 15 | if (old && old != CodeMirror.Init) { 16 | cm.off("cursorActivity", doMatchTags); 17 | cm.off("viewportChange", maybeUpdateMatch); 18 | clear(cm); 19 | } 20 | if (val) { 21 | cm.state.matchBothTags = typeof val == "object" && val.bothTags; 22 | cm.on("cursorActivity", doMatchTags); 23 | cm.on("viewportChange", maybeUpdateMatch); 24 | doMatchTags(cm); 25 | } 26 | }); 27 | 28 | function clear(cm) { 29 | if (cm.state.tagHit) cm.state.tagHit.clear(); 30 | if (cm.state.tagOther) cm.state.tagOther.clear(); 31 | cm.state.tagHit = cm.state.tagOther = null; 32 | } 33 | 34 | function doMatchTags(cm) { 35 | cm.state.failedTagMatch = false; 36 | cm.operation(function() { 37 | clear(cm); 38 | if (cm.somethingSelected()) return; 39 | var cur = cm.getCursor(), range = cm.getViewport(); 40 | range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to); 41 | var match = CodeMirror.findMatchingTag(cm, cur, range); 42 | if (!match) return; 43 | if (cm.state.matchBothTags) { 44 | var hit = match.at == "open" ? match.open : match.close; 45 | if (hit) cm.state.tagHit = cm.markText(hit.from, hit.to, {className: "CodeMirror-matchingtag"}); 46 | } 47 | var other = match.at == "close" ? match.open : match.close; 48 | if (other) 49 | cm.state.tagOther = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"}); 50 | else 51 | cm.state.failedTagMatch = true; 52 | }); 53 | } 54 | 55 | function maybeUpdateMatch(cm) { 56 | if (cm.state.failedTagMatch) doMatchTags(cm); 57 | } 58 | 59 | CodeMirror.commands.toMatchingTag = function(cm) { 60 | var found = CodeMirror.findMatchingTag(cm, cm.getCursor()); 61 | if (found) { 62 | var other = found.at == "close" ? found.open : found.close; 63 | if (other) cm.extendSelection(other.to, other.from); 64 | } 65 | }; 66 | }); 67 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/edit/trailingspace.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) { 13 | if (prev == CodeMirror.Init) prev = false; 14 | if (prev && !val) 15 | cm.removeOverlay("trailingspace"); 16 | else if (!prev && val) 17 | cm.addOverlay({ 18 | token: function(stream) { 19 | for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {} 20 | if (i > stream.pos) { stream.pos = i; return null; } 21 | stream.pos = l; 22 | return "trailingspace"; 23 | }, 24 | name: "trailingspace" 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/fold/brace-fold.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.registerHelper("fold", "brace", function(cm, start) { 15 | var line = start.line, lineText = cm.getLine(line); 16 | var tokenType; 17 | 18 | function findOpening(openCh) { 19 | for (var at = start.ch, pass = 0;;) { 20 | var found = at <= 0 ? -1 : lineText.lastIndexOf(openCh, at - 1); 21 | if (found == -1) { 22 | if (pass == 1) break; 23 | pass = 1; 24 | at = lineText.length; 25 | continue; 26 | } 27 | if (pass == 1 && found < start.ch) break; 28 | tokenType = cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1)); 29 | if (!/^(comment|string)/.test(tokenType)) return found + 1; 30 | at = found - 1; 31 | } 32 | } 33 | 34 | var startToken = "{", endToken = "}", startCh = findOpening("{"); 35 | if (startCh == null) { 36 | startToken = "[", endToken = "]"; 37 | startCh = findOpening("["); 38 | } 39 | 40 | if (startCh == null) return; 41 | var count = 1, lastLine = cm.lastLine(), end, endCh; 42 | outer: for (var i = line; i <= lastLine; ++i) { 43 | var text = cm.getLine(i), pos = i == line ? startCh : 0; 44 | for (;;) { 45 | var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos); 46 | if (nextOpen < 0) nextOpen = text.length; 47 | if (nextClose < 0) nextClose = text.length; 48 | pos = Math.min(nextOpen, nextClose); 49 | if (pos == text.length) break; 50 | if (cm.getTokenTypeAt(CodeMirror.Pos(i, pos + 1)) == tokenType) { 51 | if (pos == nextOpen) ++count; 52 | else if (!--count) { end = i; endCh = pos; break outer; } 53 | } 54 | ++pos; 55 | } 56 | } 57 | if (end == null || line == end) return; 58 | return {from: CodeMirror.Pos(line, startCh), 59 | to: CodeMirror.Pos(end, endCh)}; 60 | }); 61 | 62 | CodeMirror.registerHelper("fold", "import", function(cm, start) { 63 | function hasImport(line) { 64 | if (line < cm.firstLine() || line > cm.lastLine()) return null; 65 | var start = cm.getTokenAt(CodeMirror.Pos(line, 1)); 66 | if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1)); 67 | if (start.type != "keyword" || start.string != "import") return null; 68 | // Now find closing semicolon, return its position 69 | for (var i = line, e = Math.min(cm.lastLine(), line + 10); i <= e; ++i) { 70 | var text = cm.getLine(i), semi = text.indexOf(";"); 71 | if (semi != -1) return {startCh: start.end, end: CodeMirror.Pos(i, semi)}; 72 | } 73 | } 74 | 75 | var startLine = start.line, has = hasImport(startLine), prev; 76 | if (!has || hasImport(startLine - 1) || ((prev = hasImport(startLine - 2)) && prev.end.line == startLine - 1)) 77 | return null; 78 | for (var end = has.end;;) { 79 | var next = hasImport(end.line + 1); 80 | if (next == null) break; 81 | end = next.end; 82 | } 83 | return {from: cm.clipPos(CodeMirror.Pos(startLine, has.startCh + 1)), to: end}; 84 | }); 85 | 86 | CodeMirror.registerHelper("fold", "include", function(cm, start) { 87 | function hasInclude(line) { 88 | if (line < cm.firstLine() || line > cm.lastLine()) return null; 89 | var start = cm.getTokenAt(CodeMirror.Pos(line, 1)); 90 | if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1)); 91 | if (start.type == "meta" && start.string.slice(0, 8) == "#include") return start.start + 8; 92 | } 93 | 94 | var startLine = start.line, has = hasInclude(startLine); 95 | if (has == null || hasInclude(startLine - 1) != null) return null; 96 | for (var end = startLine;;) { 97 | var next = hasInclude(end + 1); 98 | if (next == null) break; 99 | ++end; 100 | } 101 | return {from: CodeMirror.Pos(startLine, has + 1), 102 | to: cm.clipPos(CodeMirror.Pos(end))}; 103 | }); 104 | 105 | }); 106 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/fold/comment-fold.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.registerGlobalHelper("fold", "comment", function(mode) { 15 | return mode.blockCommentStart && mode.blockCommentEnd; 16 | }, function(cm, start) { 17 | var mode = cm.getModeAt(start), startToken = mode.blockCommentStart, endToken = mode.blockCommentEnd; 18 | if (!startToken || !endToken) return; 19 | var line = start.line, lineText = cm.getLine(line); 20 | 21 | var startCh; 22 | for (var at = start.ch, pass = 0;;) { 23 | var found = at <= 0 ? -1 : lineText.lastIndexOf(startToken, at - 1); 24 | if (found == -1) { 25 | if (pass == 1) return; 26 | pass = 1; 27 | at = lineText.length; 28 | continue; 29 | } 30 | if (pass == 1 && found < start.ch) return; 31 | if (/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1))) && 32 | (found == 0 || lineText.slice(found - endToken.length, found) == endToken || 33 | !/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found))))) { 34 | startCh = found + startToken.length; 35 | break; 36 | } 37 | at = found - 1; 38 | } 39 | 40 | var depth = 1, lastLine = cm.lastLine(), end, endCh; 41 | outer: for (var i = line; i <= lastLine; ++i) { 42 | var text = cm.getLine(i), pos = i == line ? startCh : 0; 43 | for (;;) { 44 | var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos); 45 | if (nextOpen < 0) nextOpen = text.length; 46 | if (nextClose < 0) nextClose = text.length; 47 | pos = Math.min(nextOpen, nextClose); 48 | if (pos == text.length) break; 49 | if (pos == nextOpen) ++depth; 50 | else if (!--depth) { end = i; endCh = pos; break outer; } 51 | ++pos; 52 | } 53 | } 54 | if (end == null || line == end && endCh == startCh) return; 55 | return {from: CodeMirror.Pos(line, startCh), 56 | to: CodeMirror.Pos(end, endCh)}; 57 | }); 58 | 59 | }); 60 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/fold/foldgutter.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-foldmarker { 2 | color: blue; 3 | text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px; 4 | font-family: arial; 5 | line-height: .3; 6 | cursor: pointer; 7 | } 8 | .CodeMirror-foldgutter { 9 | width: .7em; 10 | } 11 | .CodeMirror-foldgutter-open, 12 | .CodeMirror-foldgutter-folded { 13 | cursor: pointer; 14 | } 15 | .CodeMirror-foldgutter-open:after { 16 | content: "\25BE"; 17 | } 18 | .CodeMirror-foldgutter-folded:after { 19 | content: "\25B8"; 20 | } 21 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/fold/indent-fold.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | function lineIndent(cm, lineNo) { 15 | var text = cm.getLine(lineNo) 16 | var spaceTo = text.search(/\S/) 17 | if (spaceTo == -1 || /\bcomment\b/.test(cm.getTokenTypeAt(CodeMirror.Pos(lineNo, spaceTo + 1)))) 18 | return -1 19 | return CodeMirror.countColumn(text, null, cm.getOption("tabSize")) 20 | } 21 | 22 | CodeMirror.registerHelper("fold", "indent", function(cm, start) { 23 | var myIndent = lineIndent(cm, start.line) 24 | if (myIndent < 0) return 25 | var lastLineInFold = null 26 | 27 | // Go through lines until we find a line that definitely doesn't belong in 28 | // the block we're folding, or to the end. 29 | for (var i = start.line + 1, end = cm.lastLine(); i <= end; ++i) { 30 | var indent = lineIndent(cm, i) 31 | if (indent == -1) { 32 | } else if (indent > myIndent) { 33 | // Lines with a greater indent are considered part of the block. 34 | lastLineInFold = i; 35 | } else { 36 | // If this line has non-space, non-comment content, and is 37 | // indented less or equal to the start line, it is the start of 38 | // another block. 39 | break; 40 | } 41 | } 42 | if (lastLineInFold) return { 43 | from: CodeMirror.Pos(start.line, cm.getLine(start.line).length), 44 | to: CodeMirror.Pos(lastLineInFold, cm.getLine(lastLineInFold).length) 45 | }; 46 | }); 47 | 48 | }); 49 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/fold/markdown-fold.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.registerHelper("fold", "markdown", function(cm, start) { 15 | var maxDepth = 100; 16 | 17 | function isHeader(lineNo) { 18 | var tokentype = cm.getTokenTypeAt(CodeMirror.Pos(lineNo, 0)); 19 | return tokentype && /\bheader\b/.test(tokentype); 20 | } 21 | 22 | function headerLevel(lineNo, line, nextLine) { 23 | var match = line && line.match(/^#+/); 24 | if (match && isHeader(lineNo)) return match[0].length; 25 | match = nextLine && nextLine.match(/^[=\-]+\s*$/); 26 | if (match && isHeader(lineNo + 1)) return nextLine[0] == "=" ? 1 : 2; 27 | return maxDepth; 28 | } 29 | 30 | var firstLine = cm.getLine(start.line), nextLine = cm.getLine(start.line + 1); 31 | var level = headerLevel(start.line, firstLine, nextLine); 32 | if (level === maxDepth) return undefined; 33 | 34 | var lastLineNo = cm.lastLine(); 35 | var end = start.line, nextNextLine = cm.getLine(end + 2); 36 | while (end < lastLineNo) { 37 | if (headerLevel(end + 1, nextLine, nextNextLine) <= level) break; 38 | ++end; 39 | nextLine = nextNextLine; 40 | nextNextLine = cm.getLine(end + 2); 41 | } 42 | 43 | return { 44 | from: CodeMirror.Pos(start.line, firstLine.length), 45 | to: CodeMirror.Pos(end, cm.getLine(end).length) 46 | }; 47 | }); 48 | 49 | }); 50 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/hint/anyword-hint.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | var WORD = /[\w$]+/, RANGE = 500; 15 | 16 | CodeMirror.registerHelper("hint", "anyword", function(editor, options) { 17 | var word = options && options.word || WORD; 18 | var range = options && options.range || RANGE; 19 | var cur = editor.getCursor(), curLine = editor.getLine(cur.line); 20 | var end = cur.ch, start = end; 21 | while (start && word.test(curLine.charAt(start - 1))) --start; 22 | var curWord = start != end && curLine.slice(start, end); 23 | 24 | var list = options && options.list || [], seen = {}; 25 | var re = new RegExp(word.source, "g"); 26 | for (var dir = -1; dir <= 1; dir += 2) { 27 | var line = cur.line, endLine = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir; 28 | for (; line != endLine; line += dir) { 29 | var text = editor.getLine(line), m; 30 | while (m = re.exec(text)) { 31 | if (line == cur.line && m[0] === curWord) continue; 32 | if ((!curWord || m[0].lastIndexOf(curWord, 0) == 0) && !Object.prototype.hasOwnProperty.call(seen, m[0])) { 33 | seen[m[0]] = true; 34 | list.push(m[0]); 35 | } 36 | } 37 | } 38 | } 39 | return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)}; 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/hint/css-hint.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror"), require("../../mode/css/css")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror", "../../mode/css/css"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | var pseudoClasses = {"active":1, "after":1, "before":1, "checked":1, "default":1, 15 | "disabled":1, "empty":1, "enabled":1, "first-child":1, "first-letter":1, 16 | "first-line":1, "first-of-type":1, "focus":1, "hover":1, "in-range":1, 17 | "indeterminate":1, "invalid":1, "lang":1, "last-child":1, "last-of-type":1, 18 | "link":1, "not":1, "nth-child":1, "nth-last-child":1, "nth-last-of-type":1, 19 | "nth-of-type":1, "only-of-type":1, "only-child":1, "optional":1, "out-of-range":1, 20 | "placeholder":1, "read-only":1, "read-write":1, "required":1, "root":1, 21 | "selection":1, "target":1, "valid":1, "visited":1 22 | }; 23 | 24 | CodeMirror.registerHelper("hint", "css", function(cm) { 25 | var cur = cm.getCursor(), token = cm.getTokenAt(cur); 26 | var inner = CodeMirror.innerMode(cm.getMode(), token.state); 27 | if (inner.mode.name != "css") return; 28 | 29 | if (token.type == "keyword" && "!important".indexOf(token.string) == 0) 30 | return {list: ["!important"], from: CodeMirror.Pos(cur.line, token.start), 31 | to: CodeMirror.Pos(cur.line, token.end)}; 32 | 33 | var start = token.start, end = cur.ch, word = token.string.slice(0, end - start); 34 | if (/[^\w$_-]/.test(word)) { 35 | word = ""; start = end = cur.ch; 36 | } 37 | 38 | var spec = CodeMirror.resolveMode("text/css"); 39 | 40 | var result = []; 41 | function add(keywords) { 42 | for (var name in keywords) 43 | if (!word || name.lastIndexOf(word, 0) == 0) 44 | result.push(name); 45 | } 46 | 47 | var st = inner.state.state; 48 | if (st == "pseudo" || token.type == "variable-3") { 49 | add(pseudoClasses); 50 | } else if (st == "block" || st == "maybeprop") { 51 | add(spec.propertyKeywords); 52 | } else if (st == "prop" || st == "parens" || st == "at" || st == "params") { 53 | add(spec.valueKeywords); 54 | add(spec.colorKeywords); 55 | } else if (st == "media" || st == "media_parens") { 56 | add(spec.mediaTypes); 57 | add(spec.mediaFeatures); 58 | } 59 | 60 | if (result.length) return { 61 | list: result, 62 | from: CodeMirror.Pos(cur.line, start), 63 | to: CodeMirror.Pos(cur.line, end) 64 | }; 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/hint/show-hint.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-hints { 2 | position: absolute; 3 | z-index: 10; 4 | overflow: hidden; 5 | list-style: none; 6 | 7 | margin: 0; 8 | padding: 2px; 9 | 10 | -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 11 | -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 12 | box-shadow: 2px 3px 5px rgba(0,0,0,.2); 13 | border-radius: 3px; 14 | border: 1px solid silver; 15 | 16 | background: white; 17 | font-size: 90%; 18 | font-family: monospace; 19 | 20 | max-height: 20em; 21 | overflow-y: auto; 22 | } 23 | 24 | .CodeMirror-hint { 25 | margin: 0; 26 | padding: 0 4px; 27 | border-radius: 2px; 28 | white-space: pre; 29 | color: black; 30 | cursor: pointer; 31 | } 32 | 33 | li.CodeMirror-hint-active { 34 | background: #08f; 35 | color: white; 36 | } 37 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/lint/coffeescript-lint.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | // Depends on coffeelint.js from http://www.coffeelint.org/js/coffeelint.js 5 | 6 | // declare global: coffeelint 7 | 8 | (function(mod) { 9 | if (typeof exports == "object" && typeof module == "object") // CommonJS 10 | mod(require("../../lib/codemirror")); 11 | else if (typeof define == "function" && define.amd) // AMD 12 | define(["../../lib/codemirror"], mod); 13 | else // Plain browser env 14 | mod(CodeMirror); 15 | })(function(CodeMirror) { 16 | "use strict"; 17 | 18 | CodeMirror.registerHelper("lint", "coffeescript", function(text) { 19 | var found = []; 20 | if (!window.coffeelint) { 21 | if (window.console) { 22 | window.console.error("Error: window.coffeelint not defined, CodeMirror CoffeeScript linting cannot run."); 23 | } 24 | return found; 25 | } 26 | var parseError = function(err) { 27 | var loc = err.lineNumber; 28 | found.push({from: CodeMirror.Pos(loc-1, 0), 29 | to: CodeMirror.Pos(loc, 0), 30 | severity: err.level, 31 | message: err.message}); 32 | }; 33 | try { 34 | var res = coffeelint.lint(text); 35 | for(var i = 0; i < res.length; i++) { 36 | parseError(res[i]); 37 | } 38 | } catch(e) { 39 | found.push({from: CodeMirror.Pos(e.location.first_line, 0), 40 | to: CodeMirror.Pos(e.location.last_line, e.location.last_column), 41 | severity: 'error', 42 | message: e.message}); 43 | } 44 | return found; 45 | }); 46 | 47 | }); 48 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/lint/css-lint.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | // Depends on csslint.js from https://github.com/stubbornella/csslint 5 | 6 | // declare global: CSSLint 7 | 8 | (function(mod) { 9 | if (typeof exports == "object" && typeof module == "object") // CommonJS 10 | mod(require("../../lib/codemirror")); 11 | else if (typeof define == "function" && define.amd) // AMD 12 | define(["../../lib/codemirror"], mod); 13 | else // Plain browser env 14 | mod(CodeMirror); 15 | })(function(CodeMirror) { 16 | "use strict"; 17 | 18 | CodeMirror.registerHelper("lint", "css", function(text, options) { 19 | var found = []; 20 | if (!window.CSSLint) { 21 | if (window.console) { 22 | window.console.error("Error: window.CSSLint not defined, CodeMirror CSS linting cannot run."); 23 | } 24 | return found; 25 | } 26 | var results = CSSLint.verify(text, options), messages = results.messages, message = null; 27 | for ( var i = 0; i < messages.length; i++) { 28 | message = messages[i]; 29 | var startLine = message.line -1, endLine = message.line -1, startCol = message.col -1, endCol = message.col; 30 | found.push({ 31 | from: CodeMirror.Pos(startLine, startCol), 32 | to: CodeMirror.Pos(endLine, endCol), 33 | message: message.message, 34 | severity : message.type 35 | }); 36 | } 37 | return found; 38 | }); 39 | 40 | }); 41 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/lint/html-lint.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | // Depends on htmlhint.js from http://htmlhint.com/js/htmlhint.js 5 | 6 | // declare global: HTMLHint 7 | 8 | (function(mod) { 9 | if (typeof exports == "object" && typeof module == "object") // CommonJS 10 | mod(require("../../lib/codemirror"), require("htmlhint")); 11 | else if (typeof define == "function" && define.amd) // AMD 12 | define(["../../lib/codemirror", "htmlhint"], mod); 13 | else // Plain browser env 14 | mod(CodeMirror, window.HTMLHint); 15 | })(function(CodeMirror, HTMLHint) { 16 | "use strict"; 17 | 18 | var defaultRules = { 19 | "tagname-lowercase": true, 20 | "attr-lowercase": true, 21 | "attr-value-double-quotes": true, 22 | "doctype-first": false, 23 | "tag-pair": true, 24 | "spec-char-escape": true, 25 | "id-unique": true, 26 | "src-not-empty": true, 27 | "attr-no-duplication": true 28 | }; 29 | 30 | CodeMirror.registerHelper("lint", "html", function(text, options) { 31 | var found = []; 32 | if (HTMLHint && !HTMLHint.verify) { 33 | if(typeof HTMLHint.default !== 'undefined') { 34 | HTMLHint = HTMLHint.default; 35 | } else { 36 | HTMLHint = HTMLHint.HTMLHint; 37 | } 38 | } 39 | if (!HTMLHint) HTMLHint = window.HTMLHint; 40 | if (!HTMLHint) { 41 | if (window.console) { 42 | window.console.error("Error: HTMLHint not found, not defined on window, or not available through define/require, CodeMirror HTML linting cannot run."); 43 | } 44 | return found; 45 | } 46 | var messages = HTMLHint.verify(text, options && options.rules || defaultRules); 47 | for (var i = 0; i < messages.length; i++) { 48 | var message = messages[i]; 49 | var startLine = message.line - 1, endLine = message.line - 1, startCol = message.col - 1, endCol = message.col; 50 | found.push({ 51 | from: CodeMirror.Pos(startLine, startCol), 52 | to: CodeMirror.Pos(endLine, endCol), 53 | message: message.message, 54 | severity : message.type 55 | }); 56 | } 57 | return found; 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/lint/javascript-lint.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | // declare global: JSHINT 14 | 15 | function validator(text, options) { 16 | if (!window.JSHINT) { 17 | if (window.console) { 18 | window.console.error("Error: window.JSHINT not defined, CodeMirror JavaScript linting cannot run."); 19 | } 20 | return []; 21 | } 22 | if (!options.indent) // JSHint error.character actually is a column index, this fixes underlining on lines using tabs for indentation 23 | options.indent = 1; // JSHint default value is 4 24 | JSHINT(text, options, options.globals); 25 | var errors = JSHINT.data().errors, result = []; 26 | if (errors) parseErrors(errors, result); 27 | return result; 28 | } 29 | 30 | CodeMirror.registerHelper("lint", "javascript", validator); 31 | 32 | function parseErrors(errors, output) { 33 | for ( var i = 0; i < errors.length; i++) { 34 | var error = errors[i]; 35 | if (error) { 36 | if (error.line <= 0) { 37 | if (window.console) { 38 | window.console.warn("Cannot display JSHint error (invalid line " + error.line + ")", error); 39 | } 40 | continue; 41 | } 42 | 43 | var start = error.character - 1, end = start + 1; 44 | if (error.evidence) { 45 | var index = error.evidence.substring(start).search(/.\b/); 46 | if (index > -1) { 47 | end += index; 48 | } 49 | } 50 | 51 | // Convert to format expected by validation service 52 | var hint = { 53 | message: error.reason, 54 | severity: error.code ? (error.code.startsWith('W') ? "warning" : "error") : "error", 55 | from: CodeMirror.Pos(error.line - 1, start), 56 | to: CodeMirror.Pos(error.line - 1, end) 57 | }; 58 | 59 | output.push(hint); 60 | } 61 | } 62 | } 63 | }); 64 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/lint/json-lint.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | // Depends on jsonlint.js from https://github.com/zaach/jsonlint 5 | 6 | // declare global: jsonlint 7 | 8 | (function(mod) { 9 | if (typeof exports == "object" && typeof module == "object") // CommonJS 10 | mod(require("../../lib/codemirror")); 11 | else if (typeof define == "function" && define.amd) // AMD 12 | define(["../../lib/codemirror"], mod); 13 | else // Plain browser env 14 | mod(CodeMirror); 15 | })(function(CodeMirror) { 16 | "use strict"; 17 | 18 | CodeMirror.registerHelper("lint", "json", function(text) { 19 | var found = []; 20 | if (!window.jsonlint) { 21 | if (window.console) { 22 | window.console.error("Error: window.jsonlint not defined, CodeMirror JSON linting cannot run."); 23 | } 24 | return found; 25 | } 26 | // for jsonlint's web dist jsonlint is exported as an object with a single property parser, of which parseError 27 | // is a subproperty 28 | var jsonlint = window.jsonlint.parser || window.jsonlint 29 | jsonlint.parseError = function(str, hash) { 30 | var loc = hash.loc; 31 | found.push({from: CodeMirror.Pos(loc.first_line - 1, loc.first_column), 32 | to: CodeMirror.Pos(loc.last_line - 1, loc.last_column), 33 | message: str}); 34 | }; 35 | try { jsonlint.parse(text); } 36 | catch(e) {} 37 | return found; 38 | }); 39 | 40 | }); 41 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/lint/lint.css: -------------------------------------------------------------------------------- 1 | /* The lint marker gutter */ 2 | .CodeMirror-lint-markers { 3 | width: 16px; 4 | } 5 | 6 | .CodeMirror-lint-tooltip { 7 | background-color: #ffd; 8 | border: 1px solid black; 9 | border-radius: 4px 4px 4px 4px; 10 | color: black; 11 | font-family: monospace; 12 | font-size: 10pt; 13 | overflow: hidden; 14 | padding: 2px 5px; 15 | position: fixed; 16 | white-space: pre; 17 | white-space: pre-wrap; 18 | z-index: 100; 19 | max-width: 600px; 20 | opacity: 0; 21 | transition: opacity .4s; 22 | -moz-transition: opacity .4s; 23 | -webkit-transition: opacity .4s; 24 | -o-transition: opacity .4s; 25 | -ms-transition: opacity .4s; 26 | } 27 | 28 | .CodeMirror-lint-mark-error, .CodeMirror-lint-mark-warning { 29 | background-position: left bottom; 30 | background-repeat: repeat-x; 31 | } 32 | 33 | .CodeMirror-lint-mark-error { 34 | background-image: 35 | url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==") 36 | ; 37 | } 38 | 39 | .CodeMirror-lint-mark-warning { 40 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII="); 41 | } 42 | 43 | .CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning { 44 | background-position: center center; 45 | background-repeat: no-repeat; 46 | cursor: pointer; 47 | display: inline-block; 48 | height: 16px; 49 | width: 16px; 50 | vertical-align: middle; 51 | position: relative; 52 | } 53 | 54 | .CodeMirror-lint-message-error, .CodeMirror-lint-message-warning { 55 | padding-left: 18px; 56 | background-position: top left; 57 | background-repeat: no-repeat; 58 | } 59 | 60 | .CodeMirror-lint-marker-error, .CodeMirror-lint-message-error { 61 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII="); 62 | } 63 | 64 | .CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning { 65 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII="); 66 | } 67 | 68 | .CodeMirror-lint-marker-multiple { 69 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC"); 70 | background-repeat: no-repeat; 71 | background-position: right bottom; 72 | width: 100%; height: 100%; 73 | } 74 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/lint/yaml-lint.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | // Depends on js-yaml.js from https://github.com/nodeca/js-yaml 15 | 16 | // declare global: jsyaml 17 | 18 | CodeMirror.registerHelper("lint", "yaml", function(text) { 19 | var found = []; 20 | if (!window.jsyaml) { 21 | if (window.console) { 22 | window.console.error("Error: window.jsyaml not defined, CodeMirror YAML linting cannot run."); 23 | } 24 | return found; 25 | } 26 | try { jsyaml.loadAll(text); } 27 | catch(e) { 28 | var loc = e.mark, 29 | // js-yaml YAMLException doesn't always provide an accurate lineno 30 | // e.g., when there are multiple yaml docs 31 | // --- 32 | // --- 33 | // foo:bar 34 | from = loc ? CodeMirror.Pos(loc.line, loc.column) : CodeMirror.Pos(0, 0), 35 | to = from; 36 | found.push({ from: from, to: to, message: e.message }); 37 | } 38 | return found; 39 | }); 40 | 41 | }); 42 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/merge/merge.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-merge { 2 | position: relative; 3 | border: 1px solid #ddd; 4 | white-space: pre; 5 | } 6 | 7 | .CodeMirror-merge, .CodeMirror-merge .CodeMirror { 8 | height: 350px; 9 | } 10 | 11 | .CodeMirror-merge-2pane .CodeMirror-merge-pane { width: 47%; } 12 | .CodeMirror-merge-2pane .CodeMirror-merge-gap { width: 6%; } 13 | .CodeMirror-merge-3pane .CodeMirror-merge-pane { width: 31%; } 14 | .CodeMirror-merge-3pane .CodeMirror-merge-gap { width: 3.5%; } 15 | 16 | .CodeMirror-merge-pane { 17 | display: inline-block; 18 | white-space: normal; 19 | vertical-align: top; 20 | } 21 | .CodeMirror-merge-pane-rightmost { 22 | position: absolute; 23 | right: 0px; 24 | z-index: 1; 25 | } 26 | 27 | .CodeMirror-merge-gap { 28 | z-index: 2; 29 | display: inline-block; 30 | height: 100%; 31 | -moz-box-sizing: border-box; 32 | box-sizing: border-box; 33 | overflow: hidden; 34 | border-left: 1px solid #ddd; 35 | border-right: 1px solid #ddd; 36 | position: relative; 37 | background: #f8f8f8; 38 | } 39 | 40 | .CodeMirror-merge-scrolllock-wrap { 41 | position: absolute; 42 | bottom: 0; left: 50%; 43 | } 44 | .CodeMirror-merge-scrolllock { 45 | position: relative; 46 | left: -50%; 47 | cursor: pointer; 48 | color: #555; 49 | line-height: 1; 50 | } 51 | .CodeMirror-merge-scrolllock:after { 52 | content: "\21db\00a0\00a0\21da"; 53 | } 54 | .CodeMirror-merge-scrolllock.CodeMirror-merge-scrolllock-enabled:after { 55 | content: "\21db\21da"; 56 | } 57 | 58 | .CodeMirror-merge-copybuttons-left, .CodeMirror-merge-copybuttons-right { 59 | position: absolute; 60 | left: 0; top: 0; 61 | right: 0; bottom: 0; 62 | line-height: 1; 63 | } 64 | 65 | .CodeMirror-merge-copy { 66 | position: absolute; 67 | cursor: pointer; 68 | color: #44c; 69 | z-index: 3; 70 | } 71 | 72 | .CodeMirror-merge-copy-reverse { 73 | position: absolute; 74 | cursor: pointer; 75 | color: #44c; 76 | } 77 | 78 | .CodeMirror-merge-copybuttons-left .CodeMirror-merge-copy { left: 2px; } 79 | .CodeMirror-merge-copybuttons-right .CodeMirror-merge-copy { right: 2px; } 80 | 81 | .CodeMirror-merge-r-inserted, .CodeMirror-merge-l-inserted { 82 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12MwuCXy3+CWyH8GBgYGJgYkAABZbAQ9ELXurwAAAABJRU5ErkJggg==); 83 | background-position: bottom left; 84 | background-repeat: repeat-x; 85 | } 86 | 87 | .CodeMirror-merge-r-deleted, .CodeMirror-merge-l-deleted { 88 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12M4Kyb2/6yY2H8GBgYGJgYkAABURgPz6Ks7wQAAAABJRU5ErkJggg==); 89 | background-position: bottom left; 90 | background-repeat: repeat-x; 91 | } 92 | 93 | .CodeMirror-merge-r-chunk { background: #ffffe0; } 94 | .CodeMirror-merge-r-chunk-start { border-top: 1px solid #ee8; } 95 | .CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #ee8; } 96 | .CodeMirror-merge-r-connect { fill: #ffffe0; stroke: #ee8; stroke-width: 1px; } 97 | 98 | .CodeMirror-merge-l-chunk { background: #eef; } 99 | .CodeMirror-merge-l-chunk-start { border-top: 1px solid #88e; } 100 | .CodeMirror-merge-l-chunk-end { border-bottom: 1px solid #88e; } 101 | .CodeMirror-merge-l-connect { fill: #eef; stroke: #88e; stroke-width: 1px; } 102 | 103 | .CodeMirror-merge-l-chunk.CodeMirror-merge-r-chunk { background: #dfd; } 104 | .CodeMirror-merge-l-chunk-start.CodeMirror-merge-r-chunk-start { border-top: 1px solid #4e4; } 105 | .CodeMirror-merge-l-chunk-end.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #4e4; } 106 | 107 | .CodeMirror-merge-collapsed-widget:before { 108 | content: "(...)"; 109 | } 110 | .CodeMirror-merge-collapsed-widget { 111 | cursor: pointer; 112 | color: #88b; 113 | background: #eef; 114 | border: 1px solid #ddf; 115 | font-size: 90%; 116 | padding: 0 3px; 117 | border-radius: 4px; 118 | } 119 | .CodeMirror-merge-collapsed-line .CodeMirror-gutter-elt { display: none; } 120 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/mode/loadmode.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror"), "cjs"); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], function(CM) { mod(CM, "amd"); }); 9 | else // Plain browser env 10 | mod(CodeMirror, "plain"); 11 | })(function(CodeMirror, env) { 12 | if (!CodeMirror.modeURL) CodeMirror.modeURL = "../mode/%N/%N.js"; 13 | 14 | var loading = {}; 15 | function splitCallback(cont, n) { 16 | var countDown = n; 17 | return function() { if (--countDown == 0) cont(); }; 18 | } 19 | function ensureDeps(mode, cont, options) { 20 | var modeObj = CodeMirror.modes[mode], deps = modeObj && modeObj.dependencies; 21 | if (!deps) return cont(); 22 | var missing = []; 23 | for (var i = 0; i < deps.length; ++i) { 24 | if (!CodeMirror.modes.hasOwnProperty(deps[i])) 25 | missing.push(deps[i]); 26 | } 27 | if (!missing.length) return cont(); 28 | var split = splitCallback(cont, missing.length); 29 | for (var i = 0; i < missing.length; ++i) 30 | CodeMirror.requireMode(missing[i], split, options); 31 | } 32 | 33 | CodeMirror.requireMode = function(mode, cont, options) { 34 | if (typeof mode != "string") mode = mode.name; 35 | if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont, options); 36 | if (loading.hasOwnProperty(mode)) return loading[mode].push(cont); 37 | 38 | var file = options && options.path ? options.path(mode) : CodeMirror.modeURL.replace(/%N/g, mode); 39 | if (options && options.loadMode) { 40 | options.loadMode(file, function() { ensureDeps(mode, cont, options) }) 41 | } else if (env == "plain") { 42 | var script = document.createElement("script"); 43 | script.src = file; 44 | var others = document.getElementsByTagName("script")[0]; 45 | var list = loading[mode] = [cont]; 46 | CodeMirror.on(script, "load", function() { 47 | ensureDeps(mode, function() { 48 | for (var i = 0; i < list.length; ++i) list[i](); 49 | }, options); 50 | }); 51 | others.parentNode.insertBefore(script, others); 52 | } else if (env == "cjs") { 53 | require(file); 54 | cont(); 55 | } else if (env == "amd") { 56 | requirejs([file], cont); 57 | } 58 | }; 59 | 60 | CodeMirror.autoLoadMode = function(instance, mode, options) { 61 | if (!CodeMirror.modes.hasOwnProperty(mode)) 62 | CodeMirror.requireMode(mode, function() { 63 | instance.setOption("mode", instance.getOption("mode")); 64 | }, options); 65 | }; 66 | }); 67 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/mode/multiplex.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.multiplexingMode = function(outer /*, others */) { 15 | // Others should be {open, close, mode [, delimStyle] [, innerStyle]} objects 16 | var others = Array.prototype.slice.call(arguments, 1); 17 | 18 | function indexOf(string, pattern, from, returnEnd) { 19 | if (typeof pattern == "string") { 20 | var found = string.indexOf(pattern, from); 21 | return returnEnd && found > -1 ? found + pattern.length : found; 22 | } 23 | var m = pattern.exec(from ? string.slice(from) : string); 24 | return m ? m.index + from + (returnEnd ? m[0].length : 0) : -1; 25 | } 26 | 27 | return { 28 | startState: function() { 29 | return { 30 | outer: CodeMirror.startState(outer), 31 | innerActive: null, 32 | inner: null 33 | }; 34 | }, 35 | 36 | copyState: function(state) { 37 | return { 38 | outer: CodeMirror.copyState(outer, state.outer), 39 | innerActive: state.innerActive, 40 | inner: state.innerActive && CodeMirror.copyState(state.innerActive.mode, state.inner) 41 | }; 42 | }, 43 | 44 | token: function(stream, state) { 45 | if (!state.innerActive) { 46 | var cutOff = Infinity, oldContent = stream.string; 47 | for (var i = 0; i < others.length; ++i) { 48 | var other = others[i]; 49 | var found = indexOf(oldContent, other.open, stream.pos); 50 | if (found == stream.pos) { 51 | if (!other.parseDelimiters) stream.match(other.open); 52 | state.innerActive = other; 53 | 54 | // Get the outer indent, making sure to handle CodeMirror.Pass 55 | var outerIndent = 0; 56 | if (outer.indent) { 57 | var possibleOuterIndent = outer.indent(state.outer, "", ""); 58 | if (possibleOuterIndent !== CodeMirror.Pass) outerIndent = possibleOuterIndent; 59 | } 60 | 61 | state.inner = CodeMirror.startState(other.mode, outerIndent); 62 | return other.delimStyle && (other.delimStyle + " " + other.delimStyle + "-open"); 63 | } else if (found != -1 && found < cutOff) { 64 | cutOff = found; 65 | } 66 | } 67 | if (cutOff != Infinity) stream.string = oldContent.slice(0, cutOff); 68 | var outerToken = outer.token(stream, state.outer); 69 | if (cutOff != Infinity) stream.string = oldContent; 70 | return outerToken; 71 | } else { 72 | var curInner = state.innerActive, oldContent = stream.string; 73 | if (!curInner.close && stream.sol()) { 74 | state.innerActive = state.inner = null; 75 | return this.token(stream, state); 76 | } 77 | var found = curInner.close ? indexOf(oldContent, curInner.close, stream.pos, curInner.parseDelimiters) : -1; 78 | if (found == stream.pos && !curInner.parseDelimiters) { 79 | stream.match(curInner.close); 80 | state.innerActive = state.inner = null; 81 | return curInner.delimStyle && (curInner.delimStyle + " " + curInner.delimStyle + "-close"); 82 | } 83 | if (found > -1) stream.string = oldContent.slice(0, found); 84 | var innerToken = curInner.mode.token(stream, state.inner); 85 | if (found > -1) stream.string = oldContent; 86 | 87 | if (found == stream.pos && curInner.parseDelimiters) 88 | state.innerActive = state.inner = null; 89 | 90 | if (curInner.innerStyle) { 91 | if (innerToken) innerToken = innerToken + " " + curInner.innerStyle; 92 | else innerToken = curInner.innerStyle; 93 | } 94 | 95 | return innerToken; 96 | } 97 | }, 98 | 99 | indent: function(state, textAfter, line) { 100 | var mode = state.innerActive ? state.innerActive.mode : outer; 101 | if (!mode.indent) return CodeMirror.Pass; 102 | return mode.indent(state.innerActive ? state.inner : state.outer, textAfter, line); 103 | }, 104 | 105 | blankLine: function(state) { 106 | var mode = state.innerActive ? state.innerActive.mode : outer; 107 | if (mode.blankLine) { 108 | mode.blankLine(state.innerActive ? state.inner : state.outer); 109 | } 110 | if (!state.innerActive) { 111 | for (var i = 0; i < others.length; ++i) { 112 | var other = others[i]; 113 | if (other.open === "\n") { 114 | state.innerActive = other; 115 | state.inner = CodeMirror.startState(other.mode, mode.indent ? mode.indent(state.outer, "", "") : 0); 116 | } 117 | } 118 | } else if (state.innerActive.close === "\n") { 119 | state.innerActive = state.inner = null; 120 | } 121 | }, 122 | 123 | electricChars: outer.electricChars, 124 | 125 | innerMode: function(state) { 126 | return state.inner ? {state: state.inner, mode: state.innerActive.mode} : {state: state.outer, mode: outer}; 127 | } 128 | }; 129 | }; 130 | 131 | }); 132 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/mode/multiplex_test.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function() { 5 | CodeMirror.defineMode("markdown_with_stex", function(){ 6 | var inner = CodeMirror.getMode({}, "stex"); 7 | var outer = CodeMirror.getMode({}, "markdown"); 8 | 9 | var innerOptions = { 10 | open: '$', 11 | close: '$', 12 | mode: inner, 13 | delimStyle: 'delim', 14 | innerStyle: 'inner' 15 | }; 16 | 17 | return CodeMirror.multiplexingMode(outer, innerOptions); 18 | }); 19 | 20 | var mode = CodeMirror.getMode({}, "markdown_with_stex"); 21 | 22 | function MT(name) { 23 | test.mode( 24 | name, 25 | mode, 26 | Array.prototype.slice.call(arguments, 1), 27 | 'multiplexing'); 28 | } 29 | 30 | MT( 31 | "stexInsideMarkdown", 32 | "[strong **Equation:**] [delim&delim-open $][inner&tag \\pi][delim&delim-close $]"); 33 | })(); 34 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/mode/overlay.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | // Utility function that allows modes to be combined. The mode given 5 | // as the base argument takes care of most of the normal mode 6 | // functionality, but a second (typically simple) mode is used, which 7 | // can override the style of text. Both modes get to parse all of the 8 | // text, but when both assign a non-null style to a piece of code, the 9 | // overlay wins, unless the combine argument was true and not overridden, 10 | // or state.overlay.combineTokens was true, in which case the styles are 11 | // combined. 12 | 13 | (function(mod) { 14 | if (typeof exports == "object" && typeof module == "object") // CommonJS 15 | mod(require("../../lib/codemirror")); 16 | else if (typeof define == "function" && define.amd) // AMD 17 | define(["../../lib/codemirror"], mod); 18 | else // Plain browser env 19 | mod(CodeMirror); 20 | })(function(CodeMirror) { 21 | "use strict"; 22 | 23 | CodeMirror.overlayMode = function(base, overlay, combine) { 24 | return { 25 | startState: function() { 26 | return { 27 | base: CodeMirror.startState(base), 28 | overlay: CodeMirror.startState(overlay), 29 | basePos: 0, baseCur: null, 30 | overlayPos: 0, overlayCur: null, 31 | streamSeen: null 32 | }; 33 | }, 34 | copyState: function(state) { 35 | return { 36 | base: CodeMirror.copyState(base, state.base), 37 | overlay: CodeMirror.copyState(overlay, state.overlay), 38 | basePos: state.basePos, baseCur: null, 39 | overlayPos: state.overlayPos, overlayCur: null 40 | }; 41 | }, 42 | 43 | token: function(stream, state) { 44 | if (stream != state.streamSeen || 45 | Math.min(state.basePos, state.overlayPos) < stream.start) { 46 | state.streamSeen = stream; 47 | state.basePos = state.overlayPos = stream.start; 48 | } 49 | 50 | if (stream.start == state.basePos) { 51 | state.baseCur = base.token(stream, state.base); 52 | state.basePos = stream.pos; 53 | } 54 | if (stream.start == state.overlayPos) { 55 | stream.pos = stream.start; 56 | state.overlayCur = overlay.token(stream, state.overlay); 57 | state.overlayPos = stream.pos; 58 | } 59 | stream.pos = Math.min(state.basePos, state.overlayPos); 60 | 61 | // state.overlay.combineTokens always takes precedence over combine, 62 | // unless set to null 63 | if (state.overlayCur == null) return state.baseCur; 64 | else if (state.baseCur != null && 65 | state.overlay.combineTokens || 66 | combine && state.overlay.combineTokens == null) 67 | return state.baseCur + " " + state.overlayCur; 68 | else return state.overlayCur; 69 | }, 70 | 71 | indent: base.indent && function(state, textAfter, line) { 72 | return base.indent(state.base, textAfter, line); 73 | }, 74 | electricChars: base.electricChars, 75 | 76 | innerMode: function(state) { return {state: state.base, mode: base}; }, 77 | 78 | blankLine: function(state) { 79 | var baseToken, overlayToken; 80 | if (base.blankLine) baseToken = base.blankLine(state.base); 81 | if (overlay.blankLine) overlayToken = overlay.blankLine(state.overlay); 82 | 83 | return overlayToken == null ? 84 | baseToken : 85 | (combine && baseToken != null ? baseToken + " " + overlayToken : overlayToken); 86 | } 87 | }; 88 | }; 89 | 90 | }); 91 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/runmode/colorize.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror"), require("./runmode")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror", "./runmode"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | var isBlock = /^(p|li|div|h\\d|pre|blockquote|td)$/; 15 | 16 | function textContent(node, out) { 17 | if (node.nodeType == 3) return out.push(node.nodeValue); 18 | for (var ch = node.firstChild; ch; ch = ch.nextSibling) { 19 | textContent(ch, out); 20 | if (isBlock.test(node.nodeType)) out.push("\n"); 21 | } 22 | } 23 | 24 | CodeMirror.colorize = function(collection, defaultMode) { 25 | if (!collection) collection = document.body.getElementsByTagName("pre"); 26 | 27 | for (var i = 0; i < collection.length; ++i) { 28 | var node = collection[i]; 29 | var mode = node.getAttribute("data-lang") || defaultMode; 30 | if (!mode) continue; 31 | 32 | var text = []; 33 | textContent(node, text); 34 | node.innerHTML = ""; 35 | CodeMirror.runMode(text.join(""), mode, node); 36 | 37 | node.className += " cm-s-default"; 38 | } 39 | }; 40 | }); 41 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/runmode/runmode.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.runMode = function(string, modespec, callback, options) { 15 | var mode = CodeMirror.getMode(CodeMirror.defaults, modespec); 16 | var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize; 17 | 18 | // Create a tokenizing callback function if passed-in callback is a DOM element. 19 | if (callback.appendChild) { 20 | var ie = /MSIE \d/.test(navigator.userAgent); 21 | var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9); 22 | var node = callback, col = 0; 23 | node.innerHTML = ""; 24 | callback = function(text, style) { 25 | if (text == "\n") { 26 | // Emitting LF or CRLF on IE8 or earlier results in an incorrect display. 27 | // Emitting a carriage return makes everything ok. 28 | node.appendChild(document.createTextNode(ie_lt9 ? '\r' : text)); 29 | col = 0; 30 | return; 31 | } 32 | var content = ""; 33 | // replace tabs 34 | for (var pos = 0;;) { 35 | var idx = text.indexOf("\t", pos); 36 | if (idx == -1) { 37 | content += text.slice(pos); 38 | col += text.length - pos; 39 | break; 40 | } else { 41 | col += idx - pos; 42 | content += text.slice(pos, idx); 43 | var size = tabSize - col % tabSize; 44 | col += size; 45 | for (var i = 0; i < size; ++i) content += " "; 46 | pos = idx + 1; 47 | } 48 | } 49 | // Create a node with token style and append it to the callback DOM element. 50 | if (style) { 51 | var sp = node.appendChild(document.createElement("span")); 52 | sp.className = "cm-" + style.replace(/ +/g, " cm-"); 53 | sp.appendChild(document.createTextNode(content)); 54 | } else { 55 | node.appendChild(document.createTextNode(content)); 56 | } 57 | }; 58 | } 59 | 60 | var lines = CodeMirror.splitLines(string), state = (options && options.state) || CodeMirror.startState(mode); 61 | for (var i = 0, e = lines.length; i < e; ++i) { 62 | if (i) callback("\n"); 63 | var stream = new CodeMirror.StringStream(lines[i], null, { 64 | lookAhead: function(n) { return lines[i + n] }, 65 | baseToken: function() {} 66 | }); 67 | if (!stream.string && mode.blankLine) mode.blankLine(state); 68 | while (!stream.eol()) { 69 | var style = mode.token(stream, state); 70 | callback(stream.current(), style, i, stream.start, state); 71 | stream.start = stream.pos; 72 | } 73 | } 74 | }; 75 | 76 | }); 77 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/scroll/annotatescrollbar.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.defineExtension("annotateScrollbar", function(options) { 15 | if (typeof options == "string") options = {className: options}; 16 | return new Annotation(this, options); 17 | }); 18 | 19 | CodeMirror.defineOption("scrollButtonHeight", 0); 20 | 21 | function Annotation(cm, options) { 22 | this.cm = cm; 23 | this.options = options; 24 | this.buttonHeight = options.scrollButtonHeight || cm.getOption("scrollButtonHeight"); 25 | this.annotations = []; 26 | this.doRedraw = this.doUpdate = null; 27 | this.div = cm.getWrapperElement().appendChild(document.createElement("div")); 28 | this.div.style.cssText = "position: absolute; right: 0; top: 0; z-index: 7; pointer-events: none"; 29 | this.computeScale(); 30 | 31 | function scheduleRedraw(delay) { 32 | clearTimeout(self.doRedraw); 33 | self.doRedraw = setTimeout(function() { self.redraw(); }, delay); 34 | } 35 | 36 | var self = this; 37 | cm.on("refresh", this.resizeHandler = function() { 38 | clearTimeout(self.doUpdate); 39 | self.doUpdate = setTimeout(function() { 40 | if (self.computeScale()) scheduleRedraw(20); 41 | }, 100); 42 | }); 43 | cm.on("markerAdded", this.resizeHandler); 44 | cm.on("markerCleared", this.resizeHandler); 45 | if (options.listenForChanges !== false) 46 | cm.on("changes", this.changeHandler = function() { 47 | scheduleRedraw(250); 48 | }); 49 | } 50 | 51 | Annotation.prototype.computeScale = function() { 52 | var cm = this.cm; 53 | var hScale = (cm.getWrapperElement().clientHeight - cm.display.barHeight - this.buttonHeight * 2) / 54 | cm.getScrollerElement().scrollHeight 55 | if (hScale != this.hScale) { 56 | this.hScale = hScale; 57 | return true; 58 | } 59 | }; 60 | 61 | Annotation.prototype.update = function(annotations) { 62 | this.annotations = annotations; 63 | this.redraw(); 64 | }; 65 | 66 | Annotation.prototype.redraw = function(compute) { 67 | if (compute !== false) this.computeScale(); 68 | var cm = this.cm, hScale = this.hScale; 69 | 70 | var frag = document.createDocumentFragment(), anns = this.annotations; 71 | 72 | var wrapping = cm.getOption("lineWrapping"); 73 | var singleLineH = wrapping && cm.defaultTextHeight() * 1.5; 74 | var curLine = null, curLineObj = null; 75 | function getY(pos, top) { 76 | if (curLine != pos.line) { 77 | curLine = pos.line; 78 | curLineObj = cm.getLineHandle(curLine); 79 | } 80 | if ((curLineObj.widgets && curLineObj.widgets.length) || 81 | (wrapping && curLineObj.height > singleLineH)) 82 | return cm.charCoords(pos, "local")[top ? "top" : "bottom"]; 83 | var topY = cm.heightAtLine(curLineObj, "local"); 84 | return topY + (top ? 0 : curLineObj.height); 85 | } 86 | 87 | var lastLine = cm.lastLine() 88 | if (cm.display.barWidth) for (var i = 0, nextTop; i < anns.length; i++) { 89 | var ann = anns[i]; 90 | if (ann.to.line > lastLine) continue; 91 | var top = nextTop || getY(ann.from, true) * hScale; 92 | var bottom = getY(ann.to, false) * hScale; 93 | while (i < anns.length - 1) { 94 | if (anns[i + 1].to.line > lastLine) break; 95 | nextTop = getY(anns[i + 1].from, true) * hScale; 96 | if (nextTop > bottom + .9) break; 97 | ann = anns[++i]; 98 | bottom = getY(ann.to, false) * hScale; 99 | } 100 | if (bottom == top) continue; 101 | var height = Math.max(bottom - top, 3); 102 | 103 | var elt = frag.appendChild(document.createElement("div")); 104 | elt.style.cssText = "position: absolute; right: 0px; width: " + Math.max(cm.display.barWidth - 1, 2) + "px; top: " 105 | + (top + this.buttonHeight) + "px; height: " + height + "px"; 106 | elt.className = this.options.className; 107 | if (ann.id) { 108 | elt.setAttribute("annotation-id", ann.id); 109 | } 110 | } 111 | this.div.textContent = ""; 112 | this.div.appendChild(frag); 113 | }; 114 | 115 | Annotation.prototype.clear = function() { 116 | this.cm.off("refresh", this.resizeHandler); 117 | this.cm.off("markerAdded", this.resizeHandler); 118 | this.cm.off("markerCleared", this.resizeHandler); 119 | if (this.changeHandler) this.cm.off("changes", this.changeHandler); 120 | this.div.parentNode.removeChild(this.div); 121 | }; 122 | }); 123 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/scroll/scrollpastend.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.defineOption("scrollPastEnd", false, function(cm, val, old) { 15 | if (old && old != CodeMirror.Init) { 16 | cm.off("change", onChange); 17 | cm.off("refresh", updateBottomMargin); 18 | cm.display.lineSpace.parentNode.style.paddingBottom = ""; 19 | cm.state.scrollPastEndPadding = null; 20 | } 21 | if (val) { 22 | cm.on("change", onChange); 23 | cm.on("refresh", updateBottomMargin); 24 | updateBottomMargin(cm); 25 | } 26 | }); 27 | 28 | function onChange(cm, change) { 29 | if (CodeMirror.changeEnd(change).line == cm.lastLine()) 30 | updateBottomMargin(cm); 31 | } 32 | 33 | function updateBottomMargin(cm) { 34 | var padding = ""; 35 | if (cm.lineCount() > 1) { 36 | var totalH = cm.display.scroller.clientHeight - 30, 37 | lastLineH = cm.getLineHandle(cm.lastLine()).height; 38 | padding = (totalH - lastLineH) + "px"; 39 | } 40 | if (cm.state.scrollPastEndPadding != padding) { 41 | cm.state.scrollPastEndPadding = padding; 42 | cm.display.lineSpace.parentNode.style.paddingBottom = padding; 43 | cm.off("refresh", updateBottomMargin); 44 | cm.setSize(); 45 | cm.on("refresh", updateBottomMargin); 46 | } 47 | } 48 | }); 49 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/scroll/simplescrollbars.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-simplescroll-horizontal div, .CodeMirror-simplescroll-vertical div { 2 | position: absolute; 3 | background: #ccc; 4 | -moz-box-sizing: border-box; 5 | box-sizing: border-box; 6 | border: 1px solid #bbb; 7 | border-radius: 2px; 8 | } 9 | 10 | .CodeMirror-simplescroll-horizontal, .CodeMirror-simplescroll-vertical { 11 | position: absolute; 12 | z-index: 6; 13 | background: #eee; 14 | } 15 | 16 | .CodeMirror-simplescroll-horizontal { 17 | bottom: 0; left: 0; 18 | height: 8px; 19 | } 20 | .CodeMirror-simplescroll-horizontal div { 21 | bottom: 0; 22 | height: 100%; 23 | } 24 | 25 | .CodeMirror-simplescroll-vertical { 26 | right: 0; top: 0; 27 | width: 8px; 28 | } 29 | .CodeMirror-simplescroll-vertical div { 30 | right: 0; 31 | width: 100%; 32 | } 33 | 34 | 35 | .CodeMirror-overlayscroll .CodeMirror-scrollbar-filler, .CodeMirror-overlayscroll .CodeMirror-gutter-filler { 36 | display: none; 37 | } 38 | 39 | .CodeMirror-overlayscroll-horizontal div, .CodeMirror-overlayscroll-vertical div { 40 | position: absolute; 41 | background: #bcd; 42 | border-radius: 3px; 43 | } 44 | 45 | .CodeMirror-overlayscroll-horizontal, .CodeMirror-overlayscroll-vertical { 46 | position: absolute; 47 | z-index: 6; 48 | } 49 | 50 | .CodeMirror-overlayscroll-horizontal { 51 | bottom: 0; left: 0; 52 | height: 6px; 53 | } 54 | .CodeMirror-overlayscroll-horizontal div { 55 | bottom: 0; 56 | height: 100%; 57 | } 58 | 59 | .CodeMirror-overlayscroll-vertical { 60 | right: 0; top: 0; 61 | width: 6px; 62 | } 63 | .CodeMirror-overlayscroll-vertical div { 64 | right: 0; 65 | width: 100%; 66 | } 67 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/search/jump-to-line.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | // Defines jumpToLine command. Uses dialog.js if present. 5 | 6 | (function(mod) { 7 | if (typeof exports == "object" && typeof module == "object") // CommonJS 8 | mod(require("../../lib/codemirror"), require("../dialog/dialog")); 9 | else if (typeof define == "function" && define.amd) // AMD 10 | define(["../../lib/codemirror", "../dialog/dialog"], mod); 11 | else // Plain browser env 12 | mod(CodeMirror); 13 | })(function(CodeMirror) { 14 | "use strict"; 15 | 16 | function dialog(cm, text, shortText, deflt, f) { 17 | if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true}); 18 | else f(prompt(shortText, deflt)); 19 | } 20 | 21 | function getJumpDialog(cm) { 22 | return cm.phrase("Jump to line:") + ' ' + cm.phrase("(Use line:column or scroll% syntax)") + ''; 23 | } 24 | 25 | function interpretLine(cm, string) { 26 | var num = Number(string) 27 | if (/^[-+]/.test(string)) return cm.getCursor().line + num 28 | else return num - 1 29 | } 30 | 31 | CodeMirror.commands.jumpToLine = function(cm) { 32 | var cur = cm.getCursor(); 33 | dialog(cm, getJumpDialog(cm), cm.phrase("Jump to line:"), (cur.line + 1) + ":" + cur.ch, function(posStr) { 34 | if (!posStr) return; 35 | 36 | var match; 37 | if (match = /^\s*([\+\-]?\d+)\s*\:\s*(\d+)\s*$/.exec(posStr)) { 38 | cm.setCursor(interpretLine(cm, match[1]), Number(match[2])) 39 | } else if (match = /^\s*([\+\-]?\d+(\.\d+)?)\%\s*/.exec(posStr)) { 40 | var line = Math.round(cm.lineCount() * Number(match[1]) / 100); 41 | if (/^[-+]/.test(match[1])) line = cur.line + line + 1; 42 | cm.setCursor(line - 1, cur.ch); 43 | } else if (match = /^\s*\:?\s*([\+\-]?\d+)\s*/.exec(posStr)) { 44 | cm.setCursor(interpretLine(cm, match[1]), cur.ch); 45 | } 46 | }); 47 | }; 48 | 49 | CodeMirror.keyMap["default"]["Alt-G"] = "jumpToLine"; 50 | }); 51 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/search/matchesonscrollbar.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-search-match { 2 | background: gold; 3 | border-top: 1px solid orange; 4 | border-bottom: 1px solid orange; 5 | -moz-box-sizing: border-box; 6 | box-sizing: border-box; 7 | opacity: .5; 8 | } 9 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/search/matchesonscrollbar.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror"), require("./searchcursor"), require("../scroll/annotatescrollbar")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror", "./searchcursor", "../scroll/annotatescrollbar"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.defineExtension("showMatchesOnScrollbar", function(query, caseFold, options) { 15 | if (typeof options == "string") options = {className: options}; 16 | if (!options) options = {}; 17 | return new SearchAnnotation(this, query, caseFold, options); 18 | }); 19 | 20 | function SearchAnnotation(cm, query, caseFold, options) { 21 | this.cm = cm; 22 | this.options = options; 23 | var annotateOptions = {listenForChanges: false}; 24 | for (var prop in options) annotateOptions[prop] = options[prop]; 25 | if (!annotateOptions.className) annotateOptions.className = "CodeMirror-search-match"; 26 | this.annotation = cm.annotateScrollbar(annotateOptions); 27 | this.query = query; 28 | this.caseFold = caseFold; 29 | this.gap = {from: cm.firstLine(), to: cm.lastLine() + 1}; 30 | this.matches = []; 31 | this.update = null; 32 | 33 | this.findMatches(); 34 | this.annotation.update(this.matches); 35 | 36 | var self = this; 37 | cm.on("change", this.changeHandler = function(_cm, change) { self.onChange(change); }); 38 | } 39 | 40 | var MAX_MATCHES = 1000; 41 | 42 | SearchAnnotation.prototype.findMatches = function() { 43 | if (!this.gap) return; 44 | for (var i = 0; i < this.matches.length; i++) { 45 | var match = this.matches[i]; 46 | if (match.from.line >= this.gap.to) break; 47 | if (match.to.line >= this.gap.from) this.matches.splice(i--, 1); 48 | } 49 | var cursor = this.cm.getSearchCursor(this.query, CodeMirror.Pos(this.gap.from, 0), {caseFold: this.caseFold, multiline: this.options.multiline}); 50 | var maxMatches = this.options && this.options.maxMatches || MAX_MATCHES; 51 | while (cursor.findNext()) { 52 | var match = {from: cursor.from(), to: cursor.to()}; 53 | if (match.from.line >= this.gap.to) break; 54 | this.matches.splice(i++, 0, match); 55 | if (this.matches.length > maxMatches) break; 56 | } 57 | this.gap = null; 58 | }; 59 | 60 | function offsetLine(line, changeStart, sizeChange) { 61 | if (line <= changeStart) return line; 62 | return Math.max(changeStart, line + sizeChange); 63 | } 64 | 65 | SearchAnnotation.prototype.onChange = function(change) { 66 | var startLine = change.from.line; 67 | var endLine = CodeMirror.changeEnd(change).line; 68 | var sizeChange = endLine - change.to.line; 69 | if (this.gap) { 70 | this.gap.from = Math.min(offsetLine(this.gap.from, startLine, sizeChange), change.from.line); 71 | this.gap.to = Math.max(offsetLine(this.gap.to, startLine, sizeChange), change.from.line); 72 | } else { 73 | this.gap = {from: change.from.line, to: endLine + 1}; 74 | } 75 | 76 | if (sizeChange) for (var i = 0; i < this.matches.length; i++) { 77 | var match = this.matches[i]; 78 | var newFrom = offsetLine(match.from.line, startLine, sizeChange); 79 | if (newFrom != match.from.line) match.from = CodeMirror.Pos(newFrom, match.from.ch); 80 | var newTo = offsetLine(match.to.line, startLine, sizeChange); 81 | if (newTo != match.to.line) match.to = CodeMirror.Pos(newTo, match.to.ch); 82 | } 83 | clearTimeout(this.update); 84 | var self = this; 85 | this.update = setTimeout(function() { self.updateAfterChange(); }, 250); 86 | }; 87 | 88 | SearchAnnotation.prototype.updateAfterChange = function() { 89 | this.findMatches(); 90 | this.annotation.update(this.matches); 91 | }; 92 | 93 | SearchAnnotation.prototype.clear = function() { 94 | this.cm.off("change", this.changeHandler); 95 | this.annotation.clear(); 96 | }; 97 | }); 98 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/selection/active-line.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | var WRAP_CLASS = "CodeMirror-activeline"; 14 | var BACK_CLASS = "CodeMirror-activeline-background"; 15 | var GUTT_CLASS = "CodeMirror-activeline-gutter"; 16 | 17 | CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) { 18 | var prev = old == CodeMirror.Init ? false : old; 19 | if (val == prev) return 20 | if (prev) { 21 | cm.off("beforeSelectionChange", selectionChange); 22 | clearActiveLines(cm); 23 | delete cm.state.activeLines; 24 | } 25 | if (val) { 26 | cm.state.activeLines = []; 27 | updateActiveLines(cm, cm.listSelections()); 28 | cm.on("beforeSelectionChange", selectionChange); 29 | } 30 | }); 31 | 32 | function clearActiveLines(cm) { 33 | for (var i = 0; i < cm.state.activeLines.length; i++) { 34 | cm.removeLineClass(cm.state.activeLines[i], "wrap", WRAP_CLASS); 35 | cm.removeLineClass(cm.state.activeLines[i], "background", BACK_CLASS); 36 | cm.removeLineClass(cm.state.activeLines[i], "gutter", GUTT_CLASS); 37 | } 38 | } 39 | 40 | function sameArray(a, b) { 41 | if (a.length != b.length) return false; 42 | for (var i = 0; i < a.length; i++) 43 | if (a[i] != b[i]) return false; 44 | return true; 45 | } 46 | 47 | function updateActiveLines(cm, ranges) { 48 | var active = []; 49 | for (var i = 0; i < ranges.length; i++) { 50 | var range = ranges[i]; 51 | var option = cm.getOption("styleActiveLine"); 52 | if (typeof option == "object" && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty()) 53 | continue 54 | var line = cm.getLineHandleVisualStart(range.head.line); 55 | if (active[active.length - 1] != line) active.push(line); 56 | } 57 | if (sameArray(cm.state.activeLines, active)) return; 58 | cm.operation(function() { 59 | clearActiveLines(cm); 60 | for (var i = 0; i < active.length; i++) { 61 | cm.addLineClass(active[i], "wrap", WRAP_CLASS); 62 | cm.addLineClass(active[i], "background", BACK_CLASS); 63 | cm.addLineClass(active[i], "gutter", GUTT_CLASS); 64 | } 65 | cm.state.activeLines = active; 66 | }); 67 | } 68 | 69 | function selectionChange(cm, sel) { 70 | updateActiveLines(cm, sel.ranges); 71 | } 72 | }); 73 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/selection/mark-selection.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | // Because sometimes you need to mark the selected *text*. 5 | // 6 | // Adds an option 'styleSelectedText' which, when enabled, gives 7 | // selected text the CSS class given as option value, or 8 | // "CodeMirror-selectedtext" when the value is not a string. 9 | 10 | (function(mod) { 11 | if (typeof exports == "object" && typeof module == "object") // CommonJS 12 | mod(require("../../lib/codemirror")); 13 | else if (typeof define == "function" && define.amd) // AMD 14 | define(["../../lib/codemirror"], mod); 15 | else // Plain browser env 16 | mod(CodeMirror); 17 | })(function(CodeMirror) { 18 | "use strict"; 19 | 20 | CodeMirror.defineOption("styleSelectedText", false, function(cm, val, old) { 21 | var prev = old && old != CodeMirror.Init; 22 | if (val && !prev) { 23 | cm.state.markedSelection = []; 24 | cm.state.markedSelectionStyle = typeof val == "string" ? val : "CodeMirror-selectedtext"; 25 | reset(cm); 26 | cm.on("cursorActivity", onCursorActivity); 27 | cm.on("change", onChange); 28 | } else if (!val && prev) { 29 | cm.off("cursorActivity", onCursorActivity); 30 | cm.off("change", onChange); 31 | clear(cm); 32 | cm.state.markedSelection = cm.state.markedSelectionStyle = null; 33 | } 34 | }); 35 | 36 | function onCursorActivity(cm) { 37 | if (cm.state.markedSelection) 38 | cm.operation(function() { update(cm); }); 39 | } 40 | 41 | function onChange(cm) { 42 | if (cm.state.markedSelection && cm.state.markedSelection.length) 43 | cm.operation(function() { clear(cm); }); 44 | } 45 | 46 | var CHUNK_SIZE = 8; 47 | var Pos = CodeMirror.Pos; 48 | var cmp = CodeMirror.cmpPos; 49 | 50 | function coverRange(cm, from, to, addAt) { 51 | if (cmp(from, to) == 0) return; 52 | var array = cm.state.markedSelection; 53 | var cls = cm.state.markedSelectionStyle; 54 | for (var line = from.line;;) { 55 | var start = line == from.line ? from : Pos(line, 0); 56 | var endLine = line + CHUNK_SIZE, atEnd = endLine >= to.line; 57 | var end = atEnd ? to : Pos(endLine, 0); 58 | var mark = cm.markText(start, end, {className: cls}); 59 | if (addAt == null) array.push(mark); 60 | else array.splice(addAt++, 0, mark); 61 | if (atEnd) break; 62 | line = endLine; 63 | } 64 | } 65 | 66 | function clear(cm) { 67 | var array = cm.state.markedSelection; 68 | for (var i = 0; i < array.length; ++i) array[i].clear(); 69 | array.length = 0; 70 | } 71 | 72 | function reset(cm) { 73 | clear(cm); 74 | var ranges = cm.listSelections(); 75 | for (var i = 0; i < ranges.length; i++) 76 | coverRange(cm, ranges[i].from(), ranges[i].to()); 77 | } 78 | 79 | function update(cm) { 80 | if (!cm.somethingSelected()) return clear(cm); 81 | if (cm.listSelections().length > 1) return reset(cm); 82 | 83 | var from = cm.getCursor("start"), to = cm.getCursor("end"); 84 | 85 | var array = cm.state.markedSelection; 86 | if (!array.length) return coverRange(cm, from, to); 87 | 88 | var coverStart = array[0].find(), coverEnd = array[array.length - 1].find(); 89 | if (!coverStart || !coverEnd || to.line - from.line <= CHUNK_SIZE || 90 | cmp(from, coverEnd.to) >= 0 || cmp(to, coverStart.from) <= 0) 91 | return reset(cm); 92 | 93 | while (cmp(from, coverStart.from) > 0) { 94 | array.shift().clear(); 95 | coverStart = array[0].find(); 96 | } 97 | if (cmp(from, coverStart.from) < 0) { 98 | if (coverStart.to.line - from.line < CHUNK_SIZE) { 99 | array.shift().clear(); 100 | coverRange(cm, from, coverStart.to, 0); 101 | } else { 102 | coverRange(cm, from, coverStart.from, 0); 103 | } 104 | } 105 | 106 | while (cmp(to, coverEnd.to) < 0) { 107 | array.pop().clear(); 108 | coverEnd = array[array.length - 1].find(); 109 | } 110 | if (cmp(to, coverEnd.to) > 0) { 111 | if (to.line - coverEnd.from.line < CHUNK_SIZE) { 112 | array.pop().clear(); 113 | coverRange(cm, coverEnd.from, to); 114 | } else { 115 | coverRange(cm, coverEnd.to, to); 116 | } 117 | } 118 | } 119 | }); 120 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/selection/selection-pointer.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.defineOption("selectionPointer", false, function(cm, val) { 15 | var data = cm.state.selectionPointer; 16 | if (data) { 17 | CodeMirror.off(cm.getWrapperElement(), "mousemove", data.mousemove); 18 | CodeMirror.off(cm.getWrapperElement(), "mouseout", data.mouseout); 19 | CodeMirror.off(window, "scroll", data.windowScroll); 20 | cm.off("cursorActivity", reset); 21 | cm.off("scroll", reset); 22 | cm.state.selectionPointer = null; 23 | cm.display.lineDiv.style.cursor = ""; 24 | } 25 | if (val) { 26 | data = cm.state.selectionPointer = { 27 | value: typeof val == "string" ? val : "default", 28 | mousemove: function(event) { mousemove(cm, event); }, 29 | mouseout: function(event) { mouseout(cm, event); }, 30 | windowScroll: function() { reset(cm); }, 31 | rects: null, 32 | mouseX: null, mouseY: null, 33 | willUpdate: false 34 | }; 35 | CodeMirror.on(cm.getWrapperElement(), "mousemove", data.mousemove); 36 | CodeMirror.on(cm.getWrapperElement(), "mouseout", data.mouseout); 37 | CodeMirror.on(window, "scroll", data.windowScroll); 38 | cm.on("cursorActivity", reset); 39 | cm.on("scroll", reset); 40 | } 41 | }); 42 | 43 | function mousemove(cm, event) { 44 | var data = cm.state.selectionPointer; 45 | if (event.buttons == null ? event.which : event.buttons) { 46 | data.mouseX = data.mouseY = null; 47 | } else { 48 | data.mouseX = event.clientX; 49 | data.mouseY = event.clientY; 50 | } 51 | scheduleUpdate(cm); 52 | } 53 | 54 | function mouseout(cm, event) { 55 | if (!cm.getWrapperElement().contains(event.relatedTarget)) { 56 | var data = cm.state.selectionPointer; 57 | data.mouseX = data.mouseY = null; 58 | scheduleUpdate(cm); 59 | } 60 | } 61 | 62 | function reset(cm) { 63 | cm.state.selectionPointer.rects = null; 64 | scheduleUpdate(cm); 65 | } 66 | 67 | function scheduleUpdate(cm) { 68 | if (!cm.state.selectionPointer.willUpdate) { 69 | cm.state.selectionPointer.willUpdate = true; 70 | setTimeout(function() { 71 | update(cm); 72 | cm.state.selectionPointer.willUpdate = false; 73 | }, 50); 74 | } 75 | } 76 | 77 | function update(cm) { 78 | var data = cm.state.selectionPointer; 79 | if (!data) return; 80 | if (data.rects == null && data.mouseX != null) { 81 | data.rects = []; 82 | if (cm.somethingSelected()) { 83 | for (var sel = cm.display.selectionDiv.firstChild; sel; sel = sel.nextSibling) 84 | data.rects.push(sel.getBoundingClientRect()); 85 | } 86 | } 87 | var inside = false; 88 | if (data.mouseX != null) for (var i = 0; i < data.rects.length; i++) { 89 | var rect = data.rects[i]; 90 | if (rect.left <= data.mouseX && rect.right >= data.mouseX && 91 | rect.top <= data.mouseY && rect.bottom >= data.mouseY) 92 | inside = true; 93 | } 94 | var cursor = inside ? data.value : ""; 95 | if (cm.display.lineDiv.style.cursor != cursor) 96 | cm.display.lineDiv.style.cursor = cursor; 97 | } 98 | }); 99 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/tern/tern.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-Tern-completion { 2 | padding-left: 22px; 3 | position: relative; 4 | line-height: 1.5; 5 | } 6 | .CodeMirror-Tern-completion:before { 7 | position: absolute; 8 | left: 2px; 9 | bottom: 2px; 10 | border-radius: 50%; 11 | font-size: 12px; 12 | font-weight: bold; 13 | height: 15px; 14 | width: 15px; 15 | line-height: 16px; 16 | text-align: center; 17 | color: white; 18 | -moz-box-sizing: border-box; 19 | box-sizing: border-box; 20 | } 21 | .CodeMirror-Tern-completion-unknown:before { 22 | content: "?"; 23 | background: #4bb; 24 | } 25 | .CodeMirror-Tern-completion-object:before { 26 | content: "O"; 27 | background: #77c; 28 | } 29 | .CodeMirror-Tern-completion-fn:before { 30 | content: "F"; 31 | background: #7c7; 32 | } 33 | .CodeMirror-Tern-completion-array:before { 34 | content: "A"; 35 | background: #c66; 36 | } 37 | .CodeMirror-Tern-completion-number:before { 38 | content: "1"; 39 | background: #999; 40 | } 41 | .CodeMirror-Tern-completion-string:before { 42 | content: "S"; 43 | background: #999; 44 | } 45 | .CodeMirror-Tern-completion-bool:before { 46 | content: "B"; 47 | background: #999; 48 | } 49 | 50 | .CodeMirror-Tern-completion-guess { 51 | color: #999; 52 | } 53 | 54 | .CodeMirror-Tern-tooltip { 55 | border: 1px solid silver; 56 | border-radius: 3px; 57 | color: #444; 58 | padding: 2px 5px; 59 | font-size: 90%; 60 | font-family: monospace; 61 | background-color: white; 62 | white-space: pre-wrap; 63 | 64 | max-width: 40em; 65 | position: absolute; 66 | z-index: 10; 67 | -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 68 | -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 69 | box-shadow: 2px 3px 5px rgba(0,0,0,.2); 70 | 71 | transition: opacity 1s; 72 | -moz-transition: opacity 1s; 73 | -webkit-transition: opacity 1s; 74 | -o-transition: opacity 1s; 75 | -ms-transition: opacity 1s; 76 | } 77 | 78 | .CodeMirror-Tern-hint-doc { 79 | max-width: 25em; 80 | margin-top: -3px; 81 | } 82 | 83 | .CodeMirror-Tern-fname { color: black; } 84 | .CodeMirror-Tern-farg { color: #70a; } 85 | .CodeMirror-Tern-farg-current { text-decoration: underline; } 86 | .CodeMirror-Tern-type { color: #07c; } 87 | .CodeMirror-Tern-fhint-guess { opacity: .7; } 88 | -------------------------------------------------------------------------------- /play/src/main/assets/web/addon/tern/worker.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | // declare global: tern, server 5 | 6 | var server; 7 | 8 | this.onmessage = function(e) { 9 | var data = e.data; 10 | switch (data.type) { 11 | case "init": return startServer(data.defs, data.plugins, data.scripts); 12 | case "add": return server.addFile(data.name, data.text); 13 | case "del": return server.delFile(data.name); 14 | case "req": return server.request(data.body, function(err, reqData) { 15 | postMessage({id: data.id, body: reqData, err: err && String(err)}); 16 | }); 17 | case "getFile": 18 | var c = pending[data.id]; 19 | delete pending[data.id]; 20 | return c(data.err, data.text); 21 | default: throw new Error("Unknown message type: " + data.type); 22 | } 23 | }; 24 | 25 | var nextId = 0, pending = {}; 26 | function getFile(file, c) { 27 | postMessage({type: "getFile", name: file, id: ++nextId}); 28 | pending[nextId] = c; 29 | } 30 | 31 | function startServer(defs, plugins, scripts) { 32 | if (scripts) importScripts.apply(null, scripts); 33 | 34 | server = new tern.Server({ 35 | getFile: getFile, 36 | async: true, 37 | defs: defs, 38 | plugins: plugins 39 | }); 40 | } 41 | 42 | this.console = { 43 | log: function(v) { postMessage({type: "debug", message: v}); } 44 | }; 45 | -------------------------------------------------------------------------------- /play/src/main/assets/web/mode/javascript/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | CodeMirror: JavaScript mode 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 27 | 28 |
    29 |

    JavaScript mode

    30 | 31 | 32 |
    81 | 82 | 90 | 91 |

    92 | JavaScript mode supports several configuration options: 93 |

      94 |
    • json which will set the mode to expect JSON 95 | data rather than a JavaScript program.
    • 96 |
    • jsonld which will set the mode to expect 97 | JSON-LD linked data rather 98 | than a JavaScript program (demo).
    • 99 |
    • typescript which will activate additional 100 | syntax highlighting and some other things for TypeScript code 101 | (demo).
    • 102 |
    • statementIndent which (given a number) will 103 | determine the amount of indentation to use for statements 104 | continued on a new line.
    • 105 |
    • wordCharacters, a regexp that indicates which 106 | characters should be considered part of an identifier. 107 | Defaults to /[\w$]/, which does not handle 108 | non-ASCII identifiers. Can be set to something more elaborate 109 | to improve Unicode support.
    • 110 |
    111 |

    112 | 113 |

    MIME types defined: text/javascript, application/json, application/ld+json, text/typescript, application/typescript.

    114 |
    115 | -------------------------------------------------------------------------------- /play/src/main/assets/web/mode/javascript/json-ld.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | CodeMirror: JSON-LD mode 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 27 | 28 |
    29 |

    JSON-LD mode

    30 | 31 | 32 |
    61 | 62 | 70 | 71 |

    This is a specialization of the JavaScript mode.

    72 |
    73 | -------------------------------------------------------------------------------- /play/src/main/assets/web/mode/javascript/typescript.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | CodeMirror: TypeScript mode 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 25 | 26 |
    27 |

    TypeScript mode

    28 | 29 | 30 |
    52 | 53 | 60 | 61 |

    This is a specialization of the JavaScript mode.

    62 |
    63 | -------------------------------------------------------------------------------- /play/src/main/assets/web/theme/darcula.css: -------------------------------------------------------------------------------- 1 | /** 2 | Name: IntelliJ IDEA darcula theme 3 | From IntelliJ IDEA by JetBrains 4 | */ 5 | 6 | .cm-s-darcula { font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif;} 7 | .cm-s-darcula.CodeMirror { background: #2B2B2B; color: #A9B7C6; } 8 | 9 | .cm-s-darcula span.cm-meta { color: #BBB529; } 10 | .cm-s-darcula span.cm-number { color: #6897BB; } 11 | .cm-s-darcula span.cm-keyword { color: #CC7832; line-height: 1em; font-weight: bold; } 12 | .cm-s-darcula span.cm-def { color: #A9B7C6; font-style: italic; } 13 | .cm-s-darcula span.cm-variable { color: #A9B7C6; } 14 | .cm-s-darcula span.cm-variable-2 { color: #A9B7C6; } 15 | .cm-s-darcula span.cm-variable-3 { color: #9876AA; } 16 | .cm-s-darcula span.cm-type { color: #AABBCC; font-weight: bold; } 17 | .cm-s-darcula span.cm-property { color: #FFC66D; } 18 | .cm-s-darcula span.cm-operator { color: #A9B7C6; } 19 | .cm-s-darcula span.cm-string { color: #6A8759; } 20 | .cm-s-darcula span.cm-string-2 { color: #6A8759; } 21 | .cm-s-darcula span.cm-comment { color: #61A151; font-style: italic; } 22 | .cm-s-darcula span.cm-link { color: #CC7832; } 23 | .cm-s-darcula span.cm-atom { color: #CC7832; } 24 | .cm-s-darcula span.cm-error { color: #BC3F3C; } 25 | .cm-s-darcula span.cm-tag { color: #629755; font-weight: bold; font-style: italic; text-decoration: underline; } 26 | .cm-s-darcula span.cm-attribute { color: #6897bb; } 27 | .cm-s-darcula span.cm-qualifier { color: #6A8759; } 28 | .cm-s-darcula span.cm-bracket { color: #A9B7C6; } 29 | .cm-s-darcula span.cm-builtin { color: #FF9E59; } 30 | .cm-s-darcula span.cm-special { color: #FF9E59; } 31 | .cm-s-darcula span.cm-matchhighlight { color: #FFFFFF; background-color: rgba(50, 89, 48, .7); font-weight: normal;} 32 | .cm-s-darcula span.cm-searching { color: #FFFFFF; background-color: rgba(61, 115, 59, .7); font-weight: normal;} 33 | 34 | .cm-s-darcula .CodeMirror-cursor { border-left: 1px solid #A9B7C6; } 35 | .cm-s-darcula .CodeMirror-activeline-background { background: #323232; } 36 | .cm-s-darcula .CodeMirror-gutters { background: #313335; border-right: 1px solid #313335; } 37 | .cm-s-darcula .CodeMirror-guttermarker { color: #FFEE80; } 38 | .cm-s-darcula .CodeMirror-guttermarker-subtle { color: #D0D0D0; } 39 | .cm-s-darcula .CodeMirrir-linenumber { color: #606366; } 40 | .cm-s-darcula .CodeMirror-matchingbracket { background-color: #3B514D; color: #FFEF28 !important; font-weight: bold; } 41 | 42 | .cm-s-darcula div.CodeMirror-selected { background: #214283; } 43 | 44 | .CodeMirror-hints.darcula { 45 | font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; 46 | color: #9C9E9E; 47 | background-color: #3B3E3F !important; 48 | } 49 | 50 | .CodeMirror-hints.darcula .CodeMirror-hint-active { 51 | background-color: #494D4E !important; 52 | color: #9C9E9E !important; 53 | } 54 | -------------------------------------------------------------------------------- /play/src/main/assets/web/theme/monokai.css: -------------------------------------------------------------------------------- 1 | /* Based on Sublime Text's Monokai theme */ 2 | 3 | .cm-s-monokai.CodeMirror { background: #272822; color: #f8f8f2; } 4 | .cm-s-monokai div.CodeMirror-selected { background: #49483E; } 5 | .cm-s-monokai .CodeMirror-line::selection, .cm-s-monokai .CodeMirror-line > span::selection, .cm-s-monokai .CodeMirror-line > span > span::selection { background: rgba(73, 72, 62, .99); } 6 | .cm-s-monokai .CodeMirror-line::-moz-selection, .cm-s-monokai .CodeMirror-line > span::-moz-selection, .cm-s-monokai .CodeMirror-line > span > span::-moz-selection { background: rgba(73, 72, 62, .99); } 7 | .cm-s-monokai .CodeMirror-gutters { background: #272822; border-right: 0px; } 8 | .cm-s-monokai .CodeMirror-guttermarker { color: white; } 9 | .cm-s-monokai .CodeMirror-guttermarker-subtle { color: #d0d0d0; } 10 | .cm-s-monokai .CodeMirror-linenumber { color: #d0d0d0; } 11 | .cm-s-monokai .CodeMirror-cursor { border-left: 1px solid #f8f8f0; } 12 | 13 | .cm-s-monokai span.cm-comment { color: #75715e; } 14 | .cm-s-monokai span.cm-atom { color: #ae81ff; } 15 | .cm-s-monokai span.cm-number { color: #ae81ff; } 16 | 17 | .cm-s-monokai span.cm-comment.cm-attribute { color: #97b757; } 18 | .cm-s-monokai span.cm-comment.cm-def { color: #bc9262; } 19 | .cm-s-monokai span.cm-comment.cm-tag { color: #bc6283; } 20 | .cm-s-monokai span.cm-comment.cm-type { color: #5998a6; } 21 | 22 | .cm-s-monokai span.cm-property, .cm-s-monokai span.cm-attribute { color: #a6e22e; } 23 | .cm-s-monokai span.cm-keyword { color: #f92672; } 24 | .cm-s-monokai span.cm-builtin { color: #66d9ef; } 25 | .cm-s-monokai span.cm-string { color: #e6db74; } 26 | 27 | .cm-s-monokai span.cm-variable { color: #f8f8f2; } 28 | .cm-s-monokai span.cm-variable-2 { color: #9effff; } 29 | .cm-s-monokai span.cm-variable-3, .cm-s-monokai span.cm-type { color: #66d9ef; } 30 | .cm-s-monokai span.cm-def { color: #fd971f; } 31 | .cm-s-monokai span.cm-bracket { color: #f8f8f2; } 32 | .cm-s-monokai span.cm-tag { color: #f92672; } 33 | .cm-s-monokai span.cm-header { color: #ae81ff; } 34 | .cm-s-monokai span.cm-link { color: #ae81ff; } 35 | .cm-s-monokai span.cm-error { background: #f92672; color: #f8f8f0; } 36 | 37 | .cm-s-monokai .CodeMirror-activeline-background { background: #373831; } 38 | .cm-s-monokai .CodeMirror-matchingbracket { 39 | text-decoration: underline; 40 | color: white !important; 41 | } 42 | -------------------------------------------------------------------------------- /play/src/main/java/github/hotstu/lu/play/Function.java: -------------------------------------------------------------------------------- 1 | package github.hotstu.lu.play; 2 | 3 | /** 4 | * @author hglf [hglf](https://github.com/hotstu) 5 | * @desc 6 | * @since 9/18/20 7 | */ 8 | public interface Function { 9 | R apply(T input); 10 | } 11 | -------------------------------------------------------------------------------- /play/src/main/java/github/hotstu/lu/play/HttpDaemonService.java: -------------------------------------------------------------------------------- 1 | package github.hotstu.lu.play; 2 | 3 | import android.app.NotificationChannel; 4 | import android.app.NotificationManager; 5 | import android.app.Service; 6 | import android.content.Intent; 7 | import android.os.Build; 8 | import android.os.Bundle; 9 | import android.os.IBinder; 10 | import android.os.RemoteException; 11 | 12 | import androidx.annotation.Nullable; 13 | import androidx.core.app.NotificationCompat; 14 | 15 | import com.google.gson.Gson; 16 | 17 | import java.util.HashMap; 18 | 19 | import github.hotstu.lu.base.IPlayCallback; 20 | import github.hotstu.lu.base.IPlayService; 21 | 22 | /** 23 | * @author hglf [hglf](https://github.com/hotstu) 24 | * @desc 25 | * @since 7/15/20 26 | */ 27 | public class HttpDaemonService extends Service { 28 | 29 | class MyBinder extends IPlayService.Stub { 30 | private final Object accessLock = new Object(); 31 | private IPlayCallback callback = null; 32 | 33 | 34 | public IPlayCallback getCallback() { 35 | return callback; 36 | } 37 | 38 | @Override 39 | public void setCallback(final IPlayCallback callback) { 40 | synchronized (accessLock) { 41 | this.callback = callback; 42 | updateState(); 43 | } 44 | 45 | } 46 | 47 | 48 | 49 | private void updateState() { 50 | 51 | boolean empty = callback == null; 52 | 53 | if (empty) { 54 | stopServer(); 55 | } else { 56 | startServer(); 57 | } 58 | 59 | } 60 | 61 | } 62 | 63 | MyBinder mBinder = null; 64 | Gson g = new Gson(); 65 | 66 | @Nullable 67 | @Override 68 | public IBinder onBind(Intent intent) { 69 | if (mBinder == null) { 70 | mBinder = new MyBinder(); 71 | } 72 | return mBinder; 73 | } 74 | 75 | Function scriptRunner = input -> { 76 | HashMap ret = new HashMap<>(); 77 | 78 | if ((mBinder != null && mBinder.getCallback() != null)) { 79 | 80 | try { 81 | Bundle bundle = mBinder.getCallback().onEvent(0, input); 82 | ret.put("success", bundle.getBoolean("success", false)); 83 | ret.put("data", bundle.getString("data", null)); 84 | ret.put("err", bundle.getString("err", null)); 85 | } catch (RemoteException e) { 86 | e.printStackTrace(); 87 | } 88 | } else { 89 | ret.put("success", false); 90 | ret.put("data", null); 91 | ret.put("err", "LuEngine 未绑定"); 92 | } 93 | return g.toJson(ret); 94 | }; 95 | 96 | @Override 97 | public void onCreate() { 98 | super.onCreate(); 99 | ServerApp.getInstance().setCallable(scriptRunner); 100 | 101 | } 102 | 103 | @Override 104 | public int onStartCommand(Intent intent, int flags, int startId) { 105 | return START_NOT_STICKY; 106 | } 107 | 108 | @Override 109 | public void onDestroy() { 110 | ServerApp.getInstance().setCallable(null); 111 | super.onDestroy(); 112 | } 113 | 114 | private void stopServer() { 115 | ServerApp.getInstance().shutdown(); 116 | } 117 | 118 | private int startServer() { 119 | ServerApp.getInstance().startUp(); 120 | 121 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 122 | int importance = NotificationManager.IMPORTANCE_DEFAULT; 123 | NotificationChannel channel = new NotificationChannel("default", "default", importance); 124 | channel.setDescription("default"); 125 | // Register the channel with the system; you can't change the importance 126 | // or other notification behaviors after this 127 | NotificationManager notificationManager = getSystemService(NotificationManager.class); 128 | notificationManager.createNotificationChannel(channel); 129 | } 130 | 131 | 132 | NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, "default") 133 | .setSmallIcon(R.mipmap.ic_launcher) 134 | .setContentTitle("lu") 135 | .setContentText("雍和宫") 136 | .setPriority(NotificationCompat.PRIORITY_DEFAULT); 137 | 138 | 139 | startForeground(1, mBuilder.build()); 140 | return 0; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /play/src/main/java/github/hotstu/lu/play/ModuleApp.java: -------------------------------------------------------------------------------- 1 | package github.hotstu.lu.play; 2 | 3 | import android.content.Context; 4 | 5 | import github.hotstu.lu.base.IModule; 6 | 7 | /** 8 | * @author hglf [hglf](https://github.com/hotstu) 9 | * @desc 10 | * @since 9/16/20 11 | */ 12 | public class ModuleApp implements IModule { 13 | public static Context application; 14 | @Override 15 | public void onCreate(Context context) { 16 | application = context; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /play/src/main/java/github/hotstu/lu/play/ServerApp.java: -------------------------------------------------------------------------------- 1 | package github.hotstu.lu.play; 2 | 3 | import android.content.Context; 4 | import android.net.wifi.WifiInfo; 5 | import android.net.wifi.WifiManager; 6 | import android.text.format.Formatter; 7 | 8 | import com.google.gson.Gson; 9 | 10 | import java.io.BufferedInputStream; 11 | import java.io.IOException; 12 | import java.nio.charset.Charset; 13 | import java.util.HashMap; 14 | 15 | import fi.iki.elonen.NanoHTTPD; 16 | import okio.ByteString; 17 | 18 | public class ServerApp extends NanoHTTPD { 19 | 20 | private static ServerApp sInstance; 21 | private final Gson g; 22 | private final StaticHandler staticHandler; 23 | private Function scriptRunner; 24 | private String msg; 25 | 26 | public static ServerApp getInstance() { 27 | if (sInstance == null) { 28 | synchronized (ServerApp.class) { 29 | if (sInstance == null) { 30 | sInstance = new ServerApp(); 31 | } 32 | } 33 | } 34 | return sInstance; 35 | } 36 | 37 | public ServerApp() { 38 | super("0.0.0.0", 8080); 39 | g = new Gson(); 40 | 41 | staticHandler = new StaticHandler(ModuleApp.application); 42 | } 43 | 44 | public void startUp() { 45 | new Thread(() -> { 46 | try { 47 | WifiManager wifiManager = (WifiManager) ModuleApp.application.getApplicationContext().getSystemService(Context.WIFI_SERVICE); 48 | WifiInfo connectionInfo = wifiManager.getConnectionInfo(); 49 | int ipAddress = connectionInfo.getIpAddress(); 50 | String address = Formatter.formatIpAddress(ipAddress); 51 | start(NanoHTTPD.SOCKET_READ_TIMEOUT, false); 52 | System.out.println(String.format("\nRunning! Point your browsers to http://%s:8080/ \n", address)); 53 | } catch (IOException e) { 54 | e.printStackTrace(); 55 | } 56 | }).start(); 57 | } 58 | 59 | public void shutdown() { 60 | stop(); 61 | } 62 | 63 | @Override 64 | public Response serve(IHTTPSession session) { 65 | final String uri = session.getUri(); 66 | if (uri.matches("^/run$")) { 67 | HashMap files = new HashMap<>(); 68 | try { 69 | session.parseBody(files); 70 | final String postData = files.get("postData"); 71 | if (postData != null && !"".equals(postData)) { 72 | if (scriptRunner != null) { 73 | String result = scriptRunner.apply(postData); 74 | return newFixedLengthResponse(Response.Status.OK, "application/json", result); 75 | } 76 | } 77 | } catch (IOException | ResponseException e) { 78 | e.printStackTrace(); 79 | } 80 | return newFixedLengthResponse(Response.Status.OK, "application/json", "{\"success\": false}"); 81 | } else if (uri.matches("^/reset$")) { 82 | if (scriptRunner != null) { 83 | scriptRunner.apply("$context.resetContextScope()"); 84 | } 85 | return newFixedLengthResponse(Response.Status.OK, "application/json", "{\"success\": true}"); 86 | } else if (uri.matches("^/$")) { 87 | if (msg == null) { 88 | try (BufferedInputStream inputStream = new BufferedInputStream(ModuleApp.application.getAssets().open("form.html"));) { 89 | ByteString read = ByteString.read(inputStream, inputStream.available()); 90 | msg = read.string(Charset.forName("UTF-8")); 91 | } catch (Throwable throwable) { 92 | throwable.printStackTrace(); 93 | } 94 | } 95 | return newFixedLengthResponse(msg); 96 | } else if (staticHandler.isHandle(session)) { 97 | try { 98 | return staticHandler.handle(session); 99 | } catch (IOException e) { 100 | e.printStackTrace(); 101 | return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, "text/html", "Error!"); 102 | } 103 | } else { 104 | return newFixedLengthResponse(Response.Status.NOT_FOUND, "text/html", "Page Not Found"); 105 | } 106 | 107 | 108 | } 109 | 110 | public void setCallable(Function function) { 111 | this.scriptRunner = function; 112 | } 113 | } -------------------------------------------------------------------------------- /play/src/main/java/github/hotstu/lu/play/StaticHandler.java: -------------------------------------------------------------------------------- 1 | package github.hotstu.lu.play; 2 | 3 | import android.content.Context; 4 | 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.util.regex.Matcher; 8 | import java.util.regex.Pattern; 9 | 10 | import fi.iki.elonen.NanoHTTPD; 11 | 12 | public class StaticHandler { 13 | 14 | private final Context context; 15 | Pattern pattern = Pattern.compile("^/static/([\\s|\\S]*)$", Pattern.CASE_INSENSITIVE); 16 | 17 | public StaticHandler(Context context) { 18 | this.context = context; 19 | } 20 | 21 | public boolean isHandle(NanoHTTPD.IHTTPSession session) { 22 | final String uri = session.getUri(); 23 | final Matcher matcher = pattern.matcher(uri); 24 | return matcher.find(); 25 | } 26 | 27 | public NanoHTTPD.Response handle(NanoHTTPD.IHTTPSession session) throws IOException { 28 | final String uri = session.getUri(); 29 | final Matcher matcher = pattern.matcher(uri); 30 | if (matcher.matches()) { 31 | final String subPath = matcher.group(1); 32 | //TODO 文件访问控制 33 | String mimeType = "application/octet-stream"; 34 | if (subPath.endsWith("js")) { 35 | mimeType = "text/javascript"; 36 | } else if (subPath.endsWith("css")) { 37 | mimeType = "text/css"; 38 | } else if (subPath.endsWith("html")) { 39 | mimeType = "text/html"; 40 | } 41 | final InputStream inputStream = context.getAssets().open("web/" + subPath); 42 | return NanoHTTPD.newChunkedResponse(NanoHTTPD.Response.Status.OK, mimeType, inputStream); 43 | } 44 | return NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.NO_CONTENT,"application/octet-stream",""); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /play/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hotstu/lu/f2c33abaf58bd569570c86c0d77d7e408a637770/play/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /play/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hotstu/lu/f2c33abaf58bd569570c86c0d77d7e408a637770/play/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /play/src/main/resources/META-INF/services/github.hotstu.lu.base.IModule: -------------------------------------------------------------------------------- 1 | github.hotstu.lu.play.ModuleApp -------------------------------------------------------------------------------- /render/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /render/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 29 5 | buildToolsVersion "30.0.2" 6 | compileOptions { 7 | sourceCompatibility 1.8 8 | targetCompatibility 1.8 9 | } 10 | defaultConfig { 11 | minSdkVersion 22 12 | targetSdkVersion 29 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | consumerProguardFiles "consumer-rules.pro" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | } 26 | 27 | dependencies { 28 | implementation fileTree(dir: "libs", include: ["*.jar"]) 29 | implementation 'androidx.appcompat:appcompat:1.2.0' 30 | implementation group: 'org.mozilla', name: 'rhino', version: '1.7.12' 31 | implementation project(path: ':engine') 32 | 33 | 34 | } -------------------------------------------------------------------------------- /render/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hotstu/lu/f2c33abaf58bd569570c86c0d77d7e408a637770/render/consumer-rules.pro -------------------------------------------------------------------------------- /render/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /render/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /render/src/main/assets/modules/ShadowModule.js: -------------------------------------------------------------------------------- 1 | 2 | const module = (calback) => { 3 | return { 4 | pre: (e) => {console.log('pre', '渲染前')}, 5 | post: (e) => {console.log('post', '渲染后'), calback && calback()} 6 | } 7 | } 8 | 9 | export default module 10 | -------------------------------------------------------------------------------- /render/src/main/assets/modules/phatomDomAPI.js: -------------------------------------------------------------------------------- 1 | module.exports = function($api) { 2 | return { 3 | createElement: (tag) => { 4 | console.log('createElement', tag); 5 | return $api.createComponent(tag) 6 | }, 7 | createTextNode: (text) => { 8 | console.log('createTextNode', text); 9 | return $api.createText(text) 10 | }, 11 | createComment: (text) => { 12 | console.log('createComment', text); 13 | return $api.createComment(text) 14 | }, 15 | insertBefore: (parent, child, sibling) => { 16 | console.log('insertBefore', parent, child, sibling); 17 | parent.insertBefore(child, sibling) 18 | }, 19 | removeChild: (parent, child) => { 20 | console.log('removeChild', parent, child); 21 | parent.removeChild(child) 22 | }, 23 | appendChild: (parent, child) => { 24 | console.log('appendChild', parent, child); 25 | return parent.appendChild(child) 26 | }, 27 | parentNode: (node) => { 28 | console.log('parentNode', node); 29 | return node.findParent(); 30 | }, 31 | nextSibling: (node) => { 32 | console.log('nextSibling', node); 33 | return node.findParent() && node.findParent().findNextSibling(node); 34 | }, 35 | tagName: (node) => { 36 | console.log('tagName', node); 37 | return node.tag; 38 | }, 39 | setTextContent: (node, text) => { 40 | console.log('setTextContent', node, text); 41 | $api.setTextContent(node, text) 42 | }, 43 | getTextContent: (node) => { 44 | console.log('getTextContent', node); 45 | $api.getTextContent(node) 46 | }, 47 | isElement: (node) => { 48 | console.log('isElement', node); 49 | return $api.isElement(node) 50 | }, 51 | isText: (node) => { 52 | console.log('isText', node); 53 | return $api.isText(node) 54 | }, 55 | isComment: (node) => { 56 | console.log('isComment', node); 57 | return $api.isComment(node) 58 | }, 59 | }; 60 | } 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /render/src/main/assets/modules/snabbdom-class.js: -------------------------------------------------------------------------------- 1 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.snabbdom_class = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o createComponent(String tag) { 23 | return new Frame(context, tag); 24 | } 25 | 26 | public NativeComponent createText(String text) { 27 | return new Text(context, text); 28 | } 29 | 30 | public NativeComponent createComment(String text) { 31 | return new Comment(context, text); 32 | } 33 | 34 | public boolean isComment(NativeComponent node) { 35 | return node instanceof Comment; 36 | } 37 | 38 | public boolean isText(NativeComponent node) { 39 | return node instanceof Text; 40 | } 41 | 42 | public boolean isElement(NativeComponent node) { 43 | return node instanceof Frame; 44 | } 45 | 46 | public void setTextContent(NativeComponent node, String text) { 47 | if (node.children.get(0) instanceof Text) { 48 | ((Text) node.children.get(0)).setText(text); 49 | } 50 | } 51 | 52 | public String getTextContent(NativeComponent node) { 53 | if (node.children.get(0) instanceof Text) { 54 | return ((Text) node.children.get(0)).text; 55 | } 56 | return null; 57 | } 58 | 59 | 60 | public void debug(String what, Object... component) { 61 | System.out.println(what + "==>" + component); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /render/src/main/java/github/hotstu/lu/render/RenderContext.java: -------------------------------------------------------------------------------- 1 | package github.hotstu.lu.render; 2 | 3 | import android.content.Context; 4 | import android.os.Handler; 5 | import android.os.Looper; 6 | import android.transition.TransitionManager; 7 | import android.util.Log; 8 | import android.view.ViewGroup; 9 | import android.widget.LinearLayout; 10 | 11 | import org.mozilla.javascript.NativeObject; 12 | import org.mozilla.javascript.Scriptable; 13 | 14 | import java.util.ArrayList; 15 | 16 | import github.hotstu.lu.engine.LuContext; 17 | import github.hotstu.lu.engine.LuEngine; 18 | import github.hotstu.lu.render.component.Frame; 19 | import github.hotstu.lu.render.style.SimpleStyleEngine; 20 | 21 | 22 | /** 23 | * @author hglf [hglf](https://github.com/hotstu) 24 | * @desc 25 | * @since 9/11/20 26 | */ 27 | public class RenderContext extends LuContext { 28 | private final Context mContext; 29 | private final String id; 30 | private final Handler mMainHandler; 31 | private Handler mWokerHandler; 32 | private final NativeRenderAPI api; 33 | private Frame frame; 34 | private SimpleStyleEngine mStyle; 35 | 36 | public RenderContext(Context ctx, String id) { 37 | super(); 38 | this.mContext = ctx; 39 | this.id = id; 40 | this.mMainHandler = new Handler(Looper.getMainLooper()); 41 | this.mWokerHandler = new Handler(LuEngine.getInstance().getWorkerHandler().getLooper()); 42 | this.api = new NativeRenderAPI(this); 43 | this.mStyle = new SimpleStyleEngine(); 44 | } 45 | 46 | public Frame wrap(LinearLayout view) { 47 | if (frame != null) { 48 | throw new IllegalStateException("context already wrapped with a root view"); 49 | } 50 | frame = new Frame(this, view); 51 | return frame; 52 | } 53 | 54 | public NativeComponent getRootComponent() { 55 | return frame; 56 | } 57 | 58 | public Context getContext() { 59 | return mContext; 60 | } 61 | 62 | public SimpleStyleEngine getStyle() { 63 | return mStyle; 64 | } 65 | 66 | public NativeRenderAPI getApi() { 67 | return api; 68 | } 69 | 70 | public void destroy() { 71 | //TODO 72 | } 73 | 74 | private final ArrayList pendingTasks = new ArrayList<>(); 75 | private final Runnable paddingRunnable = () -> { 76 | ArrayList tasks; 77 | synchronized (pendingTasks) { 78 | tasks = new ArrayList<>(pendingTasks); 79 | pendingTasks.clear(); 80 | } 81 | Log.d("RC", "post run async in batch:" + tasks.size()); 82 | 83 | TransitionManager.beginDelayedTransition((ViewGroup) getRootComponent().mView); 84 | for (Runnable task : tasks) { 85 | task.run(); 86 | } 87 | }; 88 | /** 89 | * run in ui thread 90 | * 91 | * @param runnable 92 | */ 93 | public void post(Runnable runnable) { 94 | if (Looper.myLooper() == mMainHandler.getLooper()) { 95 | Log.d("RC", "post run sync"); 96 | runnable.run(); 97 | } else { 98 | synchronized (this.pendingTasks) { 99 | this.pendingTasks.add(runnable); 100 | } 101 | this.mMainHandler.removeCallbacks(paddingRunnable); 102 | this.mMainHandler.post(paddingRunnable); 103 | } 104 | } 105 | 106 | /** 107 | * run in worker Thread 108 | * @param runnable 109 | */ 110 | public void postWorker(Runnable runnable) { 111 | if (Looper.myLooper() == mWokerHandler.getLooper()) { 112 | runnable.run(); 113 | } else { 114 | this.mWokerHandler.post(runnable); 115 | } 116 | } 117 | 118 | @Override 119 | protected void onContextScopeCreated(Scriptable scope) { 120 | LuEngine.getInstance().putProperty(scope, "$context", this); 121 | } 122 | 123 | public void attachStyles(Object o) { 124 | mStyle.attach((NativeObject) o); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /render/src/main/java/github/hotstu/lu/render/component/Comment.java: -------------------------------------------------------------------------------- 1 | package github.hotstu.lu.render.component; 2 | 3 | import android.view.View; 4 | 5 | import github.hotstu.lu.render.NativeComponent; 6 | import github.hotstu.lu.render.RenderContext; 7 | 8 | 9 | /** 10 | * @author hglf [hglf](https://github.com/hotstu) 11 | * @desc 12 | * @since 9/14/20 13 | */ 14 | public class Comment extends NativeComponent { 15 | 16 | public String text; 17 | 18 | public Comment(RenderContext context, String text) { 19 | super(context, "!"); 20 | this.text = text; 21 | } 22 | 23 | @Override 24 | protected void onAttachChild(NativeComponent child) { 25 | throw new IllegalStateException("should never happen"); 26 | } 27 | 28 | @Override 29 | protected void onAttachChild(NativeComponent child, int index) { 30 | throw new IllegalStateException("should never happen"); 31 | } 32 | 33 | @Override 34 | protected void onDetachChild(NativeComponent child) { 35 | throw new IllegalStateException("should never happen"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /render/src/main/java/github/hotstu/lu/render/component/Frame.java: -------------------------------------------------------------------------------- 1 | package github.hotstu.lu.render.component; 2 | 3 | import android.widget.LinearLayout; 4 | 5 | import org.mozilla.javascript.NativeObject; 6 | 7 | import java.util.Iterator; 8 | 9 | import github.hotstu.lu.render.NativeComponent; 10 | import github.hotstu.lu.render.RenderContext; 11 | import github.hotstu.lu.render.widget.LuLinearLayout; 12 | 13 | 14 | /** 15 | * @author hglf [hglf](https://github.com/hotstu) 16 | * @desc 17 | * @since 9/10/20 18 | */ 19 | public class Frame extends NativeComponent { 20 | public Frame(RenderContext context, LinearLayout view) { 21 | super(context, "ROOT"); 22 | this.mView = view; 23 | } 24 | 25 | public Frame(RenderContext context, String tag) { 26 | super(context, tag); 27 | LuLinearLayout mView = new LuLinearLayout(context.getContext()); 28 | mView.setOrientation(LinearLayout.VERTICAL); 29 | mView.setComponent(this); 30 | //Random random = new Random(); 31 | //int[] colors = new int[]{Color.GREEN, Color.RED, Color.BLUE}; 32 | 33 | 34 | //mView.setBackgroundColor(colors[random.nextInt(colors.length)]); 35 | //mView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); 36 | this.mView = mView; 37 | 38 | } 39 | 40 | @Override 41 | public void setAttribute(String attr, String value) { 42 | super.setAttribute(attr, value); 43 | } 44 | 45 | @Override 46 | protected void onAttachChild(NativeComponent child) { 47 | //TODO batch update view 48 | if (child.getView() != null) { 49 | mRenderContext.post(() -> { 50 | this.mView.addView(child.getView()); 51 | child.onAttached(); 52 | }); 53 | } 54 | } 55 | 56 | @Override 57 | protected void onAttachChild(NativeComponent child, int index) { 58 | //TODO batch update view 59 | if (child.getView() != null) { 60 | mRenderContext.post(() -> { 61 | this.mView.addView(child.getView(), index); 62 | child.onAttached(); 63 | }); 64 | } 65 | 66 | } 67 | 68 | @Override 69 | protected void onDetachChild(NativeComponent child) { 70 | mRenderContext.post(() -> { 71 | this.mView.removeView(child.getView()); 72 | }); 73 | } 74 | 75 | @Override 76 | public void onAttached() { 77 | super.onAttached(); 78 | } 79 | 80 | 81 | 82 | @Override 83 | public void onApplyStyle(NativeObject o) { 84 | super.onApplyStyle(o); 85 | Iterator iterator = o.keySet().iterator(); 86 | while (iterator.hasNext()) { 87 | String next = (String) iterator.next(); 88 | String value = (String) o.get(next); 89 | switch (next) { 90 | case "orientation": 91 | mView.setOrientation(parseOrientation(value)); 92 | break; 93 | case "gravity": 94 | mView.setGravity(parseGravity(value)); 95 | break; 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /render/src/main/java/github/hotstu/lu/render/component/Text.java: -------------------------------------------------------------------------------- 1 | package github.hotstu.lu.render.component; 2 | 3 | import android.view.Gravity; 4 | import android.widget.LinearLayout; 5 | import android.widget.TextView; 6 | 7 | import github.hotstu.lu.render.NativeComponent; 8 | import github.hotstu.lu.render.RenderContext; 9 | 10 | 11 | /** 12 | * @author hglf [hglf](https://github.com/hotstu) 13 | * @desc 14 | * @since 9/10/20 15 | */ 16 | public class Text extends NativeComponent { 17 | public String text; 18 | 19 | public Text(RenderContext context, String text) { 20 | super(context, null); 21 | this.text = text; 22 | this.mView = new TextView(context.getContext()); 23 | this.mView.setText(this.text); 24 | } 25 | 26 | @Override 27 | protected void onAttachChild(NativeComponent child) { 28 | throw new IllegalStateException("should never happen"); 29 | } 30 | 31 | @Override 32 | protected void onAttachChild(NativeComponent child, int index) { 33 | throw new IllegalStateException("should never happen"); 34 | } 35 | 36 | @Override 37 | protected void onDetachChild(NativeComponent child) { 38 | throw new IllegalStateException("should never happen"); 39 | } 40 | 41 | @Override 42 | public void onAttached() { 43 | super.onAttached(); 44 | if (this.mView.getLayoutParams() != null) { 45 | ((LinearLayout.LayoutParams) this.mView.getLayoutParams()).gravity = Gravity.CENTER; 46 | } 47 | this.mView.setText(this.text); 48 | 49 | } 50 | 51 | public void setText(String text) { 52 | this.text = text; 53 | mRenderContext.post(() -> { 54 | this.mView.setText(this.text); 55 | }); 56 | 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /render/src/main/java/github/hotstu/lu/render/event/Event.java: -------------------------------------------------------------------------------- 1 | package github.hotstu.lu.render.event; 2 | 3 | /** 4 | * @author hglf [hglf](https://github.com/hotstu) 5 | * @desc 6 | * @since 9/11/20 7 | */ 8 | public class Event { 9 | public String type; 10 | public Object target; 11 | } 12 | -------------------------------------------------------------------------------- /render/src/main/java/github/hotstu/lu/render/event/LuClickHandler.java: -------------------------------------------------------------------------------- 1 | package github.hotstu.lu.render.event; 2 | 3 | import android.view.View; 4 | 5 | import org.mozilla.javascript.Context; 6 | import org.mozilla.javascript.Function; 7 | 8 | import github.hotstu.lu.render.R; 9 | import github.hotstu.lu.render.RenderContext; 10 | 11 | /** 12 | * @author hglf [hglf](https://github.com/hotstu) 13 | * @desc 14 | * @since 9/21/20 15 | */ 16 | public class LuClickHandler implements View.OnClickListener { 17 | 18 | final String event; 19 | final RenderContext context; 20 | final Function func; 21 | 22 | public LuClickHandler(final String event, final Function func, final RenderContext context) { 23 | this.event = event; 24 | this.func = func; 25 | this.context = context; 26 | } 27 | 28 | public static void removeEventListener(View v, String event, Function func) { 29 | if (v.getTag(R.id.tag_event_click) != null) { 30 | v.setOnClickListener(null); 31 | v.setTag(R.id.tag_event_click, null); 32 | 33 | } 34 | } 35 | 36 | public LuClickHandler attach(View v) { 37 | v.setTag(R.id.tag_event_click, this); 38 | v.setOnClickListener(this); 39 | return this; 40 | } 41 | 42 | @Override 43 | public void onClick(View v) { 44 | Event e = new Event(); 45 | e.type = event; 46 | context.postWorker(() -> func.call(Context.getCurrentContext(), 47 | context.getContextScope(), null, new Object[]{e})); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /render/src/main/java/github/hotstu/lu/render/module/ClassManager.java: -------------------------------------------------------------------------------- 1 | package github.hotstu.lu.render.module; 2 | 3 | import java.util.Arrays; 4 | import java.util.Iterator; 5 | import java.util.TreeSet; 6 | 7 | import github.hotstu.lu.render.NativeComponent; 8 | import github.hotstu.lu.render.RenderContext; 9 | 10 | /** 11 | * @author hglf [hglf](https://github.com/hotstu) 12 | * @desc 13 | * @since 9/21/20 14 | */ 15 | public class ClassManager { 16 | //TODO support remove style when class removed 17 | private final TreeSet clazzes = new TreeSet<>(); 18 | private final NativeComponent compoent; 19 | 20 | public ClassManager(NativeComponent component) { 21 | this.compoent = component; 22 | } 23 | 24 | public void addClass(String clazz) { 25 | boolean add = clazzes.add(clazz); 26 | if (add) { 27 | update(); 28 | } 29 | } 30 | 31 | public Iterator it() { 32 | return clazzes.iterator(); 33 | } 34 | 35 | public void removeClass(String clazz) { 36 | boolean remove = clazzes.remove(clazz); 37 | if (remove) { 38 | update(); 39 | } 40 | } 41 | 42 | public void setClass(String... clazzes) { 43 | this.clazzes.clear(); 44 | this.clazzes.addAll(Arrays.asList(clazzes)); 45 | update(); 46 | } 47 | 48 | public void update() { 49 | RenderContext renderContext = compoent.getRenderContext(); 50 | if (renderContext.getStyle() != null) { 51 | renderContext.getStyle().applyTo(compoent); 52 | } 53 | } 54 | 55 | public boolean hasClass(String clazz) { 56 | return this.clazzes.contains(clazz); 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /render/src/main/java/github/hotstu/lu/render/style/SimpleStyleEngine.java: -------------------------------------------------------------------------------- 1 | package github.hotstu.lu.render.style; 2 | 3 | import org.mozilla.javascript.NativeObject; 4 | 5 | import java.util.Iterator; 6 | 7 | import github.hotstu.lu.render.NativeComponent; 8 | 9 | /** 10 | * @author hglf [hglf](https://github.com/hotstu) 11 | * @desc 目前对css的支持只是简单处理(非常简单, 需要完善) 12 | * @since 9/21/20 13 | */ 14 | public class SimpleStyleEngine { 15 | private NativeObject mJss; 16 | 17 | public void attach(NativeObject jss) { 18 | this.mJss = jss; 19 | } 20 | 21 | public void applyTo(NativeComponent component) { 22 | Iterator it = component.getClassManager().it(); 23 | while (it.hasNext()) { 24 | final String next = it.next(); 25 | if (mJss.containsKey(next)) { 26 | component.onApplyStyle((NativeObject) mJss.get(next)); 27 | } 28 | } 29 | } 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /render/src/main/java/github/hotstu/lu/render/widget/LuLinearLayout.java: -------------------------------------------------------------------------------- 1 | package github.hotstu.lu.render.widget; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.View; 6 | import android.widget.LinearLayout; 7 | 8 | import androidx.annotation.Nullable; 9 | 10 | import github.hotstu.lu.render.NativeComponent; 11 | 12 | 13 | /** 14 | * @author hglf [hglf](https://github.com/hotstu) 15 | * @desc 16 | * @since 9/14/20 17 | */ 18 | public class LuLinearLayout extends LinearLayout { 19 | NativeComponent component; 20 | public LuLinearLayout(Context context) { 21 | super(context); 22 | } 23 | 24 | public void setComponent(NativeComponent component) { 25 | this.component = component; 26 | } 27 | 28 | public LuLinearLayout(Context context, @Nullable AttributeSet attrs) { 29 | super(context, attrs); 30 | } 31 | 32 | public LuLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 33 | super(context, attrs, defStyleAttr); 34 | } 35 | 36 | @Override 37 | public void addView(View child, int index) { 38 | super.addView(child, index); 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return "CpLinear{" + 44 | "component=" + component + 45 | '}'; 46 | } 47 | 48 | @Override 49 | public void removeView(View view) { 50 | super.removeView(view); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /render/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':base' 2 | include ':engine' 3 | include ':render' 4 | include ':play' 5 | include ':app' 6 | rootProject.name = "lu" --------------------------------------------------------------------------------