diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5c7ab2e --- /dev/null +++ b/.gitignore @@ -0,0 +1,65 @@ +# Built application files +*.apk +*.ap_ + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ + +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties +token.properties +keystore.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# Intellij +*.iml +.idea/ +.idea/workspace.xml +.idea/tasks.xml +.idea/gradle.xml +.idea/dictionaries +.idea/libraries + +# Keystore files +*.jks + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild + +# Google Services (e.g. APIs or Firebase) +google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json +apktool/ +app/sampledata/ +psFiles/ +projectFilesBackup/ +*.hprof +checksum.txt +RouterDocument/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..5398201 --- /dev/null +++ b/README.md @@ -0,0 +1,104 @@ +# Library Analysis Gradle Plugin + +## 概述 + +分析各依赖库文件的大小 + +1. 支持大文件提醒 +2. 支持忽略部分依赖库大小(大小显示为灰色) +3. 支持依赖节点大小统计 +4. 输出aar的PackageName以及冲突提示 +5. 输出依赖库被直接依赖次数(Used)、包含的依赖库数量(Contains) +6. 标识可移除的依赖库(实验性功能) + +>可移除的依赖库 +例如有这样的依赖关系:A->B->C, A->C,那么A~~->C~~ + +## 配置 + +```gradle +buildscript { + repositories { + // ... + maven { url "https://jitpack.io" } + } + dependencies { + classpath 'com.timecat.plugin:report:#LAST_VERSION' + } +} + +apply plugin: 'com.timecat.plugin.report' + +libReport { + output = [ + "txt", "html" // default + ] + ignore = [ + "com.android.support:support-v4" + ] + log = true +} +``` + +## 使用 + +``` +gradle libReportReleaseCompileClasspath ... +``` + +### output + +**/build/reports/timecat/analysis/library/compile/Tree.html** + + + + + +**/build/reports/timecat/analysis/library/compile/Tree.txt** + +``` + 6.050 MB 0 B \--- project :analysis-sample + 20.275 KB 20.275 KB +--- com.android.support:multidex:1.0.1 + 0 B 0 B +--- project :module-library + 174 KB 104 KB +--- com.squareup.moshi:moshi:1.2.0 + 70.328 KB 70.328 KB | \--- com.squareup.okio:okio:1.8.0 + 199 KB 199 KB +--- com.alibaba:fastjson:1.1.54.android + 1.432 MB 1.143 MB +--- com.fasterxml.jackson.core:jackson-databind:2.7.0 + 49.701 KB 49.701 KB | +--- com.fasterxml.jackson.core:jackson-annotations:2.7.0 + 246 KB 246 KB | \--- com.fasterxml.jackson.core:jackson-core:2.7.0 + 314 KB 259 KB +--- com.github.Raizlabs.DBFlow:dbflow:3.1.1 + 33.072 KB 33.072 KB | +--- com.github.Raizlabs.DBFlow:dbflow-core:3.1.1 + 21.275 KB 21.275 KB | \--- com.android.support:support-annotations:23.1.1 -> 24.1.0 + 325 KB 119 KB +--- com.squareup.retrofit:retrofit:1.9.0 + 206 KB 206 KB | \--- com.google.code.gson:gson:2.3.1 + 492 KB 86.272 KB +--- com.squareup.retrofit2:retrofit:2.1.0 + 406 KB 335 KB | \--- com.squareup.okhttp3:okhttp:3.3.0 + 70.328 KB 70.328 KB | \--- com.squareup.okio:okio:1.8.0 + 464 KB 464 KB +--- com.github.bumptech.glide:glide:3.7.0 + 118 KB 118 KB +--- com.squareup.picasso:picasso:2.5.2 + 1.456 MB 11.842 KB +--- com.facebook.fresco:fresco:0.13.0 + 200 KB 106 KB | +--- com.facebook.fresco:drawee:0.13.0 + 0 B 1.167 MB | | +--- com.android.support:support-v4:23.2.1 + 0 B 21.275 KB | | | \--- com.android.support:support-annotations:23.2.1 -> 24.1.0 + 93.288 KB 93.288 KB | | \--- com.facebook.fresco:fbcore:0.13.0 + 93.288 KB 93.288 KB | +--- com.facebook.fresco:fbcore:0.13.0 + 1.341 MB 1.114 MB | \--- com.facebook.fresco:imagepipeline:0.13.0 + 0 B 1.167 MB | +--- com.android.support:support-v4:23.2.1 + 232 KB 102 KB | +--- com.facebook.fresco:imagepipeline-base:0.13.0 + 0 B 1.167 MB | | +--- com.android.support:support-v4:23.2.1 + 37.199 KB 37.199 KB | | +--- com.parse.bolts:bolts-tasks:1.4.0 + 93.288 KB 93.288 KB | | \--- com.facebook.fresco:fbcore:0.13.0 + 37.199 KB 37.199 KB | +--- com.parse.bolts:bolts-tasks:1.4.0 + 93.288 KB 93.288 KB | \--- com.facebook.fresco:fbcore:0.13.0 + 18.699 KB 18.699 KB +--- com.jakewharton.timber:timber:4.3.0 + 1.111 MB 9.466 KB +--- io.reactivex:rxandroid:1.2.1 + 1.102 MB 1.102 MB | \--- io.reactivex:rxjava:1.1.6 -> 1.3.0 + 1.102 MB 1.102 MB +--- io.reactivex:rxjava:1.3.0 + 54.464 KB 16.989 KB +--- com.jakewharton:butterknife:8.4.0 + 37.475 KB 16.199 KB | +--- com.jakewharton:butterknife-annotations:8.4.0 + 21.275 KB 21.275 KB | | \--- com.android.support:support-annotations:24.1.0 + 21.275 KB 21.275 KB | \--- com.android.support:support-annotations:24.1.0 + 9.570 KB 9.570 KB +--- com.jakewharton.scalpel:scalpel:1.1.2 + 3.603 KB 3.603 KB \--- com.jakewharton:process-phoenix:1.0.2 + ... +``` diff --git a/analysis-sample/.gitignore b/analysis-sample/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/analysis-sample/.gitignore @@ -0,0 +1 @@ +/build diff --git a/analysis-sample/build.gradle b/analysis-sample/build.gradle new file mode 100644 index 0000000..2007aa4 --- /dev/null +++ b/analysis-sample/build.gradle @@ -0,0 +1,79 @@ +apply plugin: 'com.android.application' +//apply plugin: 'com.timecat.plugin.report' +//apply plugin: 'kotlin-android' +//apply plugin: 'kotlin-android-extensions' + +android { + compileSdk 30 + + defaultConfig { + applicationId "com.timecat.android.analysis.sample" + minSdkVersion 9 + targetSdkVersion 29 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + lintOptions { + abortOnError false + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + implementation 'com.android.support:multidex:1.0.1' + implementation project(":module-library") + + implementation 'com.squareup.moshi:moshi:1.2.0' + implementation 'com.alibaba:fastjson:1.1.54.android' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.7.0' + + implementation "com.github.Raizlabs.DBFlow:dbflow:3.1.1" + + implementation 'com.squareup.retrofit:retrofit:1.9.0' + implementation 'com.squareup.retrofit2:retrofit:2.1.0' + + implementation 'com.github.bumptech.glide:glide:3.7.0' + implementation 'com.squareup.picasso:picasso:2.5.2' + implementation 'com.facebook.fresco:fresco:0.13.0' + + implementation 'com.jakewharton.timber:timber:4.3.0' + + implementation 'io.reactivex:rxandroid:1.2.1' + implementation 'io.reactivex:rxjava:1.3.0' +// implementation 'io.reactivex.rxjava2:rxjava:2.0.0-RC2' + + implementation 'com.jakewharton:butterknife:8.4.0' + implementation 'com.jakewharton.scalpel:scalpel:1.1.2' + implementation 'com.jakewharton:process-phoenix:1.0.2' + + androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { + exclude group: 'com.android.support', module: 'support-annotations' + }) + + testImplementation 'junit:junit:4.12' +} + +//libReport { +//// log = true +//// fullTree = true +//// output = [ +//// "txt", "html" +//// ] +// ignore = [ +// "com.android.support:support-v4" +// ] +//} \ No newline at end of file diff --git a/analysis-sample/proguard-rules.pro b/analysis-sample/proguard-rules.pro new file mode 100644 index 0000000..eeb461f --- /dev/null +++ b/analysis-sample/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in D:\sdk\Android/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/analysis-sample/src/androidTest/java/com/timecat/android/analysis/sample/ExampleInstrumentedTest.java b/analysis-sample/src/androidTest/java/com/timecat/android/analysis/sample/ExampleInstrumentedTest.java new file mode 100644 index 0000000..38fcb27 --- /dev/null +++ b/analysis-sample/src/androidTest/java/com/timecat/android/analysis/sample/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.timecat.android.analysis.sample; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumentation test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.timecat.android.analysis.sample", appContext.getPackageName()); + } +} diff --git a/analysis-sample/src/main/AndroidManifest.xml b/analysis-sample/src/main/AndroidManifest.xml new file mode 100644 index 0000000..ccc11f8 --- /dev/null +++ b/analysis-sample/src/main/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/analysis-sample/src/main/java/com/timecat/android/analysis/sample/BaseActivity.java b/analysis-sample/src/main/java/com/timecat/android/analysis/sample/BaseActivity.java new file mode 100644 index 0000000..485df2f --- /dev/null +++ b/analysis-sample/src/main/java/com/timecat/android/analysis/sample/BaseActivity.java @@ -0,0 +1,91 @@ +package com.timecat.android.analysis.sample; + +import android.app.Activity; +import android.os.Bundle; +import android.support.annotation.Nullable; + +import rx.Observable; +import rx.Observable.OnSubscribe; +import rx.Subscriber; +import rx.functions.Func1; + +/** + * @author 林学渊 + * @email linxy59@mail2.sysu.edu.cn + * @date 2021/9/10 + * @description null + * @usage null + */ +public abstract class BaseActivity extends Activity { + + Observable> ready; + + @Override + protected final void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + onBaseCreate(savedInstanceState); + onAfterCreate(savedInstanceState); + } + + private void onAfterCreate(@Nullable Bundle state) { + if (InitManager.isHasInit()) { + ready = null; + afterCreate(state); + return; + } + createReady(state); + } + + private void createReady(Bundle state) { + if (ready == null) { + ready = InitManager + .ready() + .map(new Func1() { + @Override + public Object call(Object result) { + afterCreate(state); + return result; + } + }); + } + } + + protected abstract void afterCreate(@Nullable Bundle state); + + protected abstract void onBaseCreate(@Nullable Bundle savedInstanceState); + + @Override + protected void onResume() { + super.onResume(); + if (ready == null) { + onObsResume(); + } else { + Observable> obs = genObsResume(); + if (obs != null) { + Observable.concat(ready, obs).subscribe(); + } else { + ready.subscribe(); + } + } + } + + @Override + protected void onPause() { + super.onPause(); + ready = null; + } + + protected Observable> genObsResume() { + return Observable.create(new OnSubscribe() { + @Override + public void call(Subscriber super Object> subscriber) { + BaseActivity.this.onObsResume(); + subscriber.onNext(null); + subscriber.onCompleted(); + } + }); + } + + protected void onObsResume() { + } +} diff --git a/analysis-sample/src/main/java/com/timecat/android/analysis/sample/DemoApplication.java b/analysis-sample/src/main/java/com/timecat/android/analysis/sample/DemoApplication.java new file mode 100644 index 0000000..10b7932 --- /dev/null +++ b/analysis-sample/src/main/java/com/timecat/android/analysis/sample/DemoApplication.java @@ -0,0 +1,20 @@ +package com.timecat.android.analysis.sample; + +import android.app.Application; + +/** + * @author 林学渊 + * @email linxy59@mail2.sysu.edu.cn + * @date 2021/9/10 + * @description null + * @usage null + */ +public class DemoApplication extends Application { + + @Override + public void onCreate() { + super.onCreate(); + + InitManager.initLazy(this); + } +} diff --git a/analysis-sample/src/main/java/com/timecat/android/analysis/sample/InitManager.java b/analysis-sample/src/main/java/com/timecat/android/analysis/sample/InitManager.java new file mode 100644 index 0000000..5b0cf15 --- /dev/null +++ b/analysis-sample/src/main/java/com/timecat/android/analysis/sample/InitManager.java @@ -0,0 +1,65 @@ +package com.timecat.android.analysis.sample; + + +import android.content.Context; +import android.os.SystemClock; + +import rx.Observable; +import rx.android.schedulers.AndroidSchedulers; +import rx.functions.Func0; +import rx.schedulers.Schedulers; + +/** + * @author 林学渊 + * @email linxy59@mail2.sysu.edu.cn + * @date 2021/9/10 + * @description null + * @usage null + */ +public class InitManager { + + private static Context sContext; + + private static Context sCache; + private static boolean hasInit; + + public static void initLazy(Context ctx) { + sCache = ctx; + } + + public static boolean isHasInit() { + return hasInit; + } + + public static Context getContext() { + return sContext; + } + + public static Observable> ready() { + return Observable.defer( + new Func0>() { + @Override + public Observable call() { + init(sCache); + return Observable.just(true); + } + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } + + public static void clear() { + hasInit = false; + sContext = null; + } + + private static synchronized void init(Context ctx) { + if (hasInit) { + return; + } + SystemClock.sleep(2000); + sContext = ctx; + hasInit = true; + } + +} diff --git a/analysis-sample/src/main/java/com/timecat/android/analysis/sample/MainActivity.java b/analysis-sample/src/main/java/com/timecat/android/analysis/sample/MainActivity.java new file mode 100644 index 0000000..966189c --- /dev/null +++ b/analysis-sample/src/main/java/com/timecat/android/analysis/sample/MainActivity.java @@ -0,0 +1,43 @@ +package com.timecat.android.analysis.sample; + +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.util.Log; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.TextView; + +public class MainActivity extends BaseActivity { + + TextView mTextMain; + Button mBtnGo; + + @Override + protected void afterCreate(@Nullable Bundle state) { + Log.d("MAIN", "set fail!"); + mTextMain.setText(InitManager.getContext().getString(R.string.fail)); + } + + @Override + protected void onBaseCreate(@Nullable Bundle savedInstanceState) { + setContentView(R.layout.activity_main); + mTextMain = (TextView) findViewById(R.id.tv_main); + mBtnGo = (Button) findViewById(R.id.btn_go); + } + + @Override + protected void onObsResume() { + super.onObsResume(); + mTextMain.setText(InitManager.getContext().getString(R.string.success)); + mBtnGo.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View listener) { + InitManager.clear(); + Intent intent = new Intent(MainActivity.this, MainActivity.class); + MainActivity.this.startActivity(intent); + } + }); + } +} diff --git a/analysis-sample/src/main/res/layout/activity_main.xml b/analysis-sample/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..1133aa4 --- /dev/null +++ b/analysis-sample/src/main/res/layout/activity_main.xml @@ -0,0 +1,26 @@ + + + + + + + diff --git a/analysis-sample/src/main/res/mipmap-hdpi/ic_launcher.png b/analysis-sample/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..cde69bc Binary files /dev/null and b/analysis-sample/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/analysis-sample/src/main/res/mipmap-mdpi/ic_launcher.png b/analysis-sample/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..c133a0c Binary files /dev/null and b/analysis-sample/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/analysis-sample/src/main/res/mipmap-xhdpi/ic_launcher.png b/analysis-sample/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..bfa42f0 Binary files /dev/null and b/analysis-sample/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/analysis-sample/src/main/res/mipmap-xxhdpi/ic_launcher.png b/analysis-sample/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..324e72c Binary files /dev/null and b/analysis-sample/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/analysis-sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/analysis-sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..aee44e1 Binary files /dev/null and b/analysis-sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/analysis-sample/src/main/res/values-w820dp/dimens.xml b/analysis-sample/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 0000000..63fc816 --- /dev/null +++ b/analysis-sample/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,6 @@ + + + 64dp + diff --git a/analysis-sample/src/main/res/values/colors.xml b/analysis-sample/src/main/res/values/colors.xml new file mode 100644 index 0000000..3ab3e9c --- /dev/null +++ b/analysis-sample/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #3F51B5 + #303F9F + #FF4081 + diff --git a/analysis-sample/src/main/res/values/dimens.xml b/analysis-sample/src/main/res/values/dimens.xml new file mode 100644 index 0000000..47c8224 --- /dev/null +++ b/analysis-sample/src/main/res/values/dimens.xml @@ -0,0 +1,5 @@ + + + 16dp + 16dp + diff --git a/analysis-sample/src/main/res/values/strings.xml b/analysis-sample/src/main/res/values/strings.xml new file mode 100644 index 0000000..d4e5b6a --- /dev/null +++ b/analysis-sample/src/main/res/values/strings.xml @@ -0,0 +1,5 @@ + + AnalysisSample + Success + Fail! + diff --git a/analysis-sample/src/main/res/values/styles.xml b/analysis-sample/src/main/res/values/styles.xml new file mode 100644 index 0000000..f11f745 --- /dev/null +++ b/analysis-sample/src/main/res/values/styles.xml @@ -0,0 +1,3 @@ + + + diff --git a/analysis-sample/src/test/java/com/timecat/android/analysis/sample/ExampleUnitTest.java b/analysis-sample/src/test/java/com/timecat/android/analysis/sample/ExampleUnitTest.java new file mode 100644 index 0000000..14d1b81 --- /dev/null +++ b/analysis-sample/src/test/java/com/timecat/android/analysis/sample/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.timecat.android.analysis.sample; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..f62a7e5 --- /dev/null +++ b/build.gradle @@ -0,0 +1,31 @@ +buildscript { + repositories { + maven { url "file://${gradle.ext.localMavenDir.absolutePath}" } + mavenCentral() + google() + maven { url "https://jitpack.io" } + } + dependencies { + classpath 'com.android.tools.build:gradle:7.0.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.30" +// classpath "com.timecat.plugin:report:1.0.0" + } +} + +//plugins { +// id "com.timecat.plugin.report" apply false +//} + +allprojects { + repositories { + maven { url "file://${gradle.ext.localMavenDir.absolutePath}" } + mavenCentral() + google() + maven { url "https://jitpack.io" } + } + +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/context.gradle b/context.gradle new file mode 100644 index 0000000..c790e3c --- /dev/null +++ b/context.gradle @@ -0,0 +1,19 @@ +def loadProperties(filename) { + Properties properties = new Properties() + + def file = new File(settingsDir, filename) + + if (file.exists()) { + properties.load(file.newDataInputStream()) + } else { + logger.warn("Properties file ${file} does not exist") + } + + return properties +} + +gradle.ext.appProperties = loadProperties('app.properties') +gradle.ext.moduleConfig = loadProperties('moduleConfig.properties') +gradle.ext.localMavenDir = file(gradle.ext.moduleConfig.localMavenPath) +gradle.ext.localGradleDir = file(gradle.ext.moduleConfig.localGradlePath) + diff --git a/gradle-mvn-push.gradle b/gradle-mvn-push.gradle new file mode 100644 index 0000000..0849e69 --- /dev/null +++ b/gradle-mvn-push.gradle @@ -0,0 +1,43 @@ +apply plugin: 'maven-publish' + +afterEvaluate { + publishing { + publications { + maven_aar_with_source(MavenPublication) { + from components.java + + groupId POM_GROUP + artifactId POM_ARTIFACT_ID + version POM_VERSION_NAME + +// artifact(jar) +// artifact(source: file(project.buildDir.path + "/outputs/jar/" + "${project.name}-${buildTypeName}.jar"), extension: 'jar') { +// builtBy jarPackage +// } + pom { + name = POM_NAME + packaging = POM_PACKAGING + description = POM_DESCRIPTION + url = POM_URL + inceptionYear = POM_INCEPTION_YEAR + licenses { + license { + name = 'The Apache License, Version 2.0' + url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + id = 'linxueyuan' + name = 'Lin Xueyuan' + email = 'linxy59@mail2.sysu.edu.cn' + } + } + } + } + } + repositories { + maven { url "file://${gradle.ext.localMavenDir.absolutePath}" } + } + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..aec3fca --- /dev/null +++ b/gradle.properties @@ -0,0 +1,9 @@ +POM_GROUP=com.timecat.plugin +POM_ARTIFACT_ID=report +POM_VERSION_NAME=1.0.0 + +POM_NAME=Library Analysis Gradle Plugin +POM_PACKAGING=jar +POM_DESCRIPTION=A Gradle plugin for analysis library +POM_INCEPTION_YEAR=2021 +POM_URL=http://github.com/LinXueyuanStdio/GradleDependancyReport diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..13372ae Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..fe4ddd8 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sat Mar 13 10:38:09 CST 2021 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..9d82f78 --- /dev/null +++ b/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/image/module-list.png b/image/module-list.png new file mode 100644 index 0000000..b7ebc8b Binary files /dev/null and b/image/module-list.png differ diff --git a/image/tree-x.png b/image/tree-x.png new file mode 100644 index 0000000..3296425 Binary files /dev/null and b/image/tree-x.png differ diff --git a/image/tree.png b/image/tree.png new file mode 100644 index 0000000..8f31b1c Binary files /dev/null and b/image/tree.png differ diff --git a/kotlin-sample/.gitignore b/kotlin-sample/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/kotlin-sample/.gitignore @@ -0,0 +1 @@ +/build diff --git a/kotlin-sample/build.gradle b/kotlin-sample/build.gradle new file mode 100644 index 0000000..08b4ea3 --- /dev/null +++ b/kotlin-sample/build.gradle @@ -0,0 +1,53 @@ +apply plugin: 'com.android.application' +//apply plugin: 'com.timecat.plugin.report' + +apply plugin: 'kotlin-android' + +android { + compileSdk 25 + + defaultConfig { + applicationId "com.timecat.android.kotlin.sample" + minSdkVersion 9 + targetSdkVersion 29 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + lintOptions { + abortOnError false + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.5.30" + compileOnly 'com.android.support:support-annotations:26.1.0' + + androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { + exclude group: 'com.android.support', module: 'support-annotations' + }) + + testImplementation 'junit:junit:4.12' +} + +//libReport { +//// log = true +//// fullTree = true +//// output = [ +//// "txt", "html" +//// ] +//} \ No newline at end of file diff --git a/kotlin-sample/proguard-rules.pro b/kotlin-sample/proguard-rules.pro new file mode 100644 index 0000000..eeb461f --- /dev/null +++ b/kotlin-sample/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in D:\sdk\Android/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/kotlin-sample/src/androidTest/java/com/timecat/android/analysis/sample/ExampleInstrumentedTest.java b/kotlin-sample/src/androidTest/java/com/timecat/android/analysis/sample/ExampleInstrumentedTest.java new file mode 100644 index 0000000..38fcb27 --- /dev/null +++ b/kotlin-sample/src/androidTest/java/com/timecat/android/analysis/sample/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.timecat.android.analysis.sample; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumentation test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.timecat.android.analysis.sample", appContext.getPackageName()); + } +} diff --git a/kotlin-sample/src/main/AndroidManifest.xml b/kotlin-sample/src/main/AndroidManifest.xml new file mode 100644 index 0000000..adbb08f --- /dev/null +++ b/kotlin-sample/src/main/AndroidManifest.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/kotlin-sample/src/main/java/com/timecat/android/kotlin/sample/MainActivity.kt b/kotlin-sample/src/main/java/com/timecat/android/kotlin/sample/MainActivity.kt new file mode 100644 index 0000000..7e631da --- /dev/null +++ b/kotlin-sample/src/main/java/com/timecat/android/kotlin/sample/MainActivity.kt @@ -0,0 +1,12 @@ +package com.timecat.android.kotlin.sample + +import android.app.Activity +import android.os.Bundle + +class MainActivity : Activity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + } +} diff --git a/kotlin-sample/src/main/res/layout/activity_main.xml b/kotlin-sample/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..eb5c274 --- /dev/null +++ b/kotlin-sample/src/main/res/layout/activity_main.xml @@ -0,0 +1,9 @@ + + + + diff --git a/kotlin-sample/src/main/res/mipmap-hdpi/ic_launcher.png b/kotlin-sample/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..cde69bc Binary files /dev/null and b/kotlin-sample/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/kotlin-sample/src/main/res/mipmap-mdpi/ic_launcher.png b/kotlin-sample/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..c133a0c Binary files /dev/null and b/kotlin-sample/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/kotlin-sample/src/main/res/mipmap-xhdpi/ic_launcher.png b/kotlin-sample/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..bfa42f0 Binary files /dev/null and b/kotlin-sample/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/kotlin-sample/src/main/res/mipmap-xxhdpi/ic_launcher.png b/kotlin-sample/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..324e72c Binary files /dev/null and b/kotlin-sample/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/kotlin-sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/kotlin-sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..aee44e1 Binary files /dev/null and b/kotlin-sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/kotlin-sample/src/main/res/values-w820dp/dimens.xml b/kotlin-sample/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 0000000..63fc816 --- /dev/null +++ b/kotlin-sample/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,6 @@ + + + 64dp + diff --git a/kotlin-sample/src/main/res/values/colors.xml b/kotlin-sample/src/main/res/values/colors.xml new file mode 100644 index 0000000..3ab3e9c --- /dev/null +++ b/kotlin-sample/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #3F51B5 + #303F9F + #FF4081 + diff --git a/kotlin-sample/src/main/res/values/dimens.xml b/kotlin-sample/src/main/res/values/dimens.xml new file mode 100644 index 0000000..47c8224 --- /dev/null +++ b/kotlin-sample/src/main/res/values/dimens.xml @@ -0,0 +1,5 @@ + + + 16dp + 16dp + diff --git a/kotlin-sample/src/main/res/values/strings.xml b/kotlin-sample/src/main/res/values/strings.xml new file mode 100644 index 0000000..36ecad3 --- /dev/null +++ b/kotlin-sample/src/main/res/values/strings.xml @@ -0,0 +1,5 @@ + + AnalysisSample + Success + Fail! + diff --git a/kotlin-sample/src/main/res/values/styles.xml b/kotlin-sample/src/main/res/values/styles.xml new file mode 100644 index 0000000..f11f745 --- /dev/null +++ b/kotlin-sample/src/main/res/values/styles.xml @@ -0,0 +1,3 @@ + + + diff --git a/kotlin-sample/src/test/java/com/timecat/android/analysis/sample/ExampleUnitTest.java b/kotlin-sample/src/test/java/com/timecat/android/analysis/sample/ExampleUnitTest.java new file mode 100644 index 0000000..14d1b81 --- /dev/null +++ b/kotlin-sample/src/test/java/com/timecat/android/analysis/sample/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.timecat.android.analysis.sample; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/module-library/.gitignore b/module-library/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/module-library/.gitignore @@ -0,0 +1 @@ +/build diff --git a/module-library/build.gradle b/module-library/build.gradle new file mode 100644 index 0000000..b79a350 --- /dev/null +++ b/module-library/build.gradle @@ -0,0 +1,31 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 25 + + defaultConfig { + minSdkVersion 9 + targetSdkVersion 25 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + testImplementation 'junit:junit:4.12' + androidTestImplementation 'com.android.support.test:runner:1.0.2' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' +} diff --git a/module-library/proguard-rules.pro b/module-library/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/module-library/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/module-library/src/androidTest/java/com/timecat/android/lib/report/module/ExampleInstrumentedTest.java b/module-library/src/androidTest/java/com/timecat/android/lib/report/module/ExampleInstrumentedTest.java new file mode 100644 index 0000000..ea6e83c --- /dev/null +++ b/module-library/src/androidTest/java/com/timecat/android/lib/report/module/ExampleInstrumentedTest.java @@ -0,0 +1,27 @@ +package com.timecat.android.lib.report.module; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.timecat.android.lib.report.module.test", appContext.getPackageName()); + } +} diff --git a/module-library/src/main/AndroidManifest.xml b/module-library/src/main/AndroidManifest.xml new file mode 100644 index 0000000..3770b9a --- /dev/null +++ b/module-library/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/module-library/src/main/res/values/strings.xml b/module-library/src/main/res/values/strings.xml new file mode 100644 index 0000000..cfa6f60 --- /dev/null +++ b/module-library/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + module-library + diff --git a/module-library/src/test/java/com/timecat/android/lib/report/module/ExampleUnitTest.java b/module-library/src/test/java/com/timecat/android/lib/report/module/ExampleUnitTest.java new file mode 100644 index 0000000..12b29d8 --- /dev/null +++ b/module-library/src/test/java/com/timecat/android/lib/report/module/ExampleUnitTest.java @@ -0,0 +1,18 @@ +package com.timecat.android.lib.report.module; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/moduleConfig.properties b/moduleConfig.properties new file mode 100644 index 0000000..e025dd0 --- /dev/null +++ b/moduleConfig.properties @@ -0,0 +1,4 @@ +# 本地 maven 的路径 +localMavenPath=../mempool/TimeCatMaven +# 本地 gradle 的路径 +localGradlePath=../mempool/TimeCatGradle diff --git a/plugin/build.gradle b/plugin/build.gradle new file mode 100644 index 0000000..33a92dd --- /dev/null +++ b/plugin/build.gradle @@ -0,0 +1,48 @@ +apply plugin: 'groovy' +apply plugin: 'java-gradle-plugin' + +javadoc.options.encoding = 'UTF-8' + +sourceSets { + main { + java { + srcDir 'src/main/java' + } + + groovy { + srcDir 'src/main/groovy' + } + } +} + +repositories { + mavenCentral() + google() + maven { url "https://jitpack.io" } +} + +dependencies { + implementation gradleApi() + implementation 'com.android.tools.build:gradle:7.0.0' + implementation 'com.google.code.gson:gson:2.8.7' + implementation group: 'commons-io', name: 'commons-io', version: '2.9.0' + + testImplementation group: 'junit', name: 'junit', version: '4.13.2' + testImplementation('org.spockframework:spock-core:1.0-groovy-2.4') { + exclude module: 'groovy-all' + } + testImplementation 'org.gmock:gmock:0.8.2' +} + +gradlePlugin { + plugins { + report { + // 在 app 模块需要通过 id 引用这个插件 + id = 'com.timecat.plugin.report' + // 实现这个插件的类的路径 + implementationClass = 'com.timecat.plugin.report.analysis.LibraryAnalysisPlugin' + } + } +} + +apply from: "${rootDir}/gradle-mvn-push.gradle" diff --git a/plugin/src/main/groovy/com/timecat/plugin/report/analysis/LibraryAnalysisPlugin.groovy b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/LibraryAnalysisPlugin.groovy new file mode 100644 index 0000000..3533dd6 --- /dev/null +++ b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/LibraryAnalysisPlugin.groovy @@ -0,0 +1,65 @@ +package com.timecat.plugin.report.analysis + +import com.timecat.plugin.report.analysis.ext.LibraryAnalysisExtension +import com.timecat.plugin.report.analysis.util.Logger +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration + +/** + * 1. 白名单 + * 2. 数据统计 + * 3. 共用标识 + */ +class LibraryAnalysisPlugin implements Plugin { + private static final EXTENSION_NAME = 'libReport' + private static final BASE_GROUP = 'reporting' + private static final TASK_PREFIX = 'libReport' + + private LibraryAnalysisExtension extension + + @Override + void apply(Project project) { + extension = project.extensions.create(EXTENSION_NAME, LibraryAnalysisExtension) + + project.afterEvaluate { + createTask(project) + } + } + + void createTask(Project project) { + def configurations = project.configurations + + configurations.findAll { + return !it.allDependencies.isEmpty() && getConfigurationSize(it) > 0 + }.each { + def conf = it.getName() + def task = project.tasks.create(genTaskName(conf), com.timecat.plugin.report.analysis.task.DependencyTreeReportTask) + task.dependencyConfiguration = it + task.group = BASE_GROUP + task.dependencyExtension = extension + if (!extension.log) { + Logger.D = null + } + } + } + + static String genTaskName(String name) { + char[] arr = name.toCharArray() + if (arr[0].lowerCase) { + arr[0] = Character.toUpperCase(arr[0]) + return "${TASK_PREFIX}${String.valueOf(arr)}" + } else { + return "${TASK_PREFIX}${name}" + } + } + + static int getConfigurationSize(Configuration conf) { + try { + return conf.incoming.resolutionResult.allDependencies.size() + } catch (Exception e) { + // ignore + } + return 0 + } +} diff --git a/plugin/src/main/groovy/com/timecat/plugin/report/analysis/convert/NodeConvert.groovy b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/convert/NodeConvert.groovy new file mode 100644 index 0000000..619726b --- /dev/null +++ b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/convert/NodeConvert.groovy @@ -0,0 +1,184 @@ +package com.timecat.plugin.report.analysis.convert + +import com.timecat.plugin.report.analysis.ext.LibraryAnalysisExtension +import com.timecat.plugin.report.analysis.model.FileDictionary +import com.timecat.plugin.report.analysis.model.FileInfo +import com.timecat.plugin.report.analysis.model.Library +import com.timecat.plugin.report.analysis.util.PackageChecker +import com.timecat.plugin.report.analysis.util.FileUtils +import com.timecat.plugin.report.analysis.model.Node + +/** + * @author 林学渊 + * @email linxy59@mail2.sysu.edu.cn + * @date 2021/9/10 + * @description null + * @usage null + */ +class NodeConvert { + + static class Args { + FileDictionary fileDictionary + LibraryAnalysisExtension ext + PackageChecker checker + boolean isBrief = true + + Args(FileDictionary dic) { + this.fileDictionary = dic + } + + static Args get(FileDictionary dic) { + return new Args(dic) + } + + Args fileDictionary(FileDictionary dic) { + this.fileDictionary = dic + this + } + + Args extension(LibraryAnalysisExtension ext) { + this.ext = ext + this + } + + Args checker(PackageChecker checker) { + this.checker = checker + this + } + + Args brief(boolean isBrief) { + this.isBrief = isBrief + this + } + } + + static setNodeCanRemove(Node node) { + if (node && node.id == node.detail) { + node.canRemove = true + } + } + + static Node convert(Library lib, Args args, Set cacheIds = new HashSet<>()) { + boolean hasAdded = cacheIds.contains(lib.id) + boolean hasChildren = !lib.children.isEmpty() + + Node node = new Node() + node.id = lib.id + node.detail = lib.name + if (hasAdded && !lib.children.isEmpty() && args.isBrief) { + node.iconSkin = "omit" // 表示简略的依赖库集合 + } + + if (!hasAdded) { + cacheIds?.add(lib.id) + } + if (hasChildren && (!args.isBrief || !hasAdded)) { + if (hasChildren) { + // 已加入过的兄弟库 + Set ids = new HashSet<>() + // 已加入的节点 + Map nodes = new HashMap<>() + // 已添加过的所有依赖 + Set libs = new HashSet<>() + + lib.children.each { + Node child = convert(it, args, cacheIds) + node.addNode(child) + + // 当前依赖库的子库中是否包含兄弟库 + it.containIds.findAll { + key -> + // 找到重复的依赖库id集合 + ids.contains(key) + }.each { + key -> + // 标记重复 + setNodeCanRemove(nodes.get(key)) +// nodes.get(key)?.canRemove = true + } + + // 当前依赖库是否已在其他库中加入过 + if (libs.contains(it)) { + setNodeCanRemove(child) +// child.canRemove = true + } + + ids.add(it.id) + nodes.put(child.id, child) + libs.add(it) + libs.addAll(it.contains) + } + + // 对可移除的库进行标记 + node.children?.each { + if (it.canRemove) { + it.name = "${it.name}" + Logger.D?.log("${it.id} is can remove.") + } + } + } + } + + node.open = !args.isBrief || !hasAdded + node.totalSize = lib.getTotalSizeWithoutIgnore() + + // 存在依赖信息 + FileInfo info = lib.file + + def (txtTotalSize, txtSize, txtType) = outputTxt(info, lib, args, node) + + // Android package name + def packageName = parsePackageName(info, args.checker) + + // 累计依赖库大小 + 当前依赖库大小 + 依赖库名称 + node.name = "${txtTotalSize}${txtSize ?: ''} ${node.detail} ${packageName ?: ''}" + + node + } + + private static Object parsePackageName(FileInfo info, PackageChecker checker) { + def packageName = null + if (info?.type == 'aar' && checker) { + packageName = "${checker.parseModuleName(info.id, info.file)}" + } + packageName + } + + private static List outputTxt(FileInfo info, Library lib, Args args, Node node) { + def txtType = null + def txtSize = null + + if (info) { + def styleSize = lib.isIgnoreSize ? "tag-ignore" : (args.ext ? args.ext.getSizeTag(info.size) : 'tag-normal') + def styleType = "jar" == info.type ? 'type_jar' : 'type_aar' + + txtType = "" + txtSize = "${txtType}${FileUtils.convertFileSize(info.size)}" + + node.fileSize = info.size + } + + def styleTotalSize = "class='tag tag-ignore'" + if (!lib.isIgnoreSize) { + styleTotalSize = "class='tag' style='color:#fff;background-color:${genColorCode(node.totalSize, args.fileDictionary.totalSize)}'" + } + def txtTotalSize = "${FileUtils.convertFileSize(node.totalSize)}" + [txtTotalSize, txtSize, txtType] + } + + private static String genColorCode(long size, long max) { + if (max == 0L) { + return "#ffdddd" + } + final int color = 0xdd - 0x22 + def c = max - size * 4 + if (c < 0) { + c = 0 + } + + int result = color * c / max + result += (result << 8) + 0xff0000 + "#${Integer.toHexString(result)}" + } + +} diff --git a/plugin/src/main/groovy/com/timecat/plugin/report/analysis/ext/LibraryAnalysisExtension.groovy b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/ext/LibraryAnalysisExtension.groovy new file mode 100644 index 0000000..b1e553b --- /dev/null +++ b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/ext/LibraryAnalysisExtension.groovy @@ -0,0 +1,61 @@ +package com.timecat.plugin.report.analysis.ext + +import org.gradle.util.ConfigureUtil + +/** + * @author 林学渊 + * @email linxy59@mail2.sysu.edu.cn + * @date 2021/9/10 + * @description null + * @usage null + */ +class LibraryAnalysisExtension implements Serializable { + + private static final ANALYSIS_OUTPUT_PATH = "reports/timecat/analysis/library" + private static final SIZE_STYLES = ['tag-normal', 'tag-warning', 'tag-danger'] + private static final LAST_INDEX = SIZE_STYLES.size() - 1 + + final Set cacheIgnoreIds = new HashSet<>() + + String outputPath = ANALYSIS_OUTPUT_PATH + List ignore + List output = ["txt", "html"] + final LimitSizeConfig limit + boolean showTree = false + boolean log = false + List region = [200 * 1024, 1024 * 1024] + boolean fullTree = false + boolean showSize = true + boolean showSupport = true + + LibraryAnalysisExtension() { + limit = new LimitSizeConfig() + } + + void limit(Closure closure) { + ConfigureUtil.configure(closure, limit) + } + + String getSizeTag(long s) { + def index = region.findIndexOf { + s <= it + } + index = index < 0 ? LAST_INDEX : index + + SIZE_STYLES[index] + } + + boolean isIgnore(String id) { + if (cacheIgnoreIds.contains(id)) { + return true + } + boolean result = ignore?.find { + id.contains(it) + } + if (result) { + cacheIgnoreIds.add(id) + } + return result + } + +} diff --git a/plugin/src/main/groovy/com/timecat/plugin/report/analysis/ext/LimitSizeConfig.groovy b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/ext/LimitSizeConfig.groovy new file mode 100644 index 0000000..1751fca --- /dev/null +++ b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/ext/LimitSizeConfig.groovy @@ -0,0 +1,33 @@ +package com.timecat.plugin.report.analysis.ext; + +/** + * @author 林学渊 + * @email linxy59@mail2.sysu.edu.cn + * @date 2021/9/10 + * @description null + * @usage null + */ +class LimitSizeConfig { + + static final long DEFAULT_LIB_SIZE_LIMIT = 1024 * 1024; + static final long DEFAULT_FILE_SIZE_LIMIT = 100 * 1024; + + private long fileSize = DEFAULT_FILE_SIZE_LIMIT; + private long libSize = DEFAULT_LIB_SIZE_LIMIT; + + public long getFileSize() { + return fileSize; + } + + public void setFileSize(long fileSize) { + this.fileSize = fileSize; + } + + public long getLibSize() { + return libSize; + } + + public void setLibSize(long libSize) { + this.libSize = libSize; + } +} diff --git a/plugin/src/main/groovy/com/timecat/plugin/report/analysis/model/FileDictionary.groovy b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/model/FileDictionary.groovy new file mode 100644 index 0000000..5184df4 --- /dev/null +++ b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/model/FileDictionary.groovy @@ -0,0 +1,124 @@ +package com.timecat.plugin.report.analysis.model + + +import org.gradle.api.file.FileCollection +import com.timecat.plugin.report.analysis.util.FileUtils + + +/** + * @author 林学渊 + * @email linxy59@mail2.sysu.edu.cn + * @date 2021/9/10 + * @description 用于对指定id的依赖库查询其文件信息(主要是大小) + * @usage null + */ +class FileDictionary { + + static final SEPARATOR = File.separator + static final BUILD_DIR = "build${SEPARATOR}outputs${SEPARATOR}aar${SEPARATOR}" + final FileCollection fileCollection + Set files + final Map cacheFiles = new HashMap<>() + final Map cacheInfoMap = new HashMap<>() + long totalSize = 0L + long maxSize = 0L + long totalFindCount = 0l + + FileDictionary(FileCollection fileCollection) { + this.fileCollection = fileCollection + computeTotalSize() + } + + Set getFiles() { + if (files == null) { + files = fileCollection.getFiles() + } + files + } + + void computeTotalSize() { + totalSize = 0 + getFiles().each { + totalSize += it.size() + if (it.size() > maxSize) { + maxSize = it.size() + } + } + } + + /** + * 根据依赖库ID查找本地文件 + * @param dependencyId + * @return + */ + File findDependencyFile(String dependencyId) { + if (cacheFiles.containsKey(dependencyId)) { + return cacheFiles.get(dependencyId) + } + + def dependency = dependencyId.split("\\:") + def size = dependency.size() + def result = null + +// def sep = "\r\n" +// StringBuilder builder = new StringBuilder() +// builder.append("find start: ${dependencyId}").append(sep) + int count = 0 + + if (size == 3) { + def key = dependency.join(SEPARATOR) + def group = dependency[0].replace('.', SEPARATOR) + def keyFull = "${group}${SEPARATOR}${dependency[1]}${SEPARATOR}${dependency[2]}" + + result = getFiles().find { + count++ +// builder.append("find ${it.path} ==? ${key} ==? ${keyFull}").append(sep) + it.path.contains(keyFull) || it.path.contains(key) + } + } else if (size == 2) { + def key = "${BUILD_DIR}${dependency[1]}" + + result = getFiles().find { + count++ +// builder.append("find ${it.path} ==? ${key}").append(sep) + it.path.contains(key) + } + } + + totalFindCount += count + + if (result != null) { +// Logger.W?.log "find result: " + result.path + " tcount:" + totalFindCount + cacheFiles.put(dependencyId, result) + files.remove(result) + } else { + Logger.W?.log "not found ${dependencyId} jar/aar file." +// Logger.W?.log builder.toString() + } + result + } + + /** + * 根据依赖库ID获取相关信息 + * @param dependencyId + * @return + */ + FileInfo findDependencyInfo(String dependencyId) { + def info = cacheInfoMap.get(dependencyId) + if (info == null) { + File file = findDependencyFile(dependencyId) + info = putCache(dependencyId, file) + } + info + } + + FileInfo putCache(String id, File file) { + if (file == null) { + return null + } + FileInfo info = new FileInfo(id, file.size(), FileUtils.getFileType(file.name), file) + cacheInfoMap.put(id, info) + info + } + +} diff --git a/plugin/src/main/groovy/com/timecat/plugin/report/analysis/model/FileInfo.groovy b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/model/FileInfo.groovy new file mode 100644 index 0000000..93c54b3 --- /dev/null +++ b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/model/FileInfo.groovy @@ -0,0 +1,23 @@ +package com.timecat.plugin.report.analysis.model + +/** + * @author 林学渊 + * @email linxy59@mail2.sysu.edu.cn + * @date 2021/9/10 + * @description null + * @usage null + */ +class FileInfo { + String id + long size + String type + File file + + FileInfo(String id, long size, String type, File file) { + this.id = id + this.size = size + this.type = type + this.file = file + } + +} diff --git a/plugin/src/main/groovy/com/timecat/plugin/report/analysis/model/Library.groovy b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/model/Library.groovy new file mode 100644 index 0000000..b5dec5f --- /dev/null +++ b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/model/Library.groovy @@ -0,0 +1,216 @@ +package com.timecat.plugin.report.analysis.model + +import com.timecat.plugin.report.analysis.util.Logger +import com.timecat.plugin.report.analysis.util.Timer +import org.gradle.api.tasks.diagnostics.internal.graph.nodes.RenderableDependency + +/** + * @author 林学渊 + * @email linxy59@mail2.sysu.edu.cn + * @date 2021/9/10 + * @description null + * @usage 依赖库模型 + */ +class Library { + + String name + String id + List children = new ArrayList<>() + // 该库包含的所有子库 + Set contains = new HashSet<>() + // 该库包含的所有子库的id集合 + Set containIds = new HashSet<>() + // 被使用次数 + int useCount = 1 + // 被使用次数(直接依赖) + int useCountImmediate = 1 + // 引用的文件信息 + FileInfo file + // 是否忽略文件大小 + boolean isIgnoreSize + + Library(String name, String id) { + this.name = name + this.id = id + } + + synchronized void addUseCount() { + useCount++ + children.each { + it.addUseCount() + } + } + + static Library create(RenderableDependency root, FileDictionary dictionary) { + def timer = new Timer() + + Map libraries = new HashMap<>() + def libRoot = create(root, true, dictionary, libraries) + + timer.mark(Logger.W, "create libRoot") + + // 合并useCount TODO: 可优化 + Map useCounts = new HashMap<>() + // 被直接依赖的次数 + Map useCountImmediacies = new HashMap<>() + + parseLibraryUseCount(libRoot, useCountImmediacies) + + timer.mark(Logger.W, "parseLibraryUseCount") + + libraries.values().each { + def id = it.id + + def count = useCounts.get(id) + def immediate = useCountImmediacies.get(id) + if (count && immediate) { + count += it.useCount + useCounts.put(id, count) + } else { + useCounts.put(id, it.useCount) + } + } + + libraries.values().each { + if (useCounts.containsKey(it.id)) { + it.useCount = useCounts.get(it.id) + } + if (useCountImmediacies.containsKey(it.id)) { + it.useCountImmediate = useCountImmediacies.get(it.id) + } + } + + timer.mark(Logger.W, "compute libraries") + + libRoot + } + + static void parseLibraryUseCount(Library library, Map useCounts) { + String id = library.id + boolean hasAdded = useCounts.containsKey(id) + if (!hasAdded) { + useCounts.put(id, 1) + } else { + useCounts.put(id, useCounts.get(id) + 1) + } + + if (!hasAdded && !library.children.isEmpty()) { + library.children.each { + parseLibraryUseCount(it, useCounts) + } + } + } + + + static Library create(RenderableDependency dependency, boolean isRoot, FileDictionary dictionary, Map cache) { + String id = dependency.id + String name = dependency.name +// Library lib = cache.get(id) + Library nLib = cache.get(name) + + if (nLib) { + nLib.addUseCount() + return nLib + } else { + def result = new Library(name, id) + cache.put(name, result) + + result.file = isRoot ? null : (dictionary?.findDependencyInfo(id)) + dependency.children.each { + result.children.add(create(it, false, dictionary, cache)) + } + result.computeContains() + return result + } + } + + /** + * 解析该依赖库包含的所有子库 + */ + void computeContains() { + if (!contains.isEmpty()) { + return + } + children.each { + it.computeContains() + contains.add(it) + contains.addAll(it.contains) + + containIds.add(it.id) + containIds.addAll(it.containIds) + } + } + + long getTotalSize() { + long size = file ? file.size : 0 + contains.each { + size += it.file?.size + } + size + } + + long getTotalSizeWithoutIgnore() { + if (isIgnoreSize) { + return 0 + } + long size = file ? file.size : 0 + contains.each { + if (!it.isIgnoreSize) { + if (it.file) { + size += it.file.size + } + } + } + size + } + + long getTotalSize(boolean isWithoutIgnore) { + isWithoutIgnore ? getTotalSizeWithoutIgnore() : getTotalSize() + } + + void applyIgnoreLibrary(String... ignores) { + ignores.each { + applyIgnoreLibrary(it) + } + } + + void applyIgnoreLibrary(String ignore) { + if (isIgnoreSize || ignore == null) { + return + } + isIgnoreSize = id.contains(ignore) + if (isIgnoreSize) { + children.each { + it.isIgnoreSize = true + } + } else { + children.each { + it.applyIgnoreLibrary(ignore) + } + } + } + + /** + * 判断某个库是否是当前库或其依赖库 + * @param library + * @return + */ + boolean isContains(Library library) { + return this == library || contains.contains(library) + } + + boolean equals(o) { + if (this.is(o)) return true + if (getClass() != o.class) return false + + Library library = (Library) o + + if (id != library.id) return false + + return true + } + + int hashCode() { + return (id != null ? id.hashCode() : 0) + } +} diff --git a/plugin/src/main/groovy/com/timecat/plugin/report/analysis/model/Node.groovy b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/model/Node.groovy new file mode 100644 index 0000000..78879c2 --- /dev/null +++ b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/model/Node.groovy @@ -0,0 +1,55 @@ +package com.timecat.plugin.report.analysis.model + + +/** + * @author 林学渊 + * @email linxy59@mail2.sysu.edu.cn + * @date 2021/9/10 + * @description null + * @usage null + */ +class Node { + String id + String name + boolean open + List children + long fileSize + long totalSize + String iconSkin + String detail + // 可移除的依赖库(重复添加) + boolean canRemove + + void addNode(Node node) { + if (node == null) { + return + } + List children = getChildren() + if (children == null) { + children = new ArrayList<>() + setChildren(children) + } + children.add(node) + } + + int getChildrenSize() { + children == null ? 0 : children.size() + } + + boolean hasChildren() { + return children != null && !children.isEmpty() + } + + @Override + String toString() { + return "Node{" + + "id='" + id + '\'' + + ", name='" + name + '\'' + + ", open=" + open + + ", children=" + children + + ", fileSize=" + fileSize + + ", totalSize=" + totalSize + + ", iconSkin='" + iconSkin + '\'' + + '}' + } +} diff --git a/plugin/src/main/groovy/com/timecat/plugin/report/analysis/render/HtmlRenderer.groovy b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/render/HtmlRenderer.groovy new file mode 100644 index 0000000..06e09f9 --- /dev/null +++ b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/render/HtmlRenderer.groovy @@ -0,0 +1,49 @@ +package com.timecat.plugin.report.analysis.render + +import com.google.gson.Gson +import com.timecat.plugin.report.analysis.ext.LibraryAnalysisExtension +import com.timecat.plugin.report.analysis.model.Node +import com.timecat.plugin.report.analysis.util.ResourceUtils +import groovy.json.JsonOutput + +/** + * @author 林学渊 + * @email linxy59@mail2.sysu.edu.cn + * @date 2021/9/10 + * @description null + * @usage null + */ +class HtmlRenderer { + + private static final GSON = new Gson() + + private String targetDir + + HtmlRenderer(target) { + this.targetDir = target + } + + public String render(Node root, OutputModuleList list, String msg, LibraryAnalysisExtension ext) { + String json = root ? "[${GSON.toJson(root)}]" : '[]' + if (msg && msg.length() > 0) { + msg = msg.replace("\r\n", "") + } else { + msg = "" + } + + def modules = JsonOutput.toJson(list) + def target = new File(targetDir, "Tree.html") + def support = ext.showSupport ? ResourceUtils.getTemplateFileContent("support.html") : "" + + def html = ResourceUtils.getTemplateFileContent("Tree.html") + .replace("%output_support%", support) + .replace("%output_msg%", msg) + .replace("%data%", modules) + .replace("%title%", root.id) + .replace("%nodes%", json) + target.setText(html, "UTF-8") + + target.path + } + +} \ No newline at end of file diff --git a/plugin/src/main/groovy/com/timecat/plugin/report/analysis/render/OutputModuleList.groovy b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/render/OutputModuleList.groovy new file mode 100644 index 0000000..dc5d9e2 --- /dev/null +++ b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/render/OutputModuleList.groovy @@ -0,0 +1,76 @@ +package com.timecat.plugin.report.analysis.render + +import com.timecat.plugin.report.analysis.util.FileUtils + +/** + * @author 林学渊 + * @email linxy59@mail2.sysu.edu.cn + * @date 2021/9/10 + * @description null + * @usage null + */ +class OutputModuleList { + + List modules = new ArrayList<>() + + void sortModules(Closure closure = { + first, two -> + two.sizeValue - first.sizeValue + }) { + modules.sort(closure) + + } + + void addModule(DependencyOutput output) { + if (!modules.contains(output)) { + modules.add(output) + } + } + + static class DependencyOutput { + + String name + String size + String type + String pkgName + String extInfo + String level + // 被使用次数 + int useCount + // 直接依赖次数 + int useCountImmediate + // 依赖库个数 + int libCount + long sizeValue + + DependencyOutput(String name, long size, String pkgName, String type, String extInfo, + int libCount = 0, int useCount = 0, int useCountImmediate = 0, String level = "") { + this.name = name + this.sizeValue = size + this.size = FileUtils.convertFileSize(size) + this.pkgName = pkgName + this.type = type + this.extInfo = extInfo + this.level = level + this.libCount = libCount + this.useCount = useCount + this.useCountImmediate = useCountImmediate + } + + boolean equals(o) { + if (this.is(o)) return true + if (getClass() != o.class) return false + + DependencyOutput that = (DependencyOutput) o + + if (name != that.name) return false + + return true + } + + int hashCode() { + return (name != null ? name.hashCode() : 0) + } + } + +} diff --git a/plugin/src/main/groovy/com/timecat/plugin/report/analysis/render/TextRenderer.groovy b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/render/TextRenderer.groovy new file mode 100644 index 0000000..b26e0aa --- /dev/null +++ b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/render/TextRenderer.groovy @@ -0,0 +1,90 @@ +package com.timecat.plugin.report.analysis.render + +import com.timecat.plugin.report.analysis.model.Node +import com.timecat.plugin.report.analysis.util.FileUtils + +/** + * @author 林学渊 + * @email linxy59@mail2.sysu.edu.cn + * @date 2021/9/10 + * @description null + * @usage null + */ +class TextRenderer { + + + private String targetDir + + TextRenderer(def target) { + this.targetDir = target + } + + String render(Node root, OutputModuleList list, String msg) { + if (msg && msg.length() > 0) { + msg = msg.replace("\r\n", "") + } else { + msg = "" + } + + def target = new File(targetDir, "Tree.txt") + StringBuilder builder = new StringBuilder() + builder.append(msg).append("\r\n") + + renderNode(builder, root, "", true) + showAllModules(builder, list) + + target.setText(builder.toString(), "UTF-8") + target.path + } + + private String renderNode(StringBuilder builder, Node node, String prev, boolean isLast) { + def fileSize = FileUtils.convertFileSize(node.fileSize) + def totalSize = FileUtils.convertFileSize(node.totalSize) + builder.append("${textAlign(totalSize, 10)}\t${textAlign(fileSize, 10)}\t") + + if (prev && !prev.isEmpty()) { + builder.append(prev) + } + boolean hasChildren = node.hasChildren() + def mark = "+---" + if (isLast) { + mark = "\\---" + } + + builder.append(mark).append(" ${node.detail}").append("\r\n") + if (hasChildren) { + List children = node.children + int size = children.size() + prev = isLast ? "${prev} \t" : "${prev}|\t" + for (int i = 0; i < size - 1; i++) { + renderNode(builder, children.get(i), prev, false) + } + renderNode(builder, children.get(size - 1), prev, true) + } + } + + static String textAlign(String content, int count) { + if (count == 0) { + return content + } + if (content != null && content.length() > count) { + return content + } + + char[] chrs = new char[count] + for (int i = count; --i >= 0;) { + chrs[i] = ' ' + } + content.getChars(0, content.size(), chrs, count - content.size()) + return String.valueOf(chrs) + } + + static void showAllModules(StringBuilder builder, OutputModuleList list) { + builder.append("\r\n") + list.modules.each { + builder.append(it.type).append('\t').append(it.name).append("\r\n") +// builder.append('compile \'').append(it.name).append('@').append(it.type).append('\'').append("\r\n") + } + } + +} diff --git a/plugin/src/main/groovy/com/timecat/plugin/report/analysis/task/DependencyTreeReportTask.groovy b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/task/DependencyTreeReportTask.groovy new file mode 100644 index 0000000..fe5cc0c --- /dev/null +++ b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/task/DependencyTreeReportTask.groovy @@ -0,0 +1,194 @@ +package com.timecat.plugin.report.analysis.task + +import com.timecat.plugin.report.analysis.convert.NodeConvert +import com.timecat.plugin.report.analysis.ext.LibraryAnalysisExtension +import com.timecat.plugin.report.analysis.model.FileDictionary +import com.timecat.plugin.report.analysis.model.Library +import com.timecat.plugin.report.analysis.render.HtmlRenderer +import com.timecat.plugin.report.analysis.render.OutputModuleList +import com.timecat.plugin.report.analysis.render.TextRenderer +import com.timecat.plugin.report.analysis.util.Logger +import com.timecat.plugin.report.analysis.util.PackageChecker +import com.timecat.plugin.report.analysis.util.ResourceUtils +import com.timecat.plugin.report.analysis.util.Timer +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.internal.artifacts.dependencies.DefaultProjectDependency +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.diagnostics.DependencyReportTask +import org.gradle.api.tasks.diagnostics.internal.ReportRenderer +import org.gradle.api.tasks.diagnostics.internal.dependencies.AsciiDependencyReportRenderer +import org.gradle.api.tasks.diagnostics.internal.graph.nodes.RenderableModuleResult + +/** + * @author 林学渊 + * @email linxy59@mail2.sysu.edu.cn + * @date 2021/9/10 + * @description null + * @usage null + */ +class DependencyTreeReportTask extends DependencyReportTask { + def renderer = new AsciiDependencyReportRenderer() + + @Internal + Configuration dependencyConfiguration + @Internal + LibraryAnalysisExtension dependencyExtension + + @Override + public ReportRenderer getRenderer() { + return renderer + } + + Configuration getDependencyConfiguration() { + return dependencyConfiguration + } + + void setDependencyConfiguration(Configuration dependencyConfiguration) { + this.dependencyConfiguration = dependencyConfiguration + } + + LibraryAnalysisExtension getDependencyExtension() { + return dependencyExtension + } + + void setDependencyExtension(LibraryAnalysisExtension dependencyExtension) { + this.dependencyExtension = dependencyExtension + } + + @Override + public void generate(Project project) throws IOException { + def timer = new Timer() + + try { + outputHtml() + + if (dependencyExtension.showTree) { + renderer.startConfiguration(dependencyConfiguration) + renderer.render(dependencyConfiguration) + renderer.completeConfiguration(dependencyConfiguration) + } + } catch (Exception e) { + Logger.W.log("generate report file failed!!! ERROR: " + e.message) + if (dependencyExtension.log) { + e.printStackTrace() + } + } + + timer.mark(Logger.W, "${getName()} total") + } + + private void outputHtml() { + def timer = new Timer() + + def output = prepareOutputPath() + ResourceUtils.copyResources(output) + + timer.mark(Logger.W, "copy resources") + + def resolutionResult = dependencyConfiguration.getIncoming().getResolutionResult() + def dep = new RenderableModuleResult(resolutionResult.getRoot()) + + timer.mark(Logger.W, "get dependencies") + +// def root = Node.create(dep) + +// timer.mark(Logger.W, "create nodes") + + // 通过依赖文件创建依赖字典 + def packageChecker = new PackageChecker() + +// def fs = configuration.getIncoming().getFiles() + def fs = dependencyConfiguration.fileCollection { + !(it instanceof DefaultProjectDependency) + } + def dictionary = new FileDictionary(fs) + +// root.supplyInfo(extension, dictionary, packageChecker) + def rootLib = Library.create(dep, dictionary) + + timer.mark(Logger.W, "create root library") + + dependencyExtension.ignore?.each { + rootLib.applyIgnoreLibrary(it) + } + + def root = NodeConvert.convert( + rootLib, + NodeConvert.Args.get(dictionary) + .extension(dependencyExtension) + .checker(packageChecker) + .brief(!dependencyExtension.fullTree) + ) + + timer.mark(Logger.W, "create root node") + + def msg = packageChecker.outputPackageRepeatList() + def list = outputModuleList(rootLib, packageChecker) + list.modules.each { + Logger.D?.log("module: ${it.name}") + } + + timer.mark(Logger.W, "output module list") + + if (dependencyExtension.output.contains("html")) { + def result = new HtmlRenderer(output).render(root, list, msg, dependencyExtension) + if (msg && !msg.isEmpty()) { + println msg + } + Logger.W?.log("Html output: ${result}") + + timer.mark(Logger.W, "output html file") + } + + if (dependencyExtension.output.contains("txt")) { + def result = new TextRenderer(output).render(root, list, msg) + Logger.W?.log("Txt output: ${result}") + + timer.mark(Logger.W, "output txt file") + } + } + + static OutputModuleList outputModuleList(Library root, PackageChecker checker) { + OutputModuleList list = new OutputModuleList() + root.contains?.each { + if (!it.file) { + list.addModule(new OutputModuleList.DependencyOutput(it.id, 0, "", + "pom", "", + it.contains.size(), it.useCount, it.useCountImmediate, "")) + return + } + def pkgName = checker.parseModuleName(it.id, it.file.file) + def isRepeat = checker.isRepeatPackage(pkgName) + list.addModule(new OutputModuleList.DependencyOutput(it.id, it.file.size, pkgName, + it.file.type, isRepeat ? "package name repeat" : "", + it.contains.size(), it.useCount, it.useCountImmediate, isRepeat ? "danger" : "")) + } + list.sortModules() + list + } + + @Deprecated + static OutputModuleList outputModuleList(FileDictionary dictionary, PackageChecker checker) { + OutputModuleList list = new OutputModuleList() + dictionary.cacheInfoMap.each { + key, value -> + def pkgName = checker.parseModuleName(key, value.file) + def isRepeat = checker.isRepeatPackage(pkgName) + list.addModule(new OutputModuleList.DependencyOutput(key, value.size, pkgName, + value.type, isRepeat ? "package name repeat" : "", 0, 0, 0, isRepeat ? "danger" : "")) + } + list.sortModules() + list + } + + private String prepareOutputPath() { + def path = "${project.buildDir}/${dependencyExtension.outputPath}/${dependencyConfiguration.name}" + def file = new File(path) + if (!file.exists()) { + file.mkdirs() + } + path + } + +} \ No newline at end of file diff --git a/plugin/src/main/groovy/com/timecat/plugin/report/analysis/util/FileUtils.groovy b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/util/FileUtils.groovy new file mode 100644 index 0000000..9ee6f35 --- /dev/null +++ b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/util/FileUtils.groovy @@ -0,0 +1,38 @@ +package com.timecat.plugin.report.analysis.util; + +/** + * @author 林学渊 + * @email linxy59@mail2.sysu.edu.cn + * @date 2021/9/10 + * @description null + * @usage null + */ +class FileUtils { + + static String convertFileSize(long size) { + long kb = 1024 + long mb = kb * 1024 + long gb = mb * 1024 + + if (size >= gb) { + String.format("%.3f GB", (float) size / gb) + } else if (size >= mb) { + float f = (float) size / mb + String.format(f > 100 ? "%.0f MB" : "%.3f MB", f) + } else if (size >= kb) { + float f = (float) size / kb; + String.format(f > 100 ? "%.0f KB" : "%.3f KB", f) + } else { + String.format("%d B", size) + } + } + + static String getFileType(String fileName) { + int index = fileName.lastIndexOf(".") + if (index >= 0) { + return fileName.substring(index + 1) + } + fileName + } + +} diff --git a/plugin/src/main/groovy/com/timecat/plugin/report/analysis/util/Logger.groovy b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/util/Logger.groovy new file mode 100644 index 0000000..cb83e21 --- /dev/null +++ b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/util/Logger.groovy @@ -0,0 +1,22 @@ +package com.timecat.plugin.report.analysis.util; + +/** + * @author 林学渊 + * @email linxy59@mail2.sysu.edu.cn + * @date 2021/9/10 + * @description null + * @usage null + */ +class Logger { + + public static Logger D = new Logger() + public static Logger W = new Logger() + + private Logger() { + } + + void log(def message) { + print "LibReport " + println message + } +} diff --git a/plugin/src/main/groovy/com/timecat/plugin/report/analysis/util/PackageChecker.groovy b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/util/PackageChecker.groovy new file mode 100644 index 0000000..7cd6b08 --- /dev/null +++ b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/util/PackageChecker.groovy @@ -0,0 +1,105 @@ +package com.timecat.plugin.report.analysis.util + +import org.apache.commons.io.IOUtils + +import java.util.regex.Matcher +import java.util.regex.Pattern +import java.util.zip.ZipEntry +import java.util.zip.ZipFile + +/** + * @author 林学渊 + * @email linxy59@mail2.sysu.edu.cn + * @date 2021/9/10 + * @description null + * @usage null + */ +public class PackageChecker { + + private final Map sModuleNames = new HashMap<>(); + private final Map sModuleRepeat = new HashMap<>(); + private static final Pattern PATTERN_PACKAGE = Pattern.compile("package=\"([^\"]+)\"") + + public void initModuleInfo() { + sModuleNames.clear(); + sModuleRepeat.clear(); + } + + public String parseModuleName(String module, File aar) { + if (aar == null || !aar.name.endsWith(".aar") || !aar.exists()) { + return "" + } + Logger.D?.log("package name parse ${aar.name}") + + def path = aar.absolutePath + if (sModuleNames.containsKey(module)) { + return sModuleNames.get(module) + } + + ZipFile zipFile = new ZipFile(path) + ZipEntry manifest = zipFile.entries().find { + it.name.equals("AndroidManifest.xml") + }; + def name = null + boolean isFound = false + if (manifest) { + def content = IOUtils.readLines(zipFile.getInputStream(manifest), "UTF-8") + isFound = content.find { + Matcher m = PATTERN_PACKAGE.matcher(it) + boolean isMatch = m.find() + if (isMatch) { + name = m.group(1) + } + isMatch + } + } + + if (!manifest) { + name = "Not Found Manifest.xml" + } else if (!isFound) { + name = "Not Found Package in AndroidManifest.xml" + } else { + putModuleIfRepeat(name, module) + } + + sModuleNames.put(module, name) + Logger.D?.log("package name find result: ${module} -> ${name}") + return name + } + + private void putModuleIfRepeat(String packageName, String module) { + Map.Entry result = sModuleNames.find { + key, value -> + value.equals(packageName) + } + if (result) { + if (sModuleRepeat.containsKey(packageName)) { + def value = sModuleRepeat.get(packageName) + Logger.D?.log("repeat: ${value}") + value += ",${module}" + Logger.D?.log("repeat add to: ${value}") + sModuleRepeat.put(packageName, value) + } else { + sModuleRepeat.put(packageName, "${result.key},${module}") + } + } + } + + public boolean isRepeatPackage(String pkgName) { + return sModuleRepeat.get(pkgName) + } + + public String outputPackageRepeatList() { + def builder = new StringBuilder(); + if (!sModuleRepeat.isEmpty()) { + def enter = "\r\n"; + builder.append("WARNING: Package Name Repeat!!!").append(enter) + sModuleRepeat.each { + key, value -> + builder.append(key).append(" use in [${value}]").append(enter) + } + } + builder.toString() + } + +} diff --git a/plugin/src/main/groovy/com/timecat/plugin/report/analysis/util/ResourceUtils.groovy b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/util/ResourceUtils.groovy new file mode 100644 index 0000000..d26215d --- /dev/null +++ b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/util/ResourceUtils.groovy @@ -0,0 +1,57 @@ +package com.timecat.plugin.report.analysis.util; + +/** + * @author 林学渊 + * @email linxy59@mail2.sysu.edu.cn + * @date 2021/9/10 + * @description null + * @usage null + */ +class ResourceUtils { + + static final RESOURCE_PATH = "/com/timecat/plugin/report/analysis/" + static final RESOURCE_FILES = [ + "css/z/img/", + "css/demo.css", + "css/z/ztree.css", + "css/z/img/line_conn.gif", + "css/z/img/loading.gif", + "css/z/img/zTreeStandard.gif", + "css/z/img/zTreeStandard.png", + "css/z/img/jar.png", + "css/z/img/aar.png", + + "js/", + "js/jquery.ztree.core.min.js", + "js/jquery.min.js", + "js/cytoscape-dagre.js", + "js/dagre.min.js" + ] + + private static obj = new ResourceUtils() + + private ResourceUtils() { + } + + static void copyResources(String targetPath) { + RESOURCE_FILES.each { + if (it.endsWith('/')) { + new File(targetPath, it).mkdirs() + return + } + + def target = new File(targetPath, it) + if (!target.exists()) { + def source = obj.getClass().getResourceAsStream("${RESOURCE_PATH}${it}") + target.withDataOutputStream { + os -> os << source + } + } + } + } + + static String getTemplateFileContent(String fileName) { + return obj.getClass().getResourceAsStream("${RESOURCE_PATH}${fileName}").getText("UTF-8") + } + +} diff --git a/plugin/src/main/groovy/com/timecat/plugin/report/analysis/util/Timer.groovy b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/util/Timer.groovy new file mode 100644 index 0000000..9548513 --- /dev/null +++ b/plugin/src/main/groovy/com/timecat/plugin/report/analysis/util/Timer.groovy @@ -0,0 +1,24 @@ +package com.timecat.plugin.report.analysis.util; + +/** + * @author 林学渊 + * @email linxy59@mail2.sysu.edu.cn + * @date 2021/9/10 + * @description null + * @usage null + */ +class Timer { + + long start + + Timer() { + start = System.currentTimeMillis() + } + + void mark(Logger logger, String message) { + long now = System.currentTimeMillis() + logger?.log "${message ?: ""} cost ${now - start}ms" + start = now + } + +} diff --git a/plugin/src/main/resources/com/timecat/plugin/report/analysis/Tree.html b/plugin/src/main/resources/com/timecat/plugin/report/analysis/Tree.html new file mode 100644 index 0000000..d7259dd --- /dev/null +++ b/plugin/src/main/resources/com/timecat/plugin/report/analysis/Tree.html @@ -0,0 +1,100 @@ + + + + %title% Dependencies + + + + + + + + + + + + + + + + + + Support By Library Analysis + + %output_support% + + Module List + %output_msg% + + + + Type + Name + Size + Used + Contains + Package Name(for aar) + Remark + + + + + + + + + + + + + + + + + + + + + + + Dependencies Tree + 注:为节省数据量,含有子节点的库若已经出现过了,就不再显示其子节点。 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugin/src/main/resources/com/timecat/plugin/report/analysis/css/demo.css b/plugin/src/main/resources/com/timecat/plugin/report/analysis/css/demo.css new file mode 100644 index 0000000..7313ebd --- /dev/null +++ b/plugin/src/main/resources/com/timecat/plugin/report/analysis/css/demo.css @@ -0,0 +1,74 @@ +html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td { + margin: 0;padding: 0;border: 0;outline: 0;font-weight: inherit;font-style: inherit;font-size: 100%;font-family:inherit;vertical-align: middle;} +body {color: #2f332a;font: 15px/21px Arial, Helvetica, simsun, sans-serif;background: #f0f6e4 \9;} +h1, h2, h3, h4, h5, h6 {color: #2f332a;font-weight: bold;font-family: Helvetica, Arial, sans-serif;padding-bottom: 5px;} +h1 {font-size: 24px;line-height: 34px;text-align: center;} +h2 {font-size: 14px;line-height: 24px;padding-top: 5px;} +h6 {font-weight: normal;font-size: 12px;letter-spacing: 1px;line-height: 24px;text-align: center;} +a {color:#3C6E31;text-decoration: underline;} +a:hover {background-color:#3C6E31;color:white;} +input.radio {margin: 0 2px 0 8px;} +input.radio.first {margin-left:0;} +input.empty {color: lightgray;} +code {color: #2f332a;} +.highlight_red {color:#A60000;} +.highlight_green {color:#A7F43D;} +li {list-style: circle;font-size: 12px;} +li.title {list-style: none;} +ul.list {margin-left: 17px;} + +div.content_wrap {width: 600px;height:380px;} +div.content_wrap div.left{float: left;width: 250px;} +div.content_wrap div.right{float: right;width: 340px;} +div.zTreeDemoBackground {width:250px;height:362px;text-align:left;} + +ul.ztree {margin: 10 10 10 10px;overflow-y:scroll;overflow-x:auto;} +ul.log {border: 1px solid #617775;background: #f0f6e4;width:300px;height:170px;overflow: hidden;} +ul.log.small {height:45px;} +ul.log li {color: #666666;list-style: none;padding-left: 10px;} +ul.log li.dark {background-color: #E3E3E3;} + +/* ruler */ +div.ruler {height:20px; width:220px; background-color:#f0f6e4;border: 1px solid #333; margin-bottom: 5px; cursor: pointer} +div.ruler div.cursor {height:20px; width:30px; background-color:#3C6E31; color:white; text-align: right; padding-right: 5px; cursor: pointer} + +.tag { + line-height: 12px; + display: inline; + font-weight: normal; + text-align: center; + white-space: nowrap; + vertical-align: middle; + -ms-touch-action: manipulation; + touch-action: manipulation; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; + vertical-align:middle; +} +.tag-ignore { + color: #fff; + background-color: #c6c6c6; + border-color: #bfbfbf; +} +.tag-normal { + color: #fff; + background-color: #BBDBBB; + border-color: #AAD2AA; +} +.tag-warning { + color: #fff; + background-color: #F2B866; + border-color: #F0AD4E; +} +.tag-danger { + color: #fff; + background-color: #F5726E; + border-color: #E3615D; +} +.ztree li span.tag {padding-right:2px; padding-left:2px;} \ No newline at end of file diff --git a/plugin/src/main/resources/com/timecat/plugin/report/analysis/css/z/img/aar.png b/plugin/src/main/resources/com/timecat/plugin/report/analysis/css/z/img/aar.png new file mode 100644 index 0000000..2a9350f Binary files /dev/null and b/plugin/src/main/resources/com/timecat/plugin/report/analysis/css/z/img/aar.png differ diff --git a/plugin/src/main/resources/com/timecat/plugin/report/analysis/css/z/img/jar.png b/plugin/src/main/resources/com/timecat/plugin/report/analysis/css/z/img/jar.png new file mode 100644 index 0000000..f850a82 Binary files /dev/null and b/plugin/src/main/resources/com/timecat/plugin/report/analysis/css/z/img/jar.png differ diff --git a/plugin/src/main/resources/com/timecat/plugin/report/analysis/css/z/img/line_conn.gif b/plugin/src/main/resources/com/timecat/plugin/report/analysis/css/z/img/line_conn.gif new file mode 100644 index 0000000..d561d36 Binary files /dev/null and b/plugin/src/main/resources/com/timecat/plugin/report/analysis/css/z/img/line_conn.gif differ diff --git a/plugin/src/main/resources/com/timecat/plugin/report/analysis/css/z/img/loading.gif b/plugin/src/main/resources/com/timecat/plugin/report/analysis/css/z/img/loading.gif new file mode 100644 index 0000000..e8c2892 Binary files /dev/null and b/plugin/src/main/resources/com/timecat/plugin/report/analysis/css/z/img/loading.gif differ diff --git a/plugin/src/main/resources/com/timecat/plugin/report/analysis/css/z/img/zTreeStandard.gif b/plugin/src/main/resources/com/timecat/plugin/report/analysis/css/z/img/zTreeStandard.gif new file mode 100644 index 0000000..50c94fd Binary files /dev/null and b/plugin/src/main/resources/com/timecat/plugin/report/analysis/css/z/img/zTreeStandard.gif differ diff --git a/plugin/src/main/resources/com/timecat/plugin/report/analysis/css/z/img/zTreeStandard.png b/plugin/src/main/resources/com/timecat/plugin/report/analysis/css/z/img/zTreeStandard.png new file mode 100644 index 0000000..ffda01e Binary files /dev/null and b/plugin/src/main/resources/com/timecat/plugin/report/analysis/css/z/img/zTreeStandard.png differ diff --git a/plugin/src/main/resources/com/timecat/plugin/report/analysis/css/z/ztree.css b/plugin/src/main/resources/com/timecat/plugin/report/analysis/css/z/ztree.css new file mode 100644 index 0000000..8ba6502 --- /dev/null +++ b/plugin/src/main/resources/com/timecat/plugin/report/analysis/css/z/ztree.css @@ -0,0 +1,104 @@ +/*------------------------------------- +zTree Style + +version: 3.5.19 +author: Hunter.z +email: hunter.z@263.net +website: http://code.google.com/p/jquerytree/ + +-------------------------------------*/ + +.ztree * {padding:0; margin:0; font-size:12px; font-family: Verdana, Arial, Helvetica, AppleGothic, sans-serif} +.ztree {margin:0; padding:5px; color:#333} +.ztree li{padding:0; margin:0; list-style:none; line-height:14px; text-align:left; white-space:nowrap; outline:0} +.ztree li ul{ margin:0; padding:0 0 0 18px} +.ztree li ul.line{ background:url(./img/line_conn.gif) 0 0 repeat-y;} + +.ztree li a {padding:1px 3px 0 0; margin:0; cursor:pointer; height:17px; color:#333; background-color: transparent; + text-decoration:none; vertical-align:top; display: inline-block} +.ztree li a:hover {text-decoration:underline} +.ztree li a.curSelectedNode {padding-top:0px; background-color:#FFE6B0; color:black; height:16px; border:1px #FFB951 solid; opacity:0.8;} +.ztree li a.curSelectedNode_Edit {padding-top:0px; background-color:#FFE6B0; color:black; height:16px; border:1px #FFB951 solid; opacity:0.8;} +.ztree li a.tmpTargetNode_inner {padding-top:0px; background-color:#316AC5; color:white; height:16px; border:1px #316AC5 solid; + opacity:0.8; filter:alpha(opacity=80)} +.ztree li a.tmpTargetNode_prev {} +.ztree li a.tmpTargetNode_next {} +.ztree li a input.rename {height:14px; width:80px; padding:0; margin:0; + font-size:12px; border:1px #7EC4CC solid; *border:0px} +.ztree li span {margin-right:2px; margin-left:2px;} +.ztree li span.button {line-height:0; margin:0; width:16px; height:16px; display: inline-block; vertical-align:middle; + border:0 none; cursor: pointer;outline:none; + background-color:transparent; background-repeat:no-repeat; background-attachment: scroll; + background-image:url("./img/zTreeStandard.png"); *background-image:url("./img/zTreeStandard.gif")} + +.ztree li span.button.chk {width:13px; height:13px; margin:0 3px 0 0; cursor: auto} +.ztree li span.button.chk.checkbox_false_full {background-position:0 0} +.ztree li span.button.chk.checkbox_false_full_focus {background-position:0 -14px} +.ztree li span.button.chk.checkbox_false_part {background-position:0 -28px} +.ztree li span.button.chk.checkbox_false_part_focus {background-position:0 -42px} +.ztree li span.button.chk.checkbox_false_disable {background-position:0 -56px} +.ztree li span.button.chk.checkbox_true_full {background-position:-14px 0} +.ztree li span.button.chk.checkbox_true_full_focus {background-position:-14px -14px} +.ztree li span.button.chk.checkbox_true_part {background-position:-14px -28px} +.ztree li span.button.chk.checkbox_true_part_focus {background-position:-14px -42px} +.ztree li span.button.chk.checkbox_true_disable {background-position:-14px -56px} +.ztree li span.button.chk.radio_false_full {background-position:-28px 0} +.ztree li span.button.chk.radio_false_full_focus {background-position:-28px -14px} +.ztree li span.button.chk.radio_false_part {background-position:-28px -28px} +.ztree li span.button.chk.radio_false_part_focus {background-position:-28px -42px} +.ztree li span.button.chk.radio_false_disable {background-position:-28px -56px} +.ztree li span.button.chk.radio_true_full {background-position:-42px 0} +.ztree li span.button.chk.radio_true_full_focus {background-position:-42px -14px} +.ztree li span.button.chk.radio_true_part {background-position:-42px -28px} +.ztree li span.button.chk.radio_true_part_focus {background-position:-42px -42px} +.ztree li span.button.chk.radio_true_disable {background-position:-42px -56px} + +.ztree li span.button.switch {width:18px; height:18px} +.ztree li span.button.root_open{background-position:-92px -54px} +.ztree li span.button.root_close{background-position:-74px -54px} +.ztree li span.button.roots_open{background-position:-92px 0} +.ztree li span.button.roots_close{background-position:-74px 0} +.ztree li span.button.center_open{background-position:-92px -18px} +.ztree li span.button.center_close{background-position:-74px -18px} +.ztree li span.button.bottom_open{background-position:-92px -36px} +.ztree li span.button.bottom_close{background-position:-74px -36px} +.ztree li span.button.noline_open{background-position:-92px -72px} +.ztree li span.button.noline_close{background-position:-74px -72px} +.ztree li span.button.root_docu{ background:none;} +.ztree li span.button.roots_docu{background-position:-56px 0} +.ztree li span.button.center_docu{background-position:-56px -18px} +.ztree li span.button.bottom_docu{background-position:-56px -36px} +.ztree li span.button.noline_docu{ background:none;} + +.ztree li span.button.ico_open{margin-right:2px; background-position:-110px -16px; vertical-align:top; *vertical-align:middle} +.ztree li span.button.ico_close{margin-right:2px; background-position:-110px 0; vertical-align:top; *vertical-align:middle} +.ztree li span.button.ico_docu{margin-right:2px; background-position:-110px -32px; vertical-align:top; *vertical-align:middle} +.ztree li span.button.edit {margin-right:2px; background-position:-110px -48px; vertical-align:top; *vertical-align:middle} +.ztree li span.button.remove {margin-right:2px; background-position:-110px -64px; vertical-align:top; *vertical-align:middle} + +.ztree li span.type{line-height:0; margin:0; width:16px; height:14px; display: inline-block; vertical-align:sub; + border:0 none; cursor: pointer;outline:none; + margin-right:2px;} +.ztree li span.type_aar{background-image:url("./img/aar.png");} +.ztree li span.type_jar{background-image:url("./img/jar.png");} +.ztree li span.button.omit_ico_docu{margin-right:2px; background-position:-110px 0; vertical-align:top; *vertical-align:middle} + +.ztree li span.button.ico_loading{margin-right:2px; background:url(./img/loading.gif) no-repeat scroll 0 0 transparent; vertical-align:top; *vertical-align:middle} + +ul.tmpTargetzTree {background-color:#FFE6B0; opacity:0.8; filter:alpha(opacity=80)} + +span.tmpzTreeMove_arrow {width:16px; height:16px; display: inline-block; padding:0; margin:2px 0 0 1px; border:0 none; position:absolute; + background-color:transparent; background-repeat:no-repeat; background-attachment: scroll; + background-position:-110px -80px; background-image:url("./img/zTreeStandard.png"); *background-image:url("./img/zTreeStandard.gif")} + +ul.ztree.zTreeDragUL {margin:0; padding:0; position:absolute; width:auto; height:auto;overflow:hidden; background-color:#cfcfcf; border:1px #00B83F dotted; opacity:0.8; filter:alpha(opacity=80)} +.zTreeMask {z-index:10000; background-color:#cfcfcf; opacity:0.0; filter:alpha(opacity=0); position:absolute} + +/* level style*/ +/*.ztree li span.button.level0 { + display:none; +} +.ztree li ul.level0 { + padding:0; + background:none; +}*/ \ No newline at end of file diff --git a/plugin/src/main/resources/com/timecat/plugin/report/analysis/js/cytoscape-dagre.js b/plugin/src/main/resources/com/timecat/plugin/report/analysis/js/cytoscape-dagre.js new file mode 100644 index 0000000..c93288b --- /dev/null +++ b/plugin/src/main/resources/com/timecat/plugin/report/analysis/js/cytoscape-dagre.js @@ -0,0 +1,192 @@ +;(function(){ 'use strict'; + + // registers the extension on a cytoscape lib ref + var register = function( cytoscape, dagre ){ + if( !cytoscape || !dagre ){ return; } // can't register if cytoscape unspecified + + var isFunction = function(o){ return typeof o === 'function'; }; + + // default layout options + var defaults = { + // dagre algo options, uses default value on undefined + nodeSep: undefined, // the separation between adjacent nodes in the same rank + edgeSep: undefined, // the separation between adjacent edges in the same rank + rankSep: undefined, // the separation between adjacent nodes in the same rank + rankDir: undefined, // 'TB' for top to bottom flow, 'LR' for left to right + minLen: function( edge ){ return 1; }, // number of ranks to keep between the source and target of the edge + edgeWeight: function( edge ){ return 1; }, // higher weight edges are generally made shorter and straighter than lower weight edges + + // general layout options + fit: true, // whether to fit to viewport + padding: 30, // fit padding + animate: false, // whether to transition the node positions + animationDuration: 500, // duration of animation in ms if enabled + animationEasing: undefined, // easing of animation if enabled + boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h } + ready: function(){}, // on layoutready + stop: function(){} // on layoutstop + }; + + // constructor + // options : object containing layout options + function DagreLayout( options ){ + var opts = this.options = {}; + for( var i in defaults ){ opts[i] = defaults[i]; } + for( var i in options ){ opts[i] = options[i]; } + } + + // runs the layout + DagreLayout.prototype.run = function(){ + var options = this.options; + var layout = this; + + var cy = options.cy; // cy is automatically populated for us in the constructor + var eles = options.eles; + + var getVal = function( ele, val ){ + return isFunction(val) ? val.apply( ele, [ ele ] ) : val; + }; + + var bb = options.boundingBox || { x1: 0, y1: 0, w: cy.width(), h: cy.height() }; + if( bb.x2 === undefined ){ bb.x2 = bb.x1 + bb.w; } + if( bb.w === undefined ){ bb.w = bb.x2 - bb.x1; } + if( bb.y2 === undefined ){ bb.y2 = bb.y1 + bb.h; } + if( bb.h === undefined ){ bb.h = bb.y2 - bb.y1; } + + var g = new dagre.graphlib.Graph({ + multigraph: true, + compound: true + }); + + var gObj = {}; + var setGObj = function( name, val ){ + if( val != null ){ + gObj[ name ] = val; + } + }; + + setGObj( 'nodesep', options.nodeSep ); + setGObj( 'edgesep', options.edgeSep ); + setGObj( 'ranksep', options.rankSep ); + setGObj( 'rankdir', options.rankDir ); + + g.setGraph( gObj ); + + g.setDefaultEdgeLabel(function() { return {}; }); + g.setDefaultNodeLabel(function() { return {}; }); + + // add nodes to dagre + var nodes = eles.nodes(); + for( var i = 0; i < nodes.length; i++ ){ + var node = nodes[i]; + var nbb = node.boundingBox(); + + g.setNode( node.id(), { + width: nbb.w, + height: nbb.h, + name: node.id() + } ); + + // console.log( g.node(node.id()) ); + } + + // set compound parents + for( var i = 0; i < nodes.length; i++ ){ + var node = nodes[i]; + + if( node.isChild() ){ + g.setParent( node.id(), node.parent().id() ); + } + } + + // add edges to dagre + var edges = eles.edges().stdFilter(function( edge ){ + return !edge.source().isParent() && !edge.target().isParent(); // dagre can't handle edges on compound nodes + }); + for( var i = 0; i < edges.length; i++ ){ + var edge = edges[i]; + + g.setEdge( edge.source().id(), edge.target().id(), { + minlen: getVal( edge, options.minLen ), + weight: getVal( edge, options.edgeWeight ), + name: edge.id() + }, edge.id() ); + + // console.log( g.edge(edge.source().id(), edge.target().id(), edge.id()) ); + } + + dagre.layout( g ); + + var gNodeIds = g.nodes(); + for( var i = 0; i < gNodeIds.length; i++ ){ + var id = gNodeIds[i]; + var n = g.node( id ); + + cy.getElementById(id).scratch().dagre = n; + } + + var dagreBB; + + if( options.boundingBox ){ + dagreBB = { x1: Infinity, x2: -Infinity, y1: Infinity, y2: -Infinity }; + nodes.forEach(function( node ){ + var dModel = node.scratch().dagre; + + dagreBB.x1 = Math.min( dagreBB.x1, dModel.x ); + dagreBB.x2 = Math.max( dagreBB.x2, dModel.x ); + + dagreBB.y1 = Math.min( dagreBB.y1, dModel.y ); + dagreBB.y2 = Math.max( dagreBB.y2, dModel.y ); + }); + + dagreBB.w = dagreBB.x2 - dagreBB.x1; + dagreBB.h = dagreBB.y2 - dagreBB.y1; + } else { + dagreBB = bb; + } + + var constrainPos = function( p ){ + if( options.boundingBox ){ + var xPct = (p.x - dagreBB.x1) / dagreBB.w; + var yPct = (p.y - dagreBB.y1) / dagreBB.h; + + return { + x: bb.x1 + xPct * bb.w, + y: bb.y1 + yPct * bb.h + }; + } else { + return p; + } + }; + + nodes.layoutPositions(layout, options, function(){ + var dModel = this.scratch().dagre; + + return constrainPos({ + x: dModel.x, + y: dModel.y + }); + }); + + return this; // chaining + }; + + cytoscape('layout', 'dagre', DagreLayout); + + }; + + if( typeof module !== 'undefined' && module.exports ){ // expose as a commonjs module + module.exports = register; + } + + if( typeof define !== 'undefined' && define.amd ){ // expose as an amd/requirejs module + define('cytoscape-dagre', function(){ + return register; + }); + } + + if( typeof cytoscape !== 'undefined' && typeof dagre !== 'undefined' ){ // expose to global cytoscape (i.e. window.cytoscape) + register( cytoscape, dagre ); + } + +})(); diff --git a/plugin/src/main/resources/com/timecat/plugin/report/analysis/js/dagre.min.js b/plugin/src/main/resources/com/timecat/plugin/report/analysis/js/dagre.min.js new file mode 100644 index 0000000..b7a9bbc --- /dev/null +++ b/plugin/src/main/resources/com/timecat/plugin/report/analysis/js/dagre.min.js @@ -0,0 +1,6 @@ +!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.dagre=e()}}(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;o0;--i){entry=buckets[i].dequeue();if(entry){results=results.concat(removeNode(g,buckets,zeroIdx,entry,true));break}}}}return results}function removeNode(g,buckets,zeroIdx,entry,collectPredecessors){var results=collectPredecessors?[]:undefined;_.each(g.inEdges(entry.v),function(edge){var weight=g.edge(edge),uEntry=g.node(edge.v);if(collectPredecessors){results.push({v:edge.v,w:edge.w})}uEntry.out-=weight;assignBucket(buckets,zeroIdx,uEntry)});_.each(g.outEdges(entry.v),function(edge){var weight=g.edge(edge),w=edge.w,wEntry=g.node(w);wEntry["in"]-=weight;assignBucket(buckets,zeroIdx,wEntry)});g.removeNode(entry.v);return results}function buildState(g,weightFn){var fasGraph=new Graph,maxIn=0,maxOut=0;_.each(g.nodes(),function(v){fasGraph.setNode(v,{v:v,"in":0,out:0})});_.each(g.edges(),function(e){var prevWeight=fasGraph.edge(e.v,e.w)||0,weight=weightFn(e),edgeWeight=prevWeight+weight;fasGraph.setEdge(e.v,e.w,edgeWeight);maxOut=Math.max(maxOut,fasGraph.node(e.v).out+=weight);maxIn=Math.max(maxIn,fasGraph.node(e.w)["in"]+=weight)});var buckets=_.range(maxOut+maxIn+3).map(function(){return new List});var zeroIdx=maxIn+1;_.each(fasGraph.nodes(),function(v){assignBucket(buckets,zeroIdx,fasGraph.node(v))});return{graph:fasGraph,buckets:buckets,zeroIdx:zeroIdx}}function assignBucket(buckets,zeroIdx,entry){if(!entry.out){buckets[0].enqueue(entry)}else if(!entry["in"]){buckets[buckets.length-1].enqueue(entry)}else{buckets[entry.out-entry["in"]+zeroIdx].enqueue(entry)}}},{"./data/list":5,"./graphlib":7,"./lodash":10}],9:[function(require,module,exports){"use strict";var _=require("./lodash"),acyclic=require("./acyclic"),normalize=require("./normalize"),rank=require("./rank"),normalizeRanks=require("./util").normalizeRanks,parentDummyChains=require("./parent-dummy-chains"),removeEmptyRanks=require("./util").removeEmptyRanks,nestingGraph=require("./nesting-graph"),addBorderSegments=require("./add-border-segments"),coordinateSystem=require("./coordinate-system"),order=require("./order"),position=require("./position"),util=require("./util"),Graph=require("./graphlib").Graph;module.exports=layout;function layout(g,opts){var time=opts&&opts.debugTiming?util.time:util.notime;time("layout",function(){var layoutGraph=time(" buildLayoutGraph",function(){return buildLayoutGraph(g)});time(" runLayout",function(){runLayout(layoutGraph,time)});time(" updateInputGraph",function(){updateInputGraph(g,layoutGraph)})})}function runLayout(g,time){time(" makeSpaceForEdgeLabels",function(){makeSpaceForEdgeLabels(g)});time(" removeSelfEdges",function(){removeSelfEdges(g)});time(" acyclic",function(){acyclic.run(g)});time(" nestingGraph.run",function(){nestingGraph.run(g)});time(" rank",function(){rank(util.asNonCompoundGraph(g))});time(" injectEdgeLabelProxies",function(){injectEdgeLabelProxies(g)});time(" removeEmptyRanks",function(){removeEmptyRanks(g)});time(" nestingGraph.cleanup",function(){nestingGraph.cleanup(g)});time(" normalizeRanks",function(){normalizeRanks(g)});time(" assignRankMinMax",function(){assignRankMinMax(g)});time(" removeEdgeLabelProxies",function(){removeEdgeLabelProxies(g)});time(" normalize.run",function(){normalize.run(g)});time(" parentDummyChains",function(){parentDummyChains(g)});time(" addBorderSegments",function(){addBorderSegments(g)});time(" order",function(){order(g)});time(" insertSelfEdges",function(){insertSelfEdges(g)});time(" adjustCoordinateSystem",function(){coordinateSystem.adjust(g)});time(" position",function(){position(g)});time(" positionSelfEdges",function(){positionSelfEdges(g)});time(" removeBorderNodes",function(){removeBorderNodes(g)});time(" normalize.undo",function(){normalize.undo(g)});time(" fixupEdgeLabelCoords",function(){fixupEdgeLabelCoords(g)});time(" undoCoordinateSystem",function(){coordinateSystem.undo(g)});time(" translateGraph",function(){translateGraph(g)});time(" assignNodeIntersects",function(){assignNodeIntersects(g)});time(" reversePoints",function(){reversePointsForReversedEdges(g)});time(" acyclic.undo",function(){acyclic.undo(g)})}function updateInputGraph(inputGraph,layoutGraph){_.each(inputGraph.nodes(),function(v){var inputLabel=inputGraph.node(v),layoutLabel=layoutGraph.node(v);if(inputLabel){inputLabel.x=layoutLabel.x;inputLabel.y=layoutLabel.y;if(layoutGraph.children(v).length){inputLabel.width=layoutLabel.width;inputLabel.height=layoutLabel.height}}});_.each(inputGraph.edges(),function(e){var inputLabel=inputGraph.edge(e),layoutLabel=layoutGraph.edge(e);inputLabel.points=layoutLabel.points;if(_.has(layoutLabel,"x")){inputLabel.x=layoutLabel.x;inputLabel.y=layoutLabel.y}});inputGraph.graph().width=layoutGraph.graph().width;inputGraph.graph().height=layoutGraph.graph().height}var graphNumAttrs=["nodesep","edgesep","ranksep","marginx","marginy"],graphDefaults={ranksep:50,edgesep:20,nodesep:50,rankdir:"tb"},graphAttrs=["acyclicer","ranker","rankdir","align"],nodeNumAttrs=["width","height"],nodeDefaults={width:0,height:0},edgeNumAttrs=["minlen","weight","width","height","labeloffset"],edgeDefaults={minlen:1,weight:1,width:0,height:0,labeloffset:10,labelpos:"r"},edgeAttrs=["labelpos"];function buildLayoutGraph(inputGraph){var g=new Graph({multigraph:true,compound:true}),graph=canonicalize(inputGraph.graph());g.setGraph(_.merge({},graphDefaults,selectNumberAttrs(graph,graphNumAttrs),_.pick(graph,graphAttrs)));_.each(inputGraph.nodes(),function(v){var node=canonicalize(inputGraph.node(v));g.setNode(v,_.defaults(selectNumberAttrs(node,nodeNumAttrs),nodeDefaults));g.setParent(v,inputGraph.parent(v))});_.each(inputGraph.edges(),function(e){var edge=canonicalize(inputGraph.edge(e));g.setEdge(e,_.merge({},edgeDefaults,selectNumberAttrs(edge,edgeNumAttrs),_.pick(edge,edgeAttrs)))});return g}function makeSpaceForEdgeLabels(g){var graph=g.graph();graph.ranksep/=2;_.each(g.edges(),function(e){var edge=g.edge(e);edge.minlen*=2;if(edge.labelpos.toLowerCase()!=="c"){if(graph.rankdir==="TB"||graph.rankdir==="BT"){edge.width+=edge.labeloffset}else{edge.height+=edge.labeloffset}}})}function injectEdgeLabelProxies(g){_.each(g.edges(),function(e){var edge=g.edge(e);if(edge.width&&edge.height){var v=g.node(e.v),w=g.node(e.w),label={rank:(w.rank-v.rank)/2+v.rank,e:e};util.addDummyNode(g,"edge-proxy",label,"_ep")}})}function assignRankMinMax(g){var maxRank=0;_.each(g.nodes(),function(v){var node=g.node(v);if(node.borderTop){node.minRank=g.node(node.borderTop).rank;node.maxRank=g.node(node.borderBottom).rank;maxRank=_.max(maxRank,node.maxRank)}});g.graph().maxRank=maxRank}function removeEdgeLabelProxies(g){_.each(g.nodes(),function(v){var node=g.node(v);if(node.dummy==="edge-proxy"){g.edge(node.e).labelRank=node.rank;g.removeNode(v)}})}function translateGraph(g){var minX=Number.POSITIVE_INFINITY,maxX=0,minY=Number.POSITIVE_INFINITY,maxY=0,graphLabel=g.graph(),marginX=graphLabel.marginx||0,marginY=graphLabel.marginy||0;function getExtremes(attrs){var x=attrs.x,y=attrs.y,w=attrs.width,h=attrs.height;minX=Math.min(minX,x-w/2);maxX=Math.max(maxX,x+w/2);minY=Math.min(minY,y-h/2);maxY=Math.max(maxY,y+h/2)}_.each(g.nodes(),function(v){getExtremes(g.node(v))});_.each(g.edges(),function(e){var edge=g.edge(e);if(_.has(edge,"x")){getExtremes(edge)}});minX-=marginX;minY-=marginY;_.each(g.nodes(),function(v){var node=g.node(v);node.x-=minX;node.y-=minY});_.each(g.edges(),function(e){var edge=g.edge(e);_.each(edge.points,function(p){p.x-=minX;p.y-=minY});if(_.has(edge,"x")){edge.x-=minX}if(_.has(edge,"y")){edge.y-=minY}});graphLabel.width=maxX-minX+marginX;graphLabel.height=maxY-minY+marginY}function assignNodeIntersects(g){_.each(g.edges(),function(e){var edge=g.edge(e),nodeV=g.node(e.v),nodeW=g.node(e.w),p1,p2;if(!edge.points){edge.points=[];p1=nodeW;p2=nodeV}else{p1=edge.points[0];p2=edge.points[edge.points.length-1]}edge.points.unshift(util.intersectRect(nodeV,p1));edge.points.push(util.intersectRect(nodeW,p2))})}function fixupEdgeLabelCoords(g){_.each(g.edges(),function(e){var edge=g.edge(e);if(_.has(edge,"x")){if(edge.labelpos==="l"||edge.labelpos==="r"){edge.width-=edge.labeloffset}switch(edge.labelpos){case"l":edge.x-=edge.width/2+edge.labeloffset;break;case"r":edge.x+=edge.width/2+edge.labeloffset;break}}})}function reversePointsForReversedEdges(g){_.each(g.edges(),function(e){var edge=g.edge(e);if(edge.reversed){edge.points.reverse()}})}function removeBorderNodes(g){_.each(g.nodes(),function(v){if(g.children(v).length){var node=g.node(v),t=g.node(node.borderTop),b=g.node(node.borderBottom),l=g.node(_.last(node.borderLeft)),r=g.node(_.last(node.borderRight));node.width=Math.abs(r.x-l.x);node.height=Math.abs(b.y-t.y);node.x=l.x+node.width/2;node.y=t.y+node.height/2}});_.each(g.nodes(),function(v){if(g.node(v).dummy==="border"){g.removeNode(v)}})}function removeSelfEdges(g){_.each(g.edges(),function(e){if(e.v===e.w){var node=g.node(e.v);if(!node.selfEdges){node.selfEdges=[]}node.selfEdges.push({e:e,label:g.edge(e)});g.removeEdge(e)}})}function insertSelfEdges(g){var layers=util.buildLayerMatrix(g);_.each(layers,function(layer){var orderShift=0;_.each(layer,function(v,i){var node=g.node(v);node.order=i+orderShift;_.each(node.selfEdges,function(selfEdge){util.addDummyNode(g,"selfedge",{width:selfEdge.label.width,height:selfEdge.label.height,rank:node.rank,order:i+ ++orderShift,e:selfEdge.e,label:selfEdge.label},"_se")});delete node.selfEdges})})}function positionSelfEdges(g){_.each(g.nodes(),function(v){var node=g.node(v);if(node.dummy==="selfedge"){var selfNode=g.node(node.e.v),x=selfNode.x+selfNode.width/2,y=selfNode.y,dx=node.x-x,dy=selfNode.height/2;g.setEdge(node.e,node.label);g.removeNode(v);node.label.points=[{x:x+2*dx/3,y:y-dy},{x:x+5*dx/6,y:y-dy},{x:x+dx,y:y},{x:x+5*dx/6,y:y+dy},{x:x+2*dx/3,y:y+dy}];node.label.x=node.x;node.label.y=node.y}})}function selectNumberAttrs(obj,attrs){return _.mapValues(_.pick(obj,attrs),Number)}function canonicalize(attrs){var newAttrs={};_.each(attrs,function(v,k){newAttrs[k.toLowerCase()]=v});return newAttrs}},{"./acyclic":2,"./add-border-segments":3,"./coordinate-system":4,"./graphlib":7,"./lodash":10,"./nesting-graph":11,"./normalize":12,"./order":17,"./parent-dummy-chains":22,"./position":24,"./rank":26,"./util":29}],10:[function(require,module,exports){var lodash;if(typeof require==="function"){try{lodash=require("lodash")}catch(e){}}if(!lodash){lodash=window._}module.exports=lodash},{lodash:51}],11:[function(require,module,exports){var _=require("./lodash"),util=require("./util");module.exports={run:run,cleanup:cleanup};function run(g){var root=util.addDummyNode(g,"root",{},"_root"),depths=treeDepths(g),height=_.max(depths)-1,nodeSep=2*height+1;g.graph().nestingRoot=root;_.each(g.edges(),function(e){g.edge(e).minlen*=nodeSep});var weight=sumWeights(g)+1;_.each(g.children(),function(child){dfs(g,root,nodeSep,weight,height,depths,child)});g.graph().nodeRankFactor=nodeSep}function dfs(g,root,nodeSep,weight,height,depths,v){var children=g.children(v);if(!children.length){if(v!==root){g.setEdge(root,v,{weight:0,minlen:nodeSep})}return}var top=util.addBorderNode(g,"_bt"),bottom=util.addBorderNode(g,"_bb"),label=g.node(v);g.setParent(top,v);label.borderTop=top;g.setParent(bottom,v);label.borderBottom=bottom;_.each(children,function(child){dfs(g,root,nodeSep,weight,height,depths,child);var childNode=g.node(child),childTop=childNode.borderTop?childNode.borderTop:child,childBottom=childNode.borderBottom?childNode.borderBottom:child,thisWeight=childNode.borderTop?weight:2*weight,minlen=childTop!==childBottom?1:height-depths[v]+1;g.setEdge(top,childTop,{weight:thisWeight,minlen:minlen,nestingEdge:true});g.setEdge(childBottom,bottom,{weight:thisWeight,minlen:minlen,nestingEdge:true})});if(!g.parent(v)){g.setEdge(root,top,{weight:0,minlen:height+depths[v]})}}function treeDepths(g){var depths={};function dfs(v,depth){var children=g.children(v);if(children&&children.length){_.each(children,function(child){dfs(child,depth+1)})}depths[v]=depth}_.each(g.children(),function(v){dfs(v,1)});return depths}function sumWeights(g){return _.reduce(g.edges(),function(acc,e){return acc+g.edge(e).weight},0)}function cleanup(g){var graphLabel=g.graph();g.removeNode(graphLabel.nestingRoot);delete graphLabel.nestingRoot;_.each(g.edges(),function(e){var edge=g.edge(e);if(edge.nestingEdge){g.removeEdge(e)}})}},{"./lodash":10,"./util":29}],12:[function(require,module,exports){"use strict";var _=require("./lodash"),util=require("./util");module.exports={run:run,undo:undo};function run(g){g.graph().dummyChains=[];_.each(g.edges(),function(edge){normalizeEdge(g,edge)})}function normalizeEdge(g,e){var v=e.v,vRank=g.node(v).rank,w=e.w,wRank=g.node(w).rank,name=e.name,edgeLabel=g.edge(e),labelRank=edgeLabel.labelRank;if(wRank===vRank+1)return;g.removeEdge(e);var dummy,attrs,i;for(i=0,++vRank;vRank0){if(index%2){weightSum+=tree[index+1]}index=index-1>>1;tree[index]+=entry.weight}cc+=entry.weight*weightSum}));return cc}},{"../lodash":10}],17:[function(require,module,exports){"use strict";var _=require("../lodash"),initOrder=require("./init-order"),crossCount=require("./cross-count"),sortSubgraph=require("./sort-subgraph"),buildLayerGraph=require("./build-layer-graph"),addSubgraphConstraints=require("./add-subgraph-constraints"),Graph=require("../graphlib").Graph,util=require("../util");module.exports=order;function order(g){var maxRank=util.maxRank(g),downLayerGraphs=buildLayerGraphs(g,_.range(1,maxRank+1),"inEdges"),upLayerGraphs=buildLayerGraphs(g,_.range(maxRank-1,-1,-1),"outEdges");var layering=initOrder(g);assignOrder(g,layering);var bestCC=Number.POSITIVE_INFINITY,best;for(var i=0,lastBest=0;lastBest<4;++i,++lastBest){sweepLayerGraphs(i%2?downLayerGraphs:upLayerGraphs,i%4>=2);layering=util.buildLayerMatrix(g);var cc=crossCount(g,layering);if(cc=vEntry.barycenter){mergeEntries(vEntry,uEntry)}}}function handleOut(vEntry){return function(wEntry){wEntry["in"].push(vEntry);if(--wEntry.indegree===0){sourceSet.push(wEntry)}}}while(sourceSet.length){var entry=sourceSet.pop();entries.push(entry);_.each(entry["in"].reverse(),handleIn(entry));_.each(entry.out,handleOut(entry))}return _.chain(entries).filter(function(entry){return!entry.merged}).map(function(entry){return _.pick(entry,["vs","i","barycenter","weight"])}).value()}function mergeEntries(target,source){var sum=0,weight=0;if(target.weight){sum+=target.barycenter*target.weight;weight+=target.weight}if(source.weight){sum+=source.barycenter*source.weight;weight+=source.weight}target.vs=source.vs.concat(target.vs);target.barycenter=sum/weight;target.weight=weight;target.i=Math.min(source.i,target.i);source.merged=true}},{"../lodash":10}],20:[function(require,module,exports){var _=require("../lodash"),barycenter=require("./barycenter"),resolveConflicts=require("./resolve-conflicts"),sort=require("./sort");module.exports=sortSubgraph;function sortSubgraph(g,v,cg,biasRight){var movable=g.children(v),node=g.node(v),bl=node?node.borderLeft:undefined,br=node?node.borderRight:undefined,subgraphs={};if(bl){movable=_.filter(movable,function(w){return w!==bl&&w!==br})}var barycenters=barycenter(g,movable);_.each(barycenters,function(entry){if(g.children(entry.v).length){var subgraphResult=sortSubgraph(g,entry.v,cg,biasRight);subgraphs[entry.v]=subgraphResult;if(_.has(subgraphResult,"barycenter")){mergeBarycenters(entry,subgraphResult)}}});var entries=resolveConflicts(barycenters,cg);expandSubgraphs(entries,subgraphs);var result=sort(entries,biasRight);if(bl){result.vs=_.flatten([bl,result.vs,br],true);if(g.predecessors(bl).length){var blPred=g.node(g.predecessors(bl)[0]),brPred=g.node(g.predecessors(br)[0]);if(!_.has(result,"barycenter")){result.barycenter=0;result.weight=0}result.barycenter=(result.barycenter*result.weight+blPred.order+brPred.order)/(result.weight+2);result.weight+=2}}return result}function expandSubgraphs(entries,subgraphs){_.each(entries,function(entry){entry.vs=_.flatten(entry.vs.map(function(v){if(subgraphs[v]){return subgraphs[v].vs}return v}),true)})}function mergeBarycenters(target,other){if(!_.isUndefined(target.barycenter)){target.barycenter=(target.barycenter*target.weight+other.barycenter*other.weight)/(target.weight+other.weight);target.weight+=other.weight}else{target.barycenter=other.barycenter;target.weight=other.weight}}},{"../lodash":10,"./barycenter":14,"./resolve-conflicts":19,"./sort":21}],21:[function(require,module,exports){var _=require("../lodash"),util=require("../util");module.exports=sort;function sort(entries,biasRight){var parts=util.partition(entries,function(entry){return _.has(entry,"barycenter")});var sortable=parts.lhs,unsortable=_.sortBy(parts.rhs,function(entry){return-entry.i}),vs=[],sum=0,weight=0,vsIndex=0;sortable.sort(compareWithBias(!!biasRight));vsIndex=consumeUnsortable(vs,unsortable,vsIndex);_.each(sortable,function(entry){vsIndex+=entry.vs.length;vs.push(entry.vs);sum+=entry.barycenter*entry.weight;weight+=entry.weight;vsIndex=consumeUnsortable(vs,unsortable,vsIndex)});var result={vs:_.flatten(vs,true)};if(weight){result.barycenter=sum/weight;result.weight=weight}return result}function consumeUnsortable(vs,unsortable,index){var last;while(unsortable.length&&(last=_.last(unsortable)).i<=index){unsortable.pop();vs.push(last.vs);index++}return index}function compareWithBias(bias){return function(entryV,entryW){if(entryV.barycenterentryW.barycenter){return 1}return!bias?entryV.i-entryW.i:entryW.i-entryV.i}}},{"../lodash":10,"../util":29}],22:[function(require,module,exports){var _=require("./lodash");module.exports=parentDummyChains;function parentDummyChains(g){var postorderNums=postorder(g);_.each(g.graph().dummyChains,function(v){var node=g.node(v),edgeObj=node.edgeObj,pathData=findPath(g,postorderNums,edgeObj.v,edgeObj.w),path=pathData.path,lca=pathData.lca,pathIdx=0,pathV=path[pathIdx],ascending=true;while(v!==edgeObj.w){node=g.node(v);if(ascending){while((pathV=path[pathIdx])!==lca&&g.node(pathV).maxRanklow||lim>postorderNums[parent].lim));lca=parent;parent=w;while((parent=g.parent(parent))!==lca){wPath.push(parent)}return{path:vPath.concat(wPath.reverse()),lca:lca}}function postorder(g){var result={},lim=0;function dfs(v){var low=lim;_.each(g.children(v),dfs);result[v]={low:low,lim:lim++}}_.each(g.children(),dfs);return result}},{"./lodash":10}],23:[function(require,module,exports){"use strict";var _=require("../lodash"),Graph=require("../graphlib").Graph,util=require("../util");module.exports={positionX:positionX,findType1Conflicts:findType1Conflicts,findType2Conflicts:findType2Conflicts,addConflict:addConflict,hasConflict:hasConflict,verticalAlignment:verticalAlignment,horizontalCompaction:horizontalCompaction,alignCoordinates:alignCoordinates,findSmallestWidthAlignment:findSmallestWidthAlignment,balance:balance};function findType1Conflicts(g,layering){var conflicts={};function visitLayer(prevLayer,layer){var k0=0,scanPos=0,prevLayerLength=prevLayer.length,lastNode=_.last(layer);_.each(layer,function(v,i){var w=findOtherInnerSegmentNode(g,v),k1=w?g.node(w).order:prevLayerLength;if(w||v===lastNode){_.each(layer.slice(scanPos,i+1),function(scanNode){_.each(g.predecessors(scanNode),function(u){var uLabel=g.node(u),uPos=uLabel.order; +if((uPosnextNorthBorder)){addConflict(conflicts,u,v)}})}})}function visitLayer(north,south){var prevNorthPos=-1,nextNorthPos,southPos=0;_.each(south,function(v,southLookahead){if(g.node(v).dummy==="border"){var predecessors=g.predecessors(v);if(predecessors.length){nextNorthPos=g.node(predecessors[0]).order;scan(south,southPos,southLookahead,prevNorthPos,nextNorthPos);southPos=southLookahead;prevNorthPos=nextNorthPos}}scan(south,southPos,south.length,nextNorthPos,north.length)});return south}_.reduce(layering,visitLayer);return conflicts}function findOtherInnerSegmentNode(g,v){if(g.node(v).dummy){return _.find(g.predecessors(v),function(u){return g.node(u).dummy})}}function addConflict(conflicts,v,w){if(v>w){var tmp=v;v=w;w=tmp}var conflictsV=conflicts[v];if(!conflictsV){conflicts[v]=conflictsV={}}conflictsV[w]=true}function hasConflict(conflicts,v,w){if(v>w){var tmp=v;v=w;w=tmp}return _.has(conflicts[v],w)}function verticalAlignment(g,layering,conflicts,neighborFn){var root={},align={},pos={};_.each(layering,function(layer){_.each(layer,function(v,order){root[v]=v;align[v]=v;pos[v]=order})});_.each(layering,function(layer){var prevIdx=-1;_.each(layer,function(v){var ws=neighborFn(v);if(ws.length){ws=_.sortBy(ws,function(w){return pos[w]});var mp=(ws.length-1)/2;for(var i=Math.floor(mp),il=Math.ceil(mp);i<=il;++i){var w=ws[i];if(align[v]===v&&prevIdxwLabel.lim){tailLabel=wLabel;flip=true}var candidates=_.filter(g.edges(),function(edge){return flip===isDescendant(t,t.node(edge.v),tailLabel)&&flip!==isDescendant(t,t.node(edge.w),tailLabel)});return _.min(candidates,function(edge){return slack(g,edge)})}function exchangeEdges(t,g,e,f){var v=e.v,w=e.w;t.removeEdge(v,w);t.setEdge(f.v,f.w,{});initLowLimValues(t);initCutValues(t,g);updateRanks(t,g)}function updateRanks(t,g){var root=_.find(t.nodes(),function(v){return!g.node(v).parent}),vs=preorder(t,root);vs=vs.slice(1);_.each(vs,function(v){var parent=t.node(v).parent,edge=g.edge(v,parent),flipped=false;if(!edge){edge=g.edge(parent,v);flipped=true}g.node(v).rank=g.node(parent).rank+(flipped?edge.minlen:-edge.minlen)})}function isTreeEdge(tree,u,v){return tree.hasEdge(u,v)}function isDescendant(tree,vLabel,rootLabel){return rootLabel.low<=vLabel.lim&&vLabel.lim<=rootLabel.lim}},{"../graphlib":7,"../lodash":10,"../util":29,"./feasible-tree":25,"./util":28}],28:[function(require,module,exports){"use strict";var _=require("../lodash");module.exports={longestPath:longestPath,slack:slack};function longestPath(g){var visited={};function dfs(v){var label=g.node(v);if(_.has(visited,v)){return label.rank}visited[v]=true;var rank=_.min(_.map(g.outEdges(v),function(e){return dfs(e.w)-g.edge(e).minlen}));if(rank===Number.POSITIVE_INFINITY){rank=0}return label.rank=rank}_.each(g.sources(),dfs)}function slack(g,e){return g.node(e.w).rank-g.node(e.v).rank-g.edge(e).minlen}},{"../lodash":10}],29:[function(require,module,exports){"use strict";var _=require("./lodash"),Graph=require("./graphlib").Graph;module.exports={addDummyNode:addDummyNode,simplify:simplify,asNonCompoundGraph:asNonCompoundGraph,successorWeights:successorWeights,predecessorWeights:predecessorWeights,intersectRect:intersectRect,buildLayerMatrix:buildLayerMatrix,normalizeRanks:normalizeRanks,removeEmptyRanks:removeEmptyRanks,addBorderNode:addBorderNode,maxRank:maxRank,partition:partition,time:time,notime:notime};function addDummyNode(g,type,attrs,name){var v;do{v=_.uniqueId(name)}while(g.hasNode(v));attrs.dummy=type;g.setNode(v,attrs);return v}function simplify(g){var simplified=(new Graph).setGraph(g.graph());_.each(g.nodes(),function(v){simplified.setNode(v,g.node(v))});_.each(g.edges(),function(e){var simpleLabel=simplified.edge(e.v,e.w)||{weight:0,minlen:1},label=g.edge(e);simplified.setEdge(e.v,e.w,{weight:simpleLabel.weight+label.weight,minlen:Math.max(simpleLabel.minlen,label.minlen)})});return simplified}function asNonCompoundGraph(g){var simplified=new Graph({multigraph:g.isMultigraph()}).setGraph(g.graph());_.each(g.nodes(),function(v){if(!g.children(v).length){simplified.setNode(v,g.node(v))}});_.each(g.edges(),function(e){simplified.setEdge(e,g.edge(e))});return simplified}function successorWeights(g){var weightMap=_.map(g.nodes(),function(v){var sucs={};_.each(g.outEdges(v),function(e){sucs[e.w]=(sucs[e.w]||0)+g.edge(e).weight});return sucs});return _.zipObject(g.nodes(),weightMap)}function predecessorWeights(g){var weightMap=_.map(g.nodes(),function(v){var preds={};_.each(g.inEdges(v),function(e){preds[e.v]=(preds[e.v]||0)+g.edge(e).weight});return preds});return _.zipObject(g.nodes(),weightMap)}function intersectRect(rect,point){var x=rect.x;var y=rect.y;var dx=point.x-x;var dy=point.y-y;var w=rect.width/2;var h=rect.height/2;if(!dx&&!dy){throw new Error("Not possible to find intersection inside of the rectangle")}var sx,sy;if(Math.abs(dy)*w>Math.abs(dx)*h){if(dy<0){h=-h}sx=h*dx/dy;sy=h}else{if(dx<0){w=-w}sx=w;sy=w*dy/dx}return{x:x+sx,y:y+sy}}function buildLayerMatrix(g){var layering=_.map(_.range(maxRank(g)+1),function(){return[]});_.each(g.nodes(),function(v){var node=g.node(v),rank=node.rank;if(!_.isUndefined(rank)){layering[rank][node.order]=v}});return layering}function normalizeRanks(g){var min=_.min(_.map(g.nodes(),function(v){return g.node(v).rank}));_.each(g.nodes(),function(v){var node=g.node(v);if(_.has(node,"rank")){node.rank-=min}})}function removeEmptyRanks(g){var offset=_.min(_.map(g.nodes(),function(v){return g.node(v).rank}));var layers=[];_.each(g.nodes(),function(v){var rank=g.node(v).rank-offset;if(!layers[rank]){layers[rank]=[]}layers[rank].push(v)});var delta=0,nodeRankFactor=g.graph().nodeRankFactor;_.each(layers,function(vs,i){if(_.isUndefined(vs)&&i%nodeRankFactor!==0){--delta}else if(delta){_.each(vs,function(v){g.node(v).rank+=delta})}})}function addBorderNode(g,prefix,rank,order){var node={width:0,height:0};if(arguments.length>=4){node.rank=rank;node.order=order}return addDummyNode(g,"border",node,prefix)}function maxRank(g){return _.max(_.map(g.nodes(),function(v){var rank=g.node(v).rank;if(!_.isUndefined(rank)){return rank}}))}function partition(collection,fn){var result={lhs:[],rhs:[]};_.each(collection,function(value){if(fn(value)){result.lhs.push(value)}else{result.rhs.push(value)}});return result}function time(name,fn){var start=_.now();try{return fn()}finally{console.log(name+" time: "+(_.now()-start)+"ms")}}function notime(name,fn){return fn()}},{"./graphlib":7,"./lodash":10}],30:[function(require,module,exports){module.exports="0.7.4"},{}],31:[function(require,module,exports){var lib=require("./lib");module.exports={Graph:lib.Graph,json:require("./lib/json"),alg:require("./lib/alg"),version:lib.version}},{"./lib":47,"./lib/alg":38,"./lib/json":48}],32:[function(require,module,exports){var _=require("../lodash");module.exports=components;function components(g){var visited={},cmpts=[],cmpt;function dfs(v){if(_.has(visited,v))return;visited[v]=true;cmpt.push(v);_.each(g.successors(v),dfs);_.each(g.predecessors(v),dfs)}_.each(g.nodes(),function(v){cmpt=[];dfs(v);if(cmpt.length){cmpts.push(cmpt)}});return cmpts}},{"../lodash":49}],33:[function(require,module,exports){var _=require("../lodash");module.exports=dfs;function dfs(g,vs,order){if(!_.isArray(vs)){vs=[vs]}var acc=[],visited={};_.each(vs,function(v){if(!g.hasNode(v)){throw new Error("Graph does not have node: "+v)}doDfs(g,v,order==="post",visited,acc)});return acc}function doDfs(g,v,postorder,visited,acc){if(!_.has(visited,v)){visited[v]=true;if(!postorder){acc.push(v)}_.each(g.neighbors(v),function(w){doDfs(g,w,postorder,visited,acc)});if(postorder){acc.push(v)}}}},{"../lodash":49}],34:[function(require,module,exports){var dijkstra=require("./dijkstra"),_=require("../lodash");module.exports=dijkstraAll;function dijkstraAll(g,weightFunc,edgeFunc){return _.transform(g.nodes(),function(acc,v){acc[v]=dijkstra(g,v,weightFunc,edgeFunc)},{})}},{"../lodash":49,"./dijkstra":35}],35:[function(require,module,exports){var _=require("../lodash"),PriorityQueue=require("../data/priority-queue");module.exports=dijkstra;var DEFAULT_WEIGHT_FUNC=_.constant(1);function dijkstra(g,source,weightFn,edgeFn){return runDijkstra(g,String(source),weightFn||DEFAULT_WEIGHT_FUNC,edgeFn||function(v){return g.outEdges(v)})}function runDijkstra(g,source,weightFn,edgeFn){var results={},pq=new PriorityQueue,v,vEntry;var updateNeighbors=function(edge){var w=edge.v!==v?edge.v:edge.w,wEntry=results[w],weight=weightFn(edge),distance=vEntry.distance+weight;if(weight<0){throw new Error("dijkstra does not allow negative edge weights. "+"Bad edge: "+edge+" Weight: "+weight)}if(distance0){v=pq.removeMin();vEntry=results[v];if(vEntry.distance===Number.POSITIVE_INFINITY){break}edgeFn(v).forEach(updateNeighbors)}return results}},{"../data/priority-queue":45,"../lodash":49}],36:[function(require,module,exports){var _=require("../lodash"),tarjan=require("./tarjan");module.exports=findCycles;function findCycles(g){return _.filter(tarjan(g),function(cmpt){return cmpt.length>1||cmpt.length===1&&g.hasEdge(cmpt[0],cmpt[0])})}},{"../lodash":49,"./tarjan":43}],37:[function(require,module,exports){var _=require("../lodash");module.exports=floydWarshall;var DEFAULT_WEIGHT_FUNC=_.constant(1);function floydWarshall(g,weightFn,edgeFn){return runFloydWarshall(g,weightFn||DEFAULT_WEIGHT_FUNC,edgeFn||function(v){return g.outEdges(v)})}function runFloydWarshall(g,weightFn,edgeFn){var results={},nodes=g.nodes();nodes.forEach(function(v){results[v]={};results[v][v]={distance:0};nodes.forEach(function(w){if(v!==w){results[v][w]={distance:Number.POSITIVE_INFINITY}}});edgeFn(v).forEach(function(edge){var w=edge.v===v?edge.w:edge.v,d=weightFn(edge);results[v][w]={distance:d,predecessor:v}})});nodes.forEach(function(k){var rowK=results[k];nodes.forEach(function(i){var rowI=results[i];nodes.forEach(function(j){var ik=rowI[k];var kj=rowK[j];var ij=rowI[j];var altDistance=ik.distance+kj.distance;if(altDistance0){v=pq.removeMin();if(_.has(parents,v)){result.setEdge(v,parents[v])}else if(init){throw new Error("Input graph is not connected: "+g)}else{init=true}g.nodeEdges(v).forEach(updateNeighbors)}return result}},{"../data/priority-queue":45,"../graph":46,"../lodash":49}],43:[function(require,module,exports){var _=require("../lodash");module.exports=tarjan;function tarjan(g){var index=0,stack=[],visited={},results=[];function dfs(v){var entry=visited[v]={onStack:true,lowlink:index,index:index++};stack.push(v);g.successors(v).forEach(function(w){if(!_.has(visited,w)){dfs(w);entry.lowlink=Math.min(entry.lowlink,visited[w].lowlink)}else if(visited[w].onStack){entry.lowlink=Math.min(entry.lowlink,visited[w].index)}});if(entry.lowlink===entry.index){var cmpt=[],w;do{w=stack.pop();visited[w].onStack=false;cmpt.push(w)}while(v!==w);results.push(cmpt)}}g.nodes().forEach(function(v){if(!_.has(visited,v)){dfs(v)}});return results}},{"../lodash":49}],44:[function(require,module,exports){var _=require("../lodash");module.exports=topsort;topsort.CycleException=CycleException;function topsort(g){var visited={},stack={},results=[];function visit(node){if(_.has(stack,node)){throw new CycleException}if(!_.has(visited,node)){stack[node]=true;visited[node]=true;_.each(g.predecessors(node),visit);delete stack[node];results.push(node)}}_.each(g.sinks(),visit);if(_.size(visited)!==g.nodeCount()){throw new CycleException}return results}function CycleException(){}},{"../lodash":49}],45:[function(require,module,exports){var _=require("../lodash");module.exports=PriorityQueue;function PriorityQueue(){this._arr=[];this._keyIndices={}}PriorityQueue.prototype.size=function(){return this._arr.length};PriorityQueue.prototype.keys=function(){return this._arr.map(function(x){return x.key})};PriorityQueue.prototype.has=function(key){return _.has(this._keyIndices,key)};PriorityQueue.prototype.priority=function(key){var index=this._keyIndices[key];if(index!==undefined){return this._arr[index].priority}};PriorityQueue.prototype.min=function(){if(this.size()===0){throw new Error("Queue underflow")}return this._arr[0].key};PriorityQueue.prototype.add=function(key,priority){var keyIndices=this._keyIndices;key=String(key);if(!_.has(keyIndices,key)){var arr=this._arr;var index=arr.length;keyIndices[key]=index;arr.push({key:key,priority:priority});this._decrease(index);return true}return false};PriorityQueue.prototype.removeMin=function(){this._swap(0,this._arr.length-1);var min=this._arr.pop();delete this._keyIndices[min.key];this._heapify(0);return min.key};PriorityQueue.prototype.decrease=function(key,priority){var index=this._keyIndices[key];if(priority>this._arr[index].priority){throw new Error("New priority is greater than current priority. "+"Key: "+key+" Old: "+this._arr[index].priority+" New: "+priority)}this._arr[index].priority=priority;this._decrease(index)};PriorityQueue.prototype._heapify=function(i){var arr=this._arr;var l=2*i,r=l+1,largest=i;if(l>1;if(arr[parent].priority1){this.setNode(v,value)}else{this.setNode(v)}},this);return this};Graph.prototype.setNode=function(v,value){if(_.has(this._nodes,v)){if(arguments.length>1){this._nodes[v]=value}return this}this._nodes[v]=arguments.length>1?value:this._defaultNodeLabelFn(v);if(this._isCompound){this._parent[v]=GRAPH_NODE;this._children[v]={};this._children[GRAPH_NODE][v]=true}this._in[v]={};this._preds[v]={};this._out[v]={};this._sucs[v]={};++this._nodeCount;return this};Graph.prototype.node=function(v){return this._nodes[v]};Graph.prototype.hasNode=function(v){return _.has(this._nodes,v)};Graph.prototype.removeNode=function(v){var self=this;if(_.has(this._nodes,v)){var removeEdge=function(e){self.removeEdge(self._edgeObjs[e])};delete this._nodes[v];if(this._isCompound){this._removeFromParentsChildList(v);delete this._parent[v];_.each(this.children(v),function(child){this.setParent(child)},this);delete this._children[v]}_.each(_.keys(this._in[v]),removeEdge);delete this._in[v];delete this._preds[v];_.each(_.keys(this._out[v]),removeEdge);delete this._out[v];delete this._sucs[v];--this._nodeCount}return this};Graph.prototype.setParent=function(v,parent){if(!this._isCompound){throw new Error("Cannot set parent in a non-compound graph")}if(_.isUndefined(parent)){parent=GRAPH_NODE}else{parent+="";for(var ancestor=parent;!_.isUndefined(ancestor);ancestor=this.parent(ancestor)){if(ancestor===v){throw new Error("Setting "+parent+" as parent of "+v+" would create create a cycle")}}this.setNode(parent)}this.setNode(v);this._removeFromParentsChildList(v);this._parent[v]=parent;this._children[parent][v]=true;return this};Graph.prototype._removeFromParentsChildList=function(v){delete this._children[this._parent[v]][v]};Graph.prototype.parent=function(v){if(this._isCompound){var parent=this._parent[v];if(parent!==GRAPH_NODE){return parent}}};Graph.prototype.children=function(v){if(_.isUndefined(v)){v=GRAPH_NODE}if(this._isCompound){var children=this._children[v];if(children){return _.keys(children)}}else if(v===GRAPH_NODE){return this.nodes()}else if(this.hasNode(v)){return[]}};Graph.prototype.predecessors=function(v){var predsV=this._preds[v];if(predsV){return _.keys(predsV)}};Graph.prototype.successors=function(v){var sucsV=this._sucs[v];if(sucsV){return _.keys(sucsV)}};Graph.prototype.neighbors=function(v){var preds=this.predecessors(v);if(preds){return _.union(preds,this.successors(v))}};Graph.prototype.setDefaultEdgeLabel=function(newDefault){if(!_.isFunction(newDefault)){newDefault=_.constant(newDefault)}this._defaultEdgeLabelFn=newDefault;return this};Graph.prototype.edgeCount=function(){return this._edgeCount};Graph.prototype.edges=function(){return _.values(this._edgeObjs)};Graph.prototype.setPath=function(vs,value){var self=this,args=arguments;_.reduce(vs,function(v,w){if(args.length>1){self.setEdge(v,w,value)}else{self.setEdge(v,w)}return w});return this};Graph.prototype.setEdge=function(){var v,w,name,value,valueSpecified=false;if(_.isPlainObject(arguments[0])){v=arguments[0].v;w=arguments[0].w;name=arguments[0].name;if(arguments.length===2){value=arguments[1];valueSpecified=true}}else{v=arguments[0];w=arguments[1];name=arguments[3];if(arguments.length>2){value=arguments[2];valueSpecified=true}}v=""+v;w=""+w;if(!_.isUndefined(name)){name=""+name}var e=edgeArgsToId(this._isDirected,v,w,name);if(_.has(this._edgeLabels,e)){if(valueSpecified){this._edgeLabels[e]=value}return this}if(!_.isUndefined(name)&&!this._isMultigraph){throw new Error("Cannot set a named edge when isMultigraph = false")}this.setNode(v);this.setNode(w);this._edgeLabels[e]=valueSpecified?value:this._defaultEdgeLabelFn(v,w,name);var edgeObj=edgeArgsToObj(this._isDirected,v,w,name);v=edgeObj.v;w=edgeObj.w;Object.freeze(edgeObj);this._edgeObjs[e]=edgeObj;incrementOrInitEntry(this._preds[w],v);incrementOrInitEntry(this._sucs[v],w);this._in[w][e]=edgeObj;this._out[v][e]=edgeObj;this._edgeCount++;return this};Graph.prototype.edge=function(v,w,name){var e=arguments.length===1?edgeObjToId(this._isDirected,arguments[0]):edgeArgsToId(this._isDirected,v,w,name);return this._edgeLabels[e]};Graph.prototype.hasEdge=function(v,w,name){var e=arguments.length===1?edgeObjToId(this._isDirected,arguments[0]):edgeArgsToId(this._isDirected,v,w,name);return _.has(this._edgeLabels,e)};Graph.prototype.removeEdge=function(v,w,name){var e=arguments.length===1?edgeObjToId(this._isDirected,arguments[0]):edgeArgsToId(this._isDirected,v,w,name),edge=this._edgeObjs[e];if(edge){v=edge.v;w=edge.w;delete this._edgeLabels[e];delete this._edgeObjs[e];decrementOrRemoveEntry(this._preds[w],v);decrementOrRemoveEntry(this._sucs[v],w);delete this._in[w][e];delete this._out[v][e];this._edgeCount--}return this};Graph.prototype.inEdges=function(v,u){var inV=this._in[v];if(inV){var edges=_.values(inV);if(!u){return edges}return _.filter(edges,function(edge){return edge.v===u})}};Graph.prototype.outEdges=function(v,w){var outV=this._out[v];if(outV){var edges=_.values(outV);if(!w){return edges}return _.filter(edges,function(edge){return edge.w===w})}};Graph.prototype.nodeEdges=function(v,w){var inEdges=this.inEdges(v,w);if(inEdges){return inEdges.concat(this.outEdges(v,w))}};function incrementOrInitEntry(map,k){if(_.has(map,k)){map[k]++}else{map[k]=1}}function decrementOrRemoveEntry(map,k){if(!--map[k]){delete map[k]}}function edgeArgsToId(isDirected,v,w,name){if(!isDirected&&v>w){var tmp=v;v=w;w=tmp}return v+EDGE_KEY_DELIM+w+EDGE_KEY_DELIM+(_.isUndefined(name)?DEFAULT_EDGE_NAME:name)}function edgeArgsToObj(isDirected,v,w,name){if(!isDirected&&v>w){var tmp=v;v=w;w=tmp}var edgeObj={v:v,w:w};if(name){edgeObj.name=name}return edgeObj}function edgeObjToId(isDirected,edgeObj){return edgeArgsToId(isDirected,edgeObj.v,edgeObj.w,edgeObj.name)}},{"./lodash":49}],47:[function(require,module,exports){module.exports={Graph:require("./graph"),version:require("./version")}},{"./graph":46,"./version":50}],48:[function(require,module,exports){var _=require("./lodash"),Graph=require("./graph");module.exports={write:write,read:read};function write(g){var json={options:{directed:g.isDirected(),multigraph:g.isMultigraph(),compound:g.isCompound()},nodes:writeNodes(g),edges:writeEdges(g)}; +if(!_.isUndefined(g.graph())){json.value=_.clone(g.graph())}return json}function writeNodes(g){return _.map(g.nodes(),function(v){var nodeValue=g.node(v),parent=g.parent(v),node={v:v};if(!_.isUndefined(nodeValue)){node.value=nodeValue}if(!_.isUndefined(parent)){node.parent=parent}return node})}function writeEdges(g){return _.map(g.edges(),function(e){var edgeValue=g.edge(e),edge={v:e.v,w:e.w};if(!_.isUndefined(e.name)){edge.name=e.name}if(!_.isUndefined(edgeValue)){edge.value=edgeValue}return edge})}function read(json){var g=new Graph(json.options).setGraph(json.value);_.each(json.nodes,function(entry){g.setNode(entry.v,entry.value);if(entry.parent){g.setParent(entry.v,entry.parent)}});_.each(json.edges,function(entry){g.setEdge({v:entry.v,w:entry.w,name:entry.name},entry.value)});return g}},{"./graph":46,"./lodash":49}],49:[function(require,module,exports){module.exports=require(10)},{"/Users/cpettitt/projects/dagre/lib/lodash.js":10,lodash:51}],50:[function(require,module,exports){module.exports="1.0.5"},{}],51:[function(require,module,exports){(function(global){(function(){var undefined;var VERSION="3.10.0";var BIND_FLAG=1,BIND_KEY_FLAG=2,CURRY_BOUND_FLAG=4,CURRY_FLAG=8,CURRY_RIGHT_FLAG=16,PARTIAL_FLAG=32,PARTIAL_RIGHT_FLAG=64,ARY_FLAG=128,REARG_FLAG=256;var DEFAULT_TRUNC_LENGTH=30,DEFAULT_TRUNC_OMISSION="...";var HOT_COUNT=150,HOT_SPAN=16;var LARGE_ARRAY_SIZE=200;var LAZY_FILTER_FLAG=1,LAZY_MAP_FLAG=2;var FUNC_ERROR_TEXT="Expected a function";var PLACEHOLDER="__lodash_placeholder__";var argsTag="[object Arguments]",arrayTag="[object Array]",boolTag="[object Boolean]",dateTag="[object Date]",errorTag="[object Error]",funcTag="[object Function]",mapTag="[object Map]",numberTag="[object Number]",objectTag="[object Object]",regexpTag="[object RegExp]",setTag="[object Set]",stringTag="[object String]",weakMapTag="[object WeakMap]";var arrayBufferTag="[object ArrayBuffer]",float32Tag="[object Float32Array]",float64Tag="[object Float64Array]",int8Tag="[object Int8Array]",int16Tag="[object Int16Array]",int32Tag="[object Int32Array]",uint8Tag="[object Uint8Array]",uint8ClampedTag="[object Uint8ClampedArray]",uint16Tag="[object Uint16Array]",uint32Tag="[object Uint32Array]";var reEmptyStringLeading=/\b__p \+= '';/g,reEmptyStringMiddle=/\b(__p \+=) '' \+/g,reEmptyStringTrailing=/(__e\(.*?\)|\b__t\)) \+\n'';/g;var reEscapedHtml=/&(?:amp|lt|gt|quot|#39|#96);/g,reUnescapedHtml=/[&<>"'`]/g,reHasEscapedHtml=RegExp(reEscapedHtml.source),reHasUnescapedHtml=RegExp(reUnescapedHtml.source);var reEscape=/<%-([\s\S]+?)%>/g,reEvaluate=/<%([\s\S]+?)%>/g,reInterpolate=/<%=([\s\S]+?)%>/g;var reIsDeepProp=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/,reIsPlainProp=/^\w*$/,rePropName=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g;var reRegExpChars=/^[:!,]|[\\^$.*+?()[\]{}|\/]|(^[0-9a-fA-Fnrtuvx])|([\n\r\u2028\u2029])/g,reHasRegExpChars=RegExp(reRegExpChars.source);var reComboMark=/[\u0300-\u036f\ufe20-\ufe23]/g;var reEscapeChar=/\\(\\)?/g;var reEsTemplate=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;var reFlags=/\w*$/;var reHasHexPrefix=/^0[xX]/;var reIsHostCtor=/^\[object .+?Constructor\]$/;var reIsUint=/^\d+$/;var reLatin1=/[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g;var reNoMatch=/($^)/;var reUnescapedString=/['\n\r\u2028\u2029\\]/g;var reWords=function(){var upper="[A-Z\\xc0-\\xd6\\xd8-\\xde]",lower="[a-z\\xdf-\\xf6\\xf8-\\xff]+";return RegExp(upper+"+(?="+upper+lower+")|"+upper+"?"+lower+"|"+upper+"+|[0-9]+","g")}();var contextProps=["Array","ArrayBuffer","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Math","Number","Object","RegExp","Set","String","_","clearTimeout","isFinite","parseFloat","parseInt","setTimeout","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap"];var templateCounter=-1;var typedArrayTags={};typedArrayTags[float32Tag]=typedArrayTags[float64Tag]=typedArrayTags[int8Tag]=typedArrayTags[int16Tag]=typedArrayTags[int32Tag]=typedArrayTags[uint8Tag]=typedArrayTags[uint8ClampedTag]=typedArrayTags[uint16Tag]=typedArrayTags[uint32Tag]=true;typedArrayTags[argsTag]=typedArrayTags[arrayTag]=typedArrayTags[arrayBufferTag]=typedArrayTags[boolTag]=typedArrayTags[dateTag]=typedArrayTags[errorTag]=typedArrayTags[funcTag]=typedArrayTags[mapTag]=typedArrayTags[numberTag]=typedArrayTags[objectTag]=typedArrayTags[regexpTag]=typedArrayTags[setTag]=typedArrayTags[stringTag]=typedArrayTags[weakMapTag]=false;var cloneableTags={};cloneableTags[argsTag]=cloneableTags[arrayTag]=cloneableTags[arrayBufferTag]=cloneableTags[boolTag]=cloneableTags[dateTag]=cloneableTags[float32Tag]=cloneableTags[float64Tag]=cloneableTags[int8Tag]=cloneableTags[int16Tag]=cloneableTags[int32Tag]=cloneableTags[numberTag]=cloneableTags[objectTag]=cloneableTags[regexpTag]=cloneableTags[stringTag]=cloneableTags[uint8Tag]=cloneableTags[uint8ClampedTag]=cloneableTags[uint16Tag]=cloneableTags[uint32Tag]=true;cloneableTags[errorTag]=cloneableTags[funcTag]=cloneableTags[mapTag]=cloneableTags[setTag]=cloneableTags[weakMapTag]=false;var deburredLetters={"À":"A","Á":"A","Â":"A","Ã":"A","Ä":"A","Å":"A","à":"a","á":"a","â":"a","ã":"a","ä":"a","å":"a","Ç":"C","ç":"c","Ð":"D","ð":"d","È":"E","É":"E","Ê":"E","Ë":"E","è":"e","é":"e","ê":"e","ë":"e","Ì":"I","Í":"I","Î":"I","Ï":"I","ì":"i","í":"i","î":"i","ï":"i","Ñ":"N","ñ":"n","Ò":"O","Ó":"O","Ô":"O","Õ":"O","Ö":"O","Ø":"O","ò":"o","ó":"o","ô":"o","õ":"o","ö":"o","ø":"o","Ù":"U","Ú":"U","Û":"U","Ü":"U","ù":"u","ú":"u","û":"u","ü":"u","Ý":"Y","ý":"y","ÿ":"y","Æ":"Ae","æ":"ae","Þ":"Th","þ":"th","ß":"ss"};var htmlEscapes={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"};var htmlUnescapes={"&":"&","<":"<",">":">",""":'"',"'":"'","`":"`"};var objectTypes={"function":true,object:true};var regexpEscapes={0:"x30",1:"x31",2:"x32",3:"x33",4:"x34",5:"x35",6:"x36",7:"x37",8:"x38",9:"x39",A:"x41",B:"x42",C:"x43",D:"x44",E:"x45",F:"x46",a:"x61",b:"x62",c:"x63",d:"x64",e:"x65",f:"x66",n:"x6e",r:"x72",t:"x74",u:"x75",v:"x76",x:"x78"};var stringEscapes={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"};var freeExports=objectTypes[typeof exports]&&exports&&!exports.nodeType&&exports;var freeModule=objectTypes[typeof module]&&module&&!module.nodeType&&module;var freeGlobal=freeExports&&freeModule&&typeof global=="object"&&global&&global.Object&&global;var freeSelf=objectTypes[typeof self]&&self&&self.Object&&self;var freeWindow=objectTypes[typeof window]&&window&&window.Object&&window;var moduleExports=freeModule&&freeModule.exports===freeExports&&freeExports;var root=freeGlobal||freeWindow!==(this&&this.window)&&freeWindow||freeSelf||this;function baseCompareAscending(value,other){if(value!==other){var valIsNull=value===null,valIsUndef=value===undefined,valIsReflexive=value===value;var othIsNull=other===null,othIsUndef=other===undefined,othIsReflexive=other===other;if(value>other&&!othIsNull||!valIsReflexive||valIsNull&&!othIsUndef&&othIsReflexive||valIsUndef&&othIsReflexive){return 1}if(value-1){}return index}function charsRightIndex(string,chars){var index=string.length;while(index--&&chars.indexOf(string.charAt(index))>-1){}return index}function compareAscending(object,other){return baseCompareAscending(object.criteria,other.criteria)||object.index-other.index}function compareMultiple(object,other,orders){var index=-1,objCriteria=object.criteria,othCriteria=other.criteria,length=objCriteria.length,ordersLength=orders.length;while(++index=ordersLength){return result}var order=orders[index];return result*(order==="asc"||order===true?1:-1)}}return object.index-other.index}function deburrLetter(letter){return deburredLetters[letter]}function escapeHtmlChar(chr){return htmlEscapes[chr]}function escapeRegExpChar(chr,leadingChar,whitespaceChar){if(leadingChar){chr=regexpEscapes[chr]}else if(whitespaceChar){chr=stringEscapes[chr]}return"\\"+chr}function escapeStringChar(chr){return"\\"+stringEscapes[chr]}function indexOfNaN(array,fromIndex,fromRight){var length=array.length,index=fromIndex+(fromRight?0:-1);while(fromRight?index--:++index=9&&charCode<=13)||charCode==32||charCode==160||charCode==5760||charCode==6158||charCode>=8192&&(charCode<=8202||charCode==8232||charCode==8233||charCode==8239||charCode==8287||charCode==12288||charCode==65279)}function replaceHolders(array,placeholder){var index=-1,length=array.length,resIndex=-1,result=[];while(++index>>1;var MAX_SAFE_INTEGER=9007199254740991;var metaMap=WeakMap&&new WeakMap;var realNames={};function lodash(value){if(isObjectLike(value)&&!isArray(value)&&!(value instanceof LazyWrapper)){if(value instanceof LodashWrapper){return value}if(hasOwnProperty.call(value,"__chain__")&&hasOwnProperty.call(value,"__wrapped__")){return wrapperClone(value)}}return new LodashWrapper(value)}function baseLodash(){}function LodashWrapper(value,chainAll,actions){this.__wrapped__=value;this.__actions__=actions||[];this.__chain__=!!chainAll}var support=lodash.support={};lodash.templateSettings={escape:reEscape,evaluate:reEvaluate,interpolate:reInterpolate,variable:"",imports:{_:lodash}};function LazyWrapper(value){this.__wrapped__=value;this.__actions__=[];this.__dir__=1;this.__filtered__=false;this.__iteratees__=[];this.__takeCount__=POSITIVE_INFINITY;this.__views__=[]}function lazyClone(){var result=new LazyWrapper(this.__wrapped__);result.__actions__=arrayCopy(this.__actions__);result.__dir__=this.__dir__;result.__filtered__=this.__filtered__;result.__iteratees__=arrayCopy(this.__iteratees__);result.__takeCount__=this.__takeCount__;result.__views__=arrayCopy(this.__views__);return result}function lazyReverse(){if(this.__filtered__){var result=new LazyWrapper(this);result.__dir__=-1;result.__filtered__=true}else{result=this.clone();result.__dir__*=-1}return result}function lazyValue(){var array=this.__wrapped__.value(),dir=this.__dir__,isArr=isArray(array),isRight=dir<0,arrLength=isArr?array.length:0,view=getView(0,arrLength,this.__views__),start=view.start,end=view.end,length=end-start,index=isRight?end:start-1,iteratees=this.__iteratees__,iterLength=iteratees.length,resIndex=0,takeCount=nativeMin(length,this.__takeCount__);if(!isArr||arrLength=LARGE_ARRAY_SIZE?createCache(values):null,valuesLength=values.length;if(cache){indexOf=cacheIndexOf;isCommon=false;values=cache}outer:while(++indexlength?0:length+start}end=end===undefined||end>length?length:+end||0;if(end<0){end+=length}length=start>end?0:end>>>0;start>>>=0;while(startlength?0:length+start}end=end===undefined||end>length?length:+end||0;if(end<0){end+=length}length=start>end?0:end-start>>>0;start>>>=0;var result=Array(length);while(++index=LARGE_ARRAY_SIZE,seen=isLarge?createCache():null,result=[];if(seen){indexOf=cacheIndexOf;isCommon=false}else{isLarge=false;seen=iteratee?[]:result}outer:while(++index>>1,computed=array[mid];if((retHighest?computed<=value:computed2?sources[length-2]:undefined,guard=length>2?sources[2]:undefined,thisArg=length>1?sources[length-1]:undefined;if(typeof customizer=="function"){customizer=bindCallback(customizer,thisArg,5);length-=2}else{customizer=typeof thisArg=="function"?thisArg:undefined;length-=customizer?1:0}if(guard&&isIterateeCall(sources[0],sources[1],guard)){customizer=length<3?undefined:customizer;length=1}while(++index-1?collection[index]:undefined}return baseFind(collection,predicate,eachFunc)}}function createFindIndex(fromRight){return function(array,predicate,thisArg){if(!(array&&array.length)){return-1}predicate=getCallback(predicate,thisArg,3);return baseFindIndex(array,predicate,fromRight)}}function createFindKey(objectFunc){return function(object,predicate,thisArg){predicate=getCallback(predicate,thisArg,3);return baseFind(object,predicate,objectFunc,true)}}function createFlow(fromRight){return function(){var wrapper,length=arguments.length,index=fromRight?length:-1,leftIndex=0,funcs=Array(length);while(fromRight?index--:++index=LARGE_ARRAY_SIZE){return wrapper.plant(value).value()}var index=0,result=length?funcs[index].apply(this,args):value;while(++index=length||!nativeIsFinite(length)){return""}var padLength=length-strLength;chars=chars==null?" ":chars+"";return repeat(chars,nativeCeil(padLength/chars.length)).slice(0,padLength)}function createPartialWrapper(func,bitmask,thisArg,partials){var isBind=bitmask&BIND_FLAG,Ctor=createCtorWrapper(func);function wrapper(){var argsIndex=-1,argsLength=arguments.length,leftIndex=-1,leftLength=partials.length,args=Array(leftLength+argsLength);while(++leftIndexarrLength)){return false}while(++index-1&&value%1==0&&value-1&&value%1==0&&value<=MAX_SAFE_INTEGER}function isStrictComparable(value){return value===value&&!isObject(value)}function mergeData(data,source){var bitmask=data[1],srcBitmask=source[1],newBitmask=bitmask|srcBitmask,isCommon=newBitmask0){if(++count>=HOT_COUNT){return key}}else{count=0}return baseSetData(key,value)}}();function shimKeys(object){var props=keysIn(object),propsLength=props.length,length=propsLength&&object.length;var allowIndexes=!!length&&isLength(length)&&(isArray(object)||isArguments(object));var index=-1,result=[];while(++index=120?createCache(othIndex&&value):null}var array=arrays[0],index=-1,length=array?array.length:0,seen=caches[0];outer:while(++index-1){splice.call(array,fromIndex,1)}}return array}var pullAt=restParam(function(array,indexes){indexes=baseFlatten(indexes);var result=baseAt(array,indexes);basePullAt(array,indexes.sort(baseCompareAscending));return result});function remove(array,predicate,thisArg){var result=[];if(!(array&&array.length)){return result}var index=-1,indexes=[],length=array.length;predicate=getCallback(predicate,thisArg,3);while(++index2?arrays[length-2]:undefined,thisArg=length>1?arrays[length-1]:undefined;if(length>2&&typeof iteratee=="function"){length-=2}else{iteratee=length>1&&typeof thisArg=="function"?(--length,thisArg):undefined;thisArg=undefined}arrays.length=length;return unzipWith(arrays,iteratee,thisArg)});function chain(value){var result=lodash(value);result.__chain__=true;return result}function tap(value,interceptor,thisArg){interceptor.call(thisArg,value);return value}function thru(value,interceptor,thisArg){return interceptor.call(thisArg,value)}function wrapperChain(){return chain(this)}function wrapperCommit(){return new LodashWrapper(this.value(),this.__chain__)}var wrapperConcat=restParam(function(values){values=baseFlatten(values);return this.thru(function(array){return arrayConcat(isArray(array)?array:[toObject(array)],values)})});function wrapperPlant(value){var result,parent=this;while(parent instanceof baseLodash){var clone=wrapperClone(parent);if(result){previous.__wrapped__=clone}else{result=clone}var previous=clone;parent=parent.__wrapped__}previous.__wrapped__=value;return result}function wrapperReverse(){var value=this.__wrapped__;var interceptor=function(value){return wrapped&&wrapped.__dir__<0?value:value.reverse()};if(value instanceof LazyWrapper){var wrapped=value;if(this.__actions__.length){wrapped=new LazyWrapper(this)}wrapped=wrapped.reverse();wrapped.__actions__.push({func:thru,args:[interceptor],thisArg:undefined});return new LodashWrapper(wrapped,this.__chain__)}return this.thru(interceptor)}function wrapperToString(){return this.value()+""}function wrapperValue(){return baseWrapperValue(this.__wrapped__,this.__actions__)}var at=restParam(function(collection,props){return baseAt(collection,baseFlatten(props))});var countBy=createAggregator(function(result,value,key){hasOwnProperty.call(result,key)?++result[key]:result[key]=1});function every(collection,predicate,thisArg){var func=isArray(collection)?arrayEvery:baseEvery;if(thisArg&&isIterateeCall(collection,predicate,thisArg)){predicate=undefined}if(typeof predicate!="function"||thisArg!==undefined){predicate=getCallback(predicate,thisArg,3) +}return func(collection,predicate)}function filter(collection,predicate,thisArg){var func=isArray(collection)?arrayFilter:baseFilter;predicate=getCallback(predicate,thisArg,3);return func(collection,predicate)}var find=createFind(baseEach);var findLast=createFind(baseEachRight,true);function findWhere(collection,source){return find(collection,baseMatches(source))}var forEach=createForEach(arrayEach,baseEach);var forEachRight=createForEach(arrayEachRight,baseEachRight);var groupBy=createAggregator(function(result,value,key){if(hasOwnProperty.call(result,key)){result[key].push(value)}else{result[key]=[value]}});function includes(collection,target,fromIndex,guard){var length=collection?getLength(collection):0;if(!isLength(length)){collection=values(collection);length=collection.length}if(typeof fromIndex!="number"||guard&&isIterateeCall(target,fromIndex,guard)){fromIndex=0}else{fromIndex=fromIndex<0?nativeMax(length+fromIndex,0):fromIndex||0}return typeof collection=="string"||!isArray(collection)&&isString(collection)?fromIndex<=length&&collection.indexOf(target,fromIndex)>-1:!!length&&getIndexOf(collection,target,fromIndex)>-1}var indexBy=createAggregator(function(result,value,key){result[key]=value});var invoke=restParam(function(collection,path,args){var index=-1,isFunc=typeof path=="function",isProp=isKey(path),result=isArrayLike(collection)?Array(collection.length):[];baseEach(collection,function(value){var func=isFunc?path:isProp&&value!=null?value[path]:undefined;result[++index]=func?func.apply(value,args):invokePath(value,path,args)});return result});function map(collection,iteratee,thisArg){var func=isArray(collection)?arrayMap:baseMap;iteratee=getCallback(iteratee,thisArg,3);return func(collection,iteratee)}var partition=createAggregator(function(result,value,key){result[key?0:1].push(value)},function(){return[[],[]]});function pluck(collection,path){return map(collection,property(path))}var reduce=createReduce(arrayReduce,baseEach);var reduceRight=createReduce(arrayReduceRight,baseEachRight);function reject(collection,predicate,thisArg){var func=isArray(collection)?arrayFilter:baseFilter;predicate=getCallback(predicate,thisArg,3);return func(collection,function(value,index,collection){return!predicate(value,index,collection)})}function sample(collection,n,guard){if(guard?isIterateeCall(collection,n,guard):n==null){collection=toIterable(collection);var length=collection.length;return length>0?collection[baseRandom(0,length-1)]:undefined}var index=-1,result=toArray(collection),length=result.length,lastIndex=length-1;n=nativeMin(n<0?0:+n||0,length);while(++index0){result=func.apply(this,arguments)}if(n<=1){func=undefined}return result}}var bind=restParam(function(func,thisArg,partials){var bitmask=BIND_FLAG;if(partials.length){var holders=replaceHolders(partials,bind.placeholder);bitmask|=PARTIAL_FLAG}return createWrapper(func,bitmask,thisArg,partials,holders)});var bindAll=restParam(function(object,methodNames){methodNames=methodNames.length?baseFlatten(methodNames):functions(object);var index=-1,length=methodNames.length;while(++indexwait){complete(trailingCall,maxTimeoutId)}else{timeoutId=setTimeout(delayed,remaining)}}function maxDelayed(){complete(trailing,timeoutId)}function debounced(){args=arguments;stamp=now();thisArg=this;trailingCall=trailing&&(timeoutId||!leading);if(maxWait===false){var leadingCall=leading&&!timeoutId}else{if(!maxTimeoutId&&!leading){lastCalled=stamp}var remaining=maxWait-(stamp-lastCalled),isCalled=remaining<=0||remaining>maxWait;if(isCalled){if(maxTimeoutId){maxTimeoutId=clearTimeout(maxTimeoutId)}lastCalled=stamp;result=func.apply(thisArg,args)}else if(!maxTimeoutId){maxTimeoutId=setTimeout(maxDelayed,remaining)}}if(isCalled&&timeoutId){timeoutId=clearTimeout(timeoutId)}else if(!timeoutId&&wait!==maxWait){timeoutId=setTimeout(delayed,wait)}if(leadingCall){isCalled=true;result=func.apply(thisArg,args)}if(isCalled&&!timeoutId&&!maxTimeoutId){args=thisArg=undefined}return result}debounced.cancel=cancel;return debounced}var defer=restParam(function(func,args){return baseDelay(func,1,args)});var delay=restParam(function(func,wait,args){return baseDelay(func,wait,args)});var flow=createFlow();var flowRight=createFlow(true);function memoize(func,resolver){if(typeof func!="function"||resolver&&typeof resolver!="function"){throw new TypeError(FUNC_ERROR_TEXT)}var memoized=function(){var args=arguments,key=resolver?resolver.apply(this,args):args[0],cache=memoized.cache;if(cache.has(key)){return cache.get(key)}var result=func.apply(this,args);memoized.cache=cache.set(key,result);return result};memoized.cache=new memoize.Cache;return memoized}var modArgs=restParam(function(func,transforms){transforms=baseFlatten(transforms);if(typeof func!="function"||!arrayEvery(transforms,baseIsFunction)){throw new TypeError(FUNC_ERROR_TEXT)}var length=transforms.length;return restParam(function(args){var index=nativeMin(args.length,length);while(index--){args[index]=transforms[index](args[index])}return func.apply(this,args)})});function negate(predicate){if(typeof predicate!="function"){throw new TypeError(FUNC_ERROR_TEXT)}return function(){return!predicate.apply(this,arguments)}}function once(func){return before(2,func)}var partial=createPartial(PARTIAL_FLAG);var partialRight=createPartial(PARTIAL_RIGHT_FLAG);var rearg=restParam(function(func,indexes){return createWrapper(func,REARG_FLAG,undefined,undefined,undefined,baseFlatten(indexes))});function restParam(func,start){if(typeof func!="function"){throw new TypeError(FUNC_ERROR_TEXT)}start=nativeMax(start===undefined?func.length-1:+start||0,0);return function(){var args=arguments,index=-1,length=nativeMax(args.length-start,0),rest=Array(length);while(++indexother}function gte(value,other){return value>=other}function isArguments(value){return isObjectLike(value)&&isArrayLike(value)&&hasOwnProperty.call(value,"callee")&&!propertyIsEnumerable.call(value,"callee")}var isArray=nativeIsArray||function(value){return isObjectLike(value)&&isLength(value.length)&&objToString.call(value)==arrayTag};function isBoolean(value){return value===true||value===false||isObjectLike(value)&&objToString.call(value)==boolTag}function isDate(value){return isObjectLike(value)&&objToString.call(value)==dateTag}function isElement(value){return!!value&&value.nodeType===1&&isObjectLike(value)&&!isPlainObject(value)}function isEmpty(value){if(value==null){return true}if(isArrayLike(value)&&(isArray(value)||isString(value)||isArguments(value)||isObjectLike(value)&&isFunction(value.splice))){return!value.length}return!keys(value).length}function isEqual(value,other,customizer,thisArg){customizer=typeof customizer=="function"?bindCallback(customizer,thisArg,3):undefined;var result=customizer?customizer(value,other):undefined;return result===undefined?baseIsEqual(value,other,customizer):!!result}function isError(value){return isObjectLike(value)&&typeof value.message=="string"&&objToString.call(value)==errorTag}function isFinite(value){return typeof value=="number"&&nativeIsFinite(value)}function isFunction(value){return isObject(value)&&objToString.call(value)==funcTag}function isObject(value){var type=typeof value;return!!value&&(type=="object"||type=="function")}function isMatch(object,source,customizer,thisArg){customizer=typeof customizer=="function"?bindCallback(customizer,thisArg,3):undefined;return baseIsMatch(object,getMatchData(source),customizer)}function isNaN(value){return isNumber(value)&&value!=+value}function isNative(value){if(value==null){return false}if(isFunction(value)){return reIsNative.test(fnToString.call(value))}return isObjectLike(value)&&reIsHostCtor.test(value)}function isNull(value){return value===null}function isNumber(value){return typeof value=="number"||isObjectLike(value)&&objToString.call(value)==numberTag}function isPlainObject(value){var Ctor;if(!(isObjectLike(value)&&objToString.call(value)==objectTag&&!isArguments(value))||!hasOwnProperty.call(value,"constructor")&&(Ctor=value.constructor,typeof Ctor=="function"&&!(Ctor instanceof Ctor))){return false}var result;baseForIn(value,function(subValue,key){result=key});return result===undefined||hasOwnProperty.call(value,result)}function isRegExp(value){return isObject(value)&&objToString.call(value)==regexpTag}function isString(value){return typeof value=="string"||isObjectLike(value)&&objToString.call(value)==stringTag}function isTypedArray(value){return isObjectLike(value)&&isLength(value.length)&&!!typedArrayTags[objToString.call(value)]}function isUndefined(value){return value===undefined}function lt(value,other){return value0;while(++index=nativeMin(start,end)&&value=0&&string.indexOf(target,position)==position}function escape(string){string=baseToString(string);return string&&reHasUnescapedHtml.test(string)?string.replace(reUnescapedHtml,escapeHtmlChar):string}function escapeRegExp(string){string=baseToString(string);return string&&reHasRegExpChars.test(string)?string.replace(reRegExpChars,escapeRegExpChar):string||"(?:)"}var kebabCase=createCompounder(function(result,word,index){return result+(index?"-":"")+word.toLowerCase()});function pad(string,length,chars){string=baseToString(string);length=+length;var strLength=string.length;if(strLength>=length||!nativeIsFinite(length)){return string}var mid=(length-strLength)/2,leftLength=nativeFloor(mid),rightLength=nativeCeil(mid);chars=createPadding("",rightLength,chars);return chars.slice(0,leftLength)+string+chars}var padLeft=createPadDir();var padRight=createPadDir(true);function parseInt(string,radix,guard){if(guard?isIterateeCall(string,radix,guard):radix==null){radix=0}else if(radix){radix=+radix}string=trim(string);return nativeParseInt(string,radix||(reHasHexPrefix.test(string)?16:10))}function repeat(string,n){var result="";string=baseToString(string);n=+n;if(n<1||!string||!nativeIsFinite(n)){return result}do{if(n%2){result+=string}n=nativeFloor(n/2);string+=string}while(n);return result}var snakeCase=createCompounder(function(result,word,index){return result+(index?"_":"")+word.toLowerCase()});var startCase=createCompounder(function(result,word,index){return result+(index?" ":"")+(word.charAt(0).toUpperCase()+word.slice(1))});function startsWith(string,target,position){string=baseToString(string);position=position==null?0:nativeMin(position<0?0:+position||0,string.length);return string.lastIndexOf(target,position)==position}function template(string,options,otherOptions){var settings=lodash.templateSettings;if(otherOptions&&isIterateeCall(string,options,otherOptions)){options=otherOptions=undefined}string=baseToString(string);options=assignWith(baseAssign({},otherOptions||options),settings,assignOwnDefaults);var imports=assignWith(baseAssign({},options.imports),settings.imports,assignOwnDefaults),importsKeys=keys(imports),importsValues=baseValues(imports,importsKeys);var isEscaping,isEvaluating,index=0,interpolate=options.interpolate||reNoMatch,source="__p += '";var reDelimiters=RegExp((options.escape||reNoMatch).source+"|"+interpolate.source+"|"+(interpolate===reInterpolate?reEsTemplate:reNoMatch).source+"|"+(options.evaluate||reNoMatch).source+"|$","g");var sourceURL="//# sourceURL="+("sourceURL"in options?options.sourceURL:"lodash.templateSources["+ ++templateCounter+"]")+"\n";string.replace(reDelimiters,function(match,escapeValue,interpolateValue,esTemplateValue,evaluateValue,offset){interpolateValue||(interpolateValue=esTemplateValue);source+=string.slice(index,offset).replace(reUnescapedString,escapeStringChar);if(escapeValue){isEscaping=true;source+="' +\n__e("+escapeValue+") +\n'"}if(evaluateValue){isEvaluating=true;source+="';\n"+evaluateValue+";\n__p += '"}if(interpolateValue){source+="' +\n((__t = ("+interpolateValue+")) == null ? '' : __t) +\n'"}index=offset+match.length;return match});source+="';\n";var variable=options.variable;if(!variable){source="with (obj) {\n"+source+"\n}\n"}source=(isEvaluating?source.replace(reEmptyStringLeading,""):source).replace(reEmptyStringMiddle,"$1").replace(reEmptyStringTrailing,"$1;");source="function("+(variable||"obj")+") {\n"+(variable?"":"obj || (obj = {});\n")+"var __t, __p = ''"+(isEscaping?", __e = _.escape":"")+(isEvaluating?", __j = Array.prototype.join;\n"+"function print() { __p += __j.call(arguments, '') }\n":";\n")+source+"return __p\n}";var result=attempt(function(){return Function(importsKeys,sourceURL+"return "+source).apply(undefined,importsValues)});result.source=source;if(isError(result)){throw result}return result}function trim(string,chars,guard){var value=string;string=baseToString(string);if(!string){return string}if(guard?isIterateeCall(value,chars,guard):chars==null){return string.slice(trimmedLeftIndex(string),trimmedRightIndex(string)+1)}chars=chars+"";return string.slice(charsLeftIndex(string,chars),charsRightIndex(string,chars)+1)}function trimLeft(string,chars,guard){var value=string;string=baseToString(string);if(!string){return string}if(guard?isIterateeCall(value,chars,guard):chars==null){return string.slice(trimmedLeftIndex(string))}return string.slice(charsLeftIndex(string,chars+""))}function trimRight(string,chars,guard){var value=string;string=baseToString(string);if(!string){return string}if(guard?isIterateeCall(value,chars,guard):chars==null){return string.slice(0,trimmedRightIndex(string)+1)}return string.slice(0,charsRightIndex(string,chars+"")+1)}function trunc(string,options,guard){if(guard&&isIterateeCall(string,options,guard)){options=undefined}var length=DEFAULT_TRUNC_LENGTH,omission=DEFAULT_TRUNC_OMISSION;if(options!=null){if(isObject(options)){var separator="separator"in options?options.separator:separator;length="length"in options?+options.length||0:length;omission="omission"in options?baseToString(options.omission):omission}else{length=+options||0}}string=baseToString(string);if(length>=string.length){return string}var end=length-omission.length;if(end<1){return omission}var result=string.slice(0,end);if(separator==null){return result+omission}if(isRegExp(separator)){if(string.slice(end).search(separator)){var match,newEnd,substring=string.slice(0,end);if(!separator.global){separator=RegExp(separator.source,(reFlags.exec(separator)||"")+"g")}separator.lastIndex=0;while(match=separator.exec(substring)){newEnd=match.index}result=result.slice(0,newEnd==null?end:newEnd)}}else if(string.indexOf(separator,end)!=end){var index=result.lastIndexOf(separator);if(index>-1){result=result.slice(0,index)}}return result+omission}function unescape(string){string=baseToString(string);return string&&reHasEscapedHtml.test(string)?string.replace(reEscapedHtml,unescapeHtmlChar):string}function words(string,pattern,guard){if(guard&&isIterateeCall(string,pattern,guard)){pattern=undefined}string=baseToString(string);return string.match(pattern||reWords)||[]}var attempt=restParam(function(func,args){try{return func.apply(undefined,args)}catch(e){return isError(e)?e:new Error(e)}});function callback(func,thisArg,guard){if(guard&&isIterateeCall(func,thisArg,guard)){thisArg=undefined}return isObjectLike(func)?matches(func):baseCallback(func,thisArg)}function constant(value){return function(){return value}}function identity(value){return value}function matches(source){return baseMatches(baseClone(source,true))}function matchesProperty(path,srcValue){return baseMatchesProperty(path,baseClone(srcValue,true))}var method=restParam(function(path,args){return function(object){return invokePath(object,path,args)}});var methodOf=restParam(function(object,args){return function(path){return invokePath(object,path,args)}});function mixin(object,source,options){if(options==null){var isObj=isObject(source),props=isObj?keys(source):undefined,methodNames=props&&props.length?baseFunctions(source,props):undefined;if(!(methodNames?methodNames.length:isObj)){methodNames=false;options=source;source=object;object=this}}if(!methodNames){methodNames=baseFunctions(source,keys(source))}var chain=true,index=-1,isFunc=isFunction(object),length=methodNames.length;if(options===false){chain=false}else if(isObject(options)&&"chain"in options){chain=options.chain}while(++index0||end<0)){return new LazyWrapper(result)}if(start<0){result=result.takeRight(-start)}else if(start){result=result.drop(start)}if(end!==undefined){end=+end||0;result=end<0?result.dropRight(-end):result.take(end-start)}return result};LazyWrapper.prototype.takeRightWhile=function(predicate,thisArg){return this.reverse().takeWhile(predicate,thisArg).reverse()};LazyWrapper.prototype.toArray=function(){return this.take(POSITIVE_INFINITY)};baseForOwn(LazyWrapper.prototype,function(func,methodName){var checkIteratee=/^(?:filter|map|reject)|While$/.test(methodName),retUnwrapped=/^(?:first|last)$/.test(methodName),lodashFunc=lodash[retUnwrapped?"take"+(methodName=="last"?"Right":""):methodName];if(!lodashFunc){return}lodash.prototype[methodName]=function(){var args=retUnwrapped?[1]:arguments,chainAll=this.__chain__,value=this.__wrapped__,isHybrid=!!this.__actions__.length,isLazy=value instanceof LazyWrapper,iteratee=args[0],useLazy=isLazy||isArray(value);if(useLazy&&checkIteratee&&typeof iteratee=="function"&&iteratee.length!=1){isLazy=useLazy=false}var interceptor=function(value){return retUnwrapped&&chainAll?lodashFunc(value,1)[0]:lodashFunc.apply(undefined,arrayPush([value],args))};var action={func:thru,args:[interceptor],thisArg:undefined},onlyLazy=isLazy&&!isHybrid;if(retUnwrapped&&!chainAll){if(onlyLazy){value=value.clone();value.__actions__.push(action);return func.call(value)}return lodashFunc.call(undefined,this.value())[0]}if(!retUnwrapped&&useLazy){value=onlyLazy?value:new LazyWrapper(this);var result=func.apply(value,args);result.__actions__.push(action);return new LodashWrapper(result,chainAll)}return this.thru(interceptor)}});arrayEach(["join","pop","push","replace","shift","sort","splice","split","unshift"],function(methodName){var func=(/^(?:replace|split)$/.test(methodName)?stringProto:arrayProto)[methodName],chainName=/^(?:push|sort|unshift)$/.test(methodName)?"tap":"thru",retUnwrapped=/^(?:join|pop|replace|shift)$/.test(methodName);lodash.prototype[methodName]=function(){var args=arguments;if(retUnwrapped&&!this.__chain__){return func.apply(this.value(),args)}return this[chainName](function(value){return func.apply(value,args)})}});baseForOwn(LazyWrapper.prototype,function(func,methodName){var lodashFunc=lodash[methodName];if(lodashFunc){var key=lodashFunc.name,names=realNames[key]||(realNames[key]=[]);names.push({name:methodName,func:lodashFunc})}});realNames[createHybridWrapper(undefined,BIND_KEY_FLAG).name]=[{name:"wrapper",func:undefined}];LazyWrapper.prototype.clone=lazyClone;LazyWrapper.prototype.reverse=lazyReverse;LazyWrapper.prototype.value=lazyValue;lodash.prototype.chain=wrapperChain;lodash.prototype.commit=wrapperCommit;lodash.prototype.concat=wrapperConcat;lodash.prototype.plant=wrapperPlant;lodash.prototype.reverse=wrapperReverse;lodash.prototype.toString=wrapperToString;lodash.prototype.run=lodash.prototype.toJSON=lodash.prototype.valueOf=lodash.prototype.value=wrapperValue;lodash.prototype.collect=lodash.prototype.map;lodash.prototype.head=lodash.prototype.first;lodash.prototype.select=lodash.prototype.filter;lodash.prototype.tail=lodash.prototype.rest;return lodash}var _=runInContext();if(typeof define=="function"&&typeof define.amd=="object"&&define.amd){root._=_;define(function(){return _})}else if(freeExports&&freeModule){if(moduleExports){(freeModule.exports=_)._=_}else{freeExports._=_}}else{root._=_}}).call(this)}).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{}]},{},[1])(1)}); \ No newline at end of file diff --git a/plugin/src/main/resources/com/timecat/plugin/report/analysis/js/jquery.min.js b/plugin/src/main/resources/com/timecat/plugin/report/analysis/js/jquery.min.js new file mode 100644 index 0000000..4d9b3a2 --- /dev/null +++ b/plugin/src/main/resources/com/timecat/plugin/report/analysis/js/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";var n=[],r=e.document,i=Object.getPrototypeOf,o=n.slice,a=n.concat,s=n.push,u=n.indexOf,l={},c=l.toString,f=l.hasOwnProperty,p=f.toString,d=p.call(Object),h={},g=function e(t){return"function"==typeof t&&"number"!=typeof t.nodeType},y=function e(t){return null!=t&&t===t.window},v={type:!0,src:!0,noModule:!0};function m(e,t,n){var i,o=(t=t||r).createElement("script");if(o.text=e,n)for(i in v)n[i]&&(o[i]=n[i]);t.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[c.call(e)]||"object":typeof e}var b="3.3.1",w=function(e,t){return new w.fn.init(e,t)},T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;w.fn=w.prototype={jquery:"3.3.1",constructor:w,length:0,toArray:function(){return o.call(this)},get:function(e){return null==e?o.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=w.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return w.each(this,e)},map:function(e){return this.pushStack(w.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(o.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,s,u,l,c,f,p,d,h,g,y,v,m,x,b="sizzle"+1*new Date,w=e.document,T=0,C=0,E=ae(),k=ae(),S=ae(),D=function(e,t){return e===t&&(f=!0),0},N={}.hasOwnProperty,A=[],j=A.pop,q=A.push,L=A.push,H=A.slice,O=function(e,t){for(var n=0,r=e.length;n+~]|"+M+")"+M+"*"),z=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),X=new RegExp(W),U=new RegExp("^"+R+"$"),V={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Q=/^[^{]+\{\s*\[native \w/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ee=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=function(){p()},ie=me(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{L.apply(A=H.call(w.childNodes),w.childNodes),A[w.childNodes.length].nodeType}catch(e){L={apply:A.length?function(e,t){q.apply(e,H.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function oe(e,t,r,i){var o,s,l,c,f,h,v,m=t&&t.ownerDocument,T=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==T&&9!==T&&11!==T)return r;if(!i&&((t?t.ownerDocument||t:w)!==d&&p(t),t=t||d,g)){if(11!==T&&(f=J.exec(e)))if(o=f[1]){if(9===T){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(m&&(l=m.getElementById(o))&&x(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return L.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return L.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!S[e+" "]&&(!y||!y.test(e))){if(1!==T)m=t,v=e;else if("object"!==t.nodeName.toLowerCase()){(c=t.getAttribute("id"))?c=c.replace(te,ne):t.setAttribute("id",c=b),s=(h=a(e)).length;while(s--)h[s]="#"+c+" "+ve(h[s]);v=h.join(","),m=K.test(e)&&ge(t.parentNode)||t}if(v)try{return L.apply(r,m.querySelectorAll(v)),r}catch(e){}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace(B,"$1"),t,r,i)}function ae(){var e=[];function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}return t}function se(e){return e[b]=!0,e}function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){var n=e.split("|"),i=n.length;while(i--)r.attrHandle[n[i]]=t}function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function de(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ie(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function he(e){return se(function(t){return t=+t,se(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function ge(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}n=oe.support={},o=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},p=oe.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!==d&&9===a.nodeType&&a.documentElement?(d=a,h=d.documentElement,g=!o(d),w!==d&&(i=d.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",re,!1):i.attachEvent&&i.attachEvent("onunload",re)),n.attributes=ue(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ue(function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=Q.test(d.getElementsByClassName),n.getById=ue(function(e){return h.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&g)return t.getElementsByClassName(e)},v=[],y=[],(n.qsa=Q.test(d.querySelectorAll))&&(ue(function(e){h.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+b+"-]").length||y.push("~="),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||y.push(".#.+[+~]")}),ue(function(e){e.innerHTML="";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(n.matchesSelector=Q.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ue(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),v.push("!=",W)}),y=y.length&&new RegExp(y.join("|")),v=v.length&&new RegExp(v.join("|")),t=Q.test(h.compareDocumentPosition),x=t||Q.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===d||e.ownerDocument===w&&x(w,e)?-1:t===d||t.ownerDocument===w&&x(w,t)?1:c?O(c,e)-O(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===d?-1:t===d?1:i?-1:o?1:c?O(c,e)-O(c,t):0;if(i===o)return ce(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?ce(a[r],s[r]):a[r]===w?-1:s[r]===w?1:0},d):d},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&p(e),t=t.replace(z,"='$1']"),n.matchesSelector&&g&&!S[t+" "]&&(!v||!v.test(t))&&(!y||!y.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return oe(t,d,null,[e]).length>0},oe.contains=function(e,t){return(e.ownerDocument||e)!==d&&p(e),x(e,t)},oe.attr=function(e,t){(e.ownerDocument||e)!==d&&p(e);var i=r.attrHandle[t.toLowerCase()],o=i&&N.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},oe.escape=function(e){return(e+"").replace(te,ne)},oe.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},oe.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(D),f){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return c=null,e},i=oe.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else while(t=e[r++])n+=i(t);return n},(r=oe.selectors={cacheLength:50,createPseudo:se,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Z,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(Z,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||oe.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&oe.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return V.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Z,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=E[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=oe.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace($," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",y=t.parentNode,v=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(y){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===v:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?y.firstChild:y.lastChild],a&&m){x=(d=(l=(c=(f=(p=y)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1])&&l[2],p=d&&y.childNodes[d];while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if(1===p.nodeType&&++x&&p===t){c[e]=[T,d,x];break}}else if(m&&(x=d=(l=(c=(f=(p=t)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1]),!1===x)while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===v:1===p.nodeType)&&++x&&(m&&((c=(f=p[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]=[T,x]),p===t))break;return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||oe.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?se(function(e,n){var r,o=i(e,t),a=o.length;while(a--)e[r=O(e,o[a])]=!(n[r]=o[a])}):function(e){return i(e,0,n)}):i}},pseudos:{not:se(function(e){var t=[],n=[],r=s(e.replace(B,"$1"));return r[b]?se(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:se(function(e){return function(t){return oe(e,t).length>0}}),contains:se(function(e){return e=e.replace(Z,ee),function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:se(function(e){return U.test(e||"")||oe.error("unsupported lang: "+e),e=e.replace(Z,ee).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:de(!1),disabled:de(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Y.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:he(function(){return[0]}),last:he(function(e,t){return[t-1]}),eq:he(function(e,t,n){return[n<0?n+t:n]}),even:he(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:he(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function be(e,t,n){for(var r=0,i=t.length;r-1&&(o[l]=!(a[l]=f))}}else v=we(v===a?v.splice(h,v.length):v),i?i(null,a,v,u):L.apply(a,v)})}function Ce(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],s=a||r.relative[" "],u=a?1:0,c=me(function(e){return e===t},s,!0),f=me(function(e){return O(t,e)>-1},s,!0),p=[function(e,n,r){var i=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];u1&&xe(p),u>1&&ve(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(B,"$1"),n,u0,i=e.length>0,o=function(o,a,s,u,c){var f,h,y,v=0,m="0",x=o&&[],b=[],w=l,C=o||i&&r.find.TAG("*",c),E=T+=null==w?1:Math.random()||.1,k=C.length;for(c&&(l=a===d||a||c);m!==k&&null!=(f=C[m]);m++){if(i&&f){h=0,a||f.ownerDocument===d||(p(f),s=!g);while(y=e[h++])if(y(f,a||d,s)){u.push(f);break}c&&(T=E)}n&&((f=!y&&f)&&v--,o&&x.push(f))}if(v+=m,n&&m!==v){h=0;while(y=t[h++])y(x,b,a,s);if(o){if(v>0)while(m--)x[m]||b[m]||(b[m]=j.call(u));b=we(b)}L.apply(u,b),c&&!o&&b.length>0&&v+t.length>1&&oe.uniqueSort(u)}return c&&(T=E,l=w),x};return n?se(o):o}return s=oe.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=a(e)),n=t.length;while(n--)(o=Ce(t[n]))[b]?r.push(o):i.push(o);(o=S(e,Ee(i,r))).selector=e}return o},u=oe.select=function(e,t,n,i){var o,u,l,c,f,p="function"==typeof e&&e,d=!i&&a(e=p.selector||e);if(n=n||[],1===d.length){if((u=d[0]=d[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&g&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(Z,ee),t)||[])[0]))return n;p&&(t=t.parentNode),e=e.slice(u.shift().value.length)}o=V.needsContext.test(e)?0:u.length;while(o--){if(l=u[o],r.relative[c=l.type])break;if((f=r.find[c])&&(i=f(l.matches[0].replace(Z,ee),K.test(u[0].type)&&ge(t.parentNode)||t))){if(u.splice(o,1),!(e=i.length&&ve(u)))return L.apply(n,i),n;break}}}return(p||s(e,d))(i,t,!g,n,!t||K.test(e)&&ge(t.parentNode)||t),n},n.sortStable=b.split("").sort(D).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ue(function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))}),ue(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||le("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ue(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||le("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ue(function(e){return null==e.getAttribute("disabled")})||le(P,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),oe}(e);w.find=E,w.expr=E.selectors,w.expr[":"]=w.expr.pseudos,w.uniqueSort=w.unique=E.uniqueSort,w.text=E.getText,w.isXMLDoc=E.isXML,w.contains=E.contains,w.escapeSelector=E.escape;var k=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&w(e).is(n))break;r.push(e)}return r},S=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},D=w.expr.match.needsContext;function N(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var A=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,t,n){return g(t)?w.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?w.grep(e,function(e){return e===t!==n}):"string"!=typeof t?w.grep(e,function(e){return u.call(t,e)>-1!==n}):w.filter(t,e,n)}w.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?w.find.matchesSelector(r,e)?[r]:[]:w.find.matches(e,w.grep(t,function(e){return 1===e.nodeType}))},w.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(w(e).filter(function(){for(t=0;t1?w.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&D.test(e)?w(e):e||[],!1).length}});var q,L=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(w.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:L.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof w?t[0]:t,w.merge(this,w.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:r,!0)),A.test(i[1])&&w.isPlainObject(t))for(i in t)g(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=r.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(w):w.makeArray(e,this)}).prototype=w.fn,q=w(r);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};w.fn.extend({has:function(e){var t=w(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&w.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?w.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?u.call(w(e),this[0]):u.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(w.uniqueSort(w.merge(this.get(),w(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}w.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return k(e,"parentNode")},parentsUntil:function(e,t,n){return k(e,"parentNode",n)},next:function(e){return P(e,"nextSibling")},prev:function(e){return P(e,"previousSibling")},nextAll:function(e){return k(e,"nextSibling")},prevAll:function(e){return k(e,"previousSibling")},nextUntil:function(e,t,n){return k(e,"nextSibling",n)},prevUntil:function(e,t,n){return k(e,"previousSibling",n)},siblings:function(e){return S((e.parentNode||{}).firstChild,e)},children:function(e){return S(e.firstChild)},contents:function(e){return N(e,"iframe")?e.contentDocument:(N(e,"template")&&(e=e.content||e),w.merge([],e.childNodes))}},function(e,t){w.fn[e]=function(n,r){var i=w.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=w.filter(r,i)),this.length>1&&(O[e]||w.uniqueSort(i),H.test(e)&&i.reverse()),this.pushStack(i)}});var M=/[^\x20\t\r\n\f]+/g;function R(e){var t={};return w.each(e.match(M)||[],function(e,n){t[n]=!0}),t}w.Callbacks=function(e){e="string"==typeof e?R(e):w.extend({},e);var t,n,r,i,o=[],a=[],s=-1,u=function(){for(i=i||e.once,r=t=!0;a.length;s=-1){n=a.shift();while(++s-1)o.splice(n,1),n<=s&&s--}),this},has:function(e){return e?w.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l};function I(e){return e}function W(e){throw e}function $(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n):e&&g(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}w.extend({Deferred:function(t){var n=[["notify","progress",w.Callbacks("memory"),w.Callbacks("memory"),2],["resolve","done",w.Callbacks("once memory"),w.Callbacks("once memory"),0,"resolved"],["reject","fail",w.Callbacks("once memory"),w.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},"catch":function(e){return i.then(null,e)},pipe:function(){var e=arguments;return w.Deferred(function(t){w.each(n,function(n,r){var i=g(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&g(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){var o=0;function a(t,n,r,i){return function(){var s=this,u=arguments,l=function(){var e,l;if(!(t=o&&(r!==W&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?c():(w.Deferred.getStackHook&&(c.stackTrace=w.Deferred.getStackHook()),e.setTimeout(c))}}return w.Deferred(function(e){n[0][3].add(a(0,e,g(i)?i:I,e.notifyWith)),n[1][3].add(a(0,e,g(t)?t:I)),n[2][3].add(a(0,e,g(r)?r:W))}).promise()},promise:function(e){return null!=e?w.extend(e,i):i}},o={};return w.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=o.call(arguments),a=w.Deferred(),s=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?o.call(arguments):n,--t||a.resolveWith(r,i)}};if(t<=1&&($(e,a.done(s(n)).resolve,a.reject,!t),"pending"===a.state()||g(i[n]&&i[n].then)))return a.then();while(n--)$(i[n],s(n),a.reject);return a.promise()}});var B=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;w.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&B.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},w.readyException=function(t){e.setTimeout(function(){throw t})};var F=w.Deferred();w.fn.ready=function(e){return F.then(e)["catch"](function(e){w.readyException(e)}),this},w.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--w.readyWait:w.isReady)||(w.isReady=!0,!0!==e&&--w.readyWait>0||F.resolveWith(r,[w]))}}),w.ready.then=F.then;function _(){r.removeEventListener("DOMContentLoaded",_),e.removeEventListener("load",_),w.ready()}"complete"===r.readyState||"loading"!==r.readyState&&!r.documentElement.doScroll?e.setTimeout(w.ready):(r.addEventListener("DOMContentLoaded",_),e.addEventListener("load",_));var z=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===x(n)){i=!0;for(s in n)z(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,g(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(w(e),n)})),t))for(;s1,null,!0)},removeData:function(e){return this.each(function(){K.remove(this,e)})}}),w.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=J.get(e,t),n&&(!r||Array.isArray(n)?r=J.access(e,t,w.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=w.queue(e,t),r=n.length,i=n.shift(),o=w._queueHooks(e,t),a=function(){w.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return J.get(e,n)||J.access(e,n,{empty:w.Callbacks("once memory").add(function(){J.remove(e,[t+"queue",n])})})}}),w.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]+)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,"",""],thead:[1,"",""],col:[2,"",""],tr:[2,"",""],td:[3,"",""],_default:[0,"",""]};ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&N(e,t)?w.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n-1)i&&i.push(o);else if(l=w.contains(o.ownerDocument,o),a=ye(f.appendChild(o),"script"),l&&ve(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}!function(){var e=r.createDocumentFragment().appendChild(r.createElement("div")),t=r.createElement("input");t.setAttribute("type","radio"),t.setAttribute("checked","checked"),t.setAttribute("name","t"),e.appendChild(t),h.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="x",h.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var be=r.documentElement,we=/^key/,Te=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ce=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function ke(){return!1}function Se(){try{return r.activeElement}catch(e){}}function De(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)De(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=ke;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return w().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=w.guid++)),e.each(function(){w.event.add(this,t,i,r,n)})}w.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.get(e);if(y){n.handler&&(n=(o=n).handler,i=o.selector),i&&w.find.matchesSelector(be,i),n.guid||(n.guid=w.guid++),(u=y.events)||(u=y.events={}),(a=y.handle)||(a=y.handle=function(t){return"undefined"!=typeof w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(M)||[""]).length;while(l--)d=g=(s=Ce.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=w.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=w.event.special[d]||{},c=w.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&w.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),w.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.hasData(e)&&J.get(e);if(y&&(u=y.events)){l=(t=(t||"").match(M)||[""]).length;while(l--)if(s=Ce.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){f=w.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,y.handle)||w.removeEvent(e,d,y.handle),delete u[d])}else for(d in u)w.event.remove(e,d+t[l],n,r,!0);w.isEmptyObject(u)&&J.remove(e,"handle events")}},dispatch:function(e){var t=w.event.fix(e),n,r,i,o,a,s,u=new Array(arguments.length),l=(J.get(this,"events")||{})[t.type]||[],c=w.event.special[t.type]||{};for(u[0]=t,n=1;n=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:w.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,Ae=/
%output_msg%
注:为节省数据量,含有子节点的库若已经出现过了,就不再显示其子节点。